all: make a ton of features work

This commit is contained in:
2018-05-14 20:59:24 +02:00
parent 8cb7c37096
commit e3334b9ef0
6 changed files with 919 additions and 514 deletions

View File

@@ -7,61 +7,12 @@
## TODOS ## TODOS
- Destructuring assignment - Destructuring assignment
- Defining functions
- Anonymous functions
- Datastructures as functions
- Characters
- `let`
- `with`
- `pr`
- `prn`
- `if`
- `do`
- `when`
- `and`
- `no`
- `is`
- `in`
- `case`
- `for`
- `each`
- `while`
- `repeat`
- `:`
- `~`
- `keep`
- `rem`
- `all`
- `some`
- `pos`
- `trues`
- `table`
- `listtab`
- `obj`
- `keys`
- `vals`
- `maptable` - `maptable`
- `alref`
- `string`
- `tostring`
- `type`
- `coerce` - `coerce`
- `pop`
- `push`
- `++`
- `--`
- `zap`
- `sort`
- `insort`
- `compare`
- `o` - `o`
- `.`
- `apply`
- `len` - `len`
- `mac` - `mac`
- `\`` - `\``
- `defop`
- `asv`
- `w/link` - `w/link`
- `aform` - `aform`
- ... - ...

View File

@@ -18,6 +18,11 @@ const (
String String
Num Num
Bool Bool
Fn
Char
Table
Prim
Quoted
) )
func (tag Tag) String() string { func (tag Tag) String() string {
@@ -27,18 +32,124 @@ func (tag Tag) String() string {
"String", "String",
"Num", "Num",
"Bool", "Bool",
"Fn",
"Char",
"Table",
"Prim",
"Quoted",
} }
return names[tag] return names[tag]
} }
func (ast AST) Pretty() string { func (ast *AST) Type() *AST {
names := []string{
"sym",
"cons",
"string",
"num",
"bool",
"fn",
"char",
"table",
"fn",
"cons",
}
if ast.Tag == Quoted {
return ast.Val.(*AST).Type()
}
name := names[ast.Tag]
if ast.Tag == Num {
val := ast.Val.(float64)
if val == float64(int64(val)) {
name = "int"
}
}
res := AST{Symbol, name}
return &res
}
func (ast *AST) Pretty() string {
if ast.Tag == List { if ast.Tag == List {
var agg []string var agg []string
for _, elem := range(ast.Val.([]AST)) { for _, elem := range ast.Val.([]*AST) {
agg = append(agg, elem.Pretty()) agg = append(agg, elem.Pretty())
} }
return "(" + strings.Join(agg, " ") + ")" return "(" + strings.Join(agg, " ") + ")"
} else if ast.Tag == Quoted {
return "'" + ast.Val.(*AST).Pretty()
} else if ast.Tag == Fn {
val := ast.Val.(Func)
var agg []string
for _, elem := range val.Params {
agg = append(agg, elem)
}
opt := ""
if val.HasOpt() {
opt = " . " + *val.Opt
}
body := val.Body.Pretty()
return "(fn (" + strings.Join(agg, " ") + opt + ") " + body + ")"
} else if ast.Tag == Char {
return "#" + string(ast.Val.(rune))
} else if ast.Tag == Table {
var agg []string
for k, v := range ast.Val.(map[*AST]*AST) {
agg = append(agg, "("+k.Pretty()+" . "+v.Pretty()+")")
}
return "#hash(" + strings.Join(agg, " ") + ")"
} else if ast.Tag == Prim {
return "#<procedure: " + ast.Val.(Primitive).Name + ">"
} }
return fmt.Sprintf("%v", ast.Val) return fmt.Sprintf("%v", ast.Val)
} }
type Env struct {
parent *Env
Values map[string]*AST
}
type PrimFn func([]*AST) (*AST, error)
type Primitive struct {
Name string
Fn PrimFn
}
func newEnv(parent *Env) Env {
return Env{parent, make(map[string]*AST)}
}
func ParentEnv() Env {
return newEnv(nil)
}
func (e *Env) Lookup(elem string) (*AST, error) {
res, ok := e.Values[elem]
if !ok {
if e.parent == nil {
return nil, fmt.Errorf("Symbol not found: %s", elem)
}
return e.parent.Lookup(elem)
}
return res, nil
}
type Func struct {
Params []string
Opt *string
Body *AST
Env Env
}
func (f *Func) HasOpt() bool {
return f.Opt != nil
}

View File

@@ -1,68 +1,68 @@
package eval package eval
// TODO: a lot of code duplication that can be solved by a primitive registry
// TODO: a lot of code duplication that can be solved by a type checking helper // TODO: a lot of code duplication that can be solved by a type checking helper
import ( import (
"fmt" "fmt"
"strings"
"github.com/hellerve/argos/ast" "github.com/hellerve/argos/ast"
) )
var trueVal = ast.AST{ast.Bool, true} var trueVal = ast.AST{ast.Bool, true}
var falseVal = ast.AST{ast.Bool, false} var falseVal = ast.AST{ast.Bool, false}
var nilVal = ast.AST{ast.List, []ast.AST{}} var nilVal = ast.AST{ast.List, []*ast.AST{}}
type env struct { func RootEnv() ast.Env {
parent *env e := ast.ParentEnv()
values map[string]*ast.AST
prims := map[string]ast.PrimFn{
"cons": evalCons,
"car": evalCar,
"cdr": evalCdr,
"null?": evalNull,
"pr": evalPr,
"table": evalTable,
"type": evalType,
} }
func newEnv(parent *env) env { for k, v := range prims {
return env{parent, make(map[string]*ast.AST)} prim := ast.AST{ast.Prim, ast.Primitive{k, v}}
e.Values[k] = &prim
} }
func ParentEnv() env { return e
return newEnv(nil)
} }
func (e env) Lookup(elem string) (*ast.AST, error) { func checkArity(input []*ast.AST, arity int, name string) error {
res, ok := e.values[elem]
if !ok {
if e.parent == nil {
return nil, fmt.Errorf("Symbol not found: %s", elem)
}
return e.parent.Lookup(elem)
}
return res, nil
}
func checkArity(input []ast.AST, arity int, name string) error {
ilen := len(input) ilen := len(input)
if ilen != arity+1 { if ilen != arity {
return fmt.Errorf("Argument count to '%s' must be %d, was %d", name, arity, ilen) return fmt.Errorf("Argument count to '%s' must be %d, was %d", name, arity, ilen)
} }
return nil return nil
} }
func evalDef(input []ast.AST, e env) (*ast.AST, error) { func evalDef(input []*ast.AST, e ast.Env) (*ast.AST, error) {
err := checkArity(input, 2, "=") err := checkArity(input, 2, "=")
if err != nil { if err != nil {
return nil, err return nil, err
} }
variable := &input[1] variable := input[0]
if variable.Tag != ast.Symbol { if variable.Tag != ast.Symbol {
// TODO destructuring assignment
variable, err = Eval(variable, e) variable, err = Eval(variable, e)
if err != nil {
return nil, err
}
if variable.Tag != ast.Symbol { if variable.Tag != ast.Symbol {
return nil, fmt.Errorf("First argument to 'def' must be symbol, was %v", variable.Tag) variable, err = Eval(input[1], e)
return variable, err
} }
} }
@@ -72,19 +72,19 @@ func evalDef(input []ast.AST, e env) (*ast.AST, error) {
sym := variable.Val.(string) sym := variable.Val.(string)
evald, err := Eval(&input[2], e) evald, err := Eval(input[1], e)
if err != nil { if err != nil {
return nil, err return nil, err
} }
e.values[sym] = evald e.Values[sym] = evald
return evald, nil return evald, nil
} }
func arithCast(input ast.AST, e env) (float64, error) { func arithCast(input *ast.AST, e ast.Env) (float64, error) {
evald, err := Eval(&input, e) evald, err := Eval(input, e)
if err != nil { if err != nil {
return 0, err return 0, err
} }
@@ -96,19 +96,19 @@ func arithCast(input ast.AST, e env) (float64, error) {
return evald.Val.(float64), nil return evald.Val.(float64), nil
} }
func evalArith(input []ast.AST, e env, fn (func(float64, float64) float64)) (*ast.AST, error) { func evalArith(input []*ast.AST, e ast.Env, fn func(float64, float64) float64) (*ast.AST, error) {
ilen := len(input) ilen := len(input)
if ilen < 3 { if ilen < 2 {
return nil, fmt.Errorf("Arithmetic functions take at least 2 arguments, got %d", ilen) return nil, fmt.Errorf("Arithmetic functions take at least 2 arguments, got %d", ilen)
} }
acc, err := arithCast(input[1], e) acc, err := arithCast(input[0], e)
if err != nil { if err != nil {
return nil, err return nil, err
} }
for _, elem := range input[2:] { for _, elem := range input[1:] {
val, err := arithCast(elem, e) val, err := arithCast(elem, e)
if err != nil { if err != nil {
@@ -121,20 +121,49 @@ func evalArith(input []ast.AST, e env, fn (func(float64, float64) float64)) (*as
return &res, nil return &res, nil
} }
func evalEq(input []ast.AST, e env) (*ast.AST, error) { func evalLog(input []*ast.AST, e ast.Env, fn func(float64, float64) bool) (*ast.AST, error) {
err := checkArity(input, 2, "iso") ilen := len(input)
if ilen < 2 {
return nil, fmt.Errorf("Logic functions take at least 2 arguments, got %d", ilen)
}
old, err := arithCast(input[0], e)
if err != nil { if err != nil {
return nil, err return nil, err
} }
x, err := Eval(&input[1], e) acc := true
for _, elem := range input[1:] {
val, err := arithCast(elem, e)
if err != nil {
return nil, err
}
acc = acc && fn(old, val)
old = val
}
if acc {
return &trueVal, nil
}
return &falseVal, nil
}
func evalEq(input []*ast.AST, e ast.Env) (*ast.AST, error) {
err := checkArity(input, 2, "is")
if err != nil { if err != nil {
return nil, err return nil, err
} }
y, err := Eval(&input[2], e) x, err := Eval(input[0], e)
if err != nil {
return nil, err
}
y, err := Eval(input[1], e)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -150,125 +179,273 @@ func evalEq(input []ast.AST, e env) (*ast.AST, error) {
return &falseVal, nil return &falseVal, nil
} }
func evalCons(input []ast.AST, e env) (*ast.AST, error) { func evalCons(input []*ast.AST) (*ast.AST, error) {
err := checkArity(input, 2, "cons") err := checkArity(input, 2, "cons")
if err != nil { if err != nil {
return nil, err return nil, err
} }
lst, err := Eval(&input[2], e) lst := input[1]
if err != nil { if lst.Tag != ast.Quoted {
return nil, err
}
if lst.Tag != ast.List {
return nil, fmt.Errorf("Cannot cons to non-list %s", lst.Pretty()) return nil, fmt.Errorf("Cannot cons to non-list %s", lst.Pretty())
} }
fst, err := Eval(&input[1], e) fst := input[0]
if err != nil { res := ast.AST{ast.List, append([]*ast.AST{fst}, lst.Val.(*ast.AST).Val.([]*ast.AST)...)}
return nil, err
}
res := ast.AST{ast.List, append([]ast.AST{*fst}, lst.Val.([]ast.AST)...)}
return &res, nil return &res, nil
} }
func evalCar(input []ast.AST, e env) (*ast.AST, error) { func evalCar(input []*ast.AST) (*ast.AST, error) {
err := checkArity(input, 1, "car") err := checkArity(input, 1, "car")
if err != nil { if err != nil {
return nil, err return nil, err
} }
lst, err := Eval(&input[1], e) lst := input[0]
if err != nil { if lst.Tag != ast.Quoted {
return nil, err
}
if lst.Tag != ast.List {
return nil, fmt.Errorf("Cannot car from non-list %s", lst.Pretty()) return nil, fmt.Errorf("Cannot car from non-list %s", lst.Pretty())
} }
res := lst.Val.([]ast.AST)[0] res := lst.Val.(*ast.AST).Val.([]*ast.AST)[0]
return &res, nil return res, nil
} }
func evalCdr(input []ast.AST, e env) (*ast.AST, error) { func evalCdr(input []*ast.AST) (*ast.AST, error) {
err := checkArity(input, 1, "cdr") err := checkArity(input, 1, "cdr")
if err != nil { if err != nil {
return nil, err return nil, err
} }
lst, err := Eval(&input[1], e) lst := input[0]
if err != nil { if lst.Tag != ast.Quoted {
return nil, err
}
if lst.Tag != ast.List {
return nil, fmt.Errorf("Cannot cdr from non-list %s", lst.Pretty()) return nil, fmt.Errorf("Cannot cdr from non-list %s", lst.Pretty())
} }
res := ast.AST{ast.List, lst.Val.([]ast.AST)[1:]} res := ast.AST{ast.List, lst.Val.(*ast.AST).Val.([]*ast.AST)[1:]}
return &res, nil return &res, nil
} }
func evalNull(input []ast.AST, e env) (*ast.AST, error) { func evalNull(input []*ast.AST) (*ast.AST, error) {
err := checkArity(input, 1, "null") err := checkArity(input, 1, "null")
if err != nil { if err != nil {
return nil, err return nil, err
} }
lst, err := Eval(&input[1], e) lst := input[0]
if lst.Tag != ast.Quoted {
return nil, fmt.Errorf("Cannot call null? on non-list %s", lst.Pretty())
}
res := ast.AST{ast.Bool, len(lst.Val.(*ast.AST).Val.([]*ast.AST)) == 0}
return &res, nil
}
func evalFn(input []*ast.AST, e ast.Env) (*ast.AST, error) {
err := checkArity(input, 2, "fn")
if err != nil { if err != nil {
return nil, err return nil, err
} }
if lst.Tag != ast.List { args := input[0]
return nil, fmt.Errorf("Cannot call null? on non-list %s", lst.Pretty())
if args.Tag != ast.List {
return nil, fmt.Errorf("Cannot call fn with argument list %s", args.Pretty())
} }
res := ast.AST{ast.Bool, len(lst.Val.([]ast.AST)) == 0} var argsStr []string
var opt *string = nil
argslst := args.Val.([]*ast.AST)
for i, a := range argslst {
if a.Tag != ast.Symbol {
return nil, fmt.Errorf("Argument list cannot contain %s", a.Pretty())
}
val := a.Val.(string)
// TODO: error handling
if val == "." {
str := argslst[i+1].Val.(string)
opt = &str
break
}
argsStr = append(argsStr, val)
}
body := input[1]
res := ast.AST{ast.Fn, ast.Func{argsStr, opt, body, e}}
return &res, nil return &res, nil
} }
func evalList(input *ast.AST, e env) (*ast.AST, error) { func funcApply(f ast.Func, args []*ast.AST, e ast.Env) (*ast.AST, error) {
l := input.Val.([]ast.AST) plen := len(f.Params)
head := l[0] alen := len(args)
if head.Tag != ast.Symbol { if plen != alen && !f.HasOpt() {
err := fmt.Errorf("Calling non-symbol: %s", head.Pretty()) return nil, fmt.Errorf("Function expected %d arguments, was called with %d.", plen, alen)
}
for i, a := range f.Params {
f.Env.Values[a] = args[i]
}
if f.HasOpt() {
lst := ast.AST{ast.List, args[plen:]}
quoted := ast.AST{ast.Quoted, &lst}
f.Env.Values[*f.Opt] = &quoted
}
return Eval(f.Body, f.Env)
}
func evalPr(l []*ast.AST) (*ast.AST, error) {
var toPrint []string
for _, elem := range l {
toPrint = append(toPrint, elem.Pretty())
}
fmt.Print(strings.Join(toPrint, " "))
return &nilVal, nil
}
func evalIf(l []*ast.AST, e ast.Env) (*ast.AST, error) {
i := 0
for i < len(l) {
if i == len(l)-1 {
return Eval(l[i], e)
}
cond, err := Eval(l[i], e)
if err != nil {
return nil, err return nil, err
} }
sym := head.Val.(string) if cond == &trueVal {
return Eval(l[i+1], e)
}
i += 2
}
return &nilVal, nil
}
func evalDo(l []*ast.AST, e ast.Env) (*ast.AST, error) {
var res *ast.AST
var err error
for _, elem := range l {
res, err = Eval(elem, e)
if err != nil {
return nil, err
}
}
return res, nil
}
func evalApply(l []*ast.AST, e ast.Env) (*ast.AST, error) {
err := checkArity(l, 2, "apply")
if err != nil {
return nil, err
}
// TODO: error handling
args, err := Eval(l[1], e)
if err != nil {
return nil, err
}
if args.Tag != ast.Quoted {
return nil, fmt.Errorf("Argument 2 to apply must be a list, got %s", args.Pretty())
}
argslst := args.Val.(*ast.AST).Val.([]*ast.AST)
return evalList(append([]*ast.AST{l[0]}, argslst...), e)
}
func evalWhile(l []*ast.AST, e ast.Env) (*ast.AST, error) {
err := checkArity(l, 2, "while")
if err != nil {
return nil, err
}
var res *ast.AST
for {
cond, err := Eval(l[0], e)
if err != nil {
return nil, err
}
if cond == &falseVal {
break
}
res, err = Eval(l[1], e)
if err != nil {
return nil, err
}
}
return res, nil
}
func evalTable(l []*ast.AST) (*ast.AST, error) {
err := checkArity(l, 0, "table")
if err != nil {
return nil, err
}
res := ast.AST{ast.Table, make(map[*ast.AST]*ast.AST)}
return &res, nil
}
func evalType(l []*ast.AST) (*ast.AST, error) {
err := checkArity(l, 1, "type")
if err != nil {
return nil, err
}
return l[0].Type(), nil
}
func evalSymbolList(l []*ast.AST, e ast.Env) (*ast.AST, error) {
sym := l[0].Val.(string)
l = l[1:]
// TODO: make more of these primitives
switch sym { switch sym {
case "=": case "=":
return evalDef(l, e) return evalDef(l, e)
case "iso": case "is":
return evalEq(l, e) return evalEq(l, e)
case "apply":
return evalApply(l, e)
case "quote": case "quote":
res := l[1] lst := l[0]
res := ast.AST{ast.Quoted, lst}
return &res, nil return &res, nil
case "cons": case "fn":
return evalCons(l, e) return evalFn(l, e)
case "car": case "if":
return evalCar(l, e) return evalIf(l, e)
case "cdr": case "do":
return evalCdr(l, e) return evalDo(l, e)
case "null?": case "while":
return evalNull(l, e) return evalWhile(l, e)
case "+": case "+":
return evalArith(l, e, func(x float64, y float64) float64 { return x + y }) return evalArith(l, e, func(x float64, y float64) float64 { return x + y })
case "-": case "-":
@@ -279,15 +456,127 @@ func evalList(input *ast.AST, e env) (*ast.AST, error) {
return evalArith(l, e, func(x float64, y float64) float64 { return x / y }) return evalArith(l, e, func(x float64, y float64) float64 { return x / y })
case "%": case "%":
return evalArith(l, e, func(x float64, y float64) float64 { return float64(int64(x) % int64(y)) }) return evalArith(l, e, func(x float64, y float64) float64 { return float64(int64(x) % int64(y)) })
case "<":
return evalLog(l, e, func(x float64, y float64) bool { return x < y })
case ">":
return evalLog(l, e, func(x float64, y float64) bool { return x > y })
} }
return input, nil res, err := e.Lookup(sym)
if err != nil {
return nil, err
} }
func Eval(input *ast.AST, e env) (*ast.AST, error) { return evalList(append([]*ast.AST{res}, l...), e)
if (input.Tag == ast.List) {
return evalList(input, e)
} }
if (input.Tag == ast.Symbol) {
func evalIdx(l []*ast.AST, e ast.Env) (*ast.AST, error) {
head := l[0]
err := checkArity(l, 2, "indexing")
if err != nil {
return nil, err
}
arg, err := Eval(l[1], e)
if err != nil {
return nil, err
}
switch head.Tag {
case ast.Quoted:
if arg.Tag != ast.Num {
return nil, fmt.Errorf("Cannot index using %s", arg.Pretty())
}
idx := int64(arg.Val.(float64))
var actualLen int64
val := head.Val.(*ast.AST).Val.([]*ast.AST)
actualLen = int64(len(val))
actualIdx := idx
if idx < 0 {
actualIdx = actualLen + idx
}
if actualLen > actualIdx && actualIdx > 0 {
return val[actualIdx], nil
}
return nil, fmt.Errorf("Out of bounds access (idx %d at %s)", idx, head.Pretty())
case ast.String:
if arg.Tag != ast.Num {
return nil, fmt.Errorf("Cannot index using %s", arg.Pretty())
}
idx := int64(arg.Val.(float64))
var actualLen int64
val := []rune(head.Val.(string))
actualLen = int64(len(val))
actualIdx := idx
if idx < 0 {
actualIdx = actualLen + idx
}
if actualLen > actualIdx && actualIdx > 0 {
res := ast.AST{ast.Char, val[actualIdx]}
return &res, nil
}
return nil, fmt.Errorf("Out of bounds access (idx %d at %s)", idx, head.Pretty())
case ast.Table:
val := head.Val.(map[*ast.AST]*ast.AST)
res, ok := val[arg]
if !ok {
return &nilVal, nil
}
return res, nil
}
return nil, fmt.Errorf("Cannot index %s", head.Pretty())
}
func primCall(f ast.Primitive, l []*ast.AST, e ast.Env) (*ast.AST, error) {
var args []*ast.AST
for _, a := range l {
res, err := Eval(a, e)
if err != nil {
return nil, err
}
args = append(args, res)
}
return f.Fn(args)
}
func evalList(l []*ast.AST, e ast.Env) (*ast.AST, error) {
head := l[0]
var err error
if head.Tag == ast.List {
head, err = Eval(head, e)
if err != nil {
return nil, err
}
}
switch head.Tag {
case ast.Symbol:
return evalSymbolList(l, e)
case ast.Fn:
return funcApply(head.Val.(ast.Func), l[1:], e)
case ast.Prim:
return primCall(head.Val.(ast.Primitive), l[1:], e)
case ast.String, ast.Quoted, ast.Table:
return evalIdx(append([]*ast.AST{head}, l[1:]...), e)
}
return nil, fmt.Errorf("Cannot perform call on %s", head.Pretty())
}
func Eval(input *ast.AST, e ast.Env) (*ast.AST, error) {
if input.Tag == ast.List {
l := input.Val.([]*ast.AST)
return evalList(l, e)
}
if input.Tag == ast.Symbol {
val := input.Val.(string) val := input.Val.(string)
var err error var err error

View File

@@ -21,22 +21,8 @@
; not sure this is a mistake; strings may be subtly different from ; not sure this is a mistake; strings may be subtly different from
; lists of chars ; lists of chars
(mac def (name params body)
(assign do (annotate 'mac (list '= name (list 'fn params body)))
(fn args `((fn () ,@args)))))
(assign safeset (annotate 'mac
(fn (var val)
`(do (if (bound ',var)
(do (disp "*** redefining " (stderr))
(disp ',var (stderr))
(disp #\newline (stderr))))
(assign ,var ,val)))))
(assign def (annotate 'mac
(fn (name parms . body)
`(do (sref sig ',parms ',name)
(safeset ,name (fn ,parms ,@body))))))
(def caar (xs) (car (car xs))) (def caar (xs) (car (car xs)))
(def cadr (xs) (car (cdr xs))) (def cadr (xs) (car (cdr xs)))

View File

@@ -22,7 +22,7 @@ func runRepl() {
defer rl.Close() defer rl.Close()
e := eval.ParentEnv() e := eval.RootEnv()
for { for {
input, err := rl.Readline() input, err := rl.Readline()
@@ -30,6 +30,10 @@ func runRepl() {
return return
} }
if len(input) == 0 {
continue
}
parsed, err, unconsumed := parser.Parse(input) parsed, err, unconsumed := parser.Parse(input)
if err != nil { if err != nil {
@@ -45,7 +49,6 @@ func runRepl() {
// TODO: how to avoid bindings on error? // TODO: how to avoid bindings on error?
evald, err := eval.Eval(parsed, e) evald, err := eval.Eval(parsed, e)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
continue continue
@@ -56,7 +59,7 @@ func runRepl() {
} }
func runFile(path string) { func runFile(path string) {
e := eval.ParentEnv() e := eval.RootEnv()
input, err := ioutil.ReadFile(path) input, err := ioutil.ReadFile(path)
if err != nil { if err != nil {

View File

@@ -33,6 +33,32 @@ func tokenize(input string) []string {
return withoutEmpty(whitespace.Split(explodedBrackets, -1)) return withoutEmpty(whitespace.Split(explodedBrackets, -1))
} }
func withEscape(input string) rune {
switch input[1:] {
case "\\a":
return '\a'
case "\\b":
return '\b'
case "newline":
return '\n'
case "tab":
return '\t'
case "\\f":
return '\f'
case "\\r":
return '\r'
case "\\v":
return '\v'
case "\\\\":
return '\\'
case "\\'":
return '\''
case "\\\"":
return '"'
}
return []rune(input)[1]
}
func parseValue(input []string) (*ast.AST, error, []string) { func parseValue(input []string) (*ast.AST, error, []string) {
var res ast.AST var res ast.AST
@@ -43,6 +69,11 @@ func parseValue(input []string) (*ast.AST, error, []string) {
return &res, nil, input[1:] return &res, nil, input[1:]
} }
if input[0][0] == '#' {
res := ast.AST{ast.Char, withEscape(input[0])}
return &res, nil, input[1:]
}
if input[0][0] == '"' { if input[0][0] == '"' {
var agg []string var agg []string
for { for {
@@ -56,19 +87,40 @@ func parseValue(input []string) (*ast.AST, error, []string) {
break break
} }
} }
res = ast.AST{ast.String, strings.Join(agg, " ")} joined := strings.Join(agg, " ")
res = ast.AST{ast.String, joined[1 : len(joined)-1]}
return &res, nil, input return &res, nil, input
} }
if input[0][0] == '~' {
fn := ast.AST{ast.Symbol, input[0][1:]}
complement := ast.AST{ast.Symbol, "complement"}
res = ast.AST{ast.List, []*ast.AST{&complement, &fn}}
return &res, nil, input[1:]
}
if strings.Contains(input[0], ":") {
comp := ast.AST{ast.Symbol, "compose"}
fns := []*ast.AST{&comp}
for _, fn := range strings.Split(input[0], ":") {
astfn := ast.AST{ast.Symbol, fn}
fns = append(fns, &astfn)
}
res = ast.AST{ast.List, fns}
return &res, nil, input[1:]
}
res = ast.AST{ast.Symbol, input[0]} res = ast.AST{ast.Symbol, input[0]}
return &res, nil, input[1:] return &res, nil, input[1:]
} }
func makeFn(bodyStatements []ast.AST) ast.AST { func makeFn(bodyStatements []*ast.AST) ast.AST {
body := ast.AST{ast.List, bodyStatements} body := ast.AST{ast.List, bodyStatements}
args := ast.AST{ast.List, []ast.AST{ast.AST{ast.Symbol, "_"}}} underscore := ast.AST{ast.Symbol, "_"}
args := ast.AST{ast.List, []*ast.AST{&underscore}}
fn := ast.AST{ast.Symbol, "fn"}
return ast.AST{ast.List, []ast.AST{ast.AST{ast.Symbol, "fn"}, args, body}} return ast.AST{ast.List, []*ast.AST{&fn, &args, &body}}
} }
func parseToken(input []string) (*ast.AST, error, []string) { func parseToken(input []string) (*ast.AST, error, []string) {
@@ -77,7 +129,7 @@ func parseToken(input []string) (*ast.AST, error, []string) {
} }
if input[0][0] == '\'' { if input[0][0] == '\'' {
if (input[0] == "'") { if input[0] == "'" {
input = input[1:] input = input[1:]
} else { } else {
input = append([]string{input[0][1:]}, input[1:]...) input = append([]string{input[0][1:]}, input[1:]...)
@@ -88,13 +140,15 @@ func parseToken(input []string) (*ast.AST, error, []string) {
return nil, err, input return nil, err, input
} }
res := ast.AST{ast.List, []ast.AST{ast.AST{ast.Symbol, "quote"}, *tmp}} quote := ast.AST{ast.Symbol, "quote"}
res := ast.AST{ast.List, []*ast.AST{&quote, tmp}}
return &res, nil, input return &res, nil, input
} }
switch input[0] { switch input[0] {
case "(": { case "(":
var l []ast.AST {
var l []*ast.AST
input = input[1:] input = input[1:]
for input[0] != ")" { for input[0] != ")" {
elem, err, newInput := parseToken(input) elem, err, newInput := parseToken(input)
@@ -103,17 +157,23 @@ func parseToken(input []string) (*ast.AST, error, []string) {
return nil, err, input return nil, err, input
} }
l = append(l, *elem) if len(newInput) == 0 {
return nil, errors.New("Unmatched '('"), input
}
l = append(l, elem)
input = newInput input = newInput
} }
res := ast.AST{ast.List, l} res := ast.AST{ast.List, l}
return &res, nil, input[1:] return &res, nil, input[1:]
} }
case ")": { case ")":
{
return nil, errors.New("Unmatched ')'"), input return nil, errors.New("Unmatched ')'"), input
} }
case "[": { case "[":
var l []ast.AST {
var l []*ast.AST
input = input[1:] input = input[1:]
for input[0] != "]" { for input[0] != "]" {
elem, err, newInput := parseToken(input) elem, err, newInput := parseToken(input)
@@ -122,13 +182,18 @@ func parseToken(input []string) (*ast.AST, error, []string) {
return nil, err, input return nil, err, input
} }
l = append(l, *elem) if len(newInput) == 0 {
return nil, errors.New("Unmatched '['"), input
}
l = append(l, elem)
input = newInput input = newInput
} }
res := makeFn(l) res := makeFn(l)
return &res, nil, input[1:] return &res, nil, input[1:]
} }
case "]": { case "]":
{
return nil, errors.New("Unmatched ']'"), input return nil, errors.New("Unmatched ']'"), input
} }
} }