Add the `let` special form alongside with locatable errors
Add the `let` sform not in the triditional way. In our case the let scope is available to the bindings. Similar to clojure's let and let* in triditional sense. Also add the function `MakeErrorFor` which gets an extra arg, an `IExpr` and returns an error with the location of that expression.
This commit is contained in:
parent
d86b47c283
commit
688d169286
|
@ -41,6 +41,22 @@ type Location struct {
|
|||
knownLocation bool
|
||||
}
|
||||
|
||||
func (l *Location) GetStart() int {
|
||||
return l.start
|
||||
}
|
||||
|
||||
func (l *Location) GetEnd() int {
|
||||
return l.end
|
||||
}
|
||||
|
||||
func (l *Location) GetSource() *[]string {
|
||||
return l.source
|
||||
}
|
||||
|
||||
func (l *Location) IsKnownLocaiton() bool {
|
||||
return l.knownLocation
|
||||
}
|
||||
|
||||
type ILocatable interface {
|
||||
GetLocation() Location
|
||||
}
|
||||
|
|
|
@ -49,6 +49,15 @@ func MakeError(rt *Runtime, msg string) IError {
|
|||
}
|
||||
}
|
||||
|
||||
func MakeErrorFor(rt *Runtime, e IExpr, msg string) IError {
|
||||
loc := e.GetLocation()
|
||||
|
||||
return &Error{
|
||||
Node: MakeNodeFromLocation(loc),
|
||||
msg: msg,
|
||||
}
|
||||
}
|
||||
|
||||
func MakeRuntimeErrorf(rt *Runtime, msg string, a ...interface{}) IError {
|
||||
return &Error{
|
||||
msg: fmt.Sprintf(msg, a...),
|
||||
|
|
|
@ -197,7 +197,57 @@ tco:
|
|||
expressions = MakeBlock(list.Rest().(*List).ToSlice())
|
||||
continue tco // Loop over to execute the new expressions
|
||||
|
||||
// case "let":
|
||||
case "let":
|
||||
if list.Count() < 2 {
|
||||
return nil, MakeError(rt, "'let' needs at list 1 aruments")
|
||||
}
|
||||
|
||||
letScope := MakeScope(scope.(*Scope))
|
||||
|
||||
// Since we're using IColl for the bindings, we can use either lists
|
||||
// or vectors or even hashmaps for bindings
|
||||
var bindings IColl
|
||||
bindings = list.Rest().First().(IColl)
|
||||
|
||||
body := list.Rest().Rest().(*List).ToSlice()
|
||||
|
||||
if bindings.Count()%2 != 0 {
|
||||
return nil, MakeError(rt, "'let' bindings has to have even number of forms.")
|
||||
}
|
||||
|
||||
for {
|
||||
// We're reducing over bindings here
|
||||
if bindings.Count() == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
name := bindings.First()
|
||||
expr := bindings.Rest().First()
|
||||
|
||||
// TODO: We need to destruct the bindings here and remove this check
|
||||
// for the symbol type
|
||||
if name.GetType() != ast.Symbol {
|
||||
err := MakeErrorFor(rt, name, "'let' doesn't support desbbtructuring yet, use a symbol.")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// You might be wondering why we're using `EvalForms` here to evaluate
|
||||
// the exprs in bindings, what about TCO ?
|
||||
// Well, It's called TAIL call optimization for a reason. Exprs in the
|
||||
// bindings are not tail calls
|
||||
evaluatedExpr, e := EvalForms(rt, letScope, expr)
|
||||
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
|
||||
letScope.Insert(name.String(), evaluatedExpr, false)
|
||||
bindings = bindings.Rest().Rest().(IColl)
|
||||
}
|
||||
|
||||
expressions = MakeBlock(body)
|
||||
scope = letScope
|
||||
continue tco
|
||||
|
||||
// list evaluation rules:
|
||||
// * The first element of the list has to be an expression which is callable
|
||||
|
|
|
@ -27,5 +27,7 @@ func Print(rt *Runtime, ast IPrintable) {
|
|||
}
|
||||
|
||||
func PrintError(rt *Runtime, err IError) {
|
||||
fmt.Printf("Error: %s\n", err.String())
|
||||
loc := err.GetLocation()
|
||||
fmt.Printf("Error: %s\nAt: %d to %d\n", err.String(), loc.GetStart(), loc.GetEnd())
|
||||
|
||||
}
|
||||
|
|
|
@ -58,12 +58,17 @@ func (n Node) GetLocation() ast.Location {
|
|||
return n.location
|
||||
}
|
||||
|
||||
func MakeNode(input *[]string, start int, end int) Node {
|
||||
// Create a new Node for the given Location `loc`
|
||||
func MakeNodeFromLocation(loc ast.Location) Node {
|
||||
return Node{
|
||||
location: ast.MakeLocation(input, start, end),
|
||||
location: loc,
|
||||
}
|
||||
}
|
||||
|
||||
func MakeNode(input *[]string, start int, end int) Node {
|
||||
return MakeNodeFromLocation(ast.MakeLocation(input, start, end))
|
||||
}
|
||||
|
||||
func MakeSinglePointNode(input *[]string, point int) Node {
|
||||
return MakeNode(input, point, point)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue