(s: ITokenStream, isStatic: boolean)
| 190 | } |
| 191 | |
| 192 | function parseAtom(s: ITokenStream, isStatic: boolean): Ast.Expression { |
| 193 | const startPos = s.getPos(); |
| 194 | |
| 195 | switch (s.getTokenKind()) { |
| 196 | case TokenKind.IfKeyword: { |
| 197 | if (isStatic) break; |
| 198 | return parseIf(s); |
| 199 | } |
| 200 | case TokenKind.At: { |
| 201 | if (isStatic) break; |
| 202 | return parseFnExpr(s); |
| 203 | } |
| 204 | case TokenKind.MatchKeyword: { |
| 205 | if (isStatic) break; |
| 206 | return parseMatch(s); |
| 207 | } |
| 208 | case TokenKind.EvalKeyword: { |
| 209 | if (isStatic) break; |
| 210 | return parseEval(s); |
| 211 | } |
| 212 | case TokenKind.ExistsKeyword: { |
| 213 | if (isStatic) break; |
| 214 | return parseExists(s); |
| 215 | } |
| 216 | case TokenKind.Template: { |
| 217 | const values: Ast.Expression[] = []; |
| 218 | |
| 219 | if (isStatic) break; |
| 220 | |
| 221 | for (const [i, element] of s.getToken().children!.entries()) { |
| 222 | switch (element.kind) { |
| 223 | case TokenKind.TemplateStringElement: { |
| 224 | // トークンの終了位置を取得するために先読み |
| 225 | const nextToken = s.getToken().children![i + 1] ?? s.lookahead(1); |
| 226 | values.push(NODE('str', { value: element.value! }, element.pos, nextToken.pos)); |
| 227 | break; |
| 228 | } |
| 229 | case TokenKind.TemplateExprElement: { |
| 230 | // スキャナで埋め込み式として事前に読み取っておいたトークン列をパースする |
| 231 | const exprStream: ITokenStream = new TokenStream(element.children!); |
| 232 | if (exprStream.is(TokenKind.NewLine)) { |
| 233 | exprStream.next(); |
| 234 | } |
| 235 | const expr = parseExpr(exprStream, false); |
| 236 | if (exprStream.is(TokenKind.NewLine)) { |
| 237 | exprStream.next(); |
| 238 | } |
| 239 | exprStream.expect(TokenKind.EOF); |
| 240 | values.push(expr); |
| 241 | break; |
| 242 | } |
| 243 | default: { |
| 244 | throw unexpectedTokenError(element.kind, element.pos); |
| 245 | } |
| 246 | } |
| 247 | } |
| 248 | |
| 249 | s.next(); |
no test coverage detected