aboutsummaryrefslogtreecommitdiff
path: root/src/parser
diff options
context:
space:
mode:
authorhachem <im@hachem.wtf>2026-08-30 13:24:29 +0200
committerhachem <im@hachem.wtf>2026-08-30 13:24:29 +0200
commit9e979a49b041a9d0e8ba6f8fee50bd11520169af (patch)
treef0e5cc0047cfc9c6b11a83175a4e07b7b0917ea7 /src/parser
parent53946c982cb3290a25f7c8529ef1399f0635ef37 (diff)
add: parse expressions
Diffstat (limited to 'src/parser')
-rw-r--r--src/parser/ast.c41
-rw-r--r--src/parser/ast.h41
-rw-r--r--src/parser/parser.c96
3 files changed, 160 insertions, 18 deletions
diff --git a/src/parser/ast.c b/src/parser/ast.c
index 4f10915..97cdb6d 100644
--- a/src/parser/ast.c
+++ b/src/parser/ast.c
@@ -2,6 +2,42 @@
#include "parser/ast.h"
+void free_expr(struct Expr* expr)
+{
+ if (expr == NULL)
+ return;
+
+ switch (expr->kind)
+ {
+ case EXPR_PRIMARY:
+ break;
+ case EXPR_BINARY:
+ free_expr(expr->binary.left);
+ free_expr(expr->binary.right);
+ break;
+ case EXPR_MEMBER:
+ free_expr(expr->member.object);
+ break;
+ }
+
+ free(expr);
+}
+
+static void free_statement(struct Statement* statement)
+{
+ if (statement->kind == STATEMENT_ASSIGN)
+ free_expr(statement->assign.value);
+}
+
+void free_proc(struct ProcDecl* proc)
+{
+ free(proc->params);
+
+ for (size_t i = 0; i < proc->body_count; i += 1)
+ free_statement(&proc->body[i]);
+ free(proc->body);
+}
+
struct Program create_program(void)
{
struct Program program;
@@ -23,10 +59,7 @@ void free_program(struct Program* program)
free(program->data_decls);
for (size_t i = 0; i < program->proc_count; i += 1)
- {
- free(program->procs[i].params);
- free(program->procs[i].body);
- }
+ free_proc(&program->procs[i]);
free(program->procs);
program->consts = NULL;
diff --git a/src/parser/ast.h b/src/parser/ast.h
index 4604e1a..199d802 100644
--- a/src/parser/ast.h
+++ b/src/parser/ast.h
@@ -23,6 +23,42 @@ struct Param
struct Token reg;
};
+enum ExprKind
+{
+ EXPR_PRIMARY,
+ EXPR_BINARY,
+ EXPR_MEMBER,
+};
+
+struct PrimaryExpr
+{
+ struct Token token;
+};
+
+struct BinaryExpr
+{
+ struct Expr* left;
+ struct Token op;
+ struct Expr* right;
+};
+
+struct MemberExpr
+{
+ struct Expr* object;
+ struct Token member;
+};
+
+struct Expr
+{
+ enum ExprKind kind;
+ union
+ {
+ struct PrimaryExpr primary;
+ struct BinaryExpr binary;
+ struct MemberExpr member;
+ };
+};
+
enum StatementKind
{
STATEMENT_ASSIGN,
@@ -36,7 +72,7 @@ struct AssignStatement
bool target_deref;
struct Token target;
struct Token op;
- struct Token value;
+ struct Expr* value;
};
struct LabelStatement
@@ -93,6 +129,9 @@ void add_const(struct Program* program, struct ConstDecl decl);
void add_data(struct Program* program, struct DataDecl decl);
struct ProcDecl create_proc(void);
+void free_proc(struct ProcDecl* proc);
void add_param(struct ProcDecl* proc, struct Param param);
void add_statement(struct ProcDecl* proc, struct Statement statement);
void add_proc(struct Program* program, struct ProcDecl decl);
+
+void free_expr(struct Expr* expr);
diff --git a/src/parser/parser.c b/src/parser/parser.c
index a125b7d..5431bfd 100644
--- a/src/parser/parser.c
+++ b/src/parser/parser.c
@@ -123,17 +123,87 @@ static bool is_assign_op(enum TokenType type)
|| type == TOKEN_SLASH_EQUAL;
}
-static bool parse_value(struct Parser* parser, struct Token* out)
+static struct Expr* alloc_expr(enum ExprKind kind)
+{
+ struct Expr* expr = malloc(sizeof(struct Expr));
+ expr->kind = kind;
+ return expr;
+}
+
+static struct Expr* parse_primary(struct Parser* parser)
{
if (check(parser, TOKEN_IDENTIFIER) || check(parser, TOKEN_INTEGER) || check(parser, TOKEN_CHAR))
{
advance_parser(parser);
- *out = parser->previous;
- return true;
+
+ struct Expr* expr = alloc_expr(EXPR_PRIMARY);
+ expr->primary.token = parser->previous;
+ return expr;
}
- error_at(parser, parser->current, "expected a value");
- return false;
+ error_at(parser, parser->current, "expected an expression");
+ return NULL;
+}
+
+static struct Expr* parse_postfix(struct Parser* parser)
+{
+ struct Expr* expr = parse_primary(parser);
+ if (expr == NULL)
+ return NULL;
+
+ while (match_token(parser, TOKEN_DOT))
+ {
+ if (!consume(parser, TOKEN_IDENTIFIER, "expected member name after '.'"))
+ {
+ free_expr(expr);
+ return NULL;
+ }
+
+ struct Expr* member = alloc_expr(EXPR_MEMBER);
+ member->member.object = expr;
+ member->member.member = parser->previous;
+ expr = member;
+ }
+
+ return expr;
+}
+
+static struct Expr* parse_binary(struct Parser* parser, struct Expr* (*operand)(struct Parser*), enum TokenType a, enum TokenType b)
+{
+ struct Expr* left = operand(parser);
+ if (left == NULL)
+ return NULL;
+
+ while (check(parser, a) || check(parser, b))
+ {
+ advance_parser(parser);
+ struct Token op = parser->previous;
+
+ struct Expr* right = operand(parser);
+ if (right == NULL)
+ {
+ free_expr(left);
+ return NULL;
+ }
+
+ struct Expr* binary = alloc_expr(EXPR_BINARY);
+ binary->binary.left = left;
+ binary->binary.op = op;
+ binary->binary.right = right;
+ left = binary;
+ }
+
+ return left;
+}
+
+static struct Expr* parse_multiplicative(struct Parser* parser)
+{
+ return parse_binary(parser, parse_postfix, TOKEN_STAR, TOKEN_SLASH);
+}
+
+static struct Expr* parse_expression(struct Parser* parser)
+{
+ return parse_binary(parser, parse_multiplicative, TOKEN_PLUS, TOKEN_MINUS);
}
static bool parse_statement(struct Parser* parser, struct Statement* out)
@@ -173,17 +243,18 @@ static bool parse_statement(struct Parser* parser, struct Statement* out)
return false;
}
- struct AssignStatement assign;
- assign.target_deref = deref;
- assign.target = name;
advance_parser(parser);
- assign.op = parser->previous;
+ struct Token op = parser->previous;
- if (!parse_value(parser, &assign.value))
+ struct Expr* value = parse_expression(parser);
+ if (value == NULL)
return false;
out->kind = STATEMENT_ASSIGN;
- out->assign = assign;
+ out->assign.target_deref = deref;
+ out->assign.target = name;
+ out->assign.op = op;
+ out->assign.value = value;
return true;
}
@@ -226,8 +297,7 @@ static bool parse_proc(struct Parser* parser, struct Program* program)
return true;
error:
- free(decl.params);
- free(decl.body);
+ free_proc(&decl);
return false;
}