From d125a844da7c00e3684e047cd1d06761eaf8ad11 Mon Sep 17 00:00:00 2001 From: Simon Binder Date: Sun, 16 Jun 2019 21:23:33 +0200 Subject: [PATCH] Start implementing SELECT statements for parser --- sqlparser/lib/src/ast/ast.dart | 15 ++++++- sqlparser/lib/src/ast/clauses/limit.dart | 17 ++++++++ .../lib/src/ast/expressions/expressions.dart | 2 +- .../lib/src/ast/expressions/literals.dart | 6 +-- sqlparser/lib/src/ast/expressions/simple.dart | 5 +-- sqlparser/lib/src/ast/statements/select.dart | 16 +++++++ sqlparser/lib/src/reader/parser/parser.dart | 42 +++++++++++++++++-- sqlparser/lib/src/reader/tokenizer/token.dart | 6 +++ sqlparser/lib/src/utils/ast_equality.dart | 1 - sqlparser/pubspec.yaml | 2 +- sqlparser/test/parser/expression_test.dart | 3 +- 11 files changed, 96 insertions(+), 19 deletions(-) create mode 100644 sqlparser/lib/src/ast/clauses/limit.dart create mode 100644 sqlparser/lib/src/ast/statements/select.dart diff --git a/sqlparser/lib/src/ast/ast.dart b/sqlparser/lib/src/ast/ast.dart index f6c5630e..d5c8c539 100644 --- a/sqlparser/lib/src/ast/ast.dart +++ b/sqlparser/lib/src/ast/ast.dart @@ -1,5 +1,12 @@ -import 'package:sqlparser/src/ast/expressions/literals.dart'; -import 'package:sqlparser/src/ast/expressions/simple.dart'; +import 'package:sqlparser/src/reader/tokenizer/token.dart'; + +part 'clauses/limit.dart'; + +part 'expressions/expressions.dart'; +part 'expressions/literals.dart'; +part 'expressions/simple.dart'; + +part 'statements/select.dart'; abstract class AstNode { Iterable get childNodes; @@ -7,6 +14,10 @@ abstract class AstNode { } abstract class AstVisitor { + T visitSelectStatement(SelectStatement e); + + T visitLimit(Limit e); + T visitBinaryExpression(BinaryExpression e); T visitUnaryExpression(UnaryExpression e); T visitIsExpression(IsExpression e); diff --git a/sqlparser/lib/src/ast/clauses/limit.dart b/sqlparser/lib/src/ast/clauses/limit.dart new file mode 100644 index 00000000..9acf9641 --- /dev/null +++ b/sqlparser/lib/src/ast/clauses/limit.dart @@ -0,0 +1,17 @@ +part of '../ast.dart'; + +class Limit extends AstNode { + Expression count; + Token offsetSeparator; // can either be OFFSET or just a comma + Expression offset; + + Limit({this.count, this.offsetSeparator, this.offset}); + + @override + T accept(AstVisitor visitor) { + return visitor.visitLimit(this); + } + + @override + Iterable get childNodes => [count, if (offset != null) offset]; +} diff --git a/sqlparser/lib/src/ast/expressions/expressions.dart b/sqlparser/lib/src/ast/expressions/expressions.dart index ab458506..66a50d07 100644 --- a/sqlparser/lib/src/ast/expressions/expressions.dart +++ b/sqlparser/lib/src/ast/expressions/expressions.dart @@ -1,4 +1,4 @@ -import 'package:sqlparser/src/ast/ast.dart'; +part of '../ast.dart'; abstract class Expression implements AstNode { const Expression(); diff --git a/sqlparser/lib/src/ast/expressions/literals.dart b/sqlparser/lib/src/ast/expressions/literals.dart index a8998e42..ad029421 100644 --- a/sqlparser/lib/src/ast/expressions/literals.dart +++ b/sqlparser/lib/src/ast/expressions/literals.dart @@ -1,8 +1,4 @@ -import 'package:sqlparser/src/ast/ast.dart'; -import 'package:sqlparser/src/reader/tokenizer/token.dart'; - -import 'expressions.dart'; - +part of '../ast.dart'; // https://www.sqlite.org/syntax/literal-value.html abstract class Literal extends Expression { diff --git a/sqlparser/lib/src/ast/expressions/simple.dart b/sqlparser/lib/src/ast/expressions/simple.dart index 8dcc5ef2..0c9ef4a4 100644 --- a/sqlparser/lib/src/ast/expressions/simple.dart +++ b/sqlparser/lib/src/ast/expressions/simple.dart @@ -1,7 +1,4 @@ -import 'package:sqlparser/src/ast/ast.dart'; -import 'package:sqlparser/src/reader/tokenizer/token.dart'; - -import 'expressions.dart'; +part of '../ast.dart'; class UnaryExpression extends Expression { final Token operator; diff --git a/sqlparser/lib/src/ast/statements/select.dart b/sqlparser/lib/src/ast/statements/select.dart new file mode 100644 index 00000000..e3f43b9c --- /dev/null +++ b/sqlparser/lib/src/ast/statements/select.dart @@ -0,0 +1,16 @@ +part of '../ast.dart'; + +class SelectStatement extends AstNode { + final Expression where; + final Limit limit; + + SelectStatement({this.where, this.limit}); + + @override + T accept(AstVisitor visitor) { + return visitor.visitSelectStatement(this); + } + + @override + Iterable get childNodes => null; +} diff --git a/sqlparser/lib/src/reader/parser/parser.dart b/sqlparser/lib/src/reader/parser/parser.dart index 730f45c0..a590620c 100644 --- a/sqlparser/lib/src/reader/parser/parser.dart +++ b/sqlparser/lib/src/reader/parser/parser.dart @@ -1,7 +1,5 @@ import 'package:meta/meta.dart'; -import 'package:sqlparser/src/ast/expressions/expressions.dart'; -import 'package:sqlparser/src/ast/expressions/literals.dart'; -import 'package:sqlparser/src/ast/expressions/simple.dart'; +import 'package:sqlparser/src/ast/ast.dart'; import 'package:sqlparser/src/reader/tokenizer/token.dart'; const _comparisonOperators = [ @@ -72,6 +70,44 @@ class Parser { _error(message); } + /// Parses a [SelectStatement], or returns null if there is no select token + /// after the current position. + SelectStatement select() { + if (!_match(const [TokenType.select])) return null; + + // todo parse result column + + final where = _where(); + final limit = _limit(); + + return SelectStatement(where: where, limit: limit); + } + + /// Parses a where clause if there is one at the current position + Expression _where() { + if (_match(const [TokenType.where])) { + return expression(); + } + return null; + } + + /// Parses a [Limit] clause, or returns null if there is no limit token after + /// the current position. + Limit _limit() { + if (!_match(const [TokenType.limit])) return null; + + final count = expression(); + Token offsetSep; + Expression offset; + + if (_match(const [TokenType.comma, TokenType.offset])) { + offsetSep = _previous; + offset = expression(); + } + + return Limit(count: count, offsetSeparator: offsetSep, offset: offset); + } + /* We parse expressions here. * Operators have the following precedence: * - + ~ NOT (unary) diff --git a/sqlparser/lib/src/reader/tokenizer/token.dart b/sqlparser/lib/src/reader/tokenizer/token.dart index a55760c5..c5023a61 100644 --- a/sqlparser/lib/src/reader/tokenizer/token.dart +++ b/sqlparser/lib/src/reader/tokenizer/token.dart @@ -45,9 +45,13 @@ enum TokenType { identifier, select, + from, where, + limit, + offset, + eof, } @@ -55,6 +59,8 @@ const Map keywords = { 'SELECT': TokenType.select, 'FROM': TokenType.from, 'WHERE': TokenType.where, + 'LIMIT': TokenType.limit, + 'OFFSET': TokenType.offset, 'IS': TokenType.$is, 'IN': TokenType.$in, 'LIKE': TokenType.like, diff --git a/sqlparser/lib/src/utils/ast_equality.dart b/sqlparser/lib/src/utils/ast_equality.dart index 810d467f..21b96160 100644 --- a/sqlparser/lib/src/utils/ast_equality.dart +++ b/sqlparser/lib/src/utils/ast_equality.dart @@ -1,5 +1,4 @@ import 'package:sqlparser/src/ast/ast.dart'; -import 'package:sqlparser/src/ast/expressions/simple.dart'; /// Checks whether [a] and [b] are equal. If they aren't, throws an exception. void enforceEqual(AstNode a, AstNode b) { diff --git a/sqlparser/pubspec.yaml b/sqlparser/pubspec.yaml index 80cc4ec5..c41c5211 100644 --- a/sqlparser/pubspec.yaml +++ b/sqlparser/pubspec.yaml @@ -7,7 +7,7 @@ issue_tracker: https://github.com/simolus3/moor/issues author: Simon Binder environment: - sdk: '>=2.2.0 <3.0.0' + sdk: '>=2.2.2 <3.0.0' dev_dependencies: test: ^1.0.0 diff --git a/sqlparser/test/parser/expression_test.dart b/sqlparser/test/parser/expression_test.dart index 540c67a1..8b0a76be 100644 --- a/sqlparser/test/parser/expression_test.dart +++ b/sqlparser/test/parser/expression_test.dart @@ -1,5 +1,4 @@ -import 'package:sqlparser/src/ast/expressions/literals.dart'; -import 'package:sqlparser/src/ast/expressions/simple.dart'; +import 'package:sqlparser/src/ast/ast.dart'; import 'package:sqlparser/src/reader/parser/parser.dart'; import 'package:sqlparser/src/reader/tokenizer/scanner.dart'; import 'package:sqlparser/src/reader/tokenizer/token.dart';