cOMS/html/template/HtmlTemplateParser.h
Dennis Eichhorn 4f1cbd98f9
Some checks failed
Microsoft C++ Code Analysis / Analyze (push) Waiting to run
CodeQL / Analyze (${{ matrix.language }}) (autobuild, c-cpp) (push) Has been cancelled
started templating
2025-03-21 01:08:09 +00:00

246 lines
11 KiB
C

/**
* Jingga
*
* @copyright Jingga
* @license OMS License 2.0
* @version 1.0.0
* @link https://jingga.app
*/
#ifndef COMS_HTML_TEMPLATE_PARSER_H
#define COMS_HTML_TEMPLATE_PARSER_H
#include "HtmlTemplateLexer.h"
#include "HtmlTemplateContext.h"
enum HtmlTemplateNodeType : byte {
NODE_BINOP,
NODE_PTR,
NODE_IDENTIFIER,
NODE_BOOL,
NODE_INTEGER64,
NODE_FLOAT64,
NODE_ASSIGN, // =
NODE_INCREMENT, // ++
NODE_DECREMENT, // --
NODE_ADD_ASSIGN, // +=
NODE_SUBTRACT_ASSIGN, // -=
NODE_MULTIPLY_ASSIGN, // *=
NODE_DIVIDE_ASSIGN, // /=
NODE_EQUALS, // ==
NODE_UNEQUAL, // !=
NODE_GREATER, // >
NODE_GREATER_EQUAL, // >=
NODE_LESSER, // <
NODE_LESSER_EQUAL, // <=
NODE_PLUS,
NODE_MINUS,
NODE_MULTIPLY,
NODE_DIVIDE,
NODE_COLON, // :
NODE_QUESTION, // ?
NODE_EXCLAMATION, // !
NODE_CODE_START, // <?
NODE_CODE_END, // ?>
NODE_LBRACK, // [
NODE_RBRACK, // ]
NODE_LPAREN, // (
NODE_RPAREN, // )
NODE_LBRACE, // {
NODE_RBRACE, // }
NODE_SEMICOLON, // ;
NODE_STRING, // "..."
NODE_WHILE,
NODE_ENDWHILE,
NODE_IF,
NODE_ENDIF,
NODE_ELSEIF,
NODE_ELSE,
NODE_FOR,
NODE_ENDFOR,
NODE_FOREACH,
NODE_ENDFOREACH,
};
struct HtmlTemplateASTNode {
HtmlTemplateASTNode* left;
HtmlTemplateASTNode* right;
HtmlTemplateNodeType type;
uint32 value_length;
union {
bool boolValue;
int64 int64Value;
f64 f64Value;
char* ptrValue;
};
};
HtmlTemplateASTNode* html_template_node_create(HtmlTemplateNodeType type, HtmlTemplateToken* token, byte** memory) {
*memory = (byte *) ROUND_TO_NEAREST((uintptr_t) memory, 32);
HtmlTemplateASTNode* node = (HtmlTemplateASTNode *) *memory;
*memory = (byte *) ROUND_TO_NEAREST((uintptr_t) (memory + sizeof(HtmlTemplateASTNode)), 32);
node->type = type;
node->left = NULL;
node->right = NULL;
node->value_length = token->length;
// @question instead of handling the parsing below, why not handle it here for known types such as int, float, bool, string
memcpy(&node->int64Value, token->value, sizeof(uintptr_t));
return node;
}
HtmlTemplateASTNode* html_template_factor_parse(const char**, HtmlTemplateToken*, HtmlTemplateContextFlag, byte**);
HtmlTemplateASTNode* html_template_term_parse(const char** input, HtmlTemplateToken* token_current, HtmlTemplateContextFlag context_flag, byte** memory) {
// @bug This is wrong
HtmlTemplateASTNode* left = html_template_factor_parse(input, token_current, context_flag, memory);
while (token_current->type == TOKEN_MULTIPLY || token_current->type == TOKEN_DIVIDE) {
HtmlTemplateToken* old = token_current;
*token_current = html_template_token_next(input, context_flag);
HtmlTemplateASTNode* right = html_template_factor_parse(input, token_current, context_flag, memory);
// @bug This is wrong
left = html_template_node_create(NODE_BINOP, old, memory);
left->left = left;
left->right = right;
}
return left;
}
HtmlTemplateASTNode* html_template_expression_parse(const char** input, HtmlTemplateToken* token_current, HtmlTemplateContextFlag context_flag, byte** memory) {
HtmlTemplateASTNode* left = html_template_term_parse(input, token_current, context_flag, memory);
while (token_current->type == TOKEN_PLUS || token_current->type == TOKEN_MINUS) {
HtmlTemplateToken* old = token_current;
*token_current = html_template_token_next(input, context_flag);
HtmlTemplateASTNode* right = html_template_term_parse(input, token_current, context_flag, memory);
left = html_template_node_create(NODE_BINOP, old, memory);
left->left = left;
left->right = right;
}
return left;
}
HtmlTemplateASTNode* html_template_factor_parse(const char** input, HtmlTemplateToken* token_current, HtmlTemplateContextFlag context_flag, byte** memory) {
// @performance Consider to order the token types in a smart way and perform range check here
// @todo use switch
if (token_current->type == TOKEN_INTEGER64) {
HtmlTemplateASTNode* node = html_template_node_create(NODE_INTEGER64, token_current, memory);
*token_current = html_template_token_next(input, context_flag);
// @todo parse string representation of int
return node;
} else if (token_current->type == TOKEN_FLOAT64) {
HtmlTemplateASTNode* node = html_template_node_create(NODE_FLOAT64, token_current, memory);
*token_current = html_template_token_next(input, context_flag);
// @todo parse string representation of float
return node;
} else if (token_current->type == TOKEN_STRING) {
HtmlTemplateASTNode* node = html_template_node_create(NODE_STRING, token_current, memory);
*token_current = html_template_token_next(input, context_flag);
return node;
} else if (token_current->type == TOKEN_IDENTIFIER) {
HtmlTemplateASTNode* node = html_template_node_create(NODE_IDENTIFIER, token_current, memory);
*token_current = html_template_token_next(input, context_flag);
return node;
} else if (token_current->type == TOKEN_LPAREN) {
*token_current = html_template_token_next(input, context_flag); // Consume '('
HtmlTemplateASTNode* node = html_template_expression_parse(input, token_current, context_flag, memory);
*token_current = html_template_token_next(input, context_flag); // Consume ')'
return node;
} else if (token_current->type == TOKEN_LBRACK) {
*token_current = html_template_token_next(input, context_flag); // Consume '['
HtmlTemplateASTNode* node = html_template_expression_parse(input, token_current, context_flag, memory);
*token_current = html_template_token_next(input, context_flag); // Consume ']'
return node;
} else if (token_current->type == TOKEN_LBRACE) {
*token_current = html_template_token_next(input, context_flag); // Consume '{'
HtmlTemplateASTNode* node = html_template_expression_parse(input, token_current, context_flag, memory);
*token_current = html_template_token_next(input, context_flag); // Consume '}'
return node;
}
ASSERT_SIMPLE(false);
return NULL;
}
HtmlTemplateASTNode* html_template_statement_parse(const char**, HtmlTemplateToken*, HtmlTemplateContextStack*, HtmlTemplateContextFlag, byte**);
HtmlTemplateASTNode* html_template_assignment_parse(const char** input, HtmlTemplateToken* token_current, HtmlTemplateContextFlag context_flag, byte** memory) {
/*HtmlTemplateASTNode* left =*/ html_template_node_create(NODE_IDENTIFIER, token_current, memory);
*token_current = html_template_token_next(input, context_flag); // Consume identifier
*token_current = html_template_token_next(input, context_flag); // Consume '='
/*HtmlTemplateASTNode* right =*/ html_template_expression_parse(input, token_current, context_flag, memory);
*token_current = html_template_token_next(input, context_flag); // Consume ';'
return html_template_node_create(NODE_ASSIGN, {}, memory);
}
HtmlTemplateASTNode* html_template_parse_if(const char** input, HtmlTemplateToken* token_current, HtmlTemplateContextStack* contextStack, HtmlTemplateContextFlag context_flag, byte** memory) {
HtmlTemplateContext newContext = peekContext(contextStack);
++newContext.scope_level;
pushContext(contextStack, newContext);
*token_current = html_template_token_next(input, context_flag); // Consume 'if'
*token_current = html_template_token_next(input, context_flag); // Consume '('
HtmlTemplateASTNode* condition = html_template_expression_parse(input, token_current, context_flag, memory);
*token_current = html_template_token_next(input, context_flag); // Consume ')'
*token_current = html_template_token_next(input, context_flag); // Consume '{'
HtmlTemplateASTNode* body = html_template_statement_parse(input, token_current, contextStack, context_flag, memory);
*token_current = html_template_token_next(input, context_flag); // Consume '}'
popContext(contextStack);
HtmlTemplateASTNode* ifNode = html_template_node_create(NODE_IF, {}, memory);
ifNode->left = condition; // Condition
ifNode->right = body; // Body
return ifNode;
}
HtmlTemplateASTNode* html_template_parse_for(const char** input, HtmlTemplateToken* token_current, HtmlTemplateContextStack* contextStack, HtmlTemplateContextFlag context_flag, byte** memory) {
HtmlTemplateContext newContext = peekContext(contextStack);
++newContext.scope_level;
++newContext.loop_nesting_level;
pushContext(contextStack, newContext);
*token_current = html_template_token_next(input, context_flag); // Consume 'for'
*token_current = html_template_token_next(input, context_flag); // Consume '('
HtmlTemplateASTNode* init = html_template_assignment_parse(input, token_current, context_flag, memory);
HtmlTemplateASTNode* condition = html_template_expression_parse(input, token_current, context_flag, memory);
*token_current = html_template_token_next(input, context_flag); // Consume ';'
HtmlTemplateASTNode* update = html_template_assignment_parse(input, token_current, context_flag, memory);
*token_current = html_template_token_next(input, context_flag); // Consume ')'
*token_current = html_template_token_next(input, context_flag); // Consume '{'
HtmlTemplateASTNode* body = html_template_statement_parse(input, token_current, contextStack, context_flag, memory);
*token_current = html_template_token_next(input, context_flag); // Consume '}'
popContext(contextStack);
HtmlTemplateASTNode* forNode = html_template_node_create(NODE_FOR, {}, memory);
forNode->left = init; // Initialization
forNode->right = html_template_node_create(NODE_BINOP, {}, memory);
forNode->right->left = condition; // Condition
forNode->right->right = update; // Update
forNode->right->right->right = body; // Body
return forNode;
}
HtmlTemplateASTNode* html_template_statement_parse(const char** input, HtmlTemplateToken* token_current, HtmlTemplateContextStack* contextStack, HtmlTemplateContextFlag context_flag, byte** memory) {
if (token_current->type == TOKEN_ASSIGN) {
return html_template_assignment_parse(input, token_current, context_flag, memory);
} else if (token_current->type == TOKEN_IF) {
return html_template_parse_if(input, token_current, contextStack, context_flag, memory);
} else if (token_current->type == TOKEN_FOR) {
return html_template_parse_for(input, token_current, contextStack, context_flag, memory);
} else {
exit(1);
}
}
#endif