/* Serene --- Yet an other Lisp Copyright (c) 2020 Sameer Rahmani 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 . */ 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 // `:/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())) }