serene-golang-implementation/bootstrap/pkg/core/eval.go

689 lines
20 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 (
"fmt"
"serene-lang.org/bootstrap/pkg/ast"
"serene-lang.org/bootstrap/pkg/errors"
)
func restOfExprs(es []IExpr, i int) []IExpr {
if len(es)-1 > i {
return es[i+1:]
}
return []IExpr{}
}
// 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
case ast.Fn:
return form, nil
case ast.String:
return form, nil
// Keyword evaluation rules:
// * Keywords evaluates to themselves with respect to a
// possible namespace alias. For example `::core/xyz`
// will evaluates to `:serene.core/xyz` only if the ns
// `serene.core` is loaded in the current ns with the
// `core` alias. Also `::xyz` will evaluete to
// `:<CURRENT_NS>/xyz`
case ast.Keyword:
// Eval initialize the keyword and MUTATES the state of the keyword
// and returns the updated keyword which would be the same
return form.(*Keyword).Eval(rt, scope)
// 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:
var nsName string
sym := form.(*Symbol)
symbolName := sym.GetName()
switch symbolName {
case "true":
return MakeTrue(MakeNodeFromExpr(form)), nil
case "false":
return MakeFalse(MakeNodeFromExpr(form)), nil
case "nil":
return MakeNil(MakeNodeFromExpr(form)), nil
default:
var expr *Binding
ns := scope.GetNS(rt)
if sym.IsNSQualified() {
// Whether a namespace with the given alias loaded or not
if !ns.hasExternal(sym.GetNSPart()) {
return nil, MakeError(rt, sym,
fmt.Sprintf("Namespace '%s' is no loaded", sym.GetNSPart()),
)
}
expr = ns.LookupGlobal(rt, sym)
nsName = sym.GetNSPart()
} else {
expr = scope.Lookup(rt, symbolName)
nsName = ns.GetName()
}
if expr == nil {
return nil, MakeRuntimeError(
rt,
sym,
errors.E0003,
fmt.Sprintf(
"can't resolve symbol '%s' in ns '%s'",
symbolName,
nsName,
),
)
}
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(MakeNodeFromExpr(lst), result), nil
}
// Default case
return nil, MakeError(rt, form, fmt.Sprintf("support for '%d' is not implemented", form.GetType()))
}
// 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.
//
// `expressions` is argument is basically a tree of expressions which
// this function walks over and rewrite it as necessary. The main purpose
// of rewriting the tree is to eliminate any unnecessary function call.
// This way we can eliminate tail calls and run everything faster.
//
// Execution scopes are just regular scopes that are attached to expressions
// in order to specify the scope that they need to get executed in. Since
// we rewrite the tree some of the nodes of the tree (expressions) has different
// scope so we need to attach a scope to those expressions. An example would be
// the `let` special form. It creates a new scope for its body, after creating
// the scope we will attach it to all the expressions in the body and rewrite
// the tree to replace the original `let` node with the expressions from the
// body of `let` and reloop over the tree. So the tree contains some nodes
// from the `let` body with an execution scope and the rest of tree with
// no execution scope (no attached scope) which we will use the `scope`
// that we got as an argument.
var ret IExpr
var err IError
tco:
// TODO: With the new tree rewrite we might be able to get ride of this `for`
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 i := 0; i < len(exprs); i++ {
forms := exprs[i]
executionScope := forms.GetExecutionScope()
scope := scope
if executionScope != nil {
scope = executionScope
}
if rt.IsDebugMode() {
fmt.Printf("[DEBUG] Evaluating forms in NS: %s, Forms: %s\n", scope.GetNS(rt).GetName(), forms)
fmt.Printf("[DEBUG] * State: I: %d, Exprs: %s\n", i, exprs)
}
// Evaluate any internal instruction that has to be run.
// Instructions should change the return value, but errors
// are ok
if forms.GetType() == ast.Instruction {
e := ProcessInstruction(rt, forms.(*Instruction))
if e != nil {
return nil, e
}
continue
}
// Evaluating forms one by one
if forms.GetType() != ast.List {
ret, err = evalForm(rt, scope, forms)
if err != nil {
return nil, err
}
continue body
}
// Expand macroes that exists in the given array of expression `forms`.
// Since this implementation of Serene is an interpreter, the line
// between compile time and runtime is unclear (afterall every thing
// is happening in runtime). So we need to expand macroes before evaluating
// other forms. In the future we might want to cache the expanded AST
// as a cache and some sort of a bytecode for faster evaluation.
forms, err = macroexpand(rt, scope, forms)
if err != nil {
return nil, err
}
if forms.GetType() != ast.List {
return evalForm(rt, scope, forms)
}
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 {
// `ns` evaluation rules:
// * The first element has to be a symbol representing the
// name of the namespace. ( We won't evaluate the first
// element )
// 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:
// * Only takes one argument
// * Returns the argument without evaluating it
case "quote":
// Including the `quote` itself
if list.Count() != 2 {
return nil, MakeError(rt, list, "'quote' quote only accepts one argument.")
}
ret = list.Rest().First()
err = nil
continue body // no rewrite
// case "quasiquote-expand":
// return quasiquote(list.Rest().First()), nil
// // For `quasiquote` evaluation rules, check out the documentation on
// // the `quasiquote` function in `quasiquote.go`
// case "quasiquote":
// expressions = quasiquote(list.Rest().First())
// continue tco // Loop over to execute the new expressions
// TODO: Implement `list` in serene itself when we have destructuring available
// 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
// Concats all the collections together.
case "concat":
evaledForms, e := evalForm(rt, scope, list.Rest().(*List))
if e != nil {
return nil, e
}
lists := evaledForms.(*List).ToSlice()
result := []IExpr{}
for _, lst := range lists {
if lst.GetType() != ast.List {
return nil, MakeError(rt, lst, fmt.Sprintf("don't know how to concat '%s'", lst.String()))
}
result = append(result, lst.(*List).ToSlice()...)
}
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
// Calls the `Cons` function on the second argument to cons the first arg to it.
// In terms of a list, cons adds the first argument to as the new head of the list
// given in the second argument.
case "cons":
if list.Count() != 3 {
return nil, MakeError(rt, list, "'cons' needs exactly 3 arguments")
}
evaledForms, e := evalForm(rt, scope, list.Rest().(*List))
if e != nil {
return nil, e
}
coll, ok := evaledForms.(*List).Rest().First().(IColl)
if !ok {
return nil, MakeError(rt, list, "second arg of 'cons' has to be a collection")
}
ret, err = coll.Cons(evaledForms.(*List).First()), nil
if err != nil {
return nil, err
}
continue body // no rewrite
// `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))
if err != nil {
return nil, err
}
continue body // no rewrite
// `defmacro` evaluation rules:
// * The first argument has to be a symbol
// * The second argument has to be a list of argument for the macro
// * The rest of the arguments will form a block that acts as the
// body of the macro.
case "defmacro":
ret, err = DefMacro(rt, scope, list)
if err != nil {
return nil, err
}
continue body // no rewrite
// `macroexpand` evaluation rules:
// * It has to have only one argument
// * It WILL evaluate the only argument and tries to expand it
// as a macro and returns the expanded forms.
case "macroexpand":
if list.Count() != 2 {
return nil, MakeError(rt, list, "'macroexpand' needs exactly one argument.")
}
evaledForm, e := evalForm(rt, scope, list.Rest().(*List))
if e != nil {
return nil, e
}
ret, err = macroexpand(rt, scope, evaledForm.(*List).First())
if err != nil {
return nil, err
}
continue body // no rewrite
// `fn` evaluation rules:
// * It needs at least a collection of arguments
// * 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:
// * 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, args, "'if' needs exactly 3 aruments")
}
pred, e := EvalForms(rt, scope, args.First())
result := pred.GetType()
if e != nil {
return nil, e
}
if (result == ast.Bool && pred.(*Bool).IsFalse()) || result == ast.Nil {
// Falsy clause
exprs = append([]IExpr{args.Rest().Rest().First()}, restOfExprs(exprs, i)...)
} else {
// Truthy clause
exprs = append([]IExpr{args.Rest().First()}, restOfExprs(exprs, i)...)
}
i = 0
goto body // rewrite
// `do` evaluation rules:
// * Evaluate the body as a new block in the TCO loop
// and return the result of the last expression
case "do":
// create a new slice of expressions by using the
// do body and merging it by the remaining expressions
// in the old `exprs` value and loop over it
doExprs := list.Rest().(*List).ToSlice()
exprs = append(doExprs, exprs[i+1:]...)
i = 0
goto body // rewrite
// TODO: Implement `eval` as a native function
// `eval` evaluation rules:
// * It only takes on arguments.
// * The argument has to be a form. For example if we pass a string
// to it as an argument that contains some expressions it will
// evaluate the string as string which will result to the same
// string. So IT DOES NOT READ the argument.
// * It will evaluate the given form as the argument and return
// the result.
case "eval":
if list.Count() != 2 {
return nil, MakeError(rt, list, "'eval' needs exactly 1 arguments")
}
form, e := evalForm(rt, scope, list.Rest().(*List))
if e != nil {
return nil, e
}
ret, err = EvalForms(rt, scope, form)
if err != nil {
return nil, err
}
continue body // no rewrite
// `let` evaluation rules:
// Let's assume the following:
// L = (let (A B C D) BODY)
// * Create a new scope which has the current scope as the parent
// * Evaluate the bindings by evaluating `B` and bind it to the name `A`
// in the scope.
// * Repeat the prev step for expr D and name C
// * Eval the block `BODY` using the created scope and return the result
// which is the result of the last expre in `BODY`
case "let":
if list.Count() < 2 {
return nil, MakeError(rt, list, "'let' needs at list 1 aruments")
}
letScope := MakeScope(rt, scope.(*Scope), nil)
// 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, list.Rest().First(), "'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 {
return nil, MakeError(rt, name, "'let' doesn't support desbbtructuring yet, use a symbol.")
}
// 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)
}
changeExecutionScope(body, letScope)
exprs = append(body, exprs[i+1:]...)
i = 0
goto body
// 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
listExprs, e := evalForm(rt, scope, list)
if e != nil {
return nil, e
}
f := listExprs.(*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 {
return nil, e
}
argList := listExprs.(*List).Rest().(*List)
fnScope, e := MakeFnScope(rt, fn.GetScope(), fn.GetParams(), argList)
if e != nil {
return nil, e
}
rt.Stack.Push(list, fn)
body := append(
fn.GetBody().ToSlice(),
// Add the PopStack instruction to clean up the stack after
// returning from the function.
MakeStackPop(rt),
)
changeExecutionScope(body, fnScope)
exprs = append(body, restOfExprs(exprs, i)...)
goto body // rewrite
// If the function was a native function which is represented
// by the `NativeFunction` struct
case ast.NativeFn:
fn := f.(*NativeFunction)
rt.Stack.Push(list, fn)
ret, err = fn.Apply(
rt,
scope,
MakeNodeFromExpr(fn),
listExprs.(*List),
)
if err != nil {
return nil, err
}
rt.Stack.Pop()
continue body // no rewrite
default:
err = MakeError(rt, f, "don't know how to execute anything beside function")
ret = nil
break tco
}
}
}
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
}
// EvalNSBody evals the body of the given namespace `ns` using the given
// runtime `rt`. It makes sure that the body starts with a `ns` special
// form with the same name as the ns argument.
func EvalNSBody(rt *Runtime, ns *Namespace) (*Namespace, IError) {
body := ns.getForms()
exprs := body.ToSlice()
if len(exprs) == 0 {
return nil, MakeSemanticError(
rt,
ns,
errors.E0001,
fmt.Sprintf("the 'ns' form is missing from '%s'", ns.GetName()),
)
}
if exprs[0].GetType() == ast.List {
firstForm := exprs[0].(*List).First()
if firstForm.GetType() == ast.Symbol && firstForm.(*Symbol).GetName() == "ns" {
_, err := EvalForms(rt, ns.GetRootScope(), body)
if err != nil {
return nil, err
}
return ns, nil
}
}
return nil, MakeError(rt, ns, fmt.Sprintf("the 'ns' form is missing from '%s'", ns.GetName()))
}