From 1447f8ac456def7bb3e2cc41bff960e7c0bcd2b2 Mon Sep 17 00:00:00 2001 From: Sameer Rahmani Date: Fri, 25 Dec 2020 22:03:21 +0000 Subject: [PATCH] [Bootstrap] Implement a call stack to keep track of function calls Add `ICallStack` as the interface to the call stack with a simple FIFO implementation that tracks the recursive calls as well. --- bootstrap/cmd/repl.go | 2 +- bootstrap/cmd/root.go | 14 ++++++++++++++ bootstrap/cmd/run.go | 2 +- bootstrap/pkg/core/core.go | 14 ++++++++++---- bootstrap/pkg/core/eval.go | 20 ++++++++++++++++++-- bootstrap/pkg/core/function.go | 5 +++-- bootstrap/pkg/core/runtime.go | 6 ++++-- 7 files changed, 51 insertions(+), 12 deletions(-) diff --git a/bootstrap/cmd/repl.go b/bootstrap/cmd/repl.go index 30acd00..47429aa 100644 --- a/bootstrap/cmd/repl.go +++ b/bootstrap/cmd/repl.go @@ -29,7 +29,7 @@ var replCmd = &cobra.Command{ Long: `Runs the local Serene's REPL to interact with Serene`, Run: func(cmd *cobra.Command, args []string) { // TODO: Get the debug value from a CLI flag - core.REPL(debugMode) + core.REPL(makeFlags()) }, } diff --git a/bootstrap/cmd/root.go b/bootstrap/cmd/root.go index 9a187f4..5c618e5 100644 --- a/bootstrap/cmd/root.go +++ b/bootstrap/cmd/root.go @@ -25,6 +25,14 @@ import ( ) var debugMode bool +var stackDebugMode bool + +func makeFlags() map[string]bool { + return map[string]bool{ + "debugMode": debugMode, + "stackDebugMode": stackDebugMode, + } +} // rootCmd represents the base command when called without any subcommands var rootCmd = &cobra.Command{ @@ -62,4 +70,10 @@ func init() { false, "Turns on the debug mode.") + rootCmd.PersistentFlags().BoolVar( + &stackDebugMode, + "debug-stack", + false, + "Turns on the call stack debug mode.") + } diff --git a/bootstrap/cmd/run.go b/bootstrap/cmd/run.go index 053dd7b..1e05381 100644 --- a/bootstrap/cmd/run.go +++ b/bootstrap/cmd/run.go @@ -28,7 +28,7 @@ var runCmd = &cobra.Command{ Short: "Evaluates the given NS and runs the main function of it", Long: `Evaluates the given NS and runs the main function`, Run: func(cmd *cobra.Command, args []string) { - core.Run(debugMode, args) + core.Run(makeFlags(), args) }, } diff --git a/bootstrap/pkg/core/core.go b/bootstrap/pkg/core/core.go index f95904b..e5aa750 100644 --- a/bootstrap/pkg/core/core.go +++ b/bootstrap/pkg/core/core.go @@ -55,13 +55,13 @@ Replace the readline implementation with go-prompt. // REPL executes a Read Eval Print Loop locally reading from stdin and // writing to stdout -func REPL(debug bool) { +func REPL(flags map[string]bool) { cwd, err := os.Getwd() if err != nil { panic(err) } - rt := MakeRuntime([]string{cwd}, debug) + rt := MakeRuntime([]string{cwd}, flags) rt.CreateNS("user", "REPL", true) @@ -105,13 +105,13 @@ for details take a look at the LICENSE file. } -func Run(debug bool, args []string) { +func Run(flags map[string]bool, args []string) { cwd, e := os.Getwd() if e != nil { panic(e) } - rt := MakeRuntime([]string{cwd}, debug) + rt := MakeRuntime([]string{cwd}, flags) if len(args) == 0 { @@ -171,10 +171,16 @@ func Run(debug bool, args []string) { } } + //rt.Stack.Push(mainFn) _, err = mainFn.Apply(rt, loadedNS.GetRootScope(), mainFn.Node, MakeList(fnArgs)) if err != nil { PrintError(rt, err) os.Exit(1) } + + // rt.Stack.Pop() + // if rt.Stack.Count() != 0 { + // panic("Call stack is not empty.") + // } } diff --git a/bootstrap/pkg/core/eval.go b/bootstrap/pkg/core/eval.go index e26bfa0..d874e15 100644 --- a/bootstrap/pkg/core/eval.go +++ b/bootstrap/pkg/core/eval.go @@ -40,6 +40,7 @@ func evalForm(rt *Runtime, scope IScope, form IExpr) (IExpr, IError) { switch form.GetType() { case ast.Nil: return form, nil + case ast.Number: return form, nil @@ -195,7 +196,19 @@ tco: if rt.IsDebugMode() { fmt.Printf("[DEBUG] Evaluating forms in NS: %s, Forms: %s\n", rt.CurrentNS().GetName(), forms) - fmt.Printf("[DEBUG] -> State: I: %d, Exprs: %s\n", i, exprs) + fmt.Printf("[DEBUG] * State: I: %d, Exprs: %s\n", i, exprs) + } + + // Evaluate any internal instruction that has to be run. + // Instructions should change the return value, but errors + // are ok + if forms.GetType() == ast.Instruction { + err := ProcessInstruction(rt, forms.(*Instruction)) + if err != nil { + return nil, err + } + + continue } // Evaluating forms one by one @@ -533,7 +546,8 @@ tco: break body //return } - body := fn.GetBody().ToSlice() + rt.Stack.Push(fn) + body := append(fn.GetBody().ToSlice(), MakeStackPop(rt)) changeExecutionScope(body, fnScope) exprs = append(body, restOfExprs(exprs, i)...) goto body // rewrite @@ -542,12 +556,14 @@ tco: // by the `NativeFunction` struct case ast.NativeFn: fn := f.(*NativeFunction) + rt.Stack.Push(fn) ret, err = fn.Apply( rt, scope, MakeNodeFromExpr(fn), listExprs.(*List), ) + rt.Stack.Pop() continue body // no rewrite default: diff --git a/bootstrap/pkg/core/function.go b/bootstrap/pkg/core/function.go index dfac79b..b183b1b 100644 --- a/bootstrap/pkg/core/function.go +++ b/bootstrap/pkg/core/function.go @@ -30,7 +30,7 @@ package core // arguments names // - Evaluate the body of the function in context of the new scope and return // the result of the last expression -// * Native functions evaluates by calling the `Apply` method of the `INativeFn` +// * Native functions evaluates by calling the `Apply` method of the `IFn` // interface which is quite simple. // // TODOs: @@ -47,7 +47,8 @@ import ( type nativeFnHandler = func(rt *Runtime, scope IScope, n Node, args *List) (IExpr, IError) -type INativeFn interface { +type IFn interface { + ast.ILocatable Apply(rt *Runtime, scope IScope, n Node, args *List) (IExpr, IError) } diff --git a/bootstrap/pkg/core/runtime.go b/bootstrap/pkg/core/runtime.go index 8ae8783..9aab2cf 100644 --- a/bootstrap/pkg/core/runtime.go +++ b/bootstrap/pkg/core/runtime.go @@ -72,6 +72,7 @@ type Runtime struct { // languages paths []string + Stack CallStack // A to turn on the verbose mode, FOR DEVELOPMENT USE ONLY debugMode bool } @@ -201,12 +202,13 @@ func (r *Runtime) LookupBuiltin(k string) IExpr { // MakeRuntime creates a Runtime and returns a pointer to it. Any // runtime initialization such as adding default namespaces and vice // versa has to happen here. -func MakeRuntime(paths []string, debug bool) *Runtime { +func MakeRuntime(paths []string, flags map[string]bool) *Runtime { rt := Runtime{ namespaces: map[string]Namespace{}, currentNS: "", - debugMode: debug, + debugMode: flags["debugMode"], paths: paths, + Stack: MakeCallStack(flags["stackDebugMode"]), } rt.builtins = BUILTINS