[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:
Sameer Rahmani 2021-01-07 19:45:07 +00:00
parent 0cc7646e3a
commit c2d4273319
14 changed files with 159 additions and 60 deletions

View File

@ -63,7 +63,7 @@ func (s *Source) GetPos(start, end int) *string {
} }
func (s *Source) GetLine(linenum int) string { func (s *Source) GetLine(linenum int) string {
lines := strings.Split(strings.Join(*s.Buffer, ""), "\n") 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 lines[linenum-1]
} }
return "----" return "----"
@ -107,6 +107,21 @@ func (s *Source) LineNumberFor(pos int) int {
return result 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 { type Location struct {
start int start int
end int end int
@ -125,7 +140,10 @@ func (l *Location) GetEnd() int {
} }
func (l *Location) GetSource() *Source { func (l *Location) GetSource() *Source {
return &l.source if l.IsKnownLocaiton() {
return &l.source
}
return GetBuiltinSource()
} }
func (l *Location) IncStart(x int) { func (l *Location) IncStart(x int) {

View File

@ -22,10 +22,10 @@ package core
// the language which are implemented in Go // the language which are implemented in Go
var BUILTINS = map[string]NativeFunction{ var BUILTINS = map[string]NativeFunction{
"pr": MakeNativeFn("pr", PrNativeFn), "pr": MakeNativeFn("pr", PrNativeFn),
"prn": MakeNativeFn("pr", PrnNativeFn), "prn": MakeNativeFn("prn", PrnNativeFn),
"print": MakeNativeFn("print", PrintNativeFn), "print": MakeNativeFn("print", PrintNativeFn),
"println": MakeNativeFn("println", PrintlnNativeFn), "println": MakeNativeFn("println", PrintlnNativeFn),
"require": MakeNativeFn("print", RequireNativeFn), "require": MakeNativeFn("require", RequireNativeFn),
"hash": MakeNativeFn("hash", HashNativeFn), "hash": MakeNativeFn("hash", HashNativeFn),
} }

View File

@ -65,6 +65,10 @@ type CallStack struct {
count uint 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 { func (c *CallStack) Count() uint {
return c.count return c.count
} }
@ -158,7 +162,8 @@ func (c *CallStack) ToTraceBack() *TraceBack {
if item == nil { if item == nil {
break break
} }
tr = append(tr, &item.data) // TODO: This doesn't seem efficient. Fix it.
tr = append([]*Frame{&item.data}, tr...)
item = item.prev item = item.prev
} }

View File

@ -82,19 +82,20 @@ func evalForm(rt *Runtime, scope IScope, form IExpr) (IExpr, IError) {
return MakeNil(MakeNodeFromExpr(form)), nil return MakeNil(MakeNodeFromExpr(form)), nil
default: default:
var expr *Binding var expr *Binding
ns := scope.GetNS(rt)
if sym.IsNSQualified() { if sym.IsNSQualified() {
// Whether a namespace with the given alias loaded or not // 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, return nil, MakeError(rt, sym,
fmt.Sprintf("Namespace '%s' is no loaded", sym.GetNSPart()), fmt.Sprintf("Namespace '%s' is no loaded", sym.GetNSPart()),
) )
} }
expr = rt.CurrentNS().LookupGlobal(rt, sym) expr = ns.LookupGlobal(rt, sym)
nsName = sym.GetNSPart() nsName = sym.GetNSPart()
} else { } else {
expr = scope.Lookup(rt, symbolName) expr = scope.Lookup(rt, symbolName)
nsName = rt.CurrentNS().GetName() nsName = ns.GetName()
} }
if expr == nil { if expr == nil {
@ -199,7 +200,7 @@ 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", scope.GetNS(rt).GetName(), forms)
fmt.Printf("[DEBUG] * State: I: %d, Exprs: %s\n", i, exprs) 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 // * The rest of the arguments will form a block that acts as the
// body of the macro. // body of the macro.
case "defmacro": case "defmacro":
ret, err = DefMacro(rt, scope, list.Rest().(*List)) ret, err = DefMacro(rt, scope, list)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -511,7 +512,7 @@ tco:
return nil, MakeError(rt, list, "'let' needs at list 1 aruments") 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 // Since we're using IColl for the bindings, we can use either lists
// or vectors or even hashmaps for bindings // or vectors or even hashmaps for bindings
@ -565,6 +566,7 @@ tco:
default: default:
// Evaluating all the elements of the list // Evaluating all the elements of the list
listExprs, e := evalForm(rt, scope, list) listExprs, e := evalForm(rt, scope, list)
if e != nil { if e != nil {
return nil, e return nil, e
} }
@ -589,8 +591,8 @@ tco:
if e != nil { if e != nil {
return nil, e return nil, e
} }
rt.Stack.Push(list, fn) rt.Stack.Push(list, fn)
body := append( body := append(
fn.GetBody().ToSlice(), fn.GetBody().ToSlice(),
// Add the PopStack instruction to clean up the stack after // Add the PopStack instruction to clean up the stack after
@ -607,6 +609,7 @@ tco:
fn := f.(*NativeFunction) fn := f.(*NativeFunction)
rt.Stack.Push(list, fn) rt.Stack.Push(list, fn)
ret, err = fn.Apply( ret, err = fn.Apply(
rt, rt,
scope, scope,

View File

@ -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` // MakeFnScope a new scope for the body of a function. It binds the `bindings`
// to the given `values`. // to the given `values`.
func MakeFnScope(rt *Runtime, parent IScope, bindings IColl, values IColl) (*Scope, IError) { 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 // TODO: Implement destructuring
binds := bindings.ToSlice() binds := bindings.ToSlice()
exprs := values.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() { if lastBinding.GetType() == ast.Symbol && lastBinding.(*Symbol).IsRestable() {
numberOfBindings = len(binds) - 1 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) { if numberOfBindings > len(exprs) {
@ -201,7 +211,7 @@ func MakeFnScope(rt *Runtime, parent IScope, bindings IColl, values IColl) (*Sco
// next. // next.
rest := MakeEmptyList(MakeNodeFromExpr(binds[i])) 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 // If the number of values matches the number of bindings
// or it is more than that create a list from them // 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) // 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 { func MakeNativeFn(name string, f nativeFnHandler) NativeFunction {
return NativeFunction{ return NativeFunction{
Node: MakeNodeFromLocation(ast.MakeUnknownLocation()),
name: name, name: name,
fn: f, fn: f,
} }

View File

@ -18,7 +18,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
package core 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. // 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 // 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 macro *Function
var e IError var e IError
ok := false ok := false
//form := expr
for { for {
macro, ok = isMacroCall(rt, scope, form) macro, ok = isMacroCall(rt, scope, form)

View File

@ -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) namespace.setForms(body)
return &namespace, nil 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` // MakeNS creates a new namespace with the given `name` and `source`
func MakeNS(name string, source string) Namespace { func MakeNS(rt *Runtime, name string, source string) Namespace {
s := MakeScope(nil) s := MakeScope(rt, nil, &name)
return Namespace{ return Namespace{
name: name, name: name,
rootScope: *s, rootScope: *s,

View File

@ -158,7 +158,7 @@ func (d Double) F64() float64 {
return d.value return d.value
} }
func MakeNumberFromStr(strValue string, isDouble bool) (INumber, error) { func MakeNumberFromStr(n Node, strValue string, isDouble bool) (INumber, error) {
var ret INumber var ret INumber
if isDouble { if isDouble {
@ -169,6 +169,7 @@ func MakeNumberFromStr(strValue string, isDouble bool) (INumber, error) {
} }
ret = Double{ ret = Double{
Node: n,
value: v, value: v,
} }
} else { } else {
@ -178,6 +179,7 @@ func MakeNumberFromStr(strValue string, isDouble bool) (INumber, error) {
} }
ret = Integer{ ret = Integer{
Node: n,
value: v, value: v,
} }
} }

View File

@ -341,8 +341,9 @@ func readNumber(parser IParsable, neg bool) (IExpr, IError) {
break break
} }
} }
n := MakeSinglePointNode(parser.GetSource(), parser.GetLocation())
value, err := MakeNumberFromStr(result, isDouble) n.location.DecStart(len(result))
value, err := MakeNumberFromStr(n, result, isDouble)
if err != nil { if err != nil {
return nil, makeErrorFromError(parser, err) return nil, makeErrorFromError(parser, err)
@ -425,6 +426,7 @@ func readList(parser IParsable) (IExpr, IError) {
node.location.DecStart(1) node.location.DecStart(1)
node.location.IncEnd(1) node.location.IncEnd(1)
return MakeList(*node, list), nil return MakeList(*node, list), nil
} }

View File

@ -85,9 +85,10 @@ func printError(rt *Runtime, err IError, stage int) {
} }
color.Yellow.Printf( color.Yellow.Printf(
"%d: At '%s'\n", "%d: At '%s':%d\n",
stage, stage,
source.Path, source.Path,
source.LineNumberFor(loc.GetStart()),
) )
color.White.Printf("%s\n", lines) color.White.Printf("%s\n", lines)
@ -115,11 +116,17 @@ func printErrorWithTraceBack(rt *Runtime, err IError) {
var lines string var lines string
for i := startline; i <= endline; i++ { for i := startline; i <= endline; i++ {
fLoc := t.Fn.GetLocation() fLoc := t.Caller.GetLocation()
line := fLoc.GetSource().GetLine(i)
if line != "----" { if fLoc.IsKnownLocaiton() {
lines += fmt.Sprintf("%d:\t%s\n", i, line) line := fLoc.GetSource().GetLine(i)
if line != "----" {
lines += fmt.Sprintf("%d:\t%s\n", i, line)
}
} else {
lines += "Builtin\n"
} }
} }
color.Yellow.Printf( color.Yellow.Printf(
@ -137,6 +144,7 @@ func printErrorWithTraceBack(rt *Runtime, err IError) {
} }
func PrintError(rt *Runtime, err IError) { func PrintError(rt *Runtime, err IError) {
switch err.GetErrType() { switch err.GetErrType() {
case SyntaxError, SemanticError: case SyntaxError, SemanticError:
printError(rt, err, 0) printError(rt, err, 0)

View File

@ -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 // CreateNS is a helper function to create a namespace and set it to be
// the current namespace of the runtime. `MakeNS` is much preferred // the current namespace of the runtime. `MakeNS` is much preferred
func (r *Runtime) CreateNS(name string, source string, setAsCurrent bool) { func (r *Runtime) CreateNS(name string, source string, setAsCurrent bool) {
ns := MakeNS(name, source) ns := MakeNS(r, name, source)
if setAsCurrent { if setAsCurrent {
r.currentNS = name r.currentNS = name

View File

@ -23,6 +23,8 @@ import "fmt"
type IScope interface { type IScope interface {
Lookup(rt *Runtime, k string) *Binding Lookup(rt *Runtime, k string) *Binding
Insert(k string, v IExpr, public bool) Insert(k string, v IExpr, public bool)
GetNS(rt *Runtime) *Namespace
SetNS(ns *string)
} }
type Binding struct { type Binding struct {
@ -33,6 +35,7 @@ type Binding struct {
type Scope struct { type Scope struct {
bindings map[string]Binding bindings map[string]Binding
parent *Scope parent *Scope
ns *string
} }
func (s *Scope) Lookup(rt *Runtime, k string) *Binding { 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] v, ok := s.bindings[k]
if ok { if ok {
if rt.IsDebugMode() {
fmt.Printf("[DEBUG] Found '%s': '%s'\n", k, v.Value.String())
}
return &v return &v
} }
@ -52,6 +58,10 @@ func (s *Scope) Lookup(rt *Runtime, k string) *Binding {
builtin := rt.LookupBuiltin(k) builtin := rt.LookupBuiltin(k)
if builtin != nil { if builtin != nil {
if rt.IsDebugMode() {
fmt.Printf("[DEBUG] Found builtin '%s': '%s'\n", k, builtin)
}
return &Binding{builtin, true} 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} 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{ return &Scope{
parent: parent, parent: parent,
bindings: map[string]Binding{}, bindings: map[string]Binding{},
ns: belongsTo,
} }
} }

View File

@ -67,40 +67,40 @@ func DefMacro(rt *Runtime, scope IScope, args *List) (IExpr, IError) {
// TODO: Add support for docstrings and meta // TODO: Add support for docstrings and meta
switch args.Count() { if args.Count() < 2 {
case 3: return nil, MakeError(rt, args, "'defmacro' form need at least 2 arguments")
name := args.First()
if name.GetType() != ast.Symbol {
return nil, MakeError(rt, name, "the first argument of 'defmacro' has to be a symbol")
}
sym := name.(*Symbol)
var params IColl
body := MakeEmptyBlock()
arguments := args.Rest().First()
// TODO: Add vector in here
// Or any other icoll
if arguments.GetType() == ast.List {
params = arguments.(IColl)
}
if args.Count() > 2 {
body.SetContent(args.Rest().Rest().(*List).ToSlice())
}
macro := MakeMacro(scope, sym.GetName(), params, body)
ns := rt.CurrentNS()
ns.DefineGlobal(sym.GetName(), macro, true)
return macro, nil
} }
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")
}
sym := name.(*Symbol)
var params IColl
body := MakeEmptyBlock()
arguments := args.Rest().Rest().First()
// TODO: Add vector in here
// Or any other icoll
if arguments.GetType() == ast.List {
params = arguments.(IColl)
}
if args.Count() > 2 {
body.SetContent(args.Rest().Rest().Rest().(*List).ToSlice())
}
macro := MakeMacro(scope, sym.GetName(), params, body)
ns := scope.GetNS(rt)
ns.DefineGlobal(sym.GetName(), macro, true)
return macro, nil
} }
// Fn defines a function inside the given scope `scope` with the given `args`. // Fn defines a function inside the given scope `scope` with the given `args`.

View File

@ -74,6 +74,10 @@ type IExpr interface {
IScopable 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 // Node struct is simply representing a Node in the AST which provides the
// functionalities required to trace the code based on the location. // functionalities required to trace the code based on the location.
type Node struct { type Node struct {