//////////////////////////////////////////////////////////////////////////////// /// @brief Aql, scopes /// /// @file /// /// DISCLAIMER /// /// Copyright 2014 ArangoDB GmbH, Cologne, Germany /// Copyright 2004-2014 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 ArangoDB GmbH, Cologne, Germany /// /// @author Jan Steemann /// @author Copyright 2014, ArangoDB GmbH, Cologne, Germany /// @author Copyright 2012-2013, triAGENS GmbH, Cologne, Germany //////////////////////////////////////////////////////////////////////////////// #include "Aql/Scopes.h" #include "Basics/Exceptions.h" using namespace triagens::aql; // ----------------------------------------------------------------------------- // --SECTION-- constructors / destructors // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @brief create the scope //////////////////////////////////////////////////////////////////////////////// Scope::Scope (ScopeType type) : _type(type), _variables() { } //////////////////////////////////////////////////////////////////////////////// /// @brief destroy the scope //////////////////////////////////////////////////////////////////////////////// Scope::~Scope () { } // ----------------------------------------------------------------------------- // --SECTION-- public functions // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @brief return the name of a scope type //////////////////////////////////////////////////////////////////////////////// std::string Scope::typeName () const { return std::move(typeName(_type)); } //////////////////////////////////////////////////////////////////////////////// /// @brief return the name of a scope type //////////////////////////////////////////////////////////////////////////////// std::string Scope::typeName (ScopeType type) { switch (type) { case AQL_SCOPE_MAIN: return "main"; case AQL_SCOPE_SUBQUERY: return "subquery"; case AQL_SCOPE_FOR: return "for"; case AQL_SCOPE_COLLECT: return "collection"; } TRI_ASSERT(false); return "unknown"; } //////////////////////////////////////////////////////////////////////////////// /// @brief adds a variable to the scope //////////////////////////////////////////////////////////////////////////////// void Scope::addVariable (Variable* variable) { _variables.emplace(std::make_pair(variable->name, variable)); } //////////////////////////////////////////////////////////////////////////////// /// @brief checks if a variable exists in the scope //////////////////////////////////////////////////////////////////////////////// bool Scope::existsVariable (char const* name, size_t nameLength) const { return (getVariable(name, nameLength) != nullptr); } //////////////////////////////////////////////////////////////////////////////// /// @brief checks if a variable exists in the scope //////////////////////////////////////////////////////////////////////////////// bool Scope::existsVariable (std::string const& name) const { return (getVariable(name) != nullptr); } //////////////////////////////////////////////////////////////////////////////// /// @brief returns a variable //////////////////////////////////////////////////////////////////////////////// Variable const* Scope::getVariable (char const* name, size_t nameLength) const { std::string const varname(name, nameLength); auto it = _variables.find(varname); if (it == _variables.end()) { return nullptr; } return (*it).second; } //////////////////////////////////////////////////////////////////////////////// /// @brief returns a variable //////////////////////////////////////////////////////////////////////////////// Variable const* Scope::getVariable (std::string const& name) const { auto it = _variables.find(name); if (it == _variables.end()) { return nullptr; } return (*it).second; } //////////////////////////////////////////////////////////////////////////////// /// @brief return a variable, allowing usage of special pseudo vars such /// as OLD and NEW //////////////////////////////////////////////////////////////////////////////// Variable const* Scope::getVariable (char const* name, size_t nameLength, bool allowSpecial) const { auto variable = getVariable(name, nameLength); if (variable == nullptr && allowSpecial) { // variable does not exist // now try variable aliases OLD (= $OLD) and NEW (= $NEW) if (strcmp(name, "OLD") == 0) { variable = getVariable(TRI_CHAR_LENGTH_PAIR(Variable::NAME_OLD)); } else if (strcmp(name, "NEW") == 0) { variable = getVariable(TRI_CHAR_LENGTH_PAIR(Variable::NAME_NEW)); } } return variable; } // ----------------------------------------------------------------------------- // --SECTION-- constructors / destructors // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @brief create the scopes //////////////////////////////////////////////////////////////////////////////// Scopes::Scopes () : _activeScopes(), _currentVariables() { _activeScopes.reserve(4); } //////////////////////////////////////////////////////////////////////////////// /// @brief destroy the scopes //////////////////////////////////////////////////////////////////////////////// Scopes::~Scopes () { for (auto& it : _activeScopes) { delete it; } } // ----------------------------------------------------------------------------- // --SECTION-- public functions // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @brief start a new scope //////////////////////////////////////////////////////////////////////////////// void Scopes::start (ScopeType type) { std::unique_ptr scope(new Scope(type)); _activeScopes.emplace_back(scope.get()); scope.release(); } //////////////////////////////////////////////////////////////////////////////// /// @brief end the current scope //////////////////////////////////////////////////////////////////////////////// void Scopes::endCurrent () { TRI_ASSERT(! _activeScopes.empty()); Scope* scope = _activeScopes.back(); TRI_ASSERT(scope != nullptr); _activeScopes.pop_back(); delete scope; } //////////////////////////////////////////////////////////////////////////////// /// @brief end the current scope plus any FOR scopes it is nested in //////////////////////////////////////////////////////////////////////////////// void Scopes::endNested () { TRI_ASSERT(! _activeScopes.empty()); int iterations = 0; while (! _activeScopes.empty()) { ++iterations; auto scope = _activeScopes.back(); TRI_ASSERT(scope != nullptr); ScopeType type = scope->type(); if (type == AQL_SCOPE_MAIN || type == AQL_SCOPE_SUBQUERY) { // main and subquery scopes cannot be closed here break; } if (type != AQL_SCOPE_FOR && iterations >= 2) { // if nested, do not close anything but for scopes break; } endCurrent(); } } //////////////////////////////////////////////////////////////////////////////// /// @brief adds a variable to the current scope //////////////////////////////////////////////////////////////////////////////// void Scopes::addVariable (Variable* variable) { TRI_ASSERT(! _activeScopes.empty()); TRI_ASSERT(variable != nullptr); for (auto it = _activeScopes.rbegin(); it != _activeScopes.rend(); ++it) { auto scope = (*it); if (scope->existsVariable(variable->name)) { // duplicate variable name THROW_ARANGO_EXCEPTION_PARAMS(TRI_ERROR_QUERY_VARIABLE_REDECLARED, variable->name.c_str()); } } // if this fails, there won't be a memleak _activeScopes.back()->addVariable(variable); } //////////////////////////////////////////////////////////////////////////////// /// @brief checks whether a variable exists in any scope //////////////////////////////////////////////////////////////////////////////// bool Scopes::existsVariable (char const* name, size_t nameLength) const { return (getVariable(name, nameLength) != nullptr); } //////////////////////////////////////////////////////////////////////////////// /// @brief return a variable by name - this respects the current scopes //////////////////////////////////////////////////////////////////////////////// Variable const* Scopes::getVariable (char const* name, size_t nameLength) const { TRI_ASSERT(! _activeScopes.empty()); for (auto it = _activeScopes.rbegin(); it != _activeScopes.rend(); ++it) { auto variable = (*it)->getVariable(name, nameLength); if (variable != nullptr) { return variable; } } return nullptr; } //////////////////////////////////////////////////////////////////////////////// /// @brief return a variable by name - this respects the current scopes //////////////////////////////////////////////////////////////////////////////// Variable const* Scopes::getVariable (std::string const& name) const { TRI_ASSERT(! _activeScopes.empty()); for (auto it = _activeScopes.rbegin(); it != _activeScopes.rend(); ++it) { auto variable = (*it)->getVariable(name); if (variable != nullptr) { return variable; } } return nullptr; } //////////////////////////////////////////////////////////////////////////////// /// @brief return a variable by name - this respects the current scopes //////////////////////////////////////////////////////////////////////////////// Variable const* Scopes::getVariable (char const* name, size_t nameLength, bool allowSpecial) const { TRI_ASSERT(! _activeScopes.empty()); for (auto it = _activeScopes.rbegin(); it != _activeScopes.rend(); ++it) { auto variable = (*it)->getVariable(name, nameLength, allowSpecial); if (variable != nullptr) { return variable; } } return nullptr; } //////////////////////////////////////////////////////////////////////////////// /// @brief get the $CURRENT variable //////////////////////////////////////////////////////////////////////////////// Variable const* Scopes::getCurrentVariable () const { if (_currentVariables.empty()) { THROW_ARANGO_EXCEPTION_PARAMS(TRI_ERROR_QUERY_VARIABLE_NAME_UNKNOWN, Variable::NAME_CURRENT); } auto result = _currentVariables.back(); TRI_ASSERT(result != nullptr); return result; } //////////////////////////////////////////////////////////////////////////////// /// @brief stack a $CURRENT variable from the stack //////////////////////////////////////////////////////////////////////////////// void Scopes::stackCurrentVariable (Variable const* variable) { _currentVariables.emplace_back(variable); } //////////////////////////////////////////////////////////////////////////////// /// @brief unregister the $CURRENT variable from the stack //////////////////////////////////////////////////////////////////////////////// void Scopes::unstackCurrentVariable () { TRI_ASSERT(! _currentVariables.empty()); _currentVariables.pop_back(); } // ----------------------------------------------------------------------------- // --SECTION-- END-OF-FILE // ----------------------------------------------------------------------------- // Local Variables: // mode: outline-minor // outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @page\\|// --SECTION--\\|/// @\\}" // End: