//////////////////////////////////////////////////////////////////////////////// /// @brief Ahuacatl, scopes /// /// @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-scope.h" #include "Ahuacatl/ahuacatl-access-optimiser.h" #include "Ahuacatl/ahuacatl-variable.h" // ----------------------------------------------------------------------------- // --SECTION-- private functions // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @addtogroup Ahuacatl /// @{ //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// @brief get the next scope id //////////////////////////////////////////////////////////////////////////////// static inline size_t NextId (TRI_aql_context_t* const context) { return ++context->_scopeIndex; } //////////////////////////////////////////////////////////////////////////////// /// @brief get the current scope //////////////////////////////////////////////////////////////////////////////// static inline TRI_aql_scope_t* CurrentScope (TRI_aql_context_t* const context) { TRI_aql_scope_t* scope; size_t n; assert(context); n = context->_currentScopes._length; assert(n > 0); scope = (TRI_aql_scope_t*) TRI_AtVectorPointer(&context->_currentScopes, n - 1); assert(scope); return scope; } //////////////////////////////////////////////////////////////////////////////// /// @brief get the current type of a scope //////////////////////////////////////////////////////////////////////////////// static inline TRI_aql_scope_e CurrentType (TRI_aql_context_t* const context) { return CurrentScope(context)->_type; } //////////////////////////////////////////////////////////////////////////////// /// @brief get the type for the next scope /// /// this returns TRI_AQL_SCOPE_FOR_NESTED for a nested for scope, and the /// originally requested type in all other cases //////////////////////////////////////////////////////////////////////////////// static TRI_aql_scope_e NextType (TRI_aql_context_t* const context, const TRI_aql_scope_e requestedType) { TRI_aql_scope_e current; // we're only interested in TRI_AQL_SCOPE_FOR if (requestedType != TRI_AQL_SCOPE_FOR) { return requestedType; } current = CurrentType(context); // if we are in a TRI_AQL_SCOPE_FOR scope, we'll return TRI_AQL_SCOPE_FOR_NESTED if (current == TRI_AQL_SCOPE_FOR || current == TRI_AQL_SCOPE_FOR_NESTED) { return TRI_AQL_SCOPE_FOR_NESTED; } return requestedType; } //////////////////////////////////////////////////////////////////////////////// /// @brief create a scope //////////////////////////////////////////////////////////////////////////////// static TRI_aql_scope_t* CreateScope (TRI_aql_context_t* const context, const TRI_aql_scope_e type) { TRI_aql_scope_t* scope; assert(context); scope = (TRI_aql_scope_t*) TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_aql_scope_t), false); if (scope == NULL) { return NULL; } scope->_id = NextId(context); scope->_type = NextType(context, type); scope->_ranges = NULL; scope->_selfContained = true; TRI_InitAssociativePointer(&scope->_variables, TRI_UNKNOWN_MEM_ZONE, &TRI_HashStringKeyAssociativePointer, &TRI_HashVariableAql, &TRI_EqualVariableAql, 0); return scope; } //////////////////////////////////////////////////////////////////////////////// /// @brief free a scope //////////////////////////////////////////////////////////////////////////////// static void FreeScope (TRI_aql_scope_t* const scope) { size_t i, n; // free variables lookup hash n = scope->_variables._nrAlloc; for (i = 0; i < n; ++i) { TRI_aql_variable_t* variable = scope->_variables._table[i]; if (variable) { TRI_FreeVariableAql(variable); } } TRI_DestroyAssociativePointer(&scope->_variables); if (scope->_ranges) { // free ranges if set TRI_FreeAccessesAql(scope->_ranges); } TRI_Free(TRI_UNKNOWN_MEM_ZONE, scope); } //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// // ----------------------------------------------------------------------------- // --SECTION-- public functions // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @addtogroup Ahuacatl /// @{ //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// @brief get the name of a scope type //////////////////////////////////////////////////////////////////////////////// const char* TRI_TypeNameScopeAql (const TRI_aql_scope_e type) { switch (type) { case TRI_AQL_SCOPE_FOR: return "for"; case TRI_AQL_SCOPE_FOR_NESTED: return "for nested"; case TRI_AQL_SCOPE_SUBQUERY: return "subquery"; case TRI_AQL_SCOPE_EXPAND: return "expand"; case TRI_AQL_SCOPE_MAIN: return "main"; case TRI_AQL_SCOPE_FUNCTION: return "function"; } return "unknown"; } //////////////////////////////////////////////////////////////////////////////// /// @brief init scopes //////////////////////////////////////////////////////////////////////////////// bool TRI_InitScopesAql (TRI_aql_context_t* const context) { assert(context); TRI_InitVectorPointer(&context->_memory._scopes, TRI_UNKNOWN_MEM_ZONE); TRI_InitVectorPointer(&context->_currentScopes, TRI_UNKNOWN_MEM_ZONE); return true; } //////////////////////////////////////////////////////////////////////////////// /// @brief free all scopes //////////////////////////////////////////////////////////////////////////////// void TRI_FreeScopesAql (TRI_aql_context_t* const context) { size_t i, n; assert(context); n = context->_memory._scopes._length; for (i = 0; i < n; ++i) { TRI_aql_scope_t* scope = (TRI_aql_scope_t*) TRI_AtVectorPointer(&context->_memory._scopes, i); FreeScope(scope); } TRI_DestroyVectorPointer(&context->_memory._scopes); TRI_DestroyVectorPointer(&context->_currentScopes); } //////////////////////////////////////////////////////////////////////////////// /// @brief add a new scope //////////////////////////////////////////////////////////////////////////////// bool TRI_StartScopeAql (TRI_aql_context_t* const context, const TRI_aql_scope_e type) { TRI_aql_scope_t* scope; TRI_aql_node_t* node; assert(context); scope = CreateScope(context, type); if (scope == NULL) { return false; } LOG_TRACE("starting scope of type %s", TRI_TypeNameScopeAql(scope->_type)); TRI_PushBackVectorPointer(&context->_memory._scopes, (void*) scope); TRI_PushBackVectorPointer(&context->_currentScopes, (void*) scope); node = TRI_CreateNodeScopeStartAql(context, scope); if (node == NULL) { return false; } if (! TRI_AppendStatementListAql(context->_statements, node)) { return false; } return true; } //////////////////////////////////////////////////////////////////////////////// /// @brief end the current scope, only 1 //////////////////////////////////////////////////////////////////////////////// bool TRI_EndScopeAql (TRI_aql_context_t* const context) { TRI_aql_scope_t* scope; TRI_aql_node_t* node; size_t n; assert(context); n = context->_currentScopes._length; assert(n > 0); scope = TRI_RemoveVectorPointer(&context->_currentScopes, --n); LOG_TRACE("closing scope of type %s", TRI_TypeNameScopeAql(scope->_type)); node = TRI_CreateNodeScopeEndAql(context, scope); if (node == NULL) { return false; } if (! TRI_AppendStatementListAql(context->_statements, node)) { return false; } return true; } //////////////////////////////////////////////////////////////////////////////// /// @brief end the current scopes, 0 .. n //////////////////////////////////////////////////////////////////////////////// bool TRI_EndScopeByReturnAql (TRI_aql_context_t* const context) { TRI_aql_scope_e type; TRI_aql_node_t* node; size_t n; assert(context); type = CurrentType(context); if (type == TRI_AQL_SCOPE_MAIN || type == TRI_AQL_SCOPE_SUBQUERY) { return true; } n = context->_currentScopes._length; assert(n > 0); while (n > 0) { TRI_aql_scope_t* scope; scope = (TRI_aql_scope_t*) TRI_RemoveVectorPointer(&context->_currentScopes, --n); assert(scope); LOG_TRACE("closing scope of type %s", TRI_TypeNameScopeAql(scope->_type)); node = TRI_CreateNodeScopeEndAql(context, scope); if (node == NULL) { return false; } if (! TRI_AppendStatementListAql(context->_statements, node)) { return false; } if (scope->_type != TRI_AQL_SCOPE_FOR_NESTED) { // removed enough scopes break; } } return true; } //////////////////////////////////////////////////////////////////////////////// /// @brief checks if a variable is defined in the current scope or above //////////////////////////////////////////////////////////////////////////////// bool TRI_VariableExistsScopeAql (TRI_aql_context_t* const context, const char* const name) { size_t n; if (name == NULL) { TRI_SetErrorContextAql(context, TRI_ERROR_OUT_OF_MEMORY, NULL); return false; } n = context->_currentScopes._length; assert(n > 0); while (n > 0) { TRI_aql_scope_t* scope = (TRI_aql_scope_t*) TRI_AtVectorPointer(&context->_currentScopes, --n); assert(scope); if (TRI_LookupByKeyAssociativePointer(&scope->_variables, (void*) name)) { // duplicate variable return true; } if (n == 0) { // reached the outermost scope break; } } return false; } //////////////////////////////////////////////////////////////////////////////// /// @brief push a variable into the current scope's symbol table //////////////////////////////////////////////////////////////////////////////// bool TRI_AddVariableScopeAql (TRI_aql_context_t* const context, const char* name, TRI_aql_node_t* const definingNode) { TRI_aql_variable_t* variable; TRI_aql_scope_t* scope; assert(context); assert(name); if (TRI_VariableExistsScopeAql(context, name)) { return false; } variable = TRI_CreateVariableAql(name, definingNode); if (variable == NULL) { return false; } scope = CurrentScope(context); assert(! TRI_InsertKeyAssociativePointer(&scope->_variables, variable->_name, (void*) variable, false)); return true; } //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// // Local Variables: // mode: outline-minor // outline-regexp: "^\\(/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|// --SECTION--\\|/// @\\}\\)" // End: