1mod lower;
36mod types;
37
38pub use lower::lower_functions;
39
40#[cfg(feature = "program-image-execution")]
41mod header;
42
43#[cfg(feature = "program-image-execution")]
44pub use header::HeaderDeclarations;
45
46#[cfg(feature = "program-image-execution")]
47pub use lower::lower_functions_with_metadata;
48
49pub mod parse;
50#[cfg(not(feature = "typed-declarations"))]
51pub mod resolve;
52
53#[cfg(feature = "typed-declarations")]
54pub use types::ParsedLabel;
55pub use types::{Param, ParsedFunction, ParsedModule};
56pub use vihaco_parser::SurfaceInstruction;
57
58pub use parse::{block_i64_flat, block_i64_pairs, skip};
59#[cfg(not(feature = "typed-declarations"))]
60pub use resolve::Resolve;
61
62#[cfg(test)]
63mod tests {
64 use super::*;
65 use chumsky::Parser as _;
66 use vihaco_parser::Parse;
67
68 #[derive(Debug, Clone, PartialEq, vihaco_parser_derive::Parse)]
71 #[syntax_class(instruction, head = "stub")]
72 enum StubInst {
73 Halt,
74 Print,
75 }
76
77 #[derive(Debug, Clone, PartialEq, vihaco_parser_derive::Parse)]
78 #[syntax_class(type)]
79 enum StubType {
80 #[pattern = "`unit`"]
81 Unit,
82 }
83
84 #[test]
85 fn parses_empty_function() {
86 let src = "fn @main() {}";
87 let f = ParsedFunction::<StubInst, StubType>::parser()
88 .parse(src)
89 .into_result()
90 .unwrap();
91 assert_eq!(f.name.as_str(), "main");
92 assert!(f.params.is_empty());
93 assert!(f.body.is_empty());
94 }
95
96 #[cfg(feature = "typed-declarations")]
97 #[test]
98 fn retains_label_positions_without_executable_slots() {
99 let f = ParsedFunction::<StubInst, StubType>::parser()
100 .parse("fn @main() { label @entry label @alias stub.halt label @exit }")
101 .into_result()
102 .unwrap();
103 assert_eq!(f.body, [StubInst::Halt]);
104 assert_eq!(
105 f.labels
106 .iter()
107 .map(|label| (label.name.as_str(), label.instruction_index))
108 .collect::<Vec<_>>(),
109 [("entry", 0), ("alias", 0), ("exit", 1)],
110 );
111 }
112
113 #[test]
114 fn parses_function_parameters() {
115 let src = "fn @main(input: unit, output: unit) {}";
116 let f = ParsedFunction::<StubInst, StubType>::parser()
117 .parse(src)
118 .into_result()
119 .unwrap();
120 assert_eq!(
121 f.params,
122 vec![
123 Param {
124 name: vihaco_parser::Ident("input".to_owned()),
125 ty: StubType::Unit,
126 },
127 Param {
128 name: vihaco_parser::Ident("output".to_owned()),
129 ty: StubType::Unit,
130 },
131 ]
132 );
133 }
134
135 #[test]
136 fn parses_function_parameters_with_whitespace() {
137 let src = "fn @main( input : unit , output:unit ) {}";
138 let f = ParsedFunction::<StubInst, StubType>::parser()
139 .parse(src)
140 .into_result()
141 .unwrap();
142 assert_eq!(f.params.len(), 2);
143 assert_eq!(f.params[0].name.as_str(), "input");
144 assert_eq!(f.params[1].name.as_str(), "output");
145 }
146
147 #[test]
148 fn rejects_parameter_without_type() {
149 let src = "fn @main(input) {}";
150 assert!(
151 ParsedFunction::<StubInst, StubType>::parser()
152 .parse(src)
153 .has_errors()
154 );
155 }
156
157 #[test]
158 fn parses_function_with_canonical_body() {
159 let src = "fn @main() {\n stub.halt\n stub.print\n stub.halt\n}";
160 let f = ParsedFunction::<StubInst, StubType>::parser()
161 .parse(src)
162 .into_result()
163 .unwrap();
164 assert_eq!(
165 f.body,
166 vec![StubInst::Halt, StubInst::Print, StubInst::Halt]
167 );
168 }
169
170 #[test]
171 fn rejects_unknown_instruction() {
172 let src = "fn @main() { foo bar 1 2.0 }";
173 assert!(
174 ParsedFunction::<StubInst, StubType>::parser()
175 .parse(src)
176 .has_errors()
177 );
178 }
179
180 #[test]
181 fn parses_consumer_provided_return_type() {
182 let src = "fn @main() -> unit { stub.halt }";
183 let f = ParsedFunction::<StubInst, StubType>::parser()
184 .parse(src)
185 .into_result()
186 .unwrap();
187 assert_eq!(f.return_types, vec![StubType::Unit]);
188 }
189
190 #[test]
191 fn lexical_helpers_return_explicit_newtypes() {
192 assert_eq!(
193 parse::symbol_ref().parse("@main").into_result(),
194 Ok(vihaco_parser::Ident("main".to_owned()))
195 );
196 assert_eq!(
197 parse::string_literal()
198 .parse("\"hello\\nworld\"")
199 .into_result(),
200 Ok(vihaco_parser::QuotedString("hello\nworld".to_owned()))
201 );
202 }
203
204 #[test]
205 fn skips_line_comments() {
206 let src = "\
207fn @main() {
208 // this is ignored
209 stub.halt
210}
211";
212 let f = ParsedFunction::<StubInst, StubType>::parser()
213 .parse(src)
214 .into_result()
215 .unwrap();
216 assert_eq!(f.body.len(), 1);
217 }
218
219 #[test]
220 fn rejects_malformed_known_instruction() {
221 #[derive(Debug, Clone, PartialEq, vihaco_parser_derive::Parse)]
222 #[syntax_class(instruction, head = "stub")]
223 enum OnlyOne {
224 Dump(u32),
225 }
226
227 let src = "fn @main() { stub.dump foo }";
228 assert!(
229 ParsedFunction::<OnlyOne, StubType>::parser()
230 .parse(src)
231 .has_errors()
232 );
233 }
234
235 #[test]
236 fn block_i64_flat_parses_whitespace_separated_ints() {
237 let got = block_i64_flat().parse(" 0 1 2 3 ").into_result().unwrap();
238 assert_eq!(got, vec![0, 1, 2, 3]);
239 }
240
241 #[test]
242 fn block_i64_pairs_parses_rows() {
243 let got = block_i64_pairs()
244 .parse("\n 1 1\n 5 1\n 1 5\n")
245 .into_result()
246 .unwrap();
247 assert_eq!(got, vec![(1, 1), (5, 1), (1, 5)]);
248 }
249
250 #[test]
251 fn block_i64_flat_accepts_empty() {
252 let got = block_i64_flat().parse("").into_result().unwrap();
253 assert!(got.is_empty());
254 }
255}