1
0
Fork 0
arangodb/Ahuacatl/ahuacatl-codegen.c

2199 lines
80 KiB
C

////////////////////////////////////////////////////////////////////////////////
/// @brief Ahuacatl, AST dump functions
///
/// @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 <BasicsC/logging.h>
#include "Ahuacatl/ahuacatl-access-optimiser.h"
#include "Ahuacatl/ahuacatl-codegen.h"
#include "Ahuacatl/ahuacatl-collections.h"
#include "Ahuacatl/ahuacatl-index.h"
#include "Ahuacatl/ahuacatl-functions.h"
// -----------------------------------------------------------------------------
// --SECTION-- private macros
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @addtogroup Ahuacatl
/// @{
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief prefix for register
////////////////////////////////////////////////////////////////////////////////
#define REGISTER_PREFIX "r"
////////////////////////////////////////////////////////////////////////////////
/// @brief prefix for functions
////////////////////////////////////////////////////////////////////////////////
#define FUNCTION_PREFIX "f"
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////
static void ProcessNode (TRI_aql_codegen_js_t* const generator, const TRI_aql_node_t* const node);
// -----------------------------------------------------------------------------
// --SECTION-- private functions
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @addtogroup Ahuacatl
/// @{
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief hash a variable
////////////////////////////////////////////////////////////////////////////////
static uint64_t HashVariable (TRI_associative_pointer_t* array,
void const* element) {
TRI_aql_codegen_variable_t* variable = (TRI_aql_codegen_variable_t*) element;
return TRI_FnvHashString(variable->_name);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief comparison function used to determine variable equality
////////////////////////////////////////////////////////////////////////////////
static bool EqualVariable (TRI_associative_pointer_t* array,
void const* key,
void const* element) {
TRI_aql_codegen_variable_t* variable = (TRI_aql_codegen_variable_t*) element;
return TRI_EqualString(key, variable->_name);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief get the next register number
////////////////////////////////////////////////////////////////////////////////
static inline TRI_aql_codegen_register_t IncRegister (TRI_aql_codegen_js_t* const generator) {
return ++generator->_registerIndex;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief get the next function number
////////////////////////////////////////////////////////////////////////////////
static inline TRI_aql_codegen_register_t IncFunction (TRI_aql_codegen_js_t* const generator) {
return ++generator->_functionIndex;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief get the current scope
////////////////////////////////////////////////////////////////////////////////
static inline TRI_aql_codegen_scope_t* CurrentScope (TRI_aql_codegen_js_t* const generator) {
size_t n = generator->_scopes._length;
assert(n > 0);
return (TRI_aql_codegen_scope_t*) TRI_AtVectorPointer(&generator->_scopes, n - 1);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief append a string to the buffer
////////////////////////////////////////////////////////////////////////////////
static inline bool OutputString (TRI_string_buffer_t* const buffer,
const char* const value) {
if (!buffer) {
return true;
}
if (!value) {
return false;
}
return (TRI_AppendStringStringBuffer(buffer, value) == TRI_ERROR_NO_ERROR);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief append a single character to the buffer
////////////////////////////////////////////////////////////////////////////////
static inline bool OutputChar (TRI_string_buffer_t* const buffer,
const char value) {
if (!buffer) {
return true;
}
return (TRI_AppendCharStringBuffer(buffer, value) == TRI_ERROR_NO_ERROR);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief append an unsigned integer value to the buffer
////////////////////////////////////////////////////////////////////////////////
static inline bool OutputUInt (TRI_string_buffer_t* const buffer,
const uint64_t value) {
if (!buffer) {
return true;
}
return (TRI_AppendUInt64StringBuffer(buffer, value) == TRI_ERROR_NO_ERROR);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief append an integer value to the buffer
////////////////////////////////////////////////////////////////////////////////
static inline bool OutputInt (TRI_string_buffer_t* const buffer,
const int64_t value) {
if (!buffer) {
return true;
}
return (TRI_AppendInt64StringBuffer(buffer, value) == TRI_ERROR_NO_ERROR);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief print a string in the current scope
////////////////////////////////////////////////////////////////////////////////
static inline void ScopeOutput (TRI_aql_codegen_js_t* const generator,
const char* const value) {
TRI_aql_codegen_scope_t* scope = CurrentScope(generator);
if (!OutputString(scope->_buffer, value)) {
generator->_error = true;
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief print an integer in the current scope
////////////////////////////////////////////////////////////////////////////////
static inline void ScopeOutputInt (TRI_aql_codegen_js_t* const generator,
const int64_t value) {
TRI_aql_codegen_scope_t* scope = CurrentScope(generator);
if (!OutputInt(scope->_buffer, value)) {
generator->_error = true;
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief print an unsgined integer in the current scope
////////////////////////////////////////////////////////////////////////////////
static inline void ScopeOutputUInt (TRI_aql_codegen_js_t* const generator,
const uint64_t value) {
TRI_aql_codegen_scope_t* scope = CurrentScope(generator);
if (!OutputUInt(scope->_buffer, value)) {
generator->_error = true;
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief print an escaped string in the current scope, enclosed by '
////////////////////////////////////////////////////////////////////////////////
static inline void ScopeOutputQuoted (TRI_aql_codegen_js_t* const generator,
const char* const value) {
TRI_aql_codegen_scope_t* scope = CurrentScope(generator);
if (!OutputChar(scope->_buffer, '\'')) {
generator->_error = true;
}
if (!OutputString(scope->_buffer, value)) {
generator->_error = true;
}
if (!OutputChar(scope->_buffer, '\'')) {
generator->_error = true;
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief print an escaped string in the current scope, enclosed by "
////////////////////////////////////////////////////////////////////////////////
static inline void ScopeOutputQuoted2 (TRI_aql_codegen_js_t* const generator,
const char* const value) {
TRI_aql_codegen_scope_t* scope = CurrentScope(generator);
char* escaped;
size_t outLength;
if (!OutputChar(scope->_buffer, '"')) {
generator->_error = true;
}
escaped = TRI_EscapeUtf8StringZ(TRI_UNKNOWN_MEM_ZONE, value, strlen(value), false, &outLength);
if (escaped) {
if (!OutputString(scope->_buffer, escaped)) {
generator->_error = true;
}
TRI_FreeString(TRI_UNKNOWN_MEM_ZONE, escaped);
}
else {
generator->_error = true;
}
if (!OutputChar(scope->_buffer, '"')) {
generator->_error = true;
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief print a JSON value in the current scope
////////////////////////////////////////////////////////////////////////////////
static inline void ScopeOutputJson (TRI_aql_codegen_js_t* const generator,
const TRI_json_t* const json) {
TRI_aql_codegen_scope_t* scope = CurrentScope(generator);
if (!scope->_buffer) {
return;
}
if (TRI_StringifyJson(scope->_buffer, json) != TRI_ERROR_NO_ERROR) {
generator->_error = true;
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief print an index id in the current scope
////////////////////////////////////////////////////////////////////////////////
static inline void ScopeOutputIndexId (TRI_aql_codegen_js_t* const generator,
const TRI_aql_collection_t* const collection,
const TRI_aql_index_t* const idx) {
ScopeOutput(generator, "\"");
ScopeOutputUInt(generator, collection->_collection->_cid);
ScopeOutput(generator, "/");
ScopeOutputUInt(generator, idx->_idx->_iid);
ScopeOutput(generator, "\"");
}
////////////////////////////////////////////////////////////////////////////////
/// @brief print a function name in the current scope
////////////////////////////////////////////////////////////////////////////////
static inline void ScopeOutputFunction (TRI_aql_codegen_js_t* const generator,
const TRI_aql_codegen_register_t functionIndex) {
TRI_aql_codegen_scope_t* scope = CurrentScope(generator);
if (!OutputString(scope->_buffer, FUNCTION_PREFIX)) {
generator->_error = true;
}
if (!OutputInt(scope->_buffer, (int64_t) functionIndex)) {
generator->_error = true;
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief print a register name in the current scope
////////////////////////////////////////////////////////////////////////////////
static inline void ScopeOutputRegister (TRI_aql_codegen_js_t* const generator,
const TRI_aql_codegen_register_t registerIndex) {
TRI_aql_codegen_scope_t* scope = CurrentScope(generator);
if (!OutputString(scope->_buffer, REGISTER_PREFIX)) {
generator->_error = true;
}
if (!OutputInt(scope->_buffer, (int64_t) registerIndex)) {
generator->_error = true;
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief generate code for indexed register access
////////////////////////////////////////////////////////////////////////////////
static void ScopeOutputIndexedName (TRI_aql_codegen_js_t* const generator,
const TRI_aql_codegen_register_t sourceRegister,
const char* const indexName) {
ScopeOutputRegister(generator, sourceRegister);
ScopeOutput(generator, "[");
ScopeOutputQuoted(generator, indexName);
ScopeOutput(generator, "]");
}
////////////////////////////////////////////////////////////////////////////////
/// @brief free a scope variable
////////////////////////////////////////////////////////////////////////////////
static void FreeVariable (TRI_aql_codegen_variable_t* const variable) {
assert(variable);
assert(variable->_name);
TRI_Free(TRI_UNKNOWN_MEM_ZONE, variable->_name);
TRI_Free(TRI_UNKNOWN_MEM_ZONE, variable);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief create a scope variable
////////////////////////////////////////////////////////////////////////////////
static TRI_aql_codegen_variable_t* CreateVariable (const char* const name,
const TRI_aql_codegen_register_t registerIndex) {
TRI_aql_codegen_variable_t* variable;
variable = (TRI_aql_codegen_variable_t*) TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_aql_codegen_variable_t), false);
if (!variable) {
return NULL;
}
variable->_register = registerIndex;
variable->_name = TRI_DuplicateString(name);
if (!variable->_name) {
TRI_Free(TRI_UNKNOWN_MEM_ZONE, variable);
return NULL;
}
return variable;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief get the type for the next scope
////////////////////////////////////////////////////////////////////////////////
static TRI_aql_codegen_scope_e NextScopeType (TRI_aql_codegen_js_t* const generator,
const TRI_aql_codegen_scope_e requestedType) {
TRI_aql_codegen_scope_t* scope;
// we're only interested in TRI_AQL_SCOPE_FOR
if (requestedType != TRI_AQL_SCOPE_FOR) {
return requestedType;
}
scope = CurrentScope(generator);
// if we are in a TRI_AQL_SCOPE_FOR scope, we'll return TRI_AQL_SCOPE_FOR_NESTED
if (scope->_type == TRI_AQL_SCOPE_FOR) {
return TRI_AQL_SCOPE_FOR_NESTED;
}
return requestedType;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief start a new scope and push it onto the stack
////////////////////////////////////////////////////////////////////////////////
static void StartScope (TRI_aql_codegen_js_t* const generator,
TRI_string_buffer_t* const buffer,
const TRI_aql_codegen_scope_e const type,
const TRI_aql_codegen_register_t listRegister,
const TRI_aql_codegen_register_t keyRegister,
const TRI_aql_codegen_register_t ownRegister,
const TRI_aql_codegen_register_t resultRegister,
const char* const name) {
TRI_aql_codegen_scope_t* scope;
scope = (TRI_aql_codegen_scope_t*) TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_aql_codegen_scope_t), false);
if (!scope) {
generator->_error = true;
return;
}
scope->_buffer = buffer;
scope->_type = NextScopeType(generator, type);
scope->_listRegister = listRegister;
scope->_keyRegister = keyRegister;
scope->_ownRegister = ownRegister;
scope->_resultRegister = resultRegister;
scope->_prefix = NULL;
#ifdef TRI_DEBUG_AQL
scope->_name = name;
#endif
// init symbol table
TRI_InitAssociativePointer(&scope->_variables,
TRI_UNKNOWN_MEM_ZONE,
&TRI_HashStringKeyAssociativePointer,
&HashVariable,
&EqualVariable,
NULL);
// push the scope on the stack
TRI_PushBackVectorPointer(&generator->_scopes, (void*) scope);
#ifdef TRI_DEBUG_AQL
ScopeOutput(generator, "\n/* scope start (");
ScopeOutput(generator, scope->_name);
ScopeOutput(generator, ") */\n");
#endif
}
////////////////////////////////////////////////////////////////////////////////
/// @brief end the current scope and pop it from the stack
////////////////////////////////////////////////////////////////////////////////
static void EndScope (TRI_aql_codegen_js_t* const generator) {
TRI_aql_codegen_scope_t* scope = CurrentScope(generator);
TRI_aql_codegen_register_t i, n;
#ifdef TRI_DEBUG_AQL
ScopeOutput(generator, "\n/* scope end (");
ScopeOutput(generator, scope->_name);
ScopeOutput(generator, ") */\n");
#endif
n = generator->_scopes._length;
assert(n > 0);
scope = (TRI_aql_codegen_scope_t*) TRI_RemoveVectorPointer(&generator->_scopes, n - 1);
assert(scope);
// free all variables in scope
n = scope->_variables._nrAlloc;
for (i = 0; i < n; ++i) {
TRI_aql_codegen_variable_t* variable = (TRI_aql_codegen_variable_t*) scope->_variables._table[i];
if (variable) {
FreeVariable(variable);
}
}
// free symbol table
TRI_DestroyAssociativePointer(&scope->_variables);
TRI_Free(TRI_UNKNOWN_MEM_ZONE, scope);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief generate code for a sort function
////////////////////////////////////////////////////////////////////////////////
static TRI_aql_codegen_register_t CreateSortFunction (TRI_aql_codegen_js_t* const generator,
const TRI_aql_node_t* const node,
const size_t elementIndex) {
TRI_aql_node_t* list;
TRI_aql_codegen_scope_t* scope;
TRI_aql_codegen_register_t functionIndex = IncFunction(generator);
size_t i;
size_t n;
// start a new scope first
StartScope(generator, &generator->_functionBuffer, TRI_AQL_SCOPE_FUNCTION, 0, 0, 0, 0, "sort");
scope = CurrentScope(generator);
ScopeOutput(generator, "function ");
ScopeOutputFunction(generator, functionIndex);
ScopeOutput(generator, " (l, r) {\n");
ScopeOutput(generator, "var res, lhs, rhs;\n");
list = TRI_AQL_NODE_MEMBER(node, 0);
// loop over sort criteria
n = list->_members._length;
for (i = 0; i < n; ++i) {
TRI_aql_node_t* element = TRI_AQL_NODE_MEMBER(list, i);
scope->_prefix = "l";
ScopeOutput(generator, "lhs = ");
ProcessNode(generator, TRI_AQL_NODE_MEMBER(element, elementIndex));
ScopeOutput(generator, ";\n");
scope->_prefix = "r";
ScopeOutput(generator, "rhs = ");
ProcessNode(generator, TRI_AQL_NODE_MEMBER(element, elementIndex));
ScopeOutput(generator, ";\n");
scope->_prefix = NULL;
ScopeOutput(generator, "res = AHUACATL_RELATIONAL_CMP(lhs, rhs);\n");
ScopeOutput(generator, "if (res) {\n");
if (elementIndex || TRI_AQL_NODE_BOOL(element)) {
// ascending
ScopeOutput(generator, "return res;\n");
}
else {
// descending
ScopeOutput(generator, "return -res;\n");
}
ScopeOutput(generator, "}\n");
}
// return 0 if all elements are equal
ScopeOutput(generator, "return 0;\n");
ScopeOutput(generator, "}\n");
// finish scope
EndScope(generator);
return functionIndex;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief generate code for a group function
////////////////////////////////////////////////////////////////////////////////
static TRI_aql_codegen_register_t CreateGroupFunction (TRI_aql_codegen_js_t* const generator,
const TRI_aql_node_t* const node) {
TRI_aql_node_t* list;
TRI_aql_codegen_scope_t* scope;
TRI_aql_codegen_register_t functionIndex = IncFunction(generator);
size_t i;
size_t n;
// start a new scope first
StartScope(generator, &generator->_functionBuffer, TRI_AQL_SCOPE_FUNCTION, 0, 0, 0, 0, "group");
scope = CurrentScope(generator);
scope->_prefix = "g";
ScopeOutput(generator, "function ");
ScopeOutputFunction(generator, functionIndex);
ScopeOutput(generator, " (g) {\n");
ScopeOutput(generator, "return { ");
// loop over group criteria
list = TRI_AQL_NODE_MEMBER(node, 0);
n = list->_members._length;
for (i = 0; i < n; ++i) {
TRI_aql_node_t* element = TRI_AQL_NODE_MEMBER(list, i);
TRI_aql_node_t* varNode = TRI_AQL_NODE_MEMBER(element, 0);
if (i > 0) {
ScopeOutput(generator, ", ");
}
ScopeOutputQuoted(generator, TRI_AQL_NODE_STRING(varNode));
ScopeOutput(generator, " : ");
ProcessNode(generator, TRI_AQL_NODE_MEMBER(element, 1));
}
ScopeOutput(generator, " };\n");
ScopeOutput(generator, "}\n");
// finish scope
EndScope(generator);
return functionIndex;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief create code for an empty list (var x = [ ];)
////////////////////////////////////////////////////////////////////////////////
static void InitList (TRI_aql_codegen_js_t* const generator,
const TRI_aql_codegen_register_t resultRegister) {
ScopeOutput(generator, "var ");
ScopeOutputRegister(generator, resultRegister);
ScopeOutput(generator, " = [ ];\n");
}
////////////////////////////////////////////////////////////////////////////////
/// @brief create code for an empty array (var x = { };)
////////////////////////////////////////////////////////////////////////////////
static void InitArray (TRI_aql_codegen_js_t* const generator,
const TRI_aql_codegen_register_t resultRegister) {
ScopeOutput(generator, "var ");
ScopeOutputRegister(generator, resultRegister);
ScopeOutput(generator, " = { };\n");
}
////////////////////////////////////////////////////////////////////////////////
/// @brief enter a variable into the symbol table
////////////////////////////////////////////////////////////////////////////////
static void EnterSymbol (TRI_aql_codegen_js_t* const generator,
const char* const name,
const TRI_aql_codegen_register_t registerIndex) {
TRI_aql_codegen_scope_t* scope = CurrentScope(generator);
TRI_aql_codegen_variable_t* variable = CreateVariable(name, registerIndex);
if (!variable) {
generator->_error = true;
return;
}
if (TRI_InsertKeyAssociativePointer(&scope->_variables, name, (void*) variable, false)) {
// variable already exists in symbol table. this should never happen
LOG_TRACE("variable already registered: %s", name);
generator->_error = true;
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief look up a variable in the symbol table
////////////////////////////////////////////////////////////////////////////////
static TRI_aql_codegen_register_t LookupSymbol (TRI_aql_codegen_js_t* const generator,
const char* const name) {
size_t i = generator->_scopes._length;
// iterate from current scope to the top level scope
while (i-- > 0) {
TRI_aql_codegen_scope_t* scope = (TRI_aql_codegen_scope_t*) generator->_scopes._buffer[i];
TRI_aql_codegen_variable_t* variable;
variable = (TRI_aql_codegen_variable_t*) TRI_LookupByKeyAssociativePointer(&scope->_variables, name);
// variable found in scope
if (variable) {
// return the variable register
return variable->_register;
}
}
// variable not found. this should never happen
LOG_TRACE("variable not found: %s", name);
generator->_error = true;
return 0;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief start a for loop
////////////////////////////////////////////////////////////////////////////////
static void StartFor (TRI_aql_codegen_js_t* const generator,
TRI_string_buffer_t* const buffer,
const TRI_aql_codegen_register_t sourceRegister,
const bool sourceIsList,
const char* const variableName) {
TRI_aql_codegen_register_t listRegister = IncRegister(generator);
TRI_aql_codegen_register_t keyRegister = IncRegister(generator);
TRI_aql_codegen_register_t ownRegister = IncRegister(generator);
TRI_aql_codegen_scope_t* scope = CurrentScope(generator);
// always start a new scope
StartScope(generator,
buffer,
TRI_AQL_SCOPE_FOR,
listRegister,
keyRegister,
ownRegister,
scope->_resultRegister,
"for");
if (variableName) {
EnterSymbol(generator, variableName, ownRegister);
}
ScopeOutput(generator, "var ");
ScopeOutputRegister(generator, listRegister);
if (sourceIsList) {
// the source register we're using definitely is a list
// we can therefore get rid of the function call to AHUACATL_LIST()
ScopeOutput(generator, " = ");
ScopeOutputRegister(generator, sourceRegister);
ScopeOutput(generator, ";\n");
}
else {
// we're not sure whether the source is a list, so we need to force it to be a list
ScopeOutput(generator, " = AHUACATL_LIST(");
ScopeOutputRegister(generator, sourceRegister);
ScopeOutput(generator, ");\n");
}
// for (var keyx in listx)
ScopeOutput(generator, "for (var ");
ScopeOutputRegister(generator, keyRegister);
ScopeOutput(generator, " = 0; ");
ScopeOutputRegister(generator, keyRegister);
ScopeOutput(generator, " < ");
ScopeOutputRegister(generator, listRegister);
ScopeOutput(generator, ".length; ++");
ScopeOutputRegister(generator, keyRegister);
ScopeOutput(generator, ") {\n");
// var rx = listx[keyx];
scope = CurrentScope(generator);
ScopeOutput(generator, "var ");
ScopeOutputRegister(generator, scope->_ownRegister);
ScopeOutput(generator, " = ");
ScopeOutputRegister(generator, scope->_listRegister);
ScopeOutput(generator, "[");
ScopeOutputRegister(generator, scope->_keyRegister);
ScopeOutput(generator, "];\n");
}
////////////////////////////////////////////////////////////////////////////////
/// @brief close for loops til the parent scope is reached
///
/// all for loops will be closed until another, non-for scope is reached
////////////////////////////////////////////////////////////////////////////////
static void CloseLoops (TRI_aql_codegen_js_t* const generator) {
TRI_aql_codegen_scope_t* scope = CurrentScope(generator);
if (scope->_type == TRI_AQL_SCOPE_MAIN || scope->_type == TRI_AQL_SCOPE_SUBQUERY) {
// these scopes are closed by other means
return;
}
// we are closing at least one scope
while (true) {
TRI_aql_codegen_scope_e type = scope->_type;
ScopeOutput(generator, "}\n");
EndScope(generator);
// break if we reached the top level for loop
if (type != TRI_AQL_SCOPE_FOR_NESTED) {
break;
}
// next iteration
scope = CurrentScope(generator);
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief store scope symbols in a result variable
///
/// fetches all symbols from the current scope and all potentially available
/// parent for loops, pushes their values into a result register, and note the
/// symbol names
/// this function is called before a scope is closed and allows remembering
/// which variables were active in a scope. it is necessary to call this
/// function for language constructs like SORT, COLLECT etc. which close the
/// current scope and open a new scope afterwards with the same variables as
/// before
////////////////////////////////////////////////////////////////////////////////
static TRI_vector_string_t StoreSymbols (TRI_aql_codegen_js_t* const generator,
const TRI_aql_codegen_register_t rowRegister) {
TRI_vector_string_t variableNames;
size_t i = generator->_scopes._length;
TRI_InitVectorString(&variableNames, TRI_UNKNOWN_MEM_ZONE);
// start at current scope
while (i-- > 0) {
TRI_aql_codegen_scope_t* peek = (TRI_aql_codegen_scope_t*) generator->_scopes._buffer[i];
size_t j, n;
// iterate over all variables in the scope
n = peek->_variables._nrAlloc;
for (j = 0; j < n; ++j) {
TRI_aql_codegen_variable_t* variable = (TRI_aql_codegen_variable_t*) peek->_variables._table[j];
if (!variable) {
continue;
}
// push all variable values into the rowRegister
// row[name] = ...;
ScopeOutputIndexedName(generator, rowRegister, variable->_name);
ScopeOutput(generator, " = ");
ScopeOutputRegister(generator, variable->_register);
ScopeOutput(generator, ";\n");
TRI_PushBackVectorString(&variableNames, TRI_DuplicateString(variable->_name));
}
// break if we reached the top level for loop
if (peek->_type != TRI_AQL_SCOPE_FOR_NESTED) {
break;
}
// next scope
}
return variableNames;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief re-enter variables into the symbol table
///
/// re-enters all passed variables into the symbol table of the current scope
/// and pop them the current scope's iterator register
/// this function is after a scope is closed and StoreSymbols has been called.
/// it allows using the same variables as in the previous scope.
/// it is necessary to call this function for language constructs like SORT etc.
/// which close a scope and open a new scope afterwards with the same variables
/// as before
////////////////////////////////////////////////////////////////////////////////
static void RestoreSymbols (TRI_aql_codegen_js_t* const generator,
TRI_vector_string_t* const variableNames) {
TRI_aql_codegen_scope_t* scope = CurrentScope(generator);
size_t i, n;
// iterate over all variables passed
n = variableNames->_length;
for (i = 0; i < n; ++i) {
// create a new register for each variable
TRI_aql_codegen_register_t registerIndex = IncRegister(generator);
char* variableName = (char*) variableNames->_buffer[i];
// re-enter symbols
if (variableName) {
EnterSymbol(generator, variableName, registerIndex);
}
// unpack variables from current scope's iterator register
ScopeOutput(generator, "var ");
ScopeOutputRegister(generator, registerIndex);
ScopeOutput(generator, " = ");
ScopeOutputIndexedName(generator, scope->_ownRegister, variableName);
ScopeOutput(generator, ";\n");
}
TRI_DestroyVectorString(variableNames);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief generate code for primary index access
////////////////////////////////////////////////////////////////////////////////
static void GeneratePrimaryAccess (TRI_aql_codegen_js_t* const generator,
const TRI_aql_index_t* const idx,
const TRI_aql_collection_t* const collection,
const char* const collectionName) {
TRI_aql_field_access_t* fieldAccess;
size_t n;
n = idx->_fieldAccesses->_length;
assert(n == 1);
fieldAccess = (TRI_aql_field_access_t*) TRI_AtVectorPointer(idx->_fieldAccesses, 0);
if (fieldAccess->_type == TRI_AQL_ACCESS_LIST) {
ScopeOutput(generator, "AHUACATL_GET_DOCUMENTS_PRIMARY_LIST('");
}
else {
ScopeOutput(generator, "AHUACATL_GET_DOCUMENTS_PRIMARY('");
}
ScopeOutput(generator, collectionName);
ScopeOutput(generator, "', ");
ScopeOutputIndexId(generator, collection, idx);
ScopeOutput(generator, ", ");
ScopeOutputJson(generator, fieldAccess->_value._value);
ScopeOutput(generator, ")");
}
////////////////////////////////////////////////////////////////////////////////
/// @brief generate code for hash index access
////////////////////////////////////////////////////////////////////////////////
static void GenerateHashAccess (TRI_aql_codegen_js_t* const generator,
const TRI_aql_index_t* const idx,
const TRI_aql_collection_t* const collection,
const char* const collectionName,
const size_t offset) {
size_t i, n;
n = idx->_fieldAccesses->_length;
assert(n >= 1);
if (n == 1) {
// peek at first element and check if it is a list access
TRI_aql_field_access_t* fieldAccess = (TRI_aql_field_access_t*) TRI_AtVectorPointer(idx->_fieldAccesses, 0);
if (fieldAccess->_type == TRI_AQL_ACCESS_LIST) {
ScopeOutput(generator, "AHUACATL_GET_DOCUMENTS_HASH_LIST('");
ScopeOutput(generator, collectionName);
ScopeOutput(generator, "', ");
ScopeOutputIndexId(generator, collection, idx);
ScopeOutput(generator, ", ");
ScopeOutputQuoted2(generator, fieldAccess->_fullName + offset);
ScopeOutput(generator, ", ");
ScopeOutputJson(generator, fieldAccess->_value._value);
ScopeOutput(generator, ")");
return;
}
// fall through to exact access
}
ScopeOutput(generator, "AHUACATL_GET_DOCUMENTS_HASH('");
ScopeOutput(generator, collectionName);
ScopeOutput(generator, "', ");
ScopeOutputIndexId(generator, collection, idx);
ScopeOutput(generator, ", { ");
// write the example document
for (i = 0; i < n; ++i) {
TRI_aql_field_access_t* fieldAccess = (TRI_aql_field_access_t*) TRI_AtVectorPointer(idx->_fieldAccesses, i);
assert(fieldAccess->_type == TRI_AQL_ACCESS_EXACT);
if (i > 0) {
ScopeOutput(generator, ", ");
}
ScopeOutputQuoted2(generator, fieldAccess->_fullName + offset);
ScopeOutput(generator, " : ");
ScopeOutputJson(generator, fieldAccess->_value._value);
}
ScopeOutput(generator, " })");
}
////////////////////////////////////////////////////////////////////////////////
/// @brief generate code for skiplist access
////////////////////////////////////////////////////////////////////////////////
static void GenerateSkiplistAccess (TRI_aql_codegen_js_t* const generator,
const TRI_aql_index_t* const idx,
const TRI_aql_collection_t* const collection,
const char* const collectionName,
const size_t offset) {
size_t i, n;
n = idx->_fieldAccesses->_length;
assert(n >= 1);
if (n == 1) {
// peek at first element and check if it is a list access
TRI_aql_field_access_t* fieldAccess = (TRI_aql_field_access_t*) TRI_AtVectorPointer(idx->_fieldAccesses, 0);
if (fieldAccess->_type == TRI_AQL_ACCESS_LIST) {
ScopeOutput(generator, "AHUACATL_GET_DOCUMENTS_SKIPLIST_LIST('");
ScopeOutput(generator, collectionName);
ScopeOutput(generator, "', ");
ScopeOutputIndexId(generator, collection, idx);
ScopeOutput(generator, ", ");
ScopeOutputQuoted2(generator, fieldAccess->_fullName + offset);
ScopeOutput(generator, ", ");
ScopeOutputJson(generator, fieldAccess->_value._value);
ScopeOutput(generator, ")");
return;
}
// fall through to other access types
}
ScopeOutput(generator, "AHUACATL_GET_DOCUMENTS_SKIPLIST('");
ScopeOutput(generator, collectionName);
ScopeOutput(generator, "', ");
ScopeOutputIndexId(generator, collection, idx);
ScopeOutput(generator, ", { ");
// write the example document
for (i = 0; i < n; ++i) {
TRI_aql_field_access_t* fieldAccess = (TRI_aql_field_access_t*) TRI_AtVectorPointer(idx->_fieldAccesses, i);
assert(fieldAccess->_type == TRI_AQL_ACCESS_EXACT ||
fieldAccess->_type == TRI_AQL_ACCESS_RANGE_SINGLE ||
fieldAccess->_type == TRI_AQL_ACCESS_RANGE_DOUBLE);
if (i > 0) {
ScopeOutput(generator, ", ");
}
ScopeOutputQuoted2(generator, fieldAccess->_fullName + offset);
ScopeOutput(generator, " : [ ");
if (fieldAccess->_type == TRI_AQL_ACCESS_EXACT) {
ScopeOutput(generator, " [ \"==\", ");
ScopeOutputJson(generator, fieldAccess->_value._value);
ScopeOutput(generator, " ] ");
}
else if (fieldAccess->_type == TRI_AQL_ACCESS_RANGE_SINGLE) {
ScopeOutput(generator, " [ \"");
ScopeOutput(generator, TRI_RangeOperatorAql(fieldAccess->_value._singleRange._type));
ScopeOutput(generator, "\", ");
ScopeOutputJson(generator, fieldAccess->_value._singleRange._value);
ScopeOutput(generator, " ] ");
}
else if (fieldAccess->_type == TRI_AQL_ACCESS_RANGE_DOUBLE) {
// lower bound
ScopeOutput(generator, " [ \"");
ScopeOutput(generator, TRI_RangeOperatorAql(fieldAccess->_value._between._lower._type));
ScopeOutput(generator, "\", ");
ScopeOutputJson(generator, fieldAccess->_value._between._lower._value);
ScopeOutput(generator, " ], [ \"");
// upper bound
ScopeOutput(generator, TRI_RangeOperatorAql(fieldAccess->_value._between._upper._type));
ScopeOutput(generator, "\", ");
ScopeOutputJson(generator, fieldAccess->_value._between._upper._value);
ScopeOutput(generator, " ] ");
}
ScopeOutput(generator, " ] ");
}
ScopeOutput(generator, " })");
}
////////////////////////////////////////////////////////////////////////////////
/// @brief generate code for a reference (the name of a variable)
////////////////////////////////////////////////////////////////////////////////
static void ProcessReference (TRI_aql_codegen_js_t* const generator,
const TRI_aql_node_t* const node) {
TRI_aql_codegen_scope_t* scope = CurrentScope(generator);
char* name = TRI_AQL_NODE_STRING(node);
if (scope->_prefix) {
// sort function, group function etc. - we are using a variable
ScopeOutput(generator, scope->_prefix);
ScopeOutput(generator, "[");
ScopeOutputQuoted(generator, name);
ScopeOutput(generator, "]");
}
else {
// regular scope, we are using a register
TRI_aql_codegen_register_t registerIndex = LookupSymbol(generator, name);
ScopeOutputRegister(generator, registerIndex);
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief generate code for a value literal
////////////////////////////////////////////////////////////////////////////////
static void ProcessValue (TRI_aql_codegen_js_t* const generator,
const TRI_aql_node_t* const node) {
TRI_aql_codegen_scope_t* scope = CurrentScope(generator);
if (!scope->_buffer) {
return;
}
if (!TRI_ValueJavascriptAql(scope->_buffer, &node->_value, node->_value._type)) {
generator->_error = true;
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief generate code for a list ([ ])
////////////////////////////////////////////////////////////////////////////////
static void ProcessList (TRI_aql_codegen_js_t* const generator,
const TRI_aql_node_t* const node) {
size_t i, n;
ScopeOutput(generator, "[ ");
n = node->_members._length;
for (i = 0; i < n; ++i) {
if (i > 0) {
ScopeOutput(generator, ", ");
}
ProcessNode(generator, TRI_AQL_NODE_MEMBER(node, i));
}
ScopeOutput(generator, " ]");
}
////////////////////////////////////////////////////////////////////////////////
/// @brief generate code for an array ({ })
////////////////////////////////////////////////////////////////////////////////
static void ProcessArray (TRI_aql_codegen_js_t* const generator,
const TRI_aql_node_t* const node) {
size_t i, n;
ScopeOutput(generator, "{ ");
n = node->_members._length;
for (i = 0; i < n; ++i) {
if (i > 0) {
ScopeOutput(generator, ", ");
}
ProcessNode(generator, TRI_AQL_NODE_MEMBER(node, i));
}
ScopeOutput(generator, " }");
}
////////////////////////////////////////////////////////////////////////////////
/// @brief generate code for a single named array element
////////////////////////////////////////////////////////////////////////////////
static void ProcessArrayElement (TRI_aql_codegen_js_t* const generator,
const TRI_aql_node_t* const node) {
TRI_aql_codegen_scope_t* scope = CurrentScope(generator);
if (!scope->_buffer) {
return;
}
TRI_ValueJavascriptAql(scope->_buffer, &node->_value, AQL_TYPE_STRING);
ScopeOutput(generator, " : ");
ProcessNode(generator, TRI_AQL_NODE_MEMBER(node, 0));
}
////////////////////////////////////////////////////////////////////////////////
/// @brief generate code for a function call argument list
////////////////////////////////////////////////////////////////////////////////
static void ProcessArgList (TRI_aql_codegen_js_t* const generator,
const TRI_aql_function_t* const function,
const TRI_aql_node_t* const node) {
size_t i, n;
ScopeOutput(generator, "[ ");
n = node->_members._length;
for (i = 0; i < n; ++i) {
TRI_aql_node_t* parameter = TRI_AQL_NODE_MEMBER(node, i);
if (i > 0) {
ScopeOutput(generator, ", ");
}
if (parameter->_type == AQL_NODE_COLLECTION && TRI_ConvertParameterFunctionAql(function, i)) {
// collection arguments will be created as string argument => e.g. "users"
ScopeOutputQuoted(generator, TRI_AQL_NODE_STRING(parameter));
}
else {
// anything else will be created as is
ProcessNode(generator, parameter);
}
}
ScopeOutput(generator, " ]");
}
////////////////////////////////////////////////////////////////////////////////
/// @brief generate code for an attribute name in exapnd (e.g. users[*].name)
////////////////////////////////////////////////////////////////////////////////
static void ProcessAttribute (TRI_aql_codegen_js_t* const generator,
const TRI_aql_node_t* const node) {
TRI_aql_codegen_scope_t* scope = CurrentScope(generator);
ScopeOutput(generator, "AHUACATL_DOCUMENT_MEMBER(");
ScopeOutputRegister(generator, scope->_ownRegister);
ScopeOutput(generator, ", ");
ScopeOutputQuoted(generator, TRI_AQL_NODE_STRING(node));
ScopeOutput(generator, ")");
}
////////////////////////////////////////////////////////////////////////////////
/// @brief generate code for an attribute name (e.g. users.name)
////////////////////////////////////////////////////////////////////////////////
static void ProcessAttributeAccess (TRI_aql_codegen_js_t* const generator,
const TRI_aql_node_t* const node) {
ScopeOutput(generator, "AHUACATL_DOCUMENT_MEMBER(");
ProcessNode(generator, TRI_AQL_NODE_MEMBER(node, 0));
ScopeOutput(generator, ", ");
ScopeOutputQuoted(generator, TRI_AQL_NODE_STRING(node));
ScopeOutput(generator, ")");
}
////////////////////////////////////////////////////////////////////////////////
/// @brief generate code for indexed access (e.g. users[0])
////////////////////////////////////////////////////////////////////////////////
static void ProcessIndexed (TRI_aql_codegen_js_t* const generator,
const TRI_aql_node_t* const node) {
ScopeOutput(generator, "AHUACATL_GET_INDEX(");
ProcessNode(generator, TRI_AQL_NODE_MEMBER(node, 0));
ScopeOutput(generator, ", ");
ProcessNode(generator, TRI_AQL_NODE_MEMBER(node, 1));
ScopeOutput(generator, ")");
}
////////////////////////////////////////////////////////////////////////////////
/// @brief generate code for collection access (full table scan)
////////////////////////////////////////////////////////////////////////////////
static void ProcessCollection (TRI_aql_codegen_js_t* const generator,
const TRI_aql_node_t* const node) {
ScopeOutput(generator, "AHUACATL_GET_DOCUMENTS('");
ScopeOutput(generator, TRI_AQL_NODE_STRING(node));
ScopeOutput(generator, "')");
}
////////////////////////////////////////////////////////////////////////////////
/// @brief generate code for indexed collection access (if possible)
////////////////////////////////////////////////////////////////////////////////
static void ProcessHintedCollection (TRI_aql_codegen_js_t* const generator,
const TRI_aql_node_t* const nameNode,
const TRI_aql_node_t* const collectionNode,
const TRI_vector_pointer_t* const candidates) {
TRI_aql_index_t* idx;
TRI_aql_collection_t* collection;
TRI_vector_pointer_t* availableIndexes;
char* collectionName;
char* variableName;
size_t offset;
assert(generator);
assert(nameNode);
assert(collectionNode);
variableName = TRI_AQL_NODE_STRING(nameNode);
assert(variableName);
collectionName = TRI_AQL_NODE_STRING(collectionNode);
assert(collectionName);
collection = TRI_GetCollectionAql(generator->_context, collectionName);
if (!collection) {
generator->_error = true;
return;
}
assert(collection);
availableIndexes = &(((TRI_sim_collection_t*) collection->_collection->_collection)->_indexes);
if (!availableIndexes) {
generator->_error = true;
return;
}
idx = TRI_DetermineIndexAql(generator->_context, availableIndexes, variableName, collectionName, candidates);
if (!idx) {
// no index can be used, proceed with normally (full table scan) access
ProcessCollection(generator, collectionNode);
return;
}
switch (idx->_idx->_type) {
case TRI_IDX_TYPE_GEO1_INDEX:
case TRI_IDX_TYPE_GEO2_INDEX:
case TRI_IDX_TYPE_PRIORITY_QUEUE_INDEX:
case TRI_IDX_TYPE_CAP_CONSTRAINT:
// these indexes are not yet supported
generator->_error = true;
break;
case TRI_IDX_TYPE_PRIMARY_INDEX:
GeneratePrimaryAccess(generator, idx, collection, collectionName);
break;
case TRI_IDX_TYPE_HASH_INDEX:
offset = strlen(variableName) + 1;
assert(offset > 1);
GenerateHashAccess(generator, idx, collection, collectionName, offset);
break;
case TRI_IDX_TYPE_SKIPLIST_INDEX:
offset = strlen(variableName) + 1;
assert(offset > 1);
GenerateSkiplistAccess(generator, idx, collection, collectionName, offset);
break;
}
TRI_FreeIndexAql(idx);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief generate code for expand (e.g. users[*]....)
////////////////////////////////////////////////////////////////////////////////
static void ProcessExpand (TRI_aql_codegen_js_t* const generator,
const TRI_aql_node_t* const node) {
TRI_aql_codegen_scope_t* scope = CurrentScope(generator);
TRI_aql_node_t* nameNode1 = TRI_AQL_NODE_MEMBER(node, 0);
TRI_aql_node_t* nameNode2 = TRI_AQL_NODE_MEMBER(node, 1);
TRI_aql_node_t* expressionNode = TRI_AQL_NODE_MEMBER(node, 2);
TRI_aql_codegen_register_t sourceRegister = IncRegister(generator);
TRI_aql_codegen_register_t resultRegister = IncRegister(generator);
bool isList = TRI_IsListNodeAql(expressionNode);
// init source
ScopeOutput(generator, "var ");
ScopeOutputRegister(generator, sourceRegister);
ScopeOutput(generator, " = ");
ProcessNode(generator, expressionNode);
ScopeOutput(generator, ";\n");
// var result = [ ];
InitList(generator, resultRegister);
// expand scope
StartScope(generator, scope->_buffer, TRI_AQL_SCOPE_EXPAND, 0, 0, 0, resultRegister, "expand");
// for
StartFor(generator, scope->_buffer, sourceRegister, isList, NULL);
EnterSymbol(generator, TRI_AQL_NODE_STRING(nameNode1), CurrentScope(generator)->_ownRegister);
ScopeOutputRegister(generator, resultRegister);
ScopeOutput(generator, ".push(");
ProcessNode(generator, TRI_AQL_NODE_MEMBER(node, 3));
ScopeOutput(generator, ");\n");
// }
CloseLoops(generator);
// expand scope
EndScope(generator);
// register the variable for the result
EnterSymbol(generator, TRI_AQL_NODE_STRING(nameNode2), resultRegister);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief generate code for unary not (!value)
////////////////////////////////////////////////////////////////////////////////
static void ProcessUnaryNot (TRI_aql_codegen_js_t* const generator,
const TRI_aql_node_t* const node) {
ScopeOutput(generator, "AHUACATL_LOGICAL_NOT(");
ProcessNode(generator, TRI_AQL_NODE_MEMBER(node, 0));
ScopeOutput(generator, ")");
}
////////////////////////////////////////////////////////////////////////////////
/// @brief generate code for unary minus (-value)
////////////////////////////////////////////////////////////////////////////////
static void ProcessUnaryMinus (TRI_aql_codegen_js_t* const generator,
const TRI_aql_node_t* const node) {
ScopeOutput(generator, "AHUACATL_UNARY_MINUS(");
ProcessNode(generator, TRI_AQL_NODE_MEMBER(node, 0));
ScopeOutput(generator, ")");
}
////////////////////////////////////////////////////////////////////////////////
/// @brief generate code for unary plus (+value)
////////////////////////////////////////////////////////////////////////////////
static void ProcessUnaryPlus (TRI_aql_codegen_js_t* const generator,
const TRI_aql_node_t* const node) {
ScopeOutput(generator, "AHUACATL_UNARY_PLUS(");
ProcessNode(generator, TRI_AQL_NODE_MEMBER(node, 0));
ScopeOutput(generator, ")");
}
////////////////////////////////////////////////////////////////////////////////
/// @brief generate code for binary and (a && b)
////////////////////////////////////////////////////////////////////////////////
static void ProcessBinaryAnd (TRI_aql_codegen_js_t* const generator,
const TRI_aql_node_t* const node) {
ScopeOutput(generator, "AHUACATL_LOGICAL_AND(");
ProcessNode(generator, TRI_AQL_NODE_MEMBER(node, 0));
ScopeOutput(generator, ", ");
ProcessNode(generator, TRI_AQL_NODE_MEMBER(node, 1));
ScopeOutput(generator, ")");
}
////////////////////////////////////////////////////////////////////////////////
/// @brief generate code for binary or (a || b)
////////////////////////////////////////////////////////////////////////////////
static void ProcessBinaryOr (TRI_aql_codegen_js_t* const generator,
const TRI_aql_node_t* const node) {
ScopeOutput(generator, "AHUACATL_LOGICAL_OR(");
ProcessNode(generator, TRI_AQL_NODE_MEMBER(node, 0));
ScopeOutput(generator, ", ");
ProcessNode(generator, TRI_AQL_NODE_MEMBER(node, 1));
ScopeOutput(generator, ")");
}
////////////////////////////////////////////////////////////////////////////////
/// @brief generate code for binary plus (a + b)
////////////////////////////////////////////////////////////////////////////////
static void ProcessBinaryPlus (TRI_aql_codegen_js_t* const generator,
const TRI_aql_node_t* const node) {
ScopeOutput(generator, "AHUACATL_ARITHMETIC_PLUS(");
ProcessNode(generator, TRI_AQL_NODE_MEMBER(node, 0));
ScopeOutput(generator, ", ");
ProcessNode(generator, TRI_AQL_NODE_MEMBER(node, 1));
ScopeOutput(generator, ")");
}
////////////////////////////////////////////////////////////////////////////////
/// @brief generate code for binary minus (a - b)
////////////////////////////////////////////////////////////////////////////////
static void ProcessBinaryMinus (TRI_aql_codegen_js_t* const generator,
const TRI_aql_node_t* const node) {
ScopeOutput(generator, "AHUACATL_ARITHMETIC_MINUS(");
ProcessNode(generator, TRI_AQL_NODE_MEMBER(node, 0));
ScopeOutput(generator, ", ");
ProcessNode(generator, TRI_AQL_NODE_MEMBER(node, 1));
ScopeOutput(generator, ")");
}
////////////////////////////////////////////////////////////////////////////////
/// @brief generate code for binary times (a * b)
////////////////////////////////////////////////////////////////////////////////
static void ProcessBinaryTimes (TRI_aql_codegen_js_t* const generator,
const TRI_aql_node_t* const node) {
ScopeOutput(generator, "AHUACATL_ARITHMETIC_TIMES(");
ProcessNode(generator, TRI_AQL_NODE_MEMBER(node, 0));
ScopeOutput(generator, ", ");
ProcessNode(generator, TRI_AQL_NODE_MEMBER(node, 1));
ScopeOutput(generator, ")");
}
////////////////////////////////////////////////////////////////////////////////
/// @brief generate code for binary divide (a / b)
////////////////////////////////////////////////////////////////////////////////
static void ProcessBinaryDivide (TRI_aql_codegen_js_t* const generator,
const TRI_aql_node_t* const node) {
ScopeOutput(generator, "AHUACATL_ARITHMETIC_DIVIDE(");
ProcessNode(generator, TRI_AQL_NODE_MEMBER(node, 0));
ScopeOutput(generator, ", ");
ProcessNode(generator, TRI_AQL_NODE_MEMBER(node, 1));
ScopeOutput(generator, ")");
}
////////////////////////////////////////////////////////////////////////////////
/// @brief generate code for binary modulus (a % b)
////////////////////////////////////////////////////////////////////////////////
static void ProcessBinaryModulus(TRI_aql_codegen_js_t* const generator,
const TRI_aql_node_t* const node) {
ScopeOutput(generator, "AHUACATL_ARITHMETIC_MODULUS(");
ProcessNode(generator, TRI_AQL_NODE_MEMBER(node, 0));
ScopeOutput(generator, ", ");
ProcessNode(generator, TRI_AQL_NODE_MEMBER(node, 1));
ScopeOutput(generator, ")");
}
////////////////////////////////////////////////////////////////////////////////
/// @brief generate code for binary equality (a == b)
////////////////////////////////////////////////////////////////////////////////
static void ProcessBinaryEqual (TRI_aql_codegen_js_t* const generator,
const TRI_aql_node_t* const node) {
ScopeOutput(generator, "AHUACATL_RELATIONAL_EQUAL(");
ProcessNode(generator, TRI_AQL_NODE_MEMBER(node, 0));
ScopeOutput(generator, ", ");
ProcessNode(generator, TRI_AQL_NODE_MEMBER(node, 1));
ScopeOutput(generator, ")");
}
////////////////////////////////////////////////////////////////////////////////
/// @brief generate code for binary inequality (a != b)
////////////////////////////////////////////////////////////////////////////////
static void ProcessBinaryUnequal (TRI_aql_codegen_js_t* const generator,
const TRI_aql_node_t* const node) {
ScopeOutput(generator, "AHUACATL_RELATIONAL_UNEQUAL(");
ProcessNode(generator, TRI_AQL_NODE_MEMBER(node, 0));
ScopeOutput(generator, ", ");
ProcessNode(generator, TRI_AQL_NODE_MEMBER(node, 1));
ScopeOutput(generator, ")");
}
////////////////////////////////////////////////////////////////////////////////
/// @brief generate code for binary less equal (a <= b)
////////////////////////////////////////////////////////////////////////////////
static void ProcessBinaryLessEqual (TRI_aql_codegen_js_t* const generator,
const TRI_aql_node_t* const node) {
ScopeOutput(generator, "AHUACATL_RELATIONAL_LESSEQUAL(");
ProcessNode(generator, TRI_AQL_NODE_MEMBER(node, 0));
ScopeOutput(generator, ", ");
ProcessNode(generator, TRI_AQL_NODE_MEMBER(node, 1));
ScopeOutput(generator, ")");
}
////////////////////////////////////////////////////////////////////////////////
/// @brief generate code for binary less (a < b)
////////////////////////////////////////////////////////////////////////////////
static void ProcessBinaryLess (TRI_aql_codegen_js_t* const generator,
const TRI_aql_node_t* const node) {
ScopeOutput(generator, "AHUACATL_RELATIONAL_LESS(");
ProcessNode(generator, TRI_AQL_NODE_MEMBER(node, 0));
ScopeOutput(generator, ", ");
ProcessNode(generator, TRI_AQL_NODE_MEMBER(node, 1));
ScopeOutput(generator, ")");
}
////////////////////////////////////////////////////////////////////////////////
/// @brief generate code for binary greater equal (a >= b)
////////////////////////////////////////////////////////////////////////////////
static void ProcessBinaryGreaterEqual (TRI_aql_codegen_js_t* const generator,
const TRI_aql_node_t* const node) {
ScopeOutput(generator, "AHUACATL_RELATIONAL_GREATEREQUAL(");
ProcessNode(generator, TRI_AQL_NODE_MEMBER(node, 0));
ScopeOutput(generator, ", ");
ProcessNode(generator, TRI_AQL_NODE_MEMBER(node, 1));
ScopeOutput(generator, ")");
}
////////////////////////////////////////////////////////////////////////////////
/// @brief generate code for binary greater (a > b)
////////////////////////////////////////////////////////////////////////////////
static void ProcessBinaryGreater (TRI_aql_codegen_js_t* const generator,
const TRI_aql_node_t* const node) {
ScopeOutput(generator, "AHUACATL_RELATIONAL_GREATER(");
ProcessNode(generator, TRI_AQL_NODE_MEMBER(node, 0));
ScopeOutput(generator, ", ");
ProcessNode(generator, TRI_AQL_NODE_MEMBER(node, 1));
ScopeOutput(generator, ")");
}
////////////////////////////////////////////////////////////////////////////////
/// @brief generate code for binary in (a in b)
////////////////////////////////////////////////////////////////////////////////
static void ProcessBinaryIn (TRI_aql_codegen_js_t* const generator,
const TRI_aql_node_t* const node) {
ScopeOutput(generator, "AHUACATL_RELATIONAL_IN(");
ProcessNode(generator, TRI_AQL_NODE_MEMBER(node, 0));
ScopeOutput(generator, ", ");
ProcessNode(generator, TRI_AQL_NODE_MEMBER(node, 1));
ScopeOutput(generator, ")");
}
////////////////////////////////////////////////////////////////////////////////
/// @brief generate code for ternary
////////////////////////////////////////////////////////////////////////////////
static void ProcessTernary (TRI_aql_codegen_js_t* const generator,
const TRI_aql_node_t* const node) {
ScopeOutput(generator, "AHUACATL_TERNARY_OPERATOR(");
ProcessNode(generator, TRI_AQL_NODE_MEMBER(node, 0));
ScopeOutput(generator, ", ");
ProcessNode(generator, TRI_AQL_NODE_MEMBER(node, 1));
ScopeOutput(generator, ", ");
ProcessNode(generator, TRI_AQL_NODE_MEMBER(node, 2));
ScopeOutput(generator, ")");
}
////////////////////////////////////////////////////////////////////////////////
/// @brief generate code for subqueries (nested fors etc.)
////////////////////////////////////////////////////////////////////////////////
static void ProcessSubquery (TRI_aql_codegen_js_t* const generator,
const TRI_aql_node_t* const node) {
TRI_aql_codegen_register_t scopeRegister = IncRegister(generator);
TRI_aql_codegen_register_t resultRegister = IncRegister(generator);
TRI_aql_node_t* nameNode = TRI_AQL_NODE_MEMBER(node, 0);
StartScope(generator, &generator->_buffer, TRI_AQL_SCOPE_SUBQUERY, 0, 0, 0, scopeRegister, "subquery");
InitList(generator, scopeRegister);
ProcessNode(generator, TRI_AQL_NODE_MEMBER(node, 1));
// register might have changed
scopeRegister = CurrentScope(generator)->_resultRegister;
EndScope(generator);
ScopeOutput(generator, "var ");
ScopeOutputRegister(generator, resultRegister);
ScopeOutput(generator, " = ");
ScopeOutputRegister(generator, scopeRegister);
ScopeOutput(generator, ";\n");
EnterSymbol(generator, nameNode->_value._value._string, resultRegister);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief generate code for function calls
////////////////////////////////////////////////////////////////////////////////
static void ProcessFcall (TRI_aql_codegen_js_t* const generator,
const TRI_aql_node_t* const node) {
ScopeOutput(generator, "AHUACATL_FCALL(");
ScopeOutput(generator, TRI_GetInternalNameFunctionAql((TRI_aql_function_t*) TRI_AQL_NODE_DATA(node)));
ScopeOutput(generator, ", ");
ProcessArgList(generator, (TRI_aql_function_t*) TRI_AQL_NODE_DATA(node), TRI_AQL_NODE_MEMBER(node, 0));
ScopeOutput(generator, ")");
}
////////////////////////////////////////////////////////////////////////////////
/// @brief generate code for for keyword
////////////////////////////////////////////////////////////////////////////////
static void ProcessFor (TRI_aql_codegen_js_t* const generator,
const TRI_aql_node_t* const node) {
TRI_aql_codegen_scope_t* scope = CurrentScope(generator);
TRI_aql_node_t* nameNode = TRI_AQL_NODE_MEMBER(node, 0);
TRI_aql_node_t* expressionNode = TRI_AQL_NODE_MEMBER(node, 1);
TRI_aql_codegen_register_t sourceRegister = IncRegister(generator);
TRI_vector_pointer_t* fieldAccesses = (TRI_vector_pointer_t*) TRI_AQL_NODE_DATA(node);
TRI_string_buffer_t* buffer;
bool isList = TRI_IsListNodeAql(expressionNode);
buffer = scope->_buffer; // inherit buffer from current scope
if (fieldAccesses && TRI_ContainsImpossibleAql(fieldAccesses)) {
buffer = NULL; // NULL buffer so we do not generate any output
}
else {
ScopeOutput(generator, "var ");
ScopeOutputRegister(generator, sourceRegister);
ScopeOutput(generator, " = ");
if (expressionNode->_type == AQL_NODE_COLLECTION && fieldAccesses != NULL) {
// pick an index and write code for index access
ProcessHintedCollection(generator, nameNode, expressionNode, fieldAccesses);
}
else {
// normal for loop w/o index access
ProcessNode(generator, expressionNode);
}
ScopeOutput(generator, ";\n");
}
StartFor(generator, buffer, sourceRegister, isList, nameNode->_value._value._string);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief generate code for sort keyword
////////////////////////////////////////////////////////////////////////////////
static void ProcessSort (TRI_aql_codegen_js_t* const generator,
const TRI_aql_node_t* const node) {
TRI_aql_codegen_scope_t* scope = CurrentScope(generator);
TRI_aql_codegen_register_t rowRegister = IncRegister(generator);
TRI_aql_codegen_register_t sourceRegister = scope->_resultRegister;
TRI_aql_codegen_register_t resultRegister;
TRI_aql_codegen_register_t functionIndex;
TRI_vector_string_t variableNames;
// var row = { };
InitArray(generator, rowRegister);
// save symbols
variableNames = StoreSymbols(generator, rowRegister);
// result.push(row)
ScopeOutputRegister(generator, scope->_resultRegister);
ScopeOutput(generator, ".push(");
ScopeOutputRegister(generator, rowRegister);
ScopeOutput(generator, ");\n");
// }
CloseLoops(generator);
functionIndex = CreateSortFunction(generator, node, 0);
// now apply actual sorting
ScopeOutput(generator, "AHUACATL_SORT(");
ScopeOutputRegister(generator, sourceRegister);
ScopeOutput(generator, ", ");
ScopeOutputFunction(generator, functionIndex);
ScopeOutput(generator, ");\n");
// start new for loop
resultRegister = IncRegister(generator);
scope = CurrentScope(generator);
scope->_resultRegister = resultRegister;
InitList(generator, resultRegister);
StartFor(generator, scope->_buffer, sourceRegister, true, NULL);
// restore symbols
RestoreSymbols(generator, &variableNames);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief generate code for collect keyword
////////////////////////////////////////////////////////////////////////////////
static void ProcessCollect (TRI_aql_codegen_js_t* const generator,
const TRI_aql_node_t* const node) {
TRI_aql_codegen_scope_t* scope = CurrentScope(generator);
TRI_aql_codegen_register_t rowRegister = IncRegister(generator);
TRI_aql_codegen_register_t sourceRegister = scope->_resultRegister;
TRI_aql_codegen_register_t groupRegister = IncRegister(generator);
TRI_aql_codegen_register_t resultRegister;
TRI_aql_codegen_register_t sortFunctionIndex;
TRI_aql_codegen_register_t groupFunctionIndex;
TRI_vector_string_t variableNames;
TRI_aql_node_t* list;
size_t i, n;
// var row = { };
InitArray(generator, rowRegister);
// we are not interested in the variable names here, but
// StoreSymbols also generates code to save the current state in
// a rowRegister, and we need that
variableNames = StoreSymbols(generator, rowRegister);
TRI_DestroyVectorString(&variableNames);
// result.push(row)
ScopeOutputRegister(generator, scope->_resultRegister);
ScopeOutput(generator, ".push(");
ScopeOutputRegister(generator, rowRegister);
ScopeOutput(generator, ");\n");
// }
CloseLoops(generator);
// sort function
sortFunctionIndex = CreateSortFunction(generator, node, 1);
// group function
groupFunctionIndex = CreateGroupFunction(generator, node);
// now apply actual grouping
ScopeOutputRegister(generator, groupRegister);
ScopeOutput(generator, " = ");
ScopeOutput(generator, "AHUACATL_GROUP(");
ScopeOutputRegister(generator, sourceRegister);
ScopeOutput(generator, ", ");
ScopeOutputFunction(generator, sortFunctionIndex);
ScopeOutput(generator, ", ");
ScopeOutputFunction(generator, groupFunctionIndex);
// into
if (node->_members._length > 1) {
TRI_aql_node_t* nameNode = TRI_AQL_NODE_MEMBER(node, 1);
ScopeOutput(generator, ", ");
ScopeOutputQuoted(generator, TRI_AQL_NODE_STRING(nameNode));
}
ScopeOutput(generator, ");\n");
// start new for loop
resultRegister = IncRegister(generator);
scope = CurrentScope(generator);
scope->_resultRegister = resultRegister;
InitList(generator, resultRegister);
StartFor(generator, scope->_buffer, groupRegister, true, NULL);
scope = CurrentScope(generator);
// re-enter symbols for collect variables
list = TRI_AQL_NODE_MEMBER(node, 0);
n = list->_members._length;
for (i = 0; i < n; ++i) {
TRI_aql_node_t* element = TRI_AQL_NODE_MEMBER(list, i);
TRI_aql_node_t* varNode = TRI_AQL_NODE_MEMBER(element, 0);
TRI_aql_codegen_register_t varRegister = IncRegister(generator);
// var collect = temp['collect'];
ScopeOutputRegister(generator, varRegister);
ScopeOutput(generator, " = ");
ScopeOutputIndexedName(generator, scope->_ownRegister, TRI_AQL_NODE_STRING(varNode));
ScopeOutput(generator, ";\n");
EnterSymbol(generator, TRI_AQL_NODE_STRING(varNode), varRegister);
}
// re-enter symbol for into
if (node->_members._length > 1) {
TRI_aql_node_t* nameNode = TRI_AQL_NODE_MEMBER(node, 1);
TRI_aql_codegen_register_t intoRegister = IncRegister(generator);
// var into = temp['into'];
ScopeOutputRegister(generator, intoRegister);
ScopeOutput(generator, " = ");
ScopeOutputIndexedName(generator, scope->_ownRegister, TRI_AQL_NODE_STRING(nameNode));
ScopeOutput(generator, ";\n");
EnterSymbol(generator, TRI_AQL_NODE_STRING(nameNode), intoRegister);
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief generate code for limit keyword
////////////////////////////////////////////////////////////////////////////////
static void ProcessLimit (TRI_aql_codegen_js_t* const generator,
const TRI_aql_node_t* const node) {
TRI_aql_codegen_scope_t* scope = CurrentScope(generator);
TRI_aql_codegen_register_t rowRegister = IncRegister(generator);
TRI_aql_codegen_register_t sourceRegister = scope->_resultRegister;
TRI_aql_codegen_register_t resultRegister;
TRI_aql_codegen_register_t limitRegister;
TRI_vector_string_t variableNames;
// var row = { };
InitArray(generator, rowRegister);
// save symbols
variableNames = StoreSymbols(generator, rowRegister);
// result.push(row)
ScopeOutputRegister(generator, scope->_resultRegister);
ScopeOutput(generator, ".push(");
ScopeOutputRegister(generator, rowRegister);
ScopeOutput(generator, ");\n");
// }
CloseLoops(generator);
// now apply actual limit
limitRegister = IncRegister(generator);
ScopeOutput(generator, "var ");
ScopeOutputRegister(generator, limitRegister);
ScopeOutput(generator, " = AHUACATL_LIMIT(");
ScopeOutputRegister(generator, sourceRegister);
ScopeOutput(generator, ", ");
ProcessNode(generator, TRI_AQL_NODE_MEMBER(node, 0));
ScopeOutput(generator, ", ");
ProcessNode(generator, TRI_AQL_NODE_MEMBER(node, 1));
ScopeOutput(generator, ");\n");
// start new for loop
resultRegister = IncRegister(generator);
scope = CurrentScope(generator);
scope->_resultRegister = resultRegister;
InitList(generator, resultRegister);
StartFor(generator, scope->_buffer, limitRegister, true, NULL);
// restore symbols
RestoreSymbols(generator, &variableNames);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief generate code for return keyword
////////////////////////////////////////////////////////////////////////////////
static void ProcessReturn (TRI_aql_codegen_js_t* const generator,
const TRI_aql_node_t* const node) {
TRI_aql_codegen_scope_t* scope = CurrentScope(generator);
TRI_aql_codegen_register_t rowRegister = IncRegister(generator);
// var row = ...;
ScopeOutput(generator, "var ");
ScopeOutputRegister(generator, rowRegister);
ScopeOutput(generator, " = ");
ProcessNode(generator, TRI_AQL_NODE_MEMBER(node, 0));
ScopeOutput(generator, ";\n");
// result.push(row);
ScopeOutputRegister(generator, scope->_resultRegister);
ScopeOutput(generator, ".push(");
ScopeOutputRegister(generator, rowRegister);
ScopeOutput(generator, ");\n");
// }
CloseLoops(generator);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief generate code for let keyword
////////////////////////////////////////////////////////////////////////////////
static void ProcessLet (TRI_aql_codegen_js_t* const generator,
const TRI_aql_node_t* const node) {
TRI_aql_node_t* nameNode = TRI_AQL_NODE_MEMBER(node, 0);
TRI_aql_codegen_register_t resultRegister = IncRegister(generator);
ScopeOutput(generator, "var ");
ScopeOutputRegister(generator, resultRegister);
ScopeOutput(generator, " = ");
ProcessNode(generator, TRI_AQL_NODE_MEMBER(node, 1));
ScopeOutput(generator, ";\n");
// enter the new variable in the symbol table
EnterSymbol(generator, nameNode->_value._value._string, resultRegister);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief generate code for an assignment (used in COLLECT)
////////////////////////////////////////////////////////////////////////////////
static void ProcessAssign (TRI_aql_codegen_js_t* const generator,
const TRI_aql_node_t* const node) {
TRI_aql_node_t* nameNode = TRI_AQL_NODE_MEMBER(node, 0);
TRI_aql_codegen_register_t resultRegister = IncRegister(generator);
TRI_aql_codegen_register_t lastResultRegister;
TRI_aql_codegen_scope_t* scope = CurrentScope(generator);
InitList(generator, resultRegister);
StartScope(generator, scope->_buffer, TRI_AQL_SCOPE_SUBQUERY, 0, 0, 0, resultRegister, "let");
ProcessNode(generator, TRI_AQL_NODE_MEMBER(node, 1));
lastResultRegister = CurrentScope(generator)->_resultRegister;
EndScope(generator);
if (lastResultRegister > 0) {
// result register was modified inside the scope, e.g. due to a SORT
resultRegister = lastResultRegister;
}
// enter the new variable in the symbol table
EnterSymbol(generator, nameNode->_value._value._string, resultRegister);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief generate code for filter keyword
////////////////////////////////////////////////////////////////////////////////
static void ProcessFilter (TRI_aql_codegen_js_t* const generator,
const TRI_aql_node_t* const node) {
TRI_aql_codegen_scope_t* scope = CurrentScope(generator);
ScopeOutput(generator, "if (!(");
ProcessNode(generator, TRI_AQL_NODE_MEMBER(node, 0));
ScopeOutput(generator, ")) {\n");
if (scope->_type == TRI_AQL_SCOPE_MAIN || scope->_type == TRI_AQL_SCOPE_SUBQUERY) {
// in these scopes, we must not generated a continue statement. this would be illegal
ScopeOutput(generator, "return [ ];\n");
}
else {
ScopeOutput(generator, "continue;\n");
}
ScopeOutput(generator, "}\n");
}
////////////////////////////////////////////////////////////////////////////////
/// @brief generate code for a node
///
/// this is a dispatcher function only
////////////////////////////////////////////////////////////////////////////////
static void ProcessNode (TRI_aql_codegen_js_t* generator, const TRI_aql_node_t* const data) {
TRI_aql_node_t* node = (TRI_aql_node_t*) data;
if (!node) {
return;
}
while (node) {
switch (node->_type) {
case AQL_NODE_VALUE:
ProcessValue(generator, node);
break;
case AQL_NODE_LIST:
ProcessList(generator, node);
break;
case AQL_NODE_ARRAY:
ProcessArray(generator, node);
break;
case AQL_NODE_ARRAY_ELEMENT:
ProcessArrayElement(generator, node);
break;
case AQL_NODE_COLLECTION:
ProcessCollection(generator, node);
break;
case AQL_NODE_REFERENCE:
ProcessReference(generator, node);
break;
case AQL_NODE_ATTRIBUTE:
ProcessAttribute(generator, node);
break;
case AQL_NODE_ATTRIBUTE_ACCESS:
ProcessAttributeAccess(generator, node);
break;
case AQL_NODE_INDEXED:
ProcessIndexed(generator, node);
break;
case AQL_NODE_EXPAND:
ProcessExpand(generator, node);
break;
case AQL_NODE_OPERATOR_UNARY_NOT:
ProcessUnaryNot(generator, node);
break;
case AQL_NODE_OPERATOR_UNARY_PLUS:
ProcessUnaryPlus(generator, node);
break;
case AQL_NODE_OPERATOR_UNARY_MINUS:
ProcessUnaryMinus(generator, node);
break;
case AQL_NODE_OPERATOR_BINARY_AND:
ProcessBinaryAnd(generator, node);
break;
case AQL_NODE_OPERATOR_BINARY_OR:
ProcessBinaryOr(generator, node);
break;
case AQL_NODE_OPERATOR_BINARY_PLUS:
ProcessBinaryPlus(generator, node);
break;
case AQL_NODE_OPERATOR_BINARY_MINUS:
ProcessBinaryMinus(generator, node);
break;
case AQL_NODE_OPERATOR_BINARY_TIMES:
ProcessBinaryTimes(generator, node);
break;
case AQL_NODE_OPERATOR_BINARY_DIV:
ProcessBinaryDivide(generator, node);
break;
case AQL_NODE_OPERATOR_BINARY_MOD:
ProcessBinaryModulus(generator, node);
break;
case AQL_NODE_OPERATOR_BINARY_EQ:
ProcessBinaryEqual(generator, node);
break;
case AQL_NODE_OPERATOR_BINARY_NE:
ProcessBinaryUnequal(generator, node);
break;
case AQL_NODE_OPERATOR_BINARY_LT:
ProcessBinaryLess(generator, node);
break;
case AQL_NODE_OPERATOR_BINARY_LE:
ProcessBinaryLessEqual(generator, node);
break;
case AQL_NODE_OPERATOR_BINARY_GT:
ProcessBinaryGreater(generator, node);
break;
case AQL_NODE_OPERATOR_BINARY_GE:
ProcessBinaryGreaterEqual(generator, node);
break;
case AQL_NODE_OPERATOR_BINARY_IN:
ProcessBinaryIn(generator, node);
break;
case AQL_NODE_OPERATOR_TERNARY:
ProcessTernary(generator, node);
break;
case AQL_NODE_FCALL:
ProcessFcall(generator, node);
break;
case AQL_NODE_FOR:
ProcessFor(generator, node);
break;
case AQL_NODE_LET:
ProcessLet(generator, node);
break;
case AQL_NODE_COLLECT:
ProcessCollect(generator, node);
break;
case AQL_NODE_ASSIGN:
ProcessAssign(generator, node);
break;
case AQL_NODE_FILTER:
ProcessFilter(generator, node);
break;
case AQL_NODE_LIMIT:
ProcessLimit(generator, node);
break;
case AQL_NODE_SORT:
ProcessSort(generator, node);
break;
case AQL_NODE_RETURN:
ProcessReturn(generator, node);
break;
case AQL_NODE_SUBQUERY:
ProcessSubquery(generator, node);
break;
default: {
}
}
// now handle subnodes
node = node->_next;
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief create code for the AST starting at the specified node
////////////////////////////////////////////////////////////////////////////////
TRI_aql_codegen_register_t CreateCode (TRI_aql_codegen_js_t* generator,
const TRI_aql_node_t* const node) {
TRI_aql_codegen_register_t startRegister = IncRegister(generator);
TRI_aql_codegen_register_t resultRegister;
assert(node);
StartScope(generator, &generator->_buffer, TRI_AQL_SCOPE_MAIN, 0, 0, 0, startRegister, "main");
InitList(generator, startRegister);
ProcessNode(generator, node);
resultRegister = CurrentScope(generator)->_resultRegister;
EndScope(generator);
return resultRegister;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief free memory associated with the code generator
////////////////////////////////////////////////////////////////////////////////
static void FreeGenerator (TRI_aql_codegen_js_t* const generator) {
TRI_DestroyVectorPointer(&generator->_scopes);
TRI_DestroyStringBuffer(&generator->_buffer);
TRI_DestroyStringBuffer(&generator->_functionBuffer);
TRI_Free(TRI_UNKNOWN_MEM_ZONE, generator);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief create a code generator
////////////////////////////////////////////////////////////////////////////////
static TRI_aql_codegen_js_t* CreateGenerator (TRI_aql_context_t* const context) {
TRI_aql_codegen_js_t* generator;
assert(context);
generator = (TRI_aql_codegen_js_t*) TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_aql_codegen_js_t), false);
if (!generator) {
return NULL;
}
generator->_context = context;
TRI_InitStringBuffer(&generator->_buffer, TRI_UNKNOWN_MEM_ZONE);
TRI_InitStringBuffer(&generator->_functionBuffer, TRI_UNKNOWN_MEM_ZONE);
TRI_InitVectorPointer(&generator->_scopes, TRI_UNKNOWN_MEM_ZONE);
generator->_error = false;
generator->_registerIndex = 0;
generator->_functionIndex = 0;
return generator;
}
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////
// -----------------------------------------------------------------------------
// --SECTION-- public functions
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @addtogroup Ahuacatl
/// @{
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief generate Javascript code for the AST nodes recursively
////////////////////////////////////////////////////////////////////////////////
char* TRI_GenerateCodeAql (TRI_aql_context_t* const context, const void* const data) {
TRI_aql_codegen_js_t* generator;
TRI_aql_codegen_register_t resultRegister;
char* code;
assert(context);
generator = CreateGenerator(context);
if (!generator) {
return NULL;
}
OutputString(&generator->_functionBuffer, "(function () {\n");
resultRegister = CreateCode(generator, data);
// append result
OutputString(&generator->_buffer, "return ");
OutputString(&generator->_buffer, REGISTER_PREFIX);
OutputInt(&generator->_buffer, (int64_t) resultRegister);
OutputString(&generator->_buffer, ";\n");
OutputString(&generator->_buffer, "})()");
if (generator->_error) {
FreeGenerator(generator);
return NULL;
}
// put everything together
code = TRI_Concatenate2String(generator->_functionBuffer._buffer, generator->_buffer._buffer);
FreeGenerator(generator);
if (code) {
LOG_TRACE("generated code: %s", code);
#ifdef TRI_DEBUG_AQL
printf("generated code: %s\n", code);
#endif
}
return code;
}
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////
// Local Variables:
// mode: outline-minor
// outline-regexp: "^\\(/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|// --SECTION--\\|/// @\\}\\)"
// End: