diff options
| author | Ophestra <cat@gensokyo.uk> | 2026-10-30 23:37:34 +0900 |
|---|---|---|
| committer | Ophestra <cat@gensokyo.uk> | 2026-10-30 23:38:34 +0900 |
| commit | 3c0505619920d8656b332ef0a23295b0d13f2253 (patch) | |
| tree | 206cfca2f4aa4596bbc34eeab2fed7be6e7b2354 /internal/rosa | |
| parent | be0ef63ad467a7efbbecb92d40d1f73af82a0d84 (diff) | |
This is included in the evaluation error message.
Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'internal/rosa')
| -rw-r--r-- | internal/rosa/azalea/azalea.go | 29 | ||||
| -rw-r--r-- | internal/rosa/azalea/azalea_test.go | 61 | ||||
| -rw-r--r-- | internal/rosa/azalea/evaluate.go | 22 | ||||
| -rw-r--r-- | internal/rosa/azalea/evaluate_test.go | 66 | ||||
| -rw-r--r-- | internal/rosa/package/hakurei.az | 2 | ||||
| -rw-r--r-- | internal/rosa/state.go | 9 |
6 files changed, 162 insertions, 27 deletions
diff --git a/internal/rosa/azalea/azalea.go b/internal/rosa/azalea/azalea.go index 94e54585..d97b6f7d 100644 --- a/internal/rosa/azalea/azalea.go +++ b/internal/rosa/azalea/azalea.go @@ -126,10 +126,12 @@ func (v Val) String() string { // parseVal parses until the end of the [Val]. func (p *parser) parseVal() (v Val) { - v = append(v, p.parseExpr()) + expr, _ := p.parseExpr() + v = append(v, expr) for p.tok == '+' { p.scan() - v = append(v, p.parseExpr()) + expr, _ = p.parseExpr() + v = append(v, expr) } return } @@ -194,6 +196,8 @@ type Func struct { Package bool // Key-value arguments. Args []Arg + // Position of the character immediately after the function identifier. + Pos scanner.Position } // fullFuncString controls whether [Func.String] includes the function body. @@ -233,17 +237,17 @@ func (v Func) String() string { } // parseExpr parses the current expression. -func (p *parser) parseExpr() any { +func (p *parser) parseExpr() (any, scanner.Position) { switch p.tok { case scanner.Int: v := p.parseInt() p.scan() - return v + return v, p.s.Pos() case scanner.String, scanner.RawString: v := p.parseString() p.scan() - return v + return v, p.s.Pos() case scanner.Ident: var v Func @@ -252,6 +256,7 @@ func (p *parser) parseExpr() any { p.scanAs(scanner.Ident) v.Ident = Ident(p.s.TokenText()) } + v.Pos = p.s.Pos() p.scan() switch p.tok { @@ -261,7 +266,7 @@ func (p *parser) parseExpr() any { switch p.tok { case '}': p.scan() - return v + return v, p.s.Pos() case scanner.Ident: break @@ -312,7 +317,7 @@ func (p *parser) parseExpr() any { } default: - return v.Ident + return v.Ident, p.s.Pos() } case '{': @@ -322,7 +327,7 @@ func (p *parser) parseExpr() any { switch p.tok { case '}': p.scan() - return v + return v, p.s.Pos() default: pair := KV{K: p.parseVal()} @@ -360,7 +365,7 @@ func (p *parser) parseExpr() any { panic(ExprError(',')) case ']': p.scan() - return v + return v, p.s.Pos() case scanner.EOF: panic(ExprError(scanner.EOF)) default: @@ -386,9 +391,10 @@ func (ScanError) Error() string { } // Parse parses expressions from r. -func Parse(r io.Reader) (e []any, err error) { +func Parse(r io.Reader, name string) (e []any, err error) { var p parser p.s.Init(r) + p.s.Filename = name p.s.Mode = scanner.ScanIdents | scanner.ScanInts | @@ -423,7 +429,8 @@ func Parse(r io.Reader) (e []any, err error) { p.scan() for p.tok != scanner.EOF { - e = append(e, p.parseExpr()) + expr, _ := p.parseExpr() + e = append(e, expr) } if p.s.ErrorCount != 0 { diff --git a/internal/rosa/azalea/azalea_test.go b/internal/rosa/azalea/azalea_test.go index 6e184737..7cae6de5 100644 --- a/internal/rosa/azalea/azalea_test.go +++ b/internal/rosa/azalea/azalea_test.go @@ -35,9 +35,21 @@ func TestParse(t *testing.T) { Args: []Arg{{K: []Ident{"v"}, V: Val{ Ident("v"), String("\xfd"), - Func{Ident: "p"}, + Func{Ident: "p", Pos: scanner.Position{ + Filename: "concat.az", + Offset: 18, + Line: 1, + Column: 19, + }}, Int(9), }}}, + + Pos: scanner.Position{ + Filename: "concat.az", + Offset: 1, + Line: 1, + Column: 2, + }, }}, nil}, {"truncated string concat", `f { v = v+; }`, nil, ExprError(';')}, @@ -92,6 +104,13 @@ func TestParse(t *testing.T) { {K: []Ident{Ident("checksum")}, V: Val{String("4ASoWbxaA2FW7PAB0zzHDPC5XnNhyaAyjtDPpGzceSLeYnEIXsNYZR3PA_Zu5P0K")}}, {K: []Ident{Ident("compress")}, V: Val{Ident("gzip")}}, }, + + Pos: scanner.Position{ + Filename: "gcc.az", + Offset: 165, + Line: 7, + Column: 20, + }, }}}, {K: []Ident{Ident("patches")}, V: Val{Array{ {String("musl-off64_t-loff_t.patch")}, @@ -120,12 +139,29 @@ func TestParse(t *testing.T) { {K: []Ident{Ident("amd64"), Ident("arm64")}, V: Val{String("''")}}, {K: []Ident{Ident("default")}, V: Val{Ident("unset")}}, }, + + Pos: scanner.Position{ + Filename: "gcc.az", + Offset: 890, + Line: 32, + Column: 30, + }, }}}, }}}, {K: []Ident{Ident("make")}, V: Val{Array{ {String("BOOT_CFLAGS='-O2 -g'")}, { - Func{Ident: Ident("noop"), Args: []Arg{{K: []Ident{Ident("key")}, V: Val{Ident("value")}}}}, + Func{ + Ident: Ident("noop"), + Args: []Arg{{K: []Ident{Ident("key")}, V: Val{Ident("value")}}}, + + Pos: scanner.Position{ + Filename: "gcc.az", + Offset: 995, + Line: 39, + Column: 8, + }, + }, String("\x00"), }, {String("bootstrap")}, @@ -133,6 +169,13 @@ func TestParse(t *testing.T) { {K: []Ident{Ident("skip-check")}, V: Val{Ident("true")}}, }, + + Pos: scanner.Position{ + Filename: "gcc.az", + Offset: 627, + Line: 22, + Column: 13, + }, }}}, {K: []Ident{Ident("inputs")}, V: Val{Array{ @@ -143,13 +186,23 @@ func TestParse(t *testing.T) { {Ident("kernel-headers")}, }}}, }, + + Pos: scanner.Position{ + Filename: "gcc.az", + Offset: 11, + Line: 1, + Column: 12, + }, }}, nil}, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { t.Parallel() - p, err := Parse(strings.NewReader(tc.data)) + p, err := Parse( + strings.NewReader(tc.data), + strings.ReplaceAll(tc.name, " ", "_")+".az", + ) if !reflect.DeepEqual(p, tc.want) { t.Errorf("Parse: %#v, want %#v", p, tc.want) } @@ -163,7 +216,7 @@ func TestParse(t *testing.T) { func BenchmarkParse(b *testing.B) { r := strings.NewReader(sample) for b.Loop() { - if _, err := Parse(r); err != nil { + if _, err := Parse(r, "./testdata/gcc.az"); err != nil { b.Fatal(err) } diff --git a/internal/rosa/azalea/evaluate.go b/internal/rosa/azalea/evaluate.go index 9b119100..85e20583 100644 --- a/internal/rosa/azalea/evaluate.go +++ b/internal/rosa/azalea/evaluate.go @@ -211,6 +211,18 @@ func exprTextIndent(expr any) string { return strings.ReplaceAll(ExprText(expr), "\n", "\n\t") } +// posString returns the string representation of the position of expr, if +// available. Otherwise, it returns a hardcoded string. +func posString(expr any) string { + switch v := expr.(type) { + case Func: + return v.Pos.String() + + default: + return "<unknown>" + } +} + // EvaluationError is an error and the expression it occurred in. type EvaluationError struct { Expr any @@ -225,10 +237,16 @@ func (e EvaluationError) Unwrap() error { return e.Err } func (e EvaluationError) Error() string { v, ok := errors.AsType[EvaluationError](e.Err) if ok { - return fmt.Sprintf("%v\n%s", v, ExprText(e.Expr)) + return fmt.Sprintf( + "%v\n%s\n\t%s", + v, ExprText(e.Expr), posString(e.Expr), + ) } - return fmt.Sprintf("%v\n\n%s", e.Err, ExprText(e.Expr)) + return fmt.Sprintf( + "%v\n\n%s\n\t%s", + e.Err, ExprText(e.Expr), posString(e.Expr), + ) } var ( diff --git a/internal/rosa/azalea/evaluate_test.go b/internal/rosa/azalea/evaluate_test.go index 049fcb52..edd72432 100644 --- a/internal/rosa/azalea/evaluate_test.go +++ b/internal/rosa/azalea/evaluate_test.go @@ -6,6 +6,7 @@ import ( "reflect" "strings" "testing" + "text/scanner" "unique" . "hakurei.app/internal/rosa/azalea" @@ -81,6 +82,12 @@ func TestEvaluate(t *testing.T) { Args: []Arg{ {K: []Ident{"v"}, V: Val{Int(9)}}, }, + Pos: scanner.Position{ + Filename: "apply_undefined.az", + Offset: 1, + Line: 1, + Column: 2, + }, }, Err: UndefinedError("v"), }}, @@ -96,8 +103,13 @@ func TestEvaluate(t *testing.T) { }), "\x00", nil}, {"undefined function", `f {}`, nil, "", EvaluationError{ - Expr: Func{Ident: "f"}, - Err: UndefinedError("f"), + Expr: Func{Ident: "f", Pos: scanner.Position{ + Filename: "undefined_function.az", + Offset: 1, + Line: 1, + Column: 2, + }}, + Err: UndefinedError("f"), }}, {"error wrap deep", `f { v = nonexistent; }`, makeStackCheck(func( @@ -128,6 +140,12 @@ func TestEvaluate(t *testing.T) { Args: []Arg{ {K: []Ident{"v"}, V: Val{Ident("nil")}}, }, + Pos: scanner.Position{ + Filename: "apply_nil_string.az", + Offset: 1, + Line: 1, + Column: 2, + }, }, Err: NilError{Type: reflect.TypeFor[string]()}, }}, @@ -184,6 +202,13 @@ func TestEvaluate(t *testing.T) { "v", }, V: Val{Array(nil)}}, }, + + Pos: scanner.Position{ + Filename: "common_inputs.az", + Offset: 12, + Line: 1, + Column: 13, + }, }, Err: ErrInvalidSpecial, }}, @@ -196,6 +221,13 @@ func TestEvaluate(t *testing.T) { Args: []Arg{ {K: []Ident{"inputs"}, V: Val{Array(nil)}, R: true}, }, + + Pos: scanner.Position{ + Filename: "bound_inputs.az", + Offset: 12, + Line: 1, + Column: 13, + }, }, Err: ErrInvalidSpecial, }}, @@ -208,6 +240,13 @@ func TestEvaluate(t *testing.T) { Args: []Arg{ {K: []Ident{"runtime"}, V: Val{Array(nil)}, R: true}, }, + + Pos: scanner.Position{ + Filename: "bound_runtime.az", + Offset: 12, + Line: 1, + Column: 13, + }, }, Err: ErrInvalidSpecial, }}, @@ -220,6 +259,13 @@ func TestEvaluate(t *testing.T) { Args: []Arg{ {K: []Ident{"inputs"}, V: Val{String(""), String("")}}, }, + + Pos: scanner.Position{ + Filename: "concat_inputs.az", + Offset: 12, + Line: 1, + Column: 13, + }, }, Err: ErrInvalidSpecial, }}, @@ -232,6 +278,13 @@ func TestEvaluate(t *testing.T) { Args: []Arg{ {K: []Ident{"source"}, V: Val{String(""), String("")}}, }, + + Pos: scanner.Position{ + Filename: "concat_source.az", + Offset: 12, + Line: 1, + Column: 13, + }, }, Err: ErrInvalidSpecial, }}, @@ -250,7 +303,10 @@ func TestEvaluate(t *testing.T) { t.Parallel() var expr Func - if e, err := Parse(strings.NewReader(tc.data)); err != nil { + if e, err := Parse( + strings.NewReader(tc.data), + strings.ReplaceAll(tc.name, " ", "_")+".az", + ); err != nil { t.Fatal(err) } else if len(e) != 1 { t.Fatalf("got expression %#v", e) @@ -294,7 +350,7 @@ func TestEvaluateGCC(t *testing.T) { t.Parallel() var gcc Func - if e, err := Parse(strings.NewReader(sample)); err != nil { + if e, err := Parse(strings.NewReader(sample), "./testdata/gcc.az"); err != nil { t.Fatal(err) } else { gcc = e[0].(Func) @@ -421,7 +477,7 @@ func TestEvaluateGCC(t *testing.T) { func BenchmarkEvaluate(b *testing.B) { var gcc Func - if e, err := Parse(strings.NewReader(sample)); err != nil { + if e, err := Parse(strings.NewReader(sample), "testdata/gcc.az"); err != nil { b.Fatal(err) } else { gcc = e[0].(Func) diff --git a/internal/rosa/package/hakurei.az b/internal/rosa/package/hakurei.az index a9283d96..14faef32 100644 --- a/internal/rosa/package/hakurei.az +++ b/internal/rosa/package/hakurei.az @@ -112,7 +112,7 @@ export HAKUREI_ARCH='`+arch+`' export HAKUREI_SYSROOT='`+prefix+`/`+arch+`' export PKG_CONFIG_SYSROOT_DIR='`+prefix+`/`+arch+`' -export PKG_CONFIG_LIBDIR='`+prefix+`/`+arch+``+prefix+`/lib/pkgconfig:`+prefix+`/`+arch+``+prefix+`/share/pkgconfig' +export PKG_CONFIG_LIBDIR='`+prefix+`/`+arch+prefix+`/lib/pkgconfig:`+prefix+`/`+arch+``+prefix+`/share/pkgconfig' `; build = ` NAME=make`; diff --git a/internal/rosa/state.go b/internal/rosa/state.go index febde19f..d937d070 100644 --- a/internal/rosa/state.go +++ b/internal/rosa/state.go @@ -1309,9 +1309,9 @@ var ( // RegisterAzalea registers all package declarations from r. The backing // filesystem is directly exposed to azalea pathnames. -func (s *S) RegisterAzalea(r io.Reader, b fs.FS) error { +func (s *S) RegisterAzalea(r io.Reader, name string, b fs.FS) error { var pending []*azalea.Func - if expressions, err := azalea.Parse(r); err != nil { + if expressions, err := azalea.Parse(r, name); err != nil { return err } else { pending = make([]*azalea.Func, len(expressions)) @@ -1354,6 +1354,7 @@ func (s *S) RegisterFS(fsys fs.FS) error { return err } + const prefix = "internal/rosa/package/" var r fs.File for _, dent := range dents { if dent.IsDir() { @@ -1371,7 +1372,7 @@ func (s *S) RegisterFS(fsys fs.FS) error { return err } - err = s.RegisterAzalea(r, sub) + err = s.RegisterAzalea(r, prefix+dent.Name()+"/package.az", sub) if _err := r.Close(); err == nil { err = _err } @@ -1391,7 +1392,7 @@ func (s *S) RegisterFS(fsys fs.FS) error { return err } - err = s.RegisterAzalea(r, fsys) + err = s.RegisterAzalea(r, prefix+dent.Name(), fsys) if _err := r.Close(); err == nil { err = _err } |
