diff --git a/bootstrap/pkg/core/call_stack.go b/bootstrap/pkg/core/call_stack.go new file mode 100644 index 0000000..51b5883 --- /dev/null +++ b/bootstrap/pkg/core/call_stack.go @@ -0,0 +1,129 @@ +/* + Serene --- Yet an other Lisp + +Copyright (c) 2020 Sameer Rahmani + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +package core + +// CallStack implementation: +// * A callstack should be FIFA stack +// * It should keep track of function calls. +// * Anything that implements `IFn` can be tracked by the call stack +// * Since Serene uses eval loop to eliminate tail calls we need +// the call stack to be able to track recursive calls. For +// now by just counting number of calls to a functions that is already +// in the stack. +// +// TODOs: +// * At the moment if we call the same function twice (not as a recursive) +// function call stack will record it as a recursive call. We need +// compare the stack items by their address, identity and location. +// * Add support for iteration on the stack. + +import "fmt" + +type ICallStack interface { + // Push the given callable `f` to the stack + Push(f IFn) IError + Pop() FnCall + Count() uint +} + +type FnCall struct { + Fn IFn + // Number of recursive calls to this function + count uint +} + +type CallStackItem struct { + prev *CallStackItem + data FnCall +} + +type CallStack struct { + head *CallStackItem + count uint + debug bool +} + +func (c *CallStack) Count() uint { + return c.count +} + +func (c *CallStack) Push(f IFn) IError { + + if c.debug { + fmt.Println("[Stack] -->", f) + } + + if f == nil { + return MakePlainError("Can't push 'nil' pointer to the call stack.") + } + + // Empty Stack + if c.head == nil { + c.head = &CallStackItem{ + data: FnCall{ + Fn: f, + count: 0, + }, + } + c.count++ + } + + nodeData := &c.head.data + + // If the same function was on top of the stack + if nodeData.Fn == f { + // TODO: expand the check here to support address and location as well + nodeData.count++ + } else { + c.head = &CallStackItem{ + prev: c.head, + data: FnCall{ + Fn: f, + count: 0, + }, + } + c.count++ + } + return nil +} + +func (c *CallStack) Pop() *FnCall { + if c.head == nil { + if c.debug { + fmt.Println("[Stack] <-- nil") + } + return nil + } + + result := c.head + c.head = result.prev + c.count-- + if c.debug { + fmt.Printf("[Stack] <-- %s\n", result.data.Fn) + } + return &result.data +} + +func MakeCallStack(debugMode bool) CallStack { + return CallStack{ + count: 0, + head: nil, + debug: debugMode, + } +}