about summary refs log tree commit diff
path: root/pkg
diff options
context:
space:
mode:
authorMel <einebeere@gmail.com>2022-07-05 22:29:55 +0200
committerMel <einebeere@gmail.com>2022-07-05 22:29:55 +0200
commit7f8cf65c6e64d28a76ac52849b707dcbcbdcd923 (patch)
tree1337400205a52b51b97a483d48f34a9d7df3a6f7 /pkg
parentb09fce67753e199d4ae0fe6138f909b5e3413208 (diff)
downloadjinx-7f8cf65c6e64d28a76ac52849b707dcbcbdcd923.tar.zst
jinx-7f8cf65c6e64d28a76ac52849b707dcbcbdcd923.zip
Compile ForCond statements
Diffstat (limited to 'pkg')
-rw-r--r--pkg/lang/compiler/compiler.go48
-rw-r--r--pkg/lang/compiler/compiler_test.go31
2 files changed, 77 insertions, 2 deletions
diff --git a/pkg/lang/compiler/compiler.go b/pkg/lang/compiler/compiler.go
index cad4328..0356411 100644
--- a/pkg/lang/compiler/compiler.go
+++ b/pkg/lang/compiler/compiler.go
@@ -43,8 +43,11 @@ func (comp *Compiler) compileStmt(t *code.Builder, stmt ast.Stmt) error {
 		decl := stmt.Value.(ast.StmtVarDecl)
 		err = comp.compileVarDeclStmt(t, decl)
 	case ast.StmtKindIf:
-		ifstmt := stmt.Value.(ast.StmtIf)
-		err = comp.compileIfStmt(t, ifstmt)
+		ifStmt := stmt.Value.(ast.StmtIf)
+		err = comp.compileIfStmt(t, ifStmt)
+	case ast.StmtKindForCond:
+		forCondStmt := stmt.Value.(ast.StmtForCond)
+		err = comp.compileForCondStmt(t, forCondStmt)
 	case ast.StmtKindExpr:
 		expr := stmt.Value.(ast.StmtExpr).Value
 		err = comp.compileExpr(t, expr)
@@ -137,6 +140,47 @@ func (comp *Compiler) compileIfStmt(t *code.Builder, ifStmt ast.StmtIf) error {
 	return nil
 }
 
+func (comp *Compiler) compileForCondStmt(t *code.Builder, forCondStmt ast.StmtForCond) error {
+	// Parts:
+	// 1. Condition check: Decides whether the loop should run
+	// 2. Condition jump: Jumps to the end of the for if condition was false
+	// 3. Do block: Does something
+	// 4. Repeat jump: Jumps back to start
+
+	// Do block
+	doTarget := code.NewBuilder()
+	if err := comp.compileBlockNode(&doTarget, forCondStmt.Do); err != nil {
+		return err
+	}
+
+	jmpLength := 9
+
+	conditionTarget := code.NewBuilder()
+	if !forCondStmt.Cond.IsEmpty() {
+		// Condition check
+		if err := comp.compileExpr(&conditionTarget, forCondStmt.Cond); err != nil {
+			return err
+		}
+
+		endOfFor := conditionTarget.Len() + doTarget.Len() + jmpLength*2
+
+		// Condition jump
+		conditionTarget.AppendOp(code.OpJf)
+		conditionTarget.AppendReferenceToPc(int64(endOfFor))
+	}
+
+	subUnit := conditionTarget
+	subUnit.AppendBuilder(doTarget)
+
+	// Repeat jump
+	subUnit.AppendOp(code.OpJmp)
+	subUnit.AppendReferenceToPc(int64(0)) // Start of the for
+
+	t.AppendBuilder(subUnit)
+
+	return nil
+}
+
 func (comp *Compiler) compileExpr(t *code.Builder, expr ast.Expr) error {
 	switch expr.Kind {
 	case ast.ExprKindBinary:
diff --git a/pkg/lang/compiler/compiler_test.go b/pkg/lang/compiler/compiler_test.go
index 88413c7..1a759f8 100644
--- a/pkg/lang/compiler/compiler_test.go
+++ b/pkg/lang/compiler/compiler_test.go
@@ -234,6 +234,37 @@ func TestNestedIfs(t *testing.T) {
 	mustCompileTo(t, src, expected)
 }
 
+func TestForCond(t *testing.T) {
+	src := `
+	for true {
+		for false {
+			1
+		}
+	}
+	`
+
+	expected := `
+	@start:
+	push_true
+	jf @end
+
+	@inner_start:
+	push_false
+	jf @inner_end
+
+	push_int 1
+	jmp @inner_start
+
+	@inner_end:
+	jmp @start
+
+	@end
+	halt
+	`
+
+	mustCompileTo(t, src, expected)
+}
+
 func mustCompileTo(t *testing.T, src, expected string) {
 	scanner := scanner.New(strings.NewReader(src))
 	tokens, err := scanner.Scan()