cOMS/html/template/HtmlTemplateParser.h
Dennis Eichhorn 20770b42a4
Some checks are pending
CodeQL / Analyze (${{ matrix.language }}) (autobuild, c-cpp) (push) Waiting to run
Microsoft C++ Code Analysis / Analyze (push) Waiting to run
update
2025-04-14 07:12:31 +00:00

296 lines
12 KiB
C
Executable File

/**
* 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_RAW,
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;
const char* ptrValue;
};
};
HtmlTemplateASTNode* html_template_node_create(HtmlTemplateNodeType type, const 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
node->ptrValue = token->value;
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* context_stack, HtmlTemplateContextFlag context_flag, byte** memory) {
HtmlTemplateContext newContext = peekContext(context_stack);
++newContext.scope_level;
pushContext(context_stack, 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, context_stack, context_flag, memory);
*token_current = html_template_token_next(input, context_flag); // Consume '}'
popContext(context_stack);
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* context_stack, HtmlTemplateContextFlag context_flag, byte** memory) {
HtmlTemplateContext newContext = peekContext(context_stack);
++newContext.scope_level;
++newContext.loop_nesting_level;
pushContext(context_stack, 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, context_stack, context_flag, memory);
*token_current = html_template_token_next(input, context_flag); // Consume '}'
popContext(context_stack);
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_html_parse(
const char** input,
HtmlTemplateToken* token_current,
HtmlTemplateContextStack* context_stack,
HtmlTemplateContextFlag context_flag,
byte** memory
) {
HtmlTemplateASTNode* html = html_template_node_create(NODE_RAW, token_current, memory);
*token_current = html_template_token_next(input, context_flag); // Consume html
if (token_current->type != TOKEN_EOF) {
html->right = html_template_statement_parse(input, token_current, context_stack, context_flag, memory);
}
return html;
}
HtmlTemplateASTNode* html_template_code_parse(
const char** input,
HtmlTemplateToken* token_current,
HtmlTemplateContextStack* context_stack,
HtmlTemplateContextFlag context_flag,
byte** memory
) {
HtmlTemplateASTNode* code = html_template_node_create(NODE_RAW, token_current, memory);
*token_current = html_template_token_next(input, context_flag); // Consume code
if (token_current->type != TOKEN_EOF) {
code->right = html_template_statement_parse(input, token_current, context_stack, context_flag, memory);
}
return code;
}
HtmlTemplateASTNode* html_template_statement_parse(
const char** input,
HtmlTemplateToken* token_current,
HtmlTemplateContextStack* context_stack,
HtmlTemplateContextFlag context_flag,
byte** memory
) {
switch(token_current->type) {
case TOKEN_HTML: {
return html_template_html_parse(input, token_current, context_stack, context_flag, memory);
};
case TOKEN_ASSIGN: {
return html_template_assignment_parse(input, token_current, context_flag, memory);
};
case TOKEN_IF: {
return html_template_parse_if(input, token_current, context_stack, context_flag, memory);
};
case TOKEN_FOR: {
return html_template_parse_for(input, token_current, context_stack, context_flag, memory);
};
case TOKEN_CODE_START: {
return html_template_code_parse(input, token_current, context_stack, context_flag, memory);
};
default: UNREACHABLE();
}
}
#endif