diff options
| author | Mustafa Quraish <[email protected]> | 2022-01-29 13:52:58 -0500 |
|---|---|---|
| committer | Mustafa Quraish <[email protected]> | 2022-01-29 13:53:57 -0500 |
| commit | e403475bbf6f70b3e79b6f4503dbadcc3dad504c (patch) | |
| tree | 40c77638cf96048328358e601c541ab31fb2b23a | |
| parent | Add some tests for local variables (diff) | |
| download | cup-e403475bbf6f70b3e79b6f4503dbadcc3dad504c.tar.xz cup-e403475bbf6f70b3e79b6f4503dbadcc3dad504c.zip | |
Allow uninitialized variable declarations
| -rw-r--r-- | cup/ast.h | 6 | ||||
| -rw-r--r-- | cup/generator.c | 15 | ||||
| -rw-r--r-- | cup/lexer.c | 3 | ||||
| -rw-r--r-- | cup/parser.c | 36 | ||||
| -rw-r--r-- | cup/tokens.h | 3 | ||||
| -rw-r--r-- | examples/variables.cup | 9 | ||||
| -rw-r--r-- | tests/common.sh | 23 | ||||
| -rwxr-xr-x | tests/variables.sh | 30 |
8 files changed, 88 insertions, 37 deletions
@@ -102,12 +102,16 @@ typedef struct ast_node { }; } literal; - // FIXME: Different struct for assignment? struct { Variable var; Node *value; } var_decl; + struct { + Variable *var; + Node *value; + } assign; + Variable *variable; }; } Node; diff --git a/cup/generator.c b/cup/generator.c index 1575b57..7403fac 100644 --- a/cup/generator.c +++ b/cup/generator.c @@ -20,10 +20,15 @@ void generate_expr_into_rax(Node *expr, FILE *out) fprintf(out, " mov rax, %d\n", expr->literal.as_int); } else if (expr->type == AST_VAR) { - i64 offset = expr->variable->offset; + i64 offset = expr->variable->offset; fprintf(out, " mov rax, [rbp-%lld]\n", offset); - } else if (expr->type == OP_NEG) { + } else if (expr->type == OP_ASSIGN) { + i64 offset = expr->assign.var->offset; + generate_expr_into_rax(expr->assign.value, out); + fprintf(out, " mov [rbp-%lld], rax\n", offset); + + } else if (expr->type == OP_NEG) { generate_expr_into_rax(expr->unary_expr, out); fprintf(out, " neg rax\n"); @@ -37,7 +42,7 @@ void generate_expr_into_rax(Node *expr, FILE *out) } else if (expr->type == OP_BWINV) { generate_expr_into_rax(expr->unary_expr, out); fprintf(out, " not rax\n"); - + } else if (expr->type == OP_PLUS) { generate_expr_into_rax(expr->binary.right, out); fprintf(out, " push rax\n"); @@ -183,8 +188,8 @@ void generate_statement(Node *stmt, FILE *out) fprintf(out, " mov [rbp-%lld], rax\n", offset); } } else { - fprintf(stderr, "Unsupported statement type in generate_statement: %s\n", node_type_to_str(stmt->type)); - exit(1); + // Once again, default to an expression here... + generate_expr_into_rax(stmt, out); } } diff --git a/cup/lexer.c b/cup/lexer.c index fcfe005..88df9e0 100644 --- a/cup/lexer.c +++ b/cup/lexer.c @@ -162,7 +162,10 @@ Token Lexer_next(Lexer *lexer) default: { + // Handle keywords explicitly if (Lexer_starts_with(lexer, "fn")) return Lexer_make_token(lexer, TOKEN_FN, 2); + if (Lexer_starts_with(lexer, "if")) return Lexer_make_token(lexer, TOKEN_IF, 2); + if (Lexer_starts_with(lexer, "else")) return Lexer_make_token(lexer, TOKEN_ELSE, 4); if (Lexer_starts_with(lexer, "return")) return Lexer_make_token(lexer, TOKEN_RETURN, 6); if (Lexer_starts_with(lexer, "int")) return Lexer_make_token(lexer, TOKEN_INT, 3); if (Lexer_starts_with(lexer, "let")) return Lexer_make_token(lexer, TOKEN_LET, 3); diff --git a/cup/parser.c b/cup/parser.c index ecd12f9..a47116b 100644 --- a/cup/parser.c +++ b/cup/parser.c @@ -119,9 +119,6 @@ Node *parse_var_declaration(Lexer *lexer) node->var_decl.var.name = token.value.as_string; assert_token(Lexer_next(lexer), TOKEN_COLON); node->var_decl.var.type = parse_type(lexer); - assert_token(Lexer_next(lexer), TOKEN_ASSIGN); - node->var_decl.value = parse_expression(lexer); - assert_token(Lexer_next(lexer), TOKEN_SEMICOLON); // Set offset for variable node->var_decl.var.offset = current_function->func.cur_stack_offset; @@ -133,6 +130,14 @@ Node *parse_var_declaration(Lexer *lexer) current_function->func.num_locals++; current_function->func.cur_stack_offset += var_size; + token = Lexer_next(lexer); + if (token.type == TOKEN_ASSIGN) { + node->var_decl.value = parse_expression(lexer); + assert_token(Lexer_next(lexer), TOKEN_SEMICOLON); + } else { + assert_token(token, TOKEN_SEMICOLON); + } + return node; } @@ -203,7 +208,25 @@ bool is_logical_and_token(TokenType type) { return type == TOKEN_AND; } Node *parse_logical_and(Lexer *lexer) { BINOP_PARSER(parse_equality, is_logical_and_token); } bool is_logical_or_token(TokenType type) { return type == TOKEN_OR; } -Node *parse_expression(Lexer *lexer) { BINOP_PARSER(parse_logical_and, is_logical_or_token); } +Node *parse_logical_or(Lexer *lexer) { BINOP_PARSER(parse_logical_and, is_logical_or_token); } + +Node *parse_expression(Lexer *lexer) +{ + Node *node = parse_logical_or(lexer); + // FIXME: This is a hack to handle assignment expressions + // and can probably be done properly. + if (node->type == AST_VAR) { + Token token = Lexer_peek(lexer); + if (token.type == TOKEN_ASSIGN) { + Lexer_next(lexer); + Variable *var = node->variable; + node->type = OP_ASSIGN; + node->assign.var = var; + node->assign.value = parse_expression(lexer); + } + } + return node; +} Node *parse_statement(Lexer *lexer) { @@ -218,8 +241,9 @@ Node *parse_statement(Lexer *lexer) } else if (token.type == TOKEN_LET) { return parse_var_declaration(lexer); } else { - die_location(token.loc, ": Unexpected token in parse_statement: %s\n", token_type_to_str(token.type)); - exit(1); + // Default to trying to handle it as an expression + node = parse_expression(lexer); + assert_token(Lexer_next(lexer), TOKEN_SEMICOLON); } return node; diff --git a/cup/tokens.h b/cup/tokens.h index d6c1fdd..84aacec 100644 --- a/cup/tokens.h +++ b/cup/tokens.h @@ -11,6 +11,8 @@ F(TOKEN_CLOSE_BRACE, "}") \ F(TOKEN_CLOSE_PAREN, ")") \ F(TOKEN_COLON, ":") \ + F(TOKEN_COMMA, ",") \ + F(TOKEN_ELSE, "else") \ F(TOKEN_EOF, "EOF") \ F(TOKEN_EQ, "==") \ F(TOKEN_EXCLAMATION, "!") \ @@ -18,6 +20,7 @@ F(TOKEN_GEQ, ">=") \ F(TOKEN_GT, ">") \ F(TOKEN_IDENTIFIER, "identifier") \ + F(TOKEN_IF, "if") \ F(TOKEN_INT, "int") \ F(TOKEN_INTLIT, "integer literal") \ F(TOKEN_LEQ, "<=") \ diff --git a/examples/variables.cup b/examples/variables.cup index 3ee8ed2..60a7d6a 100644 --- a/examples/variables.cup +++ b/examples/variables.cup @@ -1,8 +1,7 @@ -fn main() { - let x: int = 5 * 10; - let y: int = x + x; - let z: int = y - x / 2; - return z; +fn main(): int { + let x: int; + let y: int; + return x; } diff --git a/tests/common.sh b/tests/common.sh index 7c8b5ae..dd64ade 100644 --- a/tests/common.sh +++ b/tests/common.sh @@ -18,8 +18,8 @@ function assert_exit_status() { echo "" echo "----------------------------------" echo "Test failed: expected $2, got $res" - echo "- Input was:" - echo " \`$1\`" + echo "----------------------------------" + echo "$1" exit 1 fi set -e @@ -27,21 +27,6 @@ function assert_exit_status() { } function assert_exit_status_stdin() { - ./cupcc - - assemble - - set +e - ./a.out - res=$? - if [ $res -ne $1 ] - then - echo "" - echo "----------------------------------" - echo "Test failed: expected $2, got $res" - echo "- Input was:" - echo " \`$1\`" - exit 1 - fi - set -e - echo -n "." + code=$(</dev/stdin) + assert_exit_status "$code" $1 }
\ No newline at end of file diff --git a/tests/variables.sh b/tests/variables.sh index 94010d7..0c88784 100755 --- a/tests/variables.sh +++ b/tests/variables.sh @@ -5,9 +5,27 @@ set -e echo -n "- One variable: " -assert_exit_status 'fn main() { let x: int = 45; return 1; }' 1 +assert_exit_status 'fn main() { let x: int; x = 45; return x; }' 45 assert_exit_status 'fn main() { let x: int = 45; return x; }' 45 assert_exit_status 'fn main() { let x: int = 45; return x+x; }' 90 + +assert_exit_status_stdin 5 <<EOF +fn main() { + let x: int; + x = 3; + x = 5; + return x; +} +EOF + +assert_exit_status_stdin 5 <<EOF +fn main() { + let x: int = 3; + x = x + x - 1; + return x; +} +EOF + echo " OK" echo -n "- Multiple variable: " @@ -31,4 +49,14 @@ fn main() { } EOF +assert_exit_status_stdin 2 <<EOF +fn main() { + let x: int = 1; + let y: int = x + x; + y = y + x; + x = (x + x) * y; + return x / y; +} +EOF + echo " OK" |