A work-in-progress SQL parser written in TypeScript
Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

parser.ts 8.5KB

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