G

G Programming Language
git clone http://git.omkov.net/G
Log | Tree | Refs | README | Download

AuthorJakob Wakeling <[email protected]>
Date2023-07-06 05:52:50
Commit2be2f4390b177036c5fa3b2ff11bf17e53003e97
Parent1aa55193278a3067cd2b55634c052bbc0ac3553a

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;