Browse Source

Parse backticks

master
Dylan Baker 4 years ago
parent
commit
e99bcf63a8
5 changed files with 99 additions and 6 deletions
  1. 3
    1
      src/ast.ts
  2. 3
    3
      src/ast/alias.ts
  3. 9
    0
      src/ast/backtick.ts
  4. 27
    2
      src/parser.ts
  5. 57
    0
      test/parser.test.ts

+ 3
- 1
src/ast.ts View File

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

+ 3
- 3
src/ast/alias.ts View File

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

+ 9
- 0
src/ast/backtick.ts View File

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

+ 27
- 2
src/parser.ts View File

@@ -83,6 +83,11 @@ export default class Parser {
83 83
 
84 84
   private from(): AST.Secondary | Error {
85 85
     this.eat(TokenKind.FROM);
86
+
87
+    if (this.match(TokenKind.BACKTICK)) {
88
+      return this.alias(this.backtick);
89
+    }
90
+
86 91
     return this.alias(this.identifier);
87 92
   }
88 93
 
@@ -246,10 +251,13 @@ export default class Parser {
246 251
 
247 252
     if (this.match(TokenKind.AS)) {
248 253
       this.eat(TokenKind.AS);
249
-      const name = this.identifier();
254
+
255
+      const name = this.match(TokenKind.BACKTICK) ? this.backtick() : this.identifier();
256
+
250 257
       if (isError(name)) {
251 258
         return name;
252 259
       }
260
+
253 261
       return new AST.Alias(primary, name);
254 262
     }
255 263
 
@@ -263,6 +271,8 @@ export default class Parser {
263 271
         return this.number();
264 272
       case TokenKind.IDENTIFIER:
265 273
         return this.identifier();
274
+      case TokenKind.BACKTICK:
275
+        return this.backtick();
266 276
     }
267 277
 
268 278
     return new Error(
@@ -271,6 +281,21 @@ export default class Parser {
271 281
     );
272 282
   }
273 283
 
284
+  private backtick(): AST.Backtick | Error {
285
+    this.eat(TokenKind.BACKTICK);
286
+    const identifier = this.identifier();
287
+    if (isError(identifier)) {
288
+      return identifier;
289
+    }
290
+
291
+    const closeTick = this.eat(TokenKind.BACKTICK);
292
+    if (isError(closeTick)) {
293
+      return closeTick;
294
+    }
295
+
296
+    return new AST.Backtick(identifier);
297
+  }
298
+
274 299
   private identifier(): AST.Identifier | Error {
275 300
     const identifier = this.eat(TokenKind.IDENTIFIER);
276 301
     if (isError(identifier)) {
@@ -294,7 +319,7 @@ export default class Parser {
294 319
       return token;
295 320
     }
296 321
 
297
-    const repr = `{ kind: ${token.kind}${token.value ? `, value: ${token.value} }` : ""}`;
322
+    const repr = `{ kind: ${token.kind}${token.value ? `, value: ${token.value} }` : " }"}`;
298 323
 
299 324
     return new Error(`Unexpected token: ${repr}`, token.line);
300 325
   }

+ 57
- 0
test/parser.test.ts View File

@@ -260,4 +260,61 @@ describe("Parser", () => {
260 260
       }),
261 261
     ]);
262 262
   });
263
+
264
+  it("should parse backticked column names", () => {
265
+    const tree = parse("select `a` from b");
266
+    expect(tree).to.deep.equal([
267
+      new AST.SelectStatement({
268
+        arguments: [new AST.Backtick(new AST.Identifier("a"))],
269
+        from: new AST.Identifier("b"),
270
+      }),
271
+    ]);
272
+  });
273
+
274
+  it("should not allow unbalanced backticks", () => {
275
+    const fn = () => parse("select `a from b");
276
+    expect(fn).to.throw("Unexpected token: { kind: FROM }");
277
+  });
278
+
279
+  it("should parse backticked FROM targets", () => {
280
+    const tree = parse("select a from `b`");
281
+    expect(tree).to.deep.equal([
282
+      new AST.SelectStatement({
283
+        arguments: [new AST.Identifier("a")],
284
+        from: new AST.Backtick(new AST.Identifier("b")),
285
+      }),
286
+    ]);
287
+  });
288
+
289
+  it("should parse backticked aliases", () => {
290
+    const tree = parse("select a as `b`");
291
+    expect(tree).to.deep.equal([
292
+      new AST.SelectStatement({
293
+        arguments: [new AST.Alias(new AST.Identifier("a"), new AST.Backtick(new AST.Identifier("b")))],
294
+      }),
295
+    ]);
296
+  });
297
+
298
+  it("should parse backticked aliases", () => {
299
+    const tree = parse("select a as `b`");
300
+    expect(tree).to.deep.equal([
301
+      new AST.SelectStatement({
302
+        arguments: [new AST.Alias(new AST.Identifier("a"), new AST.Backtick(new AST.Identifier("b")))],
303
+      }),
304
+    ]);
305
+  });
306
+
307
+  it("should parse backticked where operands", () => {
308
+    const tree = parse("select a where `b` = `c`");
309
+    expect(tree).to.deep.equal([
310
+      new AST.SelectStatement({
311
+        arguments: [new AST.Identifier("a")],
312
+        where: new AST.Binary({
313
+          left: new AST.Backtick(new AST.Identifier("b")),
314
+          right: new AST.Backtick(new AST.Identifier("c")),
315
+          type: AST.BinaryExpressionTypes.EQUALITY,
316
+        }),
317
+      }),
318
+    ]);
319
+  });
263 320
 });

Loading…
Cancel
Save