344 lines
9.6 KiB
Go
344 lines
9.6 KiB
Go
/*
|
|
Serene --- Yet an other Lisp
|
|
|
|
Copyright (c) 2020 Sameer Rahmani <lxsameer@gnu.org>
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 2 of the License.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
package core
|
|
|
|
import (
|
|
"serene-lang.org/bootstrap/pkg/ast"
|
|
)
|
|
|
|
// evalForm evaluates the given expression `form` by a slightly different
|
|
// 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, IError) {
|
|
|
|
switch form.GetType() {
|
|
case ast.Nil:
|
|
return form, nil
|
|
case ast.Number:
|
|
return form, nil
|
|
|
|
// Symbol evaluation rules:
|
|
// * If it's a NS qualified symbol (NSQS), Look it up in the external symbol table of
|
|
// the current namespace.
|
|
// * If it's not a NSQS Look up the name in the current scope.
|
|
// * Otherwise throw an error
|
|
case ast.Symbol:
|
|
symbolName := form.(*Symbol).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
|
|
}
|
|
|
|
// Evaluate all the elements in the list instead of following the lisp convention
|
|
case ast.List:
|
|
var result []IExpr
|
|
|
|
lst := form.(*List)
|
|
|
|
for {
|
|
if lst.Count() > 0 {
|
|
expr, err := EvalForms(rt, scope, lst.First())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
result = append(result, expr)
|
|
lst = lst.Rest().(*List)
|
|
} else {
|
|
break
|
|
}
|
|
}
|
|
return MakeList(result), nil
|
|
}
|
|
|
|
// Default case
|
|
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, 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.
|
|
// In order to avoid stackoverflows and implement TCO ( which is a must for
|
|
// a functional language we need to avoid unnecessary calls and keep as much
|
|
// as possible in a loop.
|
|
var ret IExpr
|
|
var err IError
|
|
|
|
tco:
|
|
for {
|
|
// The TCO loop is there to take advantage or the fact that
|
|
// in order to call a function or a block we simply can change
|
|
// the value of the `expressions` and `scope`
|
|
var exprs []IExpr
|
|
|
|
// Block evaluation rules:
|
|
// * If empty, return Nothing
|
|
// * Otherwise evaluate the expressions in the block one by one
|
|
// and return the last result
|
|
if expressions.GetType() == ast.Block {
|
|
if expressions.(*Block).Count() == 0 {
|
|
return &Nothing, nil
|
|
}
|
|
exprs = expressions.(*Block).ToSlice()
|
|
} else {
|
|
exprs = []IExpr{expressions}
|
|
}
|
|
|
|
body:
|
|
for _, forms := range exprs {
|
|
// Evaluating forms one by one
|
|
|
|
if forms.GetType() != ast.List {
|
|
ret, err = evalForm(rt, scope, forms)
|
|
break tco // return ret, err
|
|
}
|
|
|
|
list := forms.(*List)
|
|
|
|
// Empty list evaluates to itself
|
|
if list.Count() == 0 {
|
|
ret = list
|
|
break tco // return &Nil, nil
|
|
}
|
|
|
|
rawFirst := list.First()
|
|
sform := ""
|
|
|
|
// Handling special forms by looking up the first
|
|
// element of the list. If it is a symbol, Grab
|
|
// the name and check it for build it forms.
|
|
//
|
|
// Note: If we don't care about recursion in any
|
|
// case we can simply extract it to a function
|
|
// for example in `def` since we are going to
|
|
// evaluate the value separately, we don't care
|
|
// about recursion because we're going to handle
|
|
// it wen we're evaluating the value. But in the
|
|
// case of let it's a different story.
|
|
if rawFirst.GetType() == ast.Symbol {
|
|
sform = rawFirst.(*Symbol).GetName()
|
|
}
|
|
|
|
switch sform {
|
|
|
|
// `quote` evaluation rules:
|
|
// * Only takes one argument
|
|
// * Returns the argument without evaluating it
|
|
case "quote":
|
|
// Including the `quote` itself
|
|
if list.Count() != 2 {
|
|
return nil, MakeErrorFor(rt, list, "'quote' quote only accepts one argument.")
|
|
}
|
|
return list.Rest().First(), nil
|
|
|
|
// `def` evaluation rules
|
|
// * The first argument has to be a symbol.
|
|
// * The second argument has to be evaluated and be used as
|
|
// the value.
|
|
// * Defines a global binding in the current namespace using
|
|
// the symbol name binded to the value
|
|
case "def":
|
|
ret, err = Def(rt, scope, list.Rest().(*List))
|
|
break tco // return
|
|
|
|
// `fn` evaluation rules:
|
|
// * It needs at least a collection of arguments
|
|
// * Defines an anonymous function.
|
|
case "fn":
|
|
ret, err = Fn(rt, scope, list.Rest().(*List))
|
|
break tco // return
|
|
|
|
// `if` evaluation rules:
|
|
// * It has to get only 3 arguments: PRED THEN ELSE
|
|
// * Evaluate only the PRED expression if the result
|
|
// is not `nil` or `false` evaluates THEN otherwise
|
|
// evaluate the ELSE expression and return the result.
|
|
case "if":
|
|
args := list.Rest().(*List)
|
|
if args.Count() != 3 {
|
|
return nil, MakeError(rt, "'if' needs exactly 3 aruments")
|
|
}
|
|
|
|
pred, err := EvalForms(rt, scope, args.First())
|
|
result := pred.GetType()
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if result != ast.False && result != ast.Nil {
|
|
// Truthy clause
|
|
expressions = args.Rest().First()
|
|
} else {
|
|
|
|
// Falsy clause
|
|
expressions = args.Rest().Rest().First()
|
|
}
|
|
|
|
continue tco // Loop over to execute the new expressions
|
|
|
|
// `do` evaluation rules:
|
|
// * Evaluate the body as a new block in the TCO loop
|
|
// and return the result of the last expression
|
|
case "do":
|
|
expressions = MakeBlock(list.Rest().(*List).ToSlice())
|
|
continue tco // Loop over to execute the new expressions
|
|
|
|
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
|
|
// * An empty list evaluates to itself.
|
|
default:
|
|
// Evaluating all the elements of the list
|
|
exprs, e := evalForm(rt, scope, list)
|
|
if e != nil {
|
|
err = e
|
|
ret = nil
|
|
break tco //return
|
|
}
|
|
|
|
f := exprs.(*List).First()
|
|
|
|
switch f.GetType() {
|
|
case ast.Fn:
|
|
// If the first element of the evaluated list is a function
|
|
// create a scope for it by creating the binding to the given
|
|
// parameters in the new scope and set the parent of it to
|
|
// the scope which the function defined in and then set the
|
|
// `expressions` to the body of function and loop again
|
|
fn := f.(*Function)
|
|
if e != nil {
|
|
err = e
|
|
ret = nil
|
|
break body //return
|
|
|
|
}
|
|
|
|
argList := exprs.(*List).Rest().(*List)
|
|
|
|
scope, e = MakeFnScope(rt, fn.GetScope(), fn.GetParams(), argList)
|
|
if e != nil {
|
|
err = e
|
|
ret = nil
|
|
break body //return
|
|
}
|
|
|
|
expressions = fn.GetBody()
|
|
continue tco
|
|
default:
|
|
err = MakeError(rt, "don't know how to execute anything beside function")
|
|
ret = nil
|
|
break tco
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret, err
|
|
}
|
|
|
|
// Eval the given `Block` of code with the given runtime `rt`.
|
|
// The Important part here is that any expression that we need
|
|
// to Eval has to be wrapped in a Block. Don't confused the
|
|
// 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, IError) {
|
|
if forms.Count() == 0 {
|
|
// Nothing is literally Nothing
|
|
return &Nothing, nil
|
|
}
|
|
|
|
v, err := EvalForms(rt, rt.CurrentNS().GetRootScope(), forms)
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return v, nil
|
|
}
|