mirror of https://gitee.com/bigwinds/arangodb
342 lines
11 KiB
C
342 lines
11 KiB
C
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief Ahuacatl, statement list walking
|
|
///
|
|
/// @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 "Ahuacatl/ahuacatl-statement-walker.h"
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- statement list walker
|
|
// -----------------------------------------------------------------------------
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- private functions
|
|
// -----------------------------------------------------------------------------
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @addtogroup Ahuacatl
|
|
/// @{
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief process a statement
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void VisitStatement (TRI_aql_statement_walker_t* const walker,
|
|
const size_t position,
|
|
TRI_aql_visit_f func) {
|
|
TRI_aql_node_t* node;
|
|
TRI_aql_node_t* modified;
|
|
|
|
node = (TRI_aql_node_t*) TRI_AtVectorPointer(&walker->_statements->_statements, position);
|
|
assert(node);
|
|
|
|
modified = func(walker, node);
|
|
if (walker->_canModify && modified != node) {
|
|
if (modified == NULL) {
|
|
modified = TRI_GetDummyNopNodeAql();
|
|
}
|
|
|
|
walker->_statements->_statements._buffer[position] = modified;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief process a node's members
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void VisitMembers (TRI_aql_statement_walker_t* const walker,
|
|
TRI_aql_node_t* const node) {
|
|
size_t i, n;
|
|
|
|
assert(node);
|
|
|
|
n = node->_members._length;
|
|
for (i = 0; i < n; ++i) {
|
|
TRI_aql_node_t* member;
|
|
TRI_aql_node_t* modified;
|
|
|
|
member = (TRI_aql_node_t*) TRI_AtVectorPointer(&node->_members, i);
|
|
|
|
if (!member) {
|
|
continue;
|
|
}
|
|
|
|
VisitMembers(walker, member);
|
|
|
|
modified = walker->visitMember(walker, member);
|
|
if (walker->_canModify && modified != member) {
|
|
if (modified == NULL) {
|
|
modified = TRI_GetDummyNopNodeAql();
|
|
}
|
|
|
|
node->_members._buffer[i] = modified;
|
|
}
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief actually walk the statements in the list
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void RunWalk (TRI_aql_statement_walker_t* const walker) {
|
|
size_t i, n;
|
|
|
|
assert(walker);
|
|
n = walker->_statements->_statements._length;
|
|
|
|
for (i = 0; i < n; ++i) {
|
|
TRI_aql_node_t* node;
|
|
TRI_aql_node_type_e nodeType;
|
|
|
|
node = (TRI_aql_node_t*) TRI_AtVectorPointer(&walker->_statements->_statements, i);
|
|
|
|
if (!node) {
|
|
continue;
|
|
}
|
|
|
|
nodeType = node->_type;
|
|
|
|
// handle scopes
|
|
if (nodeType == TRI_AQL_NODE_SCOPE_START) {
|
|
TRI_PushBackVectorPointer(&walker->_currentScopes, TRI_AQL_NODE_DATA(node));
|
|
}
|
|
|
|
// preprocess node
|
|
if (walker->preVisitStatement != NULL) {
|
|
// this might change the node ptr
|
|
VisitStatement(walker, i, walker->preVisitStatement);
|
|
node = walker->_statements->_statements._buffer[i];
|
|
}
|
|
|
|
|
|
// process node's members
|
|
if (walker->visitMember != NULL) {
|
|
VisitMembers(walker, node);
|
|
}
|
|
|
|
// post process node
|
|
if (walker->postVisitStatement != NULL) {
|
|
VisitStatement(walker, i, walker->postVisitStatement);
|
|
}
|
|
|
|
if (nodeType == TRI_AQL_NODE_SCOPE_END) {
|
|
size_t len = walker->_currentScopes._length;
|
|
|
|
assert(len > 0);
|
|
TRI_RemoveVectorPointer(&walker->_currentScopes, len - 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @}
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- public functions
|
|
// -----------------------------------------------------------------------------
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @addtogroup Ahuacatl
|
|
/// @{
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief get current ranges in top scope of statement walker
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
TRI_vector_pointer_t* TRI_GetCurrentRangesStatementWalkerAql (TRI_aql_statement_walker_t* const walker) {
|
|
TRI_aql_scope_t* scope = TRI_GetCurrentScopeStatementWalkerAql(walker);
|
|
|
|
assert(scope);
|
|
|
|
return scope->_ranges;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief set current ranges in top scope of statement walker
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void TRI_SetCurrentRangesStatementWalkerAql (TRI_aql_statement_walker_t* const walker,
|
|
TRI_vector_pointer_t* ranges) {
|
|
TRI_aql_scope_t* scope = TRI_GetCurrentScopeStatementWalkerAql(walker);
|
|
|
|
assert(scope);
|
|
|
|
if (ranges != NULL) { // ranges may be NULL
|
|
TRI_vector_pointer_t* oldRanges = scope->_ranges;
|
|
|
|
if (oldRanges != NULL) {
|
|
// free old value
|
|
TRI_FreeAccessesAql(oldRanges);
|
|
}
|
|
|
|
// set to new value
|
|
scope->_ranges = ranges;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief get current top scope of statement walker
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
TRI_aql_scope_t* TRI_GetCurrentScopeStatementWalkerAql (TRI_aql_statement_walker_t* const walker) {
|
|
size_t n;
|
|
|
|
assert(walker);
|
|
n = walker->_currentScopes._length;
|
|
|
|
assert(n > 0);
|
|
|
|
return (TRI_aql_scope_t*) TRI_AtVectorPointer(&walker->_currentScopes, n - 1);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief get current scopes of statement walker
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
TRI_vector_pointer_t* TRI_GetScopesStatementWalkerAql (TRI_aql_statement_walker_t* const walker) {
|
|
assert(walker);
|
|
|
|
return &walker->_currentScopes;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief return a pointer to a variable, identified by its name
|
|
///
|
|
/// The variable will be searched in the current and the surrounding scopes.
|
|
/// If the variable is not found and the reference to it is coming from a
|
|
/// subquery scope, we'll mark the subquery as not self-contained. This prevents
|
|
/// moving of the subquery to some other position (which would break the query
|
|
/// in this case)
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
TRI_aql_variable_t* TRI_GetVariableStatementWalkerAql (TRI_aql_statement_walker_t* const walker,
|
|
const char* const name,
|
|
size_t* scopeCount) {
|
|
size_t n;
|
|
|
|
// init scope counter to 0
|
|
*scopeCount = 0;
|
|
|
|
assert(name != NULL);
|
|
|
|
n = walker->_currentScopes._length;
|
|
while (n > 0) {
|
|
TRI_aql_scope_t* scope;
|
|
TRI_aql_variable_t* variable;
|
|
|
|
scope = (TRI_aql_scope_t*) TRI_AtVectorPointer(&walker->_currentScopes, --n);
|
|
assert(scope);
|
|
|
|
variable = TRI_LookupByKeyAssociativePointer(&scope->_variables, (void*) name);
|
|
if (variable != NULL) {
|
|
return variable;
|
|
}
|
|
|
|
if (n == 0 || scope->_type == TRI_AQL_SCOPE_SUBQUERY) {
|
|
// reached the outermost scope
|
|
if (scope->_type == TRI_AQL_SCOPE_SUBQUERY) {
|
|
// variable not found but we reached the end of the scope
|
|
// we must mark the scope as not self-contained so it is not moved to
|
|
// some other position
|
|
|
|
scope->_selfContained = false;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
// increase the scope counter
|
|
(*scopeCount)++;
|
|
}
|
|
|
|
// variable not found
|
|
return NULL;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief create a statement walker
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
TRI_aql_statement_walker_t* TRI_CreateStatementWalkerAql (void* data,
|
|
const bool canModify,
|
|
TRI_aql_visit_f visitMember,
|
|
TRI_aql_visit_f preVisitStatement,
|
|
TRI_aql_visit_f postVisitStatement) {
|
|
TRI_aql_statement_walker_t* walker;
|
|
|
|
walker = (TRI_aql_statement_walker_t*) TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_aql_statement_walker_t), false);
|
|
if (walker == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
walker->_data = data;
|
|
walker->_canModify = canModify;
|
|
|
|
walker->visitMember = visitMember;
|
|
walker->preVisitStatement = preVisitStatement;
|
|
walker->postVisitStatement = postVisitStatement;
|
|
|
|
TRI_InitVectorPointer(&walker->_currentScopes, TRI_UNKNOWN_MEM_ZONE);
|
|
|
|
return walker;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief free a statement walker
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void TRI_FreeStatementWalkerAql (TRI_aql_statement_walker_t* const walker) {
|
|
assert(walker);
|
|
|
|
TRI_DestroyVectorPointer(&walker->_currentScopes);
|
|
|
|
TRI_Free(TRI_UNKNOWN_MEM_ZONE, walker);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief run the statement list walk
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void TRI_WalkStatementsAql (TRI_aql_statement_walker_t* const walker,
|
|
TRI_aql_statement_list_t* const list) {
|
|
assert(walker);
|
|
assert(list);
|
|
|
|
walker->_statements = list;
|
|
|
|
RunWalk(walker);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @}
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Local Variables:
|
|
// mode: outline-minor
|
|
// outline-regexp: "^\\(/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|// --SECTION--\\|/// @\\}\\)"
|
|
// End:
|