From 052ec33a04be8b1e5b527e0010692e6c80884750 Mon Sep 17 00:00:00 2001 From: mae Date: Fri, 7 Aug 2026 09:49:00 -0500 Subject: [PATCH 1/7] internal/rosa/azalea: implement semicolon and comma insertion --- internal/rosa/azalea/azalea.go | 97 ++++++++++++++++++++++++++-- internal/rosa/azalea/testdata/gcc.az | 70 ++++++++++---------- 2 files changed, 126 insertions(+), 41 deletions(-) diff --git a/internal/rosa/azalea/azalea.go b/internal/rosa/azalea/azalea.go index bd24ef4e..bb7bc8e1 100644 --- a/internal/rosa/azalea/azalea.go +++ b/internal/rosa/azalea/azalea.go @@ -4,8 +4,11 @@ package azalea import ( "errors" + "fmt" "io" + "os" "strconv" + "strings" "text/scanner" ) @@ -302,6 +305,7 @@ func (ScanError) Error() string { // Parse parses expressions from r. func Parse(r io.Reader) (e []any, err error) { + r, err = insertSemicolons(r) var p parser p.s.Init(r) @@ -311,12 +315,7 @@ func Parse(r io.Reader) (e []any, err error) { scanner.ScanRawStrings | scanner.ScanComments | scanner.SkipComments - p.s.IsIdentRune = func(ch rune, i int) bool { - if i == 0 && ch >= '0' && ch <= '9' { - return false - } - return ch > 0 && ch < rune(len(idents)) && idents[ch] - } + p.s.IsIdentRune = isIdentRune defer func() { v := recover() @@ -346,3 +345,89 @@ func Parse(r io.Reader) (e []any, err error) { } return } +func isIdentRune(ch rune, i int) bool { + if i == 0 && ch >= '0' && ch <= '9' { + return false + } + return ch > 0 && ch < rune(len(idents)) && idents[ch] +} +func insertSemicolons(r io.Reader) (io.Reader, error) { + var s scanner.Scanner + s.Init(r) + s.Mode = scanner.ScanIdents | + scanner.ScanInts | + scanner.ScanStrings | + scanner.ScanRawStrings | + scanner.ScanComments | + scanner.SkipComments + s.Whitespace = 0 + s.IsIdentRune = isIdentRune + + var buf strings.Builder + nlsemi := false + nlcomma := false + b := "" + + tok := s.Scan() + for tok != scanner.EOF { + switch tok { + case '\n': + if b[len(b)-1] == '{' && nlsemi { + buf.WriteRune(';') + nlsemi = false + } + if b[len(b)-1] == '[' && nlcomma { + buf.WriteRune(',') + nlcomma = false + } + case ';': + buf.WriteRune(tok) + nlsemi = false + case ',': + buf.WriteRune(tok) + nlcomma = false + case '{', '[': + nlsemi = false + buf.WriteRune(tok) + b += string(tok) + case '}', ']': + if len(b) > 0 && b[len(b)-1] == '{' && nlsemi { + buf.WriteRune(';') + nlsemi = false + } + if len(b) > 0 && b[len(b)-1] == '[' && nlcomma { + buf.WriteRune(',') + nlcomma = false + } + if len(b) > 1 && b[len(b)-1] == '{' { + nlsemi = true + } + if len(b) > 1 && b[len(b)-1] == '[' { + nlcomma = true + nlsemi = true + } + + if len(b) > 1 { + b = b[:len(b)-1] + } + buf.WriteRune(tok) + case '+', ':', '=': + buf.WriteRune(tok) + nlsemi = false + nlcomma = false + case scanner.String, scanner.RawString, scanner.Int, scanner.Ident: + buf.WriteString(s.TokenText()) + if len(b) > 0 && b[len(b)-1] == '{' { + nlsemi = true + } else if len(b) > 0 && b[len(b)-1] == '[' { + nlcomma = true + } + default: + buf.WriteString(s.TokenText()) + } + + tok = s.Scan() + } + fmt.Fprintln(os.Stderr, buf.String()) + return strings.NewReader(buf.String()), nil +} diff --git a/internal/rosa/azalea/testdata/gcc.az b/internal/rosa/azalea/testdata/gcc.az index c1dc8f2b..65c2452d 100644 --- a/internal/rosa/azalea/testdata/gcc.az +++ b/internal/rosa/azalea/testdata/gcc.az @@ -1,19 +1,19 @@ package gcc { - description = "The GNU Compiler Collection"; - website = "https://www.gnu.org/software/gcc"; - anitya = 6502; + description = "The GNU Compiler Collection" + website = "https://www.gnu.org/software/gcc" + anitya = 6502 - version# = "16.1.0"; + version# = "16.1.0" source = remoteTar { url = "https://ftp.tsukuba.wide.ad.jp/software/gcc/releases/"+ - "gcc-"+version+"/gcc-"+version+".tar.gz"; - checksum = "4ASoWbxaA2FW7PAB0zzHDPC5XnNhyaAyjtDPpGzceSLeYnEIXsNYZR3PA_Zu5P0K"; - compress = gzip; - }; + "gcc-"+version+"/gcc-"+version+".tar.gz" + checksum = "4ASoWbxaA2FW7PAB0zzHDPC5XnNhyaAyjtDPpGzceSLeYnEIXsNYZR3PA_Zu5P0K" + compress = gzip + } patches = [ - "musl-off64_t-loff_t.patch", - "musl-legacy-lfs.patch", - ]; + "musl-off64_t-loff_t.patch" + "musl-legacy-lfs.patch" + ] // GCC spends most of its time in its many configure scripts, however // it also saturates the CPU for a consequential amount of time. @@ -21,37 +21,37 @@ package gcc { exec = make { configure = { - "disable-multilib"; - "enable-default-pie"; - "disable-nls"; - "with-gnu-as"; - "with-gnu-ld"; - "with-system-zlib"; - "enable-languages": "c,c++,go"; - "with-native-system-header-dir": "/system/include"; + "disable-multilib" + "enable-default-pie" + "disable-nls" + "with-gnu-as" + "with-gnu-ld" + "with-system-zlib" + "enable-languages": "c,c++,go" + "with-native-system-header-dir": "/system/include" "with-multilib-list": arch { - amd64, arm64 = "''"; - default = unset; - }; - }; + amd64, arm64 = "''" + default = unset + } + } make = [ - "BOOT_CFLAGS='-O2 -g'", - noop { key = value; } + "\x00", - "bootstrap", - ]; + "BOOT_CFLAGS='-O2 -g'" + noop { key = value } + "\x00" + "bootstrap" + ] // This toolchain is hacked to pieces, it is not expected to ever work // well in its current state. That does not matter as long as the // toolchain it produces passes its own test suite. - skip-check = true; - }; + skip-check = true + } inputs = [ - binutils, + binutils - mpc, - zlib, - libucontext, - kernel-headers, - ]; + mpc + zlib + libucontext + kernel-headers + ] } -- 2.54.0 From bcb92ea346ae70c98e8d5f7eb38a6758f6a05e28 Mon Sep 17 00:00:00 2001 From: mae Date: Fri, 7 Aug 2026 09:49:00 -0500 Subject: [PATCH 2/7] internal/rosa/azalea: implement semicolon and comma insertion --- internal/rosa/azalea/azalea.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/internal/rosa/azalea/azalea.go b/internal/rosa/azalea/azalea.go index bb7bc8e1..825786bc 100644 --- a/internal/rosa/azalea/azalea.go +++ b/internal/rosa/azalea/azalea.go @@ -4,9 +4,7 @@ package azalea import ( "errors" - "fmt" "io" - "os" "strconv" "strings" "text/scanner" @@ -428,6 +426,5 @@ func insertSemicolons(r io.Reader) (io.Reader, error) { tok = s.Scan() } - fmt.Fprintln(os.Stderr, buf.String()) return strings.NewReader(buf.String()), nil } -- 2.54.0 From fec6265ac7dd77ba26c60b3f54cbe9d7312322ae Mon Sep 17 00:00:00 2001 From: mae Date: Fri, 7 Aug 2026 19:46:15 -0500 Subject: [PATCH 3/7] internal/rosa/azalea: improve delimiter insertion --- internal/rosa/azalea/azalea.go | 81 +++++++++++++++++++++++++--------- 1 file changed, 60 insertions(+), 21 deletions(-) diff --git a/internal/rosa/azalea/azalea.go b/internal/rosa/azalea/azalea.go index 825786bc..7668de55 100644 --- a/internal/rosa/azalea/azalea.go +++ b/internal/rosa/azalea/azalea.go @@ -364,17 +364,17 @@ func insertSemicolons(r io.Reader) (io.Reader, error) { var buf strings.Builder nlsemi := false nlcomma := false - b := "" + b := new(RuneStack) tok := s.Scan() for tok != scanner.EOF { switch tok { case '\n': - if b[len(b)-1] == '{' && nlsemi { + if b.Peek() == '{' && nlsemi { buf.WriteRune(';') nlsemi = false } - if b[len(b)-1] == '[' && nlcomma { + if b.Peek() == '[' && nlcomma { buf.WriteRune(',') nlcomma = false } @@ -386,38 +386,40 @@ func insertSemicolons(r io.Reader) (io.Reader, error) { nlcomma = false case '{', '[': nlsemi = false + nlcomma = false buf.WriteRune(tok) - b += string(tok) + b.Push(tok) case '}', ']': - if len(b) > 0 && b[len(b)-1] == '{' && nlsemi { - buf.WriteRune(';') - nlsemi = false - } - if len(b) > 0 && b[len(b)-1] == '[' && nlcomma { - buf.WriteRune(',') - nlcomma = false - } - if len(b) > 1 && b[len(b)-1] == '{' { - nlsemi = true - } - if len(b) > 1 && b[len(b)-1] == '[' { + r := b.Pop() + if r == '{' { + if nlsemi { + buf.WriteRune(';') + } nlcomma = true nlsemi = true } - - if len(b) > 1 { - b = b[:len(b)-1] + if r == '[' { + if nlcomma { + buf.WriteRune(',') + } + nlsemi = true } buf.WriteRune(tok) + if b.Peek() == '}' && nlsemi { + buf.WriteRune(';') + } + if b.Peek() == ']' && nlcomma { + buf.WriteRune(',') + } case '+', ':', '=': buf.WriteRune(tok) nlsemi = false nlcomma = false case scanner.String, scanner.RawString, scanner.Int, scanner.Ident: buf.WriteString(s.TokenText()) - if len(b) > 0 && b[len(b)-1] == '{' { + if b.Peek() == '{' { nlsemi = true - } else if len(b) > 0 && b[len(b)-1] == '[' { + } else if b.Peek() == '[' { nlcomma = true } default: @@ -428,3 +430,40 @@ func insertSemicolons(r io.Reader) (io.Reader, error) { } return strings.NewReader(buf.String()), nil } + +type Stack[T any] interface { + Push(T) + Pop() T + Peek() T + Length() int +} +type RuneStack struct { + arr []rune + Stack[rune] +} + +func (s *RuneStack) Push(r rune) { + s.arr = append(s.arr, r) +} + +func (s *RuneStack) String() string { + return string(s.arr) +} +func (s *RuneStack) Peek() rune { + if s.Length() > 0 { + return s.arr[s.Length()-1] + } + return 0 +} +func (s *RuneStack) Pop() rune { + v := s.Peek() + if s.Length() > 1 { + s.arr = s.arr[:s.Length()-1] + } else { + s.arr = make([]rune, 0) + } + return v +} +func (s *RuneStack) Length() int { + return len(s.arr) +} -- 2.54.0 From aaabe8851c97da3ba188995ed68f65ca0db7e263 Mon Sep 17 00:00:00 2001 From: mae Date: Fri, 7 Aug 2026 20:54:00 -0500 Subject: [PATCH 4/7] internal/rosa/azalea: add mesa test --- internal/rosa/azalea/azalea.go | 6 -- internal/rosa/azalea/azalea_test.go | 149 +++++++++++++++++++++++++- internal/rosa/azalea/evaluate_test.go | 4 +- internal/rosa/azalea/testdata/mesa.az | 139 ++++++++++++++++++++++++ 4 files changed, 286 insertions(+), 12 deletions(-) create mode 100644 internal/rosa/azalea/testdata/mesa.az diff --git a/internal/rosa/azalea/azalea.go b/internal/rosa/azalea/azalea.go index 7668de55..ac0212d4 100644 --- a/internal/rosa/azalea/azalea.go +++ b/internal/rosa/azalea/azalea.go @@ -405,12 +405,6 @@ func insertSemicolons(r io.Reader) (io.Reader, error) { nlsemi = true } buf.WriteRune(tok) - if b.Peek() == '}' && nlsemi { - buf.WriteRune(';') - } - if b.Peek() == ']' && nlcomma { - buf.WriteRune(',') - } case '+', ':', '=': buf.WriteRune(tok) nlsemi = false diff --git a/internal/rosa/azalea/azalea_test.go b/internal/rosa/azalea/azalea_test.go index 73330412..87b202a1 100644 --- a/internal/rosa/azalea/azalea_test.go +++ b/internal/rosa/azalea/azalea_test.go @@ -11,7 +11,10 @@ import ( ) //go:embed testdata/gcc.az -var sample string +var gccSample string + +//go:embed testdata/mesa.az +var mesaSample string func TestParse(t *testing.T) { t.Parallel() @@ -62,7 +65,7 @@ func TestParse(t *testing.T) { {"truncated array", `[ "\x00"`, nil, ExprError(scanner.EOF)}, - {"gcc", sample, []any{Func{ + {"gcc", gccSample, []any{Func{ Ident: Ident("gcc"), Package: true, @@ -139,6 +142,144 @@ func TestParse(t *testing.T) { }}}, }, }}, nil}, + {"mesa", mesaSample, []any{Func{ + Ident: Ident("mesa"), + Package: true, + Args: []Arg{ + {K: []Ident{Ident("description")}, V: Val{String("open source implementations of OpenGL, OpenGL ES, Vulkan, OpenCL, and more")}}, + {K: []Ident{Ident("website")}, V: Val{String("https://mesa3d.org")}}, + {K: []Ident{Ident("anitya")}, V: Val{Int(1970)}}, + {K: []Ident{Ident("latest")}, V: Val{Ident("anityaFallback")}}, + {K: []Ident{Ident("version")}, V: Val{String("26.2.0")}, R: true}, + + {K: []Ident{Ident("source")}, V: Val{Func{ + Ident: Ident("remoteGitLab"), + Package: false, + Args: []Arg{ + {K: []Ident{Ident("domain")}, V: Val{String("gitlab.freedesktop.org")}}, + {K: []Ident{Ident("suffix")}, V: Val{String("mesa/mesa")}}, + {K: []Ident{Ident("ref")}, V: Val{String("mesa-"), Ident("version")}}, + {K: []Ident{Ident("checksum")}, V: Val{String("UPnIdpo5wUsE938GXY-YyYDzfF62enRoEzCYRyLLPvzBhBbP4wZ8nDiKsjivcs5v")}}, + }, + }}}, + {K: []Ident{Ident("exec")}, V: Val{Func{Ident: Ident("meson"), Package: false, Args: []Arg{ + {K: []Ident{Ident("setup")}, V: Val{[]KV{ + {K: String("platforms"), V: Val{String("x11,wayland")}}, + {K: String("video-codecs"), V: Val{String("all")}}, + {K: String("glvnd"), V: Val{String("enabled")}}, + {K: String("gbm"), V: Val{String("enabled")}}, + {K: String("egl"), V: Val{String("enabled")}}, + {K: String("gallium-drivers"), V: Val{Func{ + Ident: Ident("join"), + Package: false, + Args: []Arg{ + {K: []Ident{Ident("elems")}, V: Val{Array{ + Val{String("asahi")}, + Val{String("crocus")}, + Val{String("etnaviv")}, + Val{String("freedreno")}, + Val{String("i915")}, + Val{String("iris")}, + Val{String("lima")}, + Val{String("llvmpipe")}, + Val{String("nouveau")}, + Val{String("panfrost")}, + Val{String("r300")}, + Val{String("r600")}, + Val{String("radeonsi")}, + Val{String("softpipe")}, + Val{String("svga")}, + Val{String("tegra")}, + Val{String("v3d")}, + Val{String("vc4")}, + Val{String("virgl")}, + Val{String("zink")}}}}, + {K: []Ident{Ident("sep")}, V: Val{String(",")}}, + }, + }}}, + {K: String("vulkan-drivers"), V: Val{Func{ + Ident: Ident("join"), + Package: false, + Args: []Arg{ + {K: []Ident{Ident("elems")}, V: Val{Array{ + Val{String("amd")}, + Val{String("broadcom")}, + Val{String("freedreno")}, + Val{String("intel")}, + Val{String("intel_hasvk")}, + Val{String("panfrost")}, + Val{String("swrast")}, + Val{String("virtio")}, + Val{String("imagination")}, + Val{String("asahi")}, + Val{String("gfxstream")}, + }}}, + {K: []Ident{Ident("sep")}, V: Val{String(",")}}}, + }}}, + {K: String("vulkan-layers"), V: Val{Func{ + Ident: Ident("join"), + Package: false, + Args: []Arg{ + {K: []Ident{Ident("elems")}, V: Val{Array{ + Val{String("device-select")}, + Val{String("intel-nullhw")}, + Val{String("overlay")}, + Val{String("screenshot")}, + Val{String("anti-lag")}, + Val{String("vram-report-limit")}, + }}}, + {K: []Ident{Ident("sep")}, V: Val{String(",")}}}, + }}}, + {K: String("freedreno-kmds"), V: Val{String("msm,virtio")}}, + {K: String("amdgpu-virtio"), V: Val{String("true")}}}, + }}, + }}}}, + {K: []Ident{Ident("inputs")}, V: Val{Array{ + Val{Ident("m4")}, + Val{Ident("python-packaging")}, + Val{Ident("python-mako")}, + Val{Ident("python-pyyaml")}, + Val{Ident("python-pycparser")}, + Val{Ident("glslang")}, + Val{Ident("spirv-llvm-translator")}, + Val{Ident("zlib")}, + Val{Ident("zstd")}, + Val{Ident("gzip")}, + Val{Ident("ncurses")}, + Val{Ident("libglvnd")}, + Val{Ident("libexpat")}, + Val{Ident("libva")}, + Val{Ident("libdrm")}, + Val{Ident("elfutils")}, + Val{Ident("bison")}, + Val{Ident("flex")}, + Val{Ident("lm_sensors")}, + Val{Ident("libconfig")}, + Val{Ident("libdisplay-info")}, + Val{Ident("wayland")}, + Val{Ident("wayland-protocols")}, + Val{Ident("libxshmfence")}, + Val{Ident("libXxf86vm")}, + Val{Ident("libXrandr")}, + Val{Ident("libxcb-keysyms")}, + Val{Ident("libpng")}, + Val{Ident("libarchive")}, + Val{Ident("kernel-headers")}, + }}}, + {K: []Ident{Ident("runtime")}, V: Val{Array{ + Val{Ident("llvm")}, + Val{Ident("libdrm")}, + Val{Ident("elfutils")}, + Val{Ident("lm_sensors")}, + Val{Ident("libdisplay-info")}, + Val{Ident("wayland")}, + Val{Ident("libxshmfence")}, + Val{Ident("libXxf86vm")}, + Val{Ident("libXrandr")}, + Val{Ident("libxcb-keysyms")}, + Val{Ident("libpng")}}}}, + }, + }}, nil}, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { @@ -156,14 +297,14 @@ func TestParse(t *testing.T) { } func BenchmarkParse(b *testing.B) { - r := strings.NewReader(sample) + r := strings.NewReader(gccSample) for b.Loop() { if _, err := Parse(r); err != nil { b.Fatal(err) } b.StopTimer() - r.Reset(sample) + r.Reset(gccSample) b.StartTimer() } } diff --git a/internal/rosa/azalea/evaluate_test.go b/internal/rosa/azalea/evaluate_test.go index 4a6c2f56..edb0bc2f 100644 --- a/internal/rosa/azalea/evaluate_test.go +++ b/internal/rosa/azalea/evaluate_test.go @@ -230,7 +230,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(gccSample)); err != nil { t.Fatal(err) } else { gcc = e[0].(Func) @@ -357,7 +357,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(gccSample)); err != nil { b.Fatal(err) } else { gcc = e[0].(Func) diff --git a/internal/rosa/azalea/testdata/mesa.az b/internal/rosa/azalea/testdata/mesa.az new file mode 100644 index 00000000..9d76b532 --- /dev/null +++ b/internal/rosa/azalea/testdata/mesa.az @@ -0,0 +1,139 @@ +package mesa { + description = "open source implementations of OpenGL, OpenGL ES, Vulkan, OpenCL, and more" + website = "https://mesa3d.org" + anitya = 1970 + latest = anityaFallback + + version# = "26.2.0" + source = remoteGitLab { + domain = "gitlab.freedesktop.org" + suffix = "mesa/mesa" + ref = "mesa-"+version + checksum = "UPnIdpo5wUsE938GXY-YyYDzfF62enRoEzCYRyLLPvzBhBbP4wZ8nDiKsjivcs5v" + } + + exec = meson { + setup = { + "platforms": "x11,wayland" + "video-codecs": "all" + + "glvnd": "enabled" + "gbm": "enabled" + "egl": "enabled" + + "gallium-drivers": join { + elems = [ + "asahi" // Apple AGX + "crocus" // Intel legacy + "etnaviv" // Vivante GPU designs (mostly NXP/Marvell SoCs) + "freedreno" // Qualcomm Adreno (all Qualcomm SoCs) + "i915" // Intel extra legacy + "iris" // new Intel (Broadwell+) + "lima" // ARM Mali 4xx + "llvmpipe" // software renderer + "nouveau" // Nvidia + "panfrost" // ARM Mali Midgard and up (T/G series) + "r300" // very old AMD + "r600" // less old AMD + "radeonsi" // new AMD (GCN+) + "softpipe" // older software renderer + "svga" // VMWare virtualized GPU + "tegra" // Nvidia Tegra SoCs + "v3d" // Broadcom VC5 (Raspberry Pi 4) + "vc4" // Broadcom VC4 (Raspberry Pi 0-3) + "virgl" // QEMU virtualized GPU (aka VirGL) + "zink" // generic OpenGL over Vulkan, experimental + + // d3d12: WSL emulated GPU (aka Dozen) + // ethosu: accelerator + // rocket: accelerator + ] + sep = "," + } + + "vulkan-drivers": join { + elems = [ + "amd" // AMD (aka RADV) + "broadcom" // Broadcom VC5 (Raspberry Pi 4, aka V3D) + "freedreno" // Qualcomm Adreno (all Qualcomm SoCs) + "intel" // new Intel (aka ANV) + "intel_hasvk" // Intel Haswell/Broadwell, "legacy" Vulkan driver (https://www.phoronix.com/news/Intel-HasVK-Drop-Dead-Code) + "panfrost" // ARM Mali Midgard and up (T/G series) + "swrast" // software renderer (aka Lavapipe) + "virtio" // QEMU virtualized GPU (aka VirGL) + "imagination" // PowerVR Rogue + "asahi" // Apple AGX + "gfxstream" // Android virtualized GPU + + // nouveau: Nouveau (aka NVK), requires rust + // microsoft-experimental: WSL virtualized GPU (aka DZN/Dozen) + // kosmickrisp: macOS-specific + ] + sep = "," + } + + "vulkan-layers": join { + elems = [ + "device-select" + "intel-nullhw" + "overlay" + "screenshot" + "anti-lag" + "vram-report-limit" + ] + sep = "," + } + + "freedreno-kmds": "msm,virtio" + "amdgpu-virtio": "true" + } + } + + inputs = [ + m4 + python-packaging + python-mako + python-pyyaml + python-pycparser + glslang + spirv-llvm-translator + + zlib + zstd + gzip + ncurses + libglvnd + libexpat + libva + libdrm + elfutils + bison + flex + lm_sensors + libconfig + libdisplay-info + wayland + wayland-protocols + libxshmfence + libXxf86vm + libXrandr + libxcb-keysyms + libpng + libarchive + kernel-headers + ] + + runtime = [ + llvm + libdrm + elfutils + lm_sensors + libdisplay-info + wayland + libxshmfence + libXxf86vm + libXrandr + libxcb-keysyms + libpng + ] +} -- 2.54.0 From a81e2f251c8adcde0b5867e4b095371f4ee120b6 Mon Sep 17 00:00:00 2001 From: mae Date: Sat, 8 Aug 2026 11:57:17 -0500 Subject: [PATCH 5/7] internal/rosa/azalea: simplified delimiter insertion function --- internal/rosa/azalea/azalea.go | 100 +++++++++++---------------------- 1 file changed, 33 insertions(+), 67 deletions(-) diff --git a/internal/rosa/azalea/azalea.go b/internal/rosa/azalea/azalea.go index ac0212d4..982fea2d 100644 --- a/internal/rosa/azalea/azalea.go +++ b/internal/rosa/azalea/azalea.go @@ -303,7 +303,7 @@ func (ScanError) Error() string { // Parse parses expressions from r. func Parse(r io.Reader) (e []any, err error) { - r, err = insertSemicolons(r) + r, err = insertDelimiters(r) var p parser p.s.Init(r) @@ -349,7 +349,7 @@ func isIdentRune(ch rune, i int) bool { } return ch > 0 && ch < rune(len(idents)) && idents[ch] } -func insertSemicolons(r io.Reader) (io.Reader, error) { +func insertDelimiters(r io.Reader) (io.Reader, error) { var s scanner.Scanner s.Init(r) s.Mode = scanner.ScanIdents | @@ -362,94 +362,60 @@ func insertSemicolons(r io.Reader) (io.Reader, error) { s.IsIdentRune = isIdentRune var buf strings.Builder - nlsemi := false - nlcomma := false - b := new(RuneStack) + nldelim := false + b := new(runeStack) - tok := s.Scan() - for tok != scanner.EOF { + for tok := s.Scan(); tok != scanner.EOF; tok = s.Scan() { switch tok { - case '\n': - if b.Peek() == '{' && nlsemi { - buf.WriteRune(';') - nlsemi = false - } - if b.Peek() == '[' && nlcomma { - buf.WriteRune(',') - nlcomma = false - } - case ';': - buf.WriteRune(tok) - nlsemi = false - case ',': - buf.WriteRune(tok) - nlcomma = false case '{', '[': - nlsemi = false - nlcomma = false - buf.WriteRune(tok) - b.Push(tok) - case '}', ']': - r := b.Pop() - if r == '{' { - if nlsemi { - buf.WriteRune(';') - } - nlcomma = true - nlsemi = true - } - if r == '[' { - if nlcomma { - buf.WriteRune(',') - } - nlsemi = true + nldelim = false + if tok == '{' { + b.Push(';') + } else { + b.Push(',') } buf.WriteRune(tok) - case '+', ':', '=': - buf.WriteRune(tok) - nlsemi = false - nlcomma = false case scanner.String, scanner.RawString, scanner.Int, scanner.Ident: buf.WriteString(s.TokenText()) - if b.Peek() == '{' { - nlsemi = true - } else if b.Peek() == '[' { - nlcomma = true + nldelim = true + case '+', ':', '=', ',', ';': + nldelim = false + buf.WriteRune(tok) + case '\n': + if nldelim { + buf.WriteRune(b.Peek()) + nldelim = false } + buf.WriteRune(tok) + case '}', ']': + if nldelim { + buf.WriteRune(b.Peek()) + } + b.Pop() + nldelim = true + buf.WriteRune(tok) default: - buf.WriteString(s.TokenText()) + buf.WriteRune(tok) } - - tok = s.Scan() } return strings.NewReader(buf.String()), nil } -type Stack[T any] interface { - Push(T) - Pop() T - Peek() T - Length() int -} -type RuneStack struct { +type runeStack struct { arr []rune - Stack[rune] } -func (s *RuneStack) Push(r rune) { +func (s *runeStack) Push(r rune) { s.arr = append(s.arr, r) } -func (s *RuneStack) String() string { - return string(s.arr) -} -func (s *RuneStack) Peek() rune { +func (s *runeStack) Peek() rune { if s.Length() > 0 { return s.arr[s.Length()-1] } - return 0 + return '\n' } -func (s *RuneStack) Pop() rune { +func (s *runeStack) Pop() rune { v := s.Peek() if s.Length() > 1 { s.arr = s.arr[:s.Length()-1] @@ -458,6 +424,6 @@ func (s *RuneStack) Pop() rune { } return v } -func (s *RuneStack) Length() int { +func (s *runeStack) Length() int { return len(s.arr) } -- 2.54.0 From 9eb3ecd9424ed7c82e597c264c66cf24d3c14ce6 Mon Sep 17 00:00:00 2001 From: mae Date: Sat, 8 Aug 2026 14:25:37 -0500 Subject: [PATCH 6/7] internal/rosa/azalea: refactor delimiter insertion, add test --- internal/rosa/azalea/azalea.go | 31 ++++++++++++++--------------- internal/rosa/azalea/azalea_test.go | 31 +++++++++++++++++++---------- 2 files changed, 36 insertions(+), 26 deletions(-) diff --git a/internal/rosa/azalea/azalea.go b/internal/rosa/azalea/azalea.go index 982fea2d..226292c3 100644 --- a/internal/rosa/azalea/azalea.go +++ b/internal/rosa/azalea/azalea.go @@ -303,7 +303,7 @@ func (ScanError) Error() string { // Parse parses expressions from r. func Parse(r io.Reader) (e []any, err error) { - r, err = insertDelimiters(r) + r = insertDelimiters(r) var p parser p.s.Init(r) @@ -349,7 +349,7 @@ func isIdentRune(ch rune, i int) bool { } return ch > 0 && ch < rune(len(idents)) && idents[ch] } -func insertDelimiters(r io.Reader) (io.Reader, error) { +func insertDelimiters(r io.Reader) io.Reader { var s scanner.Scanner s.Init(r) s.Mode = scanner.ScanIdents | @@ -361,44 +361,43 @@ func insertDelimiters(r io.Reader) (io.Reader, error) { s.Whitespace = 0 s.IsIdentRune = isIdentRune - var buf strings.Builder + var b strings.Builder nldelim := false - b := new(runeStack) + delim := new(runeStack) for tok := s.Scan(); tok != scanner.EOF; tok = s.Scan() { switch tok { case '{', '[': nldelim = false if tok == '{' { - b.Push(';') + delim.Push(';') } else { - b.Push(',') + delim.Push(',') } - buf.WriteRune(tok) + b.WriteRune(tok) case scanner.String, scanner.RawString, scanner.Int, scanner.Ident: - buf.WriteString(s.TokenText()) + b.WriteString(s.TokenText()) nldelim = true case '+', ':', '=', ',', ';': nldelim = false - buf.WriteRune(tok) + b.WriteRune(tok) case '\n': if nldelim { - buf.WriteRune(b.Peek()) + b.WriteRune(delim.Peek()) nldelim = false } - buf.WriteRune(tok) case '}', ']': if nldelim { - buf.WriteRune(b.Peek()) + b.WriteRune(delim.Peek()) } - b.Pop() + delim.Pop() nldelim = true - buf.WriteRune(tok) + b.WriteRune(tok) default: - buf.WriteRune(tok) + b.WriteRune(tok) } } - return strings.NewReader(buf.String()), nil + return strings.NewReader(b.String()) } type runeStack struct { diff --git a/internal/rosa/azalea/azalea_test.go b/internal/rosa/azalea/azalea_test.go index 87b202a1..44faf8a5 100644 --- a/internal/rosa/azalea/azalea_test.go +++ b/internal/rosa/azalea/azalea_test.go @@ -64,7 +64,22 @@ func TestParse(t *testing.T) { {"missing array delimiter", `[ v0 v1 ]`, nil, TokenError{',', scanner.Ident}}, {"truncated array", `[ "\x00"`, nil, ExprError(scanner.EOF)}, - + {"array of func", "[\nf {\n v = v\n v2 =\n v2\n }\n g {\n w = w\n w2 = w2\n}\n]\n", []any{Array{ + Val{Func{ + Ident: Ident("f"), + Args: []Arg{ + {K: []Ident{Ident("v")}, V: Val{Ident("v")}}, + {K: []Ident{Ident("v2")}, V: Val{Ident("v2")}}, + }, + }}, + Val{Func{ + Ident: Ident("g"), + Args: []Arg{ + {K: []Ident{Ident("w")}, V: Val{Ident("w")}}, + {K: []Ident{Ident("w2")}, V: Val{Ident("w2")}}, + }, + }}, + }}, nil}, {"gcc", gccSample, []any{Func{ Ident: Ident("gcc"), Package: true, @@ -153,8 +168,7 @@ func TestParse(t *testing.T) { {K: []Ident{Ident("version")}, V: Val{String("26.2.0")}, R: true}, {K: []Ident{Ident("source")}, V: Val{Func{ - Ident: Ident("remoteGitLab"), - Package: false, + Ident: Ident("remoteGitLab"), Args: []Arg{ {K: []Ident{Ident("domain")}, V: Val{String("gitlab.freedesktop.org")}}, {K: []Ident{Ident("suffix")}, V: Val{String("mesa/mesa")}}, @@ -162,7 +176,7 @@ func TestParse(t *testing.T) { {K: []Ident{Ident("checksum")}, V: Val{String("UPnIdpo5wUsE938GXY-YyYDzfF62enRoEzCYRyLLPvzBhBbP4wZ8nDiKsjivcs5v")}}, }, }}}, - {K: []Ident{Ident("exec")}, V: Val{Func{Ident: Ident("meson"), Package: false, Args: []Arg{ + {K: []Ident{Ident("exec")}, V: Val{Func{Ident: Ident("meson"), Args: []Arg{ {K: []Ident{Ident("setup")}, V: Val{[]KV{ {K: String("platforms"), V: Val{String("x11,wayland")}}, {K: String("video-codecs"), V: Val{String("all")}}, @@ -170,8 +184,7 @@ func TestParse(t *testing.T) { {K: String("gbm"), V: Val{String("enabled")}}, {K: String("egl"), V: Val{String("enabled")}}, {K: String("gallium-drivers"), V: Val{Func{ - Ident: Ident("join"), - Package: false, + Ident: Ident("join"), Args: []Arg{ {K: []Ident{Ident("elems")}, V: Val{Array{ Val{String("asahi")}, @@ -198,8 +211,7 @@ func TestParse(t *testing.T) { }, }}}, {K: String("vulkan-drivers"), V: Val{Func{ - Ident: Ident("join"), - Package: false, + Ident: Ident("join"), Args: []Arg{ {K: []Ident{Ident("elems")}, V: Val{Array{ Val{String("amd")}, @@ -217,8 +229,7 @@ func TestParse(t *testing.T) { {K: []Ident{Ident("sep")}, V: Val{String(",")}}}, }}}, {K: String("vulkan-layers"), V: Val{Func{ - Ident: Ident("join"), - Package: false, + Ident: Ident("join"), Args: []Arg{ {K: []Ident{Ident("elems")}, V: Val{Array{ Val{String("device-select")}, -- 2.54.0 From 95a6014ec192aab5b24e1674d559d33d0532d9fe Mon Sep 17 00:00:00 2001 From: mae Date: Sat, 8 Aug 2026 19:08:25 -0500 Subject: [PATCH 7/7] internal/rosa/azalea: additional test --- internal/rosa/azalea/azalea_test.go | 34 +++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/internal/rosa/azalea/azalea_test.go b/internal/rosa/azalea/azalea_test.go index 44faf8a5..6d341d54 100644 --- a/internal/rosa/azalea/azalea_test.go +++ b/internal/rosa/azalea/azalea_test.go @@ -80,6 +80,40 @@ func TestParse(t *testing.T) { }, }}, }}, nil}, + {"mixed semicolons and newlines", ` +f { + a = 4; + b = 2 + c = [ + a, + b + c + d, e + ] + d = { + "a": b; + "c": d + }; e = 2 +}`, + []any{Func{ + Ident: Ident("f"), + Args: []Arg{ + {K: []Ident{Ident("a")}, V: Val{Int(4)}}, + {K: []Ident{Ident("b")}, V: Val{Int(2)}}, + {K: []Ident{Ident("c")}, V: Val{Array{ + Val{Ident("a")}, + Val{Ident("b")}, + Val{Ident("c")}, + Val{Ident("d")}, + Val{Ident("e")}, + }}}, + {K: []Ident{Ident("d")}, V: Val{[]KV{ + {K: String("a"), V: Val{Ident("b")}}, + {K: String("c"), V: Val{Ident("d")}}, + }}}, + {K: []Ident{Ident("e")}, V: Val{Int(2)}}, + }, + }}, nil}, {"gcc", gccSample, []any{Func{ Ident: Ident("gcc"), Package: true, -- 2.54.0