From f24ae21af111e0ce34f62a37ddb4a9fc18166ae6 Mon Sep 17 00:00:00 2001 From: Ophestra Date: Sun, 17 May 2026 23:44:00 +0900 Subject: [PATCH] internal/rosa/azalea: package special case This is more efficient for the inputs array and packages in general. Signed-off-by: Ophestra --- internal/rosa/azalea/evaluate.go | 95 +++++++++++++------ internal/rosa/azalea/evaluate_test.go | 128 ++++++++++++-------------- 2 files changed, 130 insertions(+), 93 deletions(-) diff --git a/internal/rosa/azalea/evaluate.go b/internal/rosa/azalea/evaluate.go index 431d6944..daf38849 100644 --- a/internal/rosa/azalea/evaluate.go +++ b/internal/rosa/azalea/evaluate.go @@ -5,6 +5,7 @@ import ( "fmt" "maps" "reflect" + "slices" "unique" ) @@ -23,9 +24,12 @@ type ( // FArgs are arguments passed to [F]. FArgs []FArg + // PF implements the package declaration function. + PF func(name Ident, args FArgs) (v any, set bool, err error) + // F is the implementation of a [Func]. F struct { - F func(isPackage bool, args FArgs) (v any, set bool, err error) + F func(args FArgs) (v any, set bool, err error) V map[unique.Handle[Ident]]any } ) @@ -77,8 +81,8 @@ func (e UndefinedError) Error() string { } // evaluate is evaluateAny with a type parameter. -func evaluate[T Value](s []Frame, expr any, rp *T) bool { - return evaluateAny(s, expr, rp) +func evaluate[T Value](d PF, s []Frame, expr any, rp *T) bool { + return evaluateAny(d, s, expr, rp) } // TypeError is an unexpected type during evaluation. @@ -132,8 +136,18 @@ func (e EvaluationError) Error() string { return fmt.Sprintf("expression %#v: %v", e.Expr, e.Err) } +var ( + // IdentInputs is a special array argument in a package declaration whose + // values of [Ident] are kept as is when passed to a [PF]. + IdentInputs = unique.Make(Ident("inputs")) + + // ErrInvalidInputs is panicked for an [IdentInputs] argument to [PF] + // sharing its value or set for R. + ErrInvalidInputs = errors.New("inputs must not be common or bound to scope") +) + // evaluateAny implements [Evaluate]. -func evaluateAny(s []Frame, expr, rp any) bool { +func evaluateAny(d PF, s []Frame, expr, rp any) bool { defer func() { r := recover() if r == nil { @@ -193,17 +207,17 @@ func evaluateAny(s []Frame, expr, rp any) bool { store(rp, false) return true default: - return evaluateAny(s, v, rp) + return evaluateAny(d, s, v, rp) } default: - return evaluateAny(s, e[0], rp) + return evaluateAny(d, s, e[0], rp) } } var v string for i := range e { var _r string - if evaluate(s, e[i], &_r) { + if evaluate(d, s, e[i], &_r) { v += _r } } @@ -214,7 +228,7 @@ func evaluateAny(s []Frame, expr, rp any) bool { r := make([]string, 0, len(e)) for i := range e { var _r string - if evaluate(s, e[i], &_r) { + if evaluate(d, s, e[i], &_r) { r = append(r, _r) } } @@ -225,7 +239,7 @@ func evaluateAny(s []Frame, expr, rp any) bool { r := make([][2]string, 0, len(e)) for i := range e { var _r string - if e[i].V == nil || evaluate(s, e[i].V, &_r) { + if e[i].V == nil || evaluate(d, s, e[i].V, &_r) { r = append(r, [2]string{string(e[i].K), _r}) } } @@ -237,14 +251,16 @@ func evaluateAny(s []Frame, expr, rp any) bool { f F ok bool ) - for i := range s { - f, ok = s[len(s)-1-i].Func[unique.Make(e.Ident)] - if ok { - break + if !e.Package { + for i := range s { + f, ok = s[len(s)-1-i].Func[unique.Make(e.Ident)] + if ok { + break + } + } + if !ok { + panic(UndefinedError(e.Ident)) } - } - if !ok { - panic(UndefinedError(e.Ident)) } argc := len(e.Args) @@ -252,29 +268,56 @@ func evaluateAny(s []Frame, expr, rp any) bool { argc += len(arg.K) - 1 } fargs := make([]FArg, 0, len(e.Args)) - s = append(s, Frame{Val: maps.Clone(f.V)}) + s = append(s, Frame{}) fp := &s[len(s)-1] + if !e.Package { + fp.Val = maps.Clone(f.V) + } for _, arg := range e.Args { + names := make([]unique.Handle[Ident], len(arg.K)) + for i, name := range arg.K { + names[i] = unique.Make(name) + } + farg := FArg{R: arg.R} - if !evaluateAny(s, arg.V, &farg.V) { + if e.Package && slices.Contains(names, IdentInputs) { + if len(names) != 1 || len(arg.V) != 1 || arg.R { + panic(ErrInvalidInputs) + } + farg.K = names[0] + if err := storeE(&farg.V, arg.V[0]); err != nil { + panic(err) + } + fargs = append(fargs, farg) + continue + } + + if !evaluateAny(d, s, arg.V, &farg.V) { farg.V = nil } - for _, name := range arg.K { - h := unique.Make(name) - farg.K = h + for _, name := range names { + farg.K = name fargs = append(fargs, farg) if arg.R && farg.V != nil { if fp.Val == nil { fp.Val = make(map[unique.Handle[Ident]]any) } - (*fp).Val[h] = farg.V + (*fp).Val[name] = farg.V } } } - v, set, err := f.F(e.Package, fargs) + var ( + v any + err error + ) + if !e.Package { + v, ok, err = f.F(fargs) + } else { + v, ok, err = d(e.Ident, fargs) + } if err != nil { panic(err) } else if v != nil { @@ -282,7 +325,7 @@ func evaluateAny(s []Frame, expr, rp any) bool { panic(err) } } - return set + return ok default: panic(UnsupportedExprError{expr}) @@ -290,7 +333,7 @@ func evaluateAny(s []Frame, expr, rp any) bool { } // Evaluate evaluates a statement and returns its value. -func Evaluate[T Value](s []Frame, expr any) (v T, set bool, err error) { +func Evaluate[T Value](d PF, s []Frame, expr any) (v T, set bool, err error) { defer func() { r := recover() if r == nil { @@ -303,6 +346,6 @@ func Evaluate[T Value](s []Frame, expr any) (v T, set bool, err error) { } err = _err }() - set = evaluate[T](s, expr, &v) + set = evaluate[T](d, s, expr, &v) return } diff --git a/internal/rosa/azalea/evaluate_test.go b/internal/rosa/azalea/evaluate_test.go index 9036e782..aa7de27e 100644 --- a/internal/rosa/azalea/evaluate_test.go +++ b/internal/rosa/azalea/evaluate_test.go @@ -16,14 +16,10 @@ import ( func makeStackCheck(check func(args FArgs) (any, error)) []Frame { return []Frame{{Func: map[unique.Handle[Ident]]F{ unique.Make(Ident("f")): {F: func( - isPackage bool, args FArgs, ) (v any, set bool, err error) { set = true v, err = check(args) - if isPackage { - err = errors.New("unexpected package") - } return }}, }}} @@ -101,6 +97,45 @@ func TestEvaluate(t *testing.T) { Expr: Ident("nil"), Err: UndefinedError("nil"), }}, + + {"common inputs", `package name { inputs, v = []; }`, nil, "", EvaluationError{ + Expr: Func{ + Ident: Ident("name"), + Package: true, + + Args: []Arg{ + {K: []Ident{ + "inputs", + "v", + }, V: Val{Array(nil)}}, + }, + }, + Err: ErrInvalidInputs, + }}, + + {"bound inputs", `package name { inputs* = []; }`, nil, "", EvaluationError{ + Expr: Func{ + Ident: Ident("name"), + Package: true, + + Args: []Arg{ + {K: []Ident{"inputs"}, V: Val{Array(nil)}, R: true}, + }, + }, + Err: ErrInvalidInputs, + }}, + + {"concat inputs", `package name { inputs = ""+""; }`, nil, "", EvaluationError{ + Expr: Func{ + Ident: Ident("name"), + Package: true, + + Args: []Arg{ + {K: []Ident{"inputs"}, V: Val{String(""), String("")}}, + }, + }, + Err: ErrInvalidInputs, + }}, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { @@ -114,7 +149,7 @@ func TestEvaluate(t *testing.T) { } else { expr = e[0].(Func) } - r, set, err := Evaluate[string](tc.s, expr) + r, set, err := Evaluate[string](nil, tc.s, expr) if set != (err == nil) { t.Error("Evaluate: unexpected unset") } @@ -147,36 +182,19 @@ func TestEvaluateGCC(t *testing.T) { } var got [3]FArgs - if r, set, err := Evaluate[string]([]Frame{{ + if r, set, err := Evaluate[string](func( + name Ident, + args FArgs, + ) (v any, set bool, err error) { + v = "\x00" + set = true + got[0] = args + return + }, []Frame{{ Func: map[unique.Handle[Ident]]F{ - unique.Make(Ident("gcc")): {F: func( - isPackage bool, - args FArgs, - ) (v any, set bool, err error) { - v = "\x00" - if !isPackage { - err = errors.New("not a package") - } - set = true - got[0] = args - return - }, V: map[unique.Handle[Ident]]any{ - unique.Make(Ident("binutils")): "binutils", - unique.Make(Ident("mpc")): "mpc", - unique.Make(Ident("zlib")): "zlib", - unique.Make(Ident("libucontext")): "libucontext", - unique.Make(Ident("kernel-headers")): "kernel-headers", - }}, - unique.Make(Ident("remoteTar")): {F: func( - isPackage bool, args FArgs, ) (v any, set bool, err error) { - if isPackage { - err = errors.New("unexpected package") - return - } - var url, checksum string var compress int if err = args.Apply(map[unique.Handle[Ident]]any{ @@ -198,37 +216,25 @@ func TestEvaluateGCC(t *testing.T) { }}, unique.Make(Ident("make")): {F: func( - isPackage bool, args FArgs, ) (v any, set bool, err error) { v = args - if isPackage { - err = errors.New("unexpected package") - } set = true return }}, unique.Make(Ident("arch")): {F: func( - isPackage bool, args FArgs, ) (v any, set bool, err error) { set = false - if isPackage { - err = errors.New("unexpected package") - } got[1] = args return }}, unique.Make(Ident("noop")): {F: func( - isPackage bool, args FArgs, ) (v any, set bool, err error) { set = false - if isPackage { - err = errors.New("unexpected package") - } set = true got[2] = args return @@ -274,12 +280,12 @@ func TestEvaluateGCC(t *testing.T) { {K: unique.Make(Ident("skip-check")), V: true}, }}, - {K: unique.Make(Ident("inputs")), V: []string{ - "binutils", - "mpc", - "zlib", - "libucontext", - "kernel-headers", + {K: unique.Make(Ident("inputs")), V: Array{ + {Ident("binutils")}, + {Ident("mpc")}, + {Ident("zlib")}, + {Ident("libucontext")}, + {Ident("kernel-headers")}, }}, }, { @@ -304,21 +310,7 @@ func BenchmarkEvaluate(b *testing.B) { s := []Frame{{ Func: map[unique.Handle[Ident]]F{ - unique.Make(Ident("gcc")): {F: func( - bool, - FArgs, - ) (v any, set bool, err error) { - return - }, V: map[unique.Handle[Ident]]any{ - unique.Make(Ident("binutils")): "binutils", - unique.Make(Ident("mpc")): "mpc", - unique.Make(Ident("zlib")): "zlib", - unique.Make(Ident("libucontext")): "libucontext", - unique.Make(Ident("kernel-headers")): "kernel-headers", - }}, - unique.Make(Ident("remoteTar")): {F: func( - bool, FArgs, ) (v any, set bool, err error) { return @@ -327,21 +319,18 @@ func BenchmarkEvaluate(b *testing.B) { }}, unique.Make(Ident("make")): {F: func( - bool, FArgs, ) (v any, set bool, err error) { return }}, unique.Make(Ident("arch")): {F: func( - bool, FArgs, ) (v any, set bool, err error) { return }}, unique.Make(Ident("noop")): {F: func( - bool, FArgs, ) (v any, set bool, err error) { return @@ -351,7 +340,12 @@ func BenchmarkEvaluate(b *testing.B) { }, }} for b.Loop() { - if _, _, err := Evaluate[string](s, gcc); err != nil { + if _, _, err := Evaluate[string](func( + Ident, + FArgs, + ) (v any, set bool, err error) { + return + }, s, gcc); err != nil { b.Fatal(err) } }