about summary refs log tree commit diff
path: root/pkg/lang/compiler/compiler.go
diff options
context:
space:
mode:
authorMel <einebeere@gmail.com>2022-08-11 01:25:47 +0000
committerMel <einebeere@gmail.com>2022-08-11 01:25:47 +0000
commit86f31acf6789be116dcc54ed85b069a37c0f7aa8 (patch)
treebc7afd6a8c340825996d29c6cfd392ae42b4fbd5 /pkg/lang/compiler/compiler.go
parentc46b2bc7ce6df1f2c6c9494ef08015ec29992da5 (diff)
downloadjinx-86f31acf6789be116dcc54ed85b069a37c0f7aa8.tar.zst
jinx-86f31acf6789be116dcc54ed85b069a37c0f7aa8.zip
Actual modules and core
Diffstat (limited to 'pkg/lang/compiler/compiler.go')
-rw-r--r--pkg/lang/compiler/compiler.go143
1 files changed, 132 insertions, 11 deletions
diff --git a/pkg/lang/compiler/compiler.go b/pkg/lang/compiler/compiler.go
index 8010b16..d647aff 100644
--- a/pkg/lang/compiler/compiler.go
+++ b/pkg/lang/compiler/compiler.go
@@ -4,31 +4,43 @@ import (
 	"fmt"
 	"jinx/pkg/lang/ast"
 	"jinx/pkg/lang/compiler/scope"
+	"jinx/pkg/lang/modules"
 	"jinx/pkg/lang/vm/code"
 )
 
 type Compiler struct {
-	ast ast.Program
+	name   string
+	author string
+	ast    ast.Program
 
 	funcs []*code.Builder
 
+	globals []string
+	deps    map[modules.ModuleRef]struct{}
+
 	scopes scope.ScopeChain
 }
 
-func New(ast ast.Program) *Compiler {
+func New(name, author string, ast ast.Program) *Compiler {
 	return &Compiler{
-		ast:    ast,
-		funcs:  make([]*code.Builder, 0),
+		name:    name,
+		author:  author,
+		ast:     ast,
+		funcs:   make([]*code.Builder, 0),
+		globals: []string{},
+		deps: map[modules.ModuleRef]struct{}{
+			modules.CoreModuleRef: {}, // All modules depend on core
+		},
 		scopes: scope.NewScopeChain(),
 	}
 }
 
-func (comp *Compiler) Compile() (code.Code, error) {
+func (comp *Compiler) Compile() (modules.Module, error) {
 	target := code.NewBuilder()
 
 	for _, stmt := range comp.ast.Stmts {
 		if err := comp.compileStmt(&target, stmt); err != nil {
-			return code.Code{}, err
+			return modules.Module{}, err
 		}
 	}
 
@@ -38,7 +50,23 @@ func (comp *Compiler) Compile() (code.Code, error) {
 		target.AppendBuilder(*function)
 	}
 
-	return target.Build()
+	bc, err := target.Build()
+	if err != nil {
+		return modules.Module{}, err
+	}
+
+	deps := make([]modules.ModuleRef, 0, len(comp.deps))
+	for dep := range comp.deps {
+		deps = append(deps, dep)
+	}
+
+	return modules.NewModule(
+		comp.author,
+		comp.name,
+		&bc,
+		deps,
+		comp.globals,
+	), nil
 }
 
 func (comp *Compiler) compileStmt(t *code.Builder, stmt ast.Stmt) error {
@@ -47,7 +75,11 @@ func (comp *Compiler) compileStmt(t *code.Builder, stmt ast.Stmt) error {
 	case ast.StmtKindEmpty:
 		// Do nothing.
 	case ast.StmtKindUse:
-		panic("use statements not implemented")
+		useStmt := stmt.Value.(ast.StmtUse)
+		err = comp.compileUseStmt(t, useStmt)
+	case ast.StmtKindGlobal:
+		globalStmt := stmt.Value.(ast.StmtGlobal)
+		err = comp.compileGlobalStmt(t, globalStmt)
 	case ast.StmtKindFnDecl:
 		fnDeclStmt := stmt.Value.(ast.StmtFnDecl)
 		err = comp.compileFnDeclStmt(t, fnDeclStmt)
@@ -89,6 +121,57 @@ func (comp *Compiler) compileStmt(t *code.Builder, stmt ast.Stmt) error {
 	return err
 }
 
+func (comp *Compiler) compileUseStmt(t *code.Builder, stmt ast.StmtUse) error {
+	if len(stmt.Globals) == 0 {
+		// TODO: I don't want to have module objects in the VM...
+		// Maybe compileMemberExpr could check whether Obj is a reference to a module and compile the whole thing as a global reference?
+		panic("importing entire module not implemented")
+	}
+
+	// TODO: Try to resolve the module during compilation, so we don't have to fail at runtime.
+
+	for _, global := range stmt.Globals {
+		if ok := comp.scopes.DeclareGlobal(global.Value, stmt.Module.Value, stmt.Author.Value); !ok {
+			return fmt.Errorf("global %s already declared", global.Value)
+		}
+	}
+
+	comp.deps[modules.NewRef(stmt.Author.Value, stmt.Module.Value)] = struct{}{}
+
+	return nil
+}
+
+func (comp *Compiler) compileGlobalStmt(t *code.Builder, stmt ast.StmtGlobal) error {
+	if !comp.scopes.IsRootScope() {
+		return fmt.Errorf("global variables can only be declared at the root scope")
+	}
+
+	symbolId, symbolExists := comp.scopes.Lookup(stmt.Name.Value)
+	if !symbolExists {
+		return fmt.Errorf("variable %s not declared", stmt.Name.Value)
+	}
+
+	if symbolId.SymbolKind() != scope.SymbolKindVariable {
+		return fmt.Errorf("%s is not a variable", stmt.Name.Value)
+	}
+
+	if symbolId.ScopeID() != scope.ScopeID(0) {
+		return fmt.Errorf("%s is not in root scope", stmt.Name.Value)
+	}
+
+	symbol := comp.scopes.GetVariable(symbolId)
+
+	t.AppendOp(code.OpGetLocal)
+	t.AppendInt(int64(symbol.Data().LocalIndex()))
+
+	t.AppendOp(code.OpAddGlobal)
+	t.AppendString(stmt.Name.Value)
+
+	comp.globals = append(comp.globals, stmt.Name.Value)
+
+	return nil
+}
+
 func (comp *Compiler) compileFnDeclStmt(t *code.Builder, fnDeclStmt ast.StmtFnDecl) error {
 	_, ok := comp.scopes.Declare(fnDeclStmt.Name.Value)
 	if !ok {
@@ -198,7 +281,10 @@ func (comp *Compiler) compileTypeDeclStmt(t *code.Builder, typeDeclStmt ast.Stmt
 	}
 
 	if !constructorDeclared {
-		return fmt.Errorf("type %s must have a constructor", typeDeclStmt.Name.Value)
+		// The core module is allowed to have no constructor
+		if !comp.areWeCompilingCore() {
+			return fmt.Errorf("type %s must have a constructor", typeDeclStmt.Name.Value)
+		}
 	}
 
 	// Add methods to the type
@@ -228,6 +314,9 @@ func (comp *Compiler) compileTypeDeclStmt(t *code.Builder, typeDeclStmt ast.Stmt
 
 		t.AppendOp(code.OpCall)
 		t.AppendInt(int64(2))
+
+		t.AppendOp(code.OpDrop)
+		t.AppendInt(int64(1))
 	}
 
 	return nil
@@ -653,8 +742,8 @@ func (comp *Compiler) compileAssignExpr(t *code.Builder, expr ast.ExprBinary) er
 
 			t.AppendOp(code.OpSetEnv)
 			t.AppendInt(int64(symbol.Data().IndexInEnv()))
-		default:
-			panic("unknown symbol kind")
+		case scope.SymbolKindGlobal:
+			return fmt.Errorf("cannot assign to global variable %s", name)
 		}
 	case ast.ExprKindMember:
 		memberExpr := expr.Left.Value.(ast.ExprMember)
@@ -695,6 +784,13 @@ func (comp *Compiler) compileUnaryExpr(t *code.Builder, expr ast.ExprUnary) erro
 }
 
 func (comp *Compiler) compileCallExpr(t *code.Builder, expr ast.ExprCall) error {
+	// Check if call is a native call
+	if expr.Callee.Kind == ast.ExprKindIdent &&
+		expr.Callee.Value.(ast.ExprIdent).Value.Value == "native" &&
+		expr.Args[0].Kind == ast.ExprKindStringLit {
+		return comp.compileNativeCallExpr(t, expr.Args[0].Value.(ast.ExprStringLit).Value, expr.Args[1:])
+	}
+
 	if err := comp.compileExpr(t, expr.Callee); err != nil {
 		return err
 	}
@@ -711,6 +807,22 @@ func (comp *Compiler) compileCallExpr(t *code.Builder, expr ast.ExprCall) error
 	return nil
 }
 
+func (comp *Compiler) compileNativeCallExpr(t *code.Builder, nativeID string, args []ast.Expr) error {
+	t.AppendOp(code.OpGetGlobal)
+	t.AppendString(fmt.Sprintf("%s#native", nativeID))
+
+	for i := 0; i < len(args); i++ {
+		if err := comp.compileExpr(t, args[i]); err != nil {
+			return err
+		}
+	}
+
+	t.AppendOp(code.OpCall)
+	t.AppendInt(int64(len(args)))
+
+	return nil
+}
+
 func (comp *Compiler) compileSubscriptionExpr(t *code.Builder, expr ast.ExprSubscription) error {
 	if err := comp.compileExpr(t, expr.Obj); err != nil {
 		return err
@@ -781,6 +893,11 @@ func (comp *Compiler) compileIdentExpr(t *code.Builder, expr ast.ExprIdent) erro
 
 		t.AppendOp(code.OpGetEnv)
 		t.AppendInt(int64(symbol.Data().IndexInEnv()))
+	case scope.SymbolKindGlobal:
+		symbol := comp.scopes.GetGlobal(symbolId)
+
+		t.AppendOp(code.OpGetGlobal)
+		t.AppendString(symbol.Data().ID())
 	default:
 		panic(fmt.Errorf("unknown symbol kind: %v", symbolId.SymbolKind()))
 	}
@@ -890,3 +1007,7 @@ func (comp *Compiler) exitScopeAndCleanStack(t *code.Builder) {
 		t.AppendInt(int64(stackSpace))
 	}
 }
+
+func (comp *Compiler) areWeCompilingCore() bool {
+	return modules.NewRef(comp.author, comp.name) == modules.CoreModuleRef
+}