浏览代码

Parse binary expressions

master
Dylan Baker 6 年前
父节点
当前提交
9ba1a12be5
共有 7 个文件被更改,包括 248 次插入9 次删除
  1. 3
    1
      src/ast.ts
  2. 27
    0
      src/ast/binary.ts
  3. 9
    0
      src/lexer.ts
  4. 107
    6
      src/parser.ts
  5. 3
    0
      src/token.ts
  6. 5
    2
      test/lexer.test.ts
  7. 94
    0
      test/parser.test.ts

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

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

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

1
+import * as AST from "../ast";
2
+
3
+export enum BinaryExpressionTypes {
4
+  ADDITION,
5
+  SUBTRACTION,
6
+  MULTIPLICATION,
7
+  DIVISION,
8
+  EQUALITY,
9
+}
10
+
11
+interface IBinaryOptions {
12
+  type: BinaryExpressionTypes;
13
+  left: AST.Expr;
14
+  right: AST.Expr;
15
+}
16
+
17
+export class Binary {
18
+  public type: BinaryExpressionTypes;
19
+  public left: AST.Expr;
20
+  public right: AST.Expr;
21
+
22
+  constructor(opts: IBinaryOptions) {
23
+    this.type = opts.type;
24
+    this.left = opts.left;
25
+    this.right = opts.right;
26
+  }
27
+}

+ 9
- 0
src/lexer.ts 查看文件

43
     } else if (source.match(/^as/i)) {
43
     } else if (source.match(/^as/i)) {
44
       this.advance(2);
44
       this.advance(2);
45
       return new Token(TokenKind.AS, null, this.line);
45
       return new Token(TokenKind.AS, null, this.line);
46
+    } else if (source.match(/^\+/)) {
47
+      this.advance();
48
+      return new Token(TokenKind.PLUS, null, this.line);
49
+    } else if (source.match(/^-/)) {
50
+      this.advance();
51
+      return new Token(TokenKind.MINUS, null, this.line);
46
     } else if (source.match(/^\*/)) {
52
     } else if (source.match(/^\*/)) {
47
       this.advance();
53
       this.advance();
48
       return new Token(TokenKind.STAR, null, this.line);
54
       return new Token(TokenKind.STAR, null, this.line);
55
+    } else if (source.match(/^\//)) {
56
+      this.advance();
57
+      return new Token(TokenKind.SLASH, null, this.line);
49
     } else if (source.match(/^=/)) {
58
     } else if (source.match(/^=/)) {
50
       this.advance();
59
       this.advance();
51
       return new Token(TokenKind.EQUALS, null, this.line);
60
       return new Token(TokenKind.EQUALS, null, this.line);

+ 107
- 6
src/parser.ts 查看文件

44
       return args;
44
       return args;
45
     }
45
     }
46
 
46
 
47
-    const from = this.currentToken().kind === TokenKind.FROM ? this.from() : null;
47
+    const from = this.match(TokenKind.FROM) ? this.from() : null;
48
     if (isError(from)) {
48
     if (isError(from)) {
49
       return from;
49
       return from;
50
     }
50
     }
51
 
51
 
52
-    const where = this.currentToken().kind === TokenKind.WHERE ? this.where() : null;
52
+    const where = this.match(TokenKind.WHERE) ? this.where() : null;
53
     if (isError(where)) {
53
     if (isError(where)) {
54
       return where;
54
       return where;
55
     }
55
     }
71
       }
71
       }
72
       args.push(arg);
72
       args.push(arg);
73
 
73
 
74
-      if (this.currentToken().kind === TokenKind.COMMA) {
74
+      if (this.match(TokenKind.COMMA)) {
75
         this.eat(TokenKind.COMMA);
75
         this.eat(TokenKind.COMMA);
76
       } else {
76
       } else {
77
         break;
77
         break;
92
   }
92
   }
93
 
93
 
94
   private expr(): AST.Expr | Error {
94
   private expr(): AST.Expr | Error {
95
-    return this.alias();
95
+    return this.equality();
96
+  }
97
+
98
+  private equality(): AST.Expr | Error {
99
+    const left = this.addition();
100
+    if (isError(left)) {
101
+      return left;
102
+    }
103
+
104
+    if (this.match(TokenKind.EQUALS)) {
105
+      this.eat(TokenKind.EQUALS);
106
+      const right = this.addition();
107
+      if (isError(right)) {
108
+        return right;
109
+      }
110
+
111
+      return new AST.Binary({
112
+        left,
113
+        right,
114
+        type: AST.BinaryExpressionTypes.EQUALITY,
115
+      });
116
+    }
117
+
118
+    return left;
119
+  }
120
+
121
+  private addition(): AST.Expr | Error {
122
+    const left = this.multiplication();
123
+    if (isError(left)) {
124
+      return left;
125
+    }
126
+
127
+    if (this.match(TokenKind.PLUS)) {
128
+      this.eat(TokenKind.PLUS);
129
+      const right = this.multiplication();
130
+      if (isError(right)) {
131
+        return right;
132
+      }
133
+
134
+      return new AST.Binary({
135
+        left,
136
+        right,
137
+        type: AST.BinaryExpressionTypes.ADDITION,
138
+      });
139
+    }
140
+
141
+    if (this.match(TokenKind.MINUS)) {
142
+      this.eat(TokenKind.MINUS);
143
+      const right = this.multiplication();
144
+      if (isError(right)) {
145
+        return right;
146
+      }
147
+
148
+      return new AST.Binary({
149
+        left,
150
+        right,
151
+        type: AST.BinaryExpressionTypes.SUBTRACTION,
152
+      });
153
+    }
154
+
155
+    return left;
156
+  }
157
+
158
+  private multiplication(): AST.Expr | Error {
159
+    const left = this.primary();
160
+    if (isError(left)) {
161
+      return left;
162
+    }
163
+
164
+    if (this.match(TokenKind.STAR)) {
165
+      this.eat(TokenKind.STAR);
166
+      const right = this.primary();
167
+      if (isError(right)) {
168
+        return right;
169
+      }
170
+
171
+      return new AST.Binary({
172
+        left,
173
+        right,
174
+        type: AST.BinaryExpressionTypes.MULTIPLICATION,
175
+      });
176
+    }
177
+
178
+    if (this.match(TokenKind.SLASH)) {
179
+      this.eat(TokenKind.SLASH);
180
+      const right = this.primary();
181
+      if (isError(right)) {
182
+        return right;
183
+      }
184
+
185
+      return new AST.Binary({
186
+        left,
187
+        right,
188
+        type: AST.BinaryExpressionTypes.DIVISION,
189
+      });
190
+    }
191
+
192
+    return left;
96
   }
193
   }
97
 
194
 
98
   private alias(fn: () => AST.Primary | Error = this.primary): AST.Secondary | Error {
195
   private alias(fn: () => AST.Primary | Error = this.primary): AST.Secondary | Error {
101
       return primary;
198
       return primary;
102
     }
199
     }
103
 
200
 
104
-    if (this.currentToken().kind === TokenKind.AS) {
201
+    if (this.match(TokenKind.AS)) {
105
       this.eat(TokenKind.AS);
202
       this.eat(TokenKind.AS);
106
       const name = this.identifier();
203
       const name = this.identifier();
107
       if (isError(name)) {
204
       if (isError(name)) {
156
     return new Error(`Unexpected token: ${repr}`, token.line);
253
     return new Error(`Unexpected token: ${repr}`, token.line);
157
   }
254
   }
158
 
255
 
256
+  private match(kind: TokenKind): boolean {
257
+    return this.currentToken().kind === kind;
258
+  }
259
+
159
   private currentToken(): Token {
260
   private currentToken(): Token {
160
     return this.tokens[this.position];
261
     return this.tokens[this.position];
161
   }
262
   }
165
   }
266
   }
166
 
267
 
167
   private atEnd(): boolean {
268
   private atEnd(): boolean {
168
-    return this.currentToken().kind === TokenKind.EOF;
269
+    return this.match(TokenKind.EOF);
169
   }
270
   }
170
 }
271
 }

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

7
   EQUALS = "EQUALS",
7
   EQUALS = "EQUALS",
8
   FROM = "FROM",
8
   FROM = "FROM",
9
   IDENTIFIER = "IDENTIFIER",
9
   IDENTIFIER = "IDENTIFIER",
10
+  MINUS = "MINUS",
10
   NUMBER = "NUMBER",
11
   NUMBER = "NUMBER",
12
+  PLUS = "PLUS",
11
   SELECT = "SELECT",
13
   SELECT = "SELECT",
12
   SEMICOLON = "SEMICOLON",
14
   SEMICOLON = "SEMICOLON",
15
+  SLASH = "SLASH",
13
   STAR = "STAR",
16
   STAR = "STAR",
14
   WHERE = "WHERE",
17
   WHERE = "WHERE",
15
 }
18
 }

+ 5
- 2
test/lexer.test.ts 查看文件

33
   });
33
   });
34
 
34
 
35
   it("scans symbols", () => {
35
   it("scans symbols", () => {
36
-    const tokens = scan("*=,`;.");
36
+    const tokens = scan("=,`;.+-*/");
37
     expect(tokens).to.deep.equal([
37
     expect(tokens).to.deep.equal([
38
-      new Token(TokenKind.STAR, null, 1),
39
       new Token(TokenKind.EQUALS, null, 1),
38
       new Token(TokenKind.EQUALS, null, 1),
40
       new Token(TokenKind.COMMA, null, 1),
39
       new Token(TokenKind.COMMA, null, 1),
41
       new Token(TokenKind.BACKTICK, null, 1),
40
       new Token(TokenKind.BACKTICK, null, 1),
42
       new Token(TokenKind.SEMICOLON, null, 1),
41
       new Token(TokenKind.SEMICOLON, null, 1),
43
       new Token(TokenKind.DOT, null, 1),
42
       new Token(TokenKind.DOT, null, 1),
43
+      new Token(TokenKind.PLUS, null, 1),
44
+      new Token(TokenKind.MINUS, null, 1),
45
+      new Token(TokenKind.STAR, null, 1),
46
+      new Token(TokenKind.SLASH, null, 1),
44
       new Token(TokenKind.EOF, null, 1),
47
       new Token(TokenKind.EOF, null, 1),
45
     ]);
48
     ]);
46
   });
49
   });

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

113
       }),
113
       }),
114
     ]);
114
     ]);
115
   });
115
   });
116
+
117
+  it("should parse a where with an equality", () => {
118
+    const tree = parse("select a from b where c = d");
119
+    expect(tree).to.deep.equal([
120
+      new AST.SelectStatement({
121
+        arguments: [new AST.Identifier("a")],
122
+        from: new AST.Identifier("b"),
123
+        where: new AST.Binary({
124
+          left: new AST.Identifier("c"),
125
+          right: new AST.Identifier("d"),
126
+          type: AST.BinaryExpressionTypes.EQUALITY,
127
+        }),
128
+      }),
129
+    ]);
130
+  });
131
+
132
+  it("should parse a where with an addition", () => {
133
+    const tree = parse("select a from b where c + d");
134
+    expect(tree).to.deep.equal([
135
+      new AST.SelectStatement({
136
+        arguments: [new AST.Identifier("a")],
137
+        from: new AST.Identifier("b"),
138
+        where: new AST.Binary({
139
+          left: new AST.Identifier("c"),
140
+          right: new AST.Identifier("d"),
141
+          type: AST.BinaryExpressionTypes.ADDITION,
142
+        }),
143
+      }),
144
+    ]);
145
+  });
146
+
147
+  it("should parse a where with a subtraction", () => {
148
+    const tree = parse("select a from b where c - d");
149
+    expect(tree).to.deep.equal([
150
+      new AST.SelectStatement({
151
+        arguments: [new AST.Identifier("a")],
152
+        from: new AST.Identifier("b"),
153
+        where: new AST.Binary({
154
+          left: new AST.Identifier("c"),
155
+          right: new AST.Identifier("d"),
156
+          type: AST.BinaryExpressionTypes.SUBTRACTION,
157
+        }),
158
+      }),
159
+    ]);
160
+  });
161
+
162
+  it("should parse a where with a multiplication", () => {
163
+    const tree = parse("select a from b where c * d");
164
+    expect(tree).to.deep.equal([
165
+      new AST.SelectStatement({
166
+        arguments: [new AST.Identifier("a")],
167
+        from: new AST.Identifier("b"),
168
+        where: new AST.Binary({
169
+          left: new AST.Identifier("c"),
170
+          right: new AST.Identifier("d"),
171
+          type: AST.BinaryExpressionTypes.MULTIPLICATION,
172
+        }),
173
+      }),
174
+    ]);
175
+  });
176
+
177
+  it("should parse a where with a division", () => {
178
+    const tree = parse("select a from b where c / d");
179
+    expect(tree).to.deep.equal([
180
+      new AST.SelectStatement({
181
+        arguments: [new AST.Identifier("a")],
182
+        from: new AST.Identifier("b"),
183
+        where: new AST.Binary({
184
+          left: new AST.Identifier("c"),
185
+          right: new AST.Identifier("d"),
186
+          type: AST.BinaryExpressionTypes.DIVISION,
187
+        }),
188
+      }),
189
+    ]);
190
+  });
191
+
192
+  it("should parse a where with a complex binary expression", () => {
193
+    const tree = parse("select a from b where c + d = e");
194
+    expect(tree).to.deep.equal([
195
+      new AST.SelectStatement({
196
+        arguments: [new AST.Identifier("a")],
197
+        from: new AST.Identifier("b"),
198
+        where: new AST.Binary({
199
+          left: new AST.Binary({
200
+            left: new AST.Identifier("c"),
201
+            right: new AST.Identifier("d"),
202
+            type: AST.BinaryExpressionTypes.ADDITION,
203
+          }),
204
+          right: new AST.Identifier("e"),
205
+          type: AST.BinaryExpressionTypes.EQUALITY,
206
+        }),
207
+      }),
208
+    ]);
209
+  });
116
 });
210
 });

正在加载...
取消
保存