diff --git a/bootstrap/pkg/core/core.go b/bootstrap/pkg/core/core.go index ac40a56..84b914c 100644 --- a/bootstrap/pkg/core/core.go +++ b/bootstrap/pkg/core/core.go @@ -20,15 +20,20 @@ along with this program. If not, see . package core import ( + "bytes" "fmt" + "html/template" "os" "path/filepath" - "strings" "github.com/chzyer/readline" - "serene-lang.org/bootstrap/pkg/ast" ) +type mainRunner struct { + NS string + Args string +} + func rep(rt *Runtime, line string) { ast, err := ReadString("*REPL*", line) @@ -113,6 +118,7 @@ func Run(flags map[string]bool, args []string) { } rt := MakeRuntime([]string{cwd}, flags) + rt.CreateNS("user", "REPL", true) if len(args) == 0 { @@ -120,77 +126,51 @@ func Run(flags map[string]bool, args []string) { os.Exit(1) } + var buf bytes.Buffer + arguments := "" ns := args[0] - nsAsBuffer := strings.Split(ns, "") - source := &ast.Source{Buffer: &nsAsBuffer, Path: "*input-argument*"} - node := MakeNode(source, 0, len(ns)) - nsSym, err := MakeSymbol(node, ns) - - if err != nil { - PrintError(rt, err) - os.Exit(1) - } - - loadedNS, err := requireNS(rt, nsSym) - - if err != nil { - PrintError(rt, err) - os.Exit(1) - } - - rt.InsertNS(ns, loadedNS) - inserted := rt.setCurrentNS(loadedNS.GetName()) - - if !inserted { - err := MakeError( - rt, - loadedNS, - fmt.Sprintf( - "the namespace '%s' didn't get inserted in the runtime.", - loadedNS.GetName()), - ) - PrintError(rt, err) - os.Exit(1) - } - - // Evaluating the body of the loaded ns (Check for ns validation happens here) - loadedNS, err = EvalNSBody(rt, loadedNS) - if err != nil { - PrintError(rt, err) - os.Exit(1) - } - - mainBinding := loadedNS.GetRootScope().Lookup(rt, "main") - - if mainBinding == nil { - PrintError(rt, MakePlainError(fmt.Sprintf("can't find the 'main' function in '%s' namespace", ns))) - os.Exit(1) - } - - if mainBinding.Value.GetType() != ast.Fn { - PrintError(rt, MakePlainError("'main' is not a function")) - os.Exit(1) - } - - mainFn := mainBinding.Value.(*Function) - - var fnArgs []IExpr - var argNode Node if len(args) > 1 { for _, arg := range args[1:] { - node := MakeNodeFromExpr(mainFn) - fnArgs = append(fnArgs, MakeString(node, arg)) + arguments += "\"" + arg + "\"" } - argNode = MakeNodeFromExprs(fnArgs) - } else { - argNode = node } - _, err = mainFn.Apply(rt, loadedNS.GetRootScope(), mainFn.Node, MakeList(argNode, fnArgs)) + tmpl, e := template.New("run").Parse( + `(def run-main + (fn () + (require '({{.NS}} n)) + (n/main {{.Args}}))) + +(run-main)`, + ) + + if e != nil { + panic(e) + } + + e = tmpl.Execute(&buf, &mainRunner{ns, arguments}) + + if e != nil { + panic(e) + } + + if rt.IsDebugMode() { + fmt.Println("[DEBUG] Evaluating the following form to run the 'main' fn:") + fmt.Println(buf.String()) + } + + ast, err := ReadString("*RUN*", buf.String()) if err != nil { PrintError(rt, err) os.Exit(1) } + + _, err = Eval(rt, ast) + + if err != nil { + PrintError(rt, err) + return + } } diff --git a/bootstrap/pkg/core/errors.go b/bootstrap/pkg/core/errors.go index 1597a30..04f91c9 100644 --- a/bootstrap/pkg/core/errors.go +++ b/bootstrap/pkg/core/errors.go @@ -41,6 +41,14 @@ import ( "serene-lang.org/bootstrap/pkg/errors" ) +type ErrType uint8 + +const ( + SyntaxError ErrType = iota + SemanticError + RuntimeError +) + // IError defines the necessary functionality of the internal errors. type IError interface { // In order to point to a specific point in the input @@ -50,6 +58,7 @@ type IError interface { IRepresentable IDebuggable + GetErrType() ErrType GetDescription() *string GetStackTrace() *TraceBack // To wrap Golan rrrrors @@ -63,6 +72,7 @@ type IError interface { type Error struct { Node + errtype ErrType errno errors.Errno WrappedErr error msg string @@ -81,6 +91,10 @@ func (e *Error) ToDebugStr() string { return fmt.Sprintf("%s:\n\t%s", e.msg, e.WrappedErr.Error()) } +func (e *Error) GetErrType() ErrType { + return e.errtype +} + func (e *Error) WithError(err error) IError { e.WrappedErr = err return e @@ -117,19 +131,22 @@ func MakePlainError(msg string) IError { // MakeError creates an Error which points to the given IExpr `e` as // the root of the error. func MakeError(rt *Runtime, e IExpr, msg string) IError { - trace := append(*rt.Stack.ToTraceBack(), &Frame{0, rt.Stack.GetCurrentFn(), e}) + frame := MakeFrame(e, rt.Stack.GetCurrentFn(), 1) + trace := append(*rt.Stack.ToTraceBack(), frame) return &Error{ - Node: MakeNodeFromExpr(e), - msg: msg, - trace: &trace, + Node: MakeNodeFromExpr(e), + errtype: RuntimeError, + msg: msg, + trace: &trace, } } -func MakeParsetimeErrorf(n Node, msg string, a ...interface{}) IError { +func MakeSyntaxErrorf(n Node, msg string, a ...interface{}) IError { return &Error{ - Node: n, - msg: fmt.Sprintf(msg, a...), + Node: n, + errtype: SyntaxError, + msg: fmt.Sprintf(msg, a...), } } @@ -141,9 +158,10 @@ func MakeSemanticError(rt *Runtime, e IExpr, errno errors.Errno, msg string) IEr } return &Error{ - Node: MakeNodeFromExpr(e), - errno: errno, - msg: msg, - trace: frames, + Node: MakeNodeFromExpr(e), + errtype: SemanticError, + errno: errno, + msg: msg, + trace: frames, } } diff --git a/bootstrap/pkg/core/eval.go b/bootstrap/pkg/core/eval.go index 717aefb..87615cd 100644 --- a/bootstrap/pkg/core/eval.go +++ b/bootstrap/pkg/core/eval.go @@ -218,6 +218,10 @@ tco: // Evaluating forms one by one if forms.GetType() != ast.List { ret, err = evalForm(rt, scope, forms) + if err != nil { + return nil, err + } + continue body } @@ -271,6 +275,10 @@ tco: // TODO: decide on the syntax and complete the docs case "ns": ret, err = NSForm(rt, scope, list) + if err != nil { + return nil, err + } + continue body // no rewrite // `quote` evaluation rules: @@ -298,6 +306,10 @@ tco: // Creates a new list form it's arguments. case "list": ret, err = evalForm(rt, scope, list.Rest().(*List)) + if err != nil { + return nil, err + } + continue body // no rewrite // TODO: Implement `concat` in serene itself when we have protocols available @@ -320,12 +332,19 @@ tco: result = append(result, lst.(*List).ToSlice()...) } - node := MakeNodeFromExpr(list) - if len(result) > 0 { - node = MakeNodeFromExprs(result) + n := MakeNodeFromExprs(result) + + if n == nil { + n = &list.Node } + node := *n + ret, err = MakeList(node, result), nil + if err != nil { + return nil, err + } + continue body // no rewrite // TODO: Implement `list` in serene itself when we have destructuring available @@ -349,6 +368,10 @@ tco: } ret, err = coll.Cons(evaledForms.(*List).First()), nil + if err != nil { + return nil, err + } + continue body // no rewrite // `def` evaluation rules @@ -359,6 +382,10 @@ tco: // the symbol name binded to the value case "def": ret, err = Def(rt, scope, list.Rest().(*List)) + if err != nil { + return nil, err + } + continue body // no rewrite // `defmacro` evaluation rules: @@ -368,6 +395,10 @@ tco: // body of the macro. case "defmacro": ret, err = DefMacro(rt, scope, list.Rest().(*List)) + if err != nil { + return nil, err + } + continue body // no rewrite // `macroexpand` evaluation rules: @@ -385,6 +416,9 @@ tco: } ret, err = macroexpand(rt, scope, evaledForm.(*List).First()) + if err != nil { + return nil, err + } continue body // no rewrite // `fn` evaluation rules: @@ -392,6 +426,9 @@ tco: // * Defines an anonymous function. case "fn": ret, err = Fn(rt, scope, list) + if err != nil { + return nil, err + } continue body // no rewrite // `if` evaluation rules: @@ -454,6 +491,10 @@ tco: } ret, err = EvalForms(rt, scope, form) + if err != nil { + return nil, err + } + continue body // no rewrite // `let` evaluation rules: @@ -525,9 +566,7 @@ tco: // Evaluating all the elements of the list listExprs, e := evalForm(rt, scope, list) if e != nil { - err = e - ret = nil - break tco //return + return nil, e } f := listExprs.(*List).First() @@ -541,19 +580,14 @@ tco: // `expressions` to the body of function and loop again fn := f.(*Function) if e != nil { - err = e - ret = nil - break body //return - + return nil, e } argList := listExprs.(*List).Rest().(*List) fnScope, e := MakeFnScope(rt, fn.GetScope(), fn.GetParams(), argList) if e != nil { - err = e - ret = nil - break body //return + return nil, e } rt.Stack.Push(list, fn) @@ -579,6 +613,11 @@ tco: MakeNodeFromExpr(fn), listExprs.(*List), ) + + if err != nil { + return nil, err + } + rt.Stack.Pop() continue body // no rewrite diff --git a/bootstrap/pkg/core/function.go b/bootstrap/pkg/core/function.go index 1b41f6a..88d4d1f 100644 --- a/bootstrap/pkg/core/function.go +++ b/bootstrap/pkg/core/function.go @@ -209,7 +209,13 @@ func MakeFnScope(rt *Runtime, parent IScope, bindings IColl, values IColl) (*Sco var node Node if len(elements) > 0 { - node = MakeNodeFromExprs(elements) + n := MakeNodeFromExprs(elements) + + if n == nil { + n = &values.(*List).Node + } + + node = *n } else { node = MakeNodeFromExpr(binds[i]) } diff --git a/bootstrap/pkg/core/keyword.go b/bootstrap/pkg/core/keyword.go index d2a1c39..a2a024a 100644 --- a/bootstrap/pkg/core/keyword.go +++ b/bootstrap/pkg/core/keyword.go @@ -181,11 +181,11 @@ func extractParts(s string) (string, string) { func MakeKeyword(n Node, name string) (*Keyword, IError) { if strings.Count(name, ":") > 2 { - return nil, MakeParsetimeErrorf(n, "can't parse the keyword with more that two colons: '%s'", name) + return nil, MakeSyntaxErrorf(n, "can't parse the keyword with more that two colons: '%s'", name) } if strings.Count(name, "/") > 1 { - return nil, MakeParsetimeErrorf(n, "illegal namespace path for the given keyword: '%s'", name) + return nil, MakeSyntaxErrorf(n, "illegal namespace path for the given keyword: '%s'", name) } var nsName string diff --git a/bootstrap/pkg/core/list.go b/bootstrap/pkg/core/list.go index 1e3c2fc..a5bd2a7 100644 --- a/bootstrap/pkg/core/list.go +++ b/bootstrap/pkg/core/list.go @@ -76,7 +76,12 @@ func (l *List) Rest() ISeq { rest := l.exprs[1:] node := l.Node if len(rest) > 0 { - node = MakeNodeFromExprs(rest) + // n won't be nil here but we should check anyway + n := MakeNodeFromExprs(rest) + if n == nil { + panic("'MakeNodeFromExprs' has returned nil for none empty array of exprs") + } + node = *n } return MakeList(node, rest) @@ -105,7 +110,14 @@ func (l *List) ToSlice() []IExpr { func (l *List) Cons(e IExpr) IExpr { elements := append([]IExpr{e}, l.ToSlice()...) - return MakeList(MakeNodeFromExprs(elements), elements) + node := MakeNodeFromExprs(elements) + + // Since 'elements' is not empty node won't be nil but we should + // check anyway + if node == nil { + node = &l.Node + } + return MakeList(*node, elements) } // END: IColl --- diff --git a/bootstrap/pkg/core/namespace.go b/bootstrap/pkg/core/namespace.go index 0a2b48e..349b6c0 100644 --- a/bootstrap/pkg/core/namespace.go +++ b/bootstrap/pkg/core/namespace.go @@ -199,6 +199,7 @@ func RequireNamespace(rt *Runtime, namespace IExpr) (IExpr, IError) { ns = first.(*Symbol) alias = second.(*Symbol).GetName() + default: return nil, MakeError(rt, ns, "Don't know how to load the given namespace") } diff --git a/bootstrap/pkg/core/parser.go b/bootstrap/pkg/core/parser.go index 214cd3e..8f03748 100644 --- a/bootstrap/pkg/core/parser.go +++ b/bootstrap/pkg/core/parser.go @@ -176,7 +176,7 @@ func (sp *StringParser) Buffer() *[]string { // points at the current position of the buffer. func makeErrorAtPoint(p IParsable, msg string, a ...interface{}) IError { n := MakeSinglePointNode(p.GetSource(), p.GetLocation()) - return MakeParsetimeErrorf(n, msg, a...) + return MakeSyntaxErrorf(n, msg, a...) } // makeErrorFromError is a function which wraps a Golang error in an IError @@ -407,9 +407,14 @@ func readList(parser IParsable) (IExpr, IError) { } node := MakeNodeFromExprs(list) + if node == nil { + n := MakeSinglePointNode(parser.GetSource(), parser.GetLocation()) + node = &n + } + node.location.DecStart(1) node.location.IncEnd(1) - return MakeList(node, list), nil + return MakeList(*node, list), nil } func readComment(parser IParsable) (IExpr, IError) { @@ -443,9 +448,14 @@ func readQuotedExpr(parser IParsable) (IExpr, IError) { } listNode := MakeNodeFromExprs(listElems) + if listNode == nil { + n := MakeSinglePointNode(parser.GetSource(), parser.GetLocation()) + listNode = &n + } + listNode.location.DecStart(1) listNode.location.IncStart(1) - return MakeList(listNode, listElems), nil + return MakeList(*listNode, listElems), nil } // readUnquotedExpr reads different unquoting expressions from their short representaions. @@ -489,10 +499,19 @@ func readUnquotedExpr(parser IParsable) (IExpr, IError) { } listElems := []IExpr{sym, expr} + listNode := MakeNodeFromExprs(listElems) + + // listNode won't be nil in this case but it doesn't + // mean we shouldn't check + if listNode == nil { + n := MakeSinglePointNode(parser.GetSource(), parser.GetLocation()) + listNode = &n + } + listNode.location.DecStart(1) listNode.location.IncStart(1) - return MakeList(listNode, listElems), nil + return MakeList(*listNode, listElems), nil } // readQuasiquotedExpr reads the backquote and replace it with a call @@ -512,10 +531,17 @@ func readQuasiquotedExpr(parser IParsable) (IExpr, IError) { listElems := []IExpr{sym, expr} listNode := MakeNodeFromExprs(listElems) + // listNode won't be nil in this case but it doesn't + // mean we shouldn't check + if listNode == nil { + n := MakeSinglePointNode(parser.GetSource(), parser.GetLocation()) + listNode = &n + } + listNode.location.DecStart(1) listNode.location.IncStart(1) - return MakeList(listNode, listElems), nil + return MakeList(*listNode, listElems), nil } // readExpr reads one expression from the input. This function is the most diff --git a/bootstrap/pkg/core/printer.go b/bootstrap/pkg/core/printer.go index 61e0c90..7974aa9 100644 --- a/bootstrap/pkg/core/printer.go +++ b/bootstrap/pkg/core/printer.go @@ -65,9 +65,11 @@ func Println(rt *Runtime, ast ...IRepresentable) { } func PrintError(rt *Runtime, err IError) { - trace := err.GetStackTrace() + trace := err.GetStackTrace() + fmt.Println(err) for i, t := range *trace { + fmt.Println(*t) caller := t.Caller callerLoc := caller.GetLocation() callerSource := callerLoc.GetSource() @@ -82,7 +84,11 @@ func PrintError(rt *Runtime, err IError) { var lines string for i := startline; i <= endline; i++ { - lines += fmt.Sprintf("%d:\t%s\n", i, t.Fn.GetLocation().GetSource().GetLine(i)) + fmt.Println(">>>>>>>>>> ", err) + fLoc := t.Fn.GetLocation() + fmt.Println(">>>>>>>>>> ", fLoc, fLoc.GetSource()) + + lines += fmt.Sprintf("%d:\t%s\n", i, fLoc.GetSource().GetLine(i)) } color.Yellow.Printf( diff --git a/bootstrap/pkg/core/quasiquote.go b/bootstrap/pkg/core/quasiquote.go index 53c679e..fa621b6 100644 --- a/bootstrap/pkg/core/quasiquote.go +++ b/bootstrap/pkg/core/quasiquote.go @@ -103,8 +103,12 @@ func qqProcess(rt *Runtime, e IExpr) (IExpr, IError) { e, } + n := MakeNodeFromExprs(elems) + if n == nil { + n = &sym.Node + } return MakeList( - MakeNodeFromExprs(elems), + *n, elems, ), nil diff --git a/bootstrap/pkg/core/symbol.go b/bootstrap/pkg/core/symbol.go index c285fdd..d11786d 100644 --- a/bootstrap/pkg/core/symbol.go +++ b/bootstrap/pkg/core/symbol.go @@ -19,7 +19,6 @@ along with this program. If not, see . package core import ( - "fmt" "strings" "serene-lang.org/bootstrap/pkg/ast" @@ -86,7 +85,7 @@ func MakeSymbol(n Node, s string) (*Symbol, IError) { name = parts[1] nsPart = parts[0] default: - return nil, MakePlainError(fmt.Sprintf("can't create a symbol from '%s'. More that on '/' is illegal.", s)) + return nil, MakeSyntaxErrorf(n, "can't create a symbol from '%s'. More that on '/' is illegal.", s) } return &Symbol{ diff --git a/bootstrap/pkg/core/types.go b/bootstrap/pkg/core/types.go index 78ac526..ebcbb99 100644 --- a/bootstrap/pkg/core/types.go +++ b/bootstrap/pkg/core/types.go @@ -122,6 +122,12 @@ func toRepresentables(ast IColl) []IRepresentable { return params } +// TODO: I don't like the current interface and signatures of these +// 'Make*Node*' functions. Refactor them to have the same interface +// For instance if we need to return a pointer to a Node all of them +// has to return a pointer not just one of them. The return type +// should be predictable + // MakeNodeFromLocation creates a new Node for the given Location `loc` func MakeNodeFromLocation(loc *ast.Location) Node { return Node{ @@ -139,17 +145,16 @@ func MakeNodeFromExpr(e IExpr) Node { // MakeNodeFromExprs creates a new Node from the given slice of `IExpr`s. // We use the Node to pass it to other IExpr constructors to // keep the reference to the original form in the input string -func MakeNodeFromExprs(es []IExpr) Node { +func MakeNodeFromExprs(es []IExpr) *Node { if len(es) == 0 { - // TODO: This is temporary, fix it. - panic("can't create a node from empty elements.") + return nil } firstLoc := es[0].GetLocation() endLoc := es[len(es)-1].GetLocation() loc := ast.MakeLocation(firstLoc.GetSource(), firstLoc.GetStart(), endLoc.GetEnd()) - - return MakeNodeFromLocation(loc) + n := MakeNodeFromLocation(loc) + return &n } // MakeNode creates a new Node in the the given `input` that points to a