1use chumsky::error::Simple;
11use chumsky::extra;
12use chumsky::prelude::*;
13use vihaco_parser::{Ident, Parse, QuotedString};
14
15use vihaco_bytecode::SstHeader;
16use vihaco_bytecode::SstSectionView;
17
18#[cfg(feature = "typed-declarations")]
19use crate::ParsedLabel;
20use crate::{Param, ParsedFunction, ParsedModule};
21
22#[cfg(feature = "typed-declarations")]
23enum BodyItem<I> {
24 Instruction(I),
25 Label(Ident),
26}
27
28type E<'src> = extra::Err<Simple<'src, char>>;
29
30pub fn skip<'src>() -> impl Parser<'src, &'src str, (), E<'src>> + Clone {
32 vihaco_parser::trivia()
33}
34
35pub fn string_literal<'src>() -> impl Parser<'src, &'src str, QuotedString, E<'src>> + Clone {
37 let escape = just('\\').ignore_then(choice((
38 just('"').to('"'),
39 just('\\').to('\\'),
40 just('n').to('\n'),
41 just('t').to('\t'),
42 just('r').to('\r'),
43 just('0').to('\0'),
44 )));
45 let char_or_escape = choice((
46 escape,
47 any().and_is(just('"').not()).and_is(just('\\').not()),
48 ));
49 just('"')
50 .ignore_then(char_or_escape.repeated().collect::<String>())
51 .then_ignore(just('"'))
52 .map(QuotedString)
53}
54
55pub fn symbol_ref<'src>() -> impl Parser<'src, &'src str, Ident, E<'src>> + Clone {
57 just('@').ignore_then(vihaco_parser::ident().map(Ident))
58}
59
60pub fn block_i64_flat<'src>() -> impl Parser<'src, &'src str, Vec<i64>, E<'src>> + Clone {
67 let item = just('-')
68 .or_not()
69 .then(text::int(10))
70 .to_slice()
71 .map(|s: &str| s.parse::<i64>().unwrap());
72 let ws = any().filter(|c: &char| c.is_whitespace()).repeated();
74 ws.ignore_then(item)
75 .repeated()
76 .collect::<Vec<_>>()
77 .then_ignore(ws)
78}
79
80pub fn block_i64_pairs<'src>() -> impl Parser<'src, &'src str, Vec<(i64, i64)>, E<'src>> + Clone {
88 let signed_int = just('-')
89 .or_not()
90 .then(text::int(10))
91 .to_slice()
92 .map(|s: &str| s.parse::<i64>().unwrap());
93 let inline_ws = any()
94 .filter(|c: &char| c.is_whitespace() && *c != '\n')
95 .repeated()
96 .at_least(1);
97 let ws = any().filter(|c: &char| c.is_whitespace()).repeated();
98
99 let pair = signed_int.then_ignore(inline_ws).then(signed_int);
100 ws.ignore_then(pair)
101 .repeated()
102 .collect::<Vec<_>>()
103 .then_ignore(ws)
104}
105
106fn parameter_name<'src>() -> impl Parser<'src, &'src str, Ident, E<'src>> + Clone {
109 any()
110 .filter(|c: &char| {
111 !c.is_whitespace()
112 && !matches!(
113 *c,
114 ':' | ',' | ';' | '(' | ')' | '{' | '}' | '[' | ']' | '"' | '\'' | '`'
115 )
116 && *c != '@'
117 })
118 .repeated()
119 .at_least(1)
120 .collect::<String>()
121 .map(Ident)
122 .padded()
123}
124
125fn param_list<'src, Ty>() -> impl Parser<'src, &'src str, Vec<Param<Ty>>, E<'src>>
127where
128 Ty: Parse<'src> + 'src,
129{
130 let parameter = parameter_name()
131 .then_ignore(just(':').padded())
132 .then(Ty::parser().padded())
133 .map(|(name, ty)| Param { name, ty });
134
135 parameter
136 .separated_by(just(',').padded())
137 .collect::<Vec<_>>()
138 .delimited_by(just('(').padded(), just(')').padded())
139}
140
141fn functions<'src, I, Ty>() -> impl Parser<'src, &'src str, Vec<ParsedFunction<I, Ty>>, E<'src>>
142where
143 I: Parse<'src> + 'src,
144 Ty: Parse<'src> + 'src,
145{
146 skip()
147 .ignore_then(ParsedFunction::<I, Ty>::parser())
148 .repeated()
149 .collect::<Vec<_>>()
150 .then_ignore(skip())
151}
152
153impl<'src, I, Ty> Parse<'src> for ParsedFunction<I, Ty>
154where
155 I: Parse<'src> + 'src,
156 Ty: Parse<'src> + 'src,
157{
158 fn parser() -> impl Parser<'src, &'src str, Self, E<'src>> {
159 let returns = Ty::parser()
160 .padded()
161 .separated_by(just(',').padded())
162 .allow_trailing()
163 .collect::<Vec<_>>()
164 .delimited_by(just('(').padded(), just(')').padded())
165 .or(Ty::parser().map(|ty| vec![ty]));
166 let return_types = just("->")
167 .padded()
168 .ignore_then(returns)
169 .or_not()
170 .map(Option::unwrap_or_default);
171 #[cfg(not(feature = "typed-declarations"))]
172 let body = skip()
173 .ignore_then(I::parser())
174 .repeated()
175 .collect::<Vec<_>>()
176 .then_ignore(skip());
177 #[cfg(feature = "typed-declarations")]
178 let body = {
179 let label = just("label")
180 .then_ignore(text::whitespace().at_least(1))
181 .ignore_then(just('@'))
182 .ignore_then(Ident::parser())
183 .map(BodyItem::Label);
184 skip()
185 .ignore_then(label.or(I::parser().map(BodyItem::Instruction)))
186 .repeated()
187 .collect::<Vec<_>>()
188 .map(|items| {
189 let mut body = Vec::new();
190 let mut labels = Vec::new();
191 for item in items {
192 match item {
193 BodyItem::Instruction(instruction) => body.push(instruction),
194 BodyItem::Label(name) => labels.push(ParsedLabel {
195 name,
196 instruction_index: body.len(),
197 }),
198 }
199 }
200 (body, labels)
201 })
202 .then_ignore(skip())
203 };
204
205 just("fn")
206 .padded()
207 .ignore_then(just('@'))
208 .ignore_then(Ident::parser())
209 .then(param_list::<Ty>())
210 .then(return_types)
211 .then_ignore(just('{').padded())
212 .then(body)
213 .then_ignore(just('}').padded())
214 .map(|(((name, params), return_types), body)| {
215 #[cfg(feature = "typed-declarations")]
216 let (body, labels) = body;
217 ParsedFunction {
218 name,
219 params,
220 return_types,
221 body,
222 #[cfg(feature = "typed-declarations")]
223 labels,
224 }
225 })
226 }
227}
228
229impl<I, Ty, H> ParsedModule<I, Ty, H> {
230 pub fn parse_section<'src, C>(section: SstSectionView<'src, C>) -> eyre::Result<Self>
232 where
233 H: SstHeader,
234 I: vihaco_parser::Parse<'src> + 'src,
235 Ty: vihaco_parser::Parse<'src> + 'src,
236 {
237 let header = section.parse_header::<H>()?;
238 let text = section.sst();
239 let functions = functions::<I, Ty>()
240 .parse(text)
241 .into_result()
242 .map_err(|errors| eyre::eyre!("failed to parse SST functions: {:?}", errors))?;
243
244 Ok(Self { header, functions })
245 }
246}