From c873602cfde79da29a202d7732db25f31c8c2f48 Mon Sep 17 00:00:00 2001 From: hellerve Date: Mon, 14 May 2018 23:25:39 +0200 Subject: [PATCH] all: add quasiquoting --- README.md | 1 - eval/eval.go | 45 +++++++++++++++++++++++++++++++++++++++++++++ parser/parser.go | 30 ++++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c87afbb..4c47541 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,6 @@ - Destructuring assignment - `coerce` - `mac` -- ````` - `w/link` - `aform` - ... diff --git a/eval/eval.go b/eval/eval.go index 8c925ea..c14e7e2 100644 --- a/eval/eval.go +++ b/eval/eval.go @@ -524,6 +524,49 @@ func evalMapTable(l []*ast.AST, e ast.Env) (*ast.AST, error) { return "ed, nil } +func evalQuasiquote(l []*ast.AST, e ast.Env) (*ast.AST, error) { + err := checkArity(l, 1, "quasiquote") + + if err != nil { + return nil, err + } + + arg := l[0] + + if arg.Tag != ast.List { + return nil, fmt.Errorf("Argument to quasiquote needs to be list, got %s", arg.Pretty()) + } + + var res []*ast.AST + + for _, elem := range arg.Val.([]*ast.AST) { + if elem.Tag != ast.List { + res = append(res, elem) + continue + } + lst := elem.Val.([]*ast.AST) + if lst[0].Tag != ast.Symbol && lst[0].Val.(string) == "unquote" { + res = append(res, elem) + continue + } + + if len(lst) != 2 { + return nil, fmt.Errorf("Unquote takes one argument.") + } + + value, err := Eval(lst[1], e) + + if err != nil { + return nil, err + } + + res = append(res, value) + } + lst := ast.AST{ast.List, res} + quotedLst := ast.AST{ast.Quoted, &lst} + return "edLst, nil +} + func evalSymbolList(l []*ast.AST, e ast.Env) (*ast.AST, error) { sym := l[0].Val.(string) @@ -540,6 +583,8 @@ func evalSymbolList(l []*ast.AST, e ast.Env) (*ast.AST, error) { lst := l[0] res := ast.AST{ast.Quoted, lst} return &res, nil + case "quasiquote": + return evalQuasiquote(l, e) case "fn": return evalFn(l, e) case "if": diff --git a/parser/parser.go b/parser/parser.go index 83dfde6..2190fe5 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -145,6 +145,36 @@ func parseToken(input []string) (*ast.AST, error, []string) { return &res, nil, input } + if input[0] == "`" { + input = input[1:] + tmp, err, input := parseToken(input) + + if err != nil { + return nil, err, input + } + + quote := ast.AST{ast.Symbol, "quasiquote"} + res := ast.AST{ast.List, []*ast.AST{"e, tmp}} + return &res, nil, input + } + + if input[0][0] == '@' { + if input[0] == "@" { + input = input[1:] + } else { + input = append([]string{input[0][1:]}, input[1:]...) + } + tmp, err, input := parseToken(input) + + if err != nil { + return nil, err, input + } + + unquote := ast.AST{ast.Symbol, "unqote"} + res := ast.AST{ast.List, []*ast.AST{&unquote, tmp}} + return &res, nil, input + } + switch input[0] { case "(": {