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 10KB

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