123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464 |
- 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.repr()}`, token.line);
- }
-
- private select(): AST.Statement | Error {
- this.eat(TokenKind.SELECT);
- const args = this.selectArguments();
- if (isError(args)) {
- return args;
- }
-
- const from = this.match(TokenKind.FROM) ? this.from() : null;
- if (isError(from)) {
- return from;
- }
-
- const where = this.match(TokenKind.WHERE) ? this.where() : null;
- if (isError(where)) {
- return where;
- }
-
- return new AST.SelectStatement({
- arguments: args,
- from,
- where,
- });
- }
-
- private selectArguments(): AST.SelectArgument[] | Error {
- const args = [];
-
- while (true) {
- if (this.match(TokenKind.STAR)) {
- this.eat(TokenKind.STAR);
- args.push(new AST.Star());
- } else if (this.match(TokenKind.IDENTIFIER) && this.peek_match(TokenKind.DOT)) {
- const left = this.identifier();
- if (isError(left)) {
- return left;
- }
- const right = this.selectArgumentDotExpression(left);
- if (isError(right)) {
- return right;
- }
- args.push(right);
- } else if (this.match(TokenKind.BACKTICK) && this.peek_match(TokenKind.DOT, 3)) {
- const left = this.backtick();
- if (isError(left)) {
- return left;
- }
- const right = this.selectArgumentDotExpression(left);
- if (isError(right)) {
- return right;
- }
- args.push(right);
- } else {
- const arg = this.expr();
- if (isError(arg)) {
- return arg;
- }
- args.push(arg);
- }
-
- if (this.match(TokenKind.COMMA)) {
- this.eat(TokenKind.COMMA);
- } else {
- break;
- }
- }
-
- return args;
- }
-
- private selectArgumentDotExpression(left: AST.Identifier | AST.Backtick): AST.Expr | Error {
- this.eat(TokenKind.DOT);
-
- if (this.match(TokenKind.STAR)) {
- this.eat(TokenKind.STAR);
- const right = new AST.Star();
- return new AST.Binary({ left, right, type: AST.BinaryExpressionTypes.DOT });
- } else {
- const right = this.expr();
- if (isError(right)) {
- return right;
- }
- return new AST.Binary({ left, right, type: AST.BinaryExpressionTypes.DOT });
- }
- }
-
- private from(): AST.FromTarget | Error {
- this.eat(TokenKind.FROM);
- return this.fromTarget();
- }
-
- private fromTarget(): AST.FromTarget | Error {
- if (this.match(TokenKind.LPAREN)) {
- this.eat(TokenKind.LPAREN);
- const stmt = this.select();
- if (isError(stmt)) {
- return stmt;
- }
-
- const rparen = this.eat(TokenKind.RPAREN);
- if (isError(rparen)) {
- return rparen;
- }
-
- if (this.match(TokenKind.AS)) {
- return this.alias(stmt);
- }
-
- return stmt;
- }
-
- const primary = this.backtickOrIdentifier();
- if (isError(primary)) {
- return primary;
- }
-
- return this.alias(primary);
- }
-
- private alias<T extends AST.Expr | AST.Statement>(obj: T): AST.Alias | T | Error {
- if (!this.match(TokenKind.AS)) {
- return obj;
- }
-
- this.eat(TokenKind.AS);
-
- const token = this.currentToken();
- const alias = this.match(TokenKind.BACKTICK)
- ? this.backtick()
- : this.match(TokenKind.IDENTIFIER)
- ? this.identifier()
- : new Error(`Unexpected token: ${token.repr()}`, token.line);
-
- if (isError(alias)) {
- return alias;
- }
-
- return new AST.Alias(obj, alias);
- }
-
- private where(): AST.Expr | Error {
- this.eat(TokenKind.WHERE);
- return this.expr();
- }
-
- private expr(): AST.Expr | Error {
- return this.or();
- }
-
- private or(): AST.Expr | Error {
- let left = this.and();
- if (isError(left)) {
- return left;
- }
-
- while (this.match(TokenKind.OR)) {
- this.eat(TokenKind.OR);
- const right = this.and();
- if (isError(right)) {
- return right;
- }
-
- left = new AST.Binary({
- left,
- right,
- type: AST.BinaryExpressionTypes.OR,
- });
- }
-
- return left;
- }
-
- private and(): AST.Expr | Error {
- let left = this.equality();
- if (isError(left)) {
- return left;
- }
-
- while (this.match(TokenKind.AND)) {
- this.eat(TokenKind.AND);
- const right = this.equality();
- if (isError(right)) {
- return right;
- }
-
- left = new AST.Binary({
- left,
- right,
- type: AST.BinaryExpressionTypes.AND,
- });
- }
-
- return left;
- }
-
- private equality(): AST.Expr | Error {
- const left = this.addition();
- if (isError(left)) {
- return left;
- }
-
- if (this.match(TokenKind.EQUALS)) {
- this.eat(TokenKind.EQUALS);
- const right = this.addition();
- if (isError(right)) {
- return right;
- }
-
- return new AST.Binary({
- left,
- right,
- type: AST.BinaryExpressionTypes.EQUALITY,
- });
- }
-
- return left;
- }
-
- private addition(): AST.Expr | Error {
- const left = this.multiplication();
- if (isError(left)) {
- return left;
- }
-
- if (this.match(TokenKind.PLUS)) {
- this.eat(TokenKind.PLUS);
- const right = this.multiplication();
- if (isError(right)) {
- return right;
- }
-
- return new AST.Binary({
- left,
- right,
- type: AST.BinaryExpressionTypes.ADDITION,
- });
- }
-
- if (this.match(TokenKind.MINUS)) {
- this.eat(TokenKind.MINUS);
- const right = this.multiplication();
- if (isError(right)) {
- return right;
- }
-
- return new AST.Binary({
- left,
- right,
- type: AST.BinaryExpressionTypes.SUBTRACTION,
- });
- }
-
- return left;
- }
-
- private multiplication(): AST.Expr | Error {
- const left = this.primary();
- if (isError(left)) {
- return left;
- }
-
- if (this.match(TokenKind.STAR)) {
- this.eat(TokenKind.STAR);
- const right = this.primary();
- if (isError(right)) {
- return right;
- }
-
- return new AST.Binary({
- left,
- right,
- type: AST.BinaryExpressionTypes.MULTIPLICATION,
- });
- }
-
- if (this.match(TokenKind.SLASH)) {
- this.eat(TokenKind.SLASH);
- const right = this.primary();
- if (isError(right)) {
- return right;
- }
-
- return new AST.Binary({
- left,
- right,
- type: AST.BinaryExpressionTypes.DIVISION,
- });
- }
-
- return left;
- }
-
- private primary(): AST.Expr | Error {
- const token = this.currentToken();
-
- const primary = (() => {
- switch (token.kind) {
- case TokenKind.NUMBER:
- return this.number();
- case TokenKind.IDENTIFIER:
- return this.identifier();
- case TokenKind.BACKTICK:
- return this.backtick();
- case TokenKind.LPAREN:
- return this.group();
- default:
- return new Error(`Unexpected token: ${token.repr()}`, token.line);
- }
- })();
-
- if (isError(primary)) {
- return primary;
- }
-
- if (this.match(TokenKind.AS)) {
- this.eat(TokenKind.AS);
-
- const name = this.match(TokenKind.BACKTICK) ? this.backtick() : this.identifier();
-
- if (isError(name)) {
- return name;
- }
-
- return new AST.Alias(primary, name);
- } else if (this.match(TokenKind.DOT)) {
- this.eat(TokenKind.DOT);
-
- const right = this.match(TokenKind.BACKTICK) ? this.backtick() : this.identifier();
- if (isError(right)) {
- return right;
- }
-
- return new AST.Binary({
- left: primary,
- right,
- type: AST.BinaryExpressionTypes.DOT,
- });
- }
-
- return primary;
- }
-
- private group(): AST.Expr | Error {
- this.eat(TokenKind.LPAREN);
- const expr = this.expr();
- const rparen = this.eat(TokenKind.RPAREN);
- if (isError(rparen)) {
- return rparen;
- }
- return expr;
- }
-
- private backtickOrIdentifier(): AST.Backtick | AST.Identifier | Error {
- const token = this.currentToken();
- switch (token.kind) {
- case TokenKind.BACKTICK:
- return this.backtick();
- case TokenKind.IDENTIFIER:
- return this.identifier();
- default:
- return new Error(`Unexpected token: ${token.repr()}`, token.line);
- }
- }
-
- private backtick(): AST.Backtick | Error {
- this.eat(TokenKind.BACKTICK);
- const identifier = this.identifier();
- if (isError(identifier)) {
- return identifier;
- }
-
- const closeTick = this.eat(TokenKind.BACKTICK);
- if (isError(closeTick)) {
- return closeTick;
- }
-
- return new AST.Backtick(identifier);
- }
-
- 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;
- }
-
- return new Error(`Unexpected token: ${token.repr()}`, token.line);
- }
-
- private match(kind: TokenKind): boolean {
- return this.currentToken().kind === kind;
- }
-
- private peek_match(kind: TokenKind, step: number = 1): boolean {
- const token = this.tokens[this.position + step];
- return token && token.kind === kind;
- }
-
- private currentToken(): Token {
- return this.tokens[this.position];
- }
-
- private advance() {
- this.position += 1;
- }
-
- private atEnd(): boolean {
- return this.match(TokenKind.EOF);
- }
- }
|