Author | Jakob Wakeling <[email protected]> |
Date | 2023-05-02 06:29:28 |
Commit | 6dd92231459df661805b3d79c648ceb86f29413c |
Parent | 04569e6b84d3462bf923847abc39428e3c82fc21 |
Implement variable assignments
Diffstat
M | README.md | | | 5 | +++-- |
M | examples/main.g | | | 1 | + |
M | src/lex.h | | | 1 | + |
M | src/llvm.c | | | 49 | +++++++++++++++++++++++++++++++++++++++++++++++++ |
M | src/parse.c | | | 23 | ++++++++++++++++++++++- |
M | src/parse.h | | | 8 | +++++++- |
6 files changed, 83 insertions, 4 deletions
diff --git a/README.md b/README.md index 8697cdf..7cff0eb 100644 --- a/README.md +++ b/README.md @@ -59,19 +59,20 @@ command. The second command will output an executable file, *a.out* by default. - [x] Implement procedure declarations - [ ] Implement procedure calls +- [ ] Implement procedure arguments - [x] Implement variable declarations -- [ ] Implement variable assignments +- [x] Implement variable assignments - [x] Implement integers - [ ] Implement reals - [ ] Implement arrays - [x] Implement expression parsing - [x] Implement expression code generation - [ ] Implement the *type* type -- [ ] Implement multiple return values - [ ] Implement *defer* - [ ] Implement *errdefer* - [ ] Implement *if* and *else* - [ ] Implement *for* +- [ ] Implement multiple return values - [ ] Implement generics of some kind - [ ] Implement module definition - [ ] Implement module use diff --git a/examples/main.g b/examples/main.g index 97fa5ae..dc809b6 100644 --- a/examples/main.g +++ b/examples/main.g @@ -1,4 +1,5 @@ main :: proc() -> s64 { var := 42 + 8; + var += 2; return var; } diff --git a/src/lex.h b/src/lex.h index 4c06cff..24a451f 100644 --- a/src/lex.h +++ b/src/lex.h @@ -25,6 +25,7 @@ typedef enum { TK_AS_NOT, TK_AS_AND, TK_AS_OR, TK_AS_XOR, TK_AS_SHL, TK_AS_SHR, } tok_k; +/* k : Kind, ln : Line, cl : Column, h : Hash, s : String, v_? : Value */ typedef struct { tok_k k; UINT ln, cl; u64 h; char *s; union { u64 v_u64; s64 v_s64; f64 v_f64; }; diff --git a/src/llvm.c b/src/llvm.c index 6dfd355..5eeb3e6 100644 --- a/src/llvm.c +++ b/src/llvm.c @@ -26,6 +26,7 @@ static LLVMBuilderRef llvm_builder = NULL; static LLVMValueRef llvm_stmt(ast *a, syt *st); static LLVMValueRef llvm_stmt_compound(ast *a, syt *st); static LLVMValueRef llvm_stmt_decl(ast *a, syt *st); +static LLVMValueRef llvm_stmt_assn(ast *a, syt *st); static LLVMValueRef llvm_stmt_expr(ast *a, syt *st); static LLVMValueRef llvm_stmt_return(ast *a, syt *st); static LLVMValueRef llvm_stmt_if(ast *a, syt *st); @@ -85,6 +86,8 @@ static LLVMValueRef llvm_stmt(ast *a, syt *st) { switch (a->k) { case AK_COMP: { return llvm_stmt_compound(a, st); } break; case AK_DECL: { return llvm_stmt_decl(a, st); } break; + case AK_ASSIGN: case AK_AS_ADD: case AK_AS_SUB: case AK_AS_MUL: case AK_AS_DIV: case AK_AS_MOD: + { return llvm_stmt_assn(a, st); } break; case AK_RETURN: { return llvm_stmt_return(a, st); } break; case AK_IF: { return llvm_stmt_if(a, st); } break; case AK_FOR: { return llvm_stmt_for(a, st); } break; @@ -129,6 +132,52 @@ static LLVMValueRef llvm_stmt_decl(ast *a, syt *st) { return NULL; } +/* Generate IR for an assignment statement. */ +static LLVMValueRef llvm_stmt_assn(ast *a, syt *st) { + assert( + a->k == AK_ASSIGN || + a->k == AK_AS_ADD || + a->k == AK_AS_SUB || + a->k == AK_AS_MUL || + a->k == AK_AS_DIV || + a->k == AK_AS_MOD + ); + + ast *v = syt_search(st, a->s); + if (v == NULL) { error(2, "llvm_expr: Undefined variable %s", a->s); } + + switch (a->k) { + case AK_ASSIGN: { + return LLVMBuildStore(llvm_builder, llvm_expr(C[0], st), v->llvm_v); + } break; + case AK_AS_ADD: { + LLVMValueRef vr = LLVMBuildLoad2(llvm_builder, llvm_type(v->t), v->llvm_v, v->s); + LLVMValueRef rr = LLVMBuildAdd(llvm_builder, vr, llvm_expr(C[0], st), "as_add"); + return LLVMBuildStore(llvm_builder, rr, v->llvm_v); + } break; + case AK_AS_SUB: { + LLVMValueRef vr = LLVMBuildLoad2(llvm_builder, llvm_type(v->t), v->llvm_v, v->s); + LLVMValueRef rr = LLVMBuildSub(llvm_builder, vr, llvm_expr(C[0], st), "as_sub"); + return LLVMBuildStore(llvm_builder, rr, v->llvm_v); + } break; + case AK_AS_MUL: { + LLVMValueRef vr = LLVMBuildLoad2(llvm_builder, llvm_type(v->t), v->llvm_v, v->s); + LLVMValueRef rr = LLVMBuildMul(llvm_builder, vr, llvm_expr(C[0], st), "as_mul"); + return LLVMBuildStore(llvm_builder, rr, v->llvm_v); + } break; + case AK_AS_DIV: { + LLVMValueRef vr = LLVMBuildLoad2(llvm_builder, llvm_type(v->t), v->llvm_v, v->s); + LLVMValueRef rr = LLVMBuildSDiv(llvm_builder, vr, llvm_expr(C[0], st), "as_div"); + return LLVMBuildStore(llvm_builder, rr, v->llvm_v); + } break; + case AK_AS_MOD: { + LLVMValueRef vr = LLVMBuildLoad2(llvm_builder, llvm_type(v->t), v->llvm_v, v->s); + LLVMValueRef rr = LLVMBuildSRem(llvm_builder, vr, llvm_expr(C[0], st), "as_mod"); + return LLVMBuildStore(llvm_builder, rr, v->llvm_v); + } break; + } +} + /* Generate IR for an expression statement. */ static LLVMValueRef llvm_stmt_expr(ast *a, syt *st) { diff --git a/src/parse.c b/src/parse.c index ad11863..aa9b2ec 100644 --- a/src/parse.c +++ b/src/parse.c @@ -26,12 +26,15 @@ char *ast_ks[] = { "AK_OP_POS", "AK_OP_NEG", "AK_OP_ADD", "AK_OP_SUB", "AK_OP_MUL", "AK_OP_DIV", "AK_OP_MOD", + "AK_ASSIGN", "AK_AS_ADD", "AK_AS_SUB", "AK_AS_MUL", "AK_AS_DIV", "AK_AS_MOD", + "AK_ID_VAR", "AK_ID_PROC", "AK_INT", }; static ast *parse_stmt(lex *l, syt *st); static ast *parse_stmt_compound(lex *l, syt *st); static ast *parse_stmt_decl(lex *l, syt *st); +static ast *parse_stmt_assn(lex *l, syt *st, ast *a); static ast *parse_stmt_expr(lex *l, syt *st); static ast *parse_stmt_return(lex *l, syt *st); static ast *parse_stmt_if(lex *l, syt *st); @@ -112,7 +115,9 @@ static ast *parse_stmt_decl(lex *l, syt *st) { ast *sm = ast_init(); sm->k = AK_DECL; sm->ln = T.ln; sm->cl = T.cl; sm->h = T.h; sm->s = T.s; - lex_kind(l, TK_ID); lex_kind(l, TK_COLON); + lex_kind(l, TK_ID); + if (T.k != TK_COLON) { return parse_stmt_assn(l, st, sm); } + lex_kind(l, TK_COLON); /* Store the declaration's type if one is specified */ /* TODO store type when one is specified */ @@ -147,6 +152,22 @@ static ast *parse_stmt_decl(lex *l, syt *st) { end:; syt_insert_h(st, sm->h, sm->s, sm); return sm; } +/* Should only be called by parse_stmt_decl (?) */ +static ast *parse_stmt_assn(lex *l, syt *st, ast *a) { + switch (T.k) { + case TK_ASSIGN: { a->k = AK_ASSIGN; } goto expr; + case TK_AS_ADD: { a->k = AK_AS_ADD; } goto expr; + case TK_AS_SUB: { a->k = AK_AS_SUB; } goto expr; + case TK_AS_MUL: { a->k = AK_AS_MUL; } goto expr; + case TK_AS_DIV: { a->k = AK_AS_DIV; } goto expr; + case TK_AS_MOD: { a->k = AK_AS_MOD; } goto expr; + expr: { lex_next(l); ast_push(a, parse_expr(l, st)); } break; + default: { error(1, "%s:%zu:%zu: error: expected assignment operator", l->n, T.ln + 1, T.cl + 1); } break; + } + + lex_kind(l, TK_SCOLON); return a; +} + /* Parse an expression statement. */ static ast *parse_stmt_expr(lex *l, syt *st) { ast *a = NULL; if (T.k != TK_SCOLON) { a = parse_expr(l, st); } diff --git a/src/parse.h b/src/parse.h index fc7b5fd..b1e5a8d 100644 --- a/src/parse.h +++ b/src/parse.h @@ -14,6 +14,7 @@ #include <llvm-c/Types.h> +/* Remember to update ast_ks in parse.c */ typedef enum { AK_NULL, AK_PROG, AK_PROC, AK_TYPE, @@ -22,10 +23,15 @@ typedef enum { AK_OP_POS, AK_OP_NEG, AK_OP_ADD, AK_OP_SUB, AK_OP_MUL, AK_OP_DIV, AK_OP_MOD, + AK_ASSIGN, AK_AS_ADD, AK_AS_SUB, AK_AS_MUL, AK_AS_DIV, AK_AS_MOD, + AK_ID_VAR, AK_ID_PROC, AK_INT, } ast_k; -/* k : Kind, ln : Line, cl : Column, t : Type, c : Children */ +/* + k : Kind, ln : Line, cl : Column, h : Hash, s : String, t : Type, v: Value + st : Symbol Table, p : Parent, c : Children +*/ typedef struct ast_s { ast_k k; UINT ln, cl; u64 h; char *s; type *t; val v; syt st; struct ast_s *p; struct { struct ast_s **a; UINT al; } c;