refactored data-types to reduce plugin sizes

This commit is contained in:
2026-06-08 07:02:56 +02:00
parent fec7cc546c
commit b6da9bcad4
90 changed files with 1039 additions and 803 deletions
+222
View File
@@ -0,0 +1,222 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// list-type.go
package array
import (
"fmt"
"strings"
"git.portale-stac.it/go-pkg/expr/kern"
)
type ListType []any
func IsList(v any) (ok bool) {
_, ok = v.(*ListType)
return ok
}
func NewListA(listAny ...any) (list *ListType) {
if listAny == nil {
listAny = []any{}
}
return NewList(listAny)
}
func NewList(listAny []any) (list *ListType) {
if listAny != nil {
ls := make(ListType, len(listAny))
copy(ls, listAny)
list = &ls
}
return
}
func MakeList(length, capacity int) (list *ListType) {
if capacity < length {
capacity = length
}
ls := make(ListType, length, capacity)
list = &ls
return
}
func ListFromStrings(stringList []string) (list *ListType) {
list = MakeList(len(stringList), 0)
for i, s := range stringList {
(*list)[i] = s
}
return
}
func (ls *ListType) ToString(opt kern.FmtOpt) (s string) {
indent := kern.GetFormatIndent(opt)
flags := kern.GetFormatFlags(opt)
var sb strings.Builder
sb.WriteByte('[')
if len(*ls) > 0 {
innerOpt := kern.MakeFormatOptions(flags, indent+1)
nest := strings.Repeat(" ", indent+1)
if flags&kern.MultiLine != 0 {
sb.WriteByte('\n')
sb.WriteString(nest)
}
for i, item := range []any(*ls) {
if i > 0 {
if flags&kern.MultiLine != 0 {
sb.WriteString(",\n")
sb.WriteString(nest)
} else {
sb.WriteString(", ")
}
}
kern.Format(&sb, item, innerOpt)
}
if flags&kern.MultiLine != 0 {
sb.WriteByte('\n')
sb.WriteString(strings.Repeat(" ", indent))
}
}
sb.WriteByte(']')
s = sb.String()
if flags&kern.Truncate != 0 && len(s) > kern.TruncateSize {
s = kern.TruncateString(s)
}
return
}
func (ls *ListType) String() string {
return ls.ToString(0)
}
func (ls *ListType) TypeName() string {
return kern.TypeArray
}
func (ls *ListType) Contains(t *ListType) (answer bool) {
if len(*ls) >= len(*t) {
answer = true
for _, item := range *t {
if answer = ls.IndexDeepSameCmp(item) >= 0; !answer {
break
}
}
}
return
}
func (ls *ListType) EqualTo(other kern.Equaler) (equal bool) {
if otherList, ok := other.(*ListType); ok {
equal = ls.Equals(*otherList)
}
return
}
func (ls *ListType) Equals(ls2 ListType) (answer bool) {
if ls2 != nil && len(*ls) == len(ls2) {
answer = true
for index, i1 := range *ls {
// if !reflect.DeepEqual(i1, ls2[index]) {
// answer = false
// break
// }
if !kern.Equal(i1, ls2[index]) {
answer = false
break
}
}
}
return
}
func (ls1 *ListType) Clone() (ls2 *ListType) {
ls := make(ListType, len(*ls1))
for i, item := range *ls1 {
ls[i] = kern.Clone(item)
}
ls2 = &ls
return
}
func (ls *ListType) IndexDeepSameCmp(target any) (index int) {
index = -1
for i, item := range *ls {
if kern.Equal(item, target) {
index = i
break
}
}
return
}
// func (ls *ListType) IndexDeepSameCmp(target any) (index int) {
// var eq bool
// var err error
// index = -1
// for i, item := range *ls {
// if eq, err = deepSame(item, target, SameContent); err != nil {
// break
// } else if eq {
// index = i
// break
// }
// }
// return
// }
func SameContent(a, b any) (same bool, err error) {
la, _ := a.(*ListType)
lb, _ := b.(*ListType)
if len(*la) == len(*lb) {
same = true
for _, item := range *la {
if pos := lb.IndexDeepSameCmp(item); pos < 0 {
same = false
break
}
}
}
return
}
// func deepSame(a, b any, deepCmp kern.DeepFuncTemplate) (eq bool, err error) {
// if IsNumOrFract(a) && IsNumOrFract(b) {
// if IsNumber(a) && IsNumber(b) {
// if IsInteger(a) && IsInteger(b) {
// li, _ := a.(int64)
// ri, _ := b.(int64)
// eq = li == ri
// } else {
// eq = NumAsFloat(a) == NumAsFloat(b)
// }
// } else {
// var cmp int
// if cmp, err = fract.CmpAnyFract(a, b); err == nil {
// eq = cmp == 0
// }
// }
// } else if deepCmp != nil && IsList(a) && IsList(b) {
// eq, err = deepCmp(a, b)
// } else {
// eq = reflect.DeepEqual(a, b)
// }
// return
// }
func (ls *ListType) SetItem(index int64, value any) (err error) {
if index >= 0 && index < int64(len(*ls)) {
(*ls)[index] = value
} else {
err = fmt.Errorf("index %d out of bounds (0, %d)", index, len(*ls)-1)
}
return
}
func (ls *ListType) AppendItem(value any) {
*ls = append(*ls, value)
}
+34
View File
@@ -0,0 +1,34 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// bool.go
package boolean
func IsBool(v any) (ok bool) {
_, ok = v.(bool)
return ok
}
// func (b *bool) EqualTo(other kern.Equaler) (equal bool) {
// if otherBool, ok := other.(*bool); ok {
// equal = b == otherBool
// }
// return
// }
func ToBool(v any) (b bool, ok bool) {
ok = true
switch x := v.(type) {
case string:
b = len(x) > 0
case float64:
b = x != 0.0
case int64:
b = x != 0
case bool:
b = x
default:
ok = false
}
return
}
+36
View File
@@ -0,0 +1,36 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// clone-value.go
package types
// import (
// "git.portale-stac.it/go-pkg/expr/types/array"
// "git.portale-stac.it/go-pkg/expr/types/dict"
// "git.portale-stac.it/go-pkg/expr/types/list"
// )
// func Clone(v any) (c any) {
// if v == nil {
// return
// }
// switch unboxed := v.(type) {
// case int64:
// c = unboxed
// case float64:
// c = unboxed
// case string:
// c = unboxed
// case bool:
// c = unboxed
// case *array.ListType:
// c = unboxed.Clone()
// case *dict.DictType:
// c = unboxed.Clone()
// case *list.LinkedList:
// c = unboxed.Clone()
// default:
// c = v
// }
// return
// }
+52
View File
@@ -0,0 +1,52 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// compare.go
package types
import (
"reflect"
"git.portale-stac.it/go-pkg/expr/types/array"
"git.portale-stac.it/go-pkg/expr/types/boolean"
"git.portale-stac.it/go-pkg/expr/types/dict"
"git.portale-stac.it/go-pkg/expr/types/float"
"git.portale-stac.it/go-pkg/expr/types/fract"
"git.portale-stac.it/go-pkg/expr/types/list"
"git.portale-stac.it/go-pkg/expr/types/str"
)
func Equal(value1, value2 any) (equal bool) {
if value1 == nil && value2 == nil {
equal = true
} else if value1 == nil || value2 == nil {
equal = false
} else if boolean.IsBool(value1) && boolean.IsBool(value2) {
equal = value1.(bool) == value2.(bool)
} else if array.IsList(value1) && array.IsList(value2) {
ls1 := value1.(*array.ListType)
ls2 := value2.(*array.ListType)
equal = ls1.Equals(*ls2)
} else if dict.IsDict(value1) && dict.IsDict(value2) {
d1 := value1.(*dict.DictType)
d2 := value2.(*dict.DictType)
equal = d1.Equals(*d2)
} else if list.IsLinkedList(value1) && list.IsLinkedList(value2) {
ll1 := value1.(*list.LinkedList)
ll2 := value2.(*list.LinkedList)
equal = ll1.Equals(ll2)
} else if IsInteger(value1) && IsInteger(value2) {
equal = value1.(int64) == value2.(int64)
} else if str.IsString(value1) && str.IsString(value2) {
equal = value1.(string) == value2.(string)
} else if float.IsFloat(value1) && float.IsFloat(value2) {
equal = value1.(float64) == value2.(float64)
} else if IsNumOrFract(value1) && IsNumOrFract(value2) {
if eq, err := fract.CmpAnyFract(value1, value2); err == nil {
equal = eq == 0
}
} else if !reflect.DeepEqual(value1, value2) {
equal = false
}
return
}
+201
View File
@@ -0,0 +1,201 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// dict-type.go
package dict
import (
"fmt"
"reflect"
"strings"
"git.portale-stac.it/go-pkg/expr/kern"
)
const DictTypeName = "dict"
type DictType map[any]any
func IsDict(v any) (ok bool) {
_, ok = v.(*DictType)
return ok
}
func MakeDict() (dict *DictType) {
d := make(DictType)
dict = &d
return
}
func NewDict(dictAny map[any]any) (dict *DictType) {
var d DictType
if dictAny != nil {
d = make(DictType, len(dictAny))
for i, item := range dictAny {
d[i] = item
}
} else {
d = make(DictType)
}
dict = &d
return
}
func newDict(dictAny map[any]kern.Term) (dict *DictType) {
// TODO Change with a call to NewDict()
var d DictType
if dictAny != nil {
d = make(DictType, len(dictAny))
for i, item := range dictAny {
d[i] = item
}
} else {
d = make(DictType)
}
dict = &d
return
}
func (dict *DictType) toMultiLine(sb *strings.Builder, opt kern.FmtOpt) {
indent := kern.GetFormatIndent(opt)
flags := kern.GetFormatFlags(opt)
//sb.WriteString(strings.Repeat(" ", indent))
sb.WriteByte('{')
if len(*dict) > 0 {
innerOpt := kern.MakeFormatOptions(flags, indent+1)
nest := strings.Repeat(" ", indent+1)
sb.WriteByte('\n')
first := true
for name, value := range *dict {
if first {
first = false
} else {
sb.WriteByte(',')
sb.WriteByte('\n')
}
sb.WriteString(nest)
if key, ok := name.(string); ok {
sb.WriteByte('"')
sb.WriteString(key)
sb.WriteByte('"')
} else {
fmt.Fprintf(sb, "%v", name)
}
sb.WriteString(": ")
if f, ok := value.(kern.Formatter); ok {
sb.WriteString(f.ToString(innerOpt))
} else if _, ok = value.(kern.Functor); ok {
sb.WriteString("func(){}")
} else {
fmt.Fprintf(sb, "%v", value)
}
}
sb.WriteByte('\n')
sb.WriteString(strings.Repeat(" ", indent))
}
sb.WriteString("}")
}
func (dict *DictType) ToString(opt kern.FmtOpt) string {
var sb strings.Builder
flags := kern.GetFormatFlags(opt)
if flags&kern.MultiLine != 0 {
dict.toMultiLine(&sb, opt)
} else {
sb.WriteByte('{')
first := true
for key, value := range *dict {
if first {
first = false
} else {
sb.WriteString(", ")
}
if s, ok := key.(string); ok {
sb.WriteByte('"')
sb.WriteString(s)
sb.WriteByte('"')
} else {
fmt.Fprintf(&sb, "%v", key)
}
sb.WriteString(": ")
if formatter, ok := value.(kern.Formatter); ok {
sb.WriteString(formatter.ToString(opt))
} else if t, ok := value.(kern.Term); ok {
sb.WriteString(t.String())
} else {
fmt.Fprintf(&sb, "%#v", value)
}
}
sb.WriteByte('}')
}
return sb.String()
}
func (dict *DictType) String() string {
return dict.ToString(0)
}
func (dict *DictType) TypeName() string {
return DictTypeName
}
func (dict *DictType) HasKey(target any) (ok bool) {
for key := range *dict {
if ok = reflect.DeepEqual(key, target); ok {
break
}
}
return
}
func (dict *DictType) SetItem(key any, value any) {
(*dict)[key] = value
}
func (dict *DictType) GetItem(key any) (value any, exists bool) {
value, exists = (*dict)[key]
return
}
func (dict *DictType) Clone() (c *DictType) {
c = newDict(nil)
for k, v := range *dict {
(*c)[k] = kern.Clone(v)
}
return
}
func (dict *DictType) Merge(second *DictType) {
if second != nil {
for k, v := range *second {
(*dict)[k] = v
}
}
}
func (dict *DictType) Equals(dict2 DictType) (answer bool) {
if dict2 != nil && len(*dict) == len(dict2) {
answer = true
for key, value1 := range *dict {
if value2, exists := dict2.GetItem(key); exists {
if !kern.Equal(value1, value2) {
answer = false
break
}
} else {
answer = false
break
}
}
}
return
}
////////////////
// type DictFormat interface {
// ToDict() *DictType
// }
+23
View File
@@ -0,0 +1,23 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// float.go
package float
func IsFloat(v any) (ok bool) {
_, ok = v.(float64)
return ok
}
func AnyFloat(v any) (float float64, ok bool) {
ok = true
switch floatval := v.(type) {
case float32:
float = float64(floatval)
case float64:
float = floatval
default:
ok = false
}
return
}
+399
View File
@@ -0,0 +1,399 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// fraction-type.go
package fract
//https://www.youmath.it/lezioni/algebra-elementare/lezioni-di-algebra-e-aritmetica-per-scuole-medie/553-dalle-frazioni-a-numeri-decimali.html
import (
"errors"
"fmt"
"math"
"strconv"
"strings"
"git.portale-stac.it/go-pkg/expr/kern"
)
type FractionType struct {
num, den int64
}
func NewFraction(num, den int64) *FractionType {
num, den = simplifyIntegers(num, den)
return &FractionType{num, den}
}
func (fr *FractionType) EqualTo(other kern.Equaler) (equal bool) {
if otherFract, ok := other.(*FractionType); ok && otherFract != nil {
equal = fr.Equals(*otherFract)
}
return
}
func (fr *FractionType) Equals(other FractionType) bool {
// return fr.num*other.den == other.num*fr.den
return fr.num == other.num && fr.den == other.den
}
func Float64ToFraction(f float64) (fract *FractionType, err error) {
var sign string
intPart, decPart := math.Modf(f)
if decPart < 0.0 {
sign = "-"
intPart = -intPart
decPart = -decPart
}
dec := fmt.Sprintf("%.12f", decPart)
s := fmt.Sprintf("%s%.f%s", sign, intPart, dec[1:])
return MakeGeneratingFraction(s)
}
// Based on https://cs.opensource.google/go/go/+/refs/tags/go1.22.3:src/math/big/rat.go;l=39
func MakeGeneratingFraction(s string) (f *FractionType, err error) {
var num, den int64
var sign int64 = 1
var parts []string
if len(s) == 0 {
goto exit
}
switch s[0] {
case '-':
sign = int64(-1)
s = s[1:]
case '+':
s = s[1:]
}
// if strings.HasSuffix(s, "()") {
// s = s[0 : len(s)-2]
// }
s = strings.TrimSuffix(s, "()")
parts = strings.SplitN(s, ".", 2)
if num, err = strconv.ParseInt(parts[0], 10, 64); err != nil {
return
}
if len(parts) == 1 {
f = NewFraction(sign*num, 1)
} else if len(parts) == 2 {
subParts := strings.SplitN(parts[1], "(", 2)
if len(subParts) == 1 {
den = 1
dec := parts[1]
lsd := len(dec)
for i := lsd - 1; i >= 0 && dec[i] == '0'; i-- {
lsd--
}
for _, c := range dec[0:lsd] {
if c < '0' || c > '9' {
return nil, kern.ErrExpectedGot("fract", "digit", c)
}
num = num*10 + int64(c-'0')
den = den * 10
}
f = NewFraction(sign*num, den)
} else if len(subParts) == 2 {
sub := num
mul := int64(1)
for _, c := range subParts[0] {
if c < '0' || c > '9' {
return nil, kern.ErrExpectedGot("fract", "digit", c)
}
num = num*10 + int64(c-'0')
sub = sub*10 + int64(c-'0')
mul *= 10
}
if len(subParts) == 2 {
if s[len(s)-1] != ')' {
goto exit
}
p := subParts[1][0 : len(subParts[1])-1]
for _, c := range p {
if c < '0' || c > '9' {
return nil, kern.ErrExpectedGot("fract", "digit", c)
}
num = num*10 + int64(c-'0')
den = den*10 + 9
}
den *= mul
}
num -= sub
f = NewFraction(sign*num, den)
}
}
exit:
if f == nil {
err = errors.New("bad syntax")
}
return
}
func (fr *FractionType) N() int64 {
return fr.num
}
func (fr *FractionType) D() int64 {
return fr.den
}
func (fr *FractionType) ToFloat() float64 {
return float64(fr.num) / float64(fr.den)
}
func (fr *FractionType) String() string {
return fr.ToString(0)
}
func (fr *FractionType) ToString(opt kern.FmtOpt) string {
var sb strings.Builder
if opt&kern.MultiLine == 0 {
sb.WriteString(fmt.Sprintf("%d:%d", fr.num, fr.den))
} else {
var sign, num string
if fr.num < 0 && opt&kern.TTY == 0 {
num = strconv.FormatInt(-fr.num, 10)
sign = "-"
} else {
num = strconv.FormatInt(fr.num, 10)
}
den := strconv.FormatInt(fr.den, 10)
size := max(len(num), len(den))
if opt&kern.TTY != 0 {
sNum := fmt.Sprintf("\x1b[4m%[1]*s\x1b[0m\n", -size, fmt.Sprintf("%[1]*s", (size+len(num))/2, sign+num))
sb.WriteString(sNum)
} else {
if len(sign) > 0 {
sb.WriteString(" ")
}
sb.WriteString(fmt.Sprintf("%[1]*s", -size, fmt.Sprintf("%[1]*s", (size+len(num))/2, num)))
sb.WriteByte('\n')
if len(sign) > 0 {
sb.WriteString(sign)
sb.WriteByte(' ')
}
sb.WriteString(strings.Repeat("-", size))
sb.WriteByte('\n')
if len(sign) > 0 {
sb.WriteString(" ")
}
}
sDen := fmt.Sprintf("%[1]*s", size, fmt.Sprintf("%[1]*s", (size+len(den))/2, den))
sb.WriteString(sDen)
}
return sb.String()
}
func (fr *FractionType) TypeName() string {
return "fraction"
}
// -------- fraction utility functions
// greatest common divider
func Gcd(a, b int64) (g int64) {
if a < 0 {
a = -a
}
if b < 0 {
b = -b
}
if a < b {
a, b = b, a
}
r := a % b
for r > 0 {
a, b = b, r
r = a % b
}
g = b
return
}
// lower common multiple
func lcm(a, b int64) (l int64) {
g := Gcd(a, b)
l = a * b / g
return
}
// Sum two fractions
func SumFract(f1, f2 *FractionType) (sum *FractionType) {
m := lcm(f1.den, f2.den)
sum = NewFraction(f1.num*(m/f1.den)+f2.num*(m/f2.den), m)
return
}
// Multiply two fractions
func MulFract(f1, f2 *FractionType) (prod *FractionType) {
prod = NewFraction(f1.num*f2.num, f1.den*f2.den)
return
}
func anyToFract(v any) (f *FractionType, err error) {
var ok bool
if f, ok = v.(*FractionType); !ok {
if n, ok := v.(int64); ok {
f = intToFraction(n)
} else if dec, ok := v.(float64); ok {
f, err = Float64ToFraction(dec)
} else {
err = kern.ErrExpectedGot("fract", kern.TypeFraction, v)
}
}
return
}
func anyPairToFract(v1, v2 any) (f1, f2 *FractionType, err error) {
if f1, err = anyToFract(v1); err != nil {
return
}
if f2, err = anyToFract(v2); err != nil {
return
}
return
}
func SumAnyFract(af1, af2 any) (sum any, err error) {
var f1, f2 *FractionType
if f1, f2, err = anyPairToFract(af1, af2); err != nil {
return
}
f := SumFract(f1, f2)
if f.num == 0 {
sum = 0
} else {
sum = simplifyFraction(f)
}
return
}
// Returns
//
// <0 if af1 < af2
// =0 if af1 == af2
// >0 if af1 > af2
// err if af1 or af2 is not convertible to fraction
func CmpAnyFract(af1, af2 any) (result int, err error) {
var f1, f2 *FractionType
if f1, f2, err = anyPairToFract(af1, af2); err != nil {
return
}
result = cmpFract(f1, f2)
return
}
// Returns
//
// <0 if af1 < af2
// =0 if af1 == af2
// >0 if af1 > af2
func cmpFract(f1, f2 *FractionType) (result int) {
if f1 != nil && f2 != nil {
f2.num = -f2.num
f := SumFract(f1, f2)
if f.num < 0 {
result = -1
} else if f.num > 0 {
result = 1
} else {
result = 0
}
}
return
}
func SubAnyFract(af1, af2 any) (sum any, err error) {
var f1, f2 *FractionType
if f1, f2, err = anyPairToFract(af1, af2); err != nil {
return
}
f2.num = -f2.num
f := SumFract(f1, f2)
if f.num == 0 {
sum = 0
} else {
sum = simplifyFraction(f)
}
return
}
func MulAnyFract(af1, af2 any) (prod any, err error) {
var f1, f2 *FractionType
if f1, f2, err = anyPairToFract(af1, af2); err != nil {
return
}
if f1.num == 0 || f2.num == 0 {
prod = 0
} else {
f := &FractionType{f1.num * f2.num, f1.den * f2.den}
prod = simplifyFraction(f)
}
return
}
func DivAnyFract(af1, af2 any) (quot any, err error) {
var f1, f2 *FractionType
if f1, f2, err = anyPairToFract(af1, af2); err != nil {
return
}
if f2.num == 0 {
err = errors.New("division by zero")
return
}
if f1.num == 0 || f2.den == 0 {
quot = 0
} else {
f := &FractionType{f1.num * f2.den, f1.den * f2.num}
quot = simplifyFraction(f)
}
return
}
func simplifyFraction(f *FractionType) (v any) {
f.num, f.den = simplifyIntegers(f.num, f.den)
if f.den == 1 {
v = f.num
} else {
v = &FractionType{f.num, f.den}
}
return v
}
func simplifyIntegers(num, den int64) (a, b int64) {
if num == 0 {
return 0, 1
}
if den == 0 {
panic("fraction with denominator == 0")
}
if den < 0 {
den = -den
num = -num
}
g := Gcd(num, den)
a = num / g
b = den / g
return
}
func intToFraction(n int64) *FractionType {
return &FractionType{n, 1}
}
func IsFraction(v any) (ok bool) {
_, ok = v.(*FractionType)
return ok
}
// func IsFract(v any) (ok bool) {
// _, ok = v.(*FractionType)
// return ok
// }
func IsRational(v any) (ok bool) {
if _, ok = v.(*FractionType); !ok {
_, ok = v.(int64)
}
return ok
}
+122
View File
@@ -0,0 +1,122 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// linked-list-type.go
package list
import (
"strings"
"git.portale-stac.it/go-pkg/expr/kern"
)
const LinkedListTypeName = "lisked-list"
func IsLinkedList(v any) (ok bool) {
_, ok = v.(*LinkedList)
return ok
}
func NewLinkedListA(listAny ...any) (list *LinkedList) {
if listAny == nil {
listAny = []any{}
}
list = NewLinkedList()
for _, item := range listAny {
list.PushBack(kern.FixAnyNumber(item))
}
return
}
func LinkedListFromStrings(stringList []string) (list *LinkedList) {
list = NewLinkedList()
for _, s := range stringList {
list.PushBack(s)
}
return
}
func (ls *LinkedList) ToString(opt kern.FmtOpt) (s string) {
indent := kern.GetFormatIndent(opt)
flags := kern.GetFormatFlags(opt)
var sb strings.Builder
sb.WriteString("[<")
if ls.Len() > 0 {
innerOpt := kern.MakeFormatOptions(flags, indent+1)
nest := strings.Repeat(" ", indent+1)
if flags&kern.MultiLine != 0 {
sb.WriteByte('\n')
sb.WriteString(nest)
}
i := 0
for item := ls.FirstNode(); item != nil; item = item.Next() {
if i > 0 {
if flags&kern.MultiLine != 0 {
sb.WriteString(",\n")
sb.WriteString(nest)
} else {
sb.WriteString(", ")
}
}
kern.Format(&sb, item.Data(), innerOpt)
i++
}
if flags&kern.MultiLine != 0 {
sb.WriteByte('\n')
sb.WriteString(strings.Repeat(" ", indent))
}
}
sb.WriteString(">]")
s = sb.String()
if flags&kern.Truncate != 0 && len(s) > kern.TruncateSize {
s = kern.TruncateString(s)
}
return
}
func (ls *LinkedList) String() string {
return ls.ToString(0)
}
func (ls *LinkedList) TypeName() string {
return LinkedListTypeName
}
// func (ls *LinkedList) Contains(t *ListType) (answer bool) {
// if len(*ls) >= len(*t) {
// answer = true
// for _, item := range *t {
// if answer = ls.IndexDeepSameCmp(item) >= 0; !answer {
// break
// }
// }
// }
// return
// }
func (ls1 *LinkedList) Equals(ls2 *LinkedList) (answer bool) {
if ls2 != nil && ls1.Len() == ls2.Len() {
answer = true
i2 := ls2.FirstNode()
for i1 := ls1.FirstNode(); i1 != nil; i1 = i1.Next() {
if !kern.Equal(i1.Data(), i2.Data()) {
answer = false
break
}
i2 = i2.Next()
}
}
return
}
func (ls1 *LinkedList) Clone() (ls2 *LinkedList) {
ls2 = NewLinkedListA()
for i1 := ls1.FirstNode(); i1 != nil; i1 = i1.Next() {
ls2.PushBack(kern.Clone(i1.Data()))
}
return
}
+289
View File
@@ -0,0 +1,289 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// linked-list.go
package list
import (
"fmt"
"git.portale-stac.it/go-pkg/expr/kern"
)
type ListNode struct {
data any
next *ListNode
}
func (node *ListNode) Next() *ListNode {
return node.next
}
func (self *ListNode) Data() any {
return self.data
}
type LinkedList struct {
count int
first *ListNode
last *ListNode
}
func NewLinkedList() (list *LinkedList) {
list = &LinkedList{0, nil, nil}
return
}
func (ls *LinkedList) Len() int {
return ls.count
}
func (ls *LinkedList) Empty() bool {
return ls.count == 0
}
func (ls *LinkedList) PushFront(data any) *ListNode {
ls.first = &ListNode{data, ls.first}
if ls.last == nil {
ls.last = ls.first
}
ls.count++
return ls.first
}
func (ls *LinkedList) SeqPushFront(data any) (result *LinkedList) {
switch seq := data.(type) {
case kern.Iterator:
result = ls.IterPushFront(seq)
default:
ls.PushFront(data)
result = ls
}
return
}
func (ls *LinkedList) IterPushFront(it kern.Iterator) *LinkedList {
for item, err1 := it.Next(); err1 == nil; item, err1 = it.Next() {
ls.PushFront(item)
}
return ls
}
func (ls *LinkedList) PushBack(data any) (node *ListNode) {
node = &ListNode{data, nil}
if ls.last != nil {
ls.last.next = node
}
ls.last = node
if ls.first == nil {
ls.first = node
}
ls.count++
return
}
func (ls *LinkedList) SeqPushBack(data any) (result *LinkedList) {
switch seq := data.(type) {
case kern.Iterator:
result = ls.IterPushBack(seq)
default:
ls.PushBack(data)
result = ls
}
return
}
func (ls *LinkedList) IterPushBack(it kern.Iterator) *LinkedList {
for item, err1 := it.Next(); err1 == nil; item, err1 = it.Next() {
ls.PushBack(item)
}
return ls
}
func (ls *LinkedList) FirstNode() *ListNode {
return ls.first
}
func (ls *LinkedList) First() (data any, err error) {
if ls.first != nil {
data = ls.first.data
} else {
err = errorListEmpty()
}
return
}
func (ls *LinkedList) LastNode() *ListNode {
return ls.last
}
func (ls *LinkedList) Last() (data interface{}, err error) {
if ls.last != nil {
data = ls.last.data
} else {
err = errorListEmpty()
}
return
}
func (ls *LinkedList) NodeAt(index int) (node *ListNode, err error) {
if ls.count == 0 {
err = errorListEmpty()
} else if index > ls.count {
err = errorOutOfRange()
} else if index == ls.count-1 {
node = ls.last
} else {
current := ls.first
for range index {
current = current.next
}
node = current
}
return
}
func (ls *LinkedList) At(index int) (data interface{}, err error) {
node, err := ls.NodeAt(index)
if err == nil {
data = node.data
}
return
}
func (ls *LinkedList) Insert(index int, data any) *ListNode {
var prev *ListNode
prev = nil
current := ls.first
for pos := 0; current != nil && pos < index; pos++ {
prev = current
current = current.next
}
node := &ListNode{data, current}
if prev != nil {
prev.next = node
}
if ls.first == current {
ls.first = node
}
if node.next == nil {
ls.last = node
}
ls.count++
return node
}
func (ls *LinkedList) PushBackStringArray(items []string) {
for _, v := range items {
ls.PushBack(v)
}
}
func (ls *LinkedList) Sub(start, end int) (subList *LinkedList) {
subList = NewLinkedList()
if node, err := ls.NodeAt(start); err == nil {
for i := start; i < end && node != nil; i++ {
subList.PushBack(node.data)
node = node.next
}
}
return
}
// type TraverseOperator func(index int, elem interface{}, userData interface{}) (err error)
// func (self *LinkedList) Traverse(op TraverseOperator, user_data interface{}) (err error) {
// index := int(0)
// for current := self.first; current != nil; current = current.next {
// err = op(index, current.data, user_data)
// if err != nil {
// break
// }
// index++
// }
// return
// }
// func (self *LinkedList) Traverse2(observer Observer, abortOnError bool) (err error) {
// index := int(0)
// for current := self.first; current != nil; current = current.next {
// err = observer.Observe(current, index)
// if err != nil && abortOnError {
// break
// }
// index++
// }
// return
// }
type EqualFunc func(current, target any) bool
func (ls *LinkedList) FindFirst(eqFunc EqualFunc, targetData any) (targetNode *ListNode) {
for current := ls.first; current != nil && targetNode == nil; current = current.next {
if eqFunc(current.data, targetData) {
targetNode = current
}
}
return
}
func (ls *LinkedList) FindNext(eqFunc EqualFunc, startNode *ListNode) (targetNode *ListNode) {
if startNode != nil {
for current := startNode.next; current != nil && targetNode == nil; current = current.next {
if eqFunc(current.data, startNode.Data()) {
targetNode = current
}
}
}
return
}
// type DataFeeder func(user_data interface{}) interface{}
// type NodeObserver func(node *ListNode, index int, userData interface{})
// func (self *LinkedList) FeedTail(feeder DataFeeder, feederUserData interface{}, observer NodeObserver, observerUserData interface{}) (count int) {
// count = 0
// for item := feeder(feederUserData); item != nil; item = feeder(feederUserData) {
// // fmt.Println("Item", count, item)
// node := self.PushBack(item)
// if observer != nil {
// observer(node, count, observerUserData)
// }
// count++
// }
// return
// }
// func (self *LinkedList) FeedTail2(feeder Feeder, observer Observer, abortOnError bool) (count int, err error) {
// count = 0
// // item := feeder.Next()
// for item, err1 := feeder.Next(); item != nil; item, err1 = feeder.Next() {
// if err1 != nil {
// if err == nil {
// err = err1
// }
// if abortOnError {
// break
// }
// }
// // fmt.Println("Item", count, item)
// node := self.PushBack(item)
// if observer != nil {
// observer.Observe(node, count)
// }
// count++
// }
// return
// }
func errorListEmpty() error {
return fmt.Errorf("List is empty")
}
func errorOutOfRange() error {
return fmt.Errorf("Out of range")
}
+93
View File
@@ -0,0 +1,93 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// number.go
package types
import (
"fmt"
"git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/types/float"
"git.portale-stac.it/go-pkg/expr/types/fract"
"git.portale-stac.it/go-pkg/expr/types/str"
)
func IsInteger(v any) (ok bool) {
_, ok = v.(int64)
return ok
}
func IsNumber(v any) (ok bool) {
return float.IsFloat(v) || IsInteger(v)
}
func IsNumOrFract(v any) (ok bool) {
return float.IsFloat(v) || IsInteger(v) || fract.IsFraction(v)
}
func IsNumberString(v any) (ok bool) {
return str.IsString(v) || IsNumber(v)
}
func NumAsFloat(v any) (f float64) {
var ok bool
if f, ok = v.(float64); !ok {
if fract, ok := v.(*fract.FractionType); ok {
f = fract.ToFloat()
} else {
i, _ := v.(int64)
f = float64(i)
}
}
return
}
func AnyInteger(v any) (i int64, ok bool) {
ok = true
switch intval := v.(type) {
case int:
i = int64(intval)
case uint8:
i = int64(intval)
case uint16:
i = int64(intval)
case uint64:
i = int64(intval)
case uint32:
i = int64(intval)
case int8:
i = int64(intval)
case int16:
i = int64(intval)
case int32:
i = int64(intval)
case int64:
i = intval
default:
ok = false
}
return
}
func ToGoInt(value any, description string) (i int, err error) {
if valueInt64, ok := value.(int64); ok {
i = int(valueInt64)
} else if valueInt, ok := value.(int); ok {
i = valueInt
} else {
err = fmt.Errorf("%s expected integer, got %s (%v)", description, kern.TypeName(value), value)
}
return
}
func ToGoInt64(value any, description string) (i int64, err error) {
if valueInt64, ok := value.(int64); ok {
i = valueInt64
} else if valueInt, ok := value.(int); ok {
i = int64(valueInt)
} else {
err = fmt.Errorf("%s expected integer, got %s (%v)", description, kern.TypeName(value), value)
}
return
}
+25
View File
@@ -0,0 +1,25 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// string.go
package str
import (
"fmt"
"git.portale-stac.it/go-pkg/expr/kern"
)
func IsString(v any) (ok bool) {
_, ok = v.(string)
return ok
}
func ToGoString(value any, description string) (s string, err error) {
if s, ok := value.(string); ok {
return s, nil
} else {
err = fmt.Errorf("%s expected string, got %s (%v)", description, kern.TypeName(value), value)
}
return
}
+20
View File
@@ -0,0 +1,20 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// types.go
package types
// func TypeName(v any) (name string) {
// if v == nil {
// name = "nil"
// } else if typer, ok := v.(kern.Typer); ok {
// name = typer.TypeName()
// } else if IsInteger(v) {
// name = "integer"
// } else if float.IsFloat(v) {
// name = "float"
// } else {
// name = fmt.Sprintf("%T", v)
// }
// return
// }
+41
View File
@@ -0,0 +1,41 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// dict-type.go
package types
import (
"git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/types/dict"
)
func ContextToDict(ctx kern.ExprContext) (dt *dict.DictType) {
var keys []string
// Variables
keys = ctx.EnumVars(nil)
vars := dict.MakeDict()
for _, key := range keys {
value, _ := ctx.GetVar(key)
vars.SetItem(key, value)
}
// Functions
keys = ctx.EnumFuncs(func(name string) bool { return true })
funcs := dict.MakeDict()
for _, key := range keys {
funcInfo, _ := ctx.GetFuncInfo(key)
funcs.SetItem(key, funcInfo)
}
dt = dict.MakeDict()
dt.SetItem("vars", vars)
dt.SetItem("funcs", funcs)
return
}
func ContextToString(ctx kern.ExprContext, opt kern.FmtOpt) string {
if dict, ok := ctx.ToDict().(*dict.DictType); ok {
return dict.ToString(opt)
}
return ""
}