|
2 | 2 | //> using jvm 21 |
3 | 3 | //> using dep com.lihaoyi::os-lib:0.11.8 |
4 | 4 | //> using file ../CodeGlossary.scala |
| 5 | +//> using file ../Latex.scala |
5 | 6 |
|
6 | 7 | // COMPILE GATE for the mirror's example-code translation (#947/#948 + the personExample s-interp fix). |
7 | 8 | // "Only compilable code should pass": every example .scala/.java that CodeGlossary.renderCodeIds rewrites |
|
19 | 20 | // key surviving in a rendered Scala-code env / inline \code fails the gate — so a ratified sound can't regress |
20 | 21 | // to Swedish, and a NEW sound added to a .tex but not to codeStr is caught. |
21 | 22 | // |
| 23 | +// Plus a PHASE-1 INLINE .tex COMPILE GATE (#951): the same regression rule applied to the display Scala-code |
| 24 | +// envs (Code/CodeSmall/lstlisting) the mirror rewrites in .tex — skipping \ifswedish-clamped code and deferring |
| 25 | +// REPL transcripts to phase 2. See that section below. |
| 26 | +// |
22 | 27 | // scala-cli run autotranslate/scratch/verify-mirror-examples.scala -- <introprog-root> |
23 | 28 | // (exit 0 = clean, exit 1 = at least one regression or an untranslated ratified code-string) |
24 | 29 |
|
25 | 30 | @main def verifyMirrorExamples(rootStr: String): Unit = |
26 | 31 | val root = os.Path(rootStr, os.pwd) |
27 | 32 | val dir = root / "compendium" / "examples" // workspace/ is served by the hand-maintained workspace-en |
28 | | - val files = os.walk(dir).filter(f => os.isFile(f) && (f.ext == "scala" || f.ext == "java")) |
29 | | - .sortBy(_.toString) |
| 33 | + val files = |
| 34 | + if !os.exists(dir) then Seq.empty[os.Path] |
| 35 | + else os.walk(dir).filter(f => os.isFile(f) && (f.ext == "scala" || f.ext == "java")).sortBy(_.toString) |
30 | 36 | def compiles(code: String, name: String): Boolean = |
31 | 37 | val tmp = os.temp.dir(prefix = "mirror-gate") // keep the original filename (Java needs it) |
32 | 38 | os.write.over(tmp / name, code) |
|
70 | 76 | if leaks.nonEmpty then leaks.foreach(l => println(" LEAK: " + l)) |
71 | 77 | else println("PASS — every ratified code-string is translated in inline .tex code regions.") |
72 | 78 |
|
73 | | - if regressions.nonEmpty || leaks.nonEmpty then sys.exit(1) |
| 79 | + // ---- PHASE-1 INLINE .tex COMPILE GATE (#951) ---- |
| 80 | + // Gate the display Scala-code envs the mirror actually rewrites. For each Code/CodeSmall/lstlisting body that |
| 81 | + // is NOT inside an \ifswedish clamp (the mirror leaves clamped code alone, via Latex.ifswedishRanges) and that |
| 82 | + // renderCodeIds changes, compile the Swedish body AND the rendered English body. REGRESSION = Swedish compiles |
| 83 | + // but English does NOT. Skip if BOTH fail — many inline bodies aren't standalone-compilable (neighbour context |
| 84 | + // / signature-only / script style), and self-skipping those beats false alarms. REPL* transcripts are DEFERRED |
| 85 | + // to phase 2 (they need splitting on `scala>` prompts); Trace/Output aren't Scala. Uses the SAME per-file |
| 86 | + // overrides as the mirror (renderCodeIds(body, extraId)) so per-file-scoped clusters (e.g. ANIMAL) are gated. |
| 87 | + val phase1Envs = Set("Code", "CodeSmall", "lstlisting") |
| 88 | + def stripLstOpt(body: String): String = // \begin{lstlisting}[opts]: the [opts] can trail on line 1 |
| 89 | + body.replaceFirst("(?s)\\A[ \\t]*\\[[^\\]]*\\]", "") |
| 90 | + var inChecked = 0; var inSkipped = 0; var replDeferred = 0; var nonCode = 0 |
| 91 | + val inRegressions = collection.mutable.ArrayBuffer[String]() |
| 92 | + for f <- texFiles do |
| 93 | + val tex = os.read(f) |
| 94 | + val rel = f.relativeTo(root).toString |
| 95 | + val extraId = CodeGlossary.overridesFor(rel) |
| 96 | + if !CodeGlossary.isOptedOut(rel) then |
| 97 | + val clampRanges = Latex.ifswedishRanges(tex) |
| 98 | + def clamped(pos: Int): Boolean = clampRanges.exists((a, b) => pos >= a && pos < b) |
| 99 | + for m <- envRe.findAllMatchIn(tex) if !clamped(m.start) do |
| 100 | + val env = m.group(1) |
| 101 | + val body = if env == "lstlisting" then stripLstOpt(m.group(2)) else m.group(2) |
| 102 | + val en = CodeGlossary.renderCodeIds(body, extraId) |
| 103 | + if en != body then // only REWRITTEN bodies are gate-relevant |
| 104 | + if env.startsWith("REPL") then replDeferred += 1 // rewritten transcripts -> phase 2 |
| 105 | + else if !phase1Envs(env) then nonCode += 1 // Trace/Output — not compilable Scala |
| 106 | + else if compiles(en, "Inline.scala") then inChecked += 1 |
| 107 | + else if compiles(body, "Inline.scala") then |
| 108 | + inRegressions += s"$rel ($env)" |
| 109 | + println(s" INLINE REGRESSION: $rel ($env) — compiles in Swedish but NOT after rename") |
| 110 | + else inSkipped += 1 |
| 111 | + println(s"\n=== inline .tex compile gate (phase 1): $inChecked ok, $inSkipped skipped (not standalone), " + |
| 112 | + s"$replDeferred REPL deferred to phase 2, $nonCode Trace/Output not gated, ${inRegressions.size} REGRESSIONS ===") |
| 113 | + if inRegressions.nonEmpty then println("FAIL — inline translation broke compilation in: " + inRegressions.mkString(", ")) |
| 114 | + else println("PASS — every rewritten, self-contained inline Scala-code env still compiles.") |
| 115 | + |
| 116 | + if regressions.nonEmpty || leaks.nonEmpty || inRegressions.nonEmpty then sys.exit(1) |
0 commit comments