Bläddra i källkod

Parse *

master
Dylan Baker 5 år sedan
förälder
incheckning
67fada86c5
6 ändrade filer med 107 tillägg och 9 borttagningar
  1. 3
    1
      src/ast.ts
  2. 2
    2
      src/ast/binary.ts
  3. 2
    2
      src/ast/selectStatement.ts
  4. 1
    0
      src/ast/star.ts
  5. 50
    4
      src/parser.ts
  6. 49
    0
      test/parser.test.ts

+ 3
- 1
src/ast.ts Visa fil

@@ -4,6 +4,7 @@ import { Binary } from "./ast/binary";
4 4
 import { Identifier } from "./ast/identifier";
5 5
 import { Number as _Number } from "./ast/number";
6 6
 import { SelectStatement } from "./ast/selectStatement";
7
+import { Star } from "./ast/star";
7 8
 
8 9
 export * from "./ast/alias";
9 10
 export * from "./ast/backtick";
@@ -11,9 +12,10 @@ export * from "./ast/binary";
11 12
 export * from "./ast/identifier";
12 13
 export * from "./ast/number";
13 14
 export * from "./ast/selectStatement";
15
+export * from "./ast/star";
14 16
 
15 17
 export type Primary = _Number | Identifier | Backtick | Alias;
16 18
 export type Statement = SelectStatement;
17 19
 export type Expr = Primary | Binary;
18
-export type SelectArgument = Expr;
20
+export type SelectArgument = Expr | Star;
19 21
 export type FromTarget = Identifier | Backtick | Alias | SelectStatement;

+ 2
- 2
src/ast/binary.ts Visa fil

@@ -14,13 +14,13 @@ export enum BinaryExpressionTypes {
14 14
 interface IBinaryOptions {
15 15
   type: BinaryExpressionTypes;
16 16
   left: AST.Expr;
17
-  right: AST.Expr;
17
+  right: AST.Expr | AST.Star;
18 18
 }
19 19
 
20 20
 export class Binary {
21 21
   public type: BinaryExpressionTypes;
22 22
   public left: AST.Expr;
23
-  public right: AST.Expr;
23
+  public right: AST.Expr | AST.Star;
24 24
 
25 25
   constructor(opts: IBinaryOptions) {
26 26
     this.type = opts.type;

+ 2
- 2
src/ast/selectStatement.ts Visa fil

@@ -1,13 +1,13 @@
1 1
 import * as AST from "../ast";
2 2
 
3 3
 export interface ISelectStatementOptions {
4
-  arguments: AST.Expr[];
4
+  arguments: AST.SelectArgument[];
5 5
   from?: AST.FromTarget | null;
6 6
   where?: AST.Expr | null;
7 7
 }
8 8
 
9 9
 export class SelectStatement {
10
-  public arguments: AST.Expr[];
10
+  public arguments: AST.SelectArgument[];
11 11
   public from: AST.FromTarget | null;
12 12
   public where: AST.Expr | null;
13 13
 

+ 1
- 0
src/ast/star.ts Visa fil

@@ -0,0 +1 @@
1
+export class Star {}

+ 50
- 4
src/parser.ts Visa fil

@@ -65,11 +65,36 @@ export default class Parser {
65 65
     const args = [];
66 66
 
67 67
     while (true) {
68
-      const arg = this.expr();
69
-      if (isError(arg)) {
70
-        return arg;
68
+      if (this.match(TokenKind.STAR)) {
69
+        this.eat(TokenKind.STAR);
70
+        args.push(new AST.Star());
71
+      } else if (this.match(TokenKind.IDENTIFIER) && this.peek_match(TokenKind.DOT)) {
72
+        const left = this.identifier();
73
+        if (isError(left)) {
74
+          return left;
75
+        }
76
+        const right = this.selectArgumentDotExpression(left);
77
+        if (isError(right)) {
78
+          return right;
79
+        }
80
+        args.push(right);
81
+      } else if (this.match(TokenKind.BACKTICK) && this.peek_match(TokenKind.DOT, 3)) {
82
+        const left = this.backtick();
83
+        if (isError(left)) {
84
+          return left;
85
+        }
86
+        const right = this.selectArgumentDotExpression(left);
87
+        if (isError(right)) {
88
+          return right;
89
+        }
90
+        args.push(right);
91
+      } else {
92
+        const arg = this.expr();
93
+        if (isError(arg)) {
94
+          return arg;
95
+        }
96
+        args.push(arg);
71 97
       }
72
-      args.push(arg);
73 98
 
74 99
       if (this.match(TokenKind.COMMA)) {
75 100
         this.eat(TokenKind.COMMA);
@@ -81,6 +106,22 @@ export default class Parser {
81 106
     return args;
82 107
   }
83 108
 
109
+  private selectArgumentDotExpression(left: AST.Identifier | AST.Backtick): AST.Expr | Error {
110
+    this.eat(TokenKind.DOT);
111
+
112
+    if (this.match(TokenKind.STAR)) {
113
+      this.eat(TokenKind.STAR);
114
+      const right = new AST.Star();
115
+      return new AST.Binary({ left, right, type: AST.BinaryExpressionTypes.DOT });
116
+    } else {
117
+      const right = this.expr();
118
+      if (isError(right)) {
119
+        return right;
120
+      }
121
+      return new AST.Binary({ left, right, type: AST.BinaryExpressionTypes.DOT });
122
+    }
123
+  }
124
+
84 125
   private from(): AST.FromTarget | Error {
85 126
     this.eat(TokenKind.FROM);
86 127
     return this.fromTarget();
@@ -404,6 +445,11 @@ export default class Parser {
404 445
     return this.currentToken().kind === kind;
405 446
   }
406 447
 
448
+  private peek_match(kind: TokenKind, step: number = 1): boolean {
449
+    const token = this.tokens[this.position + step];
450
+    return token && token.kind === kind;
451
+  }
452
+
407 453
   private currentToken(): Token {
408 454
     return this.tokens[this.position];
409 455
   }

+ 49
- 0
test/parser.test.ts Visa fil

@@ -397,4 +397,53 @@ describe("Parser", () => {
397 397
       }),
398 398
     ]);
399 399
   });
400
+
401
+  it("should parse SELECT *", () => {
402
+    const tree = parse("select *");
403
+    expect(tree).to.deep.equal([
404
+      new AST.SelectStatement({
405
+        arguments: [
406
+          new AST.Star(),
407
+        ],
408
+      }),
409
+    ]);
410
+  });
411
+
412
+  it("should parse * on the right hand side of a dot in a SELECT", () => {
413
+    const tree = parse("select a.*");
414
+    expect(tree).to.deep.equal([
415
+      new AST.SelectStatement({
416
+        arguments: [
417
+          new AST.Binary({
418
+            left: new AST.Identifier("a"),
419
+            right: new AST.Star(),
420
+            type: AST.BinaryExpressionTypes.DOT,
421
+          }),
422
+        ],
423
+      }),
424
+    ]);
425
+  });
426
+
427
+  it("should parse * on the right of a dot where the left is a backtick", () => {
428
+    const tree = parse("select `a`.*");
429
+    expect(tree).to.deep.equal([
430
+      new AST.SelectStatement({
431
+        arguments: [
432
+          new AST.Binary({
433
+            left: new AST.Backtick(new AST.Identifier("a")),
434
+            right: new AST.Star(),
435
+            type: AST.BinaryExpressionTypes.DOT,
436
+          }),
437
+        ],
438
+      }),
439
+    ]);
440
+  });
441
+
442
+  it("should not allow stars in arithmetic operations", () => {
443
+    expect(() => parse("select 1 + *")).to.throw("Unexpected token: { kind: STAR }");
444
+    expect(() => parse("select 1 + a.*")).to.throw("Unexpected token: { kind: STAR }");
445
+    expect(() => parse("select * + 1")).to.throw("Unexpected token: { kind: PLUS }");
446
+    expect(() => parse("select a.* + b.*")).to.throw("Unexpected token: { kind: PLUS }");
447
+    expect(() => parse("select a from b where c.*")).to.throw("Unexpected token: { kind: STAR }");
448
+  });
400 449
 });

Laddar…
Avbryt
Spara