[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.
This commit is contained in:
Sameer Rahmani 2020-12-25 22:03:21 +00:00
parent 9dfe54a573
commit 1447f8ac45
7 changed files with 51 additions and 12 deletions

View File

@ -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())
},
}

View File

@ -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.")
}

View File

@ -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)
},
}

View File

@ -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.")
// }
}

View File

@ -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:

View File

@ -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)
}

View File

@ -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