diff --git a/bootstrap/pkg/ast/ast.go b/bootstrap/pkg/ast/ast.go index b8f70cd..aea5f78 100644 --- a/bootstrap/pkg/ast/ast.go +++ b/bootstrap/pkg/ast/ast.go @@ -25,6 +25,8 @@ type NodeType int const ( Nil NodeType = iota Nothing + True + False Symbol Number List diff --git a/bootstrap/pkg/core/core.go b/bootstrap/pkg/core/core.go index 8d172da..7fcd0e4 100644 --- a/bootstrap/pkg/core/core.go +++ b/bootstrap/pkg/core/core.go @@ -41,12 +41,11 @@ func rep(rt *Runtime, line string) { fmt.Print("#############\n\n") } - result, err := Eval(rt, ast) - if err != nil { + result, e := Eval(rt, ast) + if e != nil { fmt.Printf("Error: %s\n", err) return } - Print(rt, result) } diff --git a/bootstrap/pkg/core/eval.go b/bootstrap/pkg/core/eval.go index e1fbbcc..7959544 100644 --- a/bootstrap/pkg/core/eval.go +++ b/bootstrap/pkg/core/eval.go @@ -19,9 +19,6 @@ along with this program. If not, see . package core import ( - "errors" - "fmt" - "serene-lang.org/bootstrap/pkg/ast" ) @@ -29,7 +26,7 @@ import ( // evaluation rules. For example if `form` is a list instead of the formal // evaluation of a list it will evaluate all the elements and return the // evaluated list -func evalForm(rt *Runtime, scope IScope, form IExpr) (IExpr, error) { +func evalForm(rt *Runtime, scope IScope, form IExpr) (IExpr, IError) { switch form.GetType() { case ast.Nil: @@ -44,14 +41,24 @@ func evalForm(rt *Runtime, scope IScope, form IExpr) (IExpr, error) { // * Otherwise throw an error case ast.Symbol: symbolName := form.(*Symbol).GetName() - expr := scope.Lookup(symbolName) - if expr == nil { - return nil, fmt.Errorf("can't resolve symbol '%s' in ns '%s'", symbolName, rt.CurrentNS().GetName()) + switch symbolName { + case "true": + return &True, nil + case "false": + return &False, nil + case "nil": + return &Nil, nil + default: + expr := scope.Lookup(symbolName) + + if expr == nil { + return nil, MakeRuntimeErrorf(rt, "can't resolve symbol '%s' in ns '%s'", symbolName, rt.CurrentNS().GetName()) + } + + return expr.Value, nil } - return expr.Value, nil - // Evaluate all the elements in the list instead of following the lisp convention case ast.List: var result []IExpr @@ -74,13 +81,13 @@ func evalForm(rt *Runtime, scope IScope, form IExpr) (IExpr, error) { } // Default case - return nil, errors.New("not implemented") + return nil, MakeError(rt, "not implemented") } // EvalForms evaluates the given expr `expressions` (it can be a list, block, symbol or anything else) // with the given runtime `rt` and the scope `scope`. -func EvalForms(rt *Runtime, scope IScope, expressions IExpr) (IExpr, error) { +func EvalForms(rt *Runtime, scope IScope, expressions IExpr) (IExpr, IError) { // EvalForms is the main and the most important evaluation function on Serene. // It's a long loooooooooooong function. Why? Well, Because we don't want to // waste call stack spots in order to have a well organized code. @@ -88,7 +95,7 @@ func EvalForms(rt *Runtime, scope IScope, expressions IExpr) (IExpr, error) { // a functional language we need to avoid unnecessary calls and keep as much // as possible in a loop. var ret IExpr - var err error + var err IError tco: for { @@ -154,7 +161,11 @@ tco: ret, err = Fn(rt, scope, list.Rest().(*List)) break tco // return - // List evaluation rules: + // case "if": + // ret, err = If(rt, scope, list.Rest().(*List)) + // break tco // return + + // list evaluation rules: // * The first element of the list has to be an expression which is callable // * An empty list evaluates to itself. default: @@ -185,7 +196,7 @@ tco: argList := exprs.(*List).Rest().(*List) - scope, e = MakeFnScope(fn.GetScope(), fn.GetParams(), argList) + scope, e = MakeFnScope(rt, fn.GetScope(), fn.GetParams(), argList) if e != nil { err = e ret = nil @@ -195,7 +206,7 @@ tco: expressions = fn.GetBody() continue tco default: - err = errors.New("don't know how to execute anything beside function") + err = MakeError(rt, "don't know how to execute anything beside function") ret = nil break tco } @@ -212,7 +223,7 @@ tco: // concept of Block with blocks from other languages which // specify by using `{}` or indent or what ever. Blocks in terms // of Serene are just arrays of expressions and nothing more. -func Eval(rt *Runtime, forms *Block) (IExpr, error) { +func Eval(rt *Runtime, forms *Block) (IExpr, IError) { if forms.Count() == 0 { // Nothing is literally Nothing return &Nothing, nil diff --git a/bootstrap/pkg/core/function.go b/bootstrap/pkg/core/function.go index e3372f0..f4c8c37 100644 --- a/bootstrap/pkg/core/function.go +++ b/bootstrap/pkg/core/function.go @@ -19,7 +19,6 @@ along with this program. If not, see . package core import ( - "errors" "fmt" "serene-lang.org/bootstrap/pkg/ast" @@ -89,13 +88,13 @@ func MakeFunction(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(parent IScope, bindings IColl, values IColl) (*Scope, error) { +func MakeFnScope(rt *Runtime, parent IScope, bindings IColl, values IColl) (*Scope, IError) { scope := MakeScope(parent.(*Scope)) // TODO: Implement destructuring if bindings.Count() > values.Count() { - return nil, errors.New("'binding' and 'valuse' size don't match") + return nil, MakeError(rt, "'binding' and 'valuse' size don't match") } binds := bindings.ToSlice() diff --git a/bootstrap/pkg/core/parser.go b/bootstrap/pkg/core/parser.go index f3741e0..c8b94c7 100644 --- a/bootstrap/pkg/core/parser.go +++ b/bootstrap/pkg/core/parser.go @@ -19,8 +19,6 @@ along with this program. If not, see . package core import ( - "errors" - "fmt" "strings" "unicode" ) @@ -88,6 +86,15 @@ func (sp *StringParser) Buffer() *[]string { // END: IParsable --- +func makeErrorAtPoint(p IParsable, msg string, a ...interface{}) IError { + n := MakeSinglePointNode(p.Buffer(), p.GetLocation()) + return MakeParsetimeErrorf(n, msg, a) +} + +func makeErrorFromError(parser IParsable, e error) IError { + return makeErrorAtPoint(parser, "%w", e) +} + func contains(s []rune, c rune) bool { for _, v := range s { if v == c { @@ -103,19 +110,20 @@ func isValidForSymbol(char string) bool { return contains(validChars, c) || unicode.IsLetter(c) || unicode.IsDigit(c) } -func readRawSymbol(parser IParsable) (IExpr, error) { +func readRawSymbol(parser IParsable) (IExpr, IError) { c := parser.peek(false) var symbol string if c == nil { - return nil, errors.New("unexpected enf of file while parsing a symbol") + return nil, makeErrorAtPoint(parser, "unexpected enf of file while parsing a symbol") } if isValidForSymbol(*c) { parser.next(false) symbol = *c } else { - return nil, fmt.Errorf("unexpected character: got '%s', expected a symbol at %d", + return nil, makeErrorAtPoint(parser, + "unexpected character: got '%s', expected a symbol at %d", *c, parser.GetLocation(), ) @@ -141,7 +149,7 @@ func readRawSymbol(parser IParsable) (IExpr, error) { return MakeSymbol(node, symbol), nil } -func readNumber(parser IParsable, neg bool) (IExpr, error) { +func readNumber(parser IParsable, neg bool) (IExpr, IError) { isDouble := false result := "" @@ -157,7 +165,7 @@ func readNumber(parser IParsable, neg bool) (IExpr, error) { } if *c == "." && isDouble { - return nil, errors.New("a double with more that one '.' ???") + return nil, makeErrorAtPoint(parser, "a double with more that one '.' ???") } if *c == "." { @@ -177,14 +185,20 @@ func readNumber(parser IParsable, neg bool) (IExpr, error) { } } - return MakeNumberFromStr(result, isDouble) + value, err := MakeNumberFromStr(result, isDouble) + + if err != nil { + return nil, makeErrorFromError(parser, err) + } + + return value, nil } -func readSymbol(parser IParsable) (IExpr, error) { +func readSymbol(parser IParsable) (IExpr, IError) { c := parser.peek(false) if c == nil { - return nil, errors.New("unexpected end of file while scanning a symbol") + return nil, makeErrorAtPoint(parser, "unexpected end of file while scanning a symbol") } // if c == "\"" { @@ -218,13 +232,13 @@ func readSymbol(parser IParsable) (IExpr, error) { return readRawSymbol(parser) } -func readList(parser IParsable) (IExpr, error) { +func readList(parser IParsable) (IExpr, IError) { list := []IExpr{} for { c := parser.peek(true) if c == nil { - return nil, errors.New("reaching the end of file while reading a list") + return nil, makeErrorAtPoint(parser, "reaching the end of file while reading a list") } if *c == ")" { parser.next(true) @@ -243,7 +257,7 @@ func readList(parser IParsable) (IExpr, error) { return MakeList(list), nil } -func readComment(parser IParsable) (IExpr, error) { +func readComment(parser IParsable) (IExpr, IError) { for { c := parser.next(false) if c == nil || *c == "\n" { @@ -252,7 +266,7 @@ func readComment(parser IParsable) (IExpr, error) { } } -func readQuotedExpr(parser IParsable) (IExpr, error) { +func readQuotedExpr(parser IParsable) (IExpr, IError) { expr, err := readExpr(parser) if err != nil { return nil, err @@ -265,15 +279,15 @@ func readQuotedExpr(parser IParsable) (IExpr, error) { }), nil } -func readUnquotedExpr(parser IParsable) (IExpr, error) { +func readUnquotedExpr(parser IParsable) (IExpr, IError) { c := parser.peek(true) if c == nil { - return nil, errors.New("end of file while reading an unquoted expression") + return nil, makeErrorAtPoint(parser, "end of file while reading an unquoted expression") } var sym IExpr - var err error + var err IError var expr IExpr node := MakeNode(parser.Buffer(), parser.GetLocation(), parser.GetLocation()) @@ -295,7 +309,7 @@ func readUnquotedExpr(parser IParsable) (IExpr, error) { return MakeList([]IExpr{sym, expr}), nil } -func readQuasiquotedExpr(parser IParsable) (IExpr, error) { +func readQuasiquotedExpr(parser IParsable) (IExpr, IError) { expr, err := readExpr(parser) if err != nil { return nil, err @@ -308,7 +322,7 @@ func readQuasiquotedExpr(parser IParsable) (IExpr, error) { }), nil } -func readExpr(parser IParsable) (IExpr, error) { +func readExpr(parser IParsable) (IExpr, IError) { loop: c := parser.next(true) @@ -347,7 +361,7 @@ loop: } -func ParseToAST(input string) (*Block, error) { +func ParseToAST(input string) (*Block, IError) { var ast Block parser := StringParser{ diff --git a/bootstrap/pkg/core/reader.go b/bootstrap/pkg/core/reader.go index 2217fa3..3c638d9 100644 --- a/bootstrap/pkg/core/reader.go +++ b/bootstrap/pkg/core/reader.go @@ -18,6 +18,6 @@ along with this program. If not, see . package core -func ReadString(input string) (*Block, error) { +func ReadString(input string) (*Block, IError) { return ParseToAST(input) } diff --git a/bootstrap/pkg/core/sforms.go b/bootstrap/pkg/core/sforms.go index 5851014..1ebf705 100644 --- a/bootstrap/pkg/core/sforms.go +++ b/bootstrap/pkg/core/sforms.go @@ -19,15 +19,13 @@ along with this program. If not, see . package core import ( - "errors" - "serene-lang.org/bootstrap/pkg/ast" ) // Def defines a global binding in the current namespace. The first // arguments in `args` has to be a symbol ( none ns qualified ) and // the second param should be the value of the binding -func Def(rt *Runtime, scope IScope, args *List) (IExpr, error) { +func Def(rt *Runtime, scope IScope, args *List) (IExpr, IError) { // TODO: Add support for docstrings and meta @@ -36,7 +34,7 @@ func Def(rt *Runtime, scope IScope, args *List) (IExpr, error) { name := args.First() if name.GetType() != ast.Symbol { - return nil, errors.New("the first argument of 'def' has to be a symbol") + return nil, MakeError(rt, "the first argument of 'def' has to be a symbol") } sym := name.(*Symbol) @@ -53,15 +51,15 @@ func Def(rt *Runtime, scope IScope, args *List) (IExpr, error) { return sym, nil } - return nil, errors.New("'def' form need at least 2 arguments") + return nil, MakeError(rt, "'def' form need at least 2 arguments") } // Fn defines a function inside the given scope `scope` with the given `args`. // `args` contains the arugment list, docstring and body of the function. -func Fn(rt *Runtime, scope IScope, args *List) (IExpr, error) { +func Fn(rt *Runtime, scope IScope, args *List) (IExpr, IError) { if args.Count() < 1 { - return nil, errors.New("'fn' needs at least an arguments list") + return nil, MakeError(rt, "'fn' needs at least an arguments list") } var params IColl diff --git a/bootstrap/pkg/core/types.go b/bootstrap/pkg/core/types.go index 41fb5c6..9b62d50 100644 --- a/bootstrap/pkg/core/types.go +++ b/bootstrap/pkg/core/types.go @@ -63,3 +63,7 @@ func MakeNode(input *[]string, start int, end int) Node { location: ast.MakeLocation(input, start, end), } } + +func MakeSinglePointNode(input *[]string, point int) Node { + return MakeNode(input, point, point) +}