HTMLTemplate::process_template_expression() initial implementation.

This commit is contained in:
Relintai 2024-02-26 13:11:31 +01:00
parent 0d2fceecf0
commit b0a72e933d
2 changed files with 204 additions and 6 deletions

View File

@ -31,6 +31,8 @@
#include "html_template.h" #include "html_template.h"
#include "core/containers/local_vector.h"
#include "../http/web_server_request.h" #include "../http/web_server_request.h"
#include "html_template_data.h" #include "html_template_data.h"
@ -225,10 +227,185 @@ String HTMLTemplate::get_template_text(const StringName &p_name) {
return String(); return String();
} }
String HTMLTemplate::process_template_expression(const String &p_expression, const Dictionary &p_data) { String HTMLTemplate::process_template_expression_variable(const String &p_variable, const TemplateExpressionMethods p_method, const Dictionary &p_data) {
return String(); return String();
} }
String HTMLTemplate::process_template_expression(const String &p_expression, const Dictionary &p_data) {
// Supported:
// var // equivalent to p(var)
// (var) // equivalent to p(var)
// p(var) // print, escaped, also includes to string cast
// pr(var) // print_raw, not escaped, also includes to string
// pb(var) // print_newline_to_br, escaped, turns newlines into <br>, also includes to string cast
// prb(var) // print_raw_newline_to_br, not escaped, turns newlines into <br>, also includes to string cast
// vf("%d %d", var1, var2) // vformat
// p(var[1]) // Array indexing
// p(var["x"]) // Dictionary indexing
// p(var1, var2) All methods can do multiple arguments
// No actual method calls. Even though it should be relatively trivial to implement, it's probably not a good idea.
String expression = p_expression.strip_edges();
int expression_length = expression.length();
if (expression_length == 0) {
return String();
}
int i = 0;
int method_name_end_index = 0;
while (i < expression_length) {
CharType current_token = expression[i];
switch (current_token) {
case '(': {
// Found start '('
method_name_end_index = i;
goto method_name_search_done;
} break;
case ')': {
ERR_FAIL_V_MSG(String(), "There is an error in the syntax of an expression! Erroneously placed ). Expression: " + p_expression);
} break;
case '"':
case '\'':
case '[':
case ']': {
// Encountering any of these before a '(' means that a variable is just on it's own.
// Encountering a '(' ends the search.
goto method_name_search_done;
} break;
default:
break;
}
++i;
}
method_name_search_done:
TemplateExpressionMethods call_method = TEMPLATE_EXPRESSION_METHOD_PRINT;
// This will be zero even if (var)
if (method_name_end_index != 0) {
//method_name_end_index points to a '(', substr_index does not include end index.
String method_name = expression.substr_index(0, method_name_end_index).strip_edges();
if (method_name == "p") {
//default, needs to be checked so no error
} else if (method_name == "pr") {
call_method = TEMPLATE_EXPRESSION_METHOD_PRINT_RAW;
} else if (method_name == "pb") {
call_method = TEMPLATE_EXPRESSION_METHOD_PRINT_BR;
} else if (method_name == "prb") {
call_method = TEMPLATE_EXPRESSION_METHOD_PRINT_RAW_BR;
} else if (method_name == "vf") {
call_method = TEMPLATE_EXPRESSION_METHOD_VFORMAT;
} else {
ERR_FAIL_V_MSG(String(), "There is an error in the syntax of an expression! Not a valid method!. Method: " + method_name + " Expression: " + p_expression);
}
}
// From vf("%d %d", var1, var2) this ends up being ("%d %d", var1, var2)
// Note this can be (((var))) too!
String variables_str = expression.substr_index(method_name_end_index, expression.length()).strip_edges();
// Get rid of all '(' from beginning, and ')' from end
variables_str = variables_str.lstrip("(").rstrip(")");
int variables_str_length = variables_str.length();
i = 0;
bool in_string = false;
CharType current_string_type = '"';
bool escape_next = false;
int last_variable_end_index = 0;
LocalVector<String> variables;
// Find all variables, note we can't just split because of strings
while (i < variables_str_length) {
CharType current_token = variables_str[i];
if (escape_next) {
escape_next = false;
continue;
}
switch (current_token) {
case ',': {
// Nothing to do if we are in a string.
if (in_string) {
break;
}
// last_variable_end_index = v
// i = ^
// v
// var1, var2, var3
// ^
// in substr_index end index is not included:
String current_variable_str = variables_str.substr_index(last_variable_end_index, i).strip_edges();
variables.push_back(current_variable_str);
// next var:
// last_variable_end_index = i + 1 means:
// var1, var2, var3
// ^
last_variable_end_index = i + 1;
} break;
case '"': {
if (in_string) {
if (current_string_type == '"') {
in_string = false;
}
break;
}
// String start
in_string = true;
current_string_type = '"';
} break;
case '\'': {
if (in_string) {
if (current_string_type == '\'') {
in_string = false;
}
break;
}
// String start
in_string = true;
current_string_type = '\'';
} break;
case '\\': {
// There is nothing we can escape outside of strings
// This way beginning string symbol cannot be escaped. (Which is bad syntax anyway).
if (in_string) {
escape_next = true;
}
} break;
default: {
} break;
}
++i;
}
String ret;
// Finally let's just process the variables themselves, and generate the final output
for (uint32_t vi = 0; vi < variables.size(); ++vi) {
String variable = variables[vi];
ret += process_template_expression_variable(variable, call_method, p_data);
}
return ret;
}
String HTMLTemplate::render_template(const String &p_text, const Dictionary &p_data) { String HTMLTemplate::render_template(const String &p_text, const Dictionary &p_data) {
// {\{ Escaped {{ // {\{ Escaped {{
// {\\{ -> {\{ etc // {\\{ -> {\{ etc
@ -300,6 +477,8 @@ String HTMLTemplate::render_template(const String &p_text, const Dictionary &p_d
// last_section_start: ^ // last_section_start: ^
last_section_start = i; last_section_start = i;
escape_next = false;
break; break;
} }
@ -420,13 +599,13 @@ String HTMLTemplate::render_template(const String &p_text, const Dictionary &p_d
String expression = p_text.substr_index(last_section_start, i - 2); String expression = p_text.substr_index(last_section_start, i - 2);
result += process_template_expression(expression.strip_edges(), p_data); result += process_template_expression(expression, p_data);
// i points to: v // i points to: v
// ... {{ // ... {{
// last_section_start: ^ // last_section_start: ^
last_section_start = i + 1; last_section_start = i + 1;
current_state = RENDER_TEMPLATE_STATE_NORMAL_TEXT; current_state = RENDER_TEMPLATE_STATE_NORMAL_TEXT;
} break; } break;
default: { default: {
@ -440,7 +619,7 @@ String HTMLTemplate::render_template(const String &p_text, const Dictionary &p_d
} }
} break; } break;
} }
i += 1; i += 1;
} }
@ -727,10 +906,11 @@ void HTMLTemplate::_bind_methods() {
// Use // Use
ClassDB::bind_method(D_METHOD("get_template_text", "name"), &HTMLTemplate::get_template_text); ClassDB::bind_method(D_METHOD("get_template_text", "name"), &HTMLTemplate::get_template_text);
ClassDB::bind_method(D_METHOD("process_template_expression_variable", "variable", "method", "data"), &HTMLTemplate::process_template_expression_variable);
ClassDB::bind_method(D_METHOD("process_template_expression", "expression", "data"), &HTMLTemplate::process_template_expression); ClassDB::bind_method(D_METHOD("process_template_expression", "expression", "data"), &HTMLTemplate::process_template_expression);
ClassDB::bind_method(D_METHOD("render_template", "text", "data"), &HTMLTemplate::render_template); ClassDB::bind_method(D_METHOD("render_template", "text", "data"), &HTMLTemplate::render_template);
ClassDB::bind_method(D_METHOD("get_and_render_template", "name", "data"), &HTMLTemplate::get_and_render_template); ClassDB::bind_method(D_METHOD("get_and_render_template", "name", "data"), &HTMLTemplate::get_and_render_template);
BIND_VMETHOD(MethodInfo(Variant::STRING, "_render", BIND_VMETHOD(MethodInfo(Variant::STRING, "_render",
@ -739,4 +919,11 @@ void HTMLTemplate::_bind_methods() {
ClassDB::bind_method(D_METHOD("render", "request", "data"), &HTMLTemplate::render); ClassDB::bind_method(D_METHOD("render", "request", "data"), &HTMLTemplate::render);
ClassDB::bind_method(D_METHOD("_render", "request", "data"), &HTMLTemplate::_render); ClassDB::bind_method(D_METHOD("_render", "request", "data"), &HTMLTemplate::_render);
// Enums
BIND_ENUM_CONSTANT(TEMPLATE_EXPRESSION_METHOD_PRINT);
BIND_ENUM_CONSTANT(TEMPLATE_EXPRESSION_METHOD_PRINT_RAW);
BIND_ENUM_CONSTANT(TEMPLATE_EXPRESSION_METHOD_PRINT_BR);
BIND_ENUM_CONSTANT(TEMPLATE_EXPRESSION_METHOD_PRINT_RAW_BR);
BIND_ENUM_CONSTANT(TEMPLATE_EXPRESSION_METHOD_VFORMAT);
} }

View File

@ -46,6 +46,14 @@ class HTMLTemplate : public Resource {
GDCLASS(HTMLTemplate, Resource); GDCLASS(HTMLTemplate, Resource);
public: public:
enum TemplateExpressionMethods {
TEMPLATE_EXPRESSION_METHOD_PRINT = 0,
TEMPLATE_EXPRESSION_METHOD_PRINT_RAW,
TEMPLATE_EXPRESSION_METHOD_PRINT_BR,
TEMPLATE_EXPRESSION_METHOD_PRINT_RAW_BR,
TEMPLATE_EXPRESSION_METHOD_VFORMAT,
};
// Templates // Templates
int get_template_count() const; int get_template_count() const;
Ref<HTMLTemplateData> get_template(const int p_index); Ref<HTMLTemplateData> get_template(const int p_index);
@ -89,6 +97,7 @@ public:
String get_template_text(const StringName &p_name); String get_template_text(const StringName &p_name);
String process_template_expression_variable(const String &p_variable, const TemplateExpressionMethods p_method, const Dictionary &p_data);
String process_template_expression(const String &p_expression, const Dictionary &p_data); String process_template_expression(const String &p_expression, const Dictionary &p_data);
String render_template(const String &p_text, const Dictionary &p_data); String render_template(const String &p_text, const Dictionary &p_data);
@ -125,4 +134,6 @@ protected:
String _editor_new_template_default_key; String _editor_new_template_default_key;
}; };
VARIANT_ENUM_CAST(HTMLTemplate::TemplateExpressionMethods);
#endif #endif