1
0
Fork 0
arangodb/QL/parser-context.c

388 lines
12 KiB
C

////////////////////////////////////////////////////////////////////////////////
/// @brief parser context for flex-based query scanner
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2010-2012 triagens GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is triAGENS GmbH, Cologne, Germany
///
/// @author Jan Steemann
/// @author Copyright 2012, triagens GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////
#include "QL/ast-node.h"
#include "QL/parser-context.h"
////////////////////////////////////////////////////////////////////////////////
/// @addtogroup QL
/// @{
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief initializes the parser context for a query
////////////////////////////////////////////////////////////////////////////////
bool QLParseInit (QL_parser_context_t *context, const char *query) {
// init vectors needed for book-keeping memory
TRI_InitVectorPointer(&context->_nodes);
TRI_InitVectorPointer(&context->_strings);
TRI_InitVectorPointer(&context->_listHeads);
TRI_InitVectorPointer(&context->_listTails);
// set parse stage
context->_stage = STAGE_PARSE;
// init lexer/scanner
QLlex_init(&context->_scanner);
QLset_extra(context, context->_scanner);
// init query data
context->_lexState._length = strlen(query);
context->_lexState._buffer = (char *) query;
// reset position and error message
context->_lexState._errorState._message = "";
context->_lexState._errorState._line = 1;
context->_lexState._errorState._column = 1;
// init query structure
context->_query = (QL_ast_query_t *) TRI_Allocate(sizeof(QL_ast_query_t));
if (!context->_query) {
return false;
}
QLAstQueryInit(context->_query);
return true;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief free all memory allocated in context of a query
////////////////////////////////////////////////////////////////////////////////
void QLParseFree (QL_parser_context_t *context) {
size_t i;
void *nodePtr;
char *stringPtr;
// nodes
i = context->_nodes._length;
// free all nodes in vector, starting at the end (prevents copying the remaining elements in vector)
while (i > 0) {
i--;
nodePtr = TRI_RemoveVectorPointer(&context->_nodes, i);
QLParseFreeNode(nodePtr);
if (i == 0) {
break;
}
}
// strings
i = context->_strings._length;
// free all strings in vector, starting at the end (prevents copying the remaining elements in vector)
while (i > 0) {
i--;
stringPtr = TRI_RemoveVectorPointer(&context->_strings, i);
QLParseFreeString(stringPtr);
if (i == 0) {
break;
}
}
// list elements in _listHeads and _listTails must not be freed separately as they are AstNodes handled
// by _nodes already
// free vectors themselves
TRI_DestroyVectorPointer(&context->_strings);
TRI_DestroyVectorPointer(&context->_nodes);
TRI_DestroyVectorPointer(&context->_listHeads);
TRI_DestroyVectorPointer(&context->_listTails);
// free lexer/scanner
QLlex_destroy(context->_scanner);
// free collections array in query
QLAstQueryFree(context->_query);
// free query struct itself
if (context->_query != 0) {
TRI_Free(context->_query);
context->_query = 0;
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief keep track of an allocated ast node
////////////////////////////////////////////////////////////////////////////////
void QLParseRegisterNode (QL_parser_context_t *context, QL_ast_node_t *element) {
TRI_PushBackVectorPointer(&context->_nodes, element);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief free an ast node
////////////////////////////////////////////////////////////////////////////////
void QLParseFreeNode (QL_ast_node_t *element) {
if (element != 0) {
TRI_Free(element);
element = 0;
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief copies a string and keeps track of its memory location in a vector
////////////////////////////////////////////////////////////////////////////////
char *QLParseAllocString (QL_parser_context_t *context, const char *string) {
// do string duplication
char *copy = TRI_DuplicateString(string);
// store pointer to copy
return QLParseRegisterString(context, copy);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief copies a string part and keeps track of its memory location in a vector
////////////////////////////////////////////////////////////////////////////////
char *QLParseAllocString2 (QL_parser_context_t *context, const char *string, const size_t length) {
// do string part duplication
char *copy = TRI_DuplicateString2(string, length);
// store pointer to copy and return it
return QLParseRegisterString(context, copy);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief keep track of an allocated string
////////////////////////////////////////////////////////////////////////////////
char *QLParseRegisterString (QL_parser_context_t *context, const char *string) {
TRI_PushBackVectorPointer(&context->_strings, (char *) string);
return (char*) string;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief free a string
////////////////////////////////////////////////////////////////////////////////
void QLParseFreeString (char *string) {
if (string != 0) {
TRI_Free(string);
string = 0;
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief create a new node for the ast
////////////////////////////////////////////////////////////////////////////////
QL_ast_node_t *QLAstNodeCreate (QL_parser_context_t *context, const QL_ast_node_type_e type) {
// allocate memory
QL_ast_node_t *node = (QL_ast_node_t *) TRI_Allocate(sizeof(QL_ast_node_t));
if (node == 0) {
return 0;
}
// keep track of memory location
QLParseRegisterNode(context, node);
// set node type
node->_type = type;
// set initial pointers to 0
node->_value._stringValue = 0;
node->_lhs = 0;
node->_rhs = 0;
node->_next = 0;
// set position information
node->_line = QLget_lineno(context->_scanner);
node->_column = QLget_column(context->_scanner);
return node;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief open a new context layer for the parser
////////////////////////////////////////////////////////////////////////////////
void QLParseContextPush (QL_parser_context_t *context, QL_ast_node_t *element) {
TRI_PushBackVectorPointer(&context->_listHeads, element);
TRI_PushBackVectorPointer(&context->_listTails, element);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief close the current context layer of the parser and return it
////////////////////////////////////////////////////////////////////////////////
QL_ast_node_t *QLParseContextPop (QL_parser_context_t *context) {
QL_ast_node_t *head;
size_t i;
i = context->_listHeads._length;
if (i > 0) {
TRI_RemoveVectorPointer(&context->_listTails, i - 1);
head = TRI_RemoveVectorPointer(&context->_listHeads, i - 1);
return head;
}
return 0;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief add an element to the current parsing context
////////////////////////////////////////////////////////////////////////////////
void QLParseContextAddElement (QL_parser_context_t *context, QL_ast_node_t *element) {
QL_ast_node_t *last;
size_t i;
i = context->_listTails._length;
if (i > 0) {
last = *(context->_listTails._buffer + i -1);
if (last != 0) {
last->_next = element;
*(context->_listTails._buffer + i -1) = element;
}
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief pop the current parse context from the stack into the rhs element
////////////////////////////////////////////////////////////////////////////////
void QLPopIntoRhs (QL_ast_node_t *node, QL_parser_context_t *context) {
QL_ast_node_t *popped;
popped = QLParseContextPop(context);
if (node != 0) {
node->_rhs = popped;
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief Register a parse error
////////////////////////////////////////////////////////////////////////////////
void QLParseRegisterParseError (QL_parser_context_t *context, const QL_error_type_e errorCode, ...) {
va_list args;
// set line and column numbers automatically during parsing
context->_lexState._errorState._line = QLget_lineno(context->_scanner);
context->_lexState._errorState._column = QLget_column(context->_scanner);
context->_lexState._errorState._code = errorCode;
va_start(args, errorCode);
context->_lexState._errorState._message = QLParseAllocString(context, QLErrorFormat(errorCode, args));
va_end(args);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief Register a post-parse error
////////////////////////////////////////////////////////////////////////////////
void QLParseRegisterPostParseError (QL_parser_context_t *context, const int32_t line,
const int32_t column, const QL_error_type_e errorCode, ...) {
va_list args;
context->_lexState._errorState._line = line;
context->_lexState._errorState._column = column;
context->_lexState._errorState._code = errorCode;
va_start(args, errorCode);
context->_lexState._errorState._message = QLParseAllocString(context, QLErrorFormat(errorCode, args));
va_end(args);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief Validate the query
////////////////////////////////////////////////////////////////////////////////
bool QLParseValidate (QL_parser_context_t *context, QL_ast_node_t *node) {
QL_ast_node_t *lhs, *rhs, *next;
QL_ast_node_type_e type;
if (node == 0) {
return true;
}
type = node->_type;
if (type == QLNodeContainerList) {
next = node->_next;
while (next) {
if (!QLParseValidate(context, next)) {
return false;
}
next = next->_next;
}
}
if (node->_type == QLNodeReferenceCollectionAlias) {
if (!QLAstQueryIsValidAlias(context->_query, node->_value._stringValue)) {
QLParseRegisterPostParseError(context, node->_line, node->_column,
ERR_COLLECTION_NAME_UNDECLARED, node->_value._stringValue);
return false;
}
}
lhs = node->_lhs;
if (lhs != 0) {
if (!QLParseValidate(context, lhs)) {
return false;
}
}
rhs = node->_rhs;
if (rhs != 0) {
if (!QLParseValidate(context, rhs)) {
return false;
}
}
return true;
}
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////
// Local Variables:
// mode: outline-minor
// outline-regexp: "^\\(/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|// --SECTION--\\|/// @\\}\\)"
// End: