123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156 |
- import * as AST from "./ast";
- import Error, { isError } from "./error";
- import Token, { TokenKind } from "./token";
-
- export default class Parser {
- public tokens: Token[];
- public position: number;
-
- constructor(tokens: Token[]) {
- this.tokens = tokens;
- this.position = 0;
- }
-
- public parse(): AST.Statement[] | Error {
- const tree: AST.Statement[] = [];
-
- while (!this.atEnd()) {
- const stmt = this.statement();
- if (isError(stmt)) {
- return stmt;
- } else {
- tree.push(stmt);
- }
- }
-
- return tree;
- }
-
- private statement(): AST.Statement | Error {
- const token = this.currentToken();
-
- switch (token.kind) {
- case TokenKind.SELECT:
- return this.select();
- }
-
- return new Error(`Unexpected token ${token.kind}`, token.line);
- }
-
- private select(): AST.Statement | Error {
- this.eat(TokenKind.SELECT);
- const args = this.args();
- if (isError(args)) {
- return args;
- }
-
- const currentToken = this.currentToken();
- const from = currentToken.kind === TokenKind.FROM ? this.from() : null;
- if (isError(from)) {
- return from;
- }
-
- return new AST.SelectStatement({
- arguments: args,
- from,
- });
- }
-
- private args(): AST.Secondary[] | Error {
- const args = [];
-
- while (true) {
- const arg = this.alias();
- if (isError(arg)) {
- return arg;
- }
- args.push(arg);
-
- if (this.currentToken().kind === TokenKind.COMMA) {
- this.eat(TokenKind.COMMA);
- } else {
- break;
- }
- }
-
- return args;
- }
-
- private from(): AST.Secondary | Error {
- this.eat(TokenKind.FROM);
- return this.alias(this.identifier);
- }
-
- private alias(fn: () => AST.Primary | Error = this.primary): AST.Secondary | Error {
- const primary = fn.bind(this)();
- if (isError(primary)) {
- return primary;
- }
-
- if (this.currentToken().kind === TokenKind.AS) {
- this.eat(TokenKind.AS);
- const name = this.identifier();
- if (isError(name)) {
- return name;
- }
- return new AST.Alias(primary, name);
- }
-
- return primary;
- }
-
- private primary(): AST.Primary | Error {
- const token = this.currentToken();
- switch (token.kind) {
- case TokenKind.NUMBER:
- return this.number();
- case TokenKind.IDENTIFIER:
- return this.identifier();
- }
-
- return new Error(
- `Unexpected token ${token.kind} ${token.value}`,
- token.line,
- );
- }
-
- private identifier(): AST.Identifier | Error {
- const identifier = this.eat(TokenKind.IDENTIFIER);
- if (isError(identifier)) {
- return identifier;
- }
- return new AST.Identifier(identifier.value || "");
- }
-
- private number(): AST.Number | Error {
- const n = this.eat(TokenKind.NUMBER);
- if (isError(n)) {
- return n;
- }
- return new AST.Number(parseFloat(n.value || ""));
- }
-
- private eat(kind: TokenKind) {
- const token = this.currentToken();
- if (token.kind === kind) {
- this.advance();
- return token;
- }
-
- const repr = `{ kind: ${token.kind}${token.value ? `, value: ${token.value} }` : ""}`;
-
- return new Error(`Unexpected token: ${repr}`, token.line);
- }
-
- private currentToken(): Token {
- return this.tokens[this.position];
- }
-
- private advance() {
- this.position += 1;
- }
-
- private atEnd(): boolean {
- return this.currentToken().kind === TokenKind.EOF;
- }
- }
|