From 616e531dddf4bbbd70a62b8f073ed6fd74d66147 Mon Sep 17 00:00:00 2001 From: hellerve Date: Tue, 15 May 2018 00:00:30 +0200 Subject: [PATCH] macro: defining macros works --- README.md | 2 -- macro/macro.go | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++ main.go | 21 +++++++++++++-- 3 files changed, 91 insertions(+), 4 deletions(-) create mode 100644 macro/macro.go diff --git a/README.md b/README.md index 4c47541..63a2cfa 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,4 @@ - Destructuring assignment - `coerce` - `mac` -- `w/link` -- `aform` - ... diff --git a/macro/macro.go b/macro/macro.go new file mode 100644 index 0000000..f3a8901 --- /dev/null +++ b/macro/macro.go @@ -0,0 +1,72 @@ +package macro + +import ( + "fmt" + + "github.com/hellerve/argos/ast" +) + +type Macro struct { + params []string + transformer *ast.AST +} + +type MacroEnv map[string]Macro + +func NewMacroEnv() MacroEnv { + return make(MacroEnv) +} + +func handleMac(l []*ast.AST, m MacroEnv) (*ast.AST, error) { + if len(l) != 4 { + return nil, fmt.Errorf("mac takes 3 arguments") + } + + nameThunk := l[1] + + if nameThunk.Tag != ast.Symbol { + return nil, fmt.Errorf("First argument to mac must be symbol, got %s", nameThunk.Pretty()) + } + + name := nameThunk.Val.(string) + + paramsThunk := l[2] + + if paramsThunk.Tag != ast.List { + return nil, fmt.Errorf("second argument to mac must be list, got %s", paramsThunk.Pretty()) + } + + var params []string + + for _, elem := range paramsThunk.Val.([]*ast.AST) { + if elem.Tag != ast.Symbol { + return nil, fmt.Errorf("Invalid value in parameter list to mac: %s", elem.Pretty()) + } + params = append(params, elem.Val.(string)) + } + + m[name] = Macro{params, l[2]} + + nilVal := ast.AST{ast.Symbol, "nil"} + return &nilVal, nil +} + +func traverse(l *ast.AST, m MacroEnv) (*ast.AST, error) { + vals := l.Val.([]*ast.AST) + if len(vals) == 0 { + return l, nil + } + + if vals[0].Tag == ast.Symbol && vals[0].Val.(string) == "mac" { + return handleMac(vals, m) + } + + return l, nil +} + +func Expand(l *ast.AST, m MacroEnv) (*ast.AST, error) { + if l.Tag != ast.List { + return l, nil + } + return traverse(l, m) +} diff --git a/main.go b/main.go index d2d9a48..0458fd0 100644 --- a/main.go +++ b/main.go @@ -10,6 +10,7 @@ import ( "github.com/chzyer/readline" "github.com/hellerve/argos/eval" + "github.com/hellerve/argos/macro" "github.com/hellerve/argos/parser" ) @@ -23,6 +24,7 @@ func runRepl() { defer rl.Close() e := eval.RootEnv() + m := macro.NewMacroEnv() for { input, err := rl.Readline() @@ -46,8 +48,15 @@ func runRepl() { continue } + expanded, err := macro.Expand(parsed, m) + + if err != nil { + fmt.Println(err) + continue + } + // TODO: how to avoid bindings on error? - evald, err := eval.Eval(parsed, e) + evald, err := eval.Eval(expanded, e) if err != nil { fmt.Println(err) @@ -60,6 +69,7 @@ func runRepl() { func runFile(path string) { e := eval.RootEnv() + m := macro.NewMacroEnv() input, err := ioutil.ReadFile(path) if err != nil { @@ -79,7 +89,14 @@ func runFile(path string) { return } - evald, err := eval.Eval(parsed, e) + expanded, err := macro.Expand(parsed, m) + + if err != nil { + fmt.Println(err) + return + } + + evald, err := eval.Eval(expanded, e) if err != nil { fmt.Println(err)