vessel/web/temple.c
Arija A. d78d81b487
Improve Temple web structure and submodules.
Signed-off-by: Arija A. <ari@ari.lt>
2025-09-06 19:29:55 +03:00

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;
}
}