|
21 | 21 | // to Swedish, and a NEW sound added to a .tex but not to codeStr is caught. |
22 | 22 | // |
23 | 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. |
| 24 | +// envs (Code/CodeSmall/lstlisting) the mirror rewrites in .tex — skipping \ifswedish-clamped code. And a |
| 25 | +// PHASE-2 REPL GATE: REPL* transcripts split on `scala>` prompts, inputs concatenated into an object and |
| 26 | +// compiled under the same regression rule (SV/EN classification is symmetric, so a mis-split only skips). |
| 27 | +// See those sections below. |
26 | 28 | // |
27 | 29 | // scala-cli run autotranslate/scratch/verify-mirror-examples.scala -- <introprog-root> |
28 | 30 | // (exit 0 = clean, exit 1 = at least one regression or an untranslated ratified code-string) |
|
123 | 125 | println(s"--- phase-1 SKIPPED (${inSkip.size}) — rewritten inline envs that compile in NEITHER language (not standalone) ---") |
124 | 126 | inSkip.foreach(s => println(s" skip $s")) |
125 | 127 |
|
126 | | - if regressions.nonEmpty || leaks.nonEmpty || inRegressions.nonEmpty then sys.exit(1) |
| 128 | + // ---- PHASE-2 INLINE REPL COMPILE GATE (#951) ---- |
| 129 | + // REPL transcripts: split on `scala>` prompts (prompt-stripped input + its indented continuation lines; OUTPUT |
| 130 | + // lines — result echoes `val x: T = …`, `resN`, error `|`/`-- Error`, blanks — are dropped), concatenate a |
| 131 | + // transcript's inputs into one `object` body and compile SV vs EN with the phase-1 regression rule. Line |
| 132 | + // classification is IDENTICAL for the SV and EN transcripts (they have parallel structure), so a mis-split can |
| 133 | + // only SKIP a transcript — never cause a false regression. Many transcripts reference defs from a neighbouring |
| 134 | + // Code env and so compile in neither language (self-skip). Clamp-aware + changed-only + per-file overrides. |
| 135 | + val replEnvs = Set("REPL", "REPLnonum", "REPLsmall") |
| 136 | + val promptLineRe = raw"^[ \t]*scala>[ ]?(.*)".r |
| 137 | + val replOutRe = raw"^[ \t]*(val res\d|res\d+:|[|]|\d+ *[|]|-- (Error|Warning)|<console>|\^).*".r |
| 138 | + def replProgram(bodyStr: String): String = |
| 139 | + val prog = collection.mutable.ArrayBuffer[String]() |
| 140 | + var inInput = false |
| 141 | + for line <- bodyStr.split("\n", -1) do |
| 142 | + promptLineRe.findFirstMatchIn(line) match |
| 143 | + case Some(pm) => prog += pm.group(1); inInput = true // `scala> …` input line (prompt stripped) |
| 144 | + case None => |
| 145 | + val t = line.trim |
| 146 | + val isOutput = t.isEmpty || replOutRe.findFirstMatchIn(line).isDefined || |
| 147 | + t.matches("(val|var)\\s+\\w+:.*=.*") // result-echo `val x: T = …` |
| 148 | + if inInput && !isOutput then prog += line // continuation of a multi-line input |
| 149 | + else inInput = false |
| 150 | + prog.mkString("\n") |
| 151 | + def wrapObj(prog: String): String = |
| 152 | + "object ReplWrap:\n" + prog.split("\n", -1).map(" " + _).mkString("\n") |
| 153 | + var rpChecked = 0; var rpSkipped = 0 |
| 154 | + val rpRegressions = collection.mutable.ArrayBuffer[String]() |
| 155 | + val rpOk = collection.mutable.ArrayBuffer[String]() // file:line of each OK transcript (for --list) |
| 156 | + val rpSkip = collection.mutable.ArrayBuffer[String]() // file:line of each skipped transcript (for --list) |
| 157 | + for f <- texFiles do |
| 158 | + val tex = os.read(f) |
| 159 | + val rel = f.relativeTo(root).toString |
| 160 | + val extraId = CodeGlossary.overridesFor(rel) |
| 161 | + if !CodeGlossary.isOptedOut(rel) then |
| 162 | + val clampRanges = Latex.ifswedishRanges(tex) |
| 163 | + def clamped(pos: Int): Boolean = clampRanges.exists((a, b) => pos >= a && pos < b) |
| 164 | + for m <- envRe.findAllMatchIn(tex) if replEnvs(m.group(1)) && !clamped(m.start) do |
| 165 | + val body = stripLstOpt(m.group(2)) // strip a leading [numbers=none] optional arg |
| 166 | + val en = CodeGlossary.renderCodeIds(body, extraId) |
| 167 | + if en != body then |
| 168 | + val loc = s"$rel:${lineOf(tex, m.start)}" |
| 169 | + val enProg = replProgram(en) |
| 170 | + if enProg.trim.isEmpty then { rpSkipped += 1; rpSkip += s"$loc (no scala> input)" } |
| 171 | + else if compiles(wrapObj(enProg), "ReplWrap.scala") then { rpChecked += 1; rpOk += loc } |
| 172 | + else if compiles(wrapObj(replProgram(body)), "ReplWrap.scala") then |
| 173 | + rpRegressions += loc |
| 174 | + println(s" REPL REGRESSION: $loc — transcript compiles in Swedish but NOT after rename") |
| 175 | + else { rpSkipped += 1; rpSkip += loc } |
| 176 | + println(s"\n=== inline .tex REPL compile gate (phase 2): $rpChecked ok, $rpSkipped skipped (not standalone), ${rpRegressions.size} REGRESSIONS ===") |
| 177 | + if rpRegressions.nonEmpty then println("FAIL — REPL translation broke compilation in: " + rpRegressions.mkString(", ")) |
| 178 | + else println("PASS — every rewritten, self-contained REPL transcript still compiles.") |
| 179 | + if listMode then |
| 180 | + println(s"\n--- phase-2 OK (${rpOk.size}) — rewritten REPL transcripts that compile after rename ---") |
| 181 | + rpOk.foreach(s => println(s" ok $s")) |
| 182 | + println(s"--- phase-2 SKIPPED (${rpSkip.size}) — REPL transcripts that compile in NEITHER language / no input ---") |
| 183 | + rpSkip.foreach(s => println(s" skip $s")) |
| 184 | + |
| 185 | + if regressions.nonEmpty || leaks.nonEmpty || inRegressions.nonEmpty || rpRegressions.nonEmpty then sys.exit(1) |
0 commit comments