1use pest::{Parser, error::Error, iterators::Pair};
2
3use crate::synapse::{Rule, SynapseParser};
4
5use super::*;
6
7pub fn parse(input: &str) -> Result<SynFile, Error<Rule>> {
10 let file_pair = SynapseParser::parse(Rule::file, input)?.next().unwrap();
11 Ok(build_file(file_pair))
12}
13
14fn build_file(pair: Pair<Rule>) -> SynFile {
17 let items = pair
18 .into_inner()
19 .filter_map(|p| match p.as_rule() {
20 Rule::namespace_decl => Some(Item::Namespace(build_namespace(p))),
21 Rule::import_decl => Some(Item::Import(build_import(p))),
22 Rule::const_decl => Some(Item::Const(build_const(p))),
23 Rule::enum_def => Some(Item::Enum(build_enum(p))),
24 Rule::struct_def => Some(Item::Struct(build_struct(p))),
25 Rule::table_def => Some(Item::Table(build_struct(p))),
26 Rule::command_def => Some(Item::Command(build_packet(p, PacketKind::Command))),
27 Rule::telemetry_def => Some(Item::Telemetry(build_packet(p, PacketKind::Telemetry))),
28 Rule::message_def => Some(Item::Message(build_packet(p, PacketKind::Message))),
29 Rule::EOI => None,
30 r => unreachable!("unexpected rule: {:?}", r),
31 })
32 .collect();
33 SynFile { items }
34}
35
36fn build_namespace(pair: Pair<Rule>) -> NamespaceDecl {
37 let scoped = pair.into_inner().next().unwrap();
38 NamespaceDecl {
39 name: build_scoped_ident(scoped),
40 }
41}
42
43fn build_import(pair: Pair<Rule>) -> ImportDecl {
44 let s = pair.into_inner().next().unwrap().as_str();
45 ImportDecl {
46 path: s[1..s.len() - 1].to_string(),
47 }
48}
49
50fn build_const(pair: Pair<Rule>) -> ConstDecl {
51 let mut inner = pair.into_inner().peekable();
52 let doc = extract_doc(&mut inner);
53 let attrs = extract_attrs(&mut inner);
54 let name = inner.next().unwrap().as_str().to_string();
55 let ty = build_type_expr(inner.next().unwrap());
56 let value = build_literal(inner.next().unwrap());
57 ConstDecl {
58 name,
59 ty,
60 value,
61 doc,
62 attrs,
63 }
64}
65
66fn build_enum(pair: Pair<Rule>) -> EnumDef {
67 let mut inner = pair.into_inner().peekable();
68 let doc = extract_doc(&mut inner);
69 let attrs = extract_attrs(&mut inner);
70 let first = inner.next().unwrap();
71 let (repr, name) = if first.as_rule() == Rule::primitive_type {
72 let repr = build_primitive_type(first);
73 (Some(repr), inner.next().unwrap().as_str().to_string())
74 } else {
75 (None, first.as_str().to_string())
76 };
77 let variants = inner.map(build_enum_variant).collect();
78 EnumDef {
79 name,
80 repr,
81 variants,
82 doc,
83 attrs,
84 }
85}
86
87fn build_enum_variant(pair: Pair<Rule>) -> EnumVariant {
88 let mut inner = pair.into_inner().peekable();
89 let doc = extract_doc(&mut inner);
90 let name = inner.next().unwrap().as_str().to_string();
91 let value = inner.next().map(|p| p.as_str().parse::<i64>().unwrap());
92 EnumVariant { name, value, doc }
93}
94
95fn build_struct(pair: Pair<Rule>) -> StructDef {
96 let mut inner = pair.into_inner().peekable();
97 let doc = extract_doc(&mut inner);
98 let attrs = extract_attrs(&mut inner);
99 let name = inner.next().unwrap().as_str().to_string();
100 let fields = inner.map(build_field).collect();
101 StructDef {
102 name,
103 fields,
104 doc,
105 attrs,
106 }
107}
108
109fn build_packet(pair: Pair<Rule>, kind: PacketKind) -> MessageDef {
110 let mut inner = pair.into_inner().peekable();
111 let doc = extract_doc(&mut inner);
112 let attrs = extract_attrs(&mut inner);
113 let name = inner.next().unwrap().as_str().to_string();
114 let fields = inner.map(build_field).collect();
115 MessageDef {
116 kind,
117 name,
118 fields,
119 doc,
120 attrs,
121 }
122}
123
124fn build_field(pair: Pair<Rule>) -> FieldDef {
125 let mut inner = pair.into_inner().peekable();
126 let doc = extract_doc(&mut inner);
127 let name = inner.next().unwrap().as_str().to_string();
128
129 let next = inner.next().unwrap();
130 let (optional, type_pair) = if next.as_rule() == Rule::optional_marker {
131 (true, inner.next().unwrap())
132 } else {
133 (false, next)
134 };
135
136 let ty = build_type_expr(type_pair);
137 let default = inner.next().map(build_literal);
138
139 FieldDef {
140 name,
141 optional,
142 ty,
143 default,
144 doc,
145 }
146}
147
148fn extract_doc<'i>(
150 inner: &mut std::iter::Peekable<impl Iterator<Item = Pair<'i, Rule>>>,
151) -> Vec<String> {
152 if inner.peek().map(|p| p.as_rule()) == Some(Rule::doc_block) {
153 inner
154 .next()
155 .unwrap()
156 .into_inner()
157 .map(|p| {
158 p.as_str()
159 .strip_prefix("///")
160 .unwrap_or("")
161 .trim()
162 .to_string()
163 })
164 .collect()
165 } else {
166 vec![]
167 }
168}
169
170fn extract_attrs<'i>(
172 inner: &mut std::iter::Peekable<impl Iterator<Item = Pair<'i, Rule>>>,
173) -> Vec<Attribute> {
174 let mut attrs = vec![];
175 while inner.peek().map(|p| p.as_rule()) == Some(Rule::attribute) {
176 let attr = inner.next().unwrap();
177 let mut ai = attr.into_inner();
178 let name = ai.next().unwrap().as_str().to_string();
179 let value = build_literal(ai.next().unwrap());
180 attrs.push(Attribute { name, value });
181 }
182 attrs
183}
184
185fn build_type_expr(pair: Pair<Rule>) -> TypeExpr {
186 let mut inner = pair.into_inner();
187 let base = build_base_type(inner.next().unwrap());
188 let array = inner.next().map(build_array_suffix);
189 TypeExpr { base, array }
190}
191
192fn build_base_type(pair: Pair<Rule>) -> BaseType {
193 let inner = pair.into_inner().next().unwrap();
194 match inner.as_rule() {
195 Rule::string_type => BaseType::String,
196 Rule::primitive_type => BaseType::Primitive(build_primitive_type(inner)),
197 Rule::type_ref => BaseType::Ref(build_scoped_ident(inner.into_inner().next().unwrap())),
198 r => unreachable!("unexpected base_type rule: {:?}", r),
199 }
200}
201
202fn build_primitive_type(pair: Pair<Rule>) -> PrimitiveType {
203 const PRIMITIVES: &[(&str, PrimitiveType)] = &[
204 ("f32", PrimitiveType::F32),
205 ("f64", PrimitiveType::F64),
206 ("i8", PrimitiveType::I8),
207 ("i16", PrimitiveType::I16),
208 ("i32", PrimitiveType::I32),
209 ("i64", PrimitiveType::I64),
210 ("u8", PrimitiveType::U8),
211 ("u16", PrimitiveType::U16),
212 ("u32", PrimitiveType::U32),
213 ("u64", PrimitiveType::U64),
214 ("bool", PrimitiveType::Bool),
215 ("bytes", PrimitiveType::Bytes),
216 ];
217
218 let primitive = pair.as_str();
219 PRIMITIVES
220 .iter()
221 .find_map(|(name, ty)| (*name == primitive).then_some(*ty))
222 .unwrap_or_else(|| unreachable!("unknown primitive: {}", primitive))
223}
224
225fn build_array_suffix(pair: Pair<Rule>) -> ArraySuffix {
226 let Some(size) = pair.into_inner().next() else {
227 return ArraySuffix::Dynamic;
228 };
229 build_sized_array_suffix(size)
230}
231
232fn build_sized_array_suffix(pair: Pair<Rule>) -> ArraySuffix {
233 let inner = pair.into_inner().next().unwrap();
234 match inner.as_rule() {
235 Rule::bounded_size => {
236 let n = inner
237 .into_inner()
238 .next()
239 .unwrap()
240 .as_str()
241 .parse::<u64>()
242 .unwrap();
243 ArraySuffix::Bounded(n)
244 }
245 Rule::pos_int => ArraySuffix::Fixed(inner.as_str().parse::<u64>().unwrap()),
246 r => unreachable!("unexpected array_size rule: {:?}", r),
247 }
248}
249
250fn build_literal(pair: Pair<Rule>) -> Literal {
251 let inner = pair.into_inner().next().unwrap();
252 if matches!(
253 inner.as_rule(),
254 Rule::float_lit | Rule::hex_lit | Rule::int_lit
255 ) {
256 return build_numeric_literal(inner);
257 }
258 match inner.as_rule() {
259 Rule::bool_lit => build_bool_literal(inner),
260 Rule::string_lit => build_string_literal(inner),
261 Rule::ident_lit => Literal::Ident(build_scoped_ident(inner.into_inner().next().unwrap())),
262 r => unreachable!("unexpected literal rule: {:?}", r),
263 }
264}
265
266fn build_numeric_literal(pair: Pair<Rule>) -> Literal {
267 match pair.as_rule() {
268 Rule::float_lit => Literal::Float(pair.as_str().parse::<f64>().unwrap()),
269 Rule::hex_lit => {
270 let s = pair.as_str();
271 let digits = &s[2..]; Literal::Hex(u64::from_str_radix(digits, 16).unwrap())
273 }
274 Rule::int_lit => Literal::Int(pair.as_str().parse::<i64>().unwrap()),
275 r => unreachable!("unexpected numeric literal rule: {:?}", r),
276 }
277}
278
279fn build_bool_literal(pair: Pair<Rule>) -> Literal {
280 Literal::Bool(pair.as_str() == "true")
281}
282
283fn build_string_literal(pair: Pair<Rule>) -> Literal {
284 let s = pair.as_str();
285 Literal::Str(unescape(&s[1..s.len() - 1]))
286}
287
288fn build_scoped_ident(pair: Pair<Rule>) -> ScopedIdent {
289 pair.into_inner().map(|p| p.as_str().to_string()).collect()
290}
291
292fn unescape(s: &str) -> String {
293 let mut out = String::with_capacity(s.len());
294 let mut chars = s.chars();
295 while let Some(c) = chars.next() {
296 if c == '\\' {
297 push_escape(&mut out, chars.next());
298 } else {
299 out.push(c);
300 }
301 }
302 out
303}
304
305fn push_escape(out: &mut String, escaped: Option<char>) {
306 let Some(escaped) = escaped else {
307 out.push('\\');
308 return;
309 };
310
311 match escaped {
312 'n' => out.push('\n'),
313 't' => out.push('\t'),
314 'r' => out.push('\r'),
315 c => push_quoted_escape(out, c),
316 }
317}
318
319fn push_quoted_escape(out: &mut String, escaped: char) {
320 match escaped {
321 '\\' => out.push('\\'),
322 '"' => out.push('"'),
323 c => {
324 out.push('\\');
325 out.push(c);
326 }
327 }
328}