Skip to content

Commit a1e3d23

Browse files
committed
New rust driver for forth tests
1 parent cb11e2f commit a1e3d23

2 files changed

Lines changed: 169 additions & 0 deletions

File tree

forth/Cargo.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
[package]
2+
name = "harness"
3+
version = "0.1.0"
4+
edition = "2024"
5+
6+
[dependencies]
7+
rstest = "0.26.1"

forth/src/main.rs

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
const PREAMBLE: &str = r#": / /MOD SWAP DROP ;
2+
: '\n' 10 ;
3+
: BL 32 ;
4+
: CR '\n' EMIT ;
5+
: SPACE BL EMIT ;
6+
: NEGATE 0 SWAP - ;
7+
: TRUE 1 ;
8+
: FALSE 0 ;
9+
: NOT 0= ;
10+
: LITERAL IMMEDIATE ' LIT , , ;
11+
: ':' [ CHAR : ] LITERAL ;
12+
: ';' [ CHAR ; ] LITERAL ;
13+
: '"' [ CHAR " ] LITERAL ;
14+
: 'A' [ CHAR A ] LITERAL ;
15+
: '0' [ CHAR 0 ] LITERAL ;
16+
: '-' [ CHAR - ] LITERAL ;
17+
: [COMPILE] IMMEDIATE WORD FIND >CFA , ;
18+
: RECURSE IMMEDIATE LATEST @ >CFA , ;
19+
: IF IMMEDIATE ' 0BRANCH , HERE @ 0 , ;
20+
: THEN IMMEDIATE DUP HERE @ SWAP - SWAP ! ;
21+
: ELSE IMMEDIATE ' BRANCH , HERE @ 0 , SWAP DUP HERE @ SWAP - SWAP ! ;
22+
: BEGIN IMMEDIATE HERE @ ;
23+
: AGAIN IMMEDIATE ' BRANCH , HERE @ - , ;
24+
: WHILE IMMEDIATE ' 0BRANCH , HERE @ 0 , ;
25+
: REPEAT IMMEDIATE ' BRANCH , SWAP HERE @ - , DUP HERE @ SWAP - SWAP ! ;
26+
: NIP SWAP DROP ;
27+
: PICK 1+ 8 * DSP@ + @ ;
28+
: SPACES BEGIN DUP 0> WHILE SPACE 1- REPEAT DROP ;
29+
: U. BASE @ /MOD ?DUP IF RECURSE THEN DUP 10 < IF '0' ELSE 10 - 'A' THEN + EMIT ;
30+
: .S DSP@ BEGIN DUP S0 @ < WHILE DUP @ U. 8+ SPACE REPEAT DROP ;
31+
: UWIDTH BASE @ / ?DUP IF RECURSE 1+ ELSE 1 THEN ;
32+
: U.R SWAP DUP UWIDTH ROT SWAP - SPACES U. ;
33+
: .R SWAP DUP 0< IF NEGATE 1 SWAP ROT 1- ELSE 0 SWAP ROT THEN SWAP DUP
34+
UWIDTH ROT SWAP - SPACES SWAP IF '-' EMIT THEN U. ;
35+
: . 0 .R SPACE ;
36+
: U. U. SPACE ;
37+
: WITHIN -ROT OVER <= IF > IF TRUE ELSE FALSE THEN ELSE 2DROP FALSE THEN ;
38+
: ALIGNED 7 + -8 AND ;
39+
: ALIGN HERE @ ALIGNED HERE ! ;
40+
: C, HERE @ C! 1 HERE +! ;
41+
: S" IMMEDIATE STATE @ IF
42+
' LITSTRING , HERE @ 0 , BEGIN KEY DUP '"' <> WHILE
43+
C, REPEAT DROP DUP HERE @ SWAP - 8- SWAP ! ALIGN ELSE
44+
HERE @ BEGIN KEY DUP '"' <> WHILE OVER C! 1+ REPEAT DROP HERE @ - HERE @ SWAP THEN ;
45+
: ." IMMEDIATE STATE @ IF [COMPILE] S" ' TELL , ELSE BEGIN KEY DUP '"' = IF
46+
DROP EXIT THEN EMIT AGAIN THEN ;
47+
: CELLS 8 * ;
48+
: ID. 8+ DUP C@ F_LENMASK AND BEGIN DUP 0> WHILE SWAP 1+ DUP C@ EMIT SWAP 1- REPEAT
49+
2DROP ;
50+
: ?IMMEDIATE 8+ C@ F_IMMED AND ;
51+
: CASE IMMEDIATE 0 ;
52+
: OF IMMEDIATE ' OVER , ' = , [COMPILE] IF ' DROP , ;
53+
: ENDOF IMMEDIATE [COMPILE] ELSE ;
54+
: ENDCASE IMMEDIATE ' DROP , BEGIN ?DUP WHILE [COMPILE] THEN REPEAT ;
55+
: CFA> LATEST @ BEGIN ?DUP WHILE 2DUP >CFA = IF NIP EXIT THEN @ REPEAT DROP 0 ;
56+
: SEE WORD FIND HERE @ LATEST @ BEGIN 2 PICK OVER <> WHILE NIP DUP @ REPEAT DROP SWAP
57+
':' EMIT SPACE DUP ID. SPACE DUP ?IMMEDIATE IF ." IMMEDIATE " THEN >DFA
58+
BEGIN 2DUP > WHILE DUP @
59+
CASE
60+
' LIT OF 8+ DUP @ . ENDOF
61+
' LITSTRING OF [ CHAR S ] LITERAL EMIT '"' EMIT SPACE 8+ DUP @ SWAP 8+
62+
SWAP 2DUP TELL '"' EMIT SPACE + ALIGNED 8- ENDOF
63+
' 0BRANCH OF ." 0BRANCH ( " 8+ DUP @ . ." ) " ENDOF
64+
' BRANCH OF ." BRANCH ( " 8+ DUP @ . ." ) " ENDOF
65+
' ' OF [ CHAR ' ] LITERAL EMIT SPACE 8+ DUP CFA> ID. SPACE ENDOF
66+
' EXIT OF 2DUP 8+ <> IF ." EXIT " THEN ENDOF
67+
DUP CFA> ID. SPACE
68+
ENDCASE
69+
8+
70+
REPEAT
71+
';' EMIT CR 2DROP ;
72+
: ['] IMMEDIATE ' LIT , ;
73+
: EXCEPTION-MARKER RDROP 0 ;
74+
: CATCH DSP@ 8+ >R ' EXCEPTION-MARKER 8+ >R EXECUTE ;
75+
: THROW ?DUP IF RSP@ BEGIN DUP R0 8- < WHILE DUP @ ' EXCEPTION-MARKER 8+ = IF
76+
8+ RSP! DUP DUP DUP R> 8- SWAP OVER ! DSP! EXIT THEN 8+ REPEAT
77+
DROP CASE 0 1- OF ." ABORTED" CR ENDOF ." UNCAUGHT THROW " DUP . CR ENDCASE QUIT THEN
78+
;
79+
: Z" IMMEDIATE STATE @ IF
80+
' LITSTRING , HERE @ 0 , BEGIN KEY DUP '"' <> WHILE
81+
HERE @ C! 1 HERE +! REPEAT 0 HERE @ C! 1 HERE +! DROP DUP
82+
HERE @ SWAP - 8- SWAP ! ALIGN ' DROP , ELSE
83+
HERE @ BEGIN KEY DUP '"' <> WHILE OVER C! 1+ REPEAT DROP 0 SWAP C! HERE @ THEN ;
84+
: STRLEN DUP BEGIN DUP C@ 0<> WHILE 1+ REPEAT SWAP - ;
85+
: ARGC (ARGC) @ ;
86+
: ARGV 1+ CELLS (ARGC) + @ DUP STRLEN ;
87+
: ENVIRON ARGC 2 + CELLS (ARGC) + ;
88+
"#;
89+
90+
#[cfg(test)]
91+
mod tests {
92+
use std::process::{Command, Stdio};
93+
use std::io::Write;
94+
use rstest::rstest;
95+
use super::PREAMBLE;
96+
97+
#[rstest]
98+
#[case("", "65 EMIT", "A")]
99+
#[case("", "777 65 EMIT", "A")]
100+
#[case("", "32 DUP + 1+ EMIT", "A")]
101+
#[case("", "16 DUP 2DUP + + + 1+ EMIT", "A")]
102+
#[case("", "8 DUP * 1+ EMIT", "A")]
103+
#[case("", "CHAR A EMIT", "A")]
104+
#[case("", ": SLOW WORD FIND >CFA EXECUTE ; 65 SLOW EMIT", "A")]
105+
#[case("", "3480240455236671827 DSP@ 8 TELL", "SYSCALL0")]
106+
#[case("", "3480240455236671827 DSP@ HERE @ 8 CMOVE HERE @ 8 TELL", "SYSCALL0")]
107+
#[case("", "13622 DSP@ 2 NUMBER DROP EMIT", "A")]
108+
#[case("", "64 >R RSP@ 1 TELL RDROP", "@")]
109+
#[case("", "64 DSP@ RSP@ SWAP C@C! RSP@ 1 TELL", "@")]
110+
#[case("", "64 >R 1 RSP@ +! RSP@ 1 TELL", "A")]
111+
#[case("", r#"
112+
: <BUILDS WORD CREATE DODOES , 0 , ;
113+
: DOES> R> LATEST @ >DFA ! ;
114+
: CONST <BUILDS , DOES> @ ;
115+
116+
65 CONST FOO
117+
FOO EMIT
118+
"#, "A")]
119+
#[case(PREAMBLE, "VERSION .", "47 ")]
120+
#[case(PREAMBLE, "CR", "\n")]
121+
#[case(PREAMBLE, "LATEST @ ID.", "ENVIRON")]
122+
#[case(PREAMBLE, "0 1 > . 1 0 > .", "0 -1 ")]
123+
#[case(PREAMBLE, "0 1 >= . 0 0 >= .", "0 -1 ")]
124+
#[case(PREAMBLE, "0 0<> . 1 0<> .", "0 -1 ")]
125+
#[case(PREAMBLE, "1 0<= . 0 0<= .", "0 -1 ")]
126+
#[case(PREAMBLE, "-1 0>= . 0 0>= .", "0 -1 ")]
127+
#[case(PREAMBLE, "0 0 OR . 0 -1 OR .", "0 -1 ")]
128+
#[case(PREAMBLE, "-1 -1 XOR . 0 -1 XOR .", "0 -1 ")]
129+
#[case(PREAMBLE, "-1 INVERT . 0 INVERT .", "0 -1 ")]
130+
#[case(PREAMBLE, "3 4 5 .S", "5 4 3 ")]
131+
#[case(PREAMBLE, "1 2 3 4 2SWAP .S", "2 1 4 3 ")]
132+
#[case(PREAMBLE, "F_IMMED F_HIDDEN .S", "32 128 ")]
133+
#[case(PREAMBLE, ": CFA@ WORD FIND >CFA @ ; CFA@ >DFA DOCOL = .", "-1 ")]
134+
#[case(PREAMBLE, "3 4 5 WITHIN .", "0 ")]
135+
#[case(PREAMBLE, r#"S" test" SWAP 1 SYS_WRITE SYSCALL3"#, "test")]
136+
#[case(PREAMBLE, "SEE >DFA", ": >DFA >CFA 8+ EXIT ;\n")]
137+
#[case(PREAMBLE, "SEE HIDE", ": HIDE WORD FIND HIDDEN ;\n")]
138+
#[case(PREAMBLE, "SEE QUIT", ": QUIT R0 RSP! INTERPRET BRANCH ( -16 ) ;\n")]
139+
fn test_wait_with_output(
140+
#[case] preamble: &str,
141+
#[case] input: &str,
142+
#[case] expected: &str,
143+
#[values("./4th", "./5th.ll")] program: &str,
144+
) {
145+
let mut child = Command::new(program)
146+
.stdin(Stdio::piped())
147+
.stdout(Stdio::piped())
148+
.stderr(Stdio::null())
149+
.spawn()
150+
.expect("Failed to spawn command");
151+
152+
child.stdin.take()
153+
.unwrap()
154+
.write_all(format!("{} {}\n", preamble, input).as_bytes())
155+
.expect("Failed to write to stdin");
156+
157+
let output = child.wait_with_output().expect("Failed to read stdout");
158+
159+
assert!(output.status.success(), "Command failed with status: {:?}", output.status);
160+
assert_eq!(output.stdout, expected.as_bytes(), "Mismatch for: {}", input);
161+
}
162+
}

0 commit comments

Comments
 (0)