mirror of
https://codeberg.org/scip/tablizer.git
synced 2025-12-17 12:31:06 +01:00
222 lines
6.1 KiB
Go
222 lines
6.1 KiB
Go
package zygo
|
|
|
|
import "fmt"
|
|
|
|
func MapArray(env *Zlisp, fun *SexpFunction, arr *SexpArray) (Sexp, error) {
|
|
result := make([]Sexp, len(arr.Val))
|
|
var err error
|
|
|
|
var firstTyp *RegisteredType
|
|
for i := range arr.Val {
|
|
result[i], err = env.Apply(fun, arr.Val[i:i+1])
|
|
if err != nil {
|
|
return &SexpArray{Val: result, Typ: firstTyp, Env: env}, err
|
|
}
|
|
if firstTyp == nil {
|
|
firstTyp = result[i].Type()
|
|
}
|
|
}
|
|
|
|
return &SexpArray{Val: result, Typ: firstTyp, Env: env}, nil
|
|
}
|
|
|
|
func ConcatArray(arr *SexpArray, rest []Sexp) (Sexp, error) {
|
|
if arr == nil {
|
|
return SexpNull, fmt.Errorf("ConcatArray called with nil arr")
|
|
}
|
|
var res SexpArray
|
|
res.Val = arr.Val
|
|
for i, x := range rest {
|
|
switch t := x.(type) {
|
|
case *SexpArray:
|
|
res.Val = append(res.Val, t.Val...)
|
|
default:
|
|
return &res, fmt.Errorf("ConcatArray error: %d-th argument "+
|
|
"(0-based) is not an array", i)
|
|
}
|
|
}
|
|
return &res, nil
|
|
}
|
|
|
|
// (arrayidx ar [0 1])
|
|
func ArrayIndexFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) {
|
|
Q("in ArrayIndexFunction, args = '%#v'", args)
|
|
narg := len(args)
|
|
if narg != 2 {
|
|
return SexpNull, WrongNargs
|
|
}
|
|
|
|
var err error
|
|
args, err = env.ResolveDotSym(args)
|
|
if err != nil {
|
|
return SexpNull, err
|
|
}
|
|
|
|
var ar *SexpArray
|
|
switch ar2 := args[0].(type) {
|
|
case *SexpArraySelector:
|
|
x, err := ar2.RHS(env)
|
|
if err != nil {
|
|
return SexpNull, err
|
|
}
|
|
switch xArr := x.(type) {
|
|
case *SexpArray:
|
|
ar = xArr
|
|
case *SexpHash:
|
|
return HashIndexFunction(env, name, []Sexp{xArr, args[1]})
|
|
default:
|
|
return SexpNull, fmt.Errorf("bad (arrayidx ar index) call: ar as arrayidx, but that did not resolve to an array, instead '%s'/type %T", x.SexpString(nil), x)
|
|
}
|
|
case *SexpArray:
|
|
ar = ar2
|
|
case *SexpHash:
|
|
return HashIndexFunction(env, name, args)
|
|
case *SexpHashSelector:
|
|
Q("ArrayIndexFunction sees args[0] is a hashSelector")
|
|
return HashIndexFunction(env, name, args)
|
|
default:
|
|
return SexpNull, fmt.Errorf("bad (arrayidx ar index) call: ar was not an array, instead '%s'/type %T",
|
|
args[0].SexpString(nil), args[0])
|
|
}
|
|
|
|
var idx *SexpArray
|
|
switch idx2 := args[1].(type) {
|
|
case *SexpArray:
|
|
idx = idx2
|
|
default:
|
|
return SexpNull, fmt.Errorf("bad (arrayidx ar index) call: index was not an array, instead '%s'/type %T",
|
|
args[1].SexpString(nil), args[1])
|
|
}
|
|
|
|
ret := SexpArraySelector{
|
|
Select: idx,
|
|
Container: ar,
|
|
}
|
|
return &ret, nil
|
|
}
|
|
|
|
// IndexBy subsets one array (possibly multidimensional) by another.
|
|
// e.g. if arr is [a b c] and idx is [0], we'll return a.
|
|
func (arr *SexpArray) IndexBy(idx *SexpArray) (Sexp, error) {
|
|
nIdx := len(idx.Val)
|
|
nTarget := arr.NumDim()
|
|
|
|
if nIdx > nTarget {
|
|
return SexpNull, fmt.Errorf("bad (arrayidx ar index) call: index requested %d dimensions, only have %d",
|
|
nIdx, nTarget)
|
|
}
|
|
|
|
if len(idx.Val) == 0 {
|
|
return SexpNull, fmt.Errorf("bad (arrayidx ar index) call: no index supplied")
|
|
}
|
|
if len(idx.Val) != 1 {
|
|
return SexpNull, fmt.Errorf("bad (arrayidx ar index) call: we only support a single index value atm")
|
|
}
|
|
|
|
i := 0
|
|
myInt, isInt := idx.Val[i].(*SexpInt)
|
|
if !isInt {
|
|
return SexpNull, fmt.Errorf("bad (arrayidx ar index) call: index with non-integer '%v'",
|
|
idx.Val[i].SexpString(nil))
|
|
}
|
|
k := myInt.Val
|
|
pos := k % int64(len(arr.Val))
|
|
if k < 0 {
|
|
mk := -k
|
|
mod := mk % int64(len(arr.Val))
|
|
pos = int64(len(arr.Val)) - mod
|
|
}
|
|
//Q("return pos %v", pos)
|
|
return arr.Val[pos], nil
|
|
}
|
|
|
|
func (arr *SexpArray) NumDim() int {
|
|
return 1
|
|
}
|
|
|
|
// SexpSelector: select a subset of an array:
|
|
// can be multidimensional index/slice
|
|
// and hence know its container and its position(s),
|
|
// and thus be able to read and write that position as
|
|
// need be.
|
|
type SexpArraySelector struct {
|
|
Select *SexpArray
|
|
Container *SexpArray
|
|
}
|
|
|
|
func (si *SexpArraySelector) SexpString(ps *PrintState) string {
|
|
Q("in SexpArraySelector.SexpString(), si.Container.Env = %p", si.Container.Env)
|
|
rhs, err := si.RHS(si.Container.Env)
|
|
if err != nil {
|
|
return fmt.Sprintf("(arraySelector %v %v)", si.Container.SexpString(ps), si.Select.SexpString(ps))
|
|
}
|
|
|
|
Q("in SexpArraySelector.SexpString(), rhs = %v", rhs.SexpString(ps))
|
|
Q("in SexpArraySelector.SexpString(), si.Container = %v", si.Container.SexpString(ps))
|
|
Q("in SexpArraySelector.SexpString(), si.Select = %v", si.Select.SexpString(ps))
|
|
|
|
return fmt.Sprintf("%v /*(arraySelector %v %v)*/", rhs.SexpString(ps), si.Container.SexpString(ps), si.Select.SexpString(ps))
|
|
}
|
|
|
|
// Type returns the type of the value.
|
|
func (si *SexpArraySelector) Type() *RegisteredType {
|
|
return GoStructRegistry.Lookup("arraySelector")
|
|
}
|
|
|
|
// RHS applies the selector to the contain and returns
|
|
// the value obtained.
|
|
func (x *SexpArraySelector) RHS(env *Zlisp) (Sexp, error) {
|
|
if len(x.Select.Val) != 1 {
|
|
return SexpNull, fmt.Errorf("SexpArraySelector: only " +
|
|
"size 1 selectors implemented")
|
|
}
|
|
var i int64
|
|
switch asInt := x.Select.Val[0].(type) {
|
|
case *SexpInt:
|
|
i = asInt.Val
|
|
default:
|
|
return SexpNull, fmt.Errorf("SexpArraySelector: int "+
|
|
"selector required; we saw %T", x.Select.Val[0])
|
|
}
|
|
if i < 0 {
|
|
return SexpNull, fmt.Errorf("SexpArraySelector: negative "+
|
|
"indexes not supported; we saw %v", i)
|
|
}
|
|
if i >= int64(len(x.Container.Val)) {
|
|
return SexpNull, fmt.Errorf("SexpArraySelector: index "+
|
|
"%v is out-of-bounds; length is %v", i, len(x.Container.Val))
|
|
}
|
|
ret := x.Container.Val[i]
|
|
Q("arraySelector returning ret = %#v", ret)
|
|
return ret, nil
|
|
}
|
|
|
|
// Selector stores indexing information that isn't
|
|
// yet materialized for getting or setting.
|
|
//
|
|
type Selector interface {
|
|
// RHS (right-hand-side) is used to dereference
|
|
// the pointer-like Selector, yielding a value suitable for the
|
|
// right-hand-side of an assignment statement.
|
|
//
|
|
RHS(env *Zlisp) (Sexp, error)
|
|
|
|
// AssignToSelection sets the selection to rhs
|
|
// The selected elements are the left-hand-side of the
|
|
// assignment *lhs = rhs
|
|
AssignToSelection(env *Zlisp, rhs Sexp) error
|
|
}
|
|
|
|
func (x *SexpArraySelector) AssignToSelection(env *Zlisp, rhs Sexp) error {
|
|
_, err := x.RHS(x.Container.Env) // check for errors
|
|
if err != nil {
|
|
return err
|
|
}
|
|
x.Container.Val[x.Select.Val[0].(*SexpInt).Val] = rhs
|
|
return nil
|
|
}
|
|
|
|
func (env *Zlisp) NewSexpArray(arr []Sexp) *SexpArray {
|
|
return &SexpArray{Val: arr, Env: env}
|
|
}
|