A work-in-progress SQL parser written in TypeScript
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

parser.ts 3.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. import * as AST from "./ast";
  2. import Error, { isError } from "./error";
  3. import Token, { TokenKind } from "./token";
  4. export default class Parser {
  5. public tokens: Token[];
  6. public position: number;
  7. constructor(tokens: Token[]) {
  8. this.tokens = tokens;
  9. this.position = 0;
  10. }
  11. public parse(): AST.Statement[] | Error {
  12. const tree: AST.Statement[] = [];
  13. while (!this.atEnd()) {
  14. const stmt = this.statement();
  15. if (isError(stmt)) {
  16. return stmt;
  17. } else {
  18. tree.push(stmt);
  19. }
  20. }
  21. return tree;
  22. }
  23. private statement(): AST.Statement | Error {
  24. const token = this.currentToken();
  25. switch (token.kind) {
  26. case TokenKind.SELECT:
  27. return this.select();
  28. }
  29. return new Error(`Unexpected token ${token.kind}`, token.line);
  30. }
  31. private select(): AST.Statement | Error {
  32. this.eat(TokenKind.SELECT);
  33. const args = this.args();
  34. if (isError(args)) {
  35. return args;
  36. }
  37. const from = this.currentToken().kind === TokenKind.FROM ? this.from() : null;
  38. if (isError(from)) {
  39. return from;
  40. }
  41. const where = this.currentToken().kind === TokenKind.WHERE ? this.where() : null;
  42. if (isError(where)) {
  43. return where;
  44. }
  45. return new AST.SelectStatement({
  46. arguments: args,
  47. from,
  48. where,
  49. });
  50. }
  51. private args(): AST.Secondary[] | Error {
  52. const args = [];
  53. while (true) {
  54. const arg = this.alias();
  55. if (isError(arg)) {
  56. return arg;
  57. }
  58. args.push(arg);
  59. if (this.currentToken().kind === TokenKind.COMMA) {
  60. this.eat(TokenKind.COMMA);
  61. } else {
  62. break;
  63. }
  64. }
  65. return args;
  66. }
  67. private from(): AST.Secondary | Error {
  68. this.eat(TokenKind.FROM);
  69. return this.alias(this.identifier);
  70. }
  71. private where(): AST.Expr | Error {
  72. this.eat(TokenKind.WHERE);
  73. return this.expr();
  74. }
  75. private expr(): AST.Expr | Error {
  76. return this.alias();
  77. }
  78. private alias(fn: () => AST.Primary | Error = this.primary): AST.Secondary | Error {
  79. const primary = fn.bind(this)();
  80. if (isError(primary)) {
  81. return primary;
  82. }
  83. if (this.currentToken().kind === TokenKind.AS) {
  84. this.eat(TokenKind.AS);
  85. const name = this.identifier();
  86. if (isError(name)) {
  87. return name;
  88. }
  89. return new AST.Alias(primary, name);
  90. }
  91. return primary;
  92. }
  93. private primary(): AST.Primary | Error {
  94. const token = this.currentToken();
  95. switch (token.kind) {
  96. case TokenKind.NUMBER:
  97. return this.number();
  98. case TokenKind.IDENTIFIER:
  99. return this.identifier();
  100. }
  101. return new Error(
  102. `Unexpected token ${token.kind} ${token.value}`,
  103. token.line,
  104. );
  105. }
  106. private identifier(): AST.Identifier | Error {
  107. const identifier = this.eat(TokenKind.IDENTIFIER);
  108. if (isError(identifier)) {
  109. return identifier;
  110. }
  111. return new AST.Identifier(identifier.value || "");
  112. }
  113. private number(): AST.Number | Error {
  114. const n = this.eat(TokenKind.NUMBER);
  115. if (isError(n)) {
  116. return n;
  117. }
  118. return new AST.Number(parseFloat(n.value || ""));
  119. }
  120. private eat(kind: TokenKind) {
  121. const token = this.currentToken();
  122. if (token.kind === kind) {
  123. this.advance();
  124. return token;
  125. }
  126. const repr = `{ kind: ${token.kind}${token.value ? `, value: ${token.value} }` : ""}`;
  127. return new Error(`Unexpected token: ${repr}`, token.line);
  128. }
  129. private currentToken(): Token {
  130. return this.tokens[this.position];
  131. }
  132. private advance() {
  133. this.position += 1;
  134. }
  135. private atEnd(): boolean {
  136. return this.currentToken().kind === TokenKind.EOF;
  137. }
  138. }