aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cup/ast.h6
-rw-r--r--cup/generator.c15
-rw-r--r--cup/lexer.c3
-rw-r--r--cup/parser.c36
-rw-r--r--cup/tokens.h3
-rw-r--r--examples/variables.cup9
-rw-r--r--tests/common.sh23
-rwxr-xr-xtests/variables.sh30
8 files changed, 88 insertions, 37 deletions
diff --git a/cup/ast.h b/cup/ast.h
index 4b18c82..efbaf84 100644
--- a/cup/ast.h
+++ b/cup/ast.h
@@ -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"