Skip to content

Commit 7b7dfdd

Browse files
olwangclaude
andcommitted
refactor(selfhost): extract shared scan.rss prelude; dedupe tokenizer across lexer/parser/check
Extract the triplicated tokenizer/scanner and the byte-identical declaration-scanning helpers into selfhost/scan.rss, a shared prelude the test harness prepends to each tool at compile time (single-file VM has no module import). Unify on one Tok struct (with text field) and a single tokenize(source) -> fresh List<Tok> (validates SH-019). The lexer driver now runs tokenize and prints the canonical dump; parser and checker keep only their unique logic. Net 1143-line reduction; lexer/parser/checker parity all 551/551, full lib suite 280 passing. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 8b3b02b commit 7b7dfdd

5 files changed

Lines changed: 1080 additions & 2213 deletions

File tree

crates/rsscript/src/selfhost_parity.rs

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -115,12 +115,26 @@ fn parse_line(line: &str) -> Option<CanonTok> {
115115
})
116116
}
117117

118-
/// Compile `selfhost/lexer.rss` once for reuse across many inputs.
118+
/// Read a self-hosted tool source, prepended with the shared `scan.rss` prelude.
119+
/// The single-file VM model has no cross-file import, so the shared scanner is
120+
/// concatenated (scan.rss first, then a newline, then the tool file) and the
121+
/// combined program is compiled as one unit. `features: local` therefore appears
122+
/// exactly once (only in scan.rss).
123+
fn combined_tool_source(tool: &str) -> Result<String, String> {
124+
let dir = selfhost_dir();
125+
let scan_path = dir.join("scan.rss");
126+
let scan_src = std::fs::read_to_string(&scan_path)
127+
.map_err(|e| format!("cannot read {}: {e}", scan_path.display()))?;
128+
let tool_path = dir.join(tool);
129+
let tool_src = std::fs::read_to_string(&tool_path)
130+
.map_err(|e| format!("cannot read {}: {e}", tool_path.display()))?;
131+
Ok(format!("{scan_src}\n{tool_src}"))
132+
}
133+
134+
/// Compile `selfhost/lexer.rss` (with the shared prelude) once for reuse.
119135
fn compile_lexer() -> Result<RegVmExecutable, String> {
120-
let lexer_path = selfhost_dir().join("lexer.rss");
121-
let lexer_src = std::fs::read_to_string(&lexer_path)
122-
.map_err(|e| format!("cannot read {}: {e}", lexer_path.display()))?;
123-
reg_vm_compile_source("selfhost/lexer.rss", &lexer_src)
136+
let combined = combined_tool_source("lexer.rss")?;
137+
reg_vm_compile_source("selfhost/lexer.rss", &combined)
124138
.map_err(|e| format!("rss lexer failed to compile: {e:?}"))
125139
}
126140

@@ -335,10 +349,8 @@ fn parse_position_tier() -> bool {
335349
}
336350

337351
fn compile_parser() -> Result<RegVmExecutable, String> {
338-
let path = selfhost_dir().join("parser.rss");
339-
let src = std::fs::read_to_string(&path)
340-
.map_err(|e| format!("cannot read {}: {e}", path.display()))?;
341-
reg_vm_compile_source("selfhost/parser.rss", &src)
352+
let combined = combined_tool_source("parser.rss")?;
353+
reg_vm_compile_source("selfhost/parser.rss", &combined)
342354
.map_err(|e| format!("rss parser failed to compile: {e:?}"))
343355
}
344356

@@ -483,10 +495,8 @@ fn checker_oracle_codes(file: &str, source: &str) -> Vec<String> {
483495
}
484496

485497
fn compile_checker() -> Result<RegVmExecutable, String> {
486-
let path = selfhost_dir().join("check.rss");
487-
let src = std::fs::read_to_string(&path)
488-
.map_err(|e| format!("cannot read {}: {e}", path.display()))?;
489-
reg_vm_compile_source("selfhost/check.rss", &src)
498+
let combined = combined_tool_source("check.rss")?;
499+
reg_vm_compile_source("selfhost/check.rss", &combined)
490500
.map_err(|e| format!("rss checker failed to compile: {e:?}"))
491501
}
492502

0 commit comments

Comments
 (0)