瀏覽代碼

Allow dot expressions

master
Dylan Baker 4 年之前
父節點
當前提交
8c863d1e94
共有 7 個檔案被更改,包括 143 行新增36 行删除
  1. 3
    3
      src/ast.ts
  2. 2
    2
      src/ast/alias.ts
  3. 1
    0
      src/ast/binary.ts
  4. 5
    5
      src/ast/selectStatement.ts
  5. 98
    26
      src/parser.ts
  6. 4
    0
      src/token.ts
  7. 30
    0
      test/parser.test.ts

+ 3
- 3
src/ast.ts 查看文件

12
 export * from "./ast/number";
12
 export * from "./ast/number";
13
 export * from "./ast/selectStatement";
13
 export * from "./ast/selectStatement";
14
 
14
 
15
-export type Primary = _Number | Identifier | Backtick;
16
-export type Secondary = Primary | Alias;
15
+export type Primary = _Number | Identifier | Backtick | Alias;
17
 export type Statement = SelectStatement;
16
 export type Statement = SelectStatement;
18
-export type Expr = Secondary | Binary;
17
+export type Expr = Primary | Binary;
18
+export type FromTarget = Identifier | Backtick | Alias;

+ 2
- 2
src/ast/alias.ts 查看文件

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

+ 1
- 0
src/ast/binary.ts 查看文件

4
   ADDITION = "ADDITION",
4
   ADDITION = "ADDITION",
5
   AND = "AND",
5
   AND = "AND",
6
   DIVISION = "DIVISION",
6
   DIVISION = "DIVISION",
7
+  DOT = "DOT",
7
   EQUALITY = "EQUALITY",
8
   EQUALITY = "EQUALITY",
8
   MULTIPLICATION = "MULTIPLICATION",
9
   MULTIPLICATION = "MULTIPLICATION",
9
   OR = "OR",
10
   OR = "OR",

+ 5
- 5
src/ast/selectStatement.ts 查看文件

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.Secondary[];
5
-  from?: AST.Secondary | null;
6
-  where?: AST.Expr | null
4
+  arguments: AST.Expr[];
5
+  from?: AST.FromTarget | null;
6
+  where?: AST.Expr | null;
7
 }
7
 }
8
 
8
 
9
 export class SelectStatement {
9
 export class SelectStatement {
10
-  public arguments: AST.Secondary[];
11
-  public from: AST.Secondary | null;
10
+  public arguments: AST.Expr[];
11
+  public from: AST.FromTarget | null;
12
   public where: AST.Expr | null;
12
   public where: AST.Expr | null;
13
 
13
 
14
   constructor(opts: ISelectStatementOptions) {
14
   constructor(opts: ISelectStatementOptions) {

+ 98
- 26
src/parser.ts 查看文件

34
         return this.select();
34
         return this.select();
35
     }
35
     }
36
 
36
 
37
-    return new Error(`Unexpected token ${token.kind}`, token.line);
37
+    return new Error(`Unexpected token: ${token.repr()}`, token.line);
38
   }
38
   }
39
 
39
 
40
   private select(): AST.Statement | Error {
40
   private select(): AST.Statement | Error {
61
     });
61
     });
62
   }
62
   }
63
 
63
 
64
-  private args(): AST.Secondary[] | Error {
64
+  private args(): AST.Expr[] | Error {
65
     const args = [];
65
     const args = [];
66
 
66
 
67
     while (true) {
67
     while (true) {
68
-      const arg = this.alias();
68
+      const arg = this.expr();
69
       if (isError(arg)) {
69
       if (isError(arg)) {
70
         return arg;
70
         return arg;
71
       }
71
       }
81
     return args;
81
     return args;
82
   }
82
   }
83
 
83
 
84
-  private from(): AST.Secondary | Error {
84
+  private from(): AST.FromTarget | Error {
85
     this.eat(TokenKind.FROM);
85
     this.eat(TokenKind.FROM);
86
 
86
 
87
-    if (this.match(TokenKind.BACKTICK)) {
88
-      return this.alias(this.backtick);
87
+    return this.fromTarget();
88
+  }
89
+
90
+  private fromTarget(): AST.FromTarget | Error {
91
+    const token = this.currentToken();
92
+    switch (token.kind) {
93
+      case TokenKind.BACKTICK:
94
+        const backtick = this.backtick();
95
+        if (isError(backtick)) {
96
+          return backtick;
97
+        }
98
+
99
+        if (this.match(TokenKind.AS)) {
100
+          return this.fromTargetAlias(backtick);
101
+        }
102
+
103
+        return backtick;
104
+      case TokenKind.IDENTIFIER:
105
+        const identifier = this.identifier();
106
+        if (isError(identifier)) {
107
+          return identifier;
108
+        }
109
+
110
+        if (this.match(TokenKind.AS)) {
111
+          return this.fromTargetAlias(identifier);
112
+        }
113
+
114
+        return identifier;
89
     }
115
     }
90
 
116
 
91
-    return this.alias(this.identifier);
117
+    return new Error(`Unexpected token: ${token.repr()}`, token.line);
118
+  }
119
+
120
+  private fromTargetAlias(obj: AST.Identifier | AST.Backtick): AST.Alias | Error {
121
+    this.eat(TokenKind.AS);
122
+
123
+    const token = this.currentToken();
124
+    const alias = this.match(TokenKind.BACKTICK)
125
+      ? this.backtick()
126
+      : this.match(TokenKind.IDENTIFIER)
127
+        ? this.identifier()
128
+        : new Error(`Unexpected token: ${token.repr()}`, token.line);
129
+
130
+    if (isError(alias)) {
131
+      return alias;
132
+    }
133
+
134
+    return new AST.Alias(obj, alias);
92
   }
135
   }
93
 
136
 
94
   private where(): AST.Expr | Error {
137
   private where(): AST.Expr | Error {
214
 
257
 
215
     if (this.match(TokenKind.STAR)) {
258
     if (this.match(TokenKind.STAR)) {
216
       this.eat(TokenKind.STAR);
259
       this.eat(TokenKind.STAR);
217
-      const right = this.primary();
260
+      const right = this.alias();
218
       if (isError(right)) {
261
       if (isError(right)) {
219
         return right;
262
         return right;
220
       }
263
       }
228
 
271
 
229
     if (this.match(TokenKind.SLASH)) {
272
     if (this.match(TokenKind.SLASH)) {
230
       this.eat(TokenKind.SLASH);
273
       this.eat(TokenKind.SLASH);
231
-      const right = this.primary();
274
+      const right = this.alias();
232
       if (isError(right)) {
275
       if (isError(right)) {
233
         return right;
276
         return right;
234
       }
277
       }
243
     return left;
286
     return left;
244
   }
287
   }
245
 
288
 
246
-  private alias(fn: () => AST.Primary | Error = this.primary): AST.Secondary | Error {
247
-    const primary = fn.bind(this)();
289
+  private alias(): AST.Expr | Error {
290
+    const primary = this.primary();
248
     if (isError(primary)) {
291
     if (isError(primary)) {
249
       return primary;
292
       return primary;
250
     }
293
     }
264
     return primary;
307
     return primary;
265
   }
308
   }
266
 
309
 
267
-  private primary(): AST.Primary | Error {
310
+  private primary(): AST.Expr | Error {
268
     const token = this.currentToken();
311
     const token = this.currentToken();
269
-    switch (token.kind) {
270
-      case TokenKind.NUMBER:
271
-        return this.number();
272
-      case TokenKind.IDENTIFIER:
273
-        return this.identifier();
274
-      case TokenKind.BACKTICK:
275
-        return this.backtick();
312
+
313
+    const primary = (() => {
314
+      switch (token.kind) {
315
+        case TokenKind.NUMBER:
316
+          return this.number();
317
+        case TokenKind.IDENTIFIER:
318
+          return this.identifier();
319
+        case TokenKind.BACKTICK:
320
+          return this.backtick();
321
+        default:
322
+          return new Error(`Unexpected token: ${token.repr()}`, token.line);
323
+      }
324
+    })();
325
+
326
+    if (isError(primary)) {
327
+      return primary;
276
     }
328
     }
277
 
329
 
278
-    return new Error(
279
-      `Unexpected token ${token.kind} ${token.value}`,
280
-      token.line,
281
-    );
330
+    if (this.match(TokenKind.AS)) {
331
+      this.eat(TokenKind.AS);
332
+
333
+      const name = this.match(TokenKind.BACKTICK) ? this.backtick() : this.identifier();
334
+
335
+      if (isError(name)) {
336
+        return name;
337
+      }
338
+
339
+      return new AST.Alias(primary, name);
340
+    } else if (this.match(TokenKind.DOT)) {
341
+      this.eat(TokenKind.DOT);
342
+
343
+      const right = this.match(TokenKind.BACKTICK) ? this.backtick() : this.identifier();
344
+      if (isError(right)) {
345
+        return right;
346
+      }
347
+
348
+      return new AST.Binary({
349
+        left: primary,
350
+        right,
351
+        type: AST.BinaryExpressionTypes.DOT,
352
+      });
353
+    }
354
+
355
+    return primary;
282
   }
356
   }
283
 
357
 
284
   private backtick(): AST.Backtick | Error {
358
   private backtick(): AST.Backtick | Error {
319
       return token;
393
       return token;
320
     }
394
     }
321
 
395
 
322
-    const repr = `{ kind: ${token.kind}${token.value ? `, value: ${token.value} }` : " }"}`;
323
-
324
-    return new Error(`Unexpected token: ${repr}`, token.line);
396
+    return new Error(`Unexpected token: ${token.repr()}`, token.line);
325
   }
397
   }
326
 
398
 
327
   private match(kind: TokenKind): boolean {
399
   private match(kind: TokenKind): boolean {

+ 4
- 0
src/token.ts 查看文件

29
     this.value = value;
29
     this.value = value;
30
     this.line = line;
30
     this.line = line;
31
   }
31
   }
32
+
33
+  public repr(): string {
34
+    return `{ kind: ${this.kind}${this.value ? `, value: ${this.value} }` : " }"}`;
35
+  }
32
 }
36
 }

+ 30
- 0
test/parser.test.ts 查看文件

317
       }),
317
       }),
318
     ]);
318
     ]);
319
   });
319
   });
320
+
321
+  it("should parse expressions as select arguments", () => {
322
+    const tree = parse("select a = b");
323
+    expect(tree).to.deep.equal([
324
+      new AST.SelectStatement({
325
+        arguments: [
326
+          new AST.Binary({
327
+            left: new AST.Identifier("a"),
328
+            right: new AST.Identifier("b"),
329
+            type: AST.BinaryExpressionTypes.EQUALITY,
330
+          }),
331
+        ],
332
+      }),
333
+    ]);
334
+  });
335
+
336
+  it("should parse dot operators", () => {
337
+    const tree = parse("select a.b");
338
+    expect(tree).to.deep.equal([
339
+      new AST.SelectStatement({
340
+        arguments: [
341
+          new AST.Binary({
342
+            left: new AST.Identifier("a"),
343
+            right: new AST.Identifier("b"),
344
+            type: AST.BinaryExpressionTypes.DOT,
345
+          }),
346
+        ],
347
+      }),
348
+    ]);
349
+  });
320
 });
350
 });

Loading…
取消
儲存