diff options
author | Tavian Barnes <tavianator@gmail.com> | 2009-11-04 22:19:29 -0500 |
---|---|---|
committer | Tavian Barnes <tavianator@gmail.com> | 2009-11-04 22:19:29 -0500 |
commit | eb0bde2d4dc1bdb6653ee216764c373066222b79 (patch) | |
tree | 20cfd039f19e0a426e00afca5f4c3f8a5855bbe2 /dimension/parse.c | |
parent | 5bde27bf4b3064a94131b71019469464023a6f63 (diff) | |
download | dimension-eb0bde2d4dc1bdb6653ee216764c373066222b79.tar.xz |
Parse arithmetic expressions.
Diffstat (limited to 'dimension/parse.c')
-rw-r--r-- | dimension/parse.c | 291 |
1 files changed, 236 insertions, 55 deletions
diff --git a/dimension/parse.c b/dimension/parse.c index 51203c5..25d983c 100644 --- a/dimension/parse.c +++ b/dimension/parse.c @@ -50,8 +50,8 @@ dmnsn_parse_expect(dmnsn_token_type type, const dmnsn_array *tokens, } static int -dmnsn_parse_float(const dmnsn_array *tokens, unsigned int *ip, - dmnsn_array *astree) +dmnsn_parse_number(const dmnsn_array *tokens, unsigned int *ip, + dmnsn_array *astree) { unsigned int i = *ip; @@ -62,55 +62,233 @@ dmnsn_parse_float(const dmnsn_array *tokens, unsigned int *ip, return 1; } - int negative = 0; dmnsn_token token; dmnsn_array_get(tokens, i, &token); - if (token.type == DMNSN_T_MINUS) { - negative = 1; + dmnsn_astnode astnode; + astnode.children = NULL; + + if (token.type == DMNSN_T_INTEGER) { + astnode.type = DMNSN_AST_INTEGER; + long *value = malloc(sizeof(double)); + if (!value) + dmnsn_error(DMNSN_SEVERITY_HIGH, "Couldn't allocate room for integer."); + + *value = strtol(token.value, NULL, 0); + astnode.ptr = value; ++i; - if (i >= dmnsn_array_size(tokens)) { - fprintf(stderr, "Expected '%s' or '%s', found end of file\n", - dmnsn_token_string(DMNSN_T_INTEGER), - dmnsn_token_string(DMNSN_T_FLOAT)); - return 1; - } - dmnsn_array_get(tokens, i, &token); - } + } else if (token.type == DMNSN_T_FLOAT) { + astnode.type = DMNSN_AST_FLOAT; - double *value = malloc(sizeof(double)); - if (!value) - dmnsn_error(DMNSN_SEVERITY_HIGH, "Couldn't allocate room for float."); + double *value = malloc(sizeof(double)); + if (!value) + dmnsn_error(DMNSN_SEVERITY_HIGH, "Couldn't allocate room for float."); - if (token.type == DMNSN_T_INTEGER || token.type == DMNSN_T_FLOAT) { *value = strtod(token.value, NULL); + astnode.ptr = value; ++i; } else { - fprintf(stderr, "Expected '%s' or '%s', found '%s'\n", - dmnsn_token_string(DMNSN_T_INTEGER), - dmnsn_token_string(DMNSN_T_FLOAT), - dmnsn_token_string(token.type)); + dmnsn_diagnostic(token.filename, token.line, token.col, + "Expected '%s' or '%s', found '%s'", + dmnsn_token_string(DMNSN_T_INTEGER), + dmnsn_token_string(DMNSN_T_FLOAT), + dmnsn_token_string(token.type)); + goto bailout; + } + + dmnsn_array_push(astree, &astnode); + *ip = i; + return 0; + + bailout: + dmnsn_delete_astree(astnode.children); + return 1; +} + +static dmnsn_astnode_type +dmnsn_op_map(dmnsn_token_type type) +{ + switch (type) { + case DMNSN_T_PLUS: + return DMNSN_AST_ADD; + + case DMNSN_T_MINUS: + return DMNSN_AST_SUB; + + case DMNSN_T_STAR: + return DMNSN_AST_MUL; + + case DMNSN_T_SLASH: + return DMNSN_AST_DIV; + + default: + dmnsn_error(DMNSN_SEVERITY_HIGH, "Invalid token mapped to operator."); + return 0; /* Silence compiler warning */ + } +} + +static int +dmnsn_op_precedence(dmnsn_astnode_type type) +{ + switch(type) { + case DMNSN_AST_ADD: + case DMNSN_AST_SUB: + return 0; + + case DMNSN_AST_MUL: + case DMNSN_AST_DIV: return 1; + + default: + dmnsn_error(DMNSN_SEVERITY_HIGH, + "Precedence asked of invalid operator."); + return 0; /* Silence compiler warning */ } +} - if (negative) - *value *= -1.0; +static int dmnsn_parse_arithexp(const dmnsn_array *tokens, unsigned int *ip, + dmnsn_array *astree); - dmnsn_astnode astnode; - astnode.type = DMNSN_AST_FLOAT; - astnode.children = NULL; - astnode.ptr = value; +static int +dmnsn_parse_arithexp(const dmnsn_array *tokens, unsigned int *ip, + dmnsn_array *astree) +{ + dmnsn_token token; + unsigned int i = *ip; - dmnsn_array_push(astree, &astnode); + if (i >= dmnsn_array_size(tokens)) { + fprintf(stderr, "Expected arithmetic expression, found end of file\n"); + return 1; + } + + dmnsn_array *numstack = dmnsn_new_array(sizeof(dmnsn_astnode)), + *opstack = dmnsn_new_array(sizeof(dmnsn_astnode_type)); + + while (i < dmnsn_array_size(tokens)) { + dmnsn_array_get(tokens, i, &token); + + if (token.type == DMNSN_T_INTEGER || token.type == DMNSN_T_FLOAT) { + dmnsn_parse_number(tokens, &i, numstack); + } else if (token.type == DMNSN_T_LPAREN) { + ++i; + + if (dmnsn_parse_arithexp(tokens, &i, numstack) != 0) + goto bailout; + + if (dmnsn_parse_expect(DMNSN_T_RPAREN, tokens, &i) != 0) + goto bailout; + } else if (token.type == DMNSN_T_PLUS + || token.type == DMNSN_T_MINUS + || token.type == DMNSN_T_STAR + || token.type == DMNSN_T_SLASH) + + { + if (dmnsn_array_size(opstack) == dmnsn_array_size(numstack)) { + if (token.type == DMNSN_T_MINUS) { + /* Unary '-' -- negation */ + dmnsn_astnode astnode; + astnode.type = DMNSN_AST_NEGATE; + astnode.children = dmnsn_new_array(sizeof(dmnsn_astnode)); + astnode.ptr = NULL; + + ++i; + dmnsn_array_get(tokens, i, &token); + if (token.type == DMNSN_T_LPAREN) { + if (dmnsn_parse_arithexp(tokens, &i, astnode.children) != 0) + goto bailout; + } else { + if (dmnsn_parse_number(tokens, &i, astnode.children) != 0) + goto bailout; + } + + dmnsn_array_push(numstack, &astnode); + } else { + dmnsn_diagnostic(token.filename, token.line, token.col, + "Unexpected '%s' when parsing arithmetic expression", + dmnsn_token_string(token.type)); + return 1; + } + } else if (dmnsn_array_size(opstack) == 0) { + dmnsn_astnode_type type = dmnsn_op_map(token.type); + dmnsn_array_push(opstack, &type); + ++i; + } else { + dmnsn_astnode_type type = dmnsn_op_map(token.type), last_type; + dmnsn_array_get(opstack, dmnsn_array_size(opstack) - 1, &last_type); + + while (dmnsn_op_precedence(type) <= dmnsn_op_precedence(last_type)) { + dmnsn_astnode astnode, lhs, rhs; + astnode.type = last_type; + astnode.children = dmnsn_new_array(sizeof(dmnsn_astnode)); + astnode.ptr = NULL; + + dmnsn_array_pop(numstack, &rhs); + dmnsn_array_pop(numstack, &lhs); + + dmnsn_array_push(astnode.children, &lhs); + dmnsn_array_push(astnode.children, &rhs); + + dmnsn_array_push(numstack, &astnode); + dmnsn_array_resize(opstack, dmnsn_array_size(opstack) - 1); + + if (dmnsn_array_size(opstack) > 0) { + dmnsn_array_get(opstack, dmnsn_array_size(opstack) - 1, &last_type); + } else { + break; + } + } + + dmnsn_array_push(opstack, &type); + ++i; + } + } else { + break; + } + } + + if (dmnsn_array_size(numstack) == 0) { + dmnsn_diagnostic(token.filename, token.line, token.col, + "Expected arithmetic expression, found '%s'", + dmnsn_token_string(token.type)); + goto bailout; + } + + while (dmnsn_array_size(numstack) > 1) { + dmnsn_astnode_type type; + dmnsn_array_pop(opstack, &type); + + dmnsn_astnode astnode, lhs, rhs; + astnode.type = type; + astnode.children = dmnsn_new_array(sizeof(dmnsn_astnode)); + astnode.ptr = NULL; + + dmnsn_array_pop(numstack, &rhs); + dmnsn_array_pop(numstack, &lhs); + + dmnsn_array_push(astnode.children, &lhs); + dmnsn_array_push(astnode.children, &rhs); + + dmnsn_array_push(numstack, &astnode); + } + + dmnsn_array_push(astree, dmnsn_array_at(numstack, 0)); + dmnsn_delete_array(opstack); + dmnsn_delete_array(numstack); *ip = i; return 0; + + bailout: + dmnsn_delete_array(opstack); + dmnsn_delete_astree(numstack); + return 1; } static int dmnsn_parse_vector(const dmnsn_array *tokens, unsigned int *ip, dmnsn_array *astree) { + dmnsn_token token; unsigned int i = *ip; if (dmnsn_parse_expect(DMNSN_T_LESS, tokens, &i) != 0) @@ -121,26 +299,22 @@ dmnsn_parse_vector(const dmnsn_array *tokens, unsigned int *ip, astnode.children = dmnsn_new_array(sizeof(dmnsn_astnode)); astnode.ptr = NULL; - /* First element */ - if (dmnsn_parse_float(tokens, &i, astnode.children) != 0) - goto bailout; - - if (dmnsn_parse_expect(DMNSN_T_COMMA, tokens, &i) != 0) - goto bailout; - - /* Second element */ - if (dmnsn_parse_float(tokens, &i, astnode.children) != 0) - goto bailout; - - if (dmnsn_parse_expect(DMNSN_T_COMMA, tokens, &i) != 0) - goto bailout; + do { + if (dmnsn_parse_arithexp(tokens, &i, astnode.children) != 0) + goto bailout; - /* Third element */ - if (dmnsn_parse_float(tokens, &i, astnode.children) != 0) - goto bailout; + dmnsn_array_get(tokens, i, &token); + if (token.type != DMNSN_T_COMMA && token.type != DMNSN_T_GREATER) { + dmnsn_diagnostic(token.filename, token.line, token.col, + "Expected '%s' or '%s'; found '%s'", + dmnsn_token_string(DMNSN_T_COMMA), + dmnsn_token_string(DMNSN_T_GREATER), + dmnsn_token_string(token.type)); + goto bailout; + } - if (dmnsn_parse_expect(DMNSN_T_GREATER, tokens, &i) != 0) - goto bailout; + ++i; + } while (token.type != DMNSN_T_GREATER); dmnsn_array_push(astree, &astnode); *ip = i; @@ -214,7 +388,7 @@ dmnsn_parse_sphere(const dmnsn_array *tokens, unsigned int *ip, goto bailout; /* Radius */ - if (dmnsn_parse_float(tokens, &i, astnode.children) != 0) + if (dmnsn_parse_number(tokens, &i, astnode.children) != 0) goto bailout; if (dmnsn_parse_expect(DMNSN_T_RBRACE, tokens, &i) != 0) @@ -250,18 +424,13 @@ dmnsn_parse(const dmnsn_array *tokens) switch (token.type) { case DMNSN_T_BOX: - if (dmnsn_parse_box(tokens, &i, astree) != 0) { - dmnsn_diagnostic(token.filename, token.line, token.col, "Invalid box"); + if (dmnsn_parse_box(tokens, &i, astree) != 0) goto bailout; - } break; case DMNSN_T_SPHERE: - if (dmnsn_parse_sphere(tokens, &i, astree) != 0) { - dmnsn_diagnostic(token.filename, token.line, token.col, - "Invalid sphere"); + if (dmnsn_parse_sphere(tokens, &i, astree) != 0) goto bailout; - } break; default: @@ -310,9 +479,15 @@ dmnsn_delete_astree(dmnsn_array *astree) static void dmnsn_print_astnode(FILE *file, dmnsn_astnode astnode) { + long ivalue; double dvalue; switch (astnode.type) { + case DMNSN_AST_INTEGER: + ivalue = *(long *)astnode.ptr; + fprintf(file, "(%s %ld)", dmnsn_astnode_string(astnode.type), ivalue); + break; + case DMNSN_AST_FLOAT: dvalue = *(double *)astnode.ptr; fprintf(file, "(%s %g)", dmnsn_astnode_string(astnode.type), dvalue); @@ -377,9 +552,15 @@ dmnsn_astnode_string(dmnsn_astnode_type astnode_type) case type: \ return str; + dmnsn_astnode_map(DMNSN_AST_FLOAT, "float"); + dmnsn_astnode_map(DMNSN_AST_INTEGER, "integer"); + dmnsn_astnode_map(DMNSN_AST_NEGATE, "-"); + dmnsn_astnode_map(DMNSN_AST_ADD, "+"); + dmnsn_astnode_map(DMNSN_AST_SUB, "-"); + dmnsn_astnode_map(DMNSN_AST_MUL, "*"); + dmnsn_astnode_map(DMNSN_AST_DIV, "/"); dmnsn_astnode_map(DMNSN_AST_BOX, "box"); dmnsn_astnode_map(DMNSN_AST_VECTOR, "vector"); - dmnsn_astnode_map(DMNSN_AST_FLOAT, "float"); dmnsn_astnode_map(DMNSN_AST_SPHERE, "sphere"); default: |