Browse Source

Parse aliases

master
Dylan Baker 4 years ago
parent
commit
ece01514fc
8 changed files with 72 additions and 24 deletions
  1. 3
    0
      src/ast.ts
  2. 11
    0
      src/ast/alias.ts
  3. 4
    4
      src/ast/selectStatement.ts
  4. 3
    0
      src/lexer.ts
  5. 22
    4
      src/parser.ts
  6. 1
    0
      src/token.ts
  7. 4
    2
      test/lexer.test.ts
  8. 24
    14
      test/parser.test.ts

+ 3
- 0
src/ast.ts View File

@@ -1,10 +1,13 @@
1
+import { Alias } from "./ast/alias";
1 2
 import { Identifier } from "./ast/identifier";
2 3
 import { Number as _Number } from "./ast/number";
3 4
 import { SelectStatement } from "./ast/selectStatement";
4 5
 
6
+export * from "./ast/alias";
5 7
 export * from "./ast/identifier";
6 8
 export * from "./ast/number";
7 9
 export * from "./ast/selectStatement";
8 10
 
9 11
 export type Primary = _Number | Identifier;
12
+export type Secondary = Primary | Alias;
10 13
 export type Statement = SelectStatement;

+ 11
- 0
src/ast/alias.ts View File

@@ -0,0 +1,11 @@
1
+import * as AST from "../ast";
2
+
3
+export class Alias {
4
+  public object: AST.Number | AST.Identifier;
5
+  public name: AST.Identifier;
6
+
7
+  constructor(object: AST.Number | AST.Identifier, name: AST.Identifier) {
8
+    this.object = object;
9
+    this.name = name;
10
+  }
11
+}

+ 4
- 4
src/ast/selectStatement.ts View File

@@ -1,13 +1,13 @@
1 1
 import * as AST from "../ast";
2 2
 
3 3
 export interface ISelectStatementOptions {
4
-  arguments: AST.Primary[];
5
-  from?: AST.Primary | null;
4
+  arguments: AST.Secondary[];
5
+  from?: AST.Secondary | null;
6 6
 }
7 7
 
8 8
 export class SelectStatement {
9
-  public arguments: AST.Primary[];
10
-  public from: AST.Primary | null;
9
+  public arguments: AST.Secondary[];
10
+  public from: AST.Secondary | null;
11 11
 
12 12
   constructor(opts: ISelectStatementOptions) {
13 13
     this.arguments = opts.arguments;

+ 3
- 0
src/lexer.ts View File

@@ -40,6 +40,9 @@ export default class Lexer {
40 40
     } else if (source.match(/^from/i)) {
41 41
       this.advance(4);
42 42
       return new Token(TokenKind.FROM, null, this.line);
43
+    } else if (source.match(/^as/i)) {
44
+      this.advance(2);
45
+      return new Token(TokenKind.AS, null, this.line);
43 46
     } else if (source.match(/^\*/)) {
44 47
       this.advance();
45 48
       return new Token(TokenKind.STAR, null, this.line);

+ 22
- 4
src/parser.ts View File

@@ -56,11 +56,11 @@ export default class Parser {
56 56
     });
57 57
   }
58 58
 
59
-  private args(): AST.Primary[] | Error {
59
+  private args(): AST.Secondary[] | Error {
60 60
     const args = [];
61 61
 
62 62
     while (true) {
63
-      const arg = this.primary();
63
+      const arg = this.alias();
64 64
       if (isError(arg)) {
65 65
         return arg;
66 66
       }
@@ -76,9 +76,27 @@ export default class Parser {
76 76
     return args;
77 77
   }
78 78
 
79
-  private from(): AST.Primary | Error {
79
+  private from(): AST.Secondary | Error {
80 80
     this.eat(TokenKind.FROM);
81
-    return this.identifier();
81
+    return this.alias();
82
+  }
83
+
84
+  private alias(): AST.Secondary | Error {
85
+    const primary = this.primary();
86
+    if (isError(primary)) {
87
+      return primary;
88
+    }
89
+
90
+    if (this.currentToken().kind === TokenKind.AS) {
91
+      this.eat(TokenKind.AS);
92
+      const name = this.identifier();
93
+      if (isError(name)) {
94
+        return name;
95
+      }
96
+      return new AST.Alias(primary, name);
97
+    }
98
+
99
+    return primary;
82 100
   }
83 101
 
84 102
   private primary(): AST.Primary | Error {

+ 1
- 0
src/token.ts View File

@@ -1,4 +1,5 @@
1 1
 export enum TokenKind {
2
+  AS = "AS",
2 3
   BACKTICK = "BACKTICK",
3 4
   COMMA = "COMMA",
4 5
   DOT = "DOT",

+ 4
- 2
test/lexer.test.ts View File

@@ -11,21 +11,23 @@ const scan = (source: string): Token[] | Error => {
11 11
 
12 12
 describe("Lexer", () => {
13 13
   it("scans uppercase keywords", () => {
14
-    const tokens = scan("SELECT FROM WHERE");
14
+    const tokens = scan("SELECT FROM WHERE AS");
15 15
     expect(tokens).to.deep.equal([
16 16
       new Token(TokenKind.SELECT, null, 1),
17 17
       new Token(TokenKind.FROM, null, 1),
18 18
       new Token(TokenKind.WHERE, null, 1),
19
+      new Token(TokenKind.AS, null, 1),
19 20
       new Token(TokenKind.EOF, null, 1),
20 21
     ]);
21 22
   });
22 23
 
23 24
   it("scans lowercase keywords", () => {
24
-    const tokens = scan("select from where");
25
+    const tokens = scan("select from where as");
25 26
     expect(tokens).to.deep.equal([
26 27
       new Token(TokenKind.SELECT, null, 1),
27 28
       new Token(TokenKind.FROM, null, 1),
28 29
       new Token(TokenKind.WHERE, null, 1),
30
+      new Token(TokenKind.AS, null, 1),
29 31
       new Token(TokenKind.EOF, null, 1),
30 32
     ]);
31 33
   });

+ 24
- 14
test/parser.test.ts View File

@@ -33,10 +33,7 @@ describe("Parser", () => {
33 33
     const tree = parse("select 5, 6");
34 34
     expect(tree).to.deep.equal([
35 35
       new AST.SelectStatement({
36
-        arguments: [
37
-          new AST.Number(5),
38
-          new AST.Number(6),
39
-        ],
36
+        arguments: [new AST.Number(5), new AST.Number(6)],
40 37
       }),
41 38
     ]);
42 39
   });
@@ -45,10 +42,7 @@ describe("Parser", () => {
45 42
     const tree = parse("select a, b");
46 43
     expect(tree).to.deep.equal([
47 44
       new AST.SelectStatement({
48
-        arguments: [
49
-          new AST.Identifier("a"),
50
-          new AST.Identifier("b"),
51
-        ],
45
+        arguments: [new AST.Identifier("a"), new AST.Identifier("b")],
52 46
       }),
53 47
     ]);
54 48
   });
@@ -57,22 +51,38 @@ describe("Parser", () => {
57 51
     const tree = parse("select a, 5");
58 52
     expect(tree).to.deep.equal([
59 53
       new AST.SelectStatement({
60
-        arguments: [
61
-          new AST.Identifier("a"),
62
-          new AST.Number(5),
63
-        ],
54
+        arguments: [new AST.Identifier("a"), new AST.Number(5)],
64 55
       }),
65 56
     ]);
66 57
   });
67 58
 
68 59
   it("should parse the from of a selection", () => {
69 60
     const tree = parse("select a from t");
61
+    expect(tree).to.deep.equal([
62
+      new AST.SelectStatement({
63
+        arguments: [new AST.Identifier("a")],
64
+        from: new AST.Identifier("t"),
65
+      }),
66
+    ]);
67
+  });
68
+
69
+  it("should parse aliases", () => {
70
+    const tree = parse("select a as b");
70 71
     expect(tree).to.deep.equal([
71 72
       new AST.SelectStatement({
72 73
         arguments: [
73
-          new AST.Identifier("a"),
74
+          new AST.Alias(new AST.Identifier("a"), new AST.Identifier("b")),
74 75
         ],
75
-        from: new AST.Identifier("t"),
76
+      }),
77
+    ]);
78
+  });
79
+
80
+  it("should parse aliases as from targets", () => {
81
+    const tree = parse("select a from t as u");
82
+    expect(tree).to.deep.equal([
83
+      new AST.SelectStatement({
84
+        arguments: [new AST.Identifier("a")],
85
+        from: new AST.Alias(new AST.Identifier("t"), new AST.Identifier("u")),
76 86
       }),
77 87
     ]);
78 88
   });

Loading…
Cancel
Save