From cba74e29af00eaed001fc64dcf8ca1834fd8b976 Mon Sep 17 00:00:00 2001 From: Sameer Rahmani Date: Sun, 15 Nov 2020 21:38:09 +0000 Subject: [PATCH] Add the support for special forms --- bootstrap/pkg/core/def.go | 64 +++++++++++++++++++++++++ bootstrap/pkg/core/eval.go | 35 ++++++++++++-- bootstrap/pkg/namespace/namespace.go | 9 +++- bootstrap/pkg/types/{seq.go => coll.go} | 6 +++ bootstrap/pkg/types/function.go | 28 +++++++++++ bootstrap/pkg/types/list.go | 51 +++++++++++++++++--- 6 files changed, 182 insertions(+), 11 deletions(-) create mode 100644 bootstrap/pkg/core/def.go rename bootstrap/pkg/types/{seq.go => coll.go} (90%) create mode 100644 bootstrap/pkg/types/function.go diff --git a/bootstrap/pkg/core/def.go b/bootstrap/pkg/core/def.go new file mode 100644 index 0000000..5daeb5a --- /dev/null +++ b/bootstrap/pkg/core/def.go @@ -0,0 +1,64 @@ +/* + 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 ( + "errors" + + "serene-lang.org/bootstrap/pkg/ast" + "serene-lang.org/bootstrap/pkg/runtime" + "serene-lang.org/bootstrap/pkg/scope" + "serene-lang.org/bootstrap/pkg/types" +) + +/** TODO: +Make sure to implement INode for Def as well to be able to point +to the write place at the input stream for error messages. +*/ + +type def struct{} + +var Def = def{} + +func (d def) Apply(rt *runtime.Runtime, scope scope.IScope, args *types.List) (types.IExpr, error) { + switch args.Count() { + case 2: + name := args.First() + + if name.GetType() != ast.Symbol { + return nil, errors.New("The first argument of 'def' has to be a symbol") + } + + sym := name.(*types.Symbol) + + //value = args.Rest().(*types.List).First() + valueExpr := args.Rest().First() + value, err := EvalForm(rt, scope, valueExpr) + + if err != nil { + return nil, err + } + + ns := rt.CurrentNS() + ns.DefineGlobal(sym.GetName(), value, true) + return sym, nil + } + + return nil, errors.New("'def' form need at least 2 arguments") +} diff --git a/bootstrap/pkg/core/eval.go b/bootstrap/pkg/core/eval.go index 41b0bb0..7d48c30 100644 --- a/bootstrap/pkg/core/eval.go +++ b/bootstrap/pkg/core/eval.go @@ -28,13 +28,21 @@ import ( "serene-lang.org/bootstrap/pkg/types" ) -func evalForm(rt *runtime.Runtime, scope scope.IScope, form types.IExpr) (types.IExpr, error) { +var sFormsTable = map[string]types.ICallable{ + "def": Def, +} + +func GetBuildIn(s *types.Symbol) (types.ICallable, bool) { + return sFormsTable[s.GetName()] +} + +func EvalForm(rt *runtime.Runtime, scope scope.IScope, form types.IExpr) (types.IExpr, error) { switch form.GetType() { case ast.Nil: case ast.Number: return form, nil - // Symbol Evaluation Rules: + // 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. @@ -49,6 +57,27 @@ func evalForm(rt *runtime.Runtime, scope scope.IScope, form types.IExpr) (types. return expr.Value, nil + // List evaluation rules: + // * The first element of the list has to be an expression which implements `ICallable` + // * The rest o the elements have to be evaluated only after we have determind the the + // first element is `ICallable` and it's not a macro or special form. + // * An empty list evaluates to itself. + case ast.List: + list := form.(*types.List) + if list.Count() == 0 { + return list, nil + } + first := form.(*types.List).First() + + if first.GetType() == ast.Symbol { + sform, ok := GetBuiltIn(first.(*types.Symbol)) + if ok { + return sform.Apply(rt, scope, list.Rest()) + } + } + + fmt.Printf("no builtin %#v", first) + } // Default case @@ -64,7 +93,7 @@ func Eval(rt *runtime.Runtime, forms types.ASTree) (types.IExpr, error) { for _, form := range forms { // v is here to shut up the linter - v, err := evalForm(rt, rt.CurrentNS().GetRootScope(), form) + v, err := EvalForm(rt, rt.CurrentNS().GetRootScope(), form) if err != nil { return nil, err diff --git a/bootstrap/pkg/namespace/namespace.go b/bootstrap/pkg/namespace/namespace.go index 523d095..a40630d 100644 --- a/bootstrap/pkg/namespace/namespace.go +++ b/bootstrap/pkg/namespace/namespace.go @@ -20,7 +20,10 @@ along with this program. If not, see . // of it to be used as the basic blocks of Serene's namespaces. package namespace -import "serene-lang.org/bootstrap/pkg/scope" +import ( + "serene-lang.org/bootstrap/pkg/scope" + "serene-lang.org/bootstrap/pkg/types" +) type INamespace interface { DefineGlobal() @@ -37,7 +40,9 @@ type Namespace struct { externals map[string]Namespace } -func (n *Namespace) DefineGlobal() {} +func (n *Namespace) DefineGlobal(k string, v types.IExpr, public bool) { + n.rootScope.Insert(k, v, public) +} func (n *Namespace) LookupGlobal() {} diff --git a/bootstrap/pkg/types/seq.go b/bootstrap/pkg/types/coll.go similarity index 90% rename from bootstrap/pkg/types/seq.go rename to bootstrap/pkg/types/coll.go index e638c2c..cbae993 100644 --- a/bootstrap/pkg/types/seq.go +++ b/bootstrap/pkg/types/coll.go @@ -19,4 +19,10 @@ along with this program. If not, see . package types type ISeq interface { + First() IExpr + Rest() ISeq +} + +type ICountable interface { + Count() int } diff --git a/bootstrap/pkg/types/function.go b/bootstrap/pkg/types/function.go new file mode 100644 index 0000000..b52df54 --- /dev/null +++ b/bootstrap/pkg/types/function.go @@ -0,0 +1,28 @@ +/* + 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 types + +import ( + "serene-lang.org/bootstrap/pkg/runtime" + "serene-lang.org/bootstrap/pkg/scope" +) + +type ICallable interface { + Apply(rt *runtime.Runtime, scope scope.IScope, args *List) (IExpr, error) +} diff --git a/bootstrap/pkg/types/list.go b/bootstrap/pkg/types/list.go index 95be66f..3b7e47b 100644 --- a/bootstrap/pkg/types/list.go +++ b/bootstrap/pkg/types/list.go @@ -25,20 +25,25 @@ import ( "serene-lang.org/bootstrap/pkg/ast" ) +/** WARNING: +This List implementation may look simple and performant but since +we're using a slice here. But in fact it's not memory effecient at +all. We need to rewrite this later to be a immutable and persistent +link list of cons. +*/ + type List struct { Node exprs []IExpr } -func (l List) Eval() IExpr { - return &Nil -} +// Implementing IExpr for List --- -func (l List) GetType() ast.NodeType { +func (l *List) GetType() ast.NodeType { return ast.List } -func (l List) String() string { +func (l *List) String() string { var strs []string for _, e := range l.exprs { strs = append(strs, e.String()) @@ -46,12 +51,46 @@ func (l List) String() string { return fmt.Sprintf("(%s)", strings.Join(strs, " ")) } -func (l List) ToDebugStr() string { +func (l *List) ToDebugStr() string { return fmt.Sprintf("%#v", l) } +// END: IExpr --- + +// Implementing ISeq for List --- + +func (l *List) First() IExpr { + if l.Count() == 0 { + return Nil + } + return l.exprs[0] +} + +func (l *List) Rest() *List { + if l.Count() < 2 { + return MakeEmptyList() + } + return MakeList(l.exprs[1:]) +} + +// END: ISeq --- + +// Implementing ICountable for List --- + +func (l *List) Count() int { + return len(l.exprs) +} + +// END: ICountable --- + func MakeList(elements []IExpr) *List { return &List{ exprs: elements, } } + +func MakeEmptyList() *List { + return &List{ + exprs: []IExpr{}, + } +}