From 95ec031ce6927752107a4e19561266b48be6d0b7 Mon Sep 17 00:00:00 2001 From: Sameer Rahmani Date: Sun, 15 Nov 2020 19:30:07 +0000 Subject: [PATCH] Add a very basic eval function for symbols --- bootstrap/cmd/repl.go | 2 +- bootstrap/cmd/root.go | 8 ++++ bootstrap/pkg/core/core.go | 18 ++++++-- bootstrap/pkg/core/eval.go | 77 ++++++++++++++++++++++++++++++++ bootstrap/pkg/eval/eval.go | 39 ---------------- bootstrap/pkg/parser/parser.go | 5 +-- bootstrap/pkg/printer/printer.go | 2 +- bootstrap/pkg/runtime/runtime.go | 4 ++ bootstrap/pkg/scope/scope.go | 24 +++++----- bootstrap/pkg/types/symbol.go | 9 ++-- bootstrap/pkg/types/types.go | 1 - 11 files changed, 124 insertions(+), 65 deletions(-) create mode 100644 bootstrap/pkg/core/eval.go delete mode 100644 bootstrap/pkg/eval/eval.go diff --git a/bootstrap/cmd/repl.go b/bootstrap/cmd/repl.go index 83a1330..30acd00 100644 --- a/bootstrap/cmd/repl.go +++ b/bootstrap/cmd/repl.go @@ -29,7 +29,7 @@ var replCmd = &cobra.Command{ Long: `Runs the local Serene's REPL to interact with Serene`, Run: func(cmd *cobra.Command, args []string) { // TODO: Get the debug value from a CLI flag - core.REPL(false) + core.REPL(debugMode) }, } diff --git a/bootstrap/cmd/root.go b/bootstrap/cmd/root.go index 7448365..9a187f4 100644 --- a/bootstrap/cmd/root.go +++ b/bootstrap/cmd/root.go @@ -24,6 +24,8 @@ import ( "github.com/spf13/cobra" ) +var debugMode bool + // rootCmd represents the base command when called without any subcommands var rootCmd = &cobra.Command{ Use: "Serene", @@ -54,4 +56,10 @@ func Execute() { func init() { cobra.OnInitialize() + rootCmd.PersistentFlags().BoolVar( + &debugMode, + "debug", + false, + "Turns on the debug mode.") + } diff --git a/bootstrap/pkg/core/core.go b/bootstrap/pkg/core/core.go index 7bc8a49..da740fa 100644 --- a/bootstrap/pkg/core/core.go +++ b/bootstrap/pkg/core/core.go @@ -30,12 +30,24 @@ import ( func rep(rt *runtime.Runtime, line string) { ast, err := reader.ReadString(line) + if err != nil { - fmt.Printf("Error: %s", err) + fmt.Println(err) + } + + if rt.IsDebugMode() { + fmt.Println("\n### DEBUG ###") + printer.Print(rt, ast) + fmt.Println("#############\n") + } + + result, err := Eval(rt, ast) + if err != nil { + fmt.Printf("Error: %s\n", err) return } - //eval.Eval(rt, ast) - printer.Print(rt, ast) + + printer.Print(rt, result) } /** TODO: diff --git a/bootstrap/pkg/core/eval.go b/bootstrap/pkg/core/eval.go new file mode 100644 index 0000000..41b0bb0 --- /dev/null +++ b/bootstrap/pkg/core/eval.go @@ -0,0 +1,77 @@ +/* + 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" + "fmt" + + "serene-lang.org/bootstrap/pkg/ast" + "serene-lang.org/bootstrap/pkg/runtime" + "serene-lang.org/bootstrap/pkg/scope" + "serene-lang.org/bootstrap/pkg/types" +) + +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: + // * 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.(*types.Symbol).GetName() + expr := scope.Lookup(symbolName) + + if expr == nil { + return nil, fmt.Errorf("Can't resolve symbol '%s' in ns '%s'", symbolName, rt.CurrentNS().GetName()) + } + + return expr.Value, nil + + } + + // Default case + return nil, errors.New("not implemented") +} + +func Eval(rt *runtime.Runtime, forms types.ASTree) (types.IExpr, error) { + if len(forms) == 0 { + return &types.Nil, nil + } + + var ret types.IExpr + + for _, form := range forms { + // v is here to shut up the linter + v, err := evalForm(rt, rt.CurrentNS().GetRootScope(), form) + + if err != nil { + return nil, err + } + + ret = v + } + + return ret, nil +} diff --git a/bootstrap/pkg/eval/eval.go b/bootstrap/pkg/eval/eval.go deleted file mode 100644 index 05f16c9..0000000 --- a/bootstrap/pkg/eval/eval.go +++ /dev/null @@ -1,39 +0,0 @@ -/* - 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 eval provides all the necessary functions to eval expressions -package eval - -import ( - "serene-lang.org/bootstrap/pkg/runtime" - "serene-lang.org/bootstrap/pkg/types" -) - -func eval(rt *runtime.Runtime, forms types.ASTree) types.IExpr { - if len(forms) == 0 { - return &types.Nil - } - - var ret types.IExpr - - for _, form := range forms { - ret = eval_form(rt, rt.CurrentNS().GetRootScope(), form) - } - - ret -} diff --git a/bootstrap/pkg/parser/parser.go b/bootstrap/pkg/parser/parser.go index a0816af..75fe6b0 100644 --- a/bootstrap/pkg/parser/parser.go +++ b/bootstrap/pkg/parser/parser.go @@ -95,15 +95,14 @@ func readRawSymbol(parser IParsable) (types.IExpr, error) { var symbol string if c == nil { - return nil, errors.New("unexpected EOF while parsing a symbol") + return nil, errors.New("unexpected enf of file while parsing a symbol") } if isValidForSymbol(*c) { parser.next(false) symbol = *c } else { - - return nil, fmt.Errorf("unexpected character: got '%s', expected a symbol at %s", + return nil, fmt.Errorf("unexpected character: got '%s', expected a symbol at %d", *c, parser.GetLocation(), ) diff --git a/bootstrap/pkg/printer/printer.go b/bootstrap/pkg/printer/printer.go index cca79f9..46a2a46 100644 --- a/bootstrap/pkg/printer/printer.go +++ b/bootstrap/pkg/printer/printer.go @@ -26,6 +26,6 @@ import ( "serene-lang.org/bootstrap/pkg/types" ) -func Print(rt *runtime.Runtime, ast types.ASTree) { +func Print(rt *runtime.Runtime, ast types.IPrintable) { fmt.Println(ast.String()) } diff --git a/bootstrap/pkg/runtime/runtime.go b/bootstrap/pkg/runtime/runtime.go index 8038670..edf3a95 100644 --- a/bootstrap/pkg/runtime/runtime.go +++ b/bootstrap/pkg/runtime/runtime.go @@ -41,6 +41,10 @@ type Runtime struct { debugMode bool } +func (r *Runtime) IsDebugMode() bool { + return r.debugMode +} + func (r *Runtime) CurrentNS() *namespace.Namespace { if r.currentNS == "" { panic("current ns is not set on the runtime.") diff --git a/bootstrap/pkg/scope/scope.go b/bootstrap/pkg/scope/scope.go index 12ba761..ca014c5 100644 --- a/bootstrap/pkg/scope/scope.go +++ b/bootstrap/pkg/scope/scope.go @@ -21,46 +21,44 @@ along with this program. If not, see . package scope import ( - "fmt" - "serene-lang.org/bootstrap/pkg/types" ) type IScope interface { - Lookup(k string) (*Binding, error) + Lookup(k string) *Binding Insert(k string, v types.IExpr, public bool) } type Binding struct { - value types.IExpr - public bool + Value types.IExpr + Public bool } type Scope struct { - bindings map[string]*Binding - parent IScope + bindings map[string]Binding + parent *Scope } -func (s *Scope) Lookup(k string) (*Binding, error) { +func (s *Scope) Lookup(k string) *Binding { v, ok := s.bindings[k] if ok { - return v, nil + return &v } if s.parent != nil { return s.parent.Lookup(k) - } else { - return nil, fmt.Errorf("can't resolve symbol '%s'", k) } + + return nil } func (s *Scope) Insert(k string, v types.IExpr, public bool) { - s.bindings[k] = &Binding{value: v, public: public} + s.bindings[k] = Binding{Value: v, Public: public} } func MakeScope(parent *Scope) Scope { return Scope{ parent: parent, - bindings: map[string]*Binding{}, + bindings: map[string]Binding{}, } } diff --git a/bootstrap/pkg/types/symbol.go b/bootstrap/pkg/types/symbol.go index 9067e7e..16a8e88 100644 --- a/bootstrap/pkg/types/symbol.go +++ b/bootstrap/pkg/types/symbol.go @@ -25,10 +25,6 @@ type Symbol struct { name string } -func (s *Symbol) Eval() IExpr { - return &Nil -} - func (s *Symbol) GetType() ast.NodeType { return ast.Symbol } @@ -38,6 +34,11 @@ func (s *Symbol) String() string { return s.name } +func (s *Symbol) GetName() string { + // TODO: Handle ns qualified symbols here + return s.name +} + func (s *Symbol) ToDebugStr() string { return s.name } diff --git a/bootstrap/pkg/types/types.go b/bootstrap/pkg/types/types.go index 01de5f7..21bef33 100644 --- a/bootstrap/pkg/types/types.go +++ b/bootstrap/pkg/types/types.go @@ -40,7 +40,6 @@ type IExpr interface { ast.ITypable IPrintable IDebuggable - Eval() IExpr } type Node struct {