From 41490ae52e3b1ff20980c5e0837d257043b3ca92 Mon Sep 17 00:00:00 2001 From: Mel Date: Tue, 26 Jul 2022 02:37:38 +0200 Subject: Store functions in locals and remove hoisting --- pkg/lang/compiler/compiler.go | 57 ++++++++++------------------------ pkg/lang/compiler/compiler_test.go | 8 +++-- pkg/lang/compiler/scope/scope_chain.go | 37 ---------------------- pkg/lang/compiler/scope/scopes.go | 2 -- pkg/lang/compiler/scope/symbol.go | 21 ++----------- 5 files changed, 24 insertions(+), 101 deletions(-) (limited to 'pkg/lang/compiler') diff --git a/pkg/lang/compiler/compiler.go b/pkg/lang/compiler/compiler.go index e446dd6..0424862 100644 --- a/pkg/lang/compiler/compiler.go +++ b/pkg/lang/compiler/compiler.go @@ -24,15 +24,6 @@ func New(ast ast.Program) *Compiler { } func (comp *Compiler) Compile() (code.Code, error) { - // Pre-declare all top-level functions - for _, stmt := range comp.ast.Stmts { - if stmt.Kind == ast.StmtKindFnDecl { - if err := comp.preDeclareFunction(stmt.Value.(ast.StmtFnDecl)); err != nil { - return code.Code{}, err - } - } - } - target := code.NewBuilder() for _, stmt := range comp.ast.Stmts { @@ -50,14 +41,6 @@ func (comp *Compiler) Compile() (code.Code, error) { return target.Build() } -func (comp *Compiler) preDeclareFunction(fnDeclStmt ast.StmtFnDecl) error { - if _, ok := comp.scopes.DeclareFunction(fnDeclStmt.Name.Value, uint(len(fnDeclStmt.Args))); !ok { - return fmt.Errorf("function %s already declared", fnDeclStmt.Name.Value) - } - - return nil -} - func (comp *Compiler) compileStmt(t *code.Builder, stmt ast.Stmt) error { var err error switch stmt.Kind { @@ -106,23 +89,13 @@ func (comp *Compiler) compileStmt(t *code.Builder, stmt ast.Stmt) error { } func (comp *Compiler) compileFnDeclStmt(t *code.Builder, fnDeclStmt ast.StmtFnDecl) error { - marker, ok := comp.scopes.DeclareFunction(fnDeclStmt.Name.Value, uint(len(fnDeclStmt.Args))) + _, ok := comp.scopes.Declare(fnDeclStmt.Name.Value) if !ok { - // If we are in the root scope, the function was simply predeclared :) - if comp.scopes.IsRootScope() { - symbolID, ok := comp.scopes.Lookup(fnDeclStmt.Name.Value) - if !ok { - panic("function said it was declared but apparently it was lying") - } - - symbol := comp.scopes.GetFunction(symbolID) - - marker = symbol.Data().Marker() - } else { - return fmt.Errorf("function %s already declared", fnDeclStmt.Name.Value) - } + return fmt.Errorf("function %s already declared", fnDeclStmt.Name.Value) } + marker := comp.scopes.CreateFunctionSubUnit(fnDeclStmt.Name.Value) + functionTarget := code.NewBuilder() functionTarget.PutMarker(marker) @@ -144,6 +117,18 @@ func (comp *Compiler) compileFnDeclStmt(t *code.Builder, fnDeclStmt ast.StmtFnDe comp.funcs = append(comp.funcs, &functionTarget) + // Put the function value on the stack + + t.AppendOp(code.OpPushFunction) + t.AppendMarkerReference(marker) + + if len(fnDeclStmt.Args) != 0 { + t.AppendOp(code.OpSetArgCount) + t.AppendInt(int64(len(fnDeclStmt.Args))) + } + + // TODO: Attach the function environment + return nil } @@ -654,16 +639,6 @@ func (comp *Compiler) compileIdentExpr(t *code.Builder, expr ast.ExprIdent) erro t.AppendOp(code.OpGetLocal) t.AppendInt(int64(symbol.Data().LocalIndex())) - case scope.SymbolKindFunction: - symbol := comp.scopes.GetFunction(symbolId) - - t.AppendOp(code.OpPushFunction) - t.AppendMarkerReference(symbol.Data().Marker()) - - if symbol.Data().Args() != 0 { - t.AppendOp(code.OpSetArgCount) - t.AppendInt(int64(symbol.Data().Args())) - } default: panic(fmt.Errorf("unknown symbol kind: %v", symbolId.SymbolKind())) } diff --git a/pkg/lang/compiler/compiler_test.go b/pkg/lang/compiler/compiler_test.go index b8a6264..07586a6 100644 --- a/pkg/lang/compiler/compiler_test.go +++ b/pkg/lang/compiler/compiler_test.go @@ -434,15 +434,17 @@ func TestForIn(t *testing.T) { func TestSimpleFunction(t *testing.T) { src := ` - var result = the_meaning_of_life() - fn the_meaning_of_life() { return 42 } + + var result = the_meaning_of_life() ` expected := ` push_function @the_meaning_of_life + + get_local 0 call 0 halt @@ -466,6 +468,8 @@ func TestFunctionArgs(t *testing.T) { expected := ` push_function @add set_arg_count 2 + + get_local 0 push_int 4 push_int 5 call 2 diff --git a/pkg/lang/compiler/scope/scope_chain.go b/pkg/lang/compiler/scope/scope_chain.go index d0108ee..f4b9f46 100644 --- a/pkg/lang/compiler/scope/scope_chain.go +++ b/pkg/lang/compiler/scope/scope_chain.go @@ -137,35 +137,6 @@ func (sc *ScopeChain) Declare(name string) (int, bool) { return indexInScope, true } -func (sc *ScopeChain) DeclareFunction(name string, args uint) (code.Marker, bool) { - if _, ok := sc.nameToSymbol[name]; ok { - return "", false - } - - current := sc.Current() - index := len(current.functionSymbols) - - symbolID := SymbolID{ - symbolKind: SymbolKindFunction, - scopeID: sc.CurrentScopeID(), - indexInScope: index, - } - - unitName := sc.CreateFunctionSubUnit(name) - - current.functionSymbols = append(current.functionSymbols, Symbol[SymbolFunction]{ - name: name, - data: SymbolFunction{ - marker: unitName, - args: args, - }, - }) - - sc.nameToSymbol[name] = symbolID - - return unitName, true -} - func (sc *ScopeChain) DeclareAnonymous() int { current := sc.Current() index := len(current.variableSymbols) @@ -221,11 +192,3 @@ func (sc *ScopeChain) GetVariable(id SymbolID) Symbol[SymbolVariable] { return sc.symbolScopes[id.scopeID].variableSymbols[id.indexInScope] } - -func (sc *ScopeChain) GetFunction(id SymbolID) Symbol[SymbolFunction] { - if id.symbolKind != SymbolKindFunction { - panic("incorrect symbol id kind given") - } - - return sc.symbolScopes[id.scopeID].functionSymbols[id.indexInScope] -} diff --git a/pkg/lang/compiler/scope/scopes.go b/pkg/lang/compiler/scope/scopes.go index 5cfcd5d..e34b45a 100644 --- a/pkg/lang/compiler/scope/scopes.go +++ b/pkg/lang/compiler/scope/scopes.go @@ -14,13 +14,11 @@ const ( type SymbolScope struct { variableSymbols []Symbol[SymbolVariable] - functionSymbols []Symbol[SymbolFunction] } func NewSymbolScope() SymbolScope { return SymbolScope{ variableSymbols: make([]Symbol[SymbolVariable], 0), - functionSymbols: make([]Symbol[SymbolFunction], 0), } } diff --git a/pkg/lang/compiler/scope/symbol.go b/pkg/lang/compiler/scope/symbol.go index df91899..3b50108 100644 --- a/pkg/lang/compiler/scope/symbol.go +++ b/pkg/lang/compiler/scope/symbol.go @@ -1,7 +1,5 @@ package scope -import "jinx/pkg/lang/vm/code" - type SymbolID struct { symbolKind SymbolKind scopeID ScopeID @@ -15,16 +13,14 @@ func (id SymbolID) SymbolKind() SymbolKind { type SymbolKind int const ( + // A variable symbol is bound to a local on the stack. SymbolKindVariable SymbolKind = iota - SymbolKindFunction ) func (s SymbolKind) String() string { switch s { case SymbolKindVariable: return "variable" - case SymbolKindFunction: - return "function" default: panic("unknown symbol kind") } @@ -40,7 +36,7 @@ func (s Symbol[D]) Data() D { } type SymbolData interface { - SymbolVariable | SymbolFunction + SymbolVariable } type SymbolVariable struct { @@ -50,16 +46,3 @@ type SymbolVariable struct { func (sv SymbolVariable) LocalIndex() int { return sv.localIndex } - -type SymbolFunction struct { - marker code.Marker - args uint -} - -func (sf SymbolFunction) Marker() code.Marker { - return sf.marker -} - -func (sf SymbolFunction) Args() uint { - return sf.args -} -- cgit 1.4.1