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