mirror of https://github.com/AMT-Cheif/drift.git
Parse ISNULL and NOTNULL postfix expressions
This commit is contained in:
parent
6924543a47
commit
de67ca6e7b
|
@ -2,7 +2,7 @@
|
|||
|
||||
- Added a argument type and argument to the visitor classes
|
||||
- Experimental new type inference algorithm
|
||||
- Support `CAST` expressions.
|
||||
- Support `CAST` expressions and the `ISNULL` / `NOTNULL` postfixes
|
||||
- Support parsing `CREATE TRIGGER` statements
|
||||
- Support parsing `CREATE INDEX` statement
|
||||
|
||||
|
|
|
@ -115,6 +115,7 @@ class TypeResolver {
|
|||
} else if (expr is SqlInvocation) {
|
||||
return resolveFunctionCall(expr);
|
||||
} else if (expr is IsExpression ||
|
||||
expr is IsNullExpression ||
|
||||
expr is InExpression ||
|
||||
expr is StringComparisonExpression ||
|
||||
expr is BetweenExpression ||
|
||||
|
|
|
@ -105,6 +105,28 @@ class IsExpression extends Expression {
|
|||
}
|
||||
}
|
||||
|
||||
class IsNullExpression extends Expression {
|
||||
final Expression operand;
|
||||
|
||||
/// When true, this is a `NOT NULL` expression.
|
||||
final bool negated;
|
||||
|
||||
IsNullExpression(this.operand, [this.negated = false]);
|
||||
|
||||
@override
|
||||
R accept<A, R>(AstVisitor<A, R> visitor, A arg) {
|
||||
return visitor.visitIsNullExpression(this, arg);
|
||||
}
|
||||
|
||||
@override
|
||||
Iterable<AstNode> get childNodes => [operand];
|
||||
|
||||
@override
|
||||
bool contentEquals(IsNullExpression other) {
|
||||
return other.negated == negated;
|
||||
}
|
||||
}
|
||||
|
||||
/// `$check BETWEEN $lower AND $upper`
|
||||
class BetweenExpression extends Expression {
|
||||
final bool not;
|
||||
|
|
|
@ -34,6 +34,7 @@ abstract class AstVisitor<A, R> {
|
|||
R visitStringComparison(StringComparisonExpression e, A arg);
|
||||
R visitUnaryExpression(UnaryExpression e, A arg);
|
||||
R visitIsExpression(IsExpression e, A arg);
|
||||
R visitIsNullExpression(IsNullExpression e, A arg);
|
||||
R visitBetweenExpression(BetweenExpression e, A arg);
|
||||
R visitLiteral(Literal e, A arg);
|
||||
R visitReference(Reference e, A arg);
|
||||
|
@ -281,6 +282,11 @@ class RecursiveVisitor<A, R> implements AstVisitor<A, R> {
|
|||
return visitExpression(e, arg);
|
||||
}
|
||||
|
||||
@override
|
||||
R visitIsNullExpression(IsNullExpression e, A arg) {
|
||||
return visitExpression(e, arg);
|
||||
}
|
||||
|
||||
@override
|
||||
R visitBetweenExpression(BetweenExpression e, A arg) {
|
||||
return visitExpression(e, arg);
|
||||
|
|
|
@ -205,21 +205,41 @@ mixin ExpressionParser on ParserBase {
|
|||
}
|
||||
|
||||
Expression _postfix() {
|
||||
// todo parse ISNULL, NOTNULL, NOT NULL, etc.
|
||||
// I don't even know the precedence ¯\_(ツ)_/¯ (probably not higher than
|
||||
// unary)
|
||||
var expression = _primary();
|
||||
|
||||
while (_matchOne(TokenType.collate)) {
|
||||
final collateOp = _previous;
|
||||
final collateFun =
|
||||
_consume(TokenType.identifier, 'Expected a collating sequence')
|
||||
as IdentifierToken;
|
||||
expression = CollateExpression(
|
||||
inner: expression,
|
||||
operator: collateOp,
|
||||
collateFunction: collateFun,
|
||||
)..setSpan(expression.first, collateFun);
|
||||
// todo we don't currently parse "NOT NULL" (2 tokens) because of ambiguity
|
||||
// with NOT BETWEEN / NOT IN / ... expressions
|
||||
const matchedTokens = [
|
||||
TokenType.collate,
|
||||
TokenType.notNull,
|
||||
TokenType.isNull
|
||||
];
|
||||
|
||||
while (_match(matchedTokens)) {
|
||||
final operator = _previous;
|
||||
switch (operator.type) {
|
||||
case TokenType.collate:
|
||||
final collateOp = _previous;
|
||||
final collateFun =
|
||||
_consume(TokenType.identifier, 'Expected a collating sequence')
|
||||
as IdentifierToken;
|
||||
expression = CollateExpression(
|
||||
inner: expression,
|
||||
operator: collateOp,
|
||||
collateFunction: collateFun,
|
||||
);
|
||||
break;
|
||||
case TokenType.isNull:
|
||||
expression = IsNullExpression(expression);
|
||||
break;
|
||||
case TokenType.notNull:
|
||||
expression = IsNullExpression(expression, true);
|
||||
break;
|
||||
default:
|
||||
// we checked with _match, this may never happen
|
||||
throw AssertionError();
|
||||
}
|
||||
expression.setSpan(operator, _previous);
|
||||
}
|
||||
|
||||
return expression;
|
||||
|
|
|
@ -50,6 +50,8 @@ enum TokenType {
|
|||
$true,
|
||||
$false,
|
||||
$null,
|
||||
isNull,
|
||||
notNull,
|
||||
currentTime,
|
||||
currentDate,
|
||||
currentTimestamp,
|
||||
|
@ -207,6 +209,8 @@ const Map<String, TokenType> keywords = {
|
|||
'TRUE': TokenType.$true,
|
||||
'FALSE': TokenType.$false,
|
||||
'NULL': TokenType.$null,
|
||||
'ISNULL': TokenType.isNull,
|
||||
'NOTNULL': TokenType.notNull,
|
||||
'CURRENT_TIME': TokenType.currentTime,
|
||||
'CURRENT_DATE': TokenType.currentDate,
|
||||
'CURRENT_TIMESTAMP': TokenType.currentTimestamp,
|
||||
|
|
|
@ -144,6 +144,8 @@ final Map<String, Expression> _testCases = {
|
|||
),
|
||||
'TEXT',
|
||||
),
|
||||
'foo ISNULL': IsNullExpression(Reference(columnName: 'foo')),
|
||||
'foo NOTNULL': IsNullExpression(Reference(columnName: 'foo'), true),
|
||||
};
|
||||
|
||||
void main() {
|
||||
|
|
Loading…
Reference in New Issue