new type LinkedList, preliminary implementation
This commit is contained in:
@@ -275,6 +275,16 @@ func charFunc(ctx kern.ExprContext, name string, args map[string]any) (result an
|
||||
return
|
||||
}
|
||||
|
||||
func seqFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
list := kern.NewLinkedList()
|
||||
items := args[kern.ParamValue].([]any)
|
||||
for _, arg := range items {
|
||||
list.PushBack(arg)
|
||||
}
|
||||
result = list
|
||||
return
|
||||
}
|
||||
|
||||
//// import
|
||||
|
||||
func ImportBuiltinsFuncs(ctx kern.ExprContext) {
|
||||
@@ -318,6 +328,10 @@ func ImportBuiltinsFuncs(ctx kern.ExprContext) {
|
||||
ctx.RegisterFunc("char", kern.NewGolangFunctor(charFunc), kern.TypeString, []kern.ExprFuncParam{
|
||||
kern.NewFuncParam(kern.ParamValue),
|
||||
})
|
||||
|
||||
ctx.RegisterFunc("seq", kern.NewGolangFunctor(seqFunc), kern.TypeLinkedList, []kern.ExprFuncParam{
|
||||
kern.NewFuncParamFlag(kern.ParamValue, kern.PfRepeat),
|
||||
})
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
@@ -21,6 +21,8 @@ func Clone(v any) (c any) {
|
||||
c = unboxed.Clone()
|
||||
case *DictType:
|
||||
c = unboxed.Clone()
|
||||
case *LinkedList:
|
||||
c = unboxed.Clone()
|
||||
default:
|
||||
c = v
|
||||
}
|
||||
|
||||
@@ -20,4 +20,5 @@ const (
|
||||
TypeDict = "dict"
|
||||
TypeListOf = "list-of-"
|
||||
TypeListOfStrings = "list-of-strings"
|
||||
TypeLinkedList = "linked-list"
|
||||
)
|
||||
|
||||
@@ -20,6 +20,10 @@ func Equal(value1, value2 any) (equal bool) {
|
||||
d1 := value1.(*DictType)
|
||||
d2 := value2.(*DictType)
|
||||
equal = d1.Equals(*d2)
|
||||
} else if IsLinkedList(value1) && IsLinkedList(value2) {
|
||||
ll1 := value1.(*LinkedList)
|
||||
ll2 := value2.(*LinkedList)
|
||||
equal = ll1.Equals(ll2)
|
||||
} else if IsInteger(value1) && IsInteger(value2) {
|
||||
equal = value1.(int64) == value2.(int64)
|
||||
} else if IsString(value1) && IsString(value2) {
|
||||
|
||||
+16
-1
@@ -4,7 +4,10 @@
|
||||
// formatter.go
|
||||
package kern
|
||||
|
||||
import "fmt"
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type FmtOpt uint32 // lower 16 bits hold a bit-mask, higher 16 bits hold an indentation number
|
||||
|
||||
@@ -46,6 +49,18 @@ type Formatter interface {
|
||||
ToString(options FmtOpt) string
|
||||
}
|
||||
|
||||
func Format(sb *strings.Builder, item any, opt FmtOpt) {
|
||||
if s, ok := item.(string); ok {
|
||||
sb.WriteByte('"')
|
||||
sb.WriteString(s)
|
||||
sb.WriteByte('"')
|
||||
} else if formatter, ok := item.(Formatter); ok {
|
||||
sb.WriteString(formatter.ToString(opt))
|
||||
} else {
|
||||
fmt.Fprintf(sb, "%v", item)
|
||||
}
|
||||
}
|
||||
|
||||
func GetFormatted(v any, opt FmtOpt) (text string) {
|
||||
if v == nil {
|
||||
text = "(nil)"
|
||||
|
||||
+15
-23
@@ -73,15 +73,7 @@ func (ls *ListType) ToString(opt FmtOpt) (s string) {
|
||||
sb.WriteString(", ")
|
||||
}
|
||||
}
|
||||
if s, ok := item.(string); ok {
|
||||
sb.WriteByte('"')
|
||||
sb.WriteString(s)
|
||||
sb.WriteByte('"')
|
||||
} else if formatter, ok := item.(Formatter); ok {
|
||||
sb.WriteString(formatter.ToString(innerOpt))
|
||||
} else {
|
||||
sb.WriteString(fmt.Sprintf("%v", item))
|
||||
}
|
||||
Format(&sb, item, innerOpt)
|
||||
}
|
||||
if flags&MultiLine != 0 {
|
||||
sb.WriteByte('\n')
|
||||
@@ -104,11 +96,11 @@ func (ls *ListType) TypeName() string {
|
||||
return "list"
|
||||
}
|
||||
|
||||
func (dict *ListType) Contains(t *ListType) (answer bool) {
|
||||
if len(*dict) >= len(*t) {
|
||||
func (ls *ListType) Contains(t *ListType) (answer bool) {
|
||||
if len(*ls) >= len(*t) {
|
||||
answer = true
|
||||
for _, item := range *t {
|
||||
if answer = dict.IndexDeepSameCmp(item) >= 0; !answer {
|
||||
if answer = ls.IndexDeepSameCmp(item) >= 0; !answer {
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -116,10 +108,10 @@ func (dict *ListType) Contains(t *ListType) (answer bool) {
|
||||
return
|
||||
}
|
||||
|
||||
func (ls1 *ListType) Equals(ls2 ListType) (answer bool) {
|
||||
if ls2 != nil && len(*ls1) == len(ls2) {
|
||||
func (ls *ListType) Equals(ls2 ListType) (answer bool) {
|
||||
if ls2 != nil && len(*ls) == len(ls2) {
|
||||
answer = true
|
||||
for index, i1 := range *ls1 {
|
||||
for index, i1 := range *ls {
|
||||
// if !reflect.DeepEqual(i1, ls2[index]) {
|
||||
// answer = false
|
||||
// break
|
||||
@@ -142,11 +134,11 @@ func (ls1 *ListType) Clone() (ls2 *ListType) {
|
||||
return
|
||||
}
|
||||
|
||||
func (dict *ListType) IndexDeepSameCmp(target any) (index int) {
|
||||
func (ls *ListType) IndexDeepSameCmp(target any) (index int) {
|
||||
var eq bool
|
||||
var err error
|
||||
index = -1
|
||||
for i, item := range *dict {
|
||||
for i, item := range *ls {
|
||||
if eq, err = deepSame(item, target, SameContent); err != nil {
|
||||
break
|
||||
} else if eq {
|
||||
@@ -197,15 +189,15 @@ func deepSame(a, b any, deepCmp DeepFuncTemplate) (eq bool, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (dict *ListType) SetItem(index int64, value any) (err error) {
|
||||
if index >= 0 && index < int64(len(*dict)) {
|
||||
(*dict)[index] = value
|
||||
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(*dict)-1)
|
||||
err = fmt.Errorf("index %d out of bounds (0, %d)", index, len(*ls)-1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (dict *ListType) AppendItem(value any) {
|
||||
*dict = append(*dict, value)
|
||||
func (ls *ListType) AppendItem(value any) {
|
||||
*ls = append(*ls, value)
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ package expr
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"slices"
|
||||
|
||||
"git.portale-stac.it/go-pkg/expr/scan"
|
||||
@@ -164,16 +165,16 @@ func paramAlreadyDefined(args []*scan.Term, param *scan.Term) (position int) {
|
||||
return
|
||||
}
|
||||
|
||||
func (parser *parser) parseList(scanner *scan.Scanner, ctx parserContext) (listTerm *scan.Term, err error) {
|
||||
func (parser *parser) parseList(scanner *scan.Scanner, ctx parserContext, termSym scan.Symbol) (listTerm *scan.Term, err error) {
|
||||
r, c := scanner.LastPos()
|
||||
args := make([]*scan.Term, 0)
|
||||
lastSym := scan.SymUnknown
|
||||
itemExpected := false
|
||||
itemCtx := remFlags(ctx, allowIndex)
|
||||
for lastSym != scan.SymClosedSquare && lastSym != scan.SymEos {
|
||||
for lastSym != termSym && lastSym != scan.SymEos {
|
||||
zeroRequired := scanner.Current().Sym == scan.SymColon
|
||||
var itemTree *scan.Ast
|
||||
if itemTree, err = parser.parseItem(scanner, itemCtx, scan.SymComma, scan.SymClosedSquare); err == nil {
|
||||
if itemTree, err = parser.parseItem(scanner, itemCtx, scan.SymComma, termSym); err == nil {
|
||||
root := itemTree.Root()
|
||||
if root != nil {
|
||||
if hasFlag(ctx, allowIndex) && root.Symbol() == scan.SymColon {
|
||||
@@ -212,10 +213,14 @@ func (parser *parser) parseList(scanner *scan.Scanner, ctx parserContext) (listT
|
||||
}
|
||||
}
|
||||
if err == nil {
|
||||
if lastSym != scan.SymClosedSquare {
|
||||
if lastSym != termSym {
|
||||
err = scanner.Previous().ErrorExpectedGot("]")
|
||||
} else {
|
||||
} else if termSym == scan.SymClosedSquare {
|
||||
listTerm = newListTerm(r, c, args)
|
||||
} else if termSym == scan.SymGreaterClosedSquare {
|
||||
listTerm = newLinkedListTerm(r, c, args)
|
||||
} else {
|
||||
err = fmt.Errorf("[%d:%d] unknown list type", r, c)
|
||||
}
|
||||
}
|
||||
return
|
||||
@@ -327,7 +332,7 @@ func (parser *parser) parseSelectorCase(scanner *scan.Scanner, ctx parserContext
|
||||
err = tk.Errorf("case list in default clause")
|
||||
return
|
||||
}
|
||||
if filterList, err = parser.parseList(scanner, remFlags(ctx, allowIndex)); err != nil {
|
||||
if filterList, err = parser.parseList(scanner, remFlags(ctx, allowIndex), scan.SymClosedSquare); err != nil {
|
||||
return
|
||||
}
|
||||
tk = parser.Next(scanner)
|
||||
@@ -446,7 +451,7 @@ func (parser *parser) parseGeneral(scanner *scan.Scanner, ctx parserContext, ter
|
||||
tree = scan.NewAst()
|
||||
firstToken := true
|
||||
// lastSym := SymUnknown
|
||||
for tk = parser.Next(scanner); err == nil && tk != nil && !tk.IsTerm(termSymbols); /*&& !areSymbolsOutOfCtx(tk, selectorTerm, SymColon, SymDoubleColon)*/ tk = parser.Next(scanner) {
|
||||
for tk = parser.Next(scanner); err == nil && tk != nil && !tk.IsTerm(termSymbols); tk = parser.Next(scanner) {
|
||||
// if tk.Sym == SymComment {
|
||||
// continue
|
||||
// }
|
||||
@@ -491,7 +496,13 @@ func (parser *parser) parseGeneral(scanner *scan.Scanner, ctx parserContext, ter
|
||||
case scan.SymOpenSquare:
|
||||
var listTerm *scan.Term
|
||||
newCtx := addFlagsCond(addFlags(ctx, squareContext), allowIndex, couldBeACollection(currentTerm))
|
||||
if listTerm, err = parser.parseList(scanner, newCtx); err == nil {
|
||||
if listTerm, err = parser.parseList(scanner, newCtx, scan.SymClosedSquare); err == nil {
|
||||
currentTerm, err = listSubTree(tree, listTerm, hasFlag(newCtx, allowIndex))
|
||||
}
|
||||
case scan.SymOpenSquareLess:
|
||||
var listTerm *scan.Term
|
||||
newCtx := addFlagsCond(addFlags(ctx, listContext), allowIndex, false)
|
||||
if listTerm, err = parser.parseList(scanner, newCtx, scan.SymGreaterClosedSquare); err == nil {
|
||||
currentTerm, err = listSubTree(tree, listTerm, hasFlag(newCtx, allowIndex))
|
||||
}
|
||||
case scan.SymOpenBrace:
|
||||
|
||||
@@ -315,6 +315,8 @@ func (scanner *Scanner) fetchNextToken() (tk *Token) {
|
||||
} else {
|
||||
tk = scanner.accept(SymDoubleGreater, ch, next)
|
||||
}
|
||||
} else if next == ']' {
|
||||
tk = scanner.moveOn(SymGreaterClosedSquare, ch, next)
|
||||
} else {
|
||||
tk = scanner.MakeToken(SymGreater, ch)
|
||||
}
|
||||
@@ -344,7 +346,11 @@ func (scanner *Scanner) fetchNextToken() (tk *Token) {
|
||||
case ')':
|
||||
tk = scanner.MakeToken(SymClosedRound, ch)
|
||||
case '[':
|
||||
if next, _ := scanner.peek(); next == '<' {
|
||||
tk = scanner.moveOn(SymOpenSquareLess, ch, next)
|
||||
} else {
|
||||
tk = scanner.MakeToken(SymOpenSquare, ch)
|
||||
}
|
||||
case ']':
|
||||
tk = scanner.MakeToken(SymClosedSquare, ch)
|
||||
case '{':
|
||||
|
||||
@@ -106,6 +106,8 @@ func init() {
|
||||
SymLessPlus: {"<+", SymClassOperator, PosInfix}, // 70: '<+'
|
||||
SymPreInc: {"++", SymClassOperator, PosPrefix}, // 71: '++'
|
||||
SymPreDec: {"--", SymClassOperator, PosPrefix}, // 72: '--'
|
||||
SymOpenSquareLess: {"[<", SymClassOperator, PosPrefix}, // 97: '[<'
|
||||
SymGreaterClosedSquare: {">]", SymClassOperator, PosPostfix}, // 98: '>]'
|
||||
// SymChangeSign
|
||||
// SymUnchangeSign
|
||||
// SymIdentifier
|
||||
@@ -123,6 +125,7 @@ func init() {
|
||||
// SymFuncCall
|
||||
// SymFuncDef
|
||||
// SymList
|
||||
// SymLinkedList
|
||||
// SymDict
|
||||
// SymIndex
|
||||
// SymExpression
|
||||
|
||||
@@ -105,6 +105,9 @@ const (
|
||||
SymExpression // 94: expression
|
||||
SymSelector // 95: selector <selector> ::= <expr> "?" <selector-case> {":" <selector-case>} ["::" <default-selector-case>]
|
||||
SymSelectorCase // 96: <selector-case> ::= [<list>] "{" <multi-expr> "}"
|
||||
SymOpenSquareLess // 97: '[<'
|
||||
SymGreaterClosedSquare // 98: '>]'
|
||||
SymLinkedList // 99: linked-list
|
||||
// SymOpenComment // 0: '/*'
|
||||
// SymClosedComment // 0: '*/'
|
||||
// SymOneLineComment // 0: '//'
|
||||
|
||||
@@ -118,8 +118,8 @@ func TestFuncBaseOthers(t *testing.T) {
|
||||
inputs := []inputType{
|
||||
/* 1 */ {`set("a", 3); a`, int64(3), nil},
|
||||
/* 2 */ {`set(true, 3)`, nil, `set(): the "name" parameter must be a string, got a bool (true)`},
|
||||
// /* 3 */ {`a=3; unset("a"); a`, nil, `undefined variable or function "a"`},
|
||||
// /* 4 */ {`unset("a")`, nil, `undefined variable or function "a"`},
|
||||
/* 3 */ {`seq(1,2,3)`, kern.NewLinkedListA(int64(1), int64(2), int64(3)), nil},
|
||||
// /* 4 */ {`seq(1,2,4)`, kern.NewLinkedListA(int64(1), int64(2), int64(3)), nil},
|
||||
}
|
||||
|
||||
// runTestSuiteSpec(t, section, inputs, 4)
|
||||
|
||||
+15
-1
@@ -57,10 +57,24 @@ func TestListParser(t *testing.T) {
|
||||
/* 41 */ {`[0] << $([1,2,3,4])`, kern.NewListA(int64(0), int64(1), int64(2), int64(3), int64(4)), nil},
|
||||
/* 42 */ {`L=[]; [1] >> L; L`, kern.NewListA(), nil},
|
||||
/* 43 */ {`L=[]; L << [1]; L`, kern.NewListA(), nil},
|
||||
// /* 44 */ {`[0,1,2,3,4][2:3]`, kern.NewListA(int64(20)), nil},
|
||||
}
|
||||
|
||||
// t.Setenv("EXPR_PATH", ".")
|
||||
|
||||
// runTestSuiteSpec(t, section, inputs, 42)
|
||||
// runTestSuiteSpec(t, section, inputs, 44)
|
||||
runTestSuite(t, section, inputs)
|
||||
}
|
||||
|
||||
func TestLinkedListParser(t *testing.T) {
|
||||
section := "Linked-List"
|
||||
|
||||
inputs := []inputType{
|
||||
/* 1 */ {`[<1,2>]`, kern.NewLinkedListA(int64(1), int64(2)), nil},
|
||||
}
|
||||
|
||||
// t.Setenv("EXPR_PATH", ".")
|
||||
|
||||
// runTestSuiteSpec(t, section, inputs, 44)
|
||||
runTestSuite(t, section, inputs)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user