macro: defining macros works

This commit is contained in:
2018-05-15 00:00:30 +02:00
parent 702bd703b9
commit 616e531ddd
3 changed files with 91 additions and 4 deletions

View File

@@ -9,6 +9,4 @@
- Destructuring assignment
- `coerce`
- `mac`
- `w/link`
- `aform`
- ...

72
macro/macro.go Normal file
View File

@@ -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)
}

21
main.go
View File

@@ -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)