Skip to content

Commit

Permalink
Add to non-reserved keywords; rework functionName and `symbolPrimit…
Browse files Browse the repository at this point in the history
…ive` parse rules (#1457)
  • Loading branch information
alancai98 authored May 9, 2024
1 parent 2879f3a commit 4f89c2d
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 103 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -198,19 +198,29 @@ internal class PartiQLPigVisitor(

override fun visitByIdent(ctx: PartiQLParser.ByIdentContext) = visitSymbolPrimitive(ctx.symbolPrimitive())

override fun visitSymbolPrimitive(ctx: PartiQLParser.SymbolPrimitiveContext) = PartiqlAst.build {
val metas = ctx.ident.getSourceMetaContainer()
when (ctx.ident.type) {
PartiQLParser.IDENTIFIER_QUOTED -> id(
ctx.IDENTIFIER_QUOTED().getStringValue(),
caseSensitive(),
unqualified(),
metas
)

PartiQLParser.IDENTIFIER -> id(ctx.IDENTIFIER().getStringValue(), caseInsensitive(), unqualified(), metas)
private fun visitSymbolPrimitive(ctx: PartiQLParser.SymbolPrimitiveContext): PartiqlAst.Expr.Id =
when (ctx) {
is PartiQLParser.IdentifierQuotedContext -> visitIdentifierQuoted(ctx)
is PartiQLParser.IdentifierUnquotedContext -> visitIdentifierUnquoted(ctx)
else -> throw ParserException("Invalid symbol reference.", ErrorCode.PARSE_INVALID_QUERY)
}

override fun visitIdentifierQuoted(ctx: PartiQLParser.IdentifierQuotedContext): PartiqlAst.Expr.Id = PartiqlAst.build {
id(
ctx.IDENTIFIER_QUOTED().getStringValue(),
caseSensitive(),
unqualified(),
ctx.IDENTIFIER_QUOTED().getSourceMetaContainer()
)
}

override fun visitIdentifierUnquoted(ctx: PartiQLParser.IdentifierUnquotedContext): PartiqlAst.Expr.Id = PartiqlAst.build {
id(
ctx.text,
caseInsensitive(),
unqualified(),
ctx.IDENTIFIER().getSourceMetaContainer()
)
}

/**
Expand Down Expand Up @@ -660,9 +670,9 @@ internal class PartiQLPigVisitor(

override fun visitExcludeExprTupleAttr(ctx: PartiQLParser.ExcludeExprTupleAttrContext) = PartiqlAst.build {
val attr = ctx.symbolPrimitive().getString()
val caseSensitivity = when (ctx.symbolPrimitive().ident.type) {
PartiQLParser.IDENTIFIER_QUOTED -> caseSensitive()
PartiQLParser.IDENTIFIER -> caseInsensitive()
val caseSensitivity = when (ctx.symbolPrimitive()) {
is PartiQLParser.IdentifierQuotedContext -> caseSensitive()
is PartiQLParser.IdentifierUnquotedContext -> caseInsensitive()
else -> throw ParserException("Invalid symbol reference.", ErrorCode.PARSE_INVALID_QUERY)
}
excludeTupleAttr(identifier(attr, caseSensitivity))
Expand Down Expand Up @@ -1192,7 +1202,7 @@ internal class PartiQLPigVisitor(

override fun visitVariableKeyword(ctx: PartiQLParser.VariableKeywordContext): PartiqlAst.PartiqlAstNode =
PartiqlAst.build {
val keyword = ctx.nonReservedKeywords().start.text
val keyword = ctx.nonReserved().start.text
val metas = ctx.start.getSourceMetaContainer()
val qualifier = ctx.qualifier?.let { localsFirst() } ?: unqualified()
id(keyword, caseInsensitive(), qualifier, metas)
Expand Down Expand Up @@ -1316,16 +1326,11 @@ internal class PartiQLPigVisitor(
}

override fun visitFunctionCall(ctx: PartiQLParser.FunctionCallContext) = PartiqlAst.build {
val name = when (val nameCtx = ctx.functionName()) {
is PartiQLParser.FunctionNameReservedContext -> {
if (nameCtx.qualifier.isNotEmpty()) error("Legacy AST does not support qualified function names")
nameCtx.name.text.lowercase()
}
is PartiQLParser.FunctionNameSymbolContext -> {
if (nameCtx.qualifier.isNotEmpty()) error("Legacy AST does not support qualified function names")
nameCtx.name.getString().lowercase()
}
else -> error("Expected context FunctionNameReserved or FunctionNameSymbol")
val nameCtx = ctx.qualifiedName()
val name = if (nameCtx.qualifier.isNotEmpty()) {
error("Legacy AST does not support qualified function names")
} else {
nameCtx.name.getString().lowercase()
}
val args = ctx.expr().map { visitExpr(it) }
val metas = ctx.start.getSourceMetaContainer()
Expand Down Expand Up @@ -1819,9 +1824,9 @@ internal class PartiQLPigVisitor(
}
}

private fun PartiQLParser.SymbolPrimitiveContext.getSourceMetaContainer() = when (this.ident.type) {
PartiQLParser.IDENTIFIER -> this.IDENTIFIER().getSourceMetaContainer()
PartiQLParser.IDENTIFIER_QUOTED -> this.IDENTIFIER_QUOTED().getSourceMetaContainer()
private fun PartiQLParser.SymbolPrimitiveContext.getSourceMetaContainer() = when (this) {
is PartiQLParser.IdentifierQuotedContext -> this.IDENTIFIER_QUOTED().getSourceMetaContainer()
is PartiQLParser.IdentifierUnquotedContext -> this.start.getSourceMetaContainer()
else -> throw ParserException(
"Unable to get identifier's source meta-container.",
ErrorCode.PARSE_INVALID_QUERY
Expand Down Expand Up @@ -2125,25 +2130,23 @@ internal class PartiQLPigVisitor(
}

private fun PartiQLParser.SymbolPrimitiveContext.getString(): String {
return when {
this.IDENTIFIER_QUOTED() != null -> this.IDENTIFIER_QUOTED().getStringValue()
this.IDENTIFIER() != null -> this.IDENTIFIER().text
return when (this) {
is PartiQLParser.IdentifierQuotedContext -> this.IDENTIFIER_QUOTED().getStringValue()
is PartiQLParser.IdentifierUnquotedContext -> this.text
else -> throw ParserException("Unable to get symbol's text.", ErrorCode.PARSE_INVALID_QUERY)
}
}

private fun getSymbolPathExpr(ctx: PartiQLParser.SymbolPrimitiveContext) = PartiqlAst.build {
when {
ctx.IDENTIFIER_QUOTED() != null -> pathExpr(
when (ctx) {
is PartiQLParser.IdentifierQuotedContext -> pathExpr(
lit(ionString(ctx.IDENTIFIER_QUOTED().getStringValue())), caseSensitive(),
metas = ctx.IDENTIFIER_QUOTED().getSourceMetaContainer()
metas = ctx.getSourceMetaContainer()
)

ctx.IDENTIFIER() != null -> pathExpr(
lit(ionString(ctx.IDENTIFIER().text)), caseInsensitive(),
metas = ctx.IDENTIFIER().getSourceMetaContainer()
is PartiQLParser.IdentifierUnquotedContext -> pathExpr(
lit(ionString(ctx.text)), caseInsensitive(),
metas = ctx.getSourceMetaContainer()
)

else -> throw ParserException("Unable to get symbol's text.", ErrorCode.PARSE_INVALID_QUERY)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package org.partiql.lang.errors

import com.amazon.ion.Timestamp
import org.junit.Ignore
import org.junit.Test
import org.partiql.errors.ErrorCode
import org.partiql.errors.Property
Expand Down Expand Up @@ -655,6 +656,7 @@ class ParserErrorsTest : PartiQLParserTestBase() {
}

@Test
@Ignore("No longer a parser error; evaluation error")
fun callTrimNoArgs() {
checkInputThrowingParserException(
"trim()",
Expand Down Expand Up @@ -683,6 +685,7 @@ class ParserErrorsTest : PartiQLParserTestBase() {
}

@Test
@Ignore("No longer a parser error; evaluation error")
fun callTrimZeroArguments() {
checkInputThrowingParserException(
"trim()",
Expand Down Expand Up @@ -795,6 +798,7 @@ class ParserErrorsTest : PartiQLParserTestBase() {
}

@Test
@Ignore("No longer a parser error; evaluation error")
fun aggregateWithNoArgs() {
checkInputThrowingParserException(
"SUM()",
Expand All @@ -809,6 +813,7 @@ class ParserErrorsTest : PartiQLParserTestBase() {
}

@Test
@Ignore("No longer a parser error; evaluation error")
fun aggregateWithTooManyArgs() {
checkInputThrowingParserException(
"SUM(a, b)",
Expand Down Expand Up @@ -1672,6 +1677,7 @@ class ParserErrorsTest : PartiQLParserTestBase() {
}

@Test
@Ignore("No longer a parser error; evaluation error")
fun callExtractMissingFromWithComma() {
checkInputThrowingParserException(
"extract(year, b)",
Expand Down Expand Up @@ -1717,6 +1723,7 @@ class ParserErrorsTest : PartiQLParserTestBase() {
}

@Test
@Ignore("No longer a parser error; evaluation error")
fun callExtractOnlySecondArgument() {
checkInputThrowingParserException(
"extract(b)",
Expand Down Expand Up @@ -1746,6 +1753,7 @@ class ParserErrorsTest : PartiQLParserTestBase() {
}

@Test
@Ignore("No longer a parser error; evaluation error")
fun callExtractOnlyDateTimePart() {
checkInputThrowingParserException(
"extract(year)",
Expand Down
65 changes: 51 additions & 14 deletions partiql-parser/src/main/antlr/PartiQL.g4
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ byIdent
: BY symbolPrimitive;

symbolPrimitive
: ident=( IDENTIFIER | IDENTIFIER_QUOTED )
: IDENTIFIER # IdentifierUnquoted
| IDENTIFIER_QUOTED # IdentifierQuoted
| nonReserved # IdentifierUnquoted
;

/**
Expand Down Expand Up @@ -653,8 +655,8 @@ exprPrimary
| dateFunction # ExprPrimaryBase
| aggregate # ExprPrimaryBase
| trimFunction # ExprPrimaryBase
| functionCall # ExprPrimaryBase
| nullIf # ExprPrimaryBase
| functionCall # ExprPrimaryBase
| exprPrimary pathStep+ # ExprPrimaryPath
| exprGraphMatchMany # ExprPrimaryBase
| caseExpr # ExprPrimaryBase
Expand Down Expand Up @@ -761,13 +763,7 @@ dateFunction

// SQL-99 10.4 — <routine invocation> ::= <routine name> <SQL argument list>
functionCall
: functionName PAREN_LEFT ( expr ( COMMA expr )* )? PAREN_RIGHT
;

// SQL-99 10.4 — <routine name> ::= [ <schema name> <period> ] <qualified identifier>
functionName
: (qualifier+=symbolPrimitive PERIOD)* name=( CHAR_LENGTH | CHARACTER_LENGTH | OCTET_LENGTH | BIT_LENGTH | UPPER | LOWER | SIZE | EXISTS | COUNT | MOD ) # FunctionNameReserved
| (qualifier+=symbolPrimitive PERIOD)* name=symbolPrimitive # FunctionNameSymbol
: qualifiedName PAREN_LEFT ( expr ( COMMA expr )* )? PAREN_RIGHT
;

pathStep
Expand All @@ -789,11 +785,52 @@ parameter

varRefExpr
: qualifier=AT_SIGN? ident=(IDENTIFIER|IDENTIFIER_QUOTED) # VariableIdentifier
| qualifier=AT_SIGN? key=nonReservedKeywords # VariableKeyword
;

nonReservedKeywords
: EXCLUDED
| qualifier=AT_SIGN? key=nonReserved # VariableKeyword
;

nonReserved
: /* From SQL99 <non-reserved word> https://ronsavage.github.io/SQL/sql-99.bnf.html#non-reserved%20word */
ABS | ADA | ADMIN | ASENSITIVE | ASSIGNMENT | ASYMMETRIC | ATOMIC
| ATTRIBUTE | AVG
| BIT_LENGTH
| C | CALLED | CARDINALITY | CATALOG_NAME | CHAIN | CHAR_LENGTH
| CHARACTERISTICS | CHARACTER_LENGTH | CHARACTER_SET_CATALOG
| CHARACTER_SET_NAME | CHARACTER_SET_SCHEMA | CHECKED | CLASS_ORIGIN
| COALESCE | COBOL | COLLATION_CATALOG | COLLATION_NAME | COLLATION_SCHEMA
| COLUMN_NAME | COMMAND_FUNCTION | COMMAND_FUNCTION_CODE | COMMITTED
| CONDITION_IDENTIFIER | CONDITION_NUMBER | CONNECTION_NAME
| CONSTRAINT_CATALOG | CONSTRAINT_NAME | CONSTRAINT_SCHEMA | CONTAINS
| CONVERT | COUNT | CURSOR_NAME
| DATETIME_INTERVAL_CODE | DATETIME_INTERVAL_PRECISION | DEFINED
| DEFINER | DEGREE | DERIVED | DISPATCH
| EVERY | EXTRACT
| FINAL | FORTRAN
| G | GENERATED | GRANTED
| HIERARCHY
| IMPLEMENTATION | INSENSITIVE | INSTANCE | INSTANTIABLE | INVOKER
| K | KEY_MEMBER | KEY_TYPE
| LENGTH | LOWER
| M | MAX | MIN | MESSAGE_LENGTH | MESSAGE_OCTET_LENGTH | MESSAGE_TEXT
| MOD | MORE | MUMPS
| NAME | NULLABLE | NUMBER | NULLIF
| OCTET_LENGTH | ORDERING | OPTIONS | OVERLAY | OVERRIDING
| PASCAL | PARAMETER_MODE | PARAMETER_NAME
| PARAMETER_ORDINAL_POSITION | PARAMETER_SPECIFIC_CATALOG
| PARAMETER_SPECIFIC_NAME | PARAMETER_SPECIFIC_SCHEMA | PLI | POSITION
| REPEATABLE | RETURNED_CARDINALITY | RETURNED_LENGTH
| RETURNED_OCTET_LENGTH | RETURNED_SQLSTATE | ROUTINE_CATALOG
| ROUTINE_NAME | ROUTINE_SCHEMA | ROW_COUNT
| SCALE | SCHEMA_NAME | SCOPE | SECURITY | SELF | SENSITIVE | SERIALIZABLE
| SERVER_NAME | SIMPLE | SOURCE | SPECIFIC_NAME | STATEMENT | STRUCTURE
| STYLE | SUBCLASS_ORIGIN | SUBSTRING | SUM | SYMMETRIC | SYSTEM
| TABLE_NAME | TOP_LEVEL_COUNT | TRANSACTIONS_COMMITTED
| TRANSACTIONS_ROLLED_BACK | TRANSACTION_ACTIVE | TRANSFORM
| TRANSFORMS | TRANSLATE | TRIGGER_CATALOG | TRIGGER_SCHEMA
| TRIGGER_NAME | TRIM | TYPE
| UNCOMMITTED | UNNAMED | UPPER
/* PartiQL */
| EXCLUDED | EXISTS
| SIZE
;

/**
Expand Down
Loading

0 comments on commit 4f89c2d

Please sign in to comment.