package azalea_test import ( _ "embed" "reflect" "strings" "testing" "text/scanner" . "hakurei.app/internal/rosa/azalea" ) //go:embed testdata/gcc.az var gccSample string //go:embed testdata/mesa.az var mesaSample string func TestParse(t *testing.T) { t.Parallel() testCases := []struct { name string data string want []any err error }{ {"invalid", "}", nil, ExprError('}')}, {"bad sep", "f{v?}", nil, TokenError{'=', '?'}}, {"bad ident", "f{9}", nil, TokenError{scanner.Ident, scanner.Int}}, {"share bad sep", "f { v,,v = v; }", nil, ExprError(',')}, {"share missing sep", "f { v v }", nil, TokenError{',', scanner.Ident}}, {"ident", `v`, []any{Ident("v")}, nil}, {"concat", `f { v = v+"\xfd"+p{}+9; }`, []any{Func{ Ident: "f", Args: []Arg{{K: []Ident{"v"}, V: Val{ Ident("v"), String("\xfd"), Func{Ident: "p"}, Int(9), }}}, }}, nil}, {"truncated string concat", `f { v = v+; }`, nil, ExprError(';')}, {"empty pairs", `{}`, []any{[]KV(nil)}, nil}, {"short kv", `{"\x00":v;}`, []any{[]KV{ {K: "\x00", V: Val{Ident("v")}}, }}, nil}, {"truncated kv", `{"\x00"`, nil, ExprError(scanner.EOF)}, {"ident kv", `{v="";}`, nil, ExprError(scanner.Ident)}, {"empty array", `[]`, []any{Array(nil)}, nil}, {"integer array", `[9]`, []any{Array{{Int(9)}}}, nil}, {"short array", `[ "\x00" ]`, []any{ Array{{String("\x00")}}, }, nil}, {"short array delim", `[ "\x00", ]`, []any{ Array{{String("\x00")}}, }, nil}, {"missing array value", `[ "\x00", , v ]`, nil, ExprError(',')}, {"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}, {"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, Args: []Arg{ {K: []Ident{Ident("description")}, V: Val{String("The GNU Compiler Collection")}}, {K: []Ident{Ident("website")}, V: Val{String("https://www.gnu.org/software/gcc")}}, {K: []Ident{Ident("anitya")}, V: Val{Int(6502)}}, {K: []Ident{Ident("version")}, V: Val{String("16.1.0")}, R: true}, {K: []Ident{Ident("source")}, V: Val{Func{ Ident: Ident("remoteTar"), Args: []Arg{ {K: []Ident{Ident("url")}, V: Val{ String("https://ftp.tsukuba.wide.ad.jp/software/gcc/releases/"), String("gcc-"), Ident("version"), String("/gcc-"), Ident("version"), String(".tar.gz"), }}, {K: []Ident{Ident("checksum")}, V: Val{String("4ASoWbxaA2FW7PAB0zzHDPC5XnNhyaAyjtDPpGzceSLeYnEIXsNYZR3PA_Zu5P0K")}}, {K: []Ident{Ident("compress")}, V: Val{Ident("gzip")}}, }, }}}, {K: []Ident{Ident("patches")}, V: Val{Array{ {String("musl-off64_t-loff_t.patch")}, {String("musl-legacy-lfs.patch")}, }}}, {K: []Ident{Ident("exclusive")}, V: Val{Ident("true")}}, {K: []Ident{Ident("exec")}, V: Val{Func{ Ident: Ident("make"), Args: []Arg{ {K: []Ident{Ident("configure")}, V: Val{[]KV{ {K: String("disable-multilib")}, {K: String("enable-default-pie")}, {K: String("disable-nls")}, {K: String("with-gnu-as")}, {K: String("with-gnu-ld")}, {K: String("with-system-zlib")}, {K: String("enable-languages"), V: Val{String("c,c++,go")}}, {K: String("with-native-system-header-dir"), V: Val{String("/system/include")}}, {K: String("with-multilib-list"), V: Val{Func{ Ident: Ident("arch"), Args: []Arg{ {K: []Ident{Ident("amd64"), Ident("arm64")}, V: Val{String("''")}}, {K: []Ident{Ident("default")}, V: Val{Ident("unset")}}, }, }}}, }}}, {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")}}}}, String("\x00"), }, {String("bootstrap")}, }}}, {K: []Ident{Ident("skip-check")}, V: Val{Ident("true")}}, }, }}}, {K: []Ident{Ident("inputs")}, V: Val{Array{ {Ident("binutils")}, {Ident("mpc")}, {Ident("zlib")}, {Ident("libucontext")}, {Ident("kernel-headers")}, }}}, }, }}, 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) { t.Parallel() p, err := Parse(strings.NewReader(tc.data)) if !reflect.DeepEqual(p, tc.want) { t.Errorf("Parse: %#v, want %#v", p, tc.want) } if !reflect.DeepEqual(err, tc.err) { t.Errorf("Parse: error = %v, want %v", err, tc.err) } }) } } func BenchmarkParse(b *testing.B) { r := strings.NewReader(gccSample) for b.Loop() { if _, err := Parse(r); err != nil { b.Fatal(err) } b.StopTimer() r.Reset(gccSample) b.StartTimer() } }