Added lists and hashes
This commit is contained in:
parent
acdd8b3f72
commit
ea31e568b6
|
@ -11,16 +11,35 @@ type astLiteral struct {
|
||||||
Str *string `parser:"@String"`
|
Str *string `parser:"@String"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type astHashKey struct {
|
||||||
|
Literal *astLiteral `parser:"@@"`
|
||||||
|
Ident *string `parser:"| @Ident"`
|
||||||
|
Var *string `parser:"| DOLLAR @Ident"`
|
||||||
|
Sub *astPipeline `parser:"| LP @@ RP"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type astElementPair struct {
|
||||||
|
Left astCmdArg `parser:"@@"`
|
||||||
|
Right *astCmdArg `parser:"( COLON @@ )? NL?"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type astListOrHash struct {
|
||||||
|
EmptyList bool `parser:"@(LS RS)"`
|
||||||
|
EmptyHash bool `parser:"| @(LS COLON RS)"`
|
||||||
|
Elements []*astElementPair `parser:"| LS NL? @@+ @@* RS"`
|
||||||
|
}
|
||||||
|
|
||||||
type astBlock struct {
|
type astBlock struct {
|
||||||
Statements []*astStatements `parser:"LC NL? @@ NL? RC"`
|
Statements []*astStatements `parser:"LC NL? @@ NL? RC"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type astCmdArg struct {
|
type astCmdArg struct {
|
||||||
Literal *astLiteral `parser:"@@"`
|
Literal *astLiteral `parser:"@@"`
|
||||||
Ident *string `parser:"| @Ident"`
|
Ident *string `parser:"| @Ident"`
|
||||||
Var *string `parser:"| DOLLAR @Ident"`
|
Var *string `parser:"| DOLLAR @Ident"`
|
||||||
Sub *astPipeline `parser:"| LP @@ RP"`
|
Sub *astPipeline `parser:"| LP @@ RP"`
|
||||||
Block *astBlock `parser:"| @@"`
|
ListOrHash *astListOrHash `parser:"| @@"`
|
||||||
|
Block *astBlock `parser:"| @@"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type astCmd struct {
|
type astCmd struct {
|
||||||
|
@ -47,8 +66,11 @@ var scanner = lexer.MustStateful(lexer.Rules{
|
||||||
{"Whitespace", `[ \t]+`, nil},
|
{"Whitespace", `[ \t]+`, nil},
|
||||||
{"String", `"(\\"|[^"])*"`, nil},
|
{"String", `"(\\"|[^"])*"`, nil},
|
||||||
{"DOLLAR", `\$`, nil},
|
{"DOLLAR", `\$`, nil},
|
||||||
|
{"COLON", `\:`, nil},
|
||||||
{"LP", `\(`, nil},
|
{"LP", `\(`, nil},
|
||||||
{"RP", `\)`, nil},
|
{"RP", `\)`, nil},
|
||||||
|
{"LS", `\[`, nil},
|
||||||
|
{"RS", `\]`, nil},
|
||||||
{"LC", `\{`, nil},
|
{"LC", `\{`, nil},
|
||||||
{"RC", `\}`, nil},
|
{"RC", `\}`, nil},
|
||||||
{"NL", `[;\n][; \n\t]*`, nil},
|
{"NL", `[;\n][; \n\t]*`, nil},
|
||||||
|
|
|
@ -130,12 +130,57 @@ func (e evaluator) evalArg(ctx context.Context, ec *evalCtx, n astCmdArg) (objec
|
||||||
return nil, nil
|
return nil, nil
|
||||||
case n.Sub != nil:
|
case n.Sub != nil:
|
||||||
return e.evalSub(ctx, ec, n.Sub)
|
return e.evalSub(ctx, ec, n.Sub)
|
||||||
|
case n.ListOrHash != nil:
|
||||||
|
return e.evalListOrHash(ctx, ec, n.ListOrHash)
|
||||||
case n.Block != nil:
|
case n.Block != nil:
|
||||||
return blockObject{block: n.Block}, nil
|
return blockObject{block: n.Block}, nil
|
||||||
}
|
}
|
||||||
return nil, errors.New("unhandled arg type")
|
return nil, errors.New("unhandled arg type")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e evaluator) evalListOrHash(ctx context.Context, ec *evalCtx, loh *astListOrHash) (object, error) {
|
||||||
|
if loh.EmptyList {
|
||||||
|
return listObject{}, nil
|
||||||
|
} else if loh.EmptyHash {
|
||||||
|
return hashObject{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if firstIsHash := loh.Elements[0].Right != nil; firstIsHash {
|
||||||
|
h := hashObject{}
|
||||||
|
for _, el := range loh.Elements {
|
||||||
|
if el.Right == nil {
|
||||||
|
return nil, errors.New("miss-match of lists and hash")
|
||||||
|
}
|
||||||
|
|
||||||
|
n, err := e.evalArg(ctx, ec, el.Left)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
v, err := e.evalArg(ctx, ec, *el.Right)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
h[n.String()] = v
|
||||||
|
}
|
||||||
|
return h, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
l := listObject{}
|
||||||
|
for _, el := range loh.Elements {
|
||||||
|
if el.Right != nil {
|
||||||
|
return nil, errors.New("miss-match of lists and hash")
|
||||||
|
}
|
||||||
|
v, err := e.evalArg(ctx, ec, el.Left)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
l = append(l, v)
|
||||||
|
}
|
||||||
|
return l, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (e evaluator) evalLiteral(ctx context.Context, ec *evalCtx, n *astLiteral) (object, error) {
|
func (e evaluator) evalLiteral(ctx context.Context, ec *evalCtx, n *astLiteral) (object, error) {
|
||||||
switch {
|
switch {
|
||||||
case n.Str != nil:
|
case n.Str != nil:
|
||||||
|
|
|
@ -13,7 +13,7 @@ func TestInst_Eval(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
desc string
|
desc string
|
||||||
expr string
|
expr string
|
||||||
want string
|
want any
|
||||||
}{
|
}{
|
||||||
{desc: "simple string", expr: `firstarg "hello"`, want: "hello"},
|
{desc: "simple string", expr: `firstarg "hello"`, want: "hello"},
|
||||||
{desc: "simple ident", expr: `firstarg a-test`, want: "a-test"},
|
{desc: "simple ident", expr: `firstarg a-test`, want: "a-test"},
|
||||||
|
@ -39,6 +39,23 @@ func TestInst_Eval(t *testing.T) {
|
||||||
{desc: "multi 1", expr: `firstarg "hello" ; firstarg "world"`, want: "world"},
|
{desc: "multi 1", expr: `firstarg "hello" ; firstarg "world"`, want: "world"},
|
||||||
{desc: "multi 2", expr: `pipe "hello" | toUpper ; firstarg "world"`, want: "world"}, // TODO: assert for leaks
|
{desc: "multi 2", expr: `pipe "hello" | toUpper ; firstarg "world"`, want: "world"}, // TODO: assert for leaks
|
||||||
{desc: "multi 3", expr: `set new "this is new" ; firstarg $new`, want: "this is new"},
|
{desc: "multi 3", expr: `set new "this is new" ; firstarg $new`, want: "this is new"},
|
||||||
|
|
||||||
|
// Lists
|
||||||
|
{desc: "list 1", expr: `firstarg ["1" "2" "3"]`, want: []any{"1", "2", "3"}},
|
||||||
|
{desc: "list 2", expr: `set one "one" ; firstarg [$one (pipe "two" | toUpper) "three"]`, want: []any{"one", "TWO", "three"}},
|
||||||
|
{desc: "list 3", expr: `firstarg []`, want: []any{}},
|
||||||
|
|
||||||
|
// Maps
|
||||||
|
{desc: "map 1", expr: `firstarg [one:"1" two:"2" three:"3"]`, want: map[string]any{"one": "1", "two": "2", "three": "3"}},
|
||||||
|
{desc: "map 2", expr: `firstarg ["one":"1" "two":"2" "three":"3"]`, want: map[string]any{"one": "1", "two": "2", "three": "3"}},
|
||||||
|
{desc: "map 3", expr: `
|
||||||
|
set one "one" ; set n1 "1"
|
||||||
|
firstarg [
|
||||||
|
$one:$n1
|
||||||
|
(firstarg "two" | toUpper):(firstarg "2" | toUpper)
|
||||||
|
three:"3"
|
||||||
|
]`, want: map[string]any{"one": "1", "TWO": "2", "three": "3"}},
|
||||||
|
{desc: "map 4", expr: `firstarg [:]`, want: map[string]any{}},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
|
|
|
@ -12,6 +12,26 @@ type object interface {
|
||||||
Truthy() bool
|
Truthy() bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type listObject []object
|
||||||
|
|
||||||
|
func (s listObject) String() string {
|
||||||
|
return fmt.Sprintf("%v", []object(s))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s listObject) Truthy() bool {
|
||||||
|
return len(s) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type hashObject map[string]object
|
||||||
|
|
||||||
|
func (s hashObject) String() string {
|
||||||
|
return fmt.Sprintf("%v", map[string]object(s))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s hashObject) Truthy() bool {
|
||||||
|
return len(s) > 0
|
||||||
|
}
|
||||||
|
|
||||||
type strObject string
|
type strObject string
|
||||||
|
|
||||||
func (s strObject) String() string {
|
func (s strObject) String() string {
|
||||||
|
@ -28,6 +48,26 @@ func toGoValue(obj object) (interface{}, bool) {
|
||||||
return nil, true
|
return nil, true
|
||||||
case strObject:
|
case strObject:
|
||||||
return string(v), true
|
return string(v), true
|
||||||
|
case listObject:
|
||||||
|
xs := make([]interface{}, 0, len(v))
|
||||||
|
for _, va := range v {
|
||||||
|
x, ok := toGoValue(va)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
xs = append(xs, x)
|
||||||
|
}
|
||||||
|
return xs, true
|
||||||
|
case hashObject:
|
||||||
|
xs := make(map[string]interface{})
|
||||||
|
for k, va := range v {
|
||||||
|
x, ok := toGoValue(va)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
xs[k] = x
|
||||||
|
}
|
||||||
|
return xs, true
|
||||||
case proxyObject:
|
case proxyObject:
|
||||||
return v.p, true
|
return v.p, true
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue