Dylan Baker 4 роки тому
джерело
коміт
e99bcf63a8
5 змінених файлів з 99 додано та 6 видалено
  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 Переглянути файл

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

+ 3
- 3
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.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
     this.object = object;
8
     this.object = object;
9
     this.name = name;
9
     this.name = name;
10
   }
10
   }

+ 9
- 0
src/ast/backtick.ts Переглянути файл

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 Переглянути файл

83
 
83
 
84
   private from(): AST.Secondary | Error {
84
   private from(): AST.Secondary | Error {
85
     this.eat(TokenKind.FROM);
85
     this.eat(TokenKind.FROM);
86
+
87
+    if (this.match(TokenKind.BACKTICK)) {
88
+      return this.alias(this.backtick);
89
+    }
90
+
86
     return this.alias(this.identifier);
91
     return this.alias(this.identifier);
87
   }
92
   }
88
 
93
 
246
 
251
 
247
     if (this.match(TokenKind.AS)) {
252
     if (this.match(TokenKind.AS)) {
248
       this.eat(TokenKind.AS);
253
       this.eat(TokenKind.AS);
249
-      const name = this.identifier();
254
+
255
+      const name = this.match(TokenKind.BACKTICK) ? this.backtick() : this.identifier();
256
+
250
       if (isError(name)) {
257
       if (isError(name)) {
251
         return name;
258
         return name;
252
       }
259
       }
260
+
253
       return new AST.Alias(primary, name);
261
       return new AST.Alias(primary, name);
254
     }
262
     }
255
 
263
 
263
         return this.number();
271
         return this.number();
264
       case TokenKind.IDENTIFIER:
272
       case TokenKind.IDENTIFIER:
265
         return this.identifier();
273
         return this.identifier();
274
+      case TokenKind.BACKTICK:
275
+        return this.backtick();
266
     }
276
     }
267
 
277
 
268
     return new Error(
278
     return new Error(
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
   private identifier(): AST.Identifier | Error {
299
   private identifier(): AST.Identifier | Error {
275
     const identifier = this.eat(TokenKind.IDENTIFIER);
300
     const identifier = this.eat(TokenKind.IDENTIFIER);
276
     if (isError(identifier)) {
301
     if (isError(identifier)) {
294
       return token;
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
     return new Error(`Unexpected token: ${repr}`, token.line);
324
     return new Error(`Unexpected token: ${repr}`, token.line);
300
   }
325
   }

+ 57
- 0
test/parser.test.ts Переглянути файл

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
 });

Завантаження…
Відмінити
Зберегти