589 lines
23 KiB
C
589 lines
23 KiB
C
#include "include/conf.h"
|
|
|
|
#include <vessel/def.h>
|
|
|
|
#include "include/temple.h"
|
|
#include "include/temple-tokens.h"
|
|
#include "include/temple-parsers.h"
|
|
|
|
static bool vw_Temple_compile_ast(vw_TempleASTNode *root,
|
|
vs_Logger *logger,
|
|
const char *input,
|
|
const char *end);
|
|
|
|
bool vw_Temple_init(vw_Temple *ctx, bool auto_escape, vs_Logger *logger) {
|
|
if (!ctx) {
|
|
return false;
|
|
}
|
|
|
|
ctx->auto_escape = auto_escape;
|
|
ctx->logger = logger;
|
|
|
|
if (!vs_HMap_init(&ctx->vars)) {
|
|
return false;
|
|
}
|
|
|
|
ctx->root = VS_MALLOC(sizeof(vw_TempleASTNode));
|
|
ctx->root->type = vw_TempleASTNodeType_template;
|
|
ctx->root->next = NULL;
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool vw_Temple_compile_ast(vw_TempleASTNode *root,
|
|
vs_Logger *logger,
|
|
const char *input,
|
|
const char *end) {
|
|
vw_TempleASTNode **current = &root->next; /* pointer to next field to append nodes */
|
|
vw_TempleASTNode *token = NULL; /* tree child */
|
|
|
|
const char *readp = input;
|
|
const char *text_start = readp;
|
|
|
|
while (readp <= end && *readp) {
|
|
if (*readp != '{') {
|
|
/* Normal character */
|
|
++readp;
|
|
continue;
|
|
}
|
|
|
|
if (readp + 1 > end) {
|
|
/* Has a { but no characters after */
|
|
break;
|
|
}
|
|
|
|
const char dynop = *(readp + 1);
|
|
|
|
switch (dynop) {
|
|
case '#':
|
|
/* Comment node */
|
|
if (readp > text_start) {
|
|
*current = vw_TempleASTNode_new_text(text_start, readp);
|
|
current = &(*current)->next;
|
|
}
|
|
token = vw_TempleParser_parse_comment(logger, input, end, &readp);
|
|
if (!token) {
|
|
vw_TempleASTNode_free(root);
|
|
return false;
|
|
}
|
|
text_start = readp;
|
|
*current = token;
|
|
current = &(*current)->next;
|
|
break;
|
|
|
|
case '{':
|
|
/* Output node */
|
|
if (readp > text_start) {
|
|
*current = vw_TempleASTNode_new_text(text_start, readp);
|
|
current = &(*current)->next;
|
|
}
|
|
token = vw_TempleParser_parse_output(logger, input, end, &readp);
|
|
if (!token) {
|
|
vw_TempleASTNode_free(root);
|
|
return false;
|
|
}
|
|
text_start = readp;
|
|
*current = token;
|
|
current = &(*current)->next;
|
|
break;
|
|
|
|
default:
|
|
++readp;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (readp > end) {
|
|
vw_TempleASTNode_free(root);
|
|
return false;
|
|
}
|
|
|
|
/* Add trailing text */
|
|
if (readp > text_start) {
|
|
*current = vw_TempleASTNode_new_text(text_start, readp);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool vw_Temple_compile(vw_Temple *ctx, const char *input, const char *end) {
|
|
if (!ctx || !input || !ctx->root) {
|
|
return false;
|
|
}
|
|
if (!vw_Temple_compile_ast(ctx->root, ctx->logger, input, end)) {
|
|
memset(ctx, 0, sizeof(*ctx));
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool vw_Temple_destroy(vw_Temple *ctx) {
|
|
if (!ctx || !ctx->root) {
|
|
return false;
|
|
}
|
|
vw_TempleASTNode_free(ctx->root);
|
|
return vs_HMap_destroy(&ctx->vars);
|
|
}
|
|
|
|
static inline void vw_TempleASTNode_print_indent(size_t indent) {
|
|
for (size_t idx = 0; idx < indent; ++idx) {
|
|
(void)fputs(" ", stdout);
|
|
}
|
|
}
|
|
|
|
void vw_TempleASTNode_print(vw_TempleASTNode *node, size_t indent) {
|
|
if (!node) {
|
|
vw_TempleASTNode_print_indent(indent);
|
|
puts("(null)");
|
|
}
|
|
|
|
while (node) {
|
|
vw_TempleASTNode_print_indent(indent);
|
|
|
|
switch (node->type) {
|
|
case vw_TempleASTNodeType_template:
|
|
printf("Template:\n");
|
|
vw_TempleASTNode_print(node->next, indent + 1);
|
|
return;
|
|
|
|
case vw_TempleASTNodeType_text:
|
|
printf("Text [%zu]: \"%s\"\n", node->data.text.len, node->data.text.text);
|
|
break;
|
|
|
|
case vw_TempleASTNodeType_comment:
|
|
printf("Comment [%zu]: {#%s#}\n", node->data.comment.len, node->data.comment.text);
|
|
break;
|
|
|
|
case vw_TempleASTNodeType_output_expr:
|
|
printf("OutputExpr:\n");
|
|
vw_TempleASTNode_print(node->data.output.expr, indent + 1);
|
|
break;
|
|
|
|
case vw_TempleASTNodeType_set_stmt:
|
|
printf("SetStmt%s: '%s'\n",
|
|
node->data.set_stmt.trim ? " (trimmed)" : "",
|
|
vw_TempleASTAssignmentOperator_to_str(node->data.set_stmt.eq));
|
|
vw_TempleASTNode_print_indent(indent + 1);
|
|
printf("Name:\n");
|
|
vw_TempleASTNode_print(node->data.set_stmt.name, indent + 2);
|
|
vw_TempleASTNode_print_indent(indent + 1);
|
|
printf("Expr:\n");
|
|
vw_TempleASTNode_print(node->data.set_stmt.expr, indent + 2);
|
|
break;
|
|
|
|
case vw_TempleASTNodeType_unset_stmt:
|
|
printf("UnsetStmt [%zu]: %s\n",
|
|
node->data.unset_stmt.name_len,
|
|
node->data.unset_stmt.name);
|
|
break;
|
|
|
|
case vw_TempleASTNodeType_unmacro_stmt:
|
|
printf("UnmacroStmt [%zu]: %s\n",
|
|
node->data.unmacro_stmt.name_len,
|
|
node->data.unmacro_stmt.name);
|
|
break;
|
|
|
|
case vw_TempleASTNodeType_conditional:
|
|
printf("Conditional:\n");
|
|
vw_TempleASTNode_print_indent(indent + 1);
|
|
printf("Condition:\n");
|
|
vw_TempleASTNode_print(node->data.conditional.condition, indent + 2);
|
|
vw_TempleASTNode_print_indent(indent + 1);
|
|
printf("Body%s:\n", node->data.conditional.trim_body ? " (trimmed)" : "");
|
|
vw_TempleASTNode_print(node->data.conditional.body, indent + 2);
|
|
vw_TempleASTNode_print_indent(indent + 1);
|
|
printf("ElseClause%s:\n", node->data.conditional.trim_clause ? " (trimmed)" : "");
|
|
vw_TempleASTNode_print(node->data.conditional.else_clause, indent + 2);
|
|
break;
|
|
|
|
case vw_TempleASTNodeType_for_loop:
|
|
printf("ForLoop%s: varname='%s'\n",
|
|
node->data.for_loop.trim ? " (trimmed)" : "",
|
|
node->data.for_loop.varname);
|
|
vw_TempleASTNode_print_indent(indent + 1);
|
|
printf("Iterable:\n");
|
|
vw_TempleASTNode_print(node->data.for_loop.iterable, indent + 2);
|
|
vw_TempleASTNode_print_indent(indent + 1);
|
|
printf("Body:\n");
|
|
vw_TempleASTNode_print(node->data.for_loop.body, indent + 2);
|
|
break;
|
|
|
|
case vw_TempleASTNodeType_while_loop:
|
|
printf("WhileLoop%s:\n", node->data.while_loop.trim ? " (trimmed)" : "");
|
|
vw_TempleASTNode_print_indent(indent + 1);
|
|
printf("Condition:\n");
|
|
vw_TempleASTNode_print(node->data.while_loop.condition, indent + 2);
|
|
vw_TempleASTNode_print_indent(indent + 1);
|
|
printf("Body:\n");
|
|
vw_TempleASTNode_print(node->data.while_loop.body, indent + 2);
|
|
break;
|
|
|
|
case vw_TempleASTNodeType_binary_expr:
|
|
printf("BinaryExpr: op='%s'\n",
|
|
vw_TempleASTBinaryOperator_to_str(node->data.binary.op));
|
|
vw_TempleASTNode_print_indent(indent + 1);
|
|
printf("Left:\n");
|
|
vw_TempleASTNode_print(node->data.binary.left, indent + 2);
|
|
vw_TempleASTNode_print_indent(indent + 1);
|
|
printf("Right:\n");
|
|
vw_TempleASTNode_print(node->data.binary.right, indent + 2);
|
|
break;
|
|
|
|
case vw_TempleASTNodeType_unary_expr:
|
|
printf("UnaryExpr: op='%s'\n",
|
|
vw_TempleASTUnaryOperator_to_str(node->data.unary.op));
|
|
vw_TempleASTNode_print_indent(indent + 1);
|
|
printf("Operand:\n");
|
|
vw_TempleASTNode_print(node->data.unary.operand, indent + 2);
|
|
break;
|
|
|
|
case vw_TempleASTNodeType_function_call:
|
|
printf("FunctionCall:\n");
|
|
vw_TempleASTNode_print_indent(indent + 1);
|
|
printf("Name:\n");
|
|
vw_TempleASTNode_print(node->data.function_call.name, indent + 2);
|
|
if (node->data.function_call.args_count == 0) {
|
|
vw_TempleASTNode_print_indent(indent + 1);
|
|
printf("Args: (null)\n");
|
|
} else {
|
|
for (size_t arg = 0; arg < node->data.function_call.args_count; ++arg) {
|
|
vw_TempleASTNode_print_indent(indent + 1);
|
|
printf("Args [%zu]:\n", arg);
|
|
vw_TempleASTNode_print(node->data.function_call.args[arg], indent + 2);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case vw_TempleASTNodeType_variable:
|
|
printf("Variable [%zu]: name='%s'\n",
|
|
node->data.variable.name_len,
|
|
node->data.variable.name);
|
|
break;
|
|
|
|
case vw_TempleASTNodeType_literal_string:
|
|
printf("LiteralString [%zu]: %s\"%s\"\n",
|
|
node->data.string.value_len,
|
|
node->data.string.raw ? "r" : "",
|
|
node->data.string.value);
|
|
break;
|
|
|
|
case vw_TempleASTNodeType_fmt_string:
|
|
printf("FmtString:\n");
|
|
vw_TempleASTNode_print(node->data.fmt_string.string, indent + 1);
|
|
break;
|
|
|
|
case vw_TempleASTNodeType_literal_number:
|
|
printf("LiteralNumber: %ju\n", node->data.number.value);
|
|
break;
|
|
|
|
case vw_TempleASTNodeType_literal_float:
|
|
printf("LiteralFloat: %.15lf\n", node->data.floatnum.value);
|
|
break;
|
|
|
|
case vw_TempleASTNodeType_literal_boolean:
|
|
printf("LiteralBoolean: %s\n", node->data.boolean.value ? "true" : "false");
|
|
break;
|
|
|
|
case vw_TempleASTNodeType_literal_null:
|
|
printf("LiteralNull\n");
|
|
break;
|
|
|
|
case vw_TempleASTNodeType_literal_inf:
|
|
printf("LiteralInf\n");
|
|
break;
|
|
|
|
case vw_TempleASTNodeType_parent_stmt:
|
|
printf("ParentStmt\n");
|
|
break;
|
|
|
|
case vw_TempleASTNodeType_import_stmt:
|
|
printf("ImportStmt%s:\n", node->data.import_stmt.trim ? " (trimmed)" : "");
|
|
vw_TempleASTNode_print_indent(indent + 1);
|
|
printf("Name:\n");
|
|
vw_TempleASTNode_print(node->data.import_stmt.filename, indent + 1);
|
|
vw_TempleASTNode_print_indent(indent + 1);
|
|
if (node->data.import_stmt.namespace_size == 0) {
|
|
puts("Namespace: <global>");
|
|
} else {
|
|
printf("Namespace [%zu]: %s\n",
|
|
node->data.import_stmt.namespace_size,
|
|
node->data.import_stmt.namespace);
|
|
}
|
|
break;
|
|
|
|
case vw_TempleASTNodeType_lookup:
|
|
printf("Lookup:\n");
|
|
vw_TempleASTNode_print_indent(indent + 1);
|
|
printf("Operand:\n");
|
|
vw_TempleASTNode_print(node->data.lookup.operand, indent + 2);
|
|
vw_TempleASTNode_print_indent(indent + 1);
|
|
printf("Index:\n");
|
|
vw_TempleASTNode_print(node->data.lookup.index, indent + 2);
|
|
break;
|
|
|
|
case vw_TempleASTNodeType_member_lookup:
|
|
printf("MemberLookup:\n");
|
|
vw_TempleASTNode_print_indent(indent + 1);
|
|
printf("Operand:\n");
|
|
vw_TempleASTNode_print(node->data.member_lookup.operand, indent + 2);
|
|
vw_TempleASTNode_print_indent(indent + 1);
|
|
printf("Member [%zu]: membername='%s'\n",
|
|
node->data.member_lookup.member_len,
|
|
node->data.member_lookup.member);
|
|
break;
|
|
|
|
case vw_TempleASTNodeType_macro_stmt:
|
|
printf("MacroStmt%s:\n", node->data.macro_stmt.trim ? " (trimmed)" : "");
|
|
vw_TempleASTNode_print_indent(indent + 1);
|
|
printf(
|
|
"Name [%zu]: %s\n", node->data.macro_stmt.name_len, node->data.macro_stmt.name);
|
|
if (node->data.macro_stmt.args_count == 0) {
|
|
vw_TempleASTNode_print_indent(indent + 1);
|
|
printf("Args: (null)\n");
|
|
} else {
|
|
for (size_t idx = 0; idx < node->data.macro_stmt.args_count; ++idx) {
|
|
vw_TempleASTNode_print_indent(indent + 1);
|
|
printf("Args [%zu]:\n", idx);
|
|
vw_TempleASTNode_print(node->data.macro_stmt.args[idx], indent + 2);
|
|
}
|
|
}
|
|
vw_TempleASTNode_print_indent(indent + 1);
|
|
printf("Body:\n");
|
|
vw_TempleASTNode_print(node->data.macro_stmt.body, indent + 2);
|
|
break;
|
|
|
|
case vw_TempleASTNodeType_namespace_stmt:
|
|
printf("NamespaceStmt%s:\n", node->data.namespace_stmt.trim ? " (trimmed)" : "");
|
|
vw_TempleASTNode_print_indent(indent + 1);
|
|
printf("Name [%zu]: %s\n",
|
|
node->data.namespace_stmt.name_len,
|
|
node->data.namespace_stmt.name);
|
|
vw_TempleASTNode_print_indent(indent + 1);
|
|
printf("Body:\n");
|
|
vw_TempleASTNode_print(node->data.namespace_stmt.body, indent + 2);
|
|
break;
|
|
|
|
case vw_TempleASTNodeType_literal_array:
|
|
printf("LiteralArray:\n");
|
|
if (node->data.array.items_count == 0) {
|
|
vw_TempleASTNode_print_indent(indent + 1);
|
|
printf("Items: (null)\n");
|
|
} else {
|
|
for (size_t idx = 0; idx < node->data.array.items_count; ++idx) {
|
|
vw_TempleASTNode_print_indent(indent + 1);
|
|
printf("Items [%zu]:\n", idx);
|
|
vw_TempleASTNode_print(node->data.array.items[idx], indent + 2);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case vw_TempleASTNodeType_literal_hashmap:
|
|
printf("LiteralHashmap:\n");
|
|
if (node->data.array.items_count == 0) {
|
|
vw_TempleASTNode_print_indent(indent + 1);
|
|
printf("Pairs: (null)\n");
|
|
} else {
|
|
for (size_t idx = 0; idx < node->data.hashmap.count; ++idx) {
|
|
vw_TempleASTNode_print_indent(indent + 1);
|
|
printf("Pairs [%zu]:\n", idx);
|
|
vw_TempleASTNode_print_indent(indent + 2);
|
|
printf("Key:\n");
|
|
vw_TempleASTNode_print(node->data.hashmap.keys[idx], indent + 3);
|
|
vw_TempleASTNode_print_indent(indent + 2);
|
|
printf("Value:\n");
|
|
vw_TempleASTNode_print(node->data.hashmap.values[idx], indent + 3);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case vw_TempleASTNodeType_finish_stmt:
|
|
printf("FinishStmt\n");
|
|
break;
|
|
|
|
case vw_TempleASTNodeType_break_stmt:
|
|
printf("BreakStmt\n");
|
|
break;
|
|
|
|
case vw_TempleASTNodeType_continue_stmt:
|
|
printf("ContinueStmt\n");
|
|
break;
|
|
|
|
case vw_TempleASTNodeType_block_stmt:
|
|
printf("BlockStmt%s [%zu]: %s\n",
|
|
node->data.block.trim ? " (trimmed)" : "",
|
|
node->data.block.name_len,
|
|
node->data.block.name ? node->data.block.name : "<whitespace control>");
|
|
vw_TempleASTNode_print(node->data.block.body, indent + 2);
|
|
break;
|
|
|
|
case vw_TempleASTNodeType_range:
|
|
printf("Range:\n");
|
|
vw_TempleASTNode_print_indent(indent + 1);
|
|
printf("From:\n");
|
|
vw_TempleASTNode_print(node->data.range.from, indent + 2);
|
|
vw_TempleASTNode_print_indent(indent + 1);
|
|
printf("To:\n");
|
|
vw_TempleASTNode_print(node->data.range.to, indent + 2);
|
|
vw_TempleASTNode_print_indent(indent + 1);
|
|
printf("Step:\n");
|
|
vw_TempleASTNode_print(node->data.range.step, indent + 2);
|
|
break;
|
|
|
|
default:
|
|
printf("Unknown ASTNodeType: %d\n", node->type);
|
|
}
|
|
|
|
node = node->next;
|
|
}
|
|
}
|
|
|
|
void vw_TempleASTNode_free(vw_TempleASTNode *node) {
|
|
while (node) {
|
|
vw_TempleASTNode *next = node->next;
|
|
|
|
switch (node->type) {
|
|
case vw_TempleASTNodeType_template:
|
|
break;
|
|
|
|
case vw_TempleASTNodeType_text:
|
|
VS_FREE(node->data.text.text);
|
|
break;
|
|
|
|
case vw_TempleASTNodeType_comment:
|
|
VS_FREE(node->data.comment.text);
|
|
break;
|
|
|
|
case vw_TempleASTNodeType_output_expr:
|
|
vw_TempleASTNode_free(node->data.output.expr);
|
|
break;
|
|
|
|
case vw_TempleASTNodeType_set_stmt:
|
|
vw_TempleASTNode_free(node->data.set_stmt.name);
|
|
vw_TempleASTNode_free(node->data.set_stmt.expr);
|
|
break;
|
|
|
|
case vw_TempleASTNodeType_unset_stmt:
|
|
VS_FREE(node->data.unset_stmt.name);
|
|
break;
|
|
|
|
case vw_TempleASTNodeType_unmacro_stmt:
|
|
VS_FREE(node->data.unmacro_stmt.name);
|
|
break;
|
|
|
|
case vw_TempleASTNodeType_conditional:
|
|
vw_TempleASTNode_free(node->data.conditional.condition);
|
|
vw_TempleASTNode_free(node->data.conditional.body);
|
|
vw_TempleASTNode_free(node->data.conditional.else_clause);
|
|
break;
|
|
|
|
case vw_TempleASTNodeType_macro_stmt:
|
|
VS_FREE(node->data.macro_stmt.name);
|
|
for (size_t idx = 0; idx < node->data.macro_stmt.args_count; ++idx) {
|
|
vw_TempleASTNode_free(node->data.macro_stmt.args[idx]);
|
|
}
|
|
vw_TempleASTNode_free(node->data.macro_stmt.body);
|
|
break;
|
|
|
|
case vw_TempleASTNodeType_namespace_stmt:
|
|
VS_FREE(node->data.namespace_stmt.name);
|
|
vw_TempleASTNode_free(node->data.namespace_stmt.body);
|
|
break;
|
|
|
|
case vw_TempleASTNodeType_import_stmt:
|
|
vw_TempleASTNode_free(node->data.import_stmt.filename);
|
|
VS_FREE(node->data.import_stmt.namespace);
|
|
break;
|
|
|
|
case vw_TempleASTNodeType_for_loop:
|
|
VS_FREE(node->data.for_loop.varname);
|
|
vw_TempleASTNode_free(node->data.for_loop.iterable);
|
|
vw_TempleASTNode_free(node->data.for_loop.body);
|
|
break;
|
|
|
|
case vw_TempleASTNodeType_while_loop:
|
|
vw_TempleASTNode_free(node->data.while_loop.condition);
|
|
vw_TempleASTNode_free(node->data.while_loop.body);
|
|
break;
|
|
|
|
case vw_TempleASTNodeType_binary_expr:
|
|
vw_TempleASTNode_free(node->data.binary.left);
|
|
vw_TempleASTNode_free(node->data.binary.right);
|
|
break;
|
|
|
|
case vw_TempleASTNodeType_unary_expr:
|
|
vw_TempleASTNode_free(node->data.unary.operand);
|
|
break;
|
|
|
|
case vw_TempleASTNodeType_function_call:
|
|
vw_TempleASTNode_free(node->data.function_call.name);
|
|
for (size_t idx = 0; idx < node->data.function_call.args_count; ++idx) {
|
|
vw_TempleASTNode_free(node->data.function_call.args[idx]);
|
|
}
|
|
break;
|
|
|
|
case vw_TempleASTNodeType_variable:
|
|
VS_FREE(node->data.variable.name);
|
|
break;
|
|
|
|
case vw_TempleASTNodeType_literal_string:
|
|
VS_FREE(node->data.string.value);
|
|
break;
|
|
|
|
case vw_TempleASTNodeType_fmt_string:
|
|
vw_TempleASTNode_free(node->data.fmt_string.string);
|
|
break;
|
|
|
|
case vw_TempleASTNodeType_literal_number:
|
|
break;
|
|
|
|
case vw_TempleASTNodeType_literal_array:
|
|
for (size_t idx = 0; idx < node->data.array.items_count; ++idx) {
|
|
vw_TempleASTNode_free(node->data.array.items[idx]);
|
|
}
|
|
break;
|
|
|
|
case vw_TempleASTNodeType_literal_boolean:
|
|
case vw_TempleASTNodeType_literal_null:
|
|
case vw_TempleASTNodeType_literal_float:
|
|
case vw_TempleASTNodeType_literal_inf:
|
|
case vw_TempleASTNodeType_parent_stmt:
|
|
break;
|
|
|
|
case vw_TempleASTNodeType_lookup:
|
|
vw_TempleASTNode_free(node->data.lookup.operand);
|
|
vw_TempleASTNode_free(node->data.lookup.index);
|
|
break;
|
|
|
|
case vw_TempleASTNodeType_member_lookup:
|
|
vw_TempleASTNode_free(node->data.member_lookup.operand);
|
|
VS_FREE(node->data.member_lookup.member);
|
|
break;
|
|
|
|
case vw_TempleASTNodeType_finish_stmt:
|
|
case vw_TempleASTNodeType_break_stmt:
|
|
case vw_TempleASTNodeType_continue_stmt:
|
|
break;
|
|
|
|
case vw_TempleASTNodeType_block_stmt:
|
|
VS_FREE(node->data.block.name);
|
|
vw_TempleASTNode_free(node->data.block.body);
|
|
break;
|
|
|
|
case vw_TempleASTNodeType_range:
|
|
vw_TempleASTNode_free(node->data.range.from);
|
|
vw_TempleASTNode_free(node->data.range.to);
|
|
vw_TempleASTNode_free(node->data.range.step);
|
|
break;
|
|
|
|
case vw_TempleASTNodeType_literal_hashmap:
|
|
for (size_t idx = 0; idx < node->data.hashmap.count; ++idx) {
|
|
vw_TempleASTNode_free(node->data.hashmap.keys[idx]);
|
|
vw_TempleASTNode_free(node->data.hashmap.values[idx]);
|
|
}
|
|
break;
|
|
}
|
|
|
|
VS_FREE(node);
|
|
node = next;
|
|
}
|
|
}
|