macro: defining macros works
This commit is contained in:
@@ -9,6 +9,4 @@
|
||||
- Destructuring assignment
|
||||
- `coerce`
|
||||
- `mac`
|
||||
- `w/link`
|
||||
- `aform`
|
||||
- ...
|
||||
|
72
macro/macro.go
Normal file
72
macro/macro.go
Normal 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
21
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)
|
||||
|
Reference in New Issue
Block a user