[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:
parent
9dfe54a573
commit
1447f8ac45
|
@ -29,7 +29,7 @@ var replCmd = &cobra.Command{
|
||||||
Long: `Runs the local Serene's REPL to interact with Serene`,
|
Long: `Runs the local Serene's REPL to interact with Serene`,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
// TODO: Get the debug value from a CLI flag
|
// TODO: Get the debug value from a CLI flag
|
||||||
core.REPL(debugMode)
|
core.REPL(makeFlags())
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,14 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var debugMode bool
|
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
|
// rootCmd represents the base command when called without any subcommands
|
||||||
var rootCmd = &cobra.Command{
|
var rootCmd = &cobra.Command{
|
||||||
|
@ -62,4 +70,10 @@ func init() {
|
||||||
false,
|
false,
|
||||||
"Turns on the debug mode.")
|
"Turns on the debug mode.")
|
||||||
|
|
||||||
|
rootCmd.PersistentFlags().BoolVar(
|
||||||
|
&stackDebugMode,
|
||||||
|
"debug-stack",
|
||||||
|
false,
|
||||||
|
"Turns on the call stack debug mode.")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ var runCmd = &cobra.Command{
|
||||||
Short: "Evaluates the given NS and runs the main function of it",
|
Short: "Evaluates the given NS and runs the main function of it",
|
||||||
Long: `Evaluates the given NS and runs the main function`,
|
Long: `Evaluates the given NS and runs the main function`,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
core.Run(debugMode, args)
|
core.Run(makeFlags(), args)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -55,13 +55,13 @@ Replace the readline implementation with go-prompt.
|
||||||
|
|
||||||
// REPL executes a Read Eval Print Loop locally reading from stdin and
|
// REPL executes a Read Eval Print Loop locally reading from stdin and
|
||||||
// writing to stdout
|
// writing to stdout
|
||||||
func REPL(debug bool) {
|
func REPL(flags map[string]bool) {
|
||||||
cwd, err := os.Getwd()
|
cwd, err := os.Getwd()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
rt := MakeRuntime([]string{cwd}, debug)
|
rt := MakeRuntime([]string{cwd}, flags)
|
||||||
|
|
||||||
rt.CreateNS("user", "REPL", true)
|
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()
|
cwd, e := os.Getwd()
|
||||||
if e != nil {
|
if e != nil {
|
||||||
panic(e)
|
panic(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
rt := MakeRuntime([]string{cwd}, debug)
|
rt := MakeRuntime([]string{cwd}, flags)
|
||||||
|
|
||||||
if len(args) == 0 {
|
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))
|
_, err = mainFn.Apply(rt, loadedNS.GetRootScope(), mainFn.Node, MakeList(fnArgs))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
PrintError(rt, err)
|
PrintError(rt, err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// rt.Stack.Pop()
|
||||||
|
// if rt.Stack.Count() != 0 {
|
||||||
|
// panic("Call stack is not empty.")
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,6 +40,7 @@ func evalForm(rt *Runtime, scope IScope, form IExpr) (IExpr, IError) {
|
||||||
switch form.GetType() {
|
switch form.GetType() {
|
||||||
case ast.Nil:
|
case ast.Nil:
|
||||||
return form, nil
|
return form, nil
|
||||||
|
|
||||||
case ast.Number:
|
case ast.Number:
|
||||||
return form, nil
|
return form, nil
|
||||||
|
|
||||||
|
@ -195,7 +196,19 @@ tco:
|
||||||
|
|
||||||
if rt.IsDebugMode() {
|
if rt.IsDebugMode() {
|
||||||
fmt.Printf("[DEBUG] Evaluating forms in NS: %s, Forms: %s\n", rt.CurrentNS().GetName(), forms)
|
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
|
// Evaluating forms one by one
|
||||||
|
@ -533,7 +546,8 @@ tco:
|
||||||
break body //return
|
break body //return
|
||||||
}
|
}
|
||||||
|
|
||||||
body := fn.GetBody().ToSlice()
|
rt.Stack.Push(fn)
|
||||||
|
body := append(fn.GetBody().ToSlice(), MakeStackPop(rt))
|
||||||
changeExecutionScope(body, fnScope)
|
changeExecutionScope(body, fnScope)
|
||||||
exprs = append(body, restOfExprs(exprs, i)...)
|
exprs = append(body, restOfExprs(exprs, i)...)
|
||||||
goto body // rewrite
|
goto body // rewrite
|
||||||
|
@ -542,12 +556,14 @@ tco:
|
||||||
// by the `NativeFunction` struct
|
// by the `NativeFunction` struct
|
||||||
case ast.NativeFn:
|
case ast.NativeFn:
|
||||||
fn := f.(*NativeFunction)
|
fn := f.(*NativeFunction)
|
||||||
|
rt.Stack.Push(fn)
|
||||||
ret, err = fn.Apply(
|
ret, err = fn.Apply(
|
||||||
rt,
|
rt,
|
||||||
scope,
|
scope,
|
||||||
MakeNodeFromExpr(fn),
|
MakeNodeFromExpr(fn),
|
||||||
listExprs.(*List),
|
listExprs.(*List),
|
||||||
)
|
)
|
||||||
|
rt.Stack.Pop()
|
||||||
continue body // no rewrite
|
continue body // no rewrite
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -30,7 +30,7 @@ package core
|
||||||
// arguments names
|
// arguments names
|
||||||
// - Evaluate the body of the function in context of the new scope and return
|
// - Evaluate the body of the function in context of the new scope and return
|
||||||
// the result of the last expression
|
// 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.
|
// interface which is quite simple.
|
||||||
//
|
//
|
||||||
// TODOs:
|
// TODOs:
|
||||||
|
@ -47,7 +47,8 @@ import (
|
||||||
|
|
||||||
type nativeFnHandler = func(rt *Runtime, scope IScope, n Node, args *List) (IExpr, IError)
|
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)
|
Apply(rt *Runtime, scope IScope, n Node, args *List) (IExpr, IError)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -72,6 +72,7 @@ type Runtime struct {
|
||||||
// languages
|
// languages
|
||||||
paths []string
|
paths []string
|
||||||
|
|
||||||
|
Stack CallStack
|
||||||
// A to turn on the verbose mode, FOR DEVELOPMENT USE ONLY
|
// A to turn on the verbose mode, FOR DEVELOPMENT USE ONLY
|
||||||
debugMode bool
|
debugMode bool
|
||||||
}
|
}
|
||||||
|
@ -201,12 +202,13 @@ func (r *Runtime) LookupBuiltin(k string) IExpr {
|
||||||
// MakeRuntime creates a Runtime and returns a pointer to it. Any
|
// MakeRuntime creates a Runtime and returns a pointer to it. Any
|
||||||
// runtime initialization such as adding default namespaces and vice
|
// runtime initialization such as adding default namespaces and vice
|
||||||
// versa has to happen here.
|
// versa has to happen here.
|
||||||
func MakeRuntime(paths []string, debug bool) *Runtime {
|
func MakeRuntime(paths []string, flags map[string]bool) *Runtime {
|
||||||
rt := Runtime{
|
rt := Runtime{
|
||||||
namespaces: map[string]Namespace{},
|
namespaces: map[string]Namespace{},
|
||||||
currentNS: "",
|
currentNS: "",
|
||||||
debugMode: debug,
|
debugMode: flags["debugMode"],
|
||||||
paths: paths,
|
paths: paths,
|
||||||
|
Stack: MakeCallStack(flags["stackDebugMode"]),
|
||||||
}
|
}
|
||||||
|
|
||||||
rt.builtins = BUILTINS
|
rt.builtins = BUILTINS
|
||||||
|
|
Loading…
Reference in New Issue