Added lists and hashes
This commit is contained in:
parent
acdd8b3f72
commit
ea31e568b6
|
@ -11,16 +11,35 @@ type astLiteral struct {
|
|||
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 {
|
||||
Statements []*astStatements `parser:"LC NL? @@ NL? RC"`
|
||||
}
|
||||
|
||||
type astCmdArg struct {
|
||||
Literal *astLiteral `parser:"@@"`
|
||||
Ident *string `parser:"| @Ident"`
|
||||
Var *string `parser:"| DOLLAR @Ident"`
|
||||
Sub *astPipeline `parser:"| LP @@ RP"`
|
||||
Block *astBlock `parser:"| @@"`
|
||||
Literal *astLiteral `parser:"@@"`
|
||||
Ident *string `parser:"| @Ident"`
|
||||
Var *string `parser:"| DOLLAR @Ident"`
|
||||
Sub *astPipeline `parser:"| LP @@ RP"`
|
||||
ListOrHash *astListOrHash `parser:"| @@"`
|
||||
Block *astBlock `parser:"| @@"`
|
||||
}
|
||||
|
||||
type astCmd struct {
|
||||
|
@ -47,8 +66,11 @@ var scanner = lexer.MustStateful(lexer.Rules{
|
|||
{"Whitespace", `[ \t]+`, nil},
|
||||
{"String", `"(\\"|[^"])*"`, nil},
|
||||
{"DOLLAR", `\$`, nil},
|
||||
{"COLON", `\:`, nil},
|
||||
{"LP", `\(`, nil},
|
||||
{"RP", `\)`, nil},
|
||||
{"LS", `\[`, nil},
|
||||
{"RS", `\]`, nil},
|
||||
{"LC", `\{`, nil},
|
||||
{"RC", `\}`, 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
|
||||
case n.Sub != nil:
|
||||
return e.evalSub(ctx, ec, n.Sub)
|
||||
case n.ListOrHash != nil:
|
||||
return e.evalListOrHash(ctx, ec, n.ListOrHash)
|
||||
case n.Block != nil:
|
||||
return blockObject{block: n.Block}, nil
|
||||
}
|
||||
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) {
|
||||
switch {
|
||||
case n.Str != nil:
|
||||
|
|
|
@ -13,7 +13,7 @@ func TestInst_Eval(t *testing.T) {
|
|||
tests := []struct {
|
||||
desc string
|
||||
expr string
|
||||
want string
|
||||
want any
|
||||
}{
|
||||
{desc: "simple string", expr: `firstarg "hello"`, want: "hello"},
|
||||
{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 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"},
|
||||
|
||||
// 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 {
|
||||
|
|
|
@ -12,6 +12,26 @@ type object interface {
|
|||
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
|
||||
|
||||
func (s strObject) String() string {
|
||||
|
@ -28,6 +48,26 @@ func toGoValue(obj object) (interface{}, bool) {
|
|||
return nil, true
|
||||
case strObject:
|
||||
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:
|
||||
return v.p, true
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue