* ```abnf * FnType = "@" [TypeParams] "(" ParamTypes ")" "=>" Type * ParamTypes = [Type *(SEP Type)] * ```
(s: ITokenStream)
| 100 | * ``` |
| 101 | */ |
| 102 | function parseFnType(s: ITokenStream): Ast.TypeSource { |
| 103 | const startPos = s.getPos(); |
| 104 | |
| 105 | s.expect(TokenKind.At); |
| 106 | s.next(); |
| 107 | |
| 108 | let typeParams: Ast.TypeParam[]; |
| 109 | if (s.is(TokenKind.Lt)) { |
| 110 | typeParams = parseTypeParams(s); |
| 111 | } else { |
| 112 | typeParams = []; |
| 113 | } |
| 114 | |
| 115 | s.expect(TokenKind.OpenParen); |
| 116 | s.next(); |
| 117 | |
| 118 | const params: Ast.TypeSource[] = []; |
| 119 | while (!s.is(TokenKind.CloseParen)) { |
| 120 | if (params.length > 0) { |
| 121 | switch (s.getTokenKind()) { |
| 122 | case TokenKind.Comma: { |
| 123 | s.next(); |
| 124 | break; |
| 125 | } |
| 126 | case TokenKind.EOF: { |
| 127 | throw new AiScriptUnexpectedEOFError(s.getPos()); |
| 128 | } |
| 129 | default: { |
| 130 | throw new AiScriptSyntaxError('separator expected', s.getPos()); |
| 131 | } |
| 132 | } |
| 133 | } |
| 134 | const type = parseType(s); |
| 135 | params.push(type); |
| 136 | } |
| 137 | |
| 138 | s.expect(TokenKind.CloseParen); |
| 139 | s.next(); |
| 140 | s.expect(TokenKind.Arrow); |
| 141 | s.next(); |
| 142 | |
| 143 | const resultType = parseType(s); |
| 144 | |
| 145 | return NODE('fnTypeSource', { typeParams, params, result: resultType }, startPos, s.getPos()); |
| 146 | } |
| 147 | |
| 148 | /** |
| 149 | * ```abnf |
no test coverage detected