1use vihaco_parser::BareToken;
5
6#[derive(Debug, Clone, Copy, PartialEq, vihaco_parser_derive::Parse)]
7#[syntax_class(type)]
8pub enum SurfaceType {
9 #[pattern = "`undef`"]
10 Undefined,
11 #[pattern = "`str`"]
12 String,
13 #[pattern = "`bool`"]
14 Bool,
15 #[pattern = "`i64`"]
16 I64,
17 #[pattern = "`i32`"]
18 I32,
19 #[pattern = "`u32`"]
20 U32,
21 #[pattern = "`u64`"]
22 U64,
23 #[pattern = "`f64`"]
24 F64,
25 #[pattern = "`f32`"]
26 F32,
27 #[pattern = "`fn_ref`"]
28 FunctionRef,
29 #[pattern = "`heap_ref`"]
30 HeapRef,
31}
32
33#[derive(Debug, Clone, PartialEq, Eq, vihaco_parser_derive::Parse)]
34#[syntax_class(value)]
35pub enum SurfaceValue {
36 #[pattern = "$0"]
37 Quoted(vihaco_parser::QuotedString),
38 #[pattern = "$0"]
39 Bare(BareToken),
40}
41
42#[cfg(test)]
43#[allow(clippy::approx_constant)]
44mod parse_tests {
45 use super::SurfaceType;
46 use crate::SurfaceInstruction;
47 use chumsky::Parser as _;
48 use vihaco_parser::Parse;
49
50 fn parse(input: &str) -> SurfaceInstruction {
51 SurfaceInstruction::parser()
52 .parse(input)
53 .into_result()
54 .unwrap_or_else(|e| panic!("parse({input:?}) failed: {e:?}"))
55 }
56
57 fn parse_type(input: &str) -> SurfaceType {
58 SurfaceType::parser()
59 .parse(input)
60 .into_result()
61 .unwrap_or_else(|e| panic!("parse_type({input:?}) failed: {e:?}"))
62 }
63
64 macro_rules! assert_parses {
65 ($input:literal, $pattern:pat $(if $guard:expr)?) => {
66 assert!(
67 matches!(parse($input), $pattern $(if $guard)?),
68 "input {:?} parsed to the wrong variant or operands",
69 $input
70 );
71 };
72 }
73
74 #[test]
75 fn parses_unit_variants() {
76 assert_parses!("cpu.halt", SurfaceInstruction::Halt);
77 assert_parses!("cpu.print", SurfaceInstruction::Print);
78 assert_parses!("cpu.dup", SurfaceInstruction::Dup);
79 assert_parses!("cpu.breakpoint", SurfaceInstruction::Breakpoint);
80 assert_parses!(
81 "cpu.label @loop",
82 SurfaceInstruction::Label(name) if name.as_str() == "loop"
83 );
84 assert_parses!("cpu.func_start", SurfaceInstruction::FunctionStart);
85 assert_parses!("cpu.func_end", SurfaceInstruction::FunctionEnd);
86 assert_parses!("cpu.get_item", SurfaceInstruction::GetItem);
87 assert_parses!("cpu.not", SurfaceInstruction::Not);
88 assert_parses!("cpu.and", SurfaceInstruction::And);
89 assert_parses!("cpu.or", SurfaceInstruction::Or);
90 assert_parses!("cpu.xor", SurfaceInstruction::Xor);
91 assert_parses!("cpu.call_indirect", SurfaceInstruction::IndirectCall);
92 assert_parses!("cpu.ret 0", SurfaceInstruction::Return(0));
93 }
94
95 #[test]
96 fn parses_explicit_typed_variants() {
97 assert_parses!("cpu.add_i32", SurfaceInstruction::AddI32);
98 assert_parses!("cpu.add_f32", SurfaceInstruction::AddF32);
99 assert_parses!("cpu.load_i64 7", SurfaceInstruction::LoadI64(7));
100 assert_parses!("cpu.const_i64 42", SurfaceInstruction::ConstI64(_));
101 }
102
103 #[test]
104 fn parses_surface_types() {
105 for (input, expected) in [
106 ("undef", SurfaceType::Undefined),
107 ("str", SurfaceType::String),
108 ("bool", SurfaceType::Bool),
109 ("i64", SurfaceType::I64),
110 ("u32", SurfaceType::U32),
111 ("u64", SurfaceType::U64),
112 ("f64", SurfaceType::F64),
113 ("fn_ref", SurfaceType::FunctionRef),
114 ("heap_ref", SurfaceType::HeapRef),
115 ] {
116 assert_eq!(parse_type(input), expected, "input {input:?}");
117 }
118 }
119
120 #[cfg(any())]
121 #[test]
122 fn parses_typed_operations() {
123 assert_parses!("cpu.add i64", SurfaceInstruction::Add(SurfaceType::I64));
124 assert_parses!("cpu.sub f64", SurfaceInstruction::Sub(SurfaceType::F64));
125 assert_parses!("cpu.mul u32", SurfaceInstruction::Mul(SurfaceType::U32));
126 assert_parses!("cpu.div u64", SurfaceInstruction::Div(SurfaceType::U64));
127 assert_parses!("cpu.rem i64", SurfaceInstruction::Rem(SurfaceType::I64));
128 assert_parses!("cpu.neg f64", SurfaceInstruction::Neg(SurfaceType::F64));
129 assert_parses!("cpu.lt i64", SurfaceInstruction::Lt(SurfaceType::I64));
130 assert_parses!("cpu.eq i64", SurfaceInstruction::Eq(SurfaceType::I64));
131 assert_parses!("cpu.ne u64", SurfaceInstruction::Ne(SurfaceType::U64));
132 assert_parses!("cpu.gt u32", SurfaceInstruction::Gt(SurfaceType::U32));
133 assert_parses!("cpu.le f64", SurfaceInstruction::Le(SurfaceType::F64));
134 assert_parses!("cpu.ge f64", SurfaceInstruction::Ge(SurfaceType::F64));
135 assert_parses!(
136 "cpu.bitand i64",
137 SurfaceInstruction::BitAnd(SurfaceType::I64)
138 );
139 assert_parses!("cpu.bitor u64", SurfaceInstruction::BitOr(SurfaceType::U64));
140 assert_parses!(
141 "cpu.bitxor u32",
142 SurfaceInstruction::BitXor(SurfaceType::U32)
143 );
144 assert_parses!("cpu.shl u64", SurfaceInstruction::Shl(SurfaceType::U64));
145 assert_parses!("cpu.shr i64", SurfaceInstruction::Shr(SurfaceType::I64));
146 assert_parses!("cpu.rol u32", SurfaceInstruction::Rol(SurfaceType::U32));
147 assert_parses!("cpu.ror u64", SurfaceInstruction::Ror(SurfaceType::U64));
148 }
149
150 #[cfg(any())]
151 #[test]
152 fn parses_load_store() {
153 assert_parses!(
154 "cpu.load i64, 7",
155 SurfaceInstruction::Load(SurfaceType::I64, 7)
156 );
157 assert_parses!(
158 "cpu.store f64, 42",
159 SurfaceInstruction::Store(SurfaceType::F64, 42)
160 );
161 }
162
163 #[test]
164 fn parses_heap_alloc() {
165 assert_parses!("cpu.heap_alloc 5", SurfaceInstruction::HeapAlloc(5));
166 }
167
168 #[test]
169 fn parses_span() {
170 assert_parses!("cpu.span 0 1 2", SurfaceInstruction::Span(0, 1, 2));
171 }
172
173 #[cfg(any())]
174 #[test]
175 fn parses_const_numeric_flavors() {
176 assert_parses!(
177 "cpu.const i64, 42",
178 SurfaceInstruction::Const(SurfaceType::I64, value)
179 if value == SurfaceValue::Bare(BareToken("42".to_owned()))
180 );
181 assert_parses!(
182 "cpu.const u64, 7",
183 SurfaceInstruction::Const(SurfaceType::U64, value)
184 if value == SurfaceValue::Bare(BareToken("7".to_owned()))
185 );
186 assert_parses!(
187 "cpu.const u32, 3",
188 SurfaceInstruction::Const(SurfaceType::U32, value)
189 if value == SurfaceValue::Bare(BareToken("3".to_owned()))
190 );
191 assert_parses!(
192 "cpu.const f64, 3.14",
193 SurfaceInstruction::Const(SurfaceType::F64, value)
194 if value == SurfaceValue::Bare(BareToken("3.14".to_owned()))
195 );
196 assert_parses!(
197 "cpu.const bool, true",
198 SurfaceInstruction::Const(SurfaceType::Bool, value)
199 if value == SurfaceValue::Bare(BareToken("true".to_owned()))
200 );
201 }
202
203 #[cfg(any())]
204 #[test]
205 fn parses_const_quoted_string() {
206 assert_parses!(
207 "cpu.const str, \"hello world\"",
208 SurfaceInstruction::Const(SurfaceType::String, SurfaceValue::Quoted(value))
209 if value.as_str() == "hello world"
210 );
211 }
212
213 #[test]
214 fn parses_symbolic_control_flow() {
215 assert_parses!(
216 "cpu.br @body",
217 SurfaceInstruction::Branch(target) if target.as_str() == "body"
218 );
219 assert_parses!(
220 "cpu.cond_br @then, @else",
221 SurfaceInstruction::ConditionalBranch(then_target, else_target)
222 if then_target.as_str() == "then" && else_target.as_str() == "else"
223 );
224 assert_parses!(
225 "cpu.call 2, main",
226 SurfaceInstruction::Call(2, target) if target.as_str() == "main"
227 );
228 }
229
230 #[test]
231 fn rejects_malformed_quoted_value_instead_of_treating_it_as_bare() {
232 assert!(
233 SurfaceInstruction::parser()
234 .parse("cpu.const str, \"unterminated")
235 .has_errors()
236 );
237 }
238}