aboutsummaryrefslogtreecommitdiff
path: root/src/template.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/template.c')
-rw-r--r--src/template.c218
1 files changed, 170 insertions, 48 deletions
diff --git a/src/template.c b/src/template.c
index 85db75a..9343ec1 100644
--- a/src/template.c
+++ b/src/template.c
@@ -40,31 +40,163 @@ char *trim_leading_whitespace(char *str) {
return str;
}
-char *eval_expression(mpc_ast_t* node, struct hashmap *ctx) {
- if (strstr(node->tag, "symbol")) {
+enum unja_object_type {
+ OBJ_NULL,
+ OBJ_INT,
+ OBJ_STRING,
+};
+
+struct unja_object {
+ enum unja_object_type type;
+ int integer;
+ char *string;
+};
+
+struct unja_object _null_object = {
+ .type = OBJ_NULL,
+};
+struct unja_object *null_object = &_null_object;
+
+struct unja_object *make_string_object(char *value, char *value2) {
+ struct unja_object *obj = malloc(sizeof *obj);
+ obj->type = OBJ_STRING;
+ int l = strlen(value) + 1;
+ if (value2) {
+ l += strlen(value2);
+ }
+
+ obj->string = malloc(l);
+ strcpy(obj->string, value);
+
+ if(value2) {
+ strcat(obj->string, value2);
+ }
+ return obj;
+}
+
+struct unja_object *make_int_object(int value) {
+ struct unja_object *obj = malloc(sizeof *obj);
+ obj->type = OBJ_INT;
+ obj->integer = value;
+ return obj;
+}
+
+
+void object_to_str(char *dest, struct unja_object *obj) {
+ char buf[64];
+
+ switch (obj->type) {
+ case OBJ_NULL:
+ break;
+ case OBJ_STRING:
+ strcat(dest, obj->string);
+ break;
+ case OBJ_INT:
+ sprintf(buf, "%d", obj->integer);
+ strcat(dest, buf);
+ break;
+ }
+}
+
+int object_to_int(struct unja_object *obj) {
+ switch (obj->type) {
+ case OBJ_NULL: return 0;
+ case OBJ_STRING: return atoi(obj->string);
+ case OBJ_INT: return obj->integer;
+ }
+
+ return 0;
+}
+
+void object_free(struct unja_object *obj) {
+ switch(obj->type) {
+ case OBJ_NULL: return;
+ case OBJ_STRING:
+ free(obj->string);
+ break;
+ case OBJ_INT: break;
+ }
+
+ free(obj);
+}
+
+int object_is_truthy(struct unja_object *obj) {
+ switch (obj->type) {
+ case OBJ_NULL: return 0;
+ case OBJ_STRING: return strlen(obj->string) > 0;
+ case OBJ_INT: return obj->integer > 0;
+ }
+
+ return 0;
+}
+
+struct unja_object *eval_expression_value(mpc_ast_t* node, struct hashmap *ctx) {
+ if (strstr(node->tag, "symbol|")) {
/* Return empty string if no context was passed. Should probably signal error here. */
if (ctx == NULL) {
- return "";
+ return null_object;
}
char *key = node->contents;
+ char *value = hashmap_resolve(ctx, key);
+
/* TODO: Handle unexisting symbols (returns NULL currently) */
- return hashmap_resolve(ctx, key);
- } else if(strstr(node->tag, "number")) {
- return node->contents;
- } else if(strstr(node->tag, "string")) {
- return node->children[1]->contents;
+ if (value == NULL) {
+ return null_object;
+ }
+ return make_string_object(value, NULL);
+ } else if(strstr(node->tag, "number|")) {
+ return make_int_object(atoi(node->contents));
+ } else if(strstr(node->tag, "string|")) {
+ return make_string_object(node->children[1]->contents, NULL);
}
- return NULL;
+ return null_object;
+}
+
+
+struct unja_object *eval_expression(mpc_ast_t* expr, struct hashmap *ctx) {
+ if (expr->children_num >= 2 && strstr(expr->children[1]->tag, "op")) {
+ mpc_ast_t *left_node = expr->children[0];
+ mpc_ast_t *op = expr->children[1];
+ mpc_ast_t *right_node = expr->children[2];
+
+ struct unja_object *left = eval_expression_value(left_node, ctx);
+ struct unja_object *right = eval_expression_value(right_node, ctx);
+
+ /* if operator is + and either left or right node is of type string: concat */
+ if (op->contents[0] == '+' && (left->type == OBJ_STRING && right->type == OBJ_STRING)) {
+ struct unja_object *result = make_string_object(left->string, right->string);
+ object_free(left);
+ object_free(right);
+ return result;
+ }
+
+ /* eval int infix expression */
+ int result;
+ switch (op->contents[0]) {
+ case '+': result = object_to_int(left) + object_to_int(right); break;
+ case '-': result = object_to_int(left) - object_to_int(right); break;
+ case '/': result = object_to_int(left) / object_to_int(right); break;
+ case '*': result = object_to_int(left) * object_to_int(right); break;
+ case '>': result = object_to_int(left) > object_to_int(right); break;
+ case '<': result = object_to_int(left) < object_to_int(right); break;
+ }
+
+ object_free(left);
+ object_free(right);
+ return make_int_object(result);
+ }
+
+ mpc_ast_t *left_node = expr;
+ return eval_expression_value(left_node, ctx);
}
int eval(char *dest, mpc_ast_t* t, struct hashmap *ctx) {
static int trim_whitespace = 0;
- char buf[64];
- // eval expression
- if (strstr(t->tag, "content|expression|")) {
+ // eval print statement
+ if (strstr(t->tag, "content|print")) {
// maybe eat whitespace going backward
if (strstr(t->children[1]->contents, "-")) {
dest = trim_trailing_whitespace(dest);
@@ -74,36 +206,11 @@ int eval(char *dest, mpc_ast_t* t, struct hashmap *ctx) {
if (strstr(t->children[3]->contents, "-")) {
trim_whitespace = 1;
}
-
- mpc_ast_t *left_node = t->children[2];
- char *lvalue = eval_expression(left_node, ctx);
-
- mpc_ast_t *op = t->children[4];
- if (op != NULL && strstr(op->tag, "op")) {
- mpc_ast_t *right_node = t->children[6];
- char *rvalue = eval_expression(right_node, ctx);
-
- /* if operator is + and either left or right node is of type string: concat */
- if (op->contents[0] == '+' && (strstr(left_node->tag, "string") || strstr(right_node->tag, "string"))) {
- sprintf(buf, "%s%s", lvalue, rvalue);
- } else {
- /* eval int infix expression */
- int result;
- switch (op->contents[0]) {
- case '+': result = atoi(lvalue) + atoi(rvalue); break;
- case '-': result = atoi(lvalue) - atoi(rvalue); break;
- case '/': result = atoi(lvalue) / atoi(rvalue); break;
- case '*': result = atoi(lvalue) * atoi(rvalue); break;
- case '>': result = atoi(lvalue) > atoi(rvalue); break;
- case '<': result = atoi(lvalue) < atoi(rvalue); break;
- }
- sprintf(buf, "%d", result);
- }
- strcat(dest, buf);
- return 0;
- }
- strcat(dest, lvalue);
+ mpc_ast_t *expr = t->children[2];
+ struct unja_object *obj = eval_expression(expr, ctx);
+ object_to_str(dest, obj);
+ object_free(obj);
return 0;
}
@@ -118,6 +225,18 @@ int eval(char *dest, mpc_ast_t* t, struct hashmap *ctx) {
return 0;
}
+ if (strstr(t->tag, "content|statement|if")) {
+ mpc_ast_t *expr = t->children[2];
+ struct unja_object *result = eval_expression(expr, ctx);
+
+ if (object_is_truthy(result)) {
+ eval(dest, t->children[4], ctx);
+ }
+
+ object_free(result);
+ return 0;
+ }
+
if (strstr(t->tag, "content|text")) {
char *str = t->contents;
if (trim_whitespace) {
@@ -142,6 +261,7 @@ mpc_parser_t *parser_init() {
mpc_parser_t *string = mpc_new("string");
mpc_parser_t *op = mpc_new("op");
mpc_parser_t *text = mpc_new("text");
+ mpc_parser_t *print = mpc_new("print");
mpc_parser_t *expression = mpc_new("expression");
mpc_parser_t *comment = mpc_new("comment");
mpc_parser_t *statement = mpc_new("statement");
@@ -154,29 +274,31 @@ mpc_parser_t *parser_init() {
mpc_parser_t *body = mpc_new("body");
mpc_parser_t *content = mpc_new("content");
mpc_parser_t *template = mpc_new("template");
- mpca_lang(MPCA_LANG_WHITESPACE_SENSITIVE,
- " symbol : /[a-zA-Z_.]+/ ;"
+ mpca_lang(MPCA_LANG_DEFAULT,
+ " symbol : /[a-zA-Z][a-zA-Z0-9_.]*/ ;"
" number : /[0-9]+/ ;"
" string : '\"' /([^\"])*/ '\"' ;"
" op : '+' | '-' | '*' | '/' | '>' | '<';"
" text : /[^{][^{%#]*/ ;"
- " expression : \"{{\" /-? */ (<symbol> | <number> | <string> ) (/ */ <op> / */ (<symbol> | <number> | <string> ))? / *-?/ \"}}\" ;"
+ " expression: (<symbol> | <number> | <string>) (<op> (<symbol> | <number> | <string>))* ;"
+ " print : \"{{\" /-? */ <expression> / *-?/ \"}}\" ;"
" comment : \"{#\" /[^#][^#}]*/ \"#}\" ;"
" statement_open: \"{%\" /-? */;"
" statement_close: / *-?/ \"%}\";"
- " for : <statement_open> \"for \" <symbol> \" in \" <symbol> <statement_close> <body> <statement_open> \"endfor\" <statement_close> ;"
+ " for : <statement_open> \"for \" <symbol> \"in\" <symbol> <statement_close> <body> <statement_open> \"endfor\" <statement_close> ;"
" block : <statement_open> \"block \" <statement_close> <statement_open> \"endblock\" <statement_close>;"
" extends : <statement_open> \"extends \" <statement_close>;"
/* TODO: Extend parser to include expression */
- " if : <statement_open> \"if \" <statement_close> <body> <statement_open> \"endif\" <statement_close> ;"
+ " if : <statement_open> \"if \" <expression> <statement_close> <body> <statement_open> \"endif\" <statement_close> ;"
" statement : <for> | <block> | <extends> | <if> ;"
- " content : <expression> | <statement> | <text> | <comment>;"
+ " content : <print> | <statement> | <text> | <comment>;"
" body : <content>* ;"
" template : /^/ <body> /$/ ;",
symbol,
op,
number,
string,
+ print,
expression,
text,
comment,
@@ -212,7 +334,7 @@ char *template(char *tmpl, struct hashmap *ctx) {
#endif
// FIXME: Allocate precisely
- char *output = malloc(strlen(tmpl) * 2);
+ char *output = malloc(strlen(tmpl) * 4);
output[0] = '\0';
eval(output, r.output, ctx);