Add IError interface to represent system errors
This commit is contained in:
parent
57714accfc
commit
d92150f0c3
|
@ -25,6 +25,8 @@ type NodeType int
|
|||
const (
|
||||
Nil NodeType = iota
|
||||
Nothing
|
||||
True
|
||||
False
|
||||
Symbol
|
||||
Number
|
||||
List
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -19,9 +19,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
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
|
||||
|
|
|
@ -19,7 +19,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
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()
|
||||
|
|
|
@ -19,8 +19,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
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{
|
||||
|
|
|
@ -18,6 +18,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
|
||||
package core
|
||||
|
||||
func ReadString(input string) (*Block, error) {
|
||||
func ReadString(input string) (*Block, IError) {
|
||||
return ParseToAST(input)
|
||||
}
|
||||
|
|
|
@ -19,15 +19,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue