A work-in-progress SQL parser written in TypeScript
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

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. }