# TinyVM Windows test suite (PowerShell implementation of test.sh) $ErrorActionPreference = "Stop" # detect compiler (prioritizing LLVM-MinGW with UCRT headers) $llvmMinGW = Get-ChildItem "$env:LOCALAPPDATA\Microsoft\WinGet\Packages\MartinStorsjo.LLVM-MinGW*\llvm-mingw*\bin" -ErrorAction SilentlyContinue if ($llvmMinGW) { $env:Path = "$($llvmMinGW[0].FullName);$env:Path" $cc = "clang" } elseif (Get-Command clang -ErrorAction SilentlyContinue) { $cc = "clang" } elseif (Get-Command gcc -ErrorAction SilentlyContinue) { $cc = "gcc" } else { Write-Error "No C compiler (clang or gcc) found in PATH." exit 1 } Write-Host "===============================================================================" -ForegroundColor Cyan Write-Host " TinyVM Windows Automated Test Suite " -ForegroundColor Cyan Write-Host "===============================================================================" -ForegroundColor Cyan Write-Host "Compiler: $cc" Write-Host "" $tempDir = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), "tinyvm_test_" + [System.Guid]::NewGuid().ToString("N")) New-Item -ItemType Directory -Path "$tempDir\s0", "$tempDir\s1" -Force | Out-Null $totalPassed = 0 $totalFailed = 0 function Report-Result($success, $name, $detail = "") { if ($success) { $script:totalPassed++ Write-Host " [OK] $name" -ForegroundColor Green } else { $script:totalFailed++ Write-Host " [FAIL] $name $detail" -ForegroundColor Red } } try { # --- 1. bootstrap & fixpoint --- Write-Host "=== 1. Bootstrap and Fixpoint Verification ===" -ForegroundColor Yellow & $cc -fwrapv -O1 -w src\tools\zc0.c -Isrc/core -o "$tempDir\zc0.exe" & $cc -fwrapv -O1 -w src\tools\mkgenome.c -Isrc/core -o "$tempDir\mkgenome.exe" Copy-Item src\core\core.c, src\core\io_win.c, src\core\memmap.h, src\core\fops.h, src\core\bops.h, src\core\stack.h -Destination "$tempDir\s0" Copy-Item src\core\core.c, src\core\io_win.c, src\core\memmap.h, src\core\fops.h, src\core\bops.h, src\core\stack.h -Destination "$tempDir\s1" cmd.exe /c "(type src\runtime\rt.zc & type src\compiler\compiler.zc) | $tempDir\zc0.exe > $tempDir\s0\genome.h" & $cc -fwrapv -O2 -w -DJTYPE=int "$tempDir\s0\core.c" "$tempDir\s0\io_win.c" -o "$tempDir\zy0.exe" $bzc = [IO.File]::ReadAllText("src\tools\build.zc") $rt = [IO.File]::ReadAllText("src\runtime\rt.zc") $cp = [IO.File]::ReadAllText("src\compiler\compiler.zc") $combined = $bzc + [char]96 + $rt + $cp [IO.File]::WriteAllBytes("$tempDir\in.tmp", [Text.Encoding]::ASCII.GetBytes($combined)) $p1 = Start-Process -FilePath "$tempDir\zy0.exe" -NoNewWindow -PassThru -RedirectStandardInput "$tempDir\in.tmp" -RedirectStandardOutput "$tempDir\g1.bin" -Wait cmd.exe /c "$tempDir\mkgenome.exe < $tempDir\g1.bin > $tempDir\s1\genome.h" & $cc -fwrapv -O2 -w -DJTYPE=int "$tempDir\s1\core.c" "$tempDir\s1\io_win.c" -o "$tempDir\zy1.exe" $p2 = Start-Process -FilePath "$tempDir\zy1.exe" -NoNewWindow -PassThru -RedirectStandardInput "$tempDir\in.tmp" -RedirectStandardOutput "$tempDir\g2.bin" -Wait $g1 = [IO.File]::ReadAllBytes("$tempDir\g1.bin") $g2 = [IO.File]::ReadAllBytes("$tempDir\g2.bin") $isFixpoint = [System.Linq.Enumerable]::SequenceEqual($g1, $g2) Report-Result $isFixpoint "Fixpoint: compiler compiles itself to identical $($g1.Length / 8) words" $ghRepo = (Get-FileHash "build\core\genome.h").Hash $ghGen = (Get-FileHash "$tempDir\s1\genome.h").Hash Report-Result ($ghRepo -eq $ghGen) "genome.h is up to date" # --- 2. compile test executables --- Write-Host "`n=== 2. Compiling Test Executables ===" -ForegroundColor Yellow & $cc -fwrapv -O2 -w -DJTYPE=int src\core\core.c src\core\io_win.c -Isrc/core -o "$tempDir\zy.exe" & $cc -fwrapv -O2 -w src\micro\micro.c src\micro\port_win.c -Isrc/core -Isrc/micro -o "$tempDir\zym.exe" & $cc -fwrapv -O2 -w src\tools\genmap.c -Isrc/core -o "$tempDir\genmap.exe" # --- 3. feature tests on zy.exe --- Write-Host "`n=== 3. Running Feature Tests on VM Core (zy.exe) ===" -ForegroundColor Yellow $featureTests = @( @{ name = "hello"; file = "examples\hello.zc"; expect = "hello, world!" }, @{ name = "factorial"; file = "examples\fact.zc"; expect = "1101110101111100000000`n3628800`n" }, @{ name = "self-patching code"; file = "examples\selfpatch.zc"; expect = "ABC" }, @{ name = "ownership frees everything"; file = "examples\lists.zc"; expect = "[0 1 4 9 16 25]`nlive: 7`nafter the block: 0`n" }, @{ name = "typed example"; file = "examples\typed.zc"; expect = "last: 4`npoint is 2 words: x int, y int`nvec is 3 words: len int, cap int, items point**`npush is bool(vec*, T.d)`nthe heap says v is a vec, and v.items[0] is a point`n" }, @{ name = "enhancement example"; file = "examples\wiring.zc"; expect = "16 1234 25 4/9" }, @{ name = "o3 prime sieve"; file = "examples\primes.zc"; expect = "widest gap: 72, after 31397" }, @{ name = "language basics"; file = "tests\programs\basics.zc"; expect = "285 610 42 512`nhey 65 -3 -2 1024 -8`n12456`n757`n9 100-101`n5`nok`n" }, @{ name = "u m d lifetimes"; file = "tests\programs\umd.zc"; expect = "100 6 0 15 99 5 5" }, @{ name = "u, m and d names"; file = "tests\programs\names.zc"; expect = "12 1 8" }, @{ name = "mark-sweep collector"; file = "tests\programs\gc.zc"; expect = "1000 1 900 1 199900" }, @{ name = "parse words and operator tables"; file = "tests\programs\meta.zc"; expect = "1024 19`nunless ok`nrrr`n15 42`n7`n42`n7`n144`n" }, @{ name = "compile-time eval and mnemonics"; file = "tests\programs\misc.zc"; expect = "45`n54321`nok`n" }, @{ name = "types, generics, reflection"; file = "tests\programs\types.zc"; expect = "12 5 2 15 56 42 7 14 3`nrect rect* list* list*.u(int) T(list*) fn(word.d)`na:point@0 b:point@2 tag:int@4 `npoint 1 1`n" }, @{ name = "typechecker accepts valid program"; file = "tests\programs\typeok.zc"; expect = "14" }, @{ name = "typechecker off by default"; file = "tests\programs\typeoff.zc"; expect = "6" }, @{ name = "virtual enhancements"; file = "tests\programs\enhance.zc"; expect = "100 0 343 3,2 5 -2 0" }, @{ name = "joins and paths"; file = "tests\programs\joins.zc"; expect = "0 0 5 80 8 3 9 0" }, @{ name = "buddy allocator"; file = "tests\programs\buddy.zc"; expect = "131072 262144 1 0" }, @{ name = "borrow move"; file = "tests\programs\borrowmove.zc"; expect = "6" }, @{ name = "alias tracking"; file = "tests\programs\alias.zc"; expect = "11 22 4 8" }, @{ name = "quarantine stale alias"; file = "tests\programs\quarantine.zc"; expect = "0 1234" }, @{ name = "recycle dropped block"; file = "tests\programs\recycle.zc"; expect = "1 0 1" }, @{ name = "roots collector mark"; file = "tests\programs\roots.zc"; expect = "1 0" }, @{ name = "bump arena"; file = "tests\programs\arena.zc"; expect = "4 7 0 1 1" }, @{ name = "do/while, enum, cast"; file = "tests\programs\csyn.zc"; expect = "13 0 5 6 0" }, @{ name = "include and string helpers"; file = "tests\programs\use_std.zc"; expect = "42 -7 1" }, @{ name = "stdlib arena and byte buffer"; file = "tests\programs\stdbuf.zc"; expect = "4 1 266 8" }, @{ name = "64-bit shifts"; file = "tests\programs\wide.zc"; expect = "1099511627776 1 1 1 -1" }, @{ name = "floating point"; file = "tests\programs\float.zc"; expect = "4.000000 4.500000 3.000000 2.500000 2.500000 0.001500 0.100000 -0.125000 inf -inf nan 1 1 1 0 0 3 -3 3.000000 -2.000000 2" }, @{ name = "negative zero is false"; file = "tests\programs\ftruth.zc"; expect = "00111000110107" }, @{ name = "float types"; file = "tests\programs\ftype.zc"; expect = "3.500000 10.500000 3.500000 4.250000 5.250000 4.250000 6.500000 2 4 4.250000 8.500000 1 4" }, @{ name = "o3 lifts float ops"; file = "tests\programs\fo3.zc"; expect = "5997 5997 4.000000 nan 0 3.250000 1 0 0" }, @{ name = "float formatting"; file = "tests\programs\ftext.zc"; expect = "1e+308 1e-10 -0 inf -inf nan 0.1 1.50 1.250e+0 100000000000000000000.000000 1e+40 1111111111" }, @{ name = "byte addressing"; file = "tests\programs\bytes.zc"; expect = "17 34 43981 16909060 1 205 171 -3674937295934316015 1108152156331 0 0 30768`nABC" }, @{ name = "byte strings"; file = "tests\programs\strings.zc"; expect = "hi! 3 3 104 i! 1 1 1 -42 42 -42 11 1 -9223372036854775808 0 1 3 10 1 1 1 W ab" }, @{ name = "shared cons cells"; file = "tests\programs\share.zc"; expect = "1 2 1 4 1 1 1 1 42 1 1 1 6 0" }, @{ name = "memdebug shared cell"; file = "tests\programs\share_dbg.zc"; expect = "7 1" }, @{ name = "growable and fixed arrays"; file = "tests\programs\arrays.zc"; expect = "100 128 9801 9801 99 3 4 12 19 8 19 28`n0`n" }, @{ name = "o3 regions"; file = "tests\programs\o3.zc"; expect = "3142 42 43 9 57 98781 520625 10192 -18782 1152" }, @{ name = "o3 scopes"; file = "tests\programs\o3scopes.zc"; expect = "116" }, @{ name = "o3 fat data"; file = "tests\programs\o3fat.zc"; expect = "node node 3 box node int[0]" }, @{ name = "o3 direct calls"; file = "tests\programs\o3call.zc"; expect = "0 0 4 23 55 node" }, @{ name = "bounds check optimization"; file = "tests\programs\bounds.zc"; expect = "55 55 8" } ) foreach ($t in $featureTests) { $out = (cmd.exe /c "$tempDir\zy.exe < $($t.file) 2>&1") -join "`n" $normOut = $out.Trim().Replace("`r`n", "`n") $normExp = $t.expect.Trim().Replace("`r`n", "`n") $match = ($normOut -eq $normExp) -or ($normOut.Contains($normExp)) Report-Result $match $t.name } # --- 4. microcore tests --- Write-Host "`n=== 4. Running Microcore Tests (zym.exe) ===" -ForegroundColor Yellow $microTests = @( @{ name = "microcore: hello"; file = "examples\hello.zc"; expect = "hello, world!" }, @{ name = "microcore: factorial"; file = "examples\fact.zc"; expect = "3628800" }, @{ name = "microcore: primes"; file = "examples\primes.zc"; expect = "widest gap: 72" }, @{ name = "microcore: basics"; file = "tests\programs\basics.zc"; expect = "ok" }, @{ name = "microcore: umd"; file = "tests\programs\umd.zc"; expect = "100 6 0 15 99 5 5" } ) foreach ($t in $microTests) { $out = (cmd.exe /c "$tempDir\zym.exe < $($t.file) 2>&1") -join "`n" $normOut = $out.Trim().Replace("`r`n", "`n") $match = $normOut.Contains($t.expect) Report-Result $match $t.name } # --- 5. error rejection suites --- Write-Host "`n=== 5. Error Rejection Suites ===" -ForegroundColor Yellow foreach ($suite in @(@{ dir = "tests\errors"; name = "Lifetime errors" }, @{ dir = "tests\typeerrors"; name = "Typechecker errors" }, @{ dir = "tests\langerrors"; name = "Language/o3 errors" })) { $files = Get-ChildItem "$($suite.dir)\*.zc" $pass = 0 $fail = 0 foreach ($f in $files) { $want = ((Get-Content $f.FullName -TotalCount 1) -replace '^// expect:\s*').Trim() $got = (Get-Content $f.FullName -Raw | & "$tempDir\zy.exe" 2>&1 | Select-Object -First 1) if ($got -and $got.Contains($want)) { $pass++ } else { $fail++ Write-Host " Failed $($f.Name): got [$got], want [$want]" -ForegroundColor DarkYellow } } Report-Result ($fail -eq 0) "$($suite.name): $pass rejected as expected" } # --- 6. tool verification --- Write-Host "`n=== 6. Memory Map Tool Verification (genmap.exe) ===" -ForegroundColor Yellow cmd.exe /c "`"$tempDir\genmap.exe`" --preset default --check >nul 2>nul" Report-Result ($LASTEXITCODE -eq 0) "genmap default preset check passed" cmd.exe /c "`"$tempDir\genmap.exe`" --preset small -o `"$tempDir\small`" >nul 2>nul" Report-Result ($LASTEXITCODE -eq 0) "genmap small preset generated" # --- 7. ZC language server (zclsp.exe) --- Write-Host "`n=== 7. ZC Language Server (zclsp.exe) ===" -ForegroundColor Yellow # the server is the real compiler in check mode plus src\lsp\server.zc, on its own map $lspDir = "$tempDir\lsp" New-Item -ItemType Directory -Force "$lspDir\src" | Out-Null # two passes: compile once to learn the genome length, then lay the map out again for that length $lspSrc = "(type `"$lspDir\map\rt.zc`" & type `"$lspDir\map\compiler.zc`" & type src\lsp\server.zc)" cmd.exe /c "`"$tempDir\genmap.exe`" --preset lsp --grow -o `"$lspDir\map`" >nul 2>nul" & $cc -fwrapv -O1 -w src\tools\zc0.c "-I$lspDir\map" -Isrc/core -o "$lspDir\zc0.exe" cmd.exe /c "$lspSrc | `"$lspDir\zc0.exe`" > `"$lspDir\genome.h`"" $glen = [int]((Select-String -Path "$lspDir\genome.h" -Pattern '#define\s+GLEN\s+(\d+)' | Select-Object -First 1).Matches[0].Groups[1].Value) $slot = [int]([math]::Ceiling($glen / 4096.0) * 4096) Remove-Item -Recurse -Force "$lspDir\map" cmd.exe /c "`"$tempDir\genmap.exe`" --preset lsp --grow --genome $slot -o `"$lspDir\map`" >nul 2>nul" & $cc -fwrapv -O1 -w src\tools\zc0.c "-I$lspDir\map" -Isrc/core -o "$lspDir\zc0.exe" cmd.exe /c "$lspSrc | `"$lspDir\zc0.exe`" > `"$lspDir\src\lsp_genome.h`"" foreach ($f in 'core.c','io_win.c','fops.h','bops.h','stack.h') { Copy-Item "src\core\$f" "$lspDir\src\" } Copy-Item "$lspDir\map\memmap.h" "$lspDir\src\" & $cc -fwrapv -O2 -w -DJTYPE=int -DUSE_LSP_GENOME "-I$lspDir\src" "$lspDir\src\core.c" "$lspDir\src\io_win.c" -o "$lspDir\zclsp.exe" Report-Result (Test-Path "$lspDir\zclsp.exe") "zclsp.exe compiled" # CLI check: a clean file $lspHello = (cmd.exe /c "`"$lspDir\zclsp.exe`" < examples\hello.zc 2>&1") -join "`n" Report-Result ($lspHello.Trim() -eq "no errors") "zclsp: a clean program (hello.zc)" # CLI check: the compiler's own message $lspErr = (cmd.exe /c "`"$lspDir\zclsp.exe`" < tests\errors\back_to_m.zc 2>&1") -join "`n" Report-Result ($lspErr.Contains("can't be recast back to m")) "zclsp: lifetime error detection (back_to_m.zc)" # CLI check: the compiler's optimization hints $lspOpt = (cmd.exe /c "`"$lspDir\zclsp.exe`" < examples\opt_test.zc 2>&1") -join "`n" $hints = @("is never read", "is never reassigned; make it a const", "is a shift; write << 3", "the number 1234 has no name", "new inside a loop", "stack buffer", "handed off once") foreach ($h in $hints) { Report-Result ($lspOpt.Contains($h)) "zclsp hint: $h" } # methods with this, and closures, check clean $lspStruct = (cmd.exe /c "`"$lspDir\zclsp.exe`" < examples\struct_closure_opt.zc 2>&1") -join "`n" Report-Result (-not $lspStruct.Contains(": error:")) "zclsp: methods and closures check clean" # protocol and corpus tests if (Get-Command node -ErrorAction SilentlyContinue) { $lspOut = (node tests\drivers\lsp_test.js "$lspDir\zclsp.exe" 2>&1) -join "`n" $lspCode = $LASTEXITCODE Report-Result ($lspCode -eq 0) "zclsp: protocol and corpus tests" if ($lspCode -ne 0) { Write-Host $lspOut } } else { Write-Host " [SKIP] Node.js not detected in PATH, skipping lsp protocol tests." -ForegroundColor DarkYellow } # --- 8. JavaScript / TypeScript vm runtime (build/js/tinyvm.js) --- Write-Host "`n=== 8. JavaScript VM Runtime (node build/js/tinyvm.js) ===" -ForegroundColor Yellow $nodeCmd = Get-Command node -ErrorAction SilentlyContinue if ($nodeCmd) { $jsHello = (node build\js\tinyvm.js examples\hello.zc 2>&1) -join "`n" Report-Result ($jsHello.Trim() -eq "hello, world!") "JS VM: hello.zc" $jsFact = (node build\js\tinyvm.js examples\fact.zc 2>&1) -join "`n" Report-Result ($jsFact.Contains("3628800")) "JS VM: fact.zc" $jsPrimes = (node build\js\tinyvm.js examples\primes.zc 2>&1) -join "`n" Report-Result ($jsPrimes.Contains("widest gap: 72")) "JS VM: primes.zc" $jsMandel = (node build\js\tinyvm.js examples\mandel.zc 2>&1) -join "`n" Report-Result ($jsMandel.Contains("*") -or $jsMandel.Contains(".")) "JS VM: mandel.zc (custom fmul enhancement)" } else { Write-Host " [SKIP] Node.js not detected in PATH, skipping JS tests." -ForegroundColor DarkYellow } } finally { Start-Sleep -Milliseconds 200 Remove-Item -Recurse -Force -ErrorAction SilentlyContinue $tempDir } Write-Host "`n===============================================================================" -ForegroundColor Cyan $summaryColor = if ($totalFailed -eq 0) { "Green" } else { "Red" } Write-Host "Test Results: $totalPassed passed, $totalFailed failed" -ForegroundColor $summaryColor Write-Host "===============================================================================" -ForegroundColor Cyan if ($totalFailed -gt 0) { exit 1 } else { exit 0 }