aboutsummaryrefslogtreecommitdiff
path: root/pkg/lang
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/lang')
-rw-r--r--pkg/lang/compiler/compiler.go143
-rw-r--r--pkg/lang/compiler/compiler_test.go27
-rw-r--r--pkg/lang/compiler/scope/scope_chain.go37
-rw-r--r--pkg/lang/compiler/scope/scopes.go2
-rw-r--r--pkg/lang/compiler/scope/symbol.go14
-rw-r--r--pkg/lang/modules/core/compiled.go22
-rw-r--r--pkg/lang/modules/core/core.lang78
-rw-r--r--pkg/lang/modules/core/natives.go166
-rw-r--r--pkg/lang/modules/module.go104
-rw-r--r--pkg/lang/modules/natives/natives.go39
-rw-r--r--pkg/lang/vm/core.go146
-rw-r--r--pkg/lang/vm/errors.go11
-rw-r--r--pkg/lang/vm/exe.go62
-rw-r--r--pkg/lang/vm/exec.go53
-rw-r--r--pkg/lang/vm/executor/executor.go17
-rw-r--r--pkg/lang/vm/executor/nativefunc.go5
-rw-r--r--pkg/lang/vm/setup.go58
-rw-r--r--pkg/lang/vm/utils.go13
-rw-r--r--pkg/lang/vm/value/core_ptrs.go96
-rw-r--r--pkg/lang/vm/value/data.go22
-rw-r--r--pkg/lang/vm/value/value.go18
-rw-r--r--pkg/lang/vm/vm.go43
-rw-r--r--pkg/lang/vm/vm_test.go91
23 files changed, 994 insertions, 273 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
+}
diff --git a/pkg/lang/compiler/compiler_test.go b/pkg/lang/compiler/compiler_test.go
index 04bf425..725620c 100644
--- a/pkg/lang/compiler/compiler_test.go
+++ b/pkg/lang/compiler/compiler_test.go
@@ -552,6 +552,7 @@ func TestType(t *testing.T) {
set_arg_count 2
call 2
+ drop 1
get_local 0
get_member "$add_method"
@@ -560,6 +561,7 @@ func TestType(t *testing.T) {
push_function @Cat:meow
call 2
+ drop 1
get_local 0
push_string "Kitty"
@@ -601,6 +603,25 @@ func TestType(t *testing.T) {
mustCompileTo(t, src, expected)
}
+func TestGlobals(t *testing.T) {
+ src := `
+ use mul from hello by mel
+ use pi from math
+
+ var result = mul(pi, 2)
+ `
+
+ expected := `
+ get_global "mel:hello:mul"
+ get_global ":math:pi"
+ push_int 2
+ call 2
+ halt
+ `
+
+ mustCompileTo(t, src, expected)
+}
+
func mustCompileTo(t *testing.T, src, expected string) {
scanner := scanner.New(strings.NewReader(src))
tokens, err := scanner.Scan()
@@ -610,7 +631,7 @@ func mustCompileTo(t *testing.T, src, expected string) {
program, err := parser.Parse()
require.NoError(t, err)
- langCompiler := compiler.New(program)
+ langCompiler := compiler.New("test_program", "test", program)
testResult, err := langCompiler.Compile()
require.NoError(t, err)
@@ -618,9 +639,9 @@ func mustCompileTo(t *testing.T, src, expected string) {
expectedResult, err := textCompiler.Compile()
require.NoError(t, err)
- if !assert.Equal(t, expectedResult.Code(), testResult.Code()) {
+ if !assert.Equal(t, expectedResult.Code(), testResult.Code().Code()) {
d1 := text.NewDecompiler(expectedResult)
- d2 := text.NewDecompiler(testResult)
+ d2 := text.NewDecompiler(*testResult.Code())
require.Equal(t, d1.Decompile(), d2.Decompile())
}
}
diff --git a/pkg/lang/compiler/scope/scope_chain.go b/pkg/lang/compiler/scope/scope_chain.go
index f386017..0c8f5cf 100644
--- a/pkg/lang/compiler/scope/scope_chain.go
+++ b/pkg/lang/compiler/scope/scope_chain.go
@@ -160,6 +160,35 @@ func (sc *ScopeChain) DeclareTemporary() int {
return len(sc.Current().variableSymbols) // :)
}
+func (sc *ScopeChain) DeclareGlobal(name, module, author string) bool {
+ if _, ok := sc.nameToSymbol[name]; ok {
+ return false
+ }
+
+ current := sc.Current()
+ indexInScope := len(current.globalSymbols)
+
+ symbolID := SymbolID{
+ symbolKind: SymbolKindGlobal,
+ scopeID: sc.CurrentScopeID(),
+ indexInScope: indexInScope,
+ }
+
+ globalID := fmt.Sprintf("%s:%s:%s", author, module, name)
+
+ // Declare the symbol in the current scope.
+ current.globalSymbols = append(current.globalSymbols, Symbol[SymbolGlobal]{
+ name: name,
+ data: SymbolGlobal{
+ id: globalID,
+ },
+ })
+
+ sc.nameToSymbol[name] = symbolID
+
+ return true
+}
+
func (sc *ScopeChain) CreateAnonymousFunctionSubUnit() code.Marker {
fnScope := sc.CurrentFunction()
@@ -242,3 +271,11 @@ func (sc *ScopeChain) GetEnv(id SymbolID) Symbol[SymbolEnv] {
},
}
}
+
+func (sc *ScopeChain) GetGlobal(id SymbolID) Symbol[SymbolGlobal] {
+ if id.symbolKind != SymbolKindGlobal {
+ panic("incorrect symbol id kind given")
+ }
+
+ return sc.symbolScopes[id.scopeID].globalSymbols[id.indexInScope]
+}
diff --git a/pkg/lang/compiler/scope/scopes.go b/pkg/lang/compiler/scope/scopes.go
index 2a9453a..e1a7a9f 100644
--- a/pkg/lang/compiler/scope/scopes.go
+++ b/pkg/lang/compiler/scope/scopes.go
@@ -14,11 +14,13 @@ const (
type SymbolScope struct {
variableSymbols []Symbol[SymbolVariable]
+ globalSymbols []Symbol[SymbolGlobal]
}
func NewSymbolScope() SymbolScope {
return SymbolScope{
variableSymbols: make([]Symbol[SymbolVariable], 0),
+ globalSymbols: make([]Symbol[SymbolGlobal], 0),
}
}
diff --git a/pkg/lang/compiler/scope/symbol.go b/pkg/lang/compiler/scope/symbol.go
index b87d5aa..8bfe60a 100644
--- a/pkg/lang/compiler/scope/symbol.go
+++ b/pkg/lang/compiler/scope/symbol.go
@@ -26,6 +26,8 @@ const (
// An env symbol is bound to a local on the stack, outside of the function's scope.
// Emitted at lookup time, so the SymbolScope has no array for them.
SymbolKindEnv SymbolKind = iota
+ // A global symbol is bound to a global from a dependency.
+ SymbolKindGlobal SymbolKind = iota
)
func (s SymbolKind) String() string {
@@ -34,6 +36,8 @@ func (s SymbolKind) String() string {
return "variable"
case SymbolKindEnv:
return "env"
+ case SymbolKindGlobal:
+ return "global"
default:
panic("unknown symbol kind")
}
@@ -49,7 +53,7 @@ func (s Symbol[D]) Data() D {
}
type SymbolData interface {
- SymbolVariable | SymbolEnv
+ SymbolVariable | SymbolEnv | SymbolGlobal
}
type SymbolVariable struct {
@@ -67,3 +71,11 @@ type SymbolEnv struct {
func (se SymbolEnv) IndexInEnv() int {
return se.indexInEnv
}
+
+type SymbolGlobal struct {
+ id string
+}
+
+func (sg SymbolGlobal) ID() string {
+ return sg.id
+} \ No newline at end of file
diff --git a/pkg/lang/modules/core/compiled.go b/pkg/lang/modules/core/compiled.go
new file mode 100644
index 0000000..ae34cf4
--- /dev/null
+++ b/pkg/lang/modules/core/compiled.go
@@ -0,0 +1,22 @@
+package core
+
+import (
+ "jinx/pkg/lang/modules"
+ "jinx/pkg/lang/vm/code"
+)
+
+var moduleCompiled = []byte{
+ 0xb, 0x54, 0x79, 0x70, 0x65, 0x0, 0xe, 0x3a, 0x63, 0x6f, 0x72, 0x65, 0x3a, 0x73, 0x65, 0x74, 0x75, 0x70, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x23, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x24, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xd, 0x54, 0x79, 0x70, 0x65, 0x0, 0xb, 0x4e, 0x75, 0x6c, 0x6c, 0x0, 0x10, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x12, 0x24, 0x61, 0x64, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x0, 0x4, 0x24, 0x69, 0x6e, 0x69, 0x74, 0x0, 0x9, 0x5a, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x24, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb, 0x49, 0x6e, 0x74, 0x0, 0x10, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x12, 0x24, 0x61, 0x64, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x0, 0x4, 0x24, 0x69, 0x6e, 0x69, 0x74, 0x0, 0x9, 0x9a, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x24, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb, 0x46, 0x6c, 0x6f, 0x61, 0x74, 0x0, 0x10, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x12, 0x24, 0x61, 0x64, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x0, 0x4, 0x24, 0x69, 0x6e, 0x69, 0x74, 0x0, 0x9, 0xd9, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x24, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x0, 0x10, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x12, 0x24, 0x61, 0x64, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x0, 0x4, 0x24, 0x69, 0x6e, 0x69, 0x74, 0x0, 0x9, 0x1a, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x24, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb, 0x42, 0x6f, 0x6f, 0x6c, 0x0, 0x10, 0x5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x12, 0x24, 0x61, 0x64, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x0, 0x4, 0x24, 0x69, 0x6e, 0x69, 0x74, 0x0, 0x9, 0x5c, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x24, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb, 0x41, 0x72, 0x72, 0x61, 0x79, 0x0, 0x10, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x12, 0x24, 0x61, 0x64, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x0, 0x4, 0x24, 0x69, 0x6e, 0x69, 0x74, 0x0, 0x9, 0x9c, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x24, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x12, 0x24, 0x61, 0x64, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x0, 0x4, 0x70, 0x75, 0x73, 0x68, 0x0, 0x9, 0xdd, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x18, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x24, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x12, 0x24, 0x61, 0x64, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x0, 0x4, 0x70, 0x6f, 0x70, 0x0, 0x9, 0x1c, 0x5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x24, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x12, 0x24, 0x61, 0x64, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x0, 0x4, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x0, 0x9, 0x47, 0x5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x24, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x0, 0x10, 0x7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x12, 0x24, 0x61, 0x64, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x0, 0x4, 0x24, 0x69, 0x6e, 0x69, 0x74, 0x0, 0x9, 0x75, 0x5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x24, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9, 0xb9, 0x5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x18, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9, 0xe8, 0x5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xd, 0x4e, 0x75, 0x6c, 0x6c, 0x0, 0x10, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xd, 0x49, 0x6e, 0x74, 0x0, 0x10, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xd, 0x46, 0x6c, 0x6f, 0x61, 0x74, 0x0, 0x10, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xd, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x0, 0x10, 0x5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xd, 0x42, 0x6f, 0x6f, 0x6c, 0x0, 0x10, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xd, 0x41, 0x72, 0x72, 0x61, 0x79, 0x0, 0x10, 0x7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xd, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x0, 0x10, 0x8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xd, 0x73, 0x61, 0x79, 0x0, 0x10, 0x9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xd, 0x70, 0x69, 0x6e, 0x67, 0x0, 0x1, 0xa, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x17, 0xe, 0x3a, 0x63, 0x6f, 0x72, 0x65, 0x3a, 0x4e, 0x75, 0x6c, 0x6c, 0x3a, 0x24, 0x69, 0x6e, 0x69, 0x74, 0x23, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x0, 0x24, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x28, 0xa, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x17, 0xe, 0x3a, 0x63, 0x6f, 0x72, 0x65, 0x3a, 0x49, 0x6e, 0x74, 0x3a, 0x24, 0x69, 0x6e, 0x69, 0x74, 0x23, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x0, 0x24, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x28, 0xa, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x17, 0xe, 0x3a, 0x63, 0x6f, 0x72, 0x65, 0x3a, 0x46, 0x6c, 0x6f, 0x61, 0x74, 0x3a, 0x24, 0x69, 0x6e, 0x69, 0x74, 0x23, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x0, 0x24, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x28, 0xa, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x17, 0xe, 0x3a, 0x63, 0x6f, 0x72, 0x65, 0x3a, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3a, 0x24, 0x69, 0x6e, 0x69, 0x74, 0x23, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x0, 0x24, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x28, 0xa, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x17, 0xe, 0x3a, 0x63, 0x6f, 0x72, 0x65, 0x3a, 0x42, 0x6f, 0x6f, 0x6c, 0x3a, 0x24, 0x69, 0x6e, 0x69, 0x74, 0x23, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x0, 0x24, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x28, 0xa, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x17, 0xe, 0x3a, 0x63, 0x6f, 0x72, 0x65, 0x3a, 0x41, 0x72, 0x72, 0x61, 0x79, 0x3a, 0x24, 0x69, 0x6e, 0x69, 0x74, 0x23, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x0, 0x24, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x28, 0xe, 0x3a, 0x63, 0x6f, 0x72, 0x65, 0x3a, 0x41, 0x72, 0x72, 0x61, 0x79, 0x3a, 0x70, 0x75, 0x73, 0x68, 0x23, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x0, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x24, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0x28, 0xe, 0x3a, 0x63, 0x6f, 0x72, 0x65, 0x3a, 0x41, 0x72, 0x72, 0x61, 0x79, 0x3a, 0x70, 0x6f, 0x70, 0x23, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x0, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x24, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x28, 0xe, 0x3a, 0x63, 0x6f, 0x72, 0x65, 0x3a, 0x41, 0x72, 0x72, 0x61, 0x79, 0x3a, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x23, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x0, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x24, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x28, 0xa, 0x14, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x17, 0xe, 0x3a, 0x63, 0x6f, 0x72, 0x65, 0x3a, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x24, 0x69, 0x6e, 0x69, 0x74, 0x23, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x0, 0x24, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x28, 0xe, 0x3a, 0x63, 0x6f, 0x72, 0x65, 0x3a, 0x73, 0x61, 0x79, 0x23, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x24, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0x28, 0x4, 0x50, 0x6f, 0x6e, 0x67, 0x21, 0x0, 0x28,
+}
+
+var moduleCode = code.New(code.Raw(moduleCompiled), code.NewDebugInfo("core"))
+
+var Module = modules.NewModule(
+ "",
+ "core",
+ &moduleCode,
+ []modules.ModuleRef{},
+ []string{
+ "Type", "Null", "Int", "Float", "String", "Bool", "Array", "Function", "say", "ping",
+ },
+)
diff --git a/pkg/lang/modules/core/core.lang b/pkg/lang/modules/core/core.lang
new file mode 100644
index 0000000..e6be2d5
--- /dev/null
+++ b/pkg/lang/modules/core/core.lang
@@ -0,0 +1,78 @@
+type Type {
+}
+
+native(":core:setup_type", Type)
+global Type
+
+type Null {
+ () {
+ native(":core:Null:$init")
+ }
+}
+
+type Int {
+ () {
+ native(":core:Int:$init")
+ }
+}
+
+type Float {
+ () {
+ native(":core:Float:$init")
+ }
+}
+
+type String {
+ () {
+ native(":core:String:$init")
+ }
+}
+
+type Bool {
+ () {
+ native(":core:Bool:$init")
+ }
+}
+
+type Array {
+ () {
+ native(":core:Array:$init")
+ }
+
+ fn push(this, element) {
+ native(":core:Array:push", this, element)
+ }
+
+ fn pop(this) {
+ return native(":core:Array:pop", this)
+ }
+
+ fn length(this) {
+ return native(":core:Array:length", this)
+ }
+}
+
+type Function {
+ () {
+ native(":core:Function:$init")
+ }
+}
+
+fn say(message) {
+ native(":core:say", message)
+}
+
+fn ping() {
+ return "Pong!"
+}
+
+global Null
+global Int
+global Float
+global String
+global Bool
+global Array
+global Function
+
+global say
+global ping \ No newline at end of file
diff --git a/pkg/lang/modules/core/natives.go b/pkg/lang/modules/core/natives.go
new file mode 100644
index 0000000..3035b80
--- /dev/null
+++ b/pkg/lang/modules/core/natives.go
@@ -0,0 +1,166 @@
+package core
+
+import (
+ "fmt"
+ "jinx/pkg/lang/vm/code"
+ "jinx/pkg/lang/vm/executor"
+ "jinx/pkg/lang/vm/value"
+)
+
+var Natives = []any{
+ n(":core:setup_type", 1, func(exe executor.Exectutor, args []value.Value) (value.Value, error) {
+ // Add the very important "$add_method" method to the Type type.
+ // Without it no other methods can be created.
+ typeRef, err := ensureType[value.TypeRefData](args[0], value.TypeRefType)
+ if err != nil {
+ return value.Value{}, err
+ }
+
+ addMethodGlobal, ok, err := exe.GetGlobal(":core:Type:$add_method#native")
+ if err != nil {
+ return value.Value{}, err
+ }
+
+ if !ok {
+ panic("missing Type:$add_method implementation")
+ }
+
+ addMethodFn, err := ensureType[value.FunctionData](addMethodGlobal, value.FunctionType)
+ if err != nil {
+ return value.Value{}, err
+ }
+
+ typeRef.AddMethod(
+ exe.Mem(),
+ "$add_method",
+ value.NewNativeFunction(addMethodFn.Native(), 2).Data().(value.FunctionData),
+ )
+
+ return value.Value{}, nil
+ }),
+ n(":core:Type:$add_method", 2, func(exe executor.Exectutor, args []value.Value) (value.Value, error) {
+ ref, err := ensureTypeThis[value.TypeRefData](exe, value.TypeRefType)
+ if err != nil {
+ return value.Value{}, err
+ }
+
+ fn, err := ensureType[value.FunctionData](args[0], value.FunctionType)
+ if err != nil {
+ return value.Value{}, err
+ }
+
+ nameData, err := ensureType[value.StringData](args[1], value.StringType)
+ if err != nil {
+ return value.Value{}, err
+ }
+ name, err := nameData.RawString(exe.Mem())
+ if err != nil {
+ return value.Value{}, err
+ }
+
+ return value.Value{}, ref.AddMethod(exe.Mem(), name, fn)
+ }),
+
+ n(":core:Int:$init", 0, func(exe executor.Exectutor, args []value.Value) (value.Value, error) {
+ return value.NewInt(0), nil
+ }),
+ n(":core:Float:$init", 0, func(exe executor.Exectutor, args []value.Value) (value.Value, error) {
+ return value.NewFloat(0.0), nil
+ }),
+ n(":core:String:$init", 0, func(exe executor.Exectutor, args []value.Value) (value.Value, error) {
+ return value.NewString(exe.Mem(), "")
+ }),
+ n(":core:Bool:$init", 0, func(exe executor.Exectutor, args []value.Value) (value.Value, error) {
+ return value.NewBool(false), nil
+ }),
+ n(":core:Array:$init", 0, func(exe executor.Exectutor, args []value.Value) (value.Value, error) {
+ return value.NewArray(exe.Mem(), args)
+ }),
+ n(":core:Function:$init", 0, func(exe executor.Exectutor, args []value.Value) (value.Value, error) {
+ return value.NewFunction(code.NewPos(0, 0), 0), nil
+ }),
+ n(":core:Type:$init", 0, func(exe executor.Exectutor, args []value.Value) (value.Value, error) {
+ return value.NewType(exe.Mem(), "")
+ }),
+
+ n(":core:Array:push", 2, func(exe executor.Exectutor, args []value.Value) (value.Value, error) {
+ array, err := ensureType[value.ArrayData](args[0], value.ArrayType)
+ if err != nil {
+ return value.Value{}, err
+ }
+
+ element := args[1]
+ return value.Value{}, array.Push(exe.Mem(), element)
+ }),
+ n(":core:Array:pop", 1, func(exe executor.Exectutor, args []value.Value) (value.Value, error) {
+ array, err := ensureType[value.ArrayData](args[0], value.ArrayType)
+ if err != nil {
+ return value.Value{}, err
+ }
+
+ return array.Pop(exe.Mem())
+ }),
+ n(":core:Array:length", 1, func(exe executor.Exectutor, args []value.Value) (value.Value, error) {
+ array, err := ensureType[value.ArrayData](args[0], value.ArrayType)
+ if err != nil {
+ return value.Value{}, err
+ }
+
+ length, err := array.Len(exe.Mem())
+ if err != nil {
+ return value.Value{}, err
+ }
+
+ return value.NewInt(int64(length)), nil
+ }),
+
+ n(":core:say", 1, func(exe executor.Exectutor, args []value.Value) (value.Value, error) {
+ message := args[0]
+ s, err := message.Data().String(exe.Mem())
+ if err != nil {
+ return value.Value{}, err
+ }
+
+ fmt.Println(s)
+ return value.Value{}, nil
+ }),
+}
+
+type native struct {
+ name string
+ argCount int
+ fn executor.NativeFunc
+}
+
+func n(name string, argCount int, fn executor.NativeFunc) native {
+ return native{name, argCount, fn}
+}
+
+func (n native) Name() string {
+ return n.name
+}
+
+func (n native) ArgCount() int {
+ return n.argCount
+}
+
+func (n native) Fn() executor.NativeFunc {
+ return n.fn
+}
+
+func ensureTypeThis[D value.Data](exe executor.Exectutor, t value.TypeKind) (D, error) {
+ this, err := exe.GetThis()
+ if err != nil {
+ return *new(D), err
+ }
+
+ return ensureType[D](this, t)
+}
+
+func ensureType[D value.Data](val value.Value, t value.TypeKind) (D, error) {
+ if val.Type() != t {
+ return *new(D), fmt.Errorf("expected type %s, got %s", t, val.Type())
+ }
+
+ return val.Data().(D), nil
+}
diff --git a/pkg/lang/modules/module.go b/pkg/lang/modules/module.go
new file mode 100644
index 0000000..08b9987
--- /dev/null
+++ b/pkg/lang/modules/module.go
@@ -0,0 +1,104 @@
+package modules
+
+import (
+ "fmt"
+ "jinx/pkg/lang/vm/code"
+)
+
+type Module struct {
+ author string
+ name string
+ code *code.Code
+ deps []ModuleRef
+ globals []string
+}
+
+func NewModule(author, name string, code *code.Code, deps []ModuleRef, globals []string) Module {
+ return Module{
+ author: author,
+ name: name,
+ code: code,
+ deps: deps,
+ globals: globals,
+ }
+}
+
+func NewUnknownModule(code *code.Code) Module {
+ return Module{
+ author: "noone",
+ name: "program",
+ code: code,
+ deps: []ModuleRef{
+ CoreModuleRef,
+ },
+ globals: []string{},
+ }
+}
+
+func (m Module) Author() string {
+ return m.author
+}
+
+func (m Module) Name() string {
+ return m.name
+}
+
+func (m Module) Code() *code.Code {
+ return m.code
+}
+
+func (m Module) Deps() []ModuleRef {
+ return m.deps
+}
+
+func (m Module) Globals() []string {
+ return m.globals
+}
+
+func (m Module) IsCore() bool {
+ return m.author == CoreModuleRef.author && m.name == CoreModuleRef.name
+}
+
+func (m Module) GetGlobal(name string) (string, error) {
+ // TODO: Don't use loop
+ found := false
+ for _, global := range m.globals {
+ if global == name {
+ found = true
+ break
+ }
+ }
+
+ if !found {
+ return "", fmt.Errorf("global %s not in module", name)
+ }
+
+ return fmt.Sprintf("%s:%s:%s", m.author, m.name, name), nil
+}
+
+type ModuleRef struct {
+ author string
+ name string
+}
+
+func NewRef(author, name string) ModuleRef {
+ return ModuleRef{
+ author: author,
+ name: name,
+ }
+}
+
+func (m ModuleRef) Author() string {
+ return m.author
+}
+
+func (m ModuleRef) Name() string {
+ return m.name
+}
+
+var (
+ CoreModuleRef = ModuleRef{
+ author: "",
+ name: "core",
+ }
+)
diff --git a/pkg/lang/modules/natives/natives.go b/pkg/lang/modules/natives/natives.go
new file mode 100644
index 0000000..a1b09b6
--- /dev/null
+++ b/pkg/lang/modules/natives/natives.go
@@ -0,0 +1,39 @@
+package natives
+
+import (
+ "jinx/pkg/lang/modules/core"
+ "jinx/pkg/lang/vm/executor"
+)
+
+var Natives = makeNatives(
+ core.Natives,
+)
+
+func makeNatives(moduleNativesArrays ...[]any) []Native {
+ natives := make([]Native, 0)
+
+ for _, moduleNatives := range moduleNativesArrays {
+ for _, moduleNativeI := range moduleNatives {
+ moduleNative := moduleNativeI.(moduleNative)
+
+ natives = append(natives, Native{
+ Name: moduleNative.Name(),
+ ArgCount: moduleNative.ArgCount(),
+ Fn: moduleNative.Fn(),
+ })
+ }
+ }
+ return natives
+}
+
+type Native struct {
+ Name string
+ ArgCount int
+ Fn executor.NativeFunc
+}
+
+type moduleNative interface {
+ Name() string
+ ArgCount() int
+ Fn() executor.NativeFunc
+}
diff --git a/pkg/lang/vm/core.go b/pkg/lang/vm/core.go
deleted file mode 100644
index 477cee8..0000000
--- a/pkg/lang/vm/core.go
+++ /dev/null
@@ -1,146 +0,0 @@
-package vm
-
-import (
- "jinx/pkg/lang/vm/code"
- "jinx/pkg/lang/vm/value"
-)
-
-func (vm *VM) createCoreNullType() value.Type {
- return value.Type{
- Kind: value.NullType,
- }
-}
-
-func (vm *VM) createCoreIntType() value.Type {
- return value.Type{
- Kind: value.IntType,
- }
-}
-
-func (vm *VM) createCoreFloatType() value.Type {
- return value.Type{
- Kind: value.FloatType,
- }
-}
-
-func (vm *VM) createCoreStringType() value.Type {
- return value.Type{
- Kind: value.StringType,
- }
-}
-
-func (vm *VM) createCoreBoolType() value.Type {
- return value.Type{
- Kind: value.BoolType,
- }
-}
-
-func (vm *VM) createCoreArrayType() value.Type {
- return value.Type{
- Kind: value.ArrayType,
- Methods: map[string]value.FunctionData{
- "length": makeCoreFn(vm.coreArrayLength, 0),
- "push": makeCoreFn(vm.coreArrayPush, 1),
- },
- }
-}
-
-func (vm *VM) createCoreFunctionType() value.Type {
- return value.Type{
- Kind: value.FunctionType,
- }
-}
-
-func (vm *VM) createCoreTypeRefType() value.Type {
- return value.Type{
- Kind: value.TypeRefType,
- Methods: map[string]value.FunctionData{
- "$add_method": makeCoreFn(vm.coreTypeRefIntrAddMethod, 2),
- },
- }
-}
-
-func (vm *VM) coreArrayLength(args []value.Value) (value.Value, error) {
- a, err := ensureTypeThis[value.ArrayData](vm, value.ArrayType)
- if err != nil {
- return value.Value{}, err
- }
-
- len, err := a.Len(vm.memory)
- if err != nil {
- return value.Value{}, err
- }
- res := value.NewInt(int64(len))
- return res, nil
-}
-
-func (vm *VM) coreArrayPush(args []value.Value) (value.Value, error) {
- a, err := ensureTypeThis[value.ArrayData](vm, value.ArrayType)
- if err != nil {
- return value.Value{}, err
- }
-
- return value.Value{}, a.Push(vm.memory, args[0])
-}
-
-func (vm *VM) coreTypeRefIntrAddMethod(args []value.Value) (value.Value, error) {
- ref, err := ensureTypeThis[value.TypeRefData](vm, value.TypeRefType)
- if err != nil {
- return value.Value{}, err
- }
-
- fn, err := ensureType[value.FunctionData](args[0], value.FunctionType)
- if err != nil {
- return value.Value{}, err
- }
-
- nameData, err := ensureType[value.StringData](args[1], value.StringType)
- if err != nil {
- return value.Value{}, err
- }
- name, err := nameData.RawString(vm.memory)
- if err != nil {
- return value.Value{}, err
- }
-
- return value.Value{}, ref.AddMethod(vm.memory, name, fn)
-}
-
-func ensureTypeThis[D value.Data](vm *VM, t value.TypeKind) (D, error) {
- this, err := vm.getThis()
- if err != nil {
- return *new(D), err
- }
-
- return ensureType[D](this, t)
-}
-
-func ensureType[D value.Data](val value.Value, t value.TypeKind) (D, error) {
- if val.Type() != t {
- return *new(D), ErrInvalidOperandType{
- Op: code.OpNop, // TODO: Add error not dependent on op.
- X: val.Type(),
- }
- }
-
- return val.Data().(D), nil
-}
-
-func makeCoreFn(f value.NativeFunc, argCount uint) value.FunctionData {
- return value.NewNativeFunction(f, argCount).Data().(value.FunctionData)
-}
-
-func (vm *VM) getThis() (value.Value, error) {
- err := vm.execGetEnv(0)
- if err != nil {
- return value.Value{}, err
- }
-
- // Take it back!
- this, err := vm.popAndDrop()
- if err != nil {
- return value.Value{}, err
- }
-
- return this, nil
-}
diff --git a/pkg/lang/vm/errors.go b/pkg/lang/vm/errors.go
index 0dd73e7..0c1b916 100644
--- a/pkg/lang/vm/errors.go
+++ b/pkg/lang/vm/errors.go
@@ -9,16 +9,17 @@ import (
)
type Error struct {
- Pos code.Pos
- Line int
- Err error
+ Pos code.Pos
+ Module string
+ Line int
+ Err error
}
func (e Error) Error() string {
if e.Line == -1 {
- return fmt.Sprintf("vm error in module '%d' at pc %d, unknown line: %v", e.Pos.Module, e.Pos.PC, e.Err)
+ return fmt.Sprintf("vm error in module '%s' at pc %d, unknown line: %v", e.Module, e.Pos.PC, e.Err)
}
- return fmt.Sprintf("vm error in module '%d' at pc %d, line %d: %v", e.Pos.Module, e.Pos.PC, e.Line, e.Err)
+ return fmt.Sprintf("vm error in module '%s' at pc %d, line %d: %v", e.Module, e.Pos.PC, e.Line, e.Err)
}
// Fatal errors
diff --git a/pkg/lang/vm/exe.go b/pkg/lang/vm/exe.go
new file mode 100644
index 0000000..7d6fc61
--- /dev/null
+++ b/pkg/lang/vm/exe.go
@@ -0,0 +1,62 @@
+package vm
+
+import (
+ "jinx/pkg/lang/vm/mem"
+ "jinx/pkg/lang/vm/stack"
+ "jinx/pkg/lang/vm/value"
+)
+
+func (vm *VM) Mem() mem.Mem {
+ return vm.memory
+}
+
+func (vm *VM) Stack() stack.Stack {
+ return vm.stack
+}
+
+func (vm *VM) GetThis() (value.Value, error) {
+ err := vm.execGetEnv(0)
+ if err != nil {
+ return value.Value{}, err
+ }
+
+ // Take it back!
+ this, err := vm.popAndDrop()
+ if err != nil {
+ return value.Value{}, err
+ }
+
+ return this, nil
+}
+
+func (vm *VM) AddGlobal(name string, v value.Value) error {
+ globalPtr, err := vm.memory.Allocate(mem.CellKindGlobal)
+ if err != nil {
+ return err
+ }
+
+ globalCell := value.GlobalCell(v)
+ if err := vm.memory.Set(globalPtr, globalCell); err != nil {
+ return err
+ }
+
+ vm.globals[name] = globalPtr
+
+ return nil
+}
+
+func (vm *VM) GetGlobal(name string) (value.Value, bool, error) {
+ ptr, ok := vm.globals[name]
+ if !ok {
+ return value.Value{}, false, nil
+ }
+
+ cell, err := vm.getMemCell(ptr, mem.CellKindGlobal, false)
+ if err != nil {
+ return value.Value{}, false, err
+ }
+
+ v := cell.(value.GlobalCell).Get()
+
+ return v, true, nil
+}
diff --git a/pkg/lang/vm/exec.go b/pkg/lang/vm/exec.go
index eeb8710..ade6cc0 100644
--- a/pkg/lang/vm/exec.go
+++ b/pkg/lang/vm/exec.go
@@ -44,7 +44,7 @@ func (vm *VM) execPushArray() error {
func (vm *VM) execPushFunction(pc int) {
// TODO: Make push ops into functions, where the argCount can be passed.
- vm.stack.Push(value.NewFunction(code.NewPos(vm.module(), pc), 0))
+ vm.stack.Push(value.NewFunction(code.NewPos(vm.moduleID(), pc), 0))
}
func (vm *VM) execPushObject() error {
@@ -71,35 +71,56 @@ func (vm *VM) execAddGlobal(name string) error {
return err
}
- globalPtr, err := vm.memory.Allocate(mem.CellKindGlobal)
+ module := vm.module()
+ qualifiedName, err := module.GetGlobal(name)
if err != nil {
return err
}
- globalCell := value.GlobalCell(v)
- if err := vm.memory.Set(globalPtr, globalCell); err != nil {
- return err
+ // Fill the core ptrs if we got a core global.
+ if module.IsCore() {
+ if v.Type() == value.TypeRefType {
+ ptr := v.Data().(value.TypeRefData).TypeRef()
+
+ switch qualifiedName {
+ case ":core:Type":
+ vm.corePtrs.SetTypeRef(ptr)
+ case ":core:Int":
+ vm.corePtrs.SetInt(ptr)
+ case ":core:Float":
+ vm.corePtrs.SetFloat(ptr)
+ case ":core:Bool":
+ vm.corePtrs.SetBool(ptr)
+ case ":core:Null":
+ vm.corePtrs.SetNull(ptr)
+ case ":core:Array":
+ vm.corePtrs.SetArray(ptr)
+ case ":core:Function":
+ vm.corePtrs.SetFunction(ptr)
+ case ":core:String":
+ vm.corePtrs.SetString(ptr)
+ }
+ }
}
- vm.globals[name] = globalPtr
+ if err := vm.AddGlobal(qualifiedName, v); err != nil {
+ return err
+ }
return nil
}
func (vm *VM) execGetGlobal(name string) error {
- ptr, ok := vm.globals[name]
- if !ok {
- return ErrNoSuchGlobal{GlobalName: name}
- }
-
- cell, err := vm.getMemCell(ptr, mem.CellKindGlobal, false)
+ v, ok, err := vm.GetGlobal(name)
if err != nil {
return err
}
- v := cell.(value.GlobalCell).Get()
- v = v.Clone(vm.memory)
+ if !ok {
+ return ErrNoSuchGlobal{GlobalName: name}
+ }
+ v = v.Clone(vm.memory)
vm.stack.Push(v)
return nil
}
@@ -234,7 +255,7 @@ func (vm *VM) execGetMember(name string) error {
}
}
- typeCell, err := vm.getMemCell(parent.TypePtr(), mem.CellKindType, false)
+ typeCell, err := vm.getMemCell(parent.TypePtr(&vm.corePtrs), mem.CellKindType, false)
if err != nil {
return err
}
@@ -889,6 +910,8 @@ func (vm *VM) execCall(argCount uint) error {
if !val.IsEmpty() {
vm.stack.Push(val)
+ } else {
+ vm.stack.Push(value.NewNull())
}
return nil
diff --git a/pkg/lang/vm/executor/executor.go b/pkg/lang/vm/executor/executor.go
new file mode 100644
index 0000000..0b7ccef
--- /dev/null
+++ b/pkg/lang/vm/executor/executor.go
@@ -0,0 +1,17 @@
+package executor
+
+import (
+ "jinx/pkg/lang/vm/mem"
+ "jinx/pkg/lang/vm/stack"
+ "jinx/pkg/lang/vm/value"
+)
+
+type Exectutor interface {
+ Mem() mem.Mem
+ Stack() stack.Stack
+
+ GetThis() (value.Value, error)
+
+ AddGlobal(name string, v value.Value) error
+ GetGlobal(name string) (value.Value, bool, error)
+}
diff --git a/pkg/lang/vm/executor/nativefunc.go b/pkg/lang/vm/executor/nativefunc.go
new file mode 100644
index 0000000..2188bfb
--- /dev/null
+++ b/pkg/lang/vm/executor/nativefunc.go
@@ -0,0 +1,5 @@
+package executor
+
+import "jinx/pkg/lang/vm/value"
+
+type NativeFunc func(executor Exectutor, args []value.Value) (value.Value, error)
diff --git a/pkg/lang/vm/setup.go b/pkg/lang/vm/setup.go
index db44b8a..201538d 100644
--- a/pkg/lang/vm/setup.go
+++ b/pkg/lang/vm/setup.go
@@ -1,44 +1,54 @@
package vm
import (
- "jinx/pkg/lang/vm/mem"
+ "fmt"
+ "jinx/pkg/lang/modules"
+ "jinx/pkg/lang/modules/core"
+ "jinx/pkg/lang/modules/natives"
"jinx/pkg/lang/vm/value"
)
func (vm *VM) setup() error {
- if err := vm.setupCoreLib(); err != nil {
- return err
- }
+ vm.modules = make([]modules.Module, 0, len(vm.main.Deps()))
- return nil
-}
+ // Add all natives to the VM as globals.
+ for _, native := range natives.Natives {
+ decollidedName := fmt.Sprintf("%s#native", native.Name)
-func (vm *VM) setupCoreLib() error {
- type allocationJob struct {
- at mem.Ptr
- t value.Type
- }
+ if _, ok := vm.globals[decollidedName]; ok {
+ return fmt.Errorf("native %s already exists", decollidedName)
+ }
- toAllocate := []allocationJob{
- {at: value.CORE_TYPE_NULL, t: vm.createCoreNullType()},
- {at: value.CORE_TYPE_INT, t: vm.createCoreIntType()},
- {at: value.CORE_TYPE_FLOAT, t: vm.createCoreFloatType()},
- {at: value.CORE_TYPE_STRING, t: vm.createCoreStringType()},
- {at: value.CORE_TYPE_BOOL, t: vm.createCoreBoolType()},
- {at: value.CORE_TYPE_ARRAY, t: vm.createCoreArrayType()},
- {at: value.CORE_TYPE_FUNCTION, t: vm.createCoreFunctionType()},
- {at: value.CORE_TYPE_TYPE_REF, t: vm.createCoreTypeRefType()},
- }
+ nativeFunc := native.Fn // Capture the native function, because Go is fun.
+ wrappedFunc := func(args []value.Value) (value.Value, error) {
+ return nativeFunc(vm, args)
+ }
- for _, job := range toAllocate {
- if err := vm.memory.AllocateAt(job.at, mem.CellKindType); err != nil {
+ nativeFunction := value.NewNativeFunction(wrappedFunc, uint(native.ArgCount))
+ if err := vm.AddGlobal(decollidedName, nativeFunction); err != nil {
return err
}
+ }
- if err := vm.memory.Set(job.at, value.TypeCell(job.t)); err != nil {
+ for _, depRef := range vm.main.Deps() {
+ dep, err := vm.resolveModule(depRef)
+ if err != nil {
return err
}
+
+ vm.modules = append(vm.modules, dep)
}
return nil
}
+
+// TODO: Make an actual module resolver.
+func (vm *VM) resolveModule(ref modules.ModuleRef) (modules.Module, error) {
+ // TODO: Support other modules than core.
+ switch ref.Name() {
+ case "core":
+ return core.Module, nil
+ default:
+ return modules.Module{}, fmt.Errorf("unknown module: %s", ref.Name())
+ }
+}
diff --git a/pkg/lang/vm/utils.go b/pkg/lang/vm/utils.go
index 2b7fa6f..975d31b 100644
--- a/pkg/lang/vm/utils.go
+++ b/pkg/lang/vm/utils.go
@@ -1,6 +1,7 @@
package vm
import (
+ "jinx/pkg/lang/modules"
"jinx/pkg/lang/vm/code"
"jinx/pkg/lang/vm/mem"
"jinx/pkg/lang/vm/value"
@@ -35,7 +36,7 @@ func (vm *VM) getMemCell(ptr mem.Ptr, kind mem.CellKind, allowNil bool) (mem.Cel
}
if !vm.memory.Is(ptr, kind) {
- return nil, ErrUnexpectedMemCell{Ptr: ptr, Expected: mem.CellKindEnv, Got: vm.memory.Kind(ptr)}
+ return nil, ErrUnexpectedMemCell{Ptr: ptr, Expected: kind, Got: vm.memory.Kind(ptr)}
}
cell, err := vm.memory.Get(ptr)
@@ -75,10 +76,18 @@ func (vm *VM) getMemCell(ptr mem.Ptr, kind mem.CellKind, allowNil bool) (mem.Cel
return cell, nil
}
-func (vm *VM) module() int {
+func (vm *VM) moduleID() int {
return vm.pos.Module
}
+func (vm *VM) module() modules.Module {
+ if vm.moduleID() == -1 {
+ return vm.main
+ } else {
+ return vm.modules[vm.moduleID()]
+ }
+}
+
func (vm *VM) pc() int {
return vm.pos.PC
}
diff --git a/pkg/lang/vm/value/core_ptrs.go b/pkg/lang/vm/value/core_ptrs.go
index a1f0365..adb9a6b 100644
--- a/pkg/lang/vm/value/core_ptrs.go
+++ b/pkg/lang/vm/value/core_ptrs.go
@@ -4,14 +4,90 @@ import (
"jinx/pkg/lang/vm/mem"
)
-const (
- CORE_TYPE_NULL = mem.Ptr(iota + 1)
- CORE_TYPE_INT
- CORE_TYPE_FLOAT
- CORE_TYPE_STRING
- CORE_TYPE_BOOL
- CORE_TYPE_ARRAY
- CORE_TYPE_FUNCTION
+// All CorePtrs are unitialized until the core module is loaded.
+type CorePtrs struct {
+ CoreTypeNull mem.Ptr
+ CoreTypeInt mem.Ptr
+ CoreTypeFloat mem.Ptr
+ CoreTypeString mem.Ptr
+ CoreTypeBool mem.Ptr
+ CoreTypeArray mem.Ptr
+ CoreTypeFunction mem.Ptr
- CORE_TYPE_TYPE_REF
-)
+ CoreTypeTypeRef mem.Ptr
+}
+
+func (c *CorePtrs) SetNull(p mem.Ptr) {
+ if c.CoreTypeNull != mem.NullPtr {
+ panic("core ptr null already set")
+ }
+
+ c.CoreTypeNull = p
+}
+
+func (c *CorePtrs) SetInt(p mem.Ptr) {
+ if c.CoreTypeInt != mem.NullPtr {
+ panic("core ptr int already set")
+ }
+
+ c.CoreTypeInt = p
+}
+
+func (c *CorePtrs) SetFloat(p mem.Ptr) {
+ if c.CoreTypeFloat != mem.NullPtr {
+ panic("core ptr float already set")
+ }
+
+ c.CoreTypeFloat = p
+}
+
+func (c *CorePtrs) SetString(p mem.Ptr) {
+ if c.CoreTypeString != mem.NullPtr {
+ panic("core ptr string already set")
+ }
+
+ c.CoreTypeString = p
+}
+
+func (c *CorePtrs) SetBool(p mem.Ptr) {
+ if c.CoreTypeBool != mem.NullPtr {
+ panic("core ptr bool already set")
+ }
+
+ c.CoreTypeBool = p
+}
+
+func (c *CorePtrs) SetArray(p mem.Ptr) {
+ if c.CoreTypeArray != mem.NullPtr {
+ panic("core ptr array already set")
+ }
+
+ c.CoreTypeArray = p
+}
+
+func (c *CorePtrs) SetFunction(p mem.Ptr) {
+ if c.CoreTypeFunction != mem.NullPtr {
+ panic("core ptr function already set")
+ }
+
+ c.CoreTypeFunction = p
+}
+
+func (c *CorePtrs) SetTypeRef(p mem.Ptr) {
+ if c.CoreTypeTypeRef != mem.NullPtr {
+ panic("core ptr type ref already set")
+ }
+
+ c.CoreTypeTypeRef = p
+}
+
+func (c *CorePtrs) Complete() bool {
+ return c.CoreTypeNull != mem.NullPtr &&
+ c.CoreTypeInt != mem.NullPtr &&
+ c.CoreTypeFloat != mem.NullPtr &&
+ c.CoreTypeString != mem.NullPtr &&
+ c.CoreTypeBool != mem.NullPtr &&
+ c.CoreTypeArray != mem.NullPtr &&
+ c.CoreTypeFunction != mem.NullPtr &&
+ c.CoreTypeTypeRef != mem.NullPtr
+}
diff --git a/pkg/lang/vm/value/data.go b/pkg/lang/vm/value/data.go
index 39193d5..1cd258b 100644
--- a/pkg/lang/vm/value/data.go
+++ b/pkg/lang/vm/value/data.go
@@ -116,11 +116,31 @@ func (a ArrayData) Push(m mem.Mem, v Value) error {
arr := data.(ArrayCell).Get()
arr = append(arr, v)
- m.Set(a.data, ArrayCell(arr))
+ if err := m.Set(a.data, ArrayCell(arr)); err != nil {
+ return err
+ }
return nil
}
+func (a ArrayData) Pop(m mem.Mem) (Value, error) {
+ data, err := m.Get(a.data)
+ if err != nil {
+ return Value{}, err
+ }
+
+ arr := data.(ArrayCell).Get()
+
+ popped := arr[len(arr)-1]
+ arr = arr[:len(arr)-1]
+
+ if err := m.Set(a.data, ArrayCell(arr)); err != nil {
+ return Value{}, err
+ }
+
+ return popped, nil
+}
+
type NullData struct{}
func (n NullData) String(_ mem.Mem) (string, error) {
diff --git a/pkg/lang/vm/value/value.go b/pkg/lang/vm/value/value.go
index 5ef3f07..cce2f5e 100644
--- a/pkg/lang/vm/value/value.go
+++ b/pkg/lang/vm/value/value.go
@@ -93,24 +93,24 @@ func (v Value) Type() TypeKind {
return v.t
}
-func (v Value) TypePtr() mem.Ptr {
+func (v Value) TypePtr(corePtrs *CorePtrs) mem.Ptr {
switch v.t {
case IntType:
- return CORE_TYPE_INT
+ return corePtrs.CoreTypeInt
case FloatType:
- return CORE_TYPE_FLOAT
+ return corePtrs.CoreTypeFloat
case StringType:
- return CORE_TYPE_STRING
+ return corePtrs.CoreTypeFloat
case BoolType:
- return CORE_TYPE_BOOL
+ return corePtrs.CoreTypeBool
case ArrayType:
- return CORE_TYPE_ARRAY
+ return corePtrs.CoreTypeArray
case NullType:
- return CORE_TYPE_NULL
+ return corePtrs.CoreTypeNull
case FunctionType:
- return CORE_TYPE_FUNCTION
+ return corePtrs.CoreTypeFunction
case TypeRefType:
- return CORE_TYPE_TYPE_REF
+ return corePtrs.CoreTypeTypeRef
case ObjectType:
return v.d.(ObjectData).t
default:
diff --git a/pkg/lang/vm/vm.go b/pkg/lang/vm/vm.go
index 4a4aa68..4b40633 100644
--- a/pkg/lang/vm/vm.go
+++ b/pkg/lang/vm/vm.go
@@ -1,34 +1,42 @@
package vm
import (
+ "jinx/pkg/lang/modules"
"jinx/pkg/lang/vm/code"
"jinx/pkg/lang/vm/mem"
"jinx/pkg/lang/vm/stack"
+ "jinx/pkg/lang/vm/value"
)
type VM struct {
pos code.Pos
- modules []*code.Code
+ main modules.Module
+ modules []modules.Module
stack stack.Stack
memory mem.Mem
canAddGlobals bool
globals map[string]mem.Ptr
+
+ corePtrs value.CorePtrs
}
-func New(main *code.Code, deps []*code.Code) *VM {
+func New(main modules.Module) *VM {
vm := &VM{
- pos: code.NewPos(0, 0),
+ pos: code.NewPos(-1, 0),
- modules: append([]*code.Code{main}, deps...),
+ main: main,
+ modules: []modules.Module{},
stack: stack.New(),
memory: mem.New(),
canAddGlobals: false,
globals: make(map[string]mem.Ptr),
+
+ corePtrs: value.CorePtrs{},
}
if err := vm.setup(); err != nil {
@@ -57,7 +65,7 @@ func (vm *VM) GetResult() (string, error) {
func (vm *VM) Run() error {
vm.canAddGlobals = true
- for i := 1; i < len(vm.modules); i++ {
+ for i := 0; i < len(vm.modules); i++ {
if err := vm.executeModule(i); err != nil {
return err
}
@@ -77,8 +85,13 @@ func (vm *VM) Run() error {
}
}
+ // All the core ptrs should be initialized once core is loaded
+ if !vm.corePtrs.Complete() {
+ panic("core ptrs not initialized after loading core (perhaps core is missing?)")
+ }
+
vm.canAddGlobals = false
- if err := vm.executeModule(0); err != nil {
+ if err := vm.executeModule(-1); err != nil {
return err
}
@@ -93,27 +106,27 @@ func (vm *VM) executeModule(moduleID int) error {
}
for {
- module := vm.modules[vm.module()]
+ module := vm.module()
+ code := module.Code()
- if vm.pc() >= module.Len() {
+ if vm.pc() >= code.Len() {
return nil
}
- op, advance := module.GetOp(vm.pc())
+ op, advance := code.GetOp(vm.pc())
vm.advancePC(advance)
- if decision, err := vm.step(module, op); err != nil {
+ if decision, err := vm.step(code, op); err != nil {
return Error{
- Pos: vm.pos,
- Line: module.Debug().PCToLine(vm.pc()),
- Err: err,
+ Pos: vm.pos,
+ Module: module.Name(),
+ Line: code.Debug().PCToLine(vm.pc()),
+ Err: err,
}
} else if decision == stepDecisionHalt {
return nil
}
}
-
- return nil
}
type stepDecision int
diff --git a/pkg/lang/vm/vm_test.go b/pkg/lang/vm/vm_test.go
index b25a446..17edb2e 100644
--- a/pkg/lang/vm/vm_test.go
+++ b/pkg/lang/vm/vm_test.go
@@ -1,9 +1,11 @@
package vm_test
import (
+ "jinx/pkg/lang/modules"
"jinx/pkg/lang/vm"
"jinx/pkg/lang/vm/code"
"jinx/pkg/lang/vm/text"
+ "os"
"strings"
"testing"
@@ -294,6 +296,7 @@ func TestTypeConstruct(t *testing.T) {
set_arg_count 2
call 2
+ drop 1
get_local 0
get_member "$add_method"
@@ -302,6 +305,7 @@ func TestTypeConstruct(t *testing.T) {
push_function @Cat:meow
call 2
+ drop 1
# Create a new instance of Cat
get_local 0
@@ -441,49 +445,74 @@ func TestPrimes(t *testing.T) {
}
func TestModules(t *testing.T) {
- mainSrc := `
- get_global "add_one"
- get_global "x"
- call 1
- halt
- `
+ t.Skip("We can't pass in random modules anymore, they have to go through the resolver")
- librarySrc1 := `
- push_int 41
- add_global "x"
- halt
- `
+ // mainSrc := `
+ // get_global "add_one"
+ // get_global "x"
+ // call 1
+ // halt
+ // `
- librarySrc2 := `
- push_function @add_one
- set_arg_count 1
- add_global "add_one"
- halt
+ // librarySrc1 := `
+ // push_int 41
+ // add_global "x"
+ // halt
+ // `
- @add_one:
- get_local 0
- push_int 1
- add
- ret
- `
+ // librarySrc2 := `
+ // push_function @add_one
+ // set_arg_count 1
+ // add_global "add_one"
+ // halt
- main := compile(t, mainSrc)
- library1 := compile(t, librarySrc1)
- library2 := compile(t, librarySrc2)
+ // @add_one:
+ // get_local 0
+ // push_int 1
+ // add
+ // ret
+ // `
- vm := vm.New(&main, []*code.Code{&library1, &library2})
- err := vm.Run()
- require.NoError(t, err)
+ // main := compile(t, mainSrc)
+ // library1 := compile(t, librarySrc1)
+ // library2 := compile(t, librarySrc2)
- res, err := vm.GetResult()
+ // vm := vm.New(&main, []*code.Code{&library1, &library2})
+ // err := vm.Run()
+ // require.NoError(t, err)
+
+ // res, err := vm.GetResult()
+ // require.NoError(t, err)
+
+ // require.Equal(t, "42", res)
+}
+
+func TestCoreSay(t *testing.T) {
+ src := `
+ get_global ":core:say"
+ push_string "Meow!!"
+ call 1
+ push_int 0
+ halt
+ `
+
+ previous := os.Stdout
+ r, w, _ := os.Pipe()
+ defer w.Close()
+ os.Stdout = w
+ test(t, src, "0")
+ os.Stdout = previous
+
+ result := make([]byte, 8)
+ _, err := r.Read(result)
require.NoError(t, err)
- require.Equal(t, "42", res)
+ require.Equal(t, "\"Meow!!\"", string(result))
}
func test(t *testing.T, src string, expected string) {
bc := compile(t, src)
- vm := vm.New(&bc, nil)
+ vm := vm.New(modules.NewUnknownModule(&bc))
err := vm.Run()
require.NoError(t, err)