mirror of
https://codeberg.org/scip/tablizer.git
synced 2025-12-18 04:51:05 +01:00
120 lines
2.0 KiB
Go
120 lines
2.0 KiB
Go
package zygo
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
)
|
|
|
|
var NotAList = errors.New("not a list")
|
|
|
|
func ListToArray(expr Sexp) ([]Sexp, error) {
|
|
if !IsList(expr) {
|
|
return nil, NotAList
|
|
}
|
|
arr := make([]Sexp, 0)
|
|
|
|
for expr != SexpNull {
|
|
list := expr.(*SexpPair)
|
|
arr = append(arr, list.Head)
|
|
expr = list.Tail
|
|
}
|
|
|
|
return arr, nil
|
|
}
|
|
|
|
func MakeList(expressions []Sexp) Sexp {
|
|
if len(expressions) == 0 {
|
|
return SexpNull
|
|
}
|
|
|
|
return Cons(expressions[0], MakeList(expressions[1:]))
|
|
}
|
|
|
|
func MapList(env *Zlisp, fun *SexpFunction, expr Sexp) (Sexp, error) {
|
|
if expr == SexpNull {
|
|
return SexpNull, nil
|
|
}
|
|
|
|
var list = &SexpPair{}
|
|
switch e := expr.(type) {
|
|
case *SexpPair:
|
|
list.Head = e.Head
|
|
list.Tail = e.Tail
|
|
default:
|
|
return SexpNull, NotAList
|
|
}
|
|
|
|
var err error
|
|
|
|
list.Head, err = env.Apply(fun, []Sexp{list.Head})
|
|
|
|
if err != nil {
|
|
return SexpNull, err
|
|
}
|
|
|
|
list.Tail, err = MapList(env, fun, list.Tail)
|
|
|
|
if err != nil {
|
|
return SexpNull, err
|
|
}
|
|
|
|
return list, nil
|
|
}
|
|
|
|
// O(n^2) for n total nodes in all lists. So this is
|
|
// not super efficient. We have to
|
|
// find the tail of each list in turn by
|
|
// linear search. Avoid lists if possible in favor
|
|
// of arrays.
|
|
func ConcatLists(a *SexpPair, bs []Sexp) (Sexp, error) {
|
|
result := a
|
|
for _, b := range bs {
|
|
res, err := ConcatTwoLists(result, b)
|
|
if err != nil {
|
|
return SexpNull, err
|
|
}
|
|
x, ok := res.(*SexpPair)
|
|
if !ok {
|
|
return SexpNull, NotAList
|
|
}
|
|
result = x
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
func ConcatTwoLists(a *SexpPair, b Sexp) (Sexp, error) {
|
|
if !IsList(b) {
|
|
return SexpNull, NotAList
|
|
}
|
|
|
|
if a.Tail == SexpNull {
|
|
return Cons(a.Head, b), nil
|
|
}
|
|
|
|
switch t := a.Tail.(type) {
|
|
case *SexpPair:
|
|
newtail, err := ConcatTwoLists(t, b)
|
|
if err != nil {
|
|
return SexpNull, err
|
|
}
|
|
return Cons(a.Head, newtail), nil
|
|
}
|
|
|
|
return SexpNull, NotAList
|
|
}
|
|
|
|
func ListLen(expr Sexp) (int, error) {
|
|
sz := 0
|
|
var list *SexpPair
|
|
ok := false
|
|
for expr != SexpNull {
|
|
list, ok = expr.(*SexpPair)
|
|
if !ok {
|
|
return 0, fmt.Errorf("ListLen() called on non-list")
|
|
}
|
|
sz++
|
|
expr = list.Tail
|
|
}
|
|
return sz, nil
|
|
}
|