diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/template.c | 218 |
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); |