diff --git a/bootstrap/pkg/ast/ast.go b/bootstrap/pkg/ast/ast.go index 8092694..7c79f38 100644 --- a/bootstrap/pkg/ast/ast.go +++ b/bootstrap/pkg/ast/ast.go @@ -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 { - return &l.source + if l.IsKnownLocaiton() { + return &l.source + } + return GetBuiltinSource() } func (l *Location) IncStart(x int) { diff --git a/bootstrap/pkg/core/builtins.go b/bootstrap/pkg/core/builtins.go index f96a87d..e44ffa6 100644 --- a/bootstrap/pkg/core/builtins.go +++ b/bootstrap/pkg/core/builtins.go @@ -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), } diff --git a/bootstrap/pkg/core/call_stack.go b/bootstrap/pkg/core/call_stack.go index 2d0ffc8..5490fa0 100644 --- a/bootstrap/pkg/core/call_stack.go +++ b/bootstrap/pkg/core/call_stack.go @@ -65,6 +65,10 @@ type CallStack struct { count uint } +func (f *Frame) String() string { + return fmt.Sprintf("", 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 } diff --git a/bootstrap/pkg/core/eval.go b/bootstrap/pkg/core/eval.go index 87615cd..c746e26 100644 --- a/bootstrap/pkg/core/eval.go +++ b/bootstrap/pkg/core/eval.go @@ -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, diff --git a/bootstrap/pkg/core/function.go b/bootstrap/pkg/core/function.go index 88d4d1f..c641399 100644 --- a/bootstrap/pkg/core/function.go +++ b/bootstrap/pkg/core/function.go @@ -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, } diff --git a/bootstrap/pkg/core/macro.go b/bootstrap/pkg/core/macro.go index e1f2d4e..59644aa 100644 --- a/bootstrap/pkg/core/macro.go +++ b/bootstrap/pkg/core/macro.go @@ -18,7 +18,14 @@ along with this program. If not, see . 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) diff --git a/bootstrap/pkg/core/namespace.go b/bootstrap/pkg/core/namespace.go index 349b6c0..75f5679 100644 --- a/bootstrap/pkg/core/namespace.go +++ b/bootstrap/pkg/core/namespace.go @@ -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, diff --git a/bootstrap/pkg/core/numbers.go b/bootstrap/pkg/core/numbers.go index 93c39fd..12b6d65 100644 --- a/bootstrap/pkg/core/numbers.go +++ b/bootstrap/pkg/core/numbers.go @@ -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, } } diff --git a/bootstrap/pkg/core/parser.go b/bootstrap/pkg/core/parser.go index 8fc50d3..ac1362a 100644 --- a/bootstrap/pkg/core/parser.go +++ b/bootstrap/pkg/core/parser.go @@ -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 } diff --git a/bootstrap/pkg/core/printer.go b/bootstrap/pkg/core/printer.go index 8c0bb2f..ecadfb9 100644 --- a/bootstrap/pkg/core/printer.go +++ b/bootstrap/pkg/core/printer.go @@ -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() - line := fLoc.GetSource().GetLine(i) - if line != "----" { - lines += fmt.Sprintf("%d:\t%s\n", i, line) + 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) diff --git a/bootstrap/pkg/core/runtime.go b/bootstrap/pkg/core/runtime.go index bca2701..3b1370d 100644 --- a/bootstrap/pkg/core/runtime.go +++ b/bootstrap/pkg/core/runtime.go @@ -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 diff --git a/bootstrap/pkg/core/scope.go b/bootstrap/pkg/core/scope.go index 9b65527..63fd1a6 100644 --- a/bootstrap/pkg/core/scope.go +++ b/bootstrap/pkg/core/scope.go @@ -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, } } diff --git a/bootstrap/pkg/core/sforms.go b/bootstrap/pkg/core/sforms.go index aaa806f..695df6b 100644 --- a/bootstrap/pkg/core/sforms.go +++ b/bootstrap/pkg/core/sforms.go @@ -67,40 +67,40 @@ 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 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 + if args.Count() < 2 { + return nil, MakeError(rt, args, "'defmacro' form need at least 2 arguments") } - 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`. diff --git a/bootstrap/pkg/core/types.go b/bootstrap/pkg/core/types.go index ebcbb99..8a199a1 100644 --- a/bootstrap/pkg/core/types.go +++ b/bootstrap/pkg/core/types.go @@ -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 {