1
0
Fork 0
arangodb/Ahuacatl/ast-codegen-js.c

1254 lines
43 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/ast-codegen-js.h"
// -----------------------------------------------------------------------------
// --SECTION-- private functions
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @addtogroup Ahuacatl
/// @{
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief register a function name for easier later disposal
////////////////////////////////////////////////////////////////////////////////
static bool RegisterString (TRI_aql_codegen_t* const generator,
const char* const name) {
assert(generator);
TRI_PushBackVectorPointer(&generator->_strings, (char*) name);
return true;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief get a function name using the rule "f" + number
////////////////////////////////////////////////////////////////////////////////
static char* GetIndexedFunctionName (TRI_aql_codegen_t* const generator,
const size_t funcIndex) {
char* numberString;
char* functionName;
numberString = TRI_StringUInt64((uint64_t) funcIndex);
if (!numberString) {
return NULL;
}
functionName = TRI_Concatenate2String("f", numberString);
if (functionName) {
RegisterString(generator, functionName);
}
TRI_Free(numberString);
return functionName;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief get the next function name (using the rule "f" + number, number will
/// be increased on each call)
////////////////////////////////////////////////////////////////////////////////
static char* GetNextFunctionName (TRI_aql_codegen_t* const generator) {
char* functionName;
assert(generator);
functionName = GetIndexedFunctionName(generator, ++generator->_funcIndex);
return functionName;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief free a code scope
////////////////////////////////////////////////////////////////////////////////
static void FreeScope (TRI_aql_codegen_scope_t* const scope) {
assert(scope);
// note: scope->_funcName is freed globally
if (scope->_buffer) {
TRI_FreeStringBuffer(scope->_buffer);
}
TRI_Free(scope);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief create a code scope
////////////////////////////////////////////////////////////////////////////////
static TRI_aql_codegen_scope_t* CreateScope (TRI_aql_codegen_t* const generator,
const char* const funcName,
const TRI_aql_scope_type_e type) {
TRI_aql_codegen_scope_t* scope;
scope = (TRI_aql_codegen_scope_t*) TRI_Allocate(sizeof(TRI_aql_codegen_scope_t));
if (!scope) {
return NULL;
}
scope->_funcName = TRI_DuplicateString(funcName);
if (!scope->_funcName) {
FreeScope(scope);
return NULL;
}
RegisterString(generator, scope->_funcName);
scope->_variablePrefix = NULL;
scope->_indent = 0;
scope->_forLoops = 0;
scope->_type = type;
scope->_buffer = TRI_CreateStringBuffer();
if (!scope->_buffer) {
FreeScope(scope);
return NULL;
}
return scope;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief start a new code scope
////////////////////////////////////////////////////////////////////////////////
static bool StartScope (TRI_aql_codegen_t* const generator,
const TRI_aql_scope_type_e type,
const char* const funcName) {
TRI_aql_codegen_scope_t* scope;
assert(generator);
assert(funcName);
scope = CreateScope(generator, funcName, type);
if (!scope) {
return false;
}
TRI_PushBackVectorPointer(&generator->_scopes, (void*) scope);
return true;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief return the scope currently used
////////////////////////////////////////////////////////////////////////////////
static TRI_aql_codegen_scope_t* GetCurrentScope (TRI_aql_codegen_t* const generator) {
TRI_aql_codegen_scope_t* scope;
size_t length;
assert(generator);
length = generator->_scopes._length;
assert(length > 0);
scope = (TRI_aql_codegen_scope_t*) generator->_scopes._buffer[length -1];
return scope;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief increase the indent level in the current scope
////////////////////////////////////////////////////////////////////////////////
static void Indent (TRI_aql_codegen_t* const generator) {
TRI_aql_codegen_scope_t* scope;
assert(generator);
scope = GetCurrentScope(generator);
assert(scope);
scope->_indent++;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief decrease the indent level in the current scope
////////////////////////////////////////////////////////////////////////////////
static void Outdent (TRI_aql_codegen_t* const generator) {
TRI_aql_codegen_scope_t* scope;
assert(generator);
scope = GetCurrentScope(generator);
assert(scope);
assert(scope->_indent > 0);
--scope->_indent;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief add the current indentation to the output buffer in the current scope
////////////////////////////////////////////////////////////////////////////////
static void AppendIndent (TRI_aql_codegen_t* const generator) {
TRI_aql_codegen_scope_t* scope;
size_t i;
assert(generator);
scope = GetCurrentScope(generator);
for (i = 0; i <= scope->_forLoops + scope->_indent; ++i) {
TRI_AppendStringStringBuffer(scope->_buffer, " ");
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief remove the current scope from the stack
////////////////////////////////////////////////////////////////////////////////
static void RemoveCurrentScope (TRI_aql_codegen_t* const generator) {
TRI_aql_codegen_scope_t* scope;
size_t length;
assert(generator);
length = generator->_scopes._length;
assert(length > 0);
scope = (TRI_aql_codegen_scope_t*) TRI_RemoveVectorPointer(&generator->_scopes, length - 1);
FreeScope(scope);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief close all for loops in current scope
////////////////////////////////////////////////////////////////////////////////
static void CloseForLoops (TRI_aql_codegen_t* const generator) {
TRI_aql_codegen_scope_t* scope;
size_t i;
size_t n;
assert(generator);
scope = GetCurrentScope(generator);
assert(scope);
n = scope->_forLoops;
for (i = 0; i < n; ++i) {
scope->_forLoops--;
AppendIndent(generator);
TRI_AppendStringStringBuffer(scope->_buffer, "}\n");
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief write a function definition to the output buffer
////////////////////////////////////////////////////////////////////////////////
static bool AppendFunction (TRI_aql_codegen_t* const generator,
const TRI_aql_scope_type_e type,
const char* const name,
const char* const body) {
assert(generator);
assert(name);
assert(body);
TRI_AppendStringStringBuffer(&generator->_buffer, "\nfunction ");
TRI_AppendStringStringBuffer(&generator->_buffer, name);
TRI_AppendStringStringBuffer(&generator->_buffer, "(");
if (type == AQL_SCOPE_COMPARE) {
TRI_AppendStringStringBuffer(&generator->_buffer, "l, r");
}
else {
TRI_AppendStringStringBuffer(&generator->_buffer, "previous");
}
TRI_AppendStringStringBuffer(&generator->_buffer, ") {\n");
if (type != AQL_SCOPE_COMPARE) {
TRI_AppendStringStringBuffer(&generator->_buffer, " var $ = AHUACATL_CLONE(previous);\n");
}
TRI_AppendStringStringBuffer(&generator->_buffer, body);
TRI_AppendStringStringBuffer(&generator->_buffer, "\n");
TRI_AppendStringStringBuffer(&generator->_buffer, "}\n");
return true;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief end a scope. this will also write the scope's function to the
/// output buffer
////////////////////////////////////////////////////////////////////////////////
static char* EndScope (TRI_aql_codegen_t* const generator) {
TRI_aql_codegen_scope_t* scope;
TRI_string_buffer_t* body;
char* funcName;
assert(generator);
scope = GetCurrentScope(generator);
assert(scope);
funcName = scope->_funcName;
body = TRI_CreateStringBuffer();
if (!body) {
// oom
generator->_error = true;
}
else {
if (scope->_type == AQL_SCOPE_RESULT) {
TRI_AppendStringStringBuffer(body, " var result = [];\n");
TRI_AppendStringStringBuffer(body, scope->_buffer->_buffer);
TRI_AppendStringStringBuffer(body, " return result;");
}
else {
TRI_AppendStringStringBuffer(body, scope->_buffer->_buffer);
}
AppendFunction(generator, scope->_type, funcName, body->_buffer);
TRI_FreeStringBuffer(body);
}
generator->_funcName = funcName;
RemoveCurrentScope(generator);
return funcName;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief increase the number of for loops in the current scope by one
////////////////////////////////////////////////////////////////////////////////
static void IncreaseForCount (TRI_aql_codegen_t* const generator) {
TRI_aql_codegen_scope_t* scope;
size_t length;
assert(generator);
length = generator->_scopes._length;
assert(length > 0);
scope = (TRI_aql_codegen_scope_t*) generator->_scopes._buffer[length - 1];
assert(scope);
scope->_forLoops++;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief append code the current scope's output buffer
////////////////////////////////////////////////////////////////////////////////
static void AppendCode (TRI_aql_codegen_t* const generator, const char* const code) {
TRI_aql_codegen_scope_t* scope;
assert(generator);
scope = GetCurrentScope(generator);
assert(scope);
TRI_AppendStringStringBuffer(scope->_buffer, code);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief append the value of a value node to the current scope's output buffer
////////////////////////////////////////////////////////////////////////////////
static bool AppendValue (TRI_aql_codegen_t* const generator,
const TRI_aql_node_value_t* const node) {
TRI_aql_codegen_scope_t* scope;
assert(generator);
scope = GetCurrentScope(generator);
assert(scope);
switch (node->_value._type) {
case AQL_TYPE_FAIL:
TRI_AppendStringStringBuffer(scope->_buffer, "fail");
break;
case AQL_TYPE_NULL:
TRI_AppendStringStringBuffer(scope->_buffer, "null");
break;
case AQL_TYPE_BOOL:
if (node->_value._value._bool) {
TRI_AppendStringStringBuffer(scope->_buffer, "true");
}
else {
TRI_AppendStringStringBuffer(scope->_buffer, "false");
}
break;
case AQL_TYPE_INT:
TRI_AppendInt64StringBuffer(scope->_buffer, node->_value._value._int);
break;
case AQL_TYPE_DOUBLE:
TRI_AppendDoubleStringBuffer(scope->_buffer, node->_value._value._double);
break;
case AQL_TYPE_STRING: {
char* escapedString;
size_t outLength;
TRI_AppendStringStringBuffer(scope->_buffer, "'");
escapedString = TRI_EscapeUtf8String(node->_value._value._string, strlen(node->_value._value._string), false, &outLength);
if (escapedString) {
TRI_AppendStringStringBuffer(scope->_buffer, escapedString);
TRI_Free(escapedString);
}
TRI_AppendStringStringBuffer(scope->_buffer, "'");
break;
}
}
return true;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief set a variable prefix for all variables to be printed
////////////////////////////////////////////////////////////////////////////////
static void SetVariablePrefix (TRI_aql_codegen_t* const generator,
const char* const prefix) {
TRI_aql_codegen_scope_t* scope;
assert(generator);
scope = GetCurrentScope(generator);
assert(scope);
scope->_variablePrefix = (char*) prefix;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief print a variable name in the current scope, enclosed in $['...']
////////////////////////////////////////////////////////////////////////////////
static bool AppendVarname (TRI_aql_codegen_t* const generator,
const TRI_aql_node_t* const data) {
TRI_aql_node_variable_t* node = (TRI_aql_node_variable_t*) data;
AppendCode(generator, "$['");
AppendCode(generator, node->_name);
AppendCode(generator, "']");
return true;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief print a variable name in the current scope, without modifications
////////////////////////////////////////////////////////////////////////////////
static bool AppendVarname0 (TRI_aql_codegen_t* const generator,
const TRI_aql_node_t* const data) {
TRI_aql_node_variable_t* node = (TRI_aql_node_variable_t*) data;
AppendCode(generator, node->_name);
return true;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief print a variable name in the current scope, prepended by "_"
////////////////////////////////////////////////////////////////////////////////
static bool AppendVarname1 (TRI_aql_codegen_t* const generator,
const TRI_aql_node_t* const data) {
TRI_aql_node_variable_t* node = (TRI_aql_node_variable_t*) data;
AppendCode(generator, "_");
AppendCode(generator, node->_name);
return true;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief print a variable name in the current scope, prepended by "__"
////////////////////////////////////////////////////////////////////////////////
static bool AppendVarname2 (TRI_aql_codegen_t* const generator,
const TRI_aql_node_t* const data) {
TRI_aql_node_variable_t* node = (TRI_aql_node_variable_t*) data;
AppendCode(generator, "__");
AppendCode(generator, node->_name);
return true;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief print hasOwnProperty(...) if construct in current scope
////////////////////////////////////////////////////////////////////////////////
static bool AppendOwnPropertyName (TRI_aql_codegen_t* const generator,
const char* const name) {
AppendIndent(generator);
AppendCode(generator, "if (!__");
AppendCode(generator, name);
AppendCode(generator, ".hasOwnProperty(_");
AppendCode(generator, name);
AppendCode(generator, ")) {\n");
Indent(generator);
AppendIndent(generator);
AppendCode(generator, "continue;\n");
Outdent(generator);
AppendIndent(generator);
AppendCode(generator, "}\n");
return true;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief print hasOwnProperty(...) if construct in current scope
////////////////////////////////////////////////////////////////////////////////
static bool AppendOwnPropertyVar (TRI_aql_codegen_t* const generator,
const TRI_aql_node_t* const data) {
AppendIndent(generator);
AppendCode(generator, "if (!");
AppendVarname2(generator, data);
AppendCode(generator, ".hasOwnProperty(");
AppendVarname1(generator, data);
AppendCode(generator, ")) {\n");
Indent(generator);
AppendIndent(generator);
AppendCode(generator, "continue;\n");
Outdent(generator);
AppendIndent(generator);
AppendCode(generator, "}\n");
return true;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief generate the code for an individual node and all subnodes
////////////////////////////////////////////////////////////////////////////////
static void GenerateCode (TRI_aql_codegen_t* const generator,
const TRI_aql_node_t* const data) {
TRI_aql_node_t* node;
if (!data) {
return;
}
node = (TRI_aql_node_t*) data;
while (node) {
switch (node->_type) {
case AQL_NODE_VALUE:
AppendValue(generator, (TRI_aql_node_value_t*) node);
break;
case AQL_NODE_LIST: {
TRI_vector_pointer_t* values = (TRI_vector_pointer_t*) &((TRI_aql_node_list_t*) node)->_values;
size_t i, n;
AppendCode(generator, "[ ");
n = values->_length;
for (i = 0; i < n; ++i) {
if (i > 0) {
AppendCode(generator, ", ");
}
GenerateCode(generator, (TRI_aql_node_t*) values->_buffer[i]);
}
AppendCode(generator, " ]");
break;
}
case AQL_NODE_ARRAY: {
TRI_associative_pointer_t* values = (TRI_associative_pointer_t*) &((TRI_aql_node_array_t*) node)->_values;
size_t i, n;
bool found = false;
AppendCode(generator, "{ ");
n = values->_nrAlloc;
for (i = 0; i < n; ++i) {
TRI_aql_node_array_element_t* element = (TRI_aql_node_array_element_t*) values->_table[i];
if (!element) {
continue;
}
if (found) {
AppendCode(generator, ", ");
}
found = true;
AppendCode(generator, "'");
AppendCode(generator, element->_name);
AppendCode(generator, "': ");
GenerateCode(generator, (TRI_aql_node_t*) element->_value);
}
AppendCode(generator, " }");
break;
}
case AQL_NODE_FOR:
AppendIndent(generator);
AppendCode(generator, "var ");
AppendVarname2(generator, ((TRI_aql_node_for_t*) node)->_variable);
AppendCode(generator, " = AHUACATL_LIST(");
GenerateCode(generator, ((TRI_aql_node_for_t*) node)->_expression);
AppendCode(generator, ");\n");
AppendIndent(generator);
AppendCode(generator, "for (var ");
AppendVarname1(generator, ((TRI_aql_node_for_t*) node)->_variable);
AppendCode(generator, " in ");
AppendVarname2(generator, ((TRI_aql_node_for_t*) node)->_variable);
AppendCode(generator, ") {\n");
IncreaseForCount(generator);
AppendOwnPropertyVar(generator, ((TRI_aql_node_for_t*) node)->_variable);
AppendIndent(generator);
AppendVarname(generator, ((TRI_aql_node_for_t*) node)->_variable);
AppendCode(generator, " = ");
AppendVarname2(generator, ((TRI_aql_node_for_t*) node)->_variable);
AppendCode(generator, "[");
AppendVarname1(generator, ((TRI_aql_node_for_t*) node)->_variable);
AppendCode(generator, "];\n");
break;
case AQL_NODE_COLLECT: {
TRI_aql_node_list_t* list = (TRI_aql_node_list_t*) (((TRI_aql_node_collect_t*) node)->_list);
TRI_vector_pointer_t* values = (TRI_vector_pointer_t*) &list->_values;
char* previousFunction;
char* sortFuncName;
char* groupFuncName;
size_t i, n;
sortFuncName = GetNextFunctionName(generator);
StartScope(generator, AQL_SCOPE_COMPARE, sortFuncName);
AppendIndent(generator);
AppendCode(generator, "var lhs, rhs;\n");
n = list->_values._length;
for (i = 0; i < n; ++i) {
TRI_aql_node_assign_t* element = (TRI_aql_node_assign_t*) values->_buffer[i];
AppendIndent(generator);
AppendCode(generator, "lhs = ");
SetVariablePrefix(generator, "l");
GenerateCode(generator, element->_expression);
AppendCode(generator, ";\n");
AppendIndent(generator);
AppendCode(generator, "rhs = ");
SetVariablePrefix(generator, "r");
GenerateCode(generator, element->_expression);
AppendCode(generator, ";\n");
SetVariablePrefix(generator, NULL);
AppendIndent(generator);
AppendCode(generator, "if (AHUACATL_RELATIONAL_LESS(lhs, rhs)) {\n");
Indent(generator);
AppendIndent(generator);
AppendCode(generator, "return -1;\n");
Outdent(generator);
AppendIndent(generator);
AppendCode(generator, "}\n");
AppendIndent(generator);
AppendCode(generator, "if (AHUACATL_RELATIONAL_GREATER(lhs, rhs)) {\n");
Indent(generator);
AppendIndent(generator);
AppendCode(generator, "return 1;\n");
Outdent(generator);
AppendIndent(generator);
AppendCode(generator, "}\n");
}
AppendIndent(generator);
AppendCode(generator, "return 0;");
EndScope(generator);
groupFuncName = GetNextFunctionName(generator);
StartScope(generator, AQL_SCOPE_RESULT, groupFuncName);
AppendIndent(generator);
AppendCode(generator, "return { ");
n = values->_length;
for (i = 0; i < n; ++i) {
TRI_aql_node_assign_t* element = (TRI_aql_node_assign_t*) values->_buffer[i];
if (i > 0) {
AppendCode(generator, ", ");
}
AppendCode(generator, "'");
AppendCode(generator, ((TRI_aql_node_variable_t*) element->_variable)->_name);
AppendCode(generator, "' : ");
GenerateCode(generator, element->_expression);
}
AppendCode(generator, " };\n");
EndScope(generator);
AppendIndent(generator);
AppendCode(generator, "result.push(AHUACATL_CLONE($));\n");
CloseForLoops(generator);
AppendCode(generator, " result = AHUACATL_GROUP(result, ");
AppendCode(generator, sortFuncName);
AppendCode(generator, ", ");
AppendCode(generator, groupFuncName);
if (((TRI_aql_node_collect_t*) node)->_into) {
AppendCode(generator, ", '");
AppendVarname0(generator, ((TRI_aql_node_collect_t*) node)->_into);
AppendCode(generator, "'");
}
AppendCode(generator, ");\n");
previousFunction = EndScope(generator);
StartScope(generator, AQL_SCOPE_RESULT, GetNextFunctionName(generator));
AppendIndent(generator);
AppendCode(generator, "var __group = ");
AppendCode(generator, previousFunction);
AppendCode(generator, "($);\n");
AppendIndent(generator);
AppendCode(generator, "for (var _group in __group) {\n");
IncreaseForCount(generator);
AppendIndent(generator);
AppendOwnPropertyName(generator, "group");
AppendIndent(generator);
AppendCode(generator, "var $ = __group[_group];\n");
break;
}
case AQL_NODE_EXPAND: {
char* funcName = GetNextFunctionName(generator);
if (!funcName) {
assert(false);
// TODO: oom
}
StartScope(generator, AQL_SCOPE_RESULT, funcName);
AppendIndent(generator);
AppendCode(generator, "var __e = AHUACATL_LIST($);\n");
AppendIndent(generator);
AppendCode(generator, "for (var _e in __e) {\n");
Indent(generator);
AppendOwnPropertyName(generator, "e");
AppendIndent(generator);
AppendCode(generator, "result.push(");
GenerateCode(generator, ((TRI_aql_node_expand_t*) node)->_expansion);
AppendCode(generator, ");\n");
Outdent(generator);
AppendIndent(generator);
AppendCode(generator, "}\n");
EndScope(generator);
AppendCode(generator, funcName);
AppendCode(generator, "(");
GenerateCode(generator, ((TRI_aql_node_expand_t*) node)->_expanded);
AppendCode(generator, ")");
break;
}
case AQL_NODE_ASSIGN:
AppendIndent(generator);
AppendVarname(generator, ((TRI_aql_node_assign_t*) node)->_variable);
AppendCode(generator, " = ");
GenerateCode(generator, ((TRI_aql_node_assign_t*) node)->_expression);
AppendCode(generator, ";\n");
break;
case AQL_NODE_SUBQUERY: {
char* funcName = GetNextFunctionName(generator);
if (!funcName) {
assert(false);
// TODO: oom
}
StartScope(generator, AQL_SCOPE_RESULT, funcName);
GenerateCode(generator, ((TRI_aql_node_subquery_t*) node)->_query);
AppendCode(generator, funcName);
AppendCode(generator, "($)");
break;
}
case AQL_NODE_FILTER:
AppendIndent(generator);
AppendCode(generator, "if (!(");
GenerateCode(generator, ((TRI_aql_node_filter_t*) node)->_expression);
AppendCode(generator, ")) {\n");
Indent(generator);
AppendIndent(generator);
AppendCode(generator, "continue;\n");
Outdent(generator);
AppendIndent(generator);
AppendCode(generator, "}\n");
break;
case AQL_NODE_RETURN:
AppendIndent(generator);
AppendCode(generator, "result.push(");
GenerateCode(generator, ((TRI_aql_node_return_t*) node)->_expression);
AppendCode(generator, ");\n");
CloseForLoops(generator);
EndScope(generator);
break;
case AQL_NODE_LIMIT: {
char* previousFunction;
char* value;
AppendIndent(generator);
AppendCode(generator, "result.push($);\n");
CloseForLoops(generator);
AppendCode(generator, " result = AHUACATL_LIMIT(result, ");
value = TRI_StringInt64(((TRI_aql_node_limit_t*) node)->_offset);
if (!value) {
return; // todo: OOM
}
RegisterString(generator, value);
AppendCode(generator, value);
AppendCode(generator, ", ");
value = TRI_StringInt64(((TRI_aql_node_limit_t*) node)->_count);
if (!value) {
return; // todo: OOM
}
RegisterString(generator, value);
AppendCode(generator, value);
AppendCode(generator, ");\n");
previousFunction = EndScope(generator);
StartScope(generator, AQL_SCOPE_RESULT, GetNextFunctionName(generator));
AppendIndent(generator);
AppendCode(generator, "var __limit = ");
AppendCode(generator, previousFunction);
AppendCode(generator, "($);\n");
AppendCode(generator, "for (var _limit in __limit) {\n");
IncreaseForCount(generator);
AppendIndent(generator);
AppendOwnPropertyName(generator, "limit");
AppendIndent(generator);
AppendCode(generator, "var $ = __limit[_limit];\n");
break;
}
case AQL_NODE_SORT: {
TRI_aql_codegen_scope_t* scope = GetCurrentScope(generator);
TRI_aql_node_list_t* list = (TRI_aql_node_list_t*) (((TRI_aql_node_sort_t*) node)->_list);
char* previousFunction;
char* sortFuncName;
size_t i;
size_t n;
previousFunction = scope->_funcName;
// todo: OOM
sortFuncName = GetNextFunctionName(generator);
StartScope(generator, AQL_SCOPE_COMPARE, sortFuncName);
AppendIndent(generator);
AppendCode(generator, "var lhs, rhs;\n");
n = list->_values._length;
for (i = 0; i < n; ++i) {
TRI_aql_node_sort_element_t* element = (TRI_aql_node_sort_element_t*) list->_values._buffer[i];
AppendIndent(generator);
AppendCode(generator, "lhs = ");
SetVariablePrefix(generator, "l");
GenerateCode(generator, element->_expression);
AppendCode(generator, ";\n");
AppendIndent(generator);
AppendCode(generator, "rhs = ");
SetVariablePrefix(generator, "r");
GenerateCode(generator, element->_expression);
AppendCode(generator, ";\n");
SetVariablePrefix(generator, NULL);
AppendIndent(generator);
AppendCode(generator, "if (AHUACATL_RELATIONAL_LESS(lhs, rhs)) {\n");
Indent(generator);
AppendIndent(generator);
if (element->_ascending) {
AppendCode(generator, "return -1;\n");
}
else {
AppendCode(generator, "return 1;\n");
}
Outdent(generator);
AppendIndent(generator);
AppendCode(generator, "}\n");
AppendIndent(generator);
AppendCode(generator, "if (AHUACATL_RELATIONAL_GREATER(lhs, rhs)) {\n");
Indent(generator);
AppendIndent(generator);
if (element->_ascending) {
AppendCode(generator, "return 1;\n");
}
else {
AppendCode(generator, "return -1;\n");
}
Outdent(generator);
AppendIndent(generator);
AppendCode(generator, "}\n");
}
AppendIndent(generator);
AppendCode(generator, "return 0;");
EndScope(generator);
AppendIndent(generator);
AppendCode(generator, "result.push(AHUACATL_CLONE($));\n");
CloseForLoops(generator);
AppendCode(generator, " AHUACATL_SORT(result, ");
AppendCode(generator, sortFuncName);
AppendCode(generator, ");\n");
EndScope(generator);
StartScope(generator, AQL_SCOPE_RESULT, GetNextFunctionName(generator));
AppendIndent(generator);
AppendCode(generator, "var __sort = ");
AppendCode(generator, previousFunction);
AppendCode(generator, "($);\n");
AppendIndent(generator);
AppendCode(generator, "for (var _sort in __sort) {\n");
IncreaseForCount(generator);
AppendOwnPropertyName(generator, "sort");
AppendIndent(generator);
AppendCode(generator, "var $ = __sort[_sort];\n");
break;
}
case AQL_NODE_VARIABLE:
AppendVarname(generator, node);
break;
case AQL_NODE_REFERENCE:
if (((TRI_aql_node_reference_t*) node)->_isCollection) {
AppendCode(generator, "AHUACATL_GET_DOCUMENTS('");
AppendCode(generator, ((TRI_aql_node_reference_t*) node)->_name);
AppendCode(generator, "')");
}
else {
TRI_aql_codegen_scope_t* scope= GetCurrentScope(generator);
if (scope->_variablePrefix) {
AppendCode(generator, scope->_variablePrefix);
AppendCode(generator, "['");
AppendCode(generator, ((TRI_aql_node_reference_t*) node)->_name);
AppendCode(generator, "']");
}
else {
AppendCode(generator, "$['");
AppendCode(generator, ((TRI_aql_node_reference_t*) node)->_name);
AppendCode(generator, "']");
}
}
break;
case AQL_NODE_PARAMETER:
AppendCode(generator, "AHUACATL_GET_PARAMETER('");
AppendCode(generator, ((TRI_aql_node_parameter_t*) node)->_name);
AppendCode(generator, "')");
break;
case AQL_NODE_ATTRIBUTE_ACCESS:
AppendCode(generator, "AHUACATL_DOCUMENT_MEMBER(");
GenerateCode(generator, ((TRI_aql_node_attribute_access_t*) node)->_accessed);
AppendCode(generator, ", '");
AppendCode(generator, ((TRI_aql_node_attribute_access_t*) node)->_name);
AppendCode(generator, "')");
break;
case AQL_NODE_INDEXED:
AppendCode(generator, "AHUACATL_GET_INDEX(");
GenerateCode(generator, ((TRI_aql_node_indexed_t*) node)->_accessed);
AppendCode(generator, ", ");
GenerateCode(generator, ((TRI_aql_node_indexed_t*) node)->_index);
AppendCode(generator, ")");
break;
case AQL_NODE_ATTRIBUTE:
AppendCode(generator, "AHUACATL_DOCUMENT_MEMBER(__e[_e], '");
AppendCode(generator, ((TRI_aql_node_attribute_t*) node)->_name);
AppendCode(generator, "')");
break;
case AQL_NODE_OPERATOR_UNARY_NOT:
AppendCode(generator, "AHUACATL_LOGICAL_NOT(");
GenerateCode(generator, ((TRI_aql_node_operator_unary_t*) node)->_operand);
AppendCode(generator, ")");
break;
case AQL_NODE_OPERATOR_UNARY_PLUS:
AppendCode(generator, "AHUACATL_UNARY_PLUS(");
GenerateCode(generator, ((TRI_aql_node_operator_unary_t*) node)->_operand);
AppendCode(generator, ")");
break;
case AQL_NODE_OPERATOR_UNARY_MINUS:
AppendCode(generator, "AHUACATL_UNARY_MINUS(");
GenerateCode(generator, ((TRI_aql_node_operator_unary_t*) node)->_operand);
AppendCode(generator, ")");
break;
case AQL_NODE_OPERATOR_BINARY_AND:
AppendCode(generator, "AHUACATL_LOGICAL_AND(");
GenerateCode(generator, ((TRI_aql_node_operator_binary_t*) node)->_lhs);
AppendCode(generator, ", ");
GenerateCode(generator, ((TRI_aql_node_operator_binary_t*) node)->_rhs);
AppendCode(generator, ")");
break;
case AQL_NODE_OPERATOR_BINARY_OR:
AppendCode(generator, "AHUACATL_LOGICAL_OR(");
GenerateCode(generator, ((TRI_aql_node_operator_binary_t*) node)->_lhs);
AppendCode(generator, ", ");
GenerateCode(generator, ((TRI_aql_node_operator_binary_t*) node)->_rhs);
AppendCode(generator, ")");
break;
case AQL_NODE_OPERATOR_BINARY_PLUS:
AppendCode(generator, "AHUACATL_ARITHMETIC_PLUS(");
GenerateCode(generator, ((TRI_aql_node_operator_binary_t*) node)->_lhs);
AppendCode(generator, ", ");
GenerateCode(generator, ((TRI_aql_node_operator_binary_t*) node)->_rhs);
AppendCode(generator, ")");
break;
case AQL_NODE_OPERATOR_BINARY_MINUS:
AppendCode(generator, "AHUACATL_ARITHMETIC_MINUS(");
GenerateCode(generator, ((TRI_aql_node_operator_binary_t*) node)->_lhs);
AppendCode(generator, ", ");
GenerateCode(generator, ((TRI_aql_node_operator_binary_t*) node)->_rhs);
AppendCode(generator, ")");
break;
case AQL_NODE_OPERATOR_BINARY_TIMES:
AppendCode(generator, "AHUACATL_ARITHMETIC_TIMES(");
GenerateCode(generator, ((TRI_aql_node_operator_binary_t*) node)->_lhs);
AppendCode(generator, ", ");
GenerateCode(generator, ((TRI_aql_node_operator_binary_t*) node)->_rhs);
AppendCode(generator, ")");
break;
case AQL_NODE_OPERATOR_BINARY_DIV:
AppendCode(generator, "AHUACATL_ARITHMETIC_DIVIDE(");
GenerateCode(generator, ((TRI_aql_node_operator_binary_t*) node)->_lhs);
AppendCode(generator, ", ");
GenerateCode(generator, ((TRI_aql_node_operator_binary_t*) node)->_rhs);
AppendCode(generator, ")");
break;
case AQL_NODE_OPERATOR_BINARY_MOD:
AppendCode(generator, "AHUACATL_ARITHMETIC_MODULUS(");
GenerateCode(generator, ((TRI_aql_node_operator_binary_t*) node)->_lhs);
AppendCode(generator, ", ");
GenerateCode(generator, ((TRI_aql_node_operator_binary_t*) node)->_rhs);
AppendCode(generator, ")");
break;
case AQL_NODE_OPERATOR_BINARY_EQ:
AppendCode(generator, "AHUACATL_RELATIONAL_EQUAL(");
GenerateCode(generator, ((TRI_aql_node_operator_binary_t*) node)->_lhs);
AppendCode(generator, ", ");
GenerateCode(generator, ((TRI_aql_node_operator_binary_t*) node)->_rhs);
AppendCode(generator, ")");
break;
case AQL_NODE_OPERATOR_BINARY_NE:
AppendCode(generator, "AHUACATL_RELATIONAL_UNEQUAL(");
GenerateCode(generator, ((TRI_aql_node_operator_binary_t*) node)->_lhs);
AppendCode(generator, ", ");
GenerateCode(generator, ((TRI_aql_node_operator_binary_t*) node)->_rhs);
AppendCode(generator, ")");
break;
case AQL_NODE_OPERATOR_BINARY_LT:
AppendCode(generator, "AHUACATL_RELATIONAL_LESS(");
GenerateCode(generator, ((TRI_aql_node_operator_binary_t*) node)->_lhs);
AppendCode(generator, ", ");
GenerateCode(generator, ((TRI_aql_node_operator_binary_t*) node)->_rhs);
AppendCode(generator, ")");
break;
case AQL_NODE_OPERATOR_BINARY_LE:
AppendCode(generator, "AHUACATL_RELATIONAL_LESSEQUAL(");
GenerateCode(generator, ((TRI_aql_node_operator_binary_t*) node)->_lhs);
AppendCode(generator, ", ");
GenerateCode(generator, ((TRI_aql_node_operator_binary_t*) node)->_rhs);
AppendCode(generator, ")");
break;
case AQL_NODE_OPERATOR_BINARY_GT:
AppendCode(generator, "AHUACATL_RELATIONAL_GREATER(");
GenerateCode(generator, ((TRI_aql_node_operator_binary_t*) node)->_lhs);
AppendCode(generator, ", ");
GenerateCode(generator, ((TRI_aql_node_operator_binary_t*) node)->_rhs);
AppendCode(generator, ")");
break;
case AQL_NODE_OPERATOR_BINARY_GE:
AppendCode(generator, "AHUACATL_RELATIONAL_GREATEREQUAL(");
GenerateCode(generator, ((TRI_aql_node_operator_binary_t*) node)->_lhs);
AppendCode(generator, ", ");
GenerateCode(generator, ((TRI_aql_node_operator_binary_t*) node)->_rhs);
AppendCode(generator, ")");
break;
case AQL_NODE_OPERATOR_BINARY_IN:
AppendCode(generator, "AHUACATL_RELATIONAL_IN(");
GenerateCode(generator, ((TRI_aql_node_operator_binary_t*) node)->_lhs);
AppendCode(generator, ", ");
GenerateCode(generator, ((TRI_aql_node_operator_binary_t*) node)->_rhs);
AppendCode(generator, ")");
break;
case AQL_NODE_FCALL: {
AppendCode(generator, "AHUACATL_FCALL(");
AppendCode(generator, ((TRI_aql_node_fcall_t*) node)->_name);
AppendCode(generator, ", ");
GenerateCode(generator, ((TRI_aql_node_fcall_t*) node)->_parameters);
AppendCode(generator, ")");
}
default:
break;
}
node = node->_next;
}
}
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////
// -----------------------------------------------------------------------------
// --SECTION-- public functions
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @addtogroup Ahuacatl
/// @{
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief create a code generator
////////////////////////////////////////////////////////////////////////////////
TRI_aql_codegen_t* TRI_CreateCodegenAql (void) {
TRI_aql_codegen_t* generator;
generator = (TRI_aql_codegen_t*) TRI_Allocate(sizeof(TRI_aql_codegen_t));
if (!generator) {
return NULL;
}
generator->_error = false;
generator->_funcIndex = 0;
generator->_funcName = NULL;
TRI_InitStringBuffer(&generator->_buffer);
TRI_InitVectorPointer(&generator->_strings);
TRI_InitVectorPointer(&generator->_scopes);
if (!StartScope(generator, AQL_SCOPE_RESULT, GetNextFunctionName(generator))) {
TRI_FreeCodegenAql(generator);
return NULL;
}
return generator;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief free the code generator
////////////////////////////////////////////////////////////////////////////////
void TRI_FreeCodegenAql (TRI_aql_codegen_t* const generator) {
size_t i;
assert(generator);
TRI_DestroyStringBuffer(&generator->_buffer);
TRI_DestroyVectorPointer(&generator->_scopes);
// free strings
i = generator->_strings._length;
while (i--) {
void* string = generator->_strings._buffer[i];
if (string) {
TRI_Free(string);
}
}
TRI_DestroyVectorPointer(&generator->_strings);
TRI_Free(generator);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief generate Javascript code for the AST nodes recursively
////////////////////////////////////////////////////////////////////////////////
char* TRI_GenerateCodeAql (const void* const data) {
TRI_aql_node_t* node = (TRI_aql_node_t*) data;
TRI_aql_codegen_t* generator;
char* code = NULL;
if (!node) {
// todo: handle errors
return code;
}
generator = TRI_CreateCodegenAql();
if (!generator) {
// todo: handle errors
return code;
}
TRI_AppendStringStringBuffer(&generator->_buffer, "(function() {\n");
GenerateCode(generator, node);
if (!generator->_funcName) {
generator->_error = true;
}
if (!generator->_error) {
char* funcName = generator->_funcName;
TRI_AppendStringStringBuffer(&generator->_buffer, "try {\n");
TRI_AppendStringStringBuffer(&generator->_buffer, " return ");
TRI_AppendStringStringBuffer(&generator->_buffer, funcName);
TRI_AppendStringStringBuffer(&generator->_buffer, "( { } );\n");
TRI_AppendStringStringBuffer(&generator->_buffer, "}\n");
TRI_AppendStringStringBuffer(&generator->_buffer, "catch (e) {\n");
TRI_AppendStringStringBuffer(&generator->_buffer, "print(e);\n");
TRI_AppendStringStringBuffer(&generator->_buffer, "}\n");
TRI_AppendStringStringBuffer(&generator->_buffer, "})();\n");
code = TRI_DuplicateString(generator->_buffer._buffer);
LOG_TRACE("generated code:\n%s\n",code);
//printf("generated code:\n%s\n",code);
}
TRI_FreeCodegenAql(generator);
return code;
}
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////
// Local Variables:
// mode: outline-minor
// outline-regexp: "^\\(/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|// --SECTION--\\|/// @\\}\\)"
// End: