[Bootstrap] Fix the order and location on the traceback
In order to fix this issue I had to make many minor tweaks including reordering the traceback and the way we pass down nodes.
This commit is contained in:
parent
0cc7646e3a
commit
c2d4273319
|
@ -63,7 +63,7 @@ func (s *Source) GetPos(start, end int) *string {
|
|||
}
|
||||
func (s *Source) GetLine(linenum int) string {
|
||||
lines := strings.Split(strings.Join(*s.Buffer, ""), "\n")
|
||||
if linenum > 0 && linenum < len(lines) {
|
||||
if linenum > 0 && linenum <= len(lines) {
|
||||
return lines[linenum-1]
|
||||
}
|
||||
return "----"
|
||||
|
@ -107,6 +107,21 @@ func (s *Source) LineNumberFor(pos int) int {
|
|||
return result
|
||||
}
|
||||
|
||||
var builtinSource *Source
|
||||
|
||||
func GetBuiltinSource() *Source {
|
||||
if builtinSource == nil {
|
||||
buf := strings.Split("builtin", "")
|
||||
lineindex := []int{len(buf) - 1}
|
||||
builtinSource = &Source{
|
||||
Buffer: &buf,
|
||||
Path: "Builtin",
|
||||
LineIndex: &lineindex,
|
||||
}
|
||||
}
|
||||
return builtinSource
|
||||
}
|
||||
|
||||
type Location struct {
|
||||
start int
|
||||
end int
|
||||
|
@ -125,7 +140,10 @@ func (l *Location) GetEnd() int {
|
|||
}
|
||||
|
||||
func (l *Location) GetSource() *Source {
|
||||
if l.IsKnownLocaiton() {
|
||||
return &l.source
|
||||
}
|
||||
return GetBuiltinSource()
|
||||
}
|
||||
|
||||
func (l *Location) IncStart(x int) {
|
||||
|
|
|
@ -22,10 +22,10 @@ package core
|
|||
// the language which are implemented in Go
|
||||
var BUILTINS = map[string]NativeFunction{
|
||||
"pr": MakeNativeFn("pr", PrNativeFn),
|
||||
"prn": MakeNativeFn("pr", PrnNativeFn),
|
||||
"prn": MakeNativeFn("prn", PrnNativeFn),
|
||||
"print": MakeNativeFn("print", PrintNativeFn),
|
||||
"println": MakeNativeFn("println", PrintlnNativeFn),
|
||||
"require": MakeNativeFn("print", RequireNativeFn),
|
||||
"require": MakeNativeFn("require", RequireNativeFn),
|
||||
"hash": MakeNativeFn("hash", HashNativeFn),
|
||||
}
|
||||
|
||||
|
|
|
@ -65,6 +65,10 @@ type CallStack struct {
|
|||
count uint
|
||||
}
|
||||
|
||||
func (f *Frame) String() string {
|
||||
return fmt.Sprintf("<Frame: FN: %s, Count: %d Caller: \n%s\n>", f.Fn, f.Count, f.Caller)
|
||||
}
|
||||
|
||||
func (c *CallStack) Count() uint {
|
||||
return c.count
|
||||
}
|
||||
|
@ -158,7 +162,8 @@ func (c *CallStack) ToTraceBack() *TraceBack {
|
|||
if item == nil {
|
||||
break
|
||||
}
|
||||
tr = append(tr, &item.data)
|
||||
// TODO: This doesn't seem efficient. Fix it.
|
||||
tr = append([]*Frame{&item.data}, tr...)
|
||||
item = item.prev
|
||||
}
|
||||
|
||||
|
|
|
@ -82,19 +82,20 @@ func evalForm(rt *Runtime, scope IScope, form IExpr) (IExpr, IError) {
|
|||
return MakeNil(MakeNodeFromExpr(form)), nil
|
||||
default:
|
||||
var expr *Binding
|
||||
ns := scope.GetNS(rt)
|
||||
if sym.IsNSQualified() {
|
||||
// Whether a namespace with the given alias loaded or not
|
||||
if !rt.CurrentNS().hasExternal(sym.GetNSPart()) {
|
||||
if !ns.hasExternal(sym.GetNSPart()) {
|
||||
return nil, MakeError(rt, sym,
|
||||
fmt.Sprintf("Namespace '%s' is no loaded", sym.GetNSPart()),
|
||||
)
|
||||
}
|
||||
|
||||
expr = rt.CurrentNS().LookupGlobal(rt, sym)
|
||||
expr = ns.LookupGlobal(rt, sym)
|
||||
nsName = sym.GetNSPart()
|
||||
} else {
|
||||
expr = scope.Lookup(rt, symbolName)
|
||||
nsName = rt.CurrentNS().GetName()
|
||||
nsName = ns.GetName()
|
||||
}
|
||||
|
||||
if expr == nil {
|
||||
|
@ -199,7 +200,7 @@ tco:
|
|||
}
|
||||
|
||||
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", scope.GetNS(rt).GetName(), forms)
|
||||
fmt.Printf("[DEBUG] * State: I: %d, Exprs: %s\n", i, exprs)
|
||||
}
|
||||
|
||||
|
@ -394,7 +395,7 @@ tco:
|
|||
// * The rest of the arguments will form a block that acts as the
|
||||
// body of the macro.
|
||||
case "defmacro":
|
||||
ret, err = DefMacro(rt, scope, list.Rest().(*List))
|
||||
ret, err = DefMacro(rt, scope, list)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -511,7 +512,7 @@ tco:
|
|||
return nil, MakeError(rt, list, "'let' needs at list 1 aruments")
|
||||
}
|
||||
|
||||
letScope := MakeScope(scope.(*Scope))
|
||||
letScope := MakeScope(rt, scope.(*Scope), nil)
|
||||
|
||||
// Since we're using IColl for the bindings, we can use either lists
|
||||
// or vectors or even hashmaps for bindings
|
||||
|
@ -565,6 +566,7 @@ tco:
|
|||
default:
|
||||
// Evaluating all the elements of the list
|
||||
listExprs, e := evalForm(rt, scope, list)
|
||||
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
|
@ -589,8 +591,8 @@ tco:
|
|||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
|
||||
rt.Stack.Push(list, fn)
|
||||
|
||||
body := append(
|
||||
fn.GetBody().ToSlice(),
|
||||
// Add the PopStack instruction to clean up the stack after
|
||||
|
@ -607,6 +609,7 @@ tco:
|
|||
fn := f.(*NativeFunction)
|
||||
|
||||
rt.Stack.Push(list, fn)
|
||||
|
||||
ret, err = fn.Apply(
|
||||
rt,
|
||||
scope,
|
||||
|
|
|
@ -157,7 +157,7 @@ func MakeFunction(n Node, scope IScope, params IColl, body *Block) *Function {
|
|||
// MakeFnScope a new scope for the body of a function. It binds the `bindings`
|
||||
// to the given `values`.
|
||||
func MakeFnScope(rt *Runtime, parent IScope, bindings IColl, values IColl) (*Scope, IError) {
|
||||
scope := MakeScope(parent.(*Scope))
|
||||
scope := MakeScope(rt, parent.(*Scope), nil)
|
||||
// TODO: Implement destructuring
|
||||
binds := bindings.ToSlice()
|
||||
exprs := values.ToSlice()
|
||||
|
@ -169,6 +169,16 @@ func MakeFnScope(rt *Runtime, parent IScope, bindings IColl, values IColl) (*Sco
|
|||
if lastBinding.GetType() == ast.Symbol && lastBinding.(*Symbol).IsRestable() {
|
||||
numberOfBindings = len(binds) - 1
|
||||
}
|
||||
|
||||
if lastBinding.GetType() == ast.Symbol && !lastBinding.(*Symbol).IsRestable() && numberOfBindings < len(exprs) {
|
||||
return nil, MakeSemanticError(
|
||||
rt,
|
||||
values.(IExpr),
|
||||
errors.E0002,
|
||||
fmt.Sprintf("expected '%d' arguments, got '%d'.", bindings.Count(), values.Count()),
|
||||
)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if numberOfBindings > len(exprs) {
|
||||
|
@ -201,7 +211,7 @@ func MakeFnScope(rt *Runtime, parent IScope, bindings IColl, values IColl) (*Sco
|
|||
// next.
|
||||
rest := MakeEmptyList(MakeNodeFromExpr(binds[i]))
|
||||
|
||||
if i == len(exprs)-1 {
|
||||
if i <= len(exprs)-1 {
|
||||
// If the number of values matches the number of bindings
|
||||
// or it is more than that create a list from them
|
||||
// to pass it to the last argument that has to be Restable (e.g &x)
|
||||
|
@ -260,6 +270,7 @@ func (f *NativeFunction) Apply(rt *Runtime, scope IScope, n Node, args *List) (I
|
|||
|
||||
func MakeNativeFn(name string, f nativeFnHandler) NativeFunction {
|
||||
return NativeFunction{
|
||||
Node: MakeNodeFromLocation(ast.MakeUnknownLocation()),
|
||||
name: name,
|
||||
fn: f,
|
||||
}
|
||||
|
|
|
@ -18,7 +18,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
|
||||
package core
|
||||
|
||||
import "serene-lang.org/bootstrap/pkg/ast"
|
||||
// TODO:
|
||||
// * Add support for `before` and `after` state in macroexpantion
|
||||
// and call stack. So in case of an error. Users should be able
|
||||
// to see the forms before and after expansion.
|
||||
|
||||
import (
|
||||
"serene-lang.org/bootstrap/pkg/ast"
|
||||
)
|
||||
|
||||
// Serene macros are in fact functions with the `isMacro` flag set to true.
|
||||
// We have only normal macro implementation in bootstrap version of serene in
|
||||
|
@ -84,6 +91,7 @@ func macroexpand(rt *Runtime, scope IScope, form IExpr) (IExpr, IError) {
|
|||
var macro *Function
|
||||
var e IError
|
||||
ok := false
|
||||
//form := expr
|
||||
|
||||
for {
|
||||
macro, ok = isMacroCall(rt, scope, form)
|
||||
|
|
|
@ -160,7 +160,7 @@ func requireNS(rt *Runtime, ns *Symbol) (*Namespace, IError) {
|
|||
)
|
||||
}
|
||||
|
||||
namespace := MakeNS(ns.GetName(), source)
|
||||
namespace := MakeNS(rt, ns.GetName(), source)
|
||||
namespace.setForms(body)
|
||||
|
||||
return &namespace, nil
|
||||
|
@ -252,8 +252,9 @@ func RequireNamespace(rt *Runtime, namespace IExpr) (IExpr, IError) {
|
|||
}
|
||||
|
||||
// MakeNS creates a new namespace with the given `name` and `source`
|
||||
func MakeNS(name string, source string) Namespace {
|
||||
s := MakeScope(nil)
|
||||
func MakeNS(rt *Runtime, name string, source string) Namespace {
|
||||
s := MakeScope(rt, nil, &name)
|
||||
|
||||
return Namespace{
|
||||
name: name,
|
||||
rootScope: *s,
|
||||
|
|
|
@ -158,7 +158,7 @@ func (d Double) F64() float64 {
|
|||
return d.value
|
||||
}
|
||||
|
||||
func MakeNumberFromStr(strValue string, isDouble bool) (INumber, error) {
|
||||
func MakeNumberFromStr(n Node, strValue string, isDouble bool) (INumber, error) {
|
||||
var ret INumber
|
||||
|
||||
if isDouble {
|
||||
|
@ -169,6 +169,7 @@ func MakeNumberFromStr(strValue string, isDouble bool) (INumber, error) {
|
|||
}
|
||||
|
||||
ret = Double{
|
||||
Node: n,
|
||||
value: v,
|
||||
}
|
||||
} else {
|
||||
|
@ -178,6 +179,7 @@ func MakeNumberFromStr(strValue string, isDouble bool) (INumber, error) {
|
|||
}
|
||||
|
||||
ret = Integer{
|
||||
Node: n,
|
||||
value: v,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -341,8 +341,9 @@ func readNumber(parser IParsable, neg bool) (IExpr, IError) {
|
|||
break
|
||||
}
|
||||
}
|
||||
|
||||
value, err := MakeNumberFromStr(result, isDouble)
|
||||
n := MakeSinglePointNode(parser.GetSource(), parser.GetLocation())
|
||||
n.location.DecStart(len(result))
|
||||
value, err := MakeNumberFromStr(n, result, isDouble)
|
||||
|
||||
if err != nil {
|
||||
return nil, makeErrorFromError(parser, err)
|
||||
|
@ -425,6 +426,7 @@ func readList(parser IParsable) (IExpr, IError) {
|
|||
|
||||
node.location.DecStart(1)
|
||||
node.location.IncEnd(1)
|
||||
|
||||
return MakeList(*node, list), nil
|
||||
}
|
||||
|
||||
|
|
|
@ -85,9 +85,10 @@ func printError(rt *Runtime, err IError, stage int) {
|
|||
}
|
||||
|
||||
color.Yellow.Printf(
|
||||
"%d: At '%s'\n",
|
||||
"%d: At '%s':%d\n",
|
||||
stage,
|
||||
source.Path,
|
||||
source.LineNumberFor(loc.GetStart()),
|
||||
)
|
||||
|
||||
color.White.Printf("%s\n", lines)
|
||||
|
@ -115,11 +116,17 @@ func printErrorWithTraceBack(rt *Runtime, err IError) {
|
|||
|
||||
var lines string
|
||||
for i := startline; i <= endline; i++ {
|
||||
fLoc := t.Fn.GetLocation()
|
||||
fLoc := t.Caller.GetLocation()
|
||||
|
||||
if fLoc.IsKnownLocaiton() {
|
||||
line := fLoc.GetSource().GetLine(i)
|
||||
if line != "----" {
|
||||
lines += fmt.Sprintf("%d:\t%s\n", i, line)
|
||||
}
|
||||
} else {
|
||||
lines += "Builtin\n"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
color.Yellow.Printf(
|
||||
|
@ -137,6 +144,7 @@ func printErrorWithTraceBack(rt *Runtime, err IError) {
|
|||
}
|
||||
|
||||
func PrintError(rt *Runtime, err IError) {
|
||||
|
||||
switch err.GetErrType() {
|
||||
case SyntaxError, SemanticError:
|
||||
printError(rt, err, 0)
|
||||
|
|
|
@ -115,7 +115,7 @@ func (r *Runtime) GetNS(ns string) (*Namespace, bool) {
|
|||
// CreateNS is a helper function to create a namespace and set it to be
|
||||
// the current namespace of the runtime. `MakeNS` is much preferred
|
||||
func (r *Runtime) CreateNS(name string, source string, setAsCurrent bool) {
|
||||
ns := MakeNS(name, source)
|
||||
ns := MakeNS(r, name, source)
|
||||
|
||||
if setAsCurrent {
|
||||
r.currentNS = name
|
||||
|
|
|
@ -23,6 +23,8 @@ import "fmt"
|
|||
type IScope interface {
|
||||
Lookup(rt *Runtime, k string) *Binding
|
||||
Insert(k string, v IExpr, public bool)
|
||||
GetNS(rt *Runtime) *Namespace
|
||||
SetNS(ns *string)
|
||||
}
|
||||
|
||||
type Binding struct {
|
||||
|
@ -33,6 +35,7 @@ type Binding struct {
|
|||
type Scope struct {
|
||||
bindings map[string]Binding
|
||||
parent *Scope
|
||||
ns *string
|
||||
}
|
||||
|
||||
func (s *Scope) Lookup(rt *Runtime, k string) *Binding {
|
||||
|
@ -42,6 +45,9 @@ func (s *Scope) Lookup(rt *Runtime, k string) *Binding {
|
|||
|
||||
v, ok := s.bindings[k]
|
||||
if ok {
|
||||
if rt.IsDebugMode() {
|
||||
fmt.Printf("[DEBUG] Found '%s': '%s'\n", k, v.Value.String())
|
||||
}
|
||||
return &v
|
||||
}
|
||||
|
||||
|
@ -52,6 +58,10 @@ func (s *Scope) Lookup(rt *Runtime, k string) *Binding {
|
|||
builtin := rt.LookupBuiltin(k)
|
||||
|
||||
if builtin != nil {
|
||||
if rt.IsDebugMode() {
|
||||
fmt.Printf("[DEBUG] Found builtin '%s': '%s'\n", k, builtin)
|
||||
}
|
||||
|
||||
return &Binding{builtin, true}
|
||||
}
|
||||
|
||||
|
@ -62,9 +72,36 @@ func (s *Scope) Insert(k string, v IExpr, public bool) {
|
|||
s.bindings[k] = Binding{Value: v, Public: public}
|
||||
}
|
||||
|
||||
func MakeScope(parent *Scope) *Scope {
|
||||
func (s *Scope) GetNS(rt *Runtime) *Namespace {
|
||||
if s.ns == nil {
|
||||
panic("A scope with no namespace !!!!")
|
||||
}
|
||||
ns, ok := rt.GetNS(*s.ns)
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("A scope with the wrong namespace! '%s'", s.ns))
|
||||
}
|
||||
return ns
|
||||
}
|
||||
|
||||
func (s *Scope) SetNS(ns *string) {
|
||||
s.ns = ns
|
||||
}
|
||||
|
||||
func MakeScope(rt *Runtime, parent *Scope, namespace *string) *Scope {
|
||||
var belongsTo *string
|
||||
|
||||
if parent != nil {
|
||||
nsName := parent.GetNS(rt).GetName()
|
||||
belongsTo = &nsName
|
||||
} else if namespace == nil {
|
||||
panic("When the 'parent' is nil, you have to provide the 'namespace' name.")
|
||||
} else {
|
||||
belongsTo = namespace
|
||||
}
|
||||
|
||||
return &Scope{
|
||||
parent: parent,
|
||||
bindings: map[string]Binding{},
|
||||
ns: belongsTo,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,9 +67,11 @@ func DefMacro(rt *Runtime, scope IScope, args *List) (IExpr, IError) {
|
|||
|
||||
// TODO: Add support for docstrings and meta
|
||||
|
||||
switch args.Count() {
|
||||
case 3:
|
||||
name := args.First()
|
||||
if args.Count() < 2 {
|
||||
return nil, MakeError(rt, args, "'defmacro' form need at least 2 arguments")
|
||||
}
|
||||
|
||||
name := args.Rest().First()
|
||||
|
||||
if name.GetType() != ast.Symbol {
|
||||
return nil, MakeError(rt, name, "the first argument of 'defmacro' has to be a symbol")
|
||||
|
@ -80,7 +82,7 @@ func DefMacro(rt *Runtime, scope IScope, args *List) (IExpr, IError) {
|
|||
var params IColl
|
||||
body := MakeEmptyBlock()
|
||||
|
||||
arguments := args.Rest().First()
|
||||
arguments := args.Rest().Rest().First()
|
||||
|
||||
// TODO: Add vector in here
|
||||
// Or any other icoll
|
||||
|
@ -89,18 +91,16 @@ func DefMacro(rt *Runtime, scope IScope, args *List) (IExpr, IError) {
|
|||
}
|
||||
|
||||
if args.Count() > 2 {
|
||||
body.SetContent(args.Rest().Rest().(*List).ToSlice())
|
||||
body.SetContent(args.Rest().Rest().Rest().(*List).ToSlice())
|
||||
}
|
||||
|
||||
macro := MakeMacro(scope, sym.GetName(), params, body)
|
||||
|
||||
ns := rt.CurrentNS()
|
||||
ns := scope.GetNS(rt)
|
||||
ns.DefineGlobal(sym.GetName(), macro, true)
|
||||
|
||||
return macro, nil
|
||||
}
|
||||
|
||||
return nil, MakeError(rt, args, "'defmacro' form need at least 2 arguments")
|
||||
}
|
||||
|
||||
// Fn defines a function inside the given scope `scope` with the given `args`.
|
||||
|
|
|
@ -74,6 +74,10 @@ type IExpr interface {
|
|||
IScopable
|
||||
}
|
||||
|
||||
// TODO: Add helper functions to reach methods on Node.location. For example
|
||||
// Node.location.DecStart() has to have a helper on the Node liek:
|
||||
// Node.DecStartLocation
|
||||
|
||||
// Node struct is simply representing a Node in the AST which provides the
|
||||
// functionalities required to trace the code based on the location.
|
||||
type Node struct {
|
||||
|
|
Loading…
Reference in New Issue