mirror of https://github.com/AMT-Cheif/drift.git
Parse sql variables
This commit is contained in:
parent
b52dcf9a60
commit
1c75c9d3e8
|
@ -12,6 +12,7 @@ part 'expressions/expressions.dart';
|
|||
part 'expressions/literals.dart';
|
||||
part 'expressions/reference.dart';
|
||||
part 'expressions/simple.dart';
|
||||
part 'expressions/variables.dart';
|
||||
|
||||
part 'statements/select.dart';
|
||||
|
||||
|
@ -40,4 +41,7 @@ abstract class AstVisitor<T> {
|
|||
T visitIsExpression(IsExpression e);
|
||||
T visitLiteral(Literal e);
|
||||
T visitReference(Reference e);
|
||||
|
||||
T visitNumberedVariable(NumberedVariable e);
|
||||
T visitNamedVariable(ColonNamedVariable e);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
part of '../ast.dart';
|
||||
|
||||
mixin Variable on Expression {}
|
||||
|
||||
/// A "?" or "?123" variable placeholder
|
||||
class NumberedVariable extends Expression with Variable {
|
||||
final Token questionMark;
|
||||
final int explicitIndex;
|
||||
|
||||
NumberedVariable(this.questionMark, this.explicitIndex);
|
||||
|
||||
@override
|
||||
T accept<T>(AstVisitor<T> visitor) {
|
||||
return visitor.visitNumberedVariable(this);
|
||||
}
|
||||
|
||||
@override
|
||||
Iterable<AstNode> get childNodes => const [];
|
||||
|
||||
@override
|
||||
bool contentEquals(NumberedVariable other) {
|
||||
return other.explicitIndex == explicitIndex;
|
||||
}
|
||||
}
|
||||
|
||||
class ColonNamedVariable extends Expression with Variable {
|
||||
final String name;
|
||||
|
||||
ColonNamedVariable(this.name);
|
||||
|
||||
@override
|
||||
T accept<T>(AstVisitor<T> visitor) {
|
||||
return visitor.visitNamedVariable(this);
|
||||
}
|
||||
|
||||
@override
|
||||
Iterable<AstNode> get childNodes => [];
|
||||
|
||||
@override
|
||||
bool contentEquals(ColonNamedVariable other) {
|
||||
return other.name == name;
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
part of '../ast.dart';
|
||||
|
||||
class SelectStatement extends AstNode {
|
||||
final bool distinct;
|
||||
final Expression where;
|
||||
final List<ResultColumn> columns;
|
||||
final List<Queryable> from;
|
||||
|
@ -8,7 +9,12 @@ class SelectStatement extends AstNode {
|
|||
final Limit limit;
|
||||
|
||||
SelectStatement(
|
||||
{this.where, this.columns, this.from, this.orderBy, this.limit});
|
||||
{this.distinct,
|
||||
this.where,
|
||||
this.columns,
|
||||
this.from,
|
||||
this.orderBy,
|
||||
this.limit});
|
||||
|
||||
@override
|
||||
T accept<T>(AstVisitor<T> visitor) {
|
||||
|
|
|
@ -100,6 +100,13 @@ class Parser {
|
|||
SelectStatement select() {
|
||||
if (!_match(const [TokenType.select])) return null;
|
||||
|
||||
var distinct = false;
|
||||
if (_matchOne(TokenType.distinct)) {
|
||||
distinct = true;
|
||||
} else if (_matchOne(TokenType.all)) {
|
||||
distinct = false;
|
||||
}
|
||||
|
||||
final resultColumns = <ResultColumn>[];
|
||||
do {
|
||||
resultColumns.add(_resultColumn());
|
||||
|
@ -112,6 +119,7 @@ class Parser {
|
|||
final limit = _limit();
|
||||
|
||||
return SelectStatement(
|
||||
distinct: distinct,
|
||||
columns: resultColumns,
|
||||
from: from,
|
||||
where: where,
|
||||
|
@ -446,6 +454,11 @@ class Parser {
|
|||
return UnaryExpression(operator, expression);
|
||||
}
|
||||
|
||||
return _postfix();
|
||||
}
|
||||
|
||||
Expression _postfix() {
|
||||
// todo parse ISNULL, NOTNULL, NOT NULL, etc.
|
||||
return _primary();
|
||||
}
|
||||
|
||||
|
@ -481,6 +494,21 @@ class Parser {
|
|||
return Reference(columnName: first.identifier);
|
||||
}
|
||||
break;
|
||||
case TokenType.questionMark:
|
||||
final mark = _previous;
|
||||
|
||||
if (_matchOne(TokenType.numberLiteral)) {
|
||||
return NumberedVariable(mark, _parseNumber(_previous.lexeme).toInt());
|
||||
} else {
|
||||
return NumberedVariable(mark, null);
|
||||
}
|
||||
break;
|
||||
case TokenType.colon:
|
||||
final identifier = _consume(TokenType.identifier,
|
||||
'Expected an identifier for the named variable') as IdentifierToken;
|
||||
final content = identifier.identifier;
|
||||
|
||||
return ColonNamedVariable(':$content');
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -106,6 +106,13 @@ class Scanner {
|
|||
_addToken(TokenType.tilde);
|
||||
break;
|
||||
|
||||
case '?':
|
||||
_addToken(TokenType.questionMark);
|
||||
break;
|
||||
case ':':
|
||||
_addToken(TokenType.colon);
|
||||
break;
|
||||
|
||||
case 'x':
|
||||
if (_match("'")) {
|
||||
_string(binary: false);
|
||||
|
|
|
@ -34,6 +34,12 @@ enum TokenType {
|
|||
or,
|
||||
tilde,
|
||||
|
||||
questionMark,
|
||||
colon,
|
||||
// todo at and dollarSign are currently not used
|
||||
at,
|
||||
dollarSign,
|
||||
|
||||
stringLiteral,
|
||||
numberLiteral,
|
||||
$true,
|
||||
|
@ -45,6 +51,8 @@ enum TokenType {
|
|||
identifier,
|
||||
|
||||
select,
|
||||
distinct,
|
||||
all,
|
||||
from,
|
||||
as,
|
||||
where,
|
||||
|
@ -71,6 +79,8 @@ enum TokenType {
|
|||
|
||||
const Map<String, TokenType> keywords = {
|
||||
'SELECT': TokenType.select,
|
||||
'DISTINCT': TokenType.distinct,
|
||||
'ALL': TokenType.all,
|
||||
'FROM': TokenType.from,
|
||||
'NATURAL': TokenType.natural,
|
||||
'LEFT': TokenType.leftParen,
|
||||
|
|
|
@ -31,4 +31,29 @@ void main() {
|
|||
),
|
||||
);
|
||||
});
|
||||
|
||||
test('variables', () {
|
||||
final scanner = Scanner('? * ?3 + ?2 == :test');
|
||||
final tokens = scanner.scanTokens();
|
||||
final parser = Parser(tokens);
|
||||
|
||||
final expression = parser.expression();
|
||||
|
||||
enforceEqual(
|
||||
expression,
|
||||
BinaryExpression(
|
||||
BinaryExpression(
|
||||
BinaryExpression(
|
||||
NumberedVariable(token(TokenType.questionMark), null),
|
||||
token(TokenType.star),
|
||||
NumberedVariable(token(TokenType.questionMark), 3),
|
||||
),
|
||||
token(TokenType.plus),
|
||||
NumberedVariable(token(TokenType.questionMark), 2),
|
||||
),
|
||||
token(TokenType.doubleEqual),
|
||||
ColonNamedVariable(':test'),
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue