%define api.pure %name-prefix "Aql" %locations %defines %parse-param { triagens::aql::Parser* parser } %lex-param { void* scanner } %error-verbose %{ #include #include #include #include #include #include "Aql/AstNode.h" #include "Aql/Parser.h" %} %union { triagens::aql::AstNode* node; char* strval; bool boolval; int64_t intval; } %{ using namespace triagens::aql; //////////////////////////////////////////////////////////////////////////////// /// @brief forward for lexer function defined in Aql/tokens.ll //////////////////////////////////////////////////////////////////////////////// int Aqllex (YYSTYPE*, YYLTYPE*, void*); //////////////////////////////////////////////////////////////////////////////// /// @brief register parse error //////////////////////////////////////////////////////////////////////////////// void Aqlerror (YYLTYPE* locp, triagens::aql::Parser* parser, char const* message) { parser->registerParseError(TRI_ERROR_QUERY_PARSE, message, locp->first_line, locp->first_column); } //////////////////////////////////////////////////////////////////////////////// /// @brief shortcut macro for signalling out of memory //////////////////////////////////////////////////////////////////////////////// #define ABORT_OOM \ parser->registerError(TRI_ERROR_OUT_OF_MEMORY); \ YYABORT; #define scanner parser->scanner() %} /* define tokens and "nice" token names */ %token T_FOR "FOR declaration" %token T_LET "LET declaration" %token T_FILTER "FILTER declaration" %token T_RETURN "RETURN declaration" %token T_COLLECT "COLLECT declaration" %token T_SORT "SORT declaration" %token T_LIMIT "LIMIT declaration" %token T_ASC "ASC keyword" %token T_DESC "DESC keyword" %token T_IN "IN keyword" %token T_WITH "WITH keyword" %token T_INTO "INTO keyword" %token T_REMOVE "REMOVE command" %token T_INSERT "INSERT command" %token T_UPDATE "UPDATE command" %token T_REPLACE "REPLACE command" %token T_NULL "null" %token T_TRUE "true" %token T_FALSE "false" %token T_STRING "identifier" %token T_QUOTED_STRING "quoted string" %token T_INTEGER "integer number" %token T_DOUBLE "number" %token T_PARAMETER "bind parameter" %token T_ASSIGN "assignment" %token T_NOT "not operator" %token T_AND "and operator" %token T_OR "or operator" %token T_EQ "== operator" %token T_NE "!= operator" %token T_LT "< operator" %token T_GT "> operator" %token T_LE "<= operator" %token T_GE ">= operator" %token T_PLUS "+ operator" %token T_MINUS "- operator" %token T_TIMES "* operator" %token T_DIV "/ operator" %token T_MOD "% operator" %token T_EXPAND "[*] operator" %token T_QUESTION "?" %token T_COLON ":" %token T_SCOPE "::" %token T_RANGE ".." %token T_COMMA "," %token T_OPEN "(" %token T_CLOSE ")" %token T_DOC_OPEN "{" %token T_DOC_CLOSE "}" %token T_LIST_OPEN "[" %token T_LIST_CLOSE "]" %token T_END 0 "end of query string" /* define operator precedence */ %left T_COMMA T_RANGE %right T_QUESTION T_COLON %right T_ASSIGN %left T_OR %left T_AND %left T_EQ T_NE %left T_IN %left T_LT T_GT T_LE T_GE %left T_PLUS T_MINUS %left T_TIMES T_DIV T_MOD %right UMINUS UPLUS T_NOT %left T_EXPAND %left FUNCCALL %left REFERENCE %left INDEXED %left T_SCOPE /* define token return types */ %type T_STRING %type T_QUOTED_STRING %type T_INTEGER %type T_DOUBLE %type T_PARAMETER; %type sort_list; %type sort_element; %type sort_direction; %type collect_list; %type collect_element; %type optional_into; %type expression; %type operator_unary; %type operator_binary; %type operator_ternary; %type function_call; %type function_name; %type optional_function_call_arguments; %type function_arguments_list; %type compound_type; %type list; %type optional_list_elements; %type list_elements_list; %type array; %type query_options; %type optional_array_elements; %type array_elements_list; %type array_element; %type array_element_name; %type reference; %type single_reference; %type expansion; %type atomic_value; %type value_literal; %type collection_name; %type in_or_into_collection; %type bind_parameter; %type variable_name; %type numeric_value; %type integer_value; /* define start token of language */ %start query %% query: optional_statement_block_statements return_statement { } | optional_statement_block_statements remove_statement { } | optional_statement_block_statements insert_statement { } | optional_statement_block_statements update_statement { } | optional_statement_block_statements replace_statement { } ; optional_statement_block_statements: /* empty */ { } | optional_statement_block_statements statement_block_statement { } ; statement_block_statement: for_statement { } | let_statement { } | filter_statement { } | collect_statement { } | sort_statement { } | limit_statement { } ; for_statement: T_FOR variable_name T_IN expression { parser->ast()->scopes()->start(triagens::aql::AQL_SCOPE_FOR); auto node = parser->ast()->createNodeFor($2, $4); parser->ast()->addOperation(node); } ; filter_statement: T_FILTER expression { // operand is a reference. can use it directly auto node = parser->ast()->createNodeFilter($2); parser->ast()->addOperation(node); } ; let_statement: T_LET let_list { } ; let_list: let_element { } | let_list T_COMMA let_element { } ; let_element: variable_name T_ASSIGN expression { auto node = parser->ast()->createNodeLet($1, $3, true); parser->ast()->addOperation(node); } ; collect_statement: T_COLLECT { auto node = parser->ast()->createNodeList(); parser->pushStack(node); } collect_list optional_into { auto list = static_cast(parser->popStack()); if (list == nullptr) { ABORT_OOM } auto scopes = parser->ast()->scopes(); // check if we are in the main scope bool reRegisterVariables = (scopes->type() != triagens::aql::AQL_SCOPE_MAIN); if (reRegisterVariables) { // end the active scopes scopes->endNested(); // start a new scope scopes->start(triagens::aql::AQL_SCOPE_COLLECT); size_t const n = list->numMembers(); for (size_t i = 0; i < n; ++i) { auto member = list->getMember(i); if (member != nullptr) { TRI_ASSERT(member->type == NODE_TYPE_ASSIGN); auto v = static_cast(member->getMember(0)->getData()); scopes->addVariable(v); } } } auto node = parser->ast()->createNodeCollect(list, $4); parser->ast()->addOperation(node); } ; collect_list: collect_element { } | collect_list T_COMMA collect_element { } ; collect_element: variable_name T_ASSIGN expression { auto node = parser->ast()->createNodeAssign($1, $3); parser->pushList(node); } ; optional_into: /* empty */ { $$ = nullptr; } | T_INTO variable_name { $$ = $2; } ; sort_statement: T_SORT { auto node = parser->ast()->createNodeList(); parser->pushStack(node); } sort_list { auto list = static_cast(parser->popStack()); auto node = parser->ast()->createNodeSort(list); parser->ast()->addOperation(node); } ; sort_list: sort_element { parser->pushList($1); } | sort_list T_COMMA sort_element { parser->pushList($3); } ; sort_element: expression sort_direction { $$ = parser->ast()->createNodeSortElement($1, $2); } ; sort_direction: /* empty */ { $$ = true; } | T_ASC { $$ = true; } | T_DESC { $$ = false; } ; limit_statement: T_LIMIT atomic_value { auto offset = parser->ast()->createNodeValueInt(0); auto node = parser->ast()->createNodeLimit(offset, $2); parser->ast()->addOperation(node); } | T_LIMIT atomic_value T_COMMA atomic_value { auto node = parser->ast()->createNodeLimit($2, $4); parser->ast()->addOperation(node); } ; return_statement: T_RETURN expression { auto node = parser->ast()->createNodeReturn($2); parser->ast()->addOperation(node); parser->ast()->scopes()->endNested(); } ; in_or_into_collection: T_IN collection_name { $$ = $2; } | T_INTO collection_name { $$ = $2; } ; remove_statement: T_REMOVE expression in_or_into_collection query_options { if (! parser->configureWriteQuery(AQL_QUERY_REMOVE, $3, $4)) { YYABORT; } auto node = parser->ast()->createNodeRemove($2, $3, $4); parser->ast()->addOperation(node); parser->ast()->scopes()->endNested(); } ; insert_statement: T_INSERT expression in_or_into_collection query_options { if (! parser->configureWriteQuery(AQL_QUERY_INSERT, $3, $4)) { YYABORT; } auto node = parser->ast()->createNodeInsert($2, $3, $4); parser->ast()->addOperation(node); parser->ast()->scopes()->endNested(); } ; update_statement: T_UPDATE expression in_or_into_collection query_options { if (! parser->configureWriteQuery(AQL_QUERY_UPDATE, $3, $4)) { YYABORT; } auto node = parser->ast()->createNodeUpdate(nullptr, $2, $3, $4); parser->ast()->addOperation(node); parser->ast()->scopes()->endNested(); } | T_UPDATE expression T_WITH expression in_or_into_collection query_options { if (! parser->configureWriteQuery(AQL_QUERY_UPDATE, $5, $6)) { YYABORT; } auto node = parser->ast()->createNodeUpdate($2, $4, $5, $6); parser->ast()->addOperation(node); parser->ast()->scopes()->endNested(); } ; replace_statement: T_REPLACE expression in_or_into_collection query_options { if (! parser->configureWriteQuery(AQL_QUERY_REPLACE, $3, $4)) { YYABORT; } auto node = parser->ast()->createNodeReplace(nullptr, $2, $3, $4); parser->ast()->addOperation(node); parser->ast()->scopes()->endNested(); } | T_REPLACE expression T_WITH expression in_or_into_collection query_options { if (! parser->configureWriteQuery(AQL_QUERY_REPLACE, $5, $6)) { YYABORT; } auto node = parser->ast()->createNodeReplace($2, $4, $5, $6); parser->ast()->addOperation(node); parser->ast()->scopes()->endNested(); } ; expression: T_OPEN expression T_CLOSE { $$ = $2; } | T_OPEN { parser->ast()->scopes()->start(triagens::aql::AQL_SCOPE_SUBQUERY); parser->ast()->startSubQuery(); } query T_CLOSE { AstNode* node = parser->ast()->endSubQuery(); parser->ast()->scopes()->endCurrent(); std::string const variableName = parser->ast()->variables()->nextName(); auto subQuery = parser->ast()->createNodeLet(variableName.c_str(), node, false); parser->ast()->addOperation(subQuery); $$ = parser->ast()->createNodeReference(variableName.c_str()); } | operator_unary { $$ = $1; } | operator_binary { $$ = $1; } | operator_ternary { $$ = $1; } | compound_type { $$ = $1; } | atomic_value { $$ = $1; } | reference { $$ = $1; } | expression T_RANGE expression { $$ = parser->ast()->createNodeRange($1, $3); } ; function_name: T_STRING { $$ = $1; if ($$ == nullptr) { ABORT_OOM } } | function_name T_SCOPE T_STRING { if ($1 == nullptr || $3 == nullptr) { ABORT_OOM } std::string temp($1); temp.append("::"); temp.append($3); $$ = parser->query()->registerString(temp.c_str(), temp.size(), false); if ($$ == nullptr) { ABORT_OOM } } ; function_call: function_name { parser->pushStack($1); auto node = parser->ast()->createNodeList(); parser->pushStack(node); } T_OPEN optional_function_call_arguments T_CLOSE %prec FUNCCALL { auto list = static_cast(parser->popStack()); $$ = parser->ast()->createNodeFunctionCall(static_cast(parser->popStack()), list); } ; operator_unary: T_PLUS expression %prec UPLUS { $$ = parser->ast()->createNodeUnaryOperator(NODE_TYPE_OPERATOR_UNARY_PLUS, $2); } | T_MINUS expression %prec UMINUS { $$ = parser->ast()->createNodeUnaryOperator(NODE_TYPE_OPERATOR_UNARY_MINUS, $2); } | T_NOT expression %prec T_NOT { $$ = parser->ast()->createNodeUnaryOperator(NODE_TYPE_OPERATOR_UNARY_NOT, $2); } ; operator_binary: expression T_OR expression { $$ = parser->ast()->createNodeBinaryOperator(NODE_TYPE_OPERATOR_BINARY_OR, $1, $3); } | expression T_AND expression { $$ = parser->ast()->createNodeBinaryOperator(NODE_TYPE_OPERATOR_BINARY_AND, $1, $3); } | expression T_PLUS expression { $$ = parser->ast()->createNodeBinaryOperator(NODE_TYPE_OPERATOR_BINARY_PLUS, $1, $3); } | expression T_MINUS expression { $$ = parser->ast()->createNodeBinaryOperator(NODE_TYPE_OPERATOR_BINARY_MINUS, $1, $3); } | expression T_TIMES expression { $$ = parser->ast()->createNodeBinaryOperator(NODE_TYPE_OPERATOR_BINARY_TIMES, $1, $3); } | expression T_DIV expression { $$ = parser->ast()->createNodeBinaryOperator(NODE_TYPE_OPERATOR_BINARY_DIV, $1, $3); } | expression T_MOD expression { $$ = parser->ast()->createNodeBinaryOperator(NODE_TYPE_OPERATOR_BINARY_MOD, $1, $3); } | expression T_EQ expression { $$ = parser->ast()->createNodeBinaryOperator(NODE_TYPE_OPERATOR_BINARY_EQ, $1, $3); } | expression T_NE expression { $$ = parser->ast()->createNodeBinaryOperator(NODE_TYPE_OPERATOR_BINARY_NE, $1, $3); } | expression T_LT expression { $$ = parser->ast()->createNodeBinaryOperator(NODE_TYPE_OPERATOR_BINARY_LT, $1, $3); } | expression T_GT expression { $$ = parser->ast()->createNodeBinaryOperator(NODE_TYPE_OPERATOR_BINARY_GT, $1, $3); } | expression T_LE expression { $$ = parser->ast()->createNodeBinaryOperator(NODE_TYPE_OPERATOR_BINARY_LE, $1, $3); } | expression T_GE expression { $$ = parser->ast()->createNodeBinaryOperator(NODE_TYPE_OPERATOR_BINARY_GE, $1, $3); } | expression T_IN expression { $$ = parser->ast()->createNodeBinaryOperator(NODE_TYPE_OPERATOR_BINARY_IN, $1, $3); } | expression T_NOT T_IN expression { $$ = parser->ast()->createNodeBinaryOperator(NODE_TYPE_OPERATOR_BINARY_NIN, $1, $4); } ; operator_ternary: expression T_QUESTION expression T_COLON expression { $$ = parser->ast()->createNodeTernaryOperator($1, $3, $5); } ; optional_function_call_arguments: /* empty */ { } | function_arguments_list { } ; function_arguments_list: expression { parser->pushList($1); } | function_arguments_list T_COMMA expression { parser->pushList($3); } ; compound_type: list { $$ = $1; } | array { $$ = $1; } ; list: T_LIST_OPEN { auto node = parser->ast()->createNodeList(); parser->pushStack(node); } optional_list_elements T_LIST_CLOSE { $$ = static_cast(parser->popStack()); } ; optional_list_elements: /* empty */ { } | list_elements_list { } ; list_elements_list: expression { parser->pushList($1); } | list_elements_list T_COMMA expression { parser->pushList($3); } ; query_options: /* empty */ { $$ = nullptr; } | T_STRING array { if ($1 == nullptr || $2 == nullptr) { ABORT_OOM } if (! TRI_CaseEqualString($1, "OPTIONS")) { parser->registerParseError(TRI_ERROR_QUERY_PARSE, "unexpected qualifier '%s', expecting 'OPTIONS'", $1, yylloc.first_line, yylloc.first_column); } $$ = $2; } ; array: T_DOC_OPEN { auto node = parser->ast()->createNodeArray(); parser->pushStack(node); } optional_array_elements T_DOC_CLOSE { $$ = static_cast(parser->popStack()); } ; optional_array_elements: /* empty */ { } | array_elements_list { } ; array_elements_list: array_element { } | array_elements_list T_COMMA array_element { } ; array_element: array_element_name T_COLON expression { parser->pushArray($1, $3); } ; reference: single_reference { // start of reference (collection or variable name) $$ = $1; } | reference { // expanded variable access, e.g. variable[*] // create a temporary iterator variable std::string const nextName = parser->ast()->variables()->nextName() + "_"; char const* iteratorName = nextName.c_str(); auto iterator = parser->ast()->createNodeIterator(iteratorName, $1); parser->pushStack(iterator); parser->pushStack(parser->ast()->createNodeReference(iteratorName)); } T_EXPAND expansion { // return from the "expansion" subrule // push the expand node into the statement list auto iterator = static_cast(parser->popStack()); auto expand = parser->ast()->createNodeExpand(iterator, $4); std::string const nextName = parser->ast()->variables()->nextName(); char const* variableName = nextName.c_str(); auto let = parser->ast()->createNodeLet(variableName, expand, false); parser->ast()->addOperation(let); // return a reference only $$ = parser->ast()->createNodeReference(variableName); } ; single_reference: T_STRING { // variable or collection AstNode* node; if (parser->ast()->scopes()->existsVariable($1)) { node = parser->ast()->createNodeReference($1); } else { node = parser->ast()->createNodeCollection($1); } $$ = node; } | function_call { $$ = $1; if ($$ == nullptr) { ABORT_OOM } } | single_reference '.' T_STRING %prec REFERENCE { // named variable access, e.g. variable.reference $$ = parser->ast()->createNodeAttributeAccess($1, $3); } | single_reference '.' bind_parameter %prec REFERENCE { // named variable access, e.g. variable.@reference $$ = parser->ast()->createNodeBoundAttributeAccess($1, $3); } | single_reference T_LIST_OPEN expression T_LIST_CLOSE %prec INDEXED { // indexed variable access, e.g. variable[index] $$ = parser->ast()->createNodeIndexedAccess($1, $3); } ; expansion: '.' T_STRING %prec REFERENCE { // named variable access, continuation from * expansion, e.g. [*].variable.reference auto node = static_cast(parser->popStack()); $$ = parser->ast()->createNodeAttributeAccess(node, $2); } | '.' bind_parameter %prec REFERENCE { // named variable access w/ bind parameter, continuation from * expansion, e.g. [*].variable.@reference auto node = static_cast(parser->popStack()); $$ = parser->ast()->createNodeBoundAttributeAccess(node, $2); } | T_LIST_OPEN expression T_LIST_CLOSE %prec INDEXED { // indexed variable access, continuation from * expansion, e.g. [*].variable[index] auto node = static_cast(parser->popStack()); $$ = parser->ast()->createNodeIndexedAccess(node, $2); } | expansion '.' T_STRING %prec REFERENCE { // named variable access, continuation from * expansion, e.g. [*].variable.xx.reference $$ = parser->ast()->createNodeAttributeAccess($1, $3); } | expansion '.' bind_parameter %prec REFERENCE { // named variable access w/ bind parameter, continuation from * expansion, e.g. [*].variable.xx.@reference $$ = parser->ast()->createNodeBoundAttributeAccess($1, $3); } | expansion T_LIST_OPEN expression T_LIST_CLOSE %prec INDEXED { // indexed variable access, continuation from * expansion, e.g. [*].variable.xx.[index] $$ = parser->ast()->createNodeIndexedAccess($1, $3); } ; atomic_value: value_literal { $$ = $1; } | bind_parameter { $$ = $1; } ; numeric_value: integer_value { $$ = $1; } | T_DOUBLE { if ($1 == nullptr) { ABORT_OOM } double value = TRI_DoubleString($1); if (TRI_errno() != TRI_ERROR_NO_ERROR) { parser->registerParseError(TRI_ERROR_QUERY_NUMBER_OUT_OF_RANGE, TRI_errno_string(TRI_ERROR_QUERY_NUMBER_OUT_OF_RANGE), yylloc.first_line, yylloc.first_column); } $$ = parser->ast()->createNodeValueDouble(value); }; value_literal: T_QUOTED_STRING { $$ = parser->ast()->createNodeValueString($1); } | numeric_value { $$ = $1; } | T_NULL { $$ = parser->ast()->createNodeValueNull(); } | T_TRUE { $$ = parser->ast()->createNodeValueBool(true); } | T_FALSE { $$ = parser->ast()->createNodeValueBool(false); } ; collection_name: T_STRING { if ($1 == nullptr) { ABORT_OOM } $$ = parser->ast()->createNodeCollection($1); } | T_QUOTED_STRING { if ($1 == nullptr) { ABORT_OOM } $$ = parser->ast()->createNodeCollection($1); } | T_PARAMETER { if ($1 == nullptr) { ABORT_OOM } if (strlen($1) < 2 || $1[0] != '@') { parser->registerParseError(TRI_ERROR_QUERY_BIND_PARAMETER_TYPE, TRI_errno_string(TRI_ERROR_QUERY_BIND_PARAMETER_TYPE), $1, yylloc.first_line, yylloc.first_column); } $$ = parser->ast()->createNodeParameter($1); } ; bind_parameter: T_PARAMETER { $$ = parser->ast()->createNodeParameter($1); } ; array_element_name: T_STRING { if ($1 == nullptr) { ABORT_OOM } $$ = $1; } | T_QUOTED_STRING { if ($1 == nullptr) { ABORT_OOM } $$ = $1; } variable_name: T_STRING { $$ = $1; } ; integer_value: T_INTEGER { if ($1 == nullptr) { ABORT_OOM } int64_t value = TRI_Int64String($1); if (TRI_errno() != TRI_ERROR_NO_ERROR) { parser->registerParseError(TRI_ERROR_QUERY_NUMBER_OUT_OF_RANGE, TRI_errno_string(TRI_ERROR_QUERY_NUMBER_OUT_OF_RANGE), yylloc.first_line, yylloc.first_column); } $$ = parser->ast()->createNodeValueInt(value); } ;