Quellcode durchsuchen

Parse aliases

master
Dylan Baker vor 4 Jahren
Ursprung
Commit
ece01514fc
8 geänderte Dateien mit 72 neuen und 24 gelöschten Zeilen
  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 Datei anzeigen

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

+ 11
- 0
src/ast/alias.ts Datei anzeigen

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 Datei anzeigen

1
 import * as AST from "../ast";
1
 import * as AST from "../ast";
2
 
2
 
3
 export interface ISelectStatementOptions {
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
 export class SelectStatement {
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
   constructor(opts: ISelectStatementOptions) {
12
   constructor(opts: ISelectStatementOptions) {
13
     this.arguments = opts.arguments;
13
     this.arguments = opts.arguments;

+ 3
- 0
src/lexer.ts Datei anzeigen

40
     } else if (source.match(/^from/i)) {
40
     } else if (source.match(/^from/i)) {
41
       this.advance(4);
41
       this.advance(4);
42
       return new Token(TokenKind.FROM, null, this.line);
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
     } else if (source.match(/^\*/)) {
46
     } else if (source.match(/^\*/)) {
44
       this.advance();
47
       this.advance();
45
       return new Token(TokenKind.STAR, null, this.line);
48
       return new Token(TokenKind.STAR, null, this.line);

+ 22
- 4
src/parser.ts Datei anzeigen

56
     });
56
     });
57
   }
57
   }
58
 
58
 
59
-  private args(): AST.Primary[] | Error {
59
+  private args(): AST.Secondary[] | Error {
60
     const args = [];
60
     const args = [];
61
 
61
 
62
     while (true) {
62
     while (true) {
63
-      const arg = this.primary();
63
+      const arg = this.alias();
64
       if (isError(arg)) {
64
       if (isError(arg)) {
65
         return arg;
65
         return arg;
66
       }
66
       }
76
     return args;
76
     return args;
77
   }
77
   }
78
 
78
 
79
-  private from(): AST.Primary | Error {
79
+  private from(): AST.Secondary | Error {
80
     this.eat(TokenKind.FROM);
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
   private primary(): AST.Primary | Error {
102
   private primary(): AST.Primary | Error {

+ 1
- 0
src/token.ts Datei anzeigen

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

+ 4
- 2
test/lexer.test.ts Datei anzeigen

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

+ 24
- 14
test/parser.test.ts Datei anzeigen

33
     const tree = parse("select 5, 6");
33
     const tree = parse("select 5, 6");
34
     expect(tree).to.deep.equal([
34
     expect(tree).to.deep.equal([
35
       new AST.SelectStatement({
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
     const tree = parse("select a, b");
42
     const tree = parse("select a, b");
46
     expect(tree).to.deep.equal([
43
     expect(tree).to.deep.equal([
47
       new AST.SelectStatement({
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
     const tree = parse("select a, 5");
51
     const tree = parse("select a, 5");
58
     expect(tree).to.deep.equal([
52
     expect(tree).to.deep.equal([
59
       new AST.SelectStatement({
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
   it("should parse the from of a selection", () => {
59
   it("should parse the from of a selection", () => {
69
     const tree = parse("select a from t");
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
     expect(tree).to.deep.equal([
71
     expect(tree).to.deep.equal([
71
       new AST.SelectStatement({
72
       new AST.SelectStatement({
72
         arguments: [
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
   });

Laden…
Abbrechen
Speichern