diff --git a/internal/rosa/azalea/azalea.go b/internal/rosa/azalea/azalea.go index bd24ef4e..226292c3 100644 --- a/internal/rosa/azalea/azalea.go +++ b/internal/rosa/azalea/azalea.go @@ -6,6 +6,7 @@ import ( "errors" "io" "strconv" + "strings" "text/scanner" ) @@ -302,6 +303,7 @@ func (ScanError) Error() string { // Parse parses expressions from r. func Parse(r io.Reader) (e []any, err error) { + r = insertDelimiters(r) var p parser p.s.Init(r) @@ -311,12 +313,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 +343,86 @@ 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 insertDelimiters(r io.Reader) io.Reader { + 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 b strings.Builder + nldelim := false + delim := new(runeStack) + + for tok := s.Scan(); tok != scanner.EOF; tok = s.Scan() { + switch tok { + case '{', '[': + nldelim = false + if tok == '{' { + delim.Push(';') + } else { + delim.Push(',') + } + b.WriteRune(tok) + case scanner.String, scanner.RawString, scanner.Int, scanner.Ident: + b.WriteString(s.TokenText()) + nldelim = true + case '+', ':', '=', ',', ';': + nldelim = false + b.WriteRune(tok) + case '\n': + if nldelim { + b.WriteRune(delim.Peek()) + nldelim = false + } + case '}', ']': + if nldelim { + b.WriteRune(delim.Peek()) + } + delim.Pop() + nldelim = true + b.WriteRune(tok) + default: + b.WriteRune(tok) + } + } + return strings.NewReader(b.String()) +} + +type runeStack struct { + arr []rune +} + +func (s *runeStack) Push(r rune) { + s.arr = append(s.arr, r) +} + +func (s *runeStack) Peek() rune { + if s.Length() > 0 { + return s.arr[s.Length()-1] + } + return '\n' +} +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) +} diff --git a/internal/rosa/azalea/azalea_test.go b/internal/rosa/azalea/azalea_test.go index 73330412..6d341d54 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() @@ -61,8 +64,57 @@ func TestParse(t *testing.T) { {"missing array delimiter", `[ v0 v1 ]`, nil, TokenError{',', scanner.Ident}}, {"truncated array", `[ "\x00"`, nil, ExprError(scanner.EOF)}, - - {"gcc", sample, []any{Func{ + {"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}, + {"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, @@ -139,6 +191,140 @@ 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"), + 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"), 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"), + 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"), + 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"), + 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 +342,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/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 + ] } 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 + ] +}