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);  | 
