aboutsummaryrefslogtreecommitdiff
path: root/src/parser.c
diff options
context:
space:
mode:
authorMustafa Quraish <[email protected]>2022-01-31 01:26:07 -0500
committerMustafa Quraish <[email protected]>2022-01-31 01:26:07 -0500
commitcb0d77c3dc83d6c7cb39642e8708b12a29b7b8c7 (patch)
tree904a6c70509da3f9274bb2df8e214f2b9e6fd9ac /src/parser.c
parentPut tokens in their own macro to allow looping over them (diff)
downloadcup-cb0d77c3dc83d6c7cb39642e8708b12a29b7b8c7.tar.xz
cup-cb0d77c3dc83d6c7cb39642e8708b12a29b7b8c7.zip
Add basic builtin-function support
This isn't really super extendible for now, but it's a start and gives us the `print` builtin which allows us to finally actually print out values to the screen, so we can move away from testing with exit codes eventually.
Diffstat (limited to 'src/parser.c')
-rw-r--r--src/parser.c91
1 files changed, 62 insertions, 29 deletions
diff --git a/src/parser.c b/src/parser.c
index cfb5d97..2c11656 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -33,6 +33,21 @@ Token do_assert_token(Token token, TokenType type, char *filename, int line)
* Some helpers
*/
+void Node_add_child(Node *parent, Node *child)
+{
+ // TODO, use a vector
+ parent->block.children = realloc(parent->block.children, sizeof(Node *) * (parent->block.num_children + 1));
+ parent->block.children[parent->block.num_children] = child;
+ parent->block.num_children++;
+}
+
+Node *Node_new(NodeType type)
+{
+ Node *self = calloc(sizeof(Node), 1);
+ self->type = type;
+ return self;
+}
+
NodeType binary_token_to_op(TokenType type)
{
switch (type)
@@ -53,7 +68,7 @@ NodeType binary_token_to_op(TokenType type)
case TOKEN_LEQ: return OP_LEQ;
case TOKEN_GT: return OP_GT;
case TOKEN_GEQ: return OP_GEQ;
-
+
default: assert(false && "binary_token_to_op called with invalid token type");
}
}
@@ -85,6 +100,33 @@ void dump_block_stack()
}
}
+Node *builtin_print;
+Node *builtin_putc;
+
+void initialize_builtins()
+{
+ builtin_print = Node_new(AST_BUILTIN);
+ builtin_print->func.name = "print";
+ builtin_print->func.return_type = (Type){TYPE_INT,0};
+ builtin_print->func.num_args = 1;
+ builtin_print->func.args = (Variable *)calloc(sizeof(Variable), 1);
+ builtin_print->func.args[0] = (Variable){"val", (Type){TYPE_INT,0}, 0};
+
+ builtin_putc = Node_new(AST_BUILTIN);
+ builtin_putc->func.name = "putc";
+ builtin_putc->func.return_type = (Type){TYPE_INT,0};
+ builtin_putc->func.num_args = 1;
+ builtin_putc->func.args = (Variable *)calloc(sizeof(Variable), 2);
+ builtin_putc->func.args[0] = (Variable){"arg", (Type){TYPE_INT,0}, 0};
+}
+
+Node *find_builtin_function(Token *token)
+{
+ if (strcmp(token->value.as_string, "putc") == 0) { return builtin_putc; }
+ if (strcmp(token->value.as_string, "print") == 0) { return builtin_print; }
+ return NULL;
+}
+
Variable *find_local_variable(Token *token)
{
assert_token(*token, TOKEN_IDENTIFIER);
@@ -126,7 +168,7 @@ void add_variable_to_current_block(Variable *var)
int new_len = (cur_block->block.num_locals + 1);
int var_size = 8; // TODO: Compute sizes based on different types
-
+
// Add to the block
// FIXME: Use a map here
cur_block->block.locals = realloc(cur_block->block.locals, sizeof(Variable *) * new_len);
@@ -137,7 +179,7 @@ void add_variable_to_current_block(Variable *var)
// Update current stack offset (w.r.t function stack frame) and block size
cur_stack_offset += var_size;
block_stack[block_stack_count-1]->block.locals_size += var_size;
-
+
// Update function's max locals size
i64 max_offset = i64max(current_function->func.max_locals_size, cur_stack_offset);
current_function->func.max_locals_size = max_offset;
@@ -146,21 +188,6 @@ void add_variable_to_current_block(Variable *var)
assert(block_stack_count > 0);
}
-void Node_add_child(Node *parent, Node *child)
-{
- // TODO, use a vector
- parent->block.children = realloc(parent->block.children, sizeof(Node *) * (parent->block.num_children + 1));
- parent->block.children[parent->block.num_children] = child;
- parent->block.num_children++;
-}
-
-Node *Node_new(NodeType type)
-{
- Node *self = calloc(sizeof(Node), 1);
- self->type = type;
- return self;
-}
-
Type parse_type(Lexer *lexer)
{
Type type = {0};
@@ -197,13 +224,13 @@ Node *parse_var_declaration(Lexer *lexer)
// TODO: Reuse this for globals? Or maybe just make a new function?
if (!current_function || current_function->type != AST_FUNC)
die_location(token.loc, "Variable declaration outside of function");
-
+
token = assert_token(Lexer_next(lexer), TOKEN_IDENTIFIER);
- // NOTE: We don't allow shadowing of variables in the any blocks,
+ // NOTE: We don't allow shadowing of variables in the any blocks,
// this is by design since it's a common mistake.
if (find_local_variable(&token) != NULL)
die_location(token.loc, "Variable `%s` already declared", token.value.as_string);
-
+
Node *node = Node_new(AST_VARDECL);
node->var_decl.var.name = token.value.as_string;
@@ -239,7 +266,7 @@ Node *parse_function_call_args(Lexer *lexer, Node *func)
int new_size = call->call.num_args + 1;
call->call.args = realloc(call->call.args, sizeof(Node *) * new_size);
call->call.args[call->call.num_args++] = arg;
-
+
if (new_size > func->func.num_args)
die_location(identifier.loc, "Too many arguments to function `%s`", func->func.name);
@@ -280,22 +307,27 @@ Node *parse_factor(Lexer *lexer)
assert_token(Lexer_next(lexer), TOKEN_CLOSE_PAREN);
} else if (token.type == TOKEN_INTLIT) {
expr = parse_literal(lexer);
- } else if (token.type == TOKEN_IDENTIFIER) {
+ } else if (token.type == TOKEN_IDENTIFIER) {
// TODO: Check for global variables when added
- Variable *var = find_local_variable(&token);
+ Variable *var = find_local_variable(&token);
if (var != NULL) {
Lexer_next(lexer);
expr = Node_new(AST_VAR);
expr->variable = var;
return expr;
}
-
+
Node *func = find_function_definition(&token);
if (func != NULL) {
return parse_function_call_args(lexer, func);
}
+ Node *builtin = find_builtin_function(&token);
+ if (builtin != NULL) {
+ return parse_function_call_args(lexer, builtin);
+ }
+
die_location(token.loc, "Unknown identifier `%s`", token.value.as_string);
expr = NULL;
} else {
@@ -410,7 +442,7 @@ Node *parse_statement(Lexer *lexer)
Lexer_next(lexer);
node = Node_new(AST_FOR);
assert_token(Lexer_next(lexer), TOKEN_OPEN_PAREN);
-
+
// All of the expressions in the for loop are optional
// TODO: Allow this to be a declaration, need to inject
@@ -482,7 +514,7 @@ void parse_func_args(Lexer *lexer, Node *func)
token = assert_token(Lexer_next(lexer), TOKEN_IDENTIFIER);
// TODO: Check for shadowing with globals
assert_token(Lexer_next(lexer), TOKEN_COLON);
- Type type = parse_type(lexer);
+ Type type = parse_type(lexer);
i64 new_count = func->func.num_args + 1;
func->func.args = realloc(func->func.args, sizeof(Variable) * new_count);
@@ -499,10 +531,10 @@ void parse_func_args(Lexer *lexer, Node *func)
assert_token(Lexer_next(lexer), TOKEN_CLOSE_PAREN);
// Set the offsets for the arguments
-
+
// IMPORTANT: We want to skip the saved ret_addr+old_rbp that we
// pushed on the stack. Each of these is 8 bytes.
- int offset = -16;
+ int offset = -16;
for (int i = 0; i < func->func.num_args; i++) {
Variable *var = &func->func.args[i];
var->offset = offset;
@@ -546,6 +578,7 @@ Node *parse_func(Lexer *lexer)
Node *parse_program(Lexer *lexer)
{
+ initialize_builtins();
Node *program = Node_new(AST_PROGRAM);
Token token;
while ((token = Lexer_peek(lexer)).type != TOKEN_EOF) {