summaryrefslogtreecommitdiff
path: root/parser.c
diff options
context:
space:
mode:
authorCarson Fleming <[email protected]>2026-03-26 16:21:29 -0400
committerCarson Fleming <[email protected]>2026-03-26 16:22:00 -0400
commit7d9fb2c733c8c64f6f74eefa0eea35b36be102cd (patch)
tree16b6cded5f9611e0ff1948395578845c9688b926 /parser.c
parent68db110d34611fc8bb79035d3a11bba07dea43f3 (diff)
downloadccc-7d9fb2c733c8c64f6f74eefa0eea35b36be102cd.tar.gz
let's go we can parse return zero most useful program ever
Diffstat (limited to 'parser.c')
-rw-r--r--parser.c190
1 files changed, 190 insertions, 0 deletions
diff --git a/parser.c b/parser.c
new file mode 100644
index 0000000..590ed2b
--- /dev/null
+++ b/parser.c
@@ -0,0 +1,190 @@
+#include "parser.h"
+#include "lexer.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#define PARSER_PANIC(format, ...) {\
+ fprintf(\
+ stderr,\
+ "ccc: parse error: %s: line %lu, column %lu: " format "\n",\
+ tok.PATH,\
+ tok.LINE,\
+ tok.COL __VA_OPT__(,)\
+ __VA_ARGS__);\
+ exit(1);\
+}
+
+static struct token tok;
+
+static void* protected_alloc(size_t sz) {
+ void* ptr = calloc(1, sz);
+ if (ptr == NULL) {
+ fprintf(stderr, "ccc: out of memory\n");
+ exit(1);
+ }
+ return ptr;
+}
+
+static void unexpected_token(enum token_type expected) {
+ /* TODO: print what token was expected */
+ PARSER_PANIC("unexpected token");
+}
+
+static void expect(enum token_type expected) {
+ if (!lexer_pop(&tok))
+ PARSER_PANIC("unexpected EOF");
+
+ if (tok.type != expected) unexpected_token(expected);
+}
+
+/* "handle" indicates that we've peeked already */
+static void handle_expr(struct expr_node* p_node);
+static void handle_stmt(struct stmt_node* p_node);
+
+static void parse_type(struct type_node* p_node) {
+ expect(TK_IDENT);
+ /* TODO: maybe check that this type is real haha */
+ p_node->type = tok.data.ident;
+}
+
+static void parse_var_decl(struct var_decl_node* p_node) {
+ parse_type(&p_node->type);
+ expect(TK_IDENT);
+ p_node->ident = tok.data.ident;
+}
+
+static void parse_group(struct group_node* p_node) {
+ expect(TK_LCURLY);
+
+ struct stmt_node** pp_node = &p_node->body_head;
+ for (;;) {
+ if (!lexer_peek(&tok))
+ PARSER_PANIC("unexpected EOF in statement group");
+
+ if (tok.type == TK_RCURLY) break;
+
+ *pp_node = protected_alloc(sizeof(struct stmt_node));
+ handle_stmt(*pp_node);
+ pp_node = &((*pp_node)->next);
+ }
+
+ expect(TK_RCURLY);
+}
+
+static void handle_stmt(struct stmt_node* p_node) {
+ if (tok.type == TK_LCURLY) {
+ p_node->type = STMT_GROUP;
+ parse_group(&p_node->as._group);
+ } else {
+ p_node->type = STMT_EXPR;
+ handle_expr(&p_node->as._expr);
+ expect(TK_SEMI);
+ }
+}
+
+static void parse_arg_list(struct var_decl_node** pp_arg) {
+ for (;;) {
+ *pp_arg = protected_alloc(sizeof(struct var_decl_node));
+ parse_var_decl(*pp_arg);
+ pp_arg = &((*pp_arg)->next);
+
+ if (!lexer_peek(&tok))
+ PARSER_PANIC("unexpected EOF in argument list");
+
+ if (tok.type == TK_RPAREN) break;
+ expect(TK_COMMA);
+ }
+}
+
+static void parse_fn_decl(struct fn_decl_node* p_node) {
+ parse_type(&p_node->return_type);
+
+ expect(TK_IDENT);
+ p_node->name = tok.data.ident;
+
+ expect(TK_LPAREN);
+
+ if (!lexer_peek(&tok))
+ PARSER_PANIC("unexpected EOF in function declaration");
+
+
+ if (tok.type != TK_RPAREN) parse_arg_list(&p_node->args_head);
+
+ expect(TK_RPAREN);
+
+ parse_group(&p_node->body);
+}
+
+static void parse_return(struct return_node* p_node) {
+ expect(TK_IDENT);
+ if (strcmp(tok.data.ident, "return") != 0)
+ PARSER_PANIC("unexpected token %s; expected: return", tok.data.ident);
+
+ if (!lexer_peek(&tok))
+ PARSER_PANIC("unexpected EOF in return statement");
+
+ if (tok.type == TK_SEMI) {
+ p_node->ret_val = NULL;
+ return;
+ }
+
+ p_node->ret_val = protected_alloc(sizeof(struct expr_node));
+ handle_expr(p_node->ret_val);
+}
+
+static void parse_int_lit(struct int_lit_node* p_node) {
+ expect(TK_INT_LIT);
+ p_node->val = tok.data.int_lit;
+}
+
+static void handle_expr(struct expr_node* p_node) {
+ switch (tok.type) {
+ case TK_SEMI:
+ p_node->type = EXPR_EMPTY;
+ return;
+ case TK_IDENT:
+ if (strcmp(tok.data.ident, "return") == 0) {
+ p_node->type = EXPR_RETURN;
+ parse_return(&p_node->as._return);
+ } else {
+ p_node->type = EXPR_VAR_DECL;
+ parse_var_decl(&p_node->as._var_decl);
+ }
+ return;
+ case TK_INT_LIT:
+ p_node->type = EXPR_INT_LIT;
+ parse_int_lit(&p_node->as._int_lit);
+ return;
+ default:
+ PARSER_PANIC("expected expression");
+ }
+}
+
+static bool parse_root(struct root_node* p_node) {
+ if (!lexer_peek(&tok)) return false;
+
+ p_node->type = ROOT_FN_DECL;
+ parse_fn_decl(&p_node->as._fn_decl);
+ return true;
+}
+
+struct root_node* parse(const char* path) {
+ lexer_load(path);
+
+ struct root_node* root;
+ struct root_node** p_node = &root;
+
+ for (;;) {
+ *p_node = protected_alloc(sizeof(struct root_node));
+ if (!parse_root(*p_node)) {
+ free(*p_node);
+ *p_node = NULL;
+ break;
+ }
+ p_node = &((*p_node)->next);
+ }
+
+ lexer_close();
+ return root;
+}