Added support for assignments
All checks were successful
Test / build (push) Successful in 1m1s

This commit is contained in:
Leon Mika 2025-05-18 07:20:52 +10:00
parent 0f1ceba090
commit 51e35aa9a6
3 changed files with 66 additions and 14 deletions

View file

@ -103,9 +103,10 @@ type astDot struct {
}
type astCmd struct {
Pos lexer.Position
Name astDot `parser:"@@"`
Args []astDot `parser:"@@*"`
Pos lexer.Position
Name astDot `parser:"@@"`
Assign *astDot `parser:"( EQ @@"`
InvokeArgs []astDot `parser:" | @@+ )?"`
}
type astPipeline struct {
@ -141,6 +142,7 @@ var scanner = lexer.MustStateful(lexer.Rules{
{"RC", `\}`, nil},
{"NL", `[;\n][; \n\t]*`, nil},
{"PIPE", `\|`, nil},
{"EQ", `=`, nil},
{"Ident", `[-]*[a-zA-Z_][\w-!?]*`, nil},
},
"String": {

View file

@ -72,6 +72,14 @@ func (e evaluator) evalPipeline(ctx context.Context, ec *evalCtx, n *astPipeline
func (e evaluator) evalCmd(ctx context.Context, ec *evalCtx, currentPipe Object, ast *astCmd) (Object, error) {
switch {
case ast.Assign != nil:
// Assignment
assignVal, err := e.evalDot(ctx, ec, *ast.Assign)
if err != nil {
return nil, err
}
return e.assignDot(ctx, ec, ast.Assign, assignVal)
case (ast.Name.Arg.Ident != nil) && len(ast.Name.DotSuffix) == 0:
name := ast.Name.Arg.Ident.String()
@ -85,7 +93,7 @@ func (e evaluator) evalCmd(ctx context.Context, ec *evalCtx, currentPipe Object,
} else {
return nil, errors.New("unknown command: " + name)
}
case len(ast.Args) > 0:
case len(ast.InvokeArgs) > 0:
nameElem, err := e.evalDot(ctx, ec, ast.Name)
if err != nil {
return nil, err
@ -117,7 +125,7 @@ func (e evaluator) evalInvokable(ctx context.Context, ec *evalCtx, currentPipe O
if currentPipe != nil {
argsPtr.Append(currentPipe)
}
for _, arg := range ast.Args {
for _, arg := range ast.InvokeArgs {
if ident := arg.Arg.Ident; len(arg.DotSuffix) == 0 && ident != nil && ident.String()[0] == '-' {
// Arg switch
if kwargs == nil {
@ -176,6 +184,14 @@ func (e evaluator) evalDot(ctx context.Context, ec *evalCtx, n astDot) (Object,
return res, nil
}
func (e evaluator) assignDot(ctx context.Context, ec *evalCtx, n *astDot, toVal Object) (Object, error) {
if len(n.DotSuffix) == 0 {
return e.assignArg(ctx, ec, n.Arg, toVal)
}
panic("TODO")
}
func (e evaluator) evalArg(ctx context.Context, ec *evalCtx, n astCmdArg) (Object, error) {
switch {
case n.Literal != nil:
@ -211,6 +227,40 @@ func (e evaluator) evalArg(ctx context.Context, ec *evalCtx, n astCmdArg) (Objec
return nil, errors.New("unhandled arg type")
}
func (e evaluator) assignArg(ctx context.Context, ec *evalCtx, n astCmdArg, toVal Object) (Object, error) {
switch {
case n.Literal != nil:
// We may use this for variable setting?
return nil, errors.New("cannot assign to a literal")
case n.Var != nil:
ec.setOrDefineVar(*n.Var, toVal)
return toVal, nil
case n.PseudoVar != nil:
pvar, ok := ec.getPseudoVar(*n.PseudoVar)
if ok {
if err := pvar.set(ctx, *n.PseudoVar, toVal); err != nil {
return nil, err
}
return toVal, nil
}
if pvar := e.inst.missingPseudoVarHandler; pvar != nil {
if err := pvar.set(ctx, *n.PseudoVar, toVal); err != nil {
return nil, err
}
return toVal, nil
}
return nil, errors.New("unknown pseudo-variable: " + *n.Var)
case n.MaybeSub != nil:
return nil, errors.New("cannot assign to a subexpression")
case n.ListOrHash != nil:
return nil, errors.New("cannot assign to a list or hash")
case n.Block != nil:
return nil, errors.New("cannot assign to a block")
}
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

View file

@ -279,7 +279,7 @@ type macroArgs struct {
}
func (ma macroArgs) nargs() int {
return len(ma.ast.Args[ma.argShift:])
return len(ma.ast.InvokeArgs[ma.argShift:])
}
func (ma *macroArgs) shift(n int) {
@ -287,15 +287,15 @@ func (ma *macroArgs) shift(n int) {
}
func (ma macroArgs) identIs(ctx context.Context, n int, expectedIdent string) bool {
if n >= len(ma.ast.Args[ma.argShift:]) {
if n >= len(ma.ast.InvokeArgs[ma.argShift:]) {
return false
}
if len(ma.ast.Args[ma.argShift+n].DotSuffix) != 0 {
if len(ma.ast.InvokeArgs[ma.argShift+n].DotSuffix) != 0 {
return false
}
lit := ma.ast.Args[ma.argShift+n].Arg.Ident
lit := ma.ast.InvokeArgs[ma.argShift+n].Arg.Ident
if lit == nil {
return false
}
@ -304,15 +304,15 @@ func (ma macroArgs) identIs(ctx context.Context, n int, expectedIdent string) bo
}
func (ma *macroArgs) shiftIdent(ctx context.Context) (string, bool) {
if ma.argShift >= len(ma.ast.Args) {
if ma.argShift >= len(ma.ast.InvokeArgs) {
return "", false
}
if len(ma.ast.Args[ma.argShift].DotSuffix) != 0 {
if len(ma.ast.InvokeArgs[ma.argShift].DotSuffix) != 0 {
return "", false
}
lit := ma.ast.Args[ma.argShift].Arg.Ident
lit := ma.ast.InvokeArgs[ma.argShift].Arg.Ident
if lit != nil {
ma.argShift += 1
return lit.String(), true
@ -321,11 +321,11 @@ func (ma *macroArgs) shiftIdent(ctx context.Context) (string, bool) {
}
func (ma macroArgs) evalArg(ctx context.Context, n int) (Object, error) {
if n >= len(ma.ast.Args[ma.argShift:]) {
if n >= len(ma.ast.InvokeArgs[ma.argShift:]) {
return nil, errors.New("not enough arguments") // FIX
}
return ma.eval.evalDot(ctx, ma.ec, ma.ast.Args[ma.argShift+n])
return ma.eval.evalDot(ctx, ma.ec, ma.ast.InvokeArgs[ma.argShift+n])
}
func (ma macroArgs) evalBlock(ctx context.Context, n int, args []Object, pushScope bool) (Object, error) {