Author | Jakob Wakeling <[email protected]> |
Date | 2023-07-06 05:52:50 |
Commit | 2be2f4390b177036c5fa3b2ff11bf17e53003e97 |
Parent | 1aa55193278a3067cd2b55634c052bbc0ac3553a |
Implement for statements
Diffstat
M | README.md | | | 19 | ++++++++++++------- |
A | examples/loop.g | | | 9 | +++++++++ |
M | src/analyse.c | | | 10 | ++++++++-- |
M | src/lex.c | | | 21 | +++++++++++---------- |
M | src/lex.h | | | 8 | ++++---- |
M | src/llvm.c | | | 24 | ++++++++++++++++++++++++ |
M | src/parse.c | | | 28 | +++++++++++++++++++++++----- |
7 files changed, 91 insertions, 28 deletions
diff --git a/README.md b/README.md index 55eae29..afcc067 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # The G Programming Language -A modern alternative to **C** intended to be fast, simple, and pleasant. -Influenced by **C**, **C++**, **Odin**, and others. +A modern alternative to **C** intended to be simple, fast, and pleasant. +Influenced by **C**, **Odin**, **Zig**, and others. Note that at present, **G** is highly unstable and will certainly change. @@ -58,19 +58,22 @@ command. The second command will output an executable file, *a.out* by default. - [x] Implement expressions - [x] Implement type casting - [ ] Implement type casting to pointers and arrays -- [ ] Implement the *type* type -- [ ] Implement labels and *goto* -- [x] Implement *if* and *else* -- [ ] Implement *for* -- [ ] Implement *break* and *continue* -- [ ] Implement *defer* -- [ ] Implement *errdefer* (?) +- [ ] Implement the `type` type +- [ ] Implement the `error` type +- [ ] Implement labels and `goto` +- [x] Implement `if` and `else` +- [x] Implement `for` +- [ ] Implement `break` and `continue` +- [ ] Implement `defer` +- [ ] Implement `errdefer` (?) - [ ] Implement first class strings - [x] Implement syscalls - [ ] Implement generics of some kind - [ ] Implement module definition - [ ] Implement module use - [ ] Implement foreign code calling +- [ ] Optional types using `?` prefix +- [ ] Error or types using `!` prefix (?) - ... - [ ] Re-write compiler in **G** diff --git a/examples/loop.g b/examples/loop.g new file mode 100644 index 0000000..73c958d --- /dev/null +++ b/examples/loop.g @@ -0,0 +1,9 @@ +main :: proc() -> u8 { + for (i : u8 = 0; i < 6; i += 1) { + c : u8 = i + 49; nl : u8 = '\n'; + #syscall(uint(1), uint(1), &c, uint(1)); + #syscall(uint(1), uint(1), &nl, uint(1)); + } + + return 0; +} diff --git a/src/analyse.c b/src/analyse.c index 0f8f026..8a83441 100644 --- a/src/analyse.c +++ b/src/analyse.c @@ -143,7 +143,10 @@ static void analyse_stmt_if(ast *a, syt *st) { /* Analyse a for statement. AK_FOR has a scope. */ static void analyse_stmt_for(ast *a, syt *st) { - /* TODO */ + assert(A.k == AK_FOR); + assert(CL == 2 || CL == 4); + + for (UINT i = 0; i < CL; i += 1) { analyse_stmt(C[i], &A.st); } } /* Analyse an expression. */ @@ -170,9 +173,10 @@ static void analyse_expr(ast *a, syt *st) { note("TODO", A.ln, A.cl, 0, "Expression must have integral type"); } } break; - case AK_PROC: { analyse_expr_proc(a, st); } break; - case AK_HASH: { analyse_expr_hash(a, st); } break; - case AK_OP_DRF: { A.t = ast_type(C[0], st)->base; } break; + case AK_PROC: { analyse_expr_proc(a, st); } break; + case AK_HASH: { analyse_expr_hash(a, st); } break; + case AK_OP_ADO: { A.t = type_ptr(ast_type(C[0], st), 1); } break; + case AK_OP_DRF: { A.t = ast_type(C[0], st)->base; } break; default: { for (UINT i = 0; i < CL; i += 1) { analyse_expr(C[i], st); }} break; } } diff --git a/src/lex.c b/src/lex.c index 5d82014..e3c0946 100644 --- a/src/lex.c +++ b/src/lex.c @@ -20,16 +20,16 @@ char *tok_ks[] = { "TK_IF", "TK_ELSE", "TK_FOR", "TK_BREAK", "TK_CONTINUE", "TK_LPAREN", "TK_RPAREN", "TK_LBRACK", "TK_RBRACK", "TK_LBRACE", "TK_RBRACE", - "TK_COLON", "TK_SCOLON", "TK_COMMA", "TK_PERIOD", "TK_RARROW", "TK_QMARK", "TK_HASH", + "TK_COLON", "TK_SCOLON", "TK_COMMA", "TK_PERIOD", "TK_RARROW", "TK_QMARK", "TK_HASH", "TK_OP_ADD", "TK_OP_SUB", "TK_OP_MUL", "TK_OP_DIV", "TK_OP_MOD", "TK_EQ", "TK_NE", "TK_LT", "TK_LE", "TK_GT", "TK_GE", - "TK_LO_NOT", "TK_LO_AND", "TK_LO_OR", - "TK_BW_NOT", "TK_BW_AND", "TK_BW_OR", "TK_BW_XOR", "TK_BW_SHL", "TK_BW_SHR", + "TK_EMARK", "TK_LO_AND", "TK_LO_OR", + "TK_BW_NOT", "TK_BW_AND", "TK_BW_OR", "TK_BW_XOR", "TK_BW_SHL", "TK_BW_SHR", "TK_ASSIGN", "TK_AS_ADD", "TK_AS_SUB", "TK_AS_MUL", "TK_AS_DIV", "TK_AS_MOD", - "TK_AS_NOT", "TK_AS_AND", "TK_AS_OR", "TK_AS_XOR", "TK_AS_SHL", "TK_AS_SHR", + "TK_AS_NOT", "TK_AS_AND", "TK_AS_OR", "TK_AS_XOR", "TK_AS_SHL", "TK_AS_SHR", }; #define is_space(c) (c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v') @@ -166,19 +166,19 @@ tok lex_next(lex *l) { } break; case '=': switch (D) { default: { T.k = TK_ASSIGN; P += 1; CL += 1; } break; - case '=': { T.k = TK_EQ; P += 2; CL += 2; } break; + case '=': { T.k = TK_EQ; P += 2; CL += 2; } break; } break; case '<': switch (D) { - default: { T.k = TK_LT; P += 1; CL += 1; } break; - case '=': { T.k = TK_LE; P += 2; CL += 2; } break; + default: { T.k = TK_LT; P += 1; CL += 1; } break; + case '=': { T.k = TK_LE; P += 2; CL += 2; } break; } break; case '>': switch (D) { - default: { T.k = TK_GT; P += 1; CL += 1; } break; - case '=': { T.k = TK_GE; P += 2; CL += 2; } break; + default: { T.k = TK_GT; P += 1; CL += 1; } break; + case '=': { T.k = TK_GE; P += 2; CL += 2; } break; } break; case '!': switch (D) { - default: { T.k = TK_LO_NOT; P += 1; CL += 1; } break; - case '=': { T.k = TK_NE; P += 2; CL += 2; } break; + default: { T.k = TK_EMARK; P += 1; CL += 1; } break; + case '=': { T.k = TK_NE; P += 2; CL += 2; } break; } break; case '&': switch (D) { default: { T.k = TK_BW_AND; P += 1; CL += 1; } break; diff --git a/src/lex.h b/src/lex.h index 033b259..83abccd 100644 --- a/src/lex.h +++ b/src/lex.h @@ -16,16 +16,16 @@ typedef enum { TK_IF, TK_ELSE, TK_FOR, TK_BREAK, TK_CONTINUE, TK_LPAREN, TK_RPAREN, TK_LBRACK, TK_RBRACK, TK_LBRACE, TK_RBRACE, - TK_COLON, TK_SCOLON, TK_COMMA, TK_PERIOD, TK_RARROW, TK_QMARK, TK_HASH, + TK_COLON, TK_SCOLON, TK_COMMA, TK_PERIOD, TK_RARROW, TK_QMARK, TK_HASH, TK_OP_ADD, TK_OP_SUB, TK_OP_MUL, TK_OP_DIV, TK_OP_MOD, TK_EQ, TK_NE, TK_LT, TK_LE, TK_GT, TK_GE, - TK_LO_NOT, TK_LO_AND, TK_LO_OR, - TK_BW_NOT, TK_BW_AND, TK_BW_OR, TK_BW_XOR, TK_BW_SHL, TK_BW_SHR, + TK_EMARK, TK_LO_AND, TK_LO_OR, + TK_BW_NOT, TK_BW_AND, TK_BW_OR, TK_BW_XOR, TK_BW_SHL, TK_BW_SHR, TK_ASSIGN, TK_AS_ADD, TK_AS_SUB, TK_AS_MUL, TK_AS_DIV, TK_AS_MOD, - TK_AS_NOT, TK_AS_AND, TK_AS_OR, TK_AS_XOR, TK_AS_SHL, TK_AS_SHR, + TK_AS_NOT, TK_AS_AND, TK_AS_OR, TK_AS_XOR, TK_AS_SHL, TK_AS_SHR, } tok_k; /* diff --git a/src/llvm.c b/src/llvm.c index 93cdc30..bafe6b3 100644 --- a/src/llvm.c +++ b/src/llvm.c @@ -207,7 +207,30 @@ static LLVMValueRef llvm_stmt_if(ast *a, syt *st) { /* Generate IR for a for statement. */ static LLVMValueRef llvm_stmt_for(ast *a, syt *st) { - return NULL; /* TODO */ + assert(A.k == AK_FOR); + assert(CL == 2 || CL == 4); + + if (CL == 4) { llvm_stmt(C[0], &A.st); } + + ast *p = find_proc(a); assert(p->k == AK_DECL); + + LLVMBasicBlockRef b1 = LLVMAppendBasicBlock(p->llvm_v, ""); + LLVMBasicBlockRef b2 = LLVMAppendBasicBlock(p->llvm_v, ""); + LLVMBasicBlockRef b3 = LLVMAppendBasicBlock(p->llvm_v, ""); + + LLVMBuildBr(llvm_builder, b1); + + LLVMPositionBuilderAtEnd(llvm_builder, b1); + LLVMBuildCondBr(llvm_builder, llvm_expr(CL == 4 ? C[1] : C[0], &A.st, true), b2, b3); + + LLVMPositionBuilderAtEnd(llvm_builder, b2); + llvm_stmt(CL == 4 ? C[3] : C[1], &A.st); + if (CL == 4) { llvm_stmt(C[2], &A.st); } + LLVMBuildBr(llvm_builder, b1); + + LLVMPositionBuilderAtEnd(llvm_builder, b3); + + return NULL; } #define BuildICmp(b, op, lhs, rhs, st) LLVMBuildICmp(b, op, llvm_expr(lhs, st, true), llvm_expr(rhs, st, true), "") diff --git a/src/parse.c b/src/parse.c index ce6ee58..dcf206e 100644 --- a/src/parse.c +++ b/src/parse.c @@ -268,15 +268,31 @@ static ast *parse_stmt_for(lex *l, syt *st) { assert(T.k == TK_FOR); ast *a = ast_init(AK_FOR, T.ln, T.cl); - lex_next(l); lex_kind(l, TK_LPAREN); + lex_next(l); lex_kind(l, TK_LPAREN); a->st.pt = st; - /* TODO Parse one to three expressions and a closing parenthesis */ - ast_push(a, parse_stmt_expr(l, st)); - ast_push(a, parse_stmt_expr(l, st)); - ast_push(a, parse_expr(l, st, 0)); lex_kind(l, TK_RPAREN); + if (T.k != TK_RPAREN) { + register ast *c1 = parse_stmt_init(l, &a->st); + + if (T.k == TK_SCOLON) { + lex_next(l); ast_push(a, c1); + ast_push(a, parse_expr(l, &a->st, 0)); + + lex_kind(l, TK_SCOLON); + ast_push(a, parse_expr(l, &a->st, 0)); + } + else { + if (c1->k == AK_DECL) { note(l->n, c1->ln, c1->cl, 0, "Expected an expression"); } + ast_push(a, c1); + } + } + else { note(l->n, T.ln, T.cl, 0, "Expected an expression"); } + + lex_kind(l, TK_RPAREN); /* Parse the for statement body */ - ast_push(a, parse_stmt(l, st)); return a; + ast_push(a, parse_stmt(l, &a->st)); + + return a; } /* Parse an expression. */ @@ -300,7 +316,7 @@ static ast *parse_expr(lex *l, syt *st, s32 o) { case TK_LPAREN: { lex_next(l); left = parse_expr(l, st, 0); lex_kind(l, TK_RPAREN); } break; case TK_OP_ADD: { left = ast_init(AK_OP_POS, T.ln, T.cl); } goto prefix; case TK_OP_SUB: { left = ast_init(AK_OP_NEG, T.ln, T.cl); } goto prefix; - case TK_LO_NOT: { left = ast_init(AK_LO_NOT, T.ln, T.cl); } goto prefix; + case TK_EMARK: { left = ast_init(AK_LO_NOT, T.ln, T.cl); } goto prefix; case TK_BW_NOT: { left = ast_init(AK_BW_NOT, T.ln, T.cl); } goto prefix; case TK_BW_AND: { left = ast_init(AK_OP_ADO, T.ln, T.cl); } goto prefix; case TK_OP_MUL: { left = ast_init(AK_OP_DRF, T.ln, T.cl); } goto prefix;