mirror of https://github.com/AMT-Cheif/drift.git
Parse JOIN and INDEXED as identifiers when appropriate
This commit is contained in:
parent
e0fc4a3af6
commit
d9c2b5f342
|
@ -261,64 +261,51 @@ mixin ExpressionParser on ParserBase {
|
|||
final variable = _variableOrNull();
|
||||
if (variable != null) return variable;
|
||||
|
||||
final token = _advance();
|
||||
final type = token.type;
|
||||
switch (type) {
|
||||
case TokenType.leftParen:
|
||||
// An opening bracket in the context of an expression could either be
|
||||
// an inner select statement or a parenthesised expression.
|
||||
final left = token;
|
||||
if (_peek.type == TokenType.select) {
|
||||
final stmt = select();
|
||||
_consume(TokenType.rightParen, 'Expected a closing bracket');
|
||||
return SubQuery(select: stmt)..setSpan(left, _previous);
|
||||
} else {
|
||||
final expr = expression();
|
||||
_consume(TokenType.rightParen, 'Expected a closing bracket');
|
||||
return Parentheses(left, expr, token)..setSpan(left, _previous);
|
||||
}
|
||||
break;
|
||||
case TokenType.identifier:
|
||||
// could be table.column, function(...) or just column
|
||||
final first = token as IdentifierToken;
|
||||
if (_matchOne(TokenType.leftParen)) {
|
||||
final left = _previous;
|
||||
if (_peek.type == TokenType.select) {
|
||||
final stmt = select();
|
||||
_consume(TokenType.rightParen, 'Expected a closing bracket');
|
||||
return SubQuery(select: stmt)..setSpan(left, _previous);
|
||||
} else {
|
||||
final expr = expression();
|
||||
_consume(TokenType.rightParen, 'Expected a closing bracket');
|
||||
return Parentheses(left, expr, _previous)..setSpan(left, _previous);
|
||||
}
|
||||
} else if (_matchOne(TokenType.dollarSignVariable)) {
|
||||
if (enableMoorExtensions) {
|
||||
final typedToken = _previous as DollarSignVariableToken;
|
||||
return DartExpressionPlaceholder(name: typedToken.name)
|
||||
..token = typedToken
|
||||
..setSpan(_previous, _previous);
|
||||
}
|
||||
} else if (_checkLenientIdentifier()) {
|
||||
final first = _consumeIdentifier(
|
||||
'This error message should never be displayed. Please report.');
|
||||
|
||||
if (_matchOne(TokenType.dot)) {
|
||||
final second =
|
||||
_consume(TokenType.identifier, 'Expected a column name here')
|
||||
as IdentifierToken;
|
||||
return Reference(
|
||||
tableName: first.identifier, columnName: second.identifier)
|
||||
..setSpan(first, second);
|
||||
} else if (_matchOne(TokenType.leftParen)) {
|
||||
final parameters = _functionParameters();
|
||||
final rightParen = _consume(TokenType.rightParen,
|
||||
'Expected closing bracket after argument list');
|
||||
// could be table.column, function(...) or just column
|
||||
if (_matchOne(TokenType.dot)) {
|
||||
final second = _consumeIdentifier('Expected a column name here');
|
||||
return Reference(
|
||||
tableName: first.identifier, columnName: second.identifier)
|
||||
..setSpan(first, second);
|
||||
} else if (_matchOne(TokenType.leftParen)) {
|
||||
final parameters = _functionParameters();
|
||||
final rightParen = _consume(TokenType.rightParen,
|
||||
'Expected closing bracket after argument list');
|
||||
|
||||
if (_peek.type == TokenType.filter || _peek.type == TokenType.over) {
|
||||
return _aggregate(first, parameters);
|
||||
}
|
||||
if (_peek.type == TokenType.filter || _peek.type == TokenType.over) {
|
||||
return _aggregate(first, parameters);
|
||||
}
|
||||
|
||||
return FunctionExpression(
|
||||
name: first.identifier, parameters: parameters)
|
||||
..setSpan(first, rightParen);
|
||||
} else {
|
||||
return Reference(columnName: first.identifier)..setSpan(first, first);
|
||||
}
|
||||
break;
|
||||
case TokenType.dollarSignVariable:
|
||||
if (enableMoorExtensions) {
|
||||
final typedToken = token as DollarSignVariableToken;
|
||||
return DartExpressionPlaceholder(name: typedToken.name)
|
||||
..token = typedToken
|
||||
..setSpan(token, token);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
return FunctionExpression(
|
||||
name: first.identifier, parameters: parameters)
|
||||
..setSpan(first, rightParen);
|
||||
} else {
|
||||
return Reference(columnName: first.identifier)..setSpan(first, first);
|
||||
}
|
||||
}
|
||||
|
||||
// nothing found -> issue error. Step back to revert the _advance() above
|
||||
_stepBack();
|
||||
_error('Could not parse this expression');
|
||||
}
|
||||
|
||||
|
|
|
@ -101,6 +101,15 @@ abstract class ParserBase {
|
|||
return _peek.type == type;
|
||||
}
|
||||
|
||||
/// Returns whether the next token is an [TokenType.identifier] or a
|
||||
/// [KeywordToken]. If this method returns true, calling [_consumeIdentifier]
|
||||
/// with the lenient parameter will now throw.
|
||||
bool _checkLenientIdentifier() {
|
||||
final next = _peek;
|
||||
return next.type == TokenType.identifier ||
|
||||
(next is KeywordToken && next.canConvertToIdentifier());
|
||||
}
|
||||
|
||||
Token _advance() {
|
||||
if (!_isAtEnd) {
|
||||
_current++;
|
||||
|
@ -108,16 +117,6 @@ abstract class ParserBase {
|
|||
return _previous;
|
||||
}
|
||||
|
||||
/// Steps back a token. This needs to be used very carefully. We basically
|
||||
/// only use it in [ExpressionParser._primary] because we unconditionally
|
||||
/// [_advance] in there and we'd like to report more accurate errors when no
|
||||
/// matching token was found.
|
||||
void _stepBack() {
|
||||
if (_current != null) {
|
||||
_current--;
|
||||
}
|
||||
}
|
||||
|
||||
@alwaysThrows
|
||||
void _error(String message) {
|
||||
final error = ParsingError(_peek, message);
|
||||
|
@ -130,11 +129,11 @@ abstract class ParserBase {
|
|||
_error(message);
|
||||
}
|
||||
|
||||
/// Consumes an identifier. If [lenient] is true and the next token is not
|
||||
/// an identifier but rather a [KeywordToken], that token will be converted
|
||||
/// to an identifier.
|
||||
IdentifierToken _consumeIdentifier(String message, {bool lenient = false}) {
|
||||
if (lenient && _peek is KeywordToken) {
|
||||
/// Consumes an identifier.
|
||||
IdentifierToken _consumeIdentifier(String message) {
|
||||
final next = _peek;
|
||||
// non-standard keywords can be parsed as an identifier
|
||||
if (next is KeywordToken && next.canConvertToIdentifier()) {
|
||||
return (_advance() as KeywordToken).convertToIdentifier();
|
||||
}
|
||||
return _consume(TokenType.identifier, message) as IdentifierToken;
|
||||
|
@ -262,8 +261,7 @@ class Parser extends ParserBase
|
|||
|
||||
DeclaredStatement _declaredStatement() {
|
||||
if (_check(TokenType.identifier) || _peek is KeywordToken) {
|
||||
final name = _consumeIdentifier('Expected a name for a declared query',
|
||||
lenient: true);
|
||||
final name = _consumeIdentifier('Expected a name for a declared query');
|
||||
final colon =
|
||||
_consume(TokenType.colon, 'Expected colon (:) followed by a query');
|
||||
|
||||
|
|
|
@ -18,8 +18,7 @@ mixin SchemaParser on ParserBase {
|
|||
ifNotExists = true;
|
||||
}
|
||||
|
||||
final tableIdentifier =
|
||||
_consumeIdentifier('Expected a table name', lenient: true);
|
||||
final tableIdentifier = _consumeIdentifier('Expected a table name');
|
||||
|
||||
// we don't currently support CREATE TABLE x AS SELECT ... statements
|
||||
final leftParen = _consume(
|
||||
|
@ -57,10 +56,8 @@ mixin SchemaParser on ParserBase {
|
|||
|
||||
String overriddenName;
|
||||
if (enableMoorExtensions && _matchOne(TokenType.as)) {
|
||||
overriddenName = _consumeIdentifier(
|
||||
'Expected the name for the data class',
|
||||
lenient: true)
|
||||
.identifier;
|
||||
overriddenName =
|
||||
_consumeIdentifier('Expected the name for the data class').identifier;
|
||||
}
|
||||
|
||||
return CreateTableStatement(
|
||||
|
@ -249,8 +246,7 @@ mixin SchemaParser on ParserBase {
|
|||
|
||||
String _constraintNameOrNull() {
|
||||
if (_matchOne(TokenType.constraint)) {
|
||||
final name = _consumeIdentifier('Expect a name for the constraint here',
|
||||
lenient: true);
|
||||
final name = _consumeIdentifier('Expect a name for the constraint here');
|
||||
return name.identifier;
|
||||
}
|
||||
return null;
|
||||
|
|
|
@ -349,6 +349,11 @@ class KeywordToken extends Token {
|
|||
|
||||
KeywordToken(TokenType type, FileSpan span) : super(type, span);
|
||||
|
||||
bool canConvertToIdentifier() {
|
||||
// https://stackoverflow.com/a/45775719, but we don't parse indexed yet.
|
||||
return type == TokenType.join;
|
||||
}
|
||||
|
||||
IdentifierToken convertToIdentifier() {
|
||||
isIdentifier = true;
|
||||
|
||||
|
|
Loading…
Reference in New Issue