A work-in-progress SQL parser written in TypeScript
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

parser.ts 8.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418
  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.repr()}`, token.line);
  30. }
  31. private select(): AST.Statement | Error {
  32. this.eat(TokenKind.SELECT);
  33. const args = this.selectArguments();
  34. if (isError(args)) {
  35. return args;
  36. }
  37. const from = this.match(TokenKind.FROM) ? this.from() : null;
  38. if (isError(from)) {
  39. return from;
  40. }
  41. const where = this.match(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 selectArguments(): AST.SelectArgument[] | Error {
  52. const args = [];
  53. while (true) {
  54. const arg = this.expr();
  55. if (isError(arg)) {
  56. return arg;
  57. }
  58. args.push(arg);
  59. if (this.match(TokenKind.COMMA)) {
  60. this.eat(TokenKind.COMMA);
  61. } else {
  62. break;
  63. }
  64. }
  65. return args;
  66. }
  67. private from(): AST.FromTarget | Error {
  68. this.eat(TokenKind.FROM);
  69. return this.fromTarget();
  70. }
  71. private fromTarget(): AST.FromTarget | Error {
  72. if (this.match(TokenKind.LPAREN)) {
  73. this.eat(TokenKind.LPAREN);
  74. const stmt = this.select();
  75. if (isError(stmt)) {
  76. return stmt;
  77. }
  78. const rparen = this.eat(TokenKind.RPAREN);
  79. if (isError(rparen)) {
  80. return rparen;
  81. }
  82. if (this.match(TokenKind.AS)) {
  83. return this.alias(stmt);
  84. }
  85. return stmt;
  86. }
  87. const primary = this.backtickOrIdentifier();
  88. if (isError(primary)) {
  89. return primary;
  90. }
  91. return this.alias(primary);
  92. }
  93. private alias<T extends AST.Expr | AST.Statement>(obj: T): AST.Alias | T | Error {
  94. if (!this.match(TokenKind.AS)) {
  95. return obj;
  96. }
  97. this.eat(TokenKind.AS);
  98. const token = this.currentToken();
  99. const alias = this.match(TokenKind.BACKTICK)
  100. ? this.backtick()
  101. : this.match(TokenKind.IDENTIFIER)
  102. ? this.identifier()
  103. : new Error(`Unexpected token: ${token.repr()}`, token.line);
  104. if (isError(alias)) {
  105. return alias;
  106. }
  107. return new AST.Alias(obj, alias);
  108. }
  109. private where(): AST.Expr | Error {
  110. this.eat(TokenKind.WHERE);
  111. return this.expr();
  112. }
  113. private expr(): AST.Expr | Error {
  114. return this.or();
  115. }
  116. private or(): AST.Expr | Error {
  117. let left = this.and();
  118. if (isError(left)) {
  119. return left;
  120. }
  121. while (this.match(TokenKind.OR)) {
  122. this.eat(TokenKind.OR);
  123. const right = this.and();
  124. if (isError(right)) {
  125. return right;
  126. }
  127. left = new AST.Binary({
  128. left,
  129. right,
  130. type: AST.BinaryExpressionTypes.OR,
  131. });
  132. }
  133. return left;
  134. }
  135. private and(): AST.Expr | Error {
  136. let left = this.equality();
  137. if (isError(left)) {
  138. return left;
  139. }
  140. while (this.match(TokenKind.AND)) {
  141. this.eat(TokenKind.AND);
  142. const right = this.equality();
  143. if (isError(right)) {
  144. return right;
  145. }
  146. left = new AST.Binary({
  147. left,
  148. right,
  149. type: AST.BinaryExpressionTypes.AND,
  150. });
  151. }
  152. return left;
  153. }
  154. private equality(): AST.Expr | Error {
  155. const left = this.addition();
  156. if (isError(left)) {
  157. return left;
  158. }
  159. if (this.match(TokenKind.EQUALS)) {
  160. this.eat(TokenKind.EQUALS);
  161. const right = this.addition();
  162. if (isError(right)) {
  163. return right;
  164. }
  165. return new AST.Binary({
  166. left,
  167. right,
  168. type: AST.BinaryExpressionTypes.EQUALITY,
  169. });
  170. }
  171. return left;
  172. }
  173. private addition(): AST.Expr | Error {
  174. const left = this.multiplication();
  175. if (isError(left)) {
  176. return left;
  177. }
  178. if (this.match(TokenKind.PLUS)) {
  179. this.eat(TokenKind.PLUS);
  180. const right = this.multiplication();
  181. if (isError(right)) {
  182. return right;
  183. }
  184. return new AST.Binary({
  185. left,
  186. right,
  187. type: AST.BinaryExpressionTypes.ADDITION,
  188. });
  189. }
  190. if (this.match(TokenKind.MINUS)) {
  191. this.eat(TokenKind.MINUS);
  192. const right = this.multiplication();
  193. if (isError(right)) {
  194. return right;
  195. }
  196. return new AST.Binary({
  197. left,
  198. right,
  199. type: AST.BinaryExpressionTypes.SUBTRACTION,
  200. });
  201. }
  202. return left;
  203. }
  204. private multiplication(): AST.Expr | Error {
  205. const left = this.primary();
  206. if (isError(left)) {
  207. return left;
  208. }
  209. if (this.match(TokenKind.STAR)) {
  210. this.eat(TokenKind.STAR);
  211. const right = this.primary();
  212. if (isError(right)) {
  213. return right;
  214. }
  215. return new AST.Binary({
  216. left,
  217. right,
  218. type: AST.BinaryExpressionTypes.MULTIPLICATION,
  219. });
  220. }
  221. if (this.match(TokenKind.SLASH)) {
  222. this.eat(TokenKind.SLASH);
  223. const right = this.primary();
  224. if (isError(right)) {
  225. return right;
  226. }
  227. return new AST.Binary({
  228. left,
  229. right,
  230. type: AST.BinaryExpressionTypes.DIVISION,
  231. });
  232. }
  233. return left;
  234. }
  235. private primary(): AST.Expr | Error {
  236. const token = this.currentToken();
  237. const primary = (() => {
  238. switch (token.kind) {
  239. case TokenKind.NUMBER:
  240. return this.number();
  241. case TokenKind.IDENTIFIER:
  242. return this.identifier();
  243. case TokenKind.BACKTICK:
  244. return this.backtick();
  245. case TokenKind.LPAREN:
  246. return this.group();
  247. default:
  248. return new Error(`Unexpected token: ${token.repr()}`, token.line);
  249. }
  250. })();
  251. if (isError(primary)) {
  252. return primary;
  253. }
  254. if (this.match(TokenKind.AS)) {
  255. this.eat(TokenKind.AS);
  256. const name = this.match(TokenKind.BACKTICK) ? this.backtick() : this.identifier();
  257. if (isError(name)) {
  258. return name;
  259. }
  260. return new AST.Alias(primary, name);
  261. } else if (this.match(TokenKind.DOT)) {
  262. this.eat(TokenKind.DOT);
  263. const right = this.match(TokenKind.BACKTICK) ? this.backtick() : this.identifier();
  264. if (isError(right)) {
  265. return right;
  266. }
  267. return new AST.Binary({
  268. left: primary,
  269. right,
  270. type: AST.BinaryExpressionTypes.DOT,
  271. });
  272. }
  273. return primary;
  274. }
  275. private group(): AST.Expr | Error {
  276. this.eat(TokenKind.LPAREN);
  277. const expr = this.expr();
  278. const rparen = this.eat(TokenKind.RPAREN);
  279. if (isError(rparen)) {
  280. return rparen;
  281. }
  282. return expr;
  283. }
  284. private backtickOrIdentifier(): AST.Backtick | AST.Identifier | Error {
  285. const token = this.currentToken();
  286. switch (token.kind) {
  287. case TokenKind.BACKTICK:
  288. return this.backtick();
  289. case TokenKind.IDENTIFIER:
  290. return this.identifier();
  291. default:
  292. return new Error(`Unexpected token: ${token.repr()}`, token.line);
  293. }
  294. }
  295. private backtick(): AST.Backtick | Error {
  296. this.eat(TokenKind.BACKTICK);
  297. const identifier = this.identifier();
  298. if (isError(identifier)) {
  299. return identifier;
  300. }
  301. const closeTick = this.eat(TokenKind.BACKTICK);
  302. if (isError(closeTick)) {
  303. return closeTick;
  304. }
  305. return new AST.Backtick(identifier);
  306. }
  307. private identifier(): AST.Identifier | Error {
  308. const identifier = this.eat(TokenKind.IDENTIFIER);
  309. if (isError(identifier)) {
  310. return identifier;
  311. }
  312. return new AST.Identifier(identifier.value || "");
  313. }
  314. private number(): AST.Number | Error {
  315. const n = this.eat(TokenKind.NUMBER);
  316. if (isError(n)) {
  317. return n;
  318. }
  319. return new AST.Number(parseFloat(n.value || ""));
  320. }
  321. private eat(kind: TokenKind) {
  322. const token = this.currentToken();
  323. if (token.kind === kind) {
  324. this.advance();
  325. return token;
  326. }
  327. return new Error(`Unexpected token: ${token.repr()}`, token.line);
  328. }
  329. private match(kind: TokenKind): boolean {
  330. return this.currentToken().kind === kind;
  331. }
  332. private currentToken(): Token {
  333. return this.tokens[this.position];
  334. }
  335. private advance() {
  336. this.position += 1;
  337. }
  338. private atEnd(): boolean {
  339. return this.match(TokenKind.EOF);
  340. }
  341. }