all: make a ton of features work
This commit is contained in:
49
README.md
49
README.md
@@ -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`
|
||||||
- ...
|
- ...
|
||||||
|
115
ast/ast.go
115
ast/ast.go
@@ -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
|
||||||
|
}
|
||||||
|
481
eval/eval.go
481
eval/eval.go
@@ -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] = "ed
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
18
lib/arc.arc
18
lib/arc.arc
@@ -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)))
|
||||||
|
9
main.go
9
main.go
@@ -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 {
|
||||||
|
@@ -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{"e, 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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user