about summary refs log tree commit diff
path: root/pkg/lang/compiler/compiler.go
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/lang/compiler/compiler.go')
-rw-r--r--pkg/lang/compiler/compiler.go231
1 files changed, 125 insertions, 106 deletions
diff --git a/pkg/lang/compiler/compiler.go b/pkg/lang/compiler/compiler.go
index 116db1c..a31ade7 100644
--- a/pkg/lang/compiler/compiler.go
+++ b/pkg/lang/compiler/compiler.go
@@ -31,7 +31,7 @@ func (comp *Compiler) Compile() (code.Code, error) {
 
 	target.AppendOp(code.OpHalt)
 
-	return target.Build(), nil
+	return target.Build()
 }
 
 func (comp *Compiler) compileStmt(t *code.Builder, stmt ast.Stmt) error {
@@ -39,6 +39,12 @@ func (comp *Compiler) compileStmt(t *code.Builder, stmt ast.Stmt) error {
 	switch stmt.Kind {
 	case ast.StmtKindEmpty:
 		// Do nothing.
+	case ast.StmtKindUse:
+		panic("use statements not implemented")
+	case ast.StmtKindFnDecl:
+		panic("function declaration statements not implemented")
+	case ast.StmtKindObjectDecl:
+		panic("object declaration statements not implemented")
 	case ast.StmtKindVarDecl:
 		decl := stmt.Value.(ast.StmtVarDecl)
 		err = comp.compileVarDeclStmt(t, decl)
@@ -51,11 +57,21 @@ func (comp *Compiler) compileStmt(t *code.Builder, stmt ast.Stmt) error {
 	case ast.StmtKindForIn:
 		forCondIn := stmt.Value.(ast.StmtForIn)
 		err = comp.compileForInStmt(t, forCondIn)
+	case ast.StmtKindTry:
+		panic("try statements not implemented")
+	case ast.StmtKindReturn:
+		panic("return statements not implemented")
+	case ast.StmtKindContinue:
+		panic("continue statements not implemented")
+	case ast.StmtKindBreak:
+		panic("break statements not implemented")
+	case ast.StmtKindThrow:
+		panic("throw statements not implemented")
 	case ast.StmtKindExpr:
 		expr := stmt.Value.(ast.StmtExpr).Value
 		err = comp.compileExpr(t, expr)
 	default:
-		panic(fmt.Errorf("statement of kind %v not implemented", stmt.Kind))
+		panic(fmt.Errorf("unknown statement kind: %d", stmt.Kind))
 	}
 
 	return err
@@ -90,54 +106,56 @@ func (comp *Compiler) compileIfStmt(t *code.Builder, ifStmt ast.StmtIf) error {
 	//				   preventing other CondNodes from running. This is missing from the last CondNode.
 	//    Example: `jmp @end`
 
-	subUnits := make([]code.Builder, 0, len(ifStmt.Conds))
+	// First we create all the markers we'll need for the if statement
+	parentMarker := comp.scopes.CreateAnonymousFunctionSubUnit()
 
-	totalLength := 0
+	endMarker := parentMarker.SubMarker("end")
+
+	condMarkers := make([]code.Marker, 0, len(ifStmt.Conds)-1) // We don't need a marker for the first CondNode.
+	for i := 0; i < len(ifStmt.Conds)-1; i++ {
+		condMarker := parentMarker.SubMarker("cond_%d", i+1)
+		condMarkers = append(condMarkers, condMarker)
+	}
 
 	for i, cond := range ifStmt.Conds {
-		// Then block
-		thenTarget := code.NewBuilder()
-		if err := comp.compileBlockNode(&thenTarget, cond.Then); err != nil {
-			return err
-		}
+		isFirst := i == 0
+		isLast := i == len(ifStmt.Conds)-1
 
-		totalLength += thenTarget.Len()
-		if i != len(ifStmt.Conds)-1 {
-			totalLength += lengthOfAJumpInstruction
+		// If we aren't in the first CondNode, the node before it needs a marker to here.
+		if !isFirst {
+			marker := condMarkers[i-1]
+			t.PutMarker(marker)
 		}
 
-		// Condition check
-		conditionTarget := code.NewBuilder()
 		if !cond.Cond.IsEmpty() {
-			if err := comp.compileExpr(&conditionTarget, cond.Cond); err != nil {
+			// Condition check
+			if err := comp.compileExpr(t, cond.Cond); err != nil {
 				return err
 			}
 
-			totalLength += conditionTarget.Len() + lengthOfAJumpInstruction // condjmp
-
-			conditionTarget.AppendOp(code.OpJf)
 			// Condition jump
-			conditionTarget.AppendReferenceToPc(int64(totalLength))
+			t.AppendOp(code.OpJf)
+			if isLast {
+				t.AppendMarkerReference(endMarker)
+			} else {
+				nextCondMarker := condMarkers[i]
+				t.AppendMarkerReference(nextCondMarker)
+			}
 		}
 
-		subUnit := conditionTarget
-		subUnit.AppendBuilder(thenTarget)
-		subUnits = append(subUnits, subUnit)
-	}
-
-	result := code.NewBuilder()
-
-	// Then jumps
-	for i, subUnit := range subUnits {
-		if i != len(ifStmt.Conds)-1 {
-			subUnit.AppendOp(code.OpJmp)
-			subUnit.AppendReferenceToPc(int64(totalLength))
+		// Then block
+		if err := comp.compileBlockNode(t, cond.Then); err != nil {
+			return err
 		}
 
-		result.AppendBuilderWithoutAdjustingReferences(subUnit)
+		// Then jump
+		if !isLast {
+			t.AppendOp(code.OpJmp)
+			t.AppendMarkerReference(endMarker)
+		}
 	}
 
-	t.AppendBuilder(result)
+	t.PutMarker(endMarker)
 
 	return nil
 }
@@ -149,34 +167,34 @@ func (comp *Compiler) compileForCondStmt(t *code.Builder, forCondStmt ast.StmtFo
 	// 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
-	}
+	parentMarker := comp.scopes.CreateAnonymousFunctionSubUnit()
+
+	startMarker := parentMarker.SubMarker("start")
+	endMarker := parentMarker.SubMarker("end")
+
+	t.PutMarker(startMarker)
 
-	conditionTarget := code.NewBuilder()
 	if !forCondStmt.Cond.IsEmpty() {
 		// Condition check
-		if err := comp.compileExpr(&conditionTarget, forCondStmt.Cond); err != nil {
+		if err := comp.compileExpr(t, forCondStmt.Cond); err != nil {
 			return err
 		}
 
-		endOfFor := conditionTarget.Len() + doTarget.Len() + lengthOfAJumpInstruction*2
-
-		// Condition jump
-		conditionTarget.AppendOp(code.OpJf)
-		conditionTarget.AppendReferenceToPc(int64(endOfFor))
+		// Condition check
+		t.AppendOp(code.OpJf)
+		t.AppendMarkerReference(endMarker)
 	}
 
-	subUnit := conditionTarget
-	subUnit.AppendBuilder(doTarget)
+	// Do block
+	if err := comp.compileBlockNode(t, forCondStmt.Do); err != nil {
+		return err
+	}
 
 	// Repeat jump
-	subUnit.AppendOp(code.OpJmp)
-	subUnit.AppendReferenceToPc(int64(0)) // Start of the for
+	t.AppendOp(code.OpJmp)
+	t.AppendMarkerReference(startMarker)
 
-	t.AppendBuilder(subUnit)
+	t.PutMarker(endMarker)
 
 	return nil
 }
@@ -216,88 +234,81 @@ func (comp *Compiler) compileForInStmt(t *code.Builder, forInStmt ast.StmtForIn)
 	// @end:
 	// halt
 
+	parentMarker := comp.scopes.CreateAnonymousFunctionSubUnit()
+
+	checkMarker := parentMarker.SubUnit("check")
+	endMarker := parentMarker.SubUnit("end")
+
 	// Preparation
-	preparationTarget := code.NewBuilder()
-	if err := comp.compileExpr(&preparationTarget, forInStmt.Collection); err != nil {
+	if err := comp.compileExpr(t, forInStmt.Collection); err != nil {
 		return err
 	}
 	collectionLocal := comp.scopes.DeclareAnonymous()
 
-	preparationTarget.AppendOp(code.OpPushInt)
-	preparationTarget.AppendInt(0)
+	t.AppendOp(code.OpPushInt)
+	t.AppendInt(0)
 	iLocal := comp.scopes.DeclareAnonymous()
 
-	preparationTarget.AppendOp(code.OpPushNull)
+	t.AppendOp(code.OpPushNull)
 	nameLocal, ok := comp.scopes.Declare(forInStmt.Name.Value)
 	if !ok {
 		return fmt.Errorf("variable %s already declared", forInStmt.Name.Value)
 	}
 
 	// Condition check
-	conditionTarget := code.NewBuilder()
+	t.PutMarker(checkMarker)
 
-	conditionTarget.AppendOp(code.OpGetLocal)
-	conditionTarget.AppendInt(int64(iLocal))
+	t.AppendOp(code.OpGetLocal)
+	t.AppendInt(int64(iLocal))
 
-	conditionTarget.AppendOp(code.OpGetLocal)
-	conditionTarget.AppendInt(int64(collectionLocal))
+	t.AppendOp(code.OpGetLocal)
+	t.AppendInt(int64(collectionLocal))
 
-	conditionTarget.AppendOp(code.OpGetMember)
-	conditionTarget.AppendString("length")
+	t.AppendOp(code.OpGetMember)
+	t.AppendString("length")
 
-	conditionTarget.AppendOp(code.OpCall)
-	conditionTarget.AppendInt(0)
+	t.AppendOp(code.OpCall)
+	t.AppendInt(0)
 
-	conditionTarget.AppendOp(code.OpLt)
+	t.AppendOp(code.OpLt)
 
-	// Do Preparation
-	doPreparationTarget := code.NewBuilder()
+	// Condition jump
+	t.AppendOp(code.OpJf)
+	t.AppendMarkerReference(endMarker)
 
-	doPreparationTarget.AppendOp(code.OpGetLocal)
-	doPreparationTarget.AppendInt(int64(collectionLocal))
+	// Do Preparation
+	t.AppendOp(code.OpGetLocal)
+	t.AppendInt(int64(collectionLocal))
 
-	doPreparationTarget.AppendOp(code.OpGetLocal)
-	doPreparationTarget.AppendInt(int64(iLocal))
+	t.AppendOp(code.OpGetLocal)
+	t.AppendInt(int64(iLocal))
 
-	doPreparationTarget.AppendOp(code.OpIndex)
+	t.AppendOp(code.OpIndex)
 
-	doPreparationTarget.AppendOp(code.OpSetLocal)
-	doPreparationTarget.AppendInt(int64(nameLocal))
+	t.AppendOp(code.OpSetLocal)
+	t.AppendInt(int64(nameLocal))
 
-	doPreparationTarget.AppendOp(code.OpGetLocal)
-	doPreparationTarget.AppendInt(int64(iLocal))
+	t.AppendOp(code.OpGetLocal)
+	t.AppendInt(int64(iLocal))
 
-	doPreparationTarget.AppendOp(code.OpPushInt)
-	doPreparationTarget.AppendInt(1)
+	t.AppendOp(code.OpPushInt)
+	t.AppendInt(1)
 
-	doPreparationTarget.AppendOp(code.OpAdd)
+	t.AppendOp(code.OpAdd)
 
-	doPreparationTarget.AppendOp(code.OpSetLocal)
-	doPreparationTarget.AppendInt(int64(iLocal))
+	t.AppendOp(code.OpSetLocal)
+	t.AppendInt(int64(iLocal))
 
 	// Do block
-	doTarget := code.NewBuilder()
-	if err := comp.compileBlockNode(&doTarget, forInStmt.Do); err != nil {
+	if err := comp.compileBlockNode(t, forInStmt.Do); err != nil {
 		return err
 	}
 
-	// Condition Jump
-
-	endOfFor := preparationTarget.Len() + conditionTarget.Len() + doPreparationTarget.Len() + doTarget.Len() + lengthOfAJumpInstruction*2
-
-	conditionTarget.AppendOp(code.OpJf)
-	conditionTarget.AppendReferenceToPc(int64(endOfFor))
-
-	subUnit := preparationTarget
-	subUnit.AppendBuilderWithoutAdjustingReferences(conditionTarget)
-	subUnit.AppendBuilder(doPreparationTarget)
-	subUnit.AppendBuilder(doTarget)
-
 	// Repeat jump
-	subUnit.AppendOp(code.OpJmp)
-	subUnit.AppendReferenceToPc(int64(preparationTarget.Len()))
+	t.AppendOp(code.OpJmp)
+	t.AppendMarkerReference(checkMarker)
 
-	t.AppendBuilder(subUnit)
+	t.PutMarker(endMarker)
 
 	return nil
 }
@@ -389,17 +400,23 @@ func (comp *Compiler) compileAssignExpr(t *code.Builder, expr ast.ExprBinary) er
 	}
 
 	name := expr.Left.Value.(ast.ExprIdent).Value.Value
-	symbol, ok := comp.scopes.Lookup(name)
+	symbolId, ok := comp.scopes.Lookup(name)
 	if !ok {
 		return fmt.Errorf("variable %s not declared", name)
 	}
 
+	if symbolId.symbolKind != SymbolKindVariable {
+		return fmt.Errorf("can't assign to a %v", symbolId.symbolKind)
+	}
+
+	symbol := comp.scopes.GetVariable(symbolId)
+
 	if err := comp.compileExpr(t, expr.Right); err != nil {
 		return err
 	}
 
 	t.AppendOp(code.OpSetLocal)
-	t.AppendInt(int64(symbol.localIndex))
+	t.AppendInt(int64(symbol.data.localIndex))
 
 	return nil
 }
@@ -478,14 +495,20 @@ func (comp *Compiler) compileArrayLitExpr(t *code.Builder, expr ast.ExprArrayLit
 }
 
 func (comp *Compiler) compileIdentExpr(t *code.Builder, expr ast.ExprIdent) error {
-	symbol, ok := comp.scopes.Lookup(expr.Value.Value)
+	symbolId, ok := comp.scopes.Lookup(expr.Value.Value)
 	if !ok {
 		return fmt.Errorf("undefined symbol %s", expr.Value.Value)
 	}
 
+	if symbolId.symbolKind != SymbolKindVariable {
+		return fmt.Errorf("%v values are not implemeted yet", symbolId.symbolKind)
+	}
+
+	symbol := comp.scopes.GetVariable(symbolId)
+
 	// TODO: Add boundries to check how the symbol should be fetched. (local, env, global, etc.)
 	t.AppendOp(code.OpGetLocal)
-	t.AppendInt(int64(symbol.localIndex))
+	t.AppendInt(int64(symbol.data.localIndex))
 
 	return nil
 }
@@ -531,7 +554,3 @@ func (comp *Compiler) compileBlockNode(t *code.Builder, block ast.BlockNode) err
 
 	return nil
 }
-
-const (
-	lengthOfAJumpInstruction = 9 // The length of a jump Op (jmp, jf, jt) and it's following 64-bit integer.
-)