1use std::collections::HashMap;
3use std::hash::Hash;
4
5use nom::branch::alt;
6use nom::bytes::complete::{tag, take_while, take_while1};
7use nom::character::complete::{
8 char, multispace0, multispace1, none_of, one_of, space1, u64, usize,
9};
10use nom::combinator::{consumed, cut, eof, opt, value as nom_value};
11use nom::error::{ContextError, ErrorKind, ParseError, context};
12use nom::multi::{fold_many0, length_count};
13use nom::number::complete::recognize_float;
14use nom::sequence::{preceded, separated_pair, terminated};
15use nom::{AsChar, Compare, Err, Finish, IResult, Input, Offset, ParseTo, Parser};
16
17use crate::token::{
18 Arc, Component, Coordinate, Embedding, FiniteDouble, Flip, Line, Object, Polygon, Property,
19 Rectangle, Rotation, Schematic, Size, SpiceProperty, SymbolProperty, TedaXProperty, Text, Vec2,
20 VerilogProperty, Version, VhdlProperty, Wire,
21};
22
23pub const ESCAPED_CHARS: &str = r"\{}";
25pub const ESCAPED_VALUE_CHARS: &str = r#"\""#;
27pub const ESCAPE_CHAR: char = '\\';
29
30pub(crate) fn escaped0<'a, I, Error, F, G>(
31 mut normal: F,
32 control_char: char,
33 mut escapable: G,
34) -> impl FnMut(I) -> IResult<I, I, Error>
35where
36 I: Input + Offset + 'a,
37 <I as Input>::Item: AsChar,
38 F: Parser<I, Error = Error>,
39 G: Parser<I, Error = Error>,
40 Error: ParseError<I>,
41{
42 move |input: I| {
43 let mut i = input.clone();
44 let mut consumed_nothing = false;
45
46 while i.input_len() > 0 {
47 let current_len = i.input_len();
48
49 match (normal.parse(i.clone()), consumed_nothing) {
50 (Ok((i2, _)), false) => {
51 if i2.input_len() == 0 {
52 return Ok((input.take_from(input.input_len()), input));
53 }
54 if i2.input_len() == current_len {
55 consumed_nothing = true;
56 }
57 i = i2;
58 }
59 (Ok(..), true) | (Err(Err::Error(_)), _) => {
60 let next_char = i
61 .iter_elements()
62 .next()
63 .ok_or_else(|| {
64 Err::Error(Error::from_error_kind(i.clone(), ErrorKind::Escaped))
65 })?
66 .as_char();
67 if next_char == control_char {
68 let next = control_char.len_utf8();
69 if next >= i.input_len() {
70 return Err(Err::Error(Error::from_error_kind(
71 input,
72 ErrorKind::Escaped,
73 )));
74 }
75 match escapable.parse(i.take_from(next)) {
76 Ok((i2, _)) => {
77 if i2.input_len() == 0 {
78 return Ok((input.take_from(input.input_len()), input));
79 }
80 consumed_nothing = false;
81 i = i2;
82 }
83 Err(_) => {
84 return Err(Err::Error(Error::from_error_kind(
85 i,
86 ErrorKind::Escaped,
87 )));
88 }
89 }
90 } else {
91 let index = input.offset(&i);
92 return Ok(input.take_split(index));
93 }
94 }
95 (Err(e), _) => {
96 return Err(e);
97 }
98 }
99 }
100
101 Ok((input.take_from(input.input_len()), input))
102 }
103}
104
105pub(crate) fn try_skip<'a, I, O, E, F>(
106 mut parser: F,
107) -> impl Parser<I, Output = Option<O>, Error = E>
108where
109 I: Input + 'a,
110 <I as Input>::Item: AsChar,
111 E: ParseError<I>,
112 F: Parser<I, Output = O, Error = (I, ErrorKind)> + 'a,
113{
114 move |input: I| match parser.parse(input) {
115 Ok((rest, kv)) => Ok((rest, Some(kv))),
116 Err(Err::Error((rest, _))) => Ok((rest, None)),
117 Err(Err::Failure((input, kind))) => Err(Err::Failure(E::from_error_kind(input, kind))),
118 Err(Err::Incomplete(e)) => Err(Err::Incomplete(e)),
119 }
120}
121
122fn is_key_char<C: AsChar>(c: C) -> bool {
123 c.is_alphanum() || c.as_char() == '_'
124}
125
126pub(crate) fn key<'a, I, E>(input: I) -> IResult<I, I, E>
127where
128 I: Offset + Input + 'a,
129 <I as Input>::Item: AsChar,
130 E: ParseError<I> + ContextError<I>,
131{
132 context("key", take_while1(is_key_char)).parse(input)
133}
134
135fn is_value_char<C: AsChar>(c: C) -> bool {
136 c.is_alphanum() || c.as_char().is_ascii_punctuation()
137}
138
139pub(crate) fn value<'a, I, E>(input: I) -> IResult<I, I, E>
140where
141 I: Offset + Input + for<'s> Compare<&'s str> + 'a,
142 <I as Input>::Item: AsChar,
143 E: ParseError<I> + ContextError<I>,
144{
145 context(
146 "value",
147 alt((
148 preceded(
149 char('"'),
150 cut(terminated(
151 escaped0(
152 none_of(ESCAPED_VALUE_CHARS),
153 ESCAPE_CHAR,
154 alt((tag(r#"\""#), tag(r"\"), tag(r"{"), tag(r"}"))),
155 ),
156 char('"'),
157 )),
158 ),
159 take_while1(is_value_char),
160 )),
161 )
162 .parse(input)
163}
164
165pub(crate) fn key_value<'a, I, E>(input: I) -> IResult<I, (I, I), E>
166where
167 I: Offset + Input + for<'s> Compare<&'s str> + 'a,
168 <I as Input>::Item: AsChar,
169 E: ParseError<I> + ContextError<I>,
170{
171 context("key_value", separated_pair(key, char('='), value)).parse(input)
172}
173
174pub(crate) fn attributes<'a, I, E>(mut input: I) -> IResult<I, HashMap<I, I>, E>
175where
176 I: Eq + Hash + Offset + Input + for<'s> Compare<&'s str> + 'a,
177 <I as Input>::Item: AsChar,
178 E: ParseError<I> + ContextError<I>,
179{
180 let mut attrs = HashMap::new();
181
182 while input.input_len() > 0 {
183 input = match preceded(take_while(|c| !is_key_char(c)), try_skip(key_value)).parse(input) {
184 Ok((rest, Some((k, v)))) => {
185 attrs.insert(k, v);
186 rest
187 }
188 Ok((rest, None)) => rest,
189 Err(e) => return Err(e),
190 };
191 }
192
193 Ok((input, attrs))
194}
195
196pub(crate) fn brace_enclosed<'a, I, O, P, E>(parser: P) -> impl Parser<I, Output = O, Error = E>
197where
198 I: Input + 'a,
199 <I as Input>::Item: AsChar,
200 E: ParseError<I>,
201 P: Parser<I, Output = O, Error = E>,
202{
203 preceded(char('{'), cut(terminated(parser, char('}'))))
204}
205
206pub(crate) fn property_string<'a, I, E>(input: I) -> IResult<I, I, E>
207where
208 I: Input + Offset + 'a,
209 <I as Input>::Item: AsChar,
210 E: ParseError<I> + ContextError<I>,
211{
212 escaped0(none_of(ESCAPED_CHARS), ESCAPE_CHAR, one_of(ESCAPED_CHARS)).parse(input)
213}
214
215pub(crate) fn property<'a, I, E>(input: I) -> IResult<I, Property<I>, E>
216where
217 I: Eq + Hash + Input + Offset + for<'s> nom::Compare<&'s str> + 'a,
218 <I as Input>::Item: AsChar,
219 E: ParseError<I> + ContextError<I>,
220{
221 brace_enclosed(context("property", property_string))
222 .and_then(consumed(attributes))
223 .map(|(prop, attrs)| Property { prop, attrs })
224 .parse(input)
225}
226
227pub(crate) fn text<'a, I, E>(input: I) -> IResult<I, I, E>
228where
229 I: Input + Offset + 'a,
230 <I as Input>::Item: AsChar,
231 E: ParseError<I> + ContextError<I>,
232{
233 brace_enclosed(context("text", property_string)).parse(input)
234}
235
236pub(crate) fn reference<'a, I, E>(input: I) -> IResult<I, I, E>
237where
238 I: Input + Offset + 'a,
239 <I as Input>::Item: AsChar,
240 E: ParseError<I> + ContextError<I>,
241{
242 brace_enclosed(context("reference", property_string)).parse(input)
243}
244
245pub(crate) fn object<'a, I, O, P, E>(
246 name: &'static str,
247 tag: char,
248 parser: P,
249) -> impl Parser<I, Output = O, Error = E>
250where
251 I: Input + 'a,
252 <I as Input>::Item: AsChar,
253 E: ParseError<I> + ContextError<I>,
254 P: Parser<I, Output = O, Error = E>,
255{
256 context(name, preceded(char(tag), cut(parser)))
257}
258
259pub(crate) fn layer<'a, I, E>(input: I) -> IResult<I, u64, E>
260where
261 I: Input + 'a,
262 <I as Input>::Item: AsChar,
263 E: ParseError<I>,
264{
265 u64(input)
266}
267
268pub(crate) fn finite_double<'a, I, E>(input: I) -> IResult<I, FiniteDouble, E>
269where
270 I: Input + Offset + ParseTo<f64> + 'a,
271 <I as Input>::Item: AsChar,
272 E: ParseError<I>,
273{
274 let (i, s) = recognize_float(input)?;
275 match s.parse_to() {
276 Some(f) => Ok((i, f.try_into().unwrap())),
279 None => Err(Err::Error(E::from_error_kind(i, ErrorKind::Float))),
280 }
281}
282
283pub(crate) fn vec2<'a, I, E>(input: I) -> IResult<I, Vec2, E>
284where
285 I: Input + Offset + ParseTo<f64> + 'a,
286 <I as Input>::Item: AsChar,
287 E: ParseError<I>,
288{
289 Parser::into(separated_pair(finite_double, multispace1, finite_double)).parse(input)
290}
291
292pub(crate) fn coordinate<'a, I, E>(input: I) -> IResult<I, Coordinate, E>
293where
294 I: Input + Offset + ParseTo<f64> + 'a,
295 <I as Input>::Item: AsChar,
296 E: ParseError<I> + ContextError<I>,
297{
298 context("coordinate", vec2).parse(input)
299}
300
301pub(crate) fn size<'a, I, E>(input: I) -> IResult<I, Size, E>
302where
303 I: Input + Offset + ParseTo<f64> + 'a,
304 <I as Input>::Item: AsChar,
305 E: ParseError<I> + ContextError<I>,
306{
307 context("size", vec2).parse(input)
308}
309
310pub(crate) fn rotation<'a, I, E>(input: I) -> IResult<I, Rotation, E>
311where
312 I: Input + 'a,
313 <I as Input>::Item: AsChar,
314 E: ParseError<I> + ContextError<I>,
315{
316 context(
317 "rotation",
318 alt((
319 nom_value(Rotation::Zero, char('0')),
320 nom_value(Rotation::One, char('1')),
321 nom_value(Rotation::Two, char('2')),
322 nom_value(Rotation::Three, char('3')),
323 )),
324 )
325 .parse(input)
326}
327
328pub(crate) fn flip<'a, I, E>(input: I) -> IResult<I, Flip, E>
329where
330 I: Input + 'a,
331 <I as Input>::Item: AsChar,
332 E: ParseError<I> + ContextError<I>,
333{
334 context(
335 "flip",
336 alt((
337 nom_value(Flip::Unflipped, char('0')),
338 nom_value(Flip::Flipped, char('1')),
339 )),
340 )
341 .parse(input)
342}
343
344pub(crate) fn embedding<'a, I, E>(input: I) -> IResult<I, Embedding<I>, E>
345where
346 I: Eq + Hash + Input + Offset + ParseTo<f64> + for<'s> nom::Compare<&'s str> + 'a,
347 <I as Input>::Item: AsChar,
348 E: ParseError<I> + ContextError<I>,
349{
350 object(
351 "embedded symbol",
352 '[',
353 terminated(
354 preceded(multispace1, Parser::into(schematic)),
355 preceded(multispace1, char(']')),
356 ),
357 )
358 .parse(input)
359}
360
361pub(crate) fn version_object<'a, I, E>(input: I) -> IResult<I, Version<I>, E>
362where
363 I: Eq + Hash + Input + Offset + for<'s> Compare<&'s str> + 'a,
364 <I as Input>::Item: AsChar,
365 E: ParseError<I> + ContextError<I>,
366{
367 object("version", 'v', preceded(multispace1, property))
368 .map(Version)
369 .parse(input)
370}
371
372pub(crate) fn property_object<'a, I, E>(
373 tag: char,
374) -> impl Parser<I, Output = Property<I>, Error = E>
375where
376 I: Eq + Hash + Input + Offset + for<'s> Compare<&'s str> + 'a,
377 <I as Input>::Item: AsChar,
378 E: ParseError<I> + ContextError<I>,
379{
380 object("global property", tag, preceded(multispace1, property))
381}
382
383pub(crate) fn arc_object<'a, I, E>(input: I) -> IResult<I, Arc<I>, E>
384where
385 I: Eq + Hash + Input + Offset + ParseTo<f64> + for<'s> Compare<&'s str> + 'a,
386 <I as Input>::Item: AsChar,
387 E: ParseError<I> + ContextError<I>,
388{
389 object(
390 "arc",
391 'A',
392 (
393 preceded(multispace1, layer),
394 preceded(multispace1, coordinate),
395 preceded(multispace1, finite_double),
396 preceded(multispace1, finite_double),
397 preceded(multispace1, finite_double),
398 preceded(multispace1, property),
399 ),
400 )
401 .map(
402 |(layer, center, radius, start_angle, sweep_angle, property)| Arc {
403 layer,
404 center,
405 radius,
406 start_angle,
407 sweep_angle,
408 property,
409 },
410 )
411 .parse(input)
412}
413
414pub(crate) fn component_instance<'a, I, E>(input: I) -> IResult<I, Component<I>, E>
415where
416 I: Eq + Hash + Input + Offset + ParseTo<f64> + for<'s> Compare<&'s str> + 'a,
417 <I as Input>::Item: AsChar,
418 E: ParseError<I> + ContextError<I>,
419{
420 object(
421 "component",
422 'C',
423 (
424 preceded(multispace1, reference),
425 preceded(multispace1, coordinate),
426 preceded(multispace1, rotation),
427 preceded(multispace1, flip),
428 preceded(multispace1, property),
429 opt(preceded(multispace1, embedding)),
430 ),
431 )
432 .map(
433 |(reference, position, rotation, flip, property, embedding)| Component {
434 reference,
435 position,
436 rotation,
437 flip,
438 property,
439 embedding,
440 },
441 )
442 .parse(input)
443}
444
445pub(crate) fn line_object<'a, I, E>(input: I) -> IResult<I, Line<I>, E>
446where
447 I: Eq + Hash + Input + Offset + ParseTo<f64> + for<'s> Compare<&'s str> + 'a,
448 <I as Input>::Item: AsChar,
449 E: ParseError<I> + ContextError<I>,
450{
451 object(
452 "line",
453 'L',
454 (
455 preceded(multispace1, layer),
456 preceded(multispace1, coordinate),
457 preceded(multispace1, coordinate),
458 preceded(multispace1, property),
459 ),
460 )
461 .map(|(layer, start, end, property)| Line {
462 layer,
463 start,
464 end,
465 property,
466 })
467 .parse(input)
468}
469
470pub(crate) fn polygon_object<'a, I, E>(input: I) -> IResult<I, Polygon<I>, E>
471where
472 I: Eq + Hash + Input + Offset + ParseTo<f64> + for<'s> Compare<&'s str> + 'a,
473 <I as Input>::Item: AsChar,
474 E: ParseError<I> + ContextError<I>,
475{
476 object(
477 "polygon",
478 'P',
479 (
480 preceded(multispace1, layer),
481 preceded(
482 multispace1,
483 length_count(usize, preceded(space1, coordinate)),
484 ),
485 preceded(multispace1, property),
486 ),
487 )
488 .map(|(layer, points, property)| Polygon {
489 layer,
490 points: points.into(),
491 property,
492 })
493 .parse(input)
494}
495
496pub(crate) fn rectangle_object<'a, I, E>(input: I) -> IResult<I, Rectangle<I>, E>
497where
498 I: Eq + Hash + Input + Offset + ParseTo<f64> + for<'s> Compare<&'s str> + 'a,
499 <I as Input>::Item: AsChar,
500 E: ParseError<I> + ContextError<I>,
501{
502 object(
503 "rectangle",
504 'B',
505 (
506 preceded(multispace1, layer),
507 preceded(multispace1, coordinate),
508 preceded(multispace1, coordinate),
509 preceded(multispace1, property),
510 ),
511 )
512 .map(|(layer, start, end, property)| Rectangle {
513 layer,
514 start,
515 end,
516 property,
517 })
518 .parse(input)
519}
520
521pub(crate) fn text_object<'a, I, E>(input: I) -> IResult<I, Text<I>, E>
522where
523 I: Eq + Hash + Input + Offset + ParseTo<f64> + for<'s> Compare<&'s str> + 'a,
524 <I as Input>::Item: AsChar,
525 E: ParseError<I> + ContextError<I>,
526{
527 object(
528 "text",
529 'T',
530 (
531 preceded(multispace1, text),
532 preceded(multispace1, coordinate),
533 preceded(multispace1, rotation),
534 preceded(multispace1, flip),
535 preceded(multispace1, size),
536 preceded(multispace1, property),
537 ),
538 )
539 .map(|(text, position, rotation, flip, size, property)| Text {
540 text,
541 position,
542 rotation,
543 flip,
544 size,
545 property,
546 })
547 .parse(input)
548}
549
550pub(crate) fn wire_object<'a, I, E>(input: I) -> IResult<I, Wire<I>, E>
551where
552 I: Eq + Hash + Input + Offset + ParseTo<f64> + for<'s> Compare<&'s str> + 'a,
553 <I as Input>::Item: AsChar,
554 E: ParseError<I> + ContextError<I>,
555{
556 object(
557 "wire",
558 'N',
559 (
560 preceded(multispace1, coordinate),
561 preceded(multispace1, coordinate),
562 preceded(multispace1, property),
563 ),
564 )
565 .map(|(start, end, property)| Wire {
566 start,
567 end,
568 property,
569 })
570 .parse(input)
571}
572
573pub(crate) fn any_object<'a, I, E>(input: I) -> IResult<I, Object<I>, E>
574where
575 I: Eq + Hash + Input + Offset + ParseTo<f64> + for<'s> Compare<&'s str> + 'a,
576 <I as Input>::Item: AsChar,
577 E: ParseError<I> + ContextError<I>,
578{
579 alt((
580 Parser::into(Parser::into::<VhdlProperty<I>, E>(property_object('G'))),
581 Parser::into(Parser::into::<SymbolProperty<I>, E>(property_object('K'))),
582 Parser::into(Parser::into::<VerilogProperty<I>, E>(property_object('V'))),
583 Parser::into(Parser::into::<SpiceProperty<I>, E>(property_object('S'))),
584 Parser::into(Parser::into::<TedaXProperty<I>, E>(property_object('E'))),
585 Parser::into(arc_object),
586 Parser::into(component_instance),
587 Parser::into(line_object),
588 Parser::into(polygon_object),
589 Parser::into(rectangle_object),
590 Parser::into(text_object),
591 Parser::into(wire_object),
592 ))
593 .parse(input)
594}
595
596pub fn schematic<'a, I, E>(input: I) -> IResult<I, Schematic<I>, E>
598where
599 I: Eq + Hash + Input + Offset + ParseTo<f64> + for<'s> Compare<&'s str> + 'a,
600 <I as Input>::Item: AsChar,
601 E: ParseError<I> + ContextError<I>,
602{
603 preceded(
604 multispace0,
605 version_object.flat_map(|version| {
606 fold_many0(
607 preceded(multispace1, any_object),
608 move || Schematic::new(version.clone()),
609 Schematic::add_object,
610 )
611 }),
612 )
613 .parse(input)
614}
615
616pub fn schematic_full<'a, I, E>(input: I) -> Result<Schematic<I>, E>
618where
619 I: Eq + Hash + Input + Offset + ParseTo<f64> + for<'s> Compare<&'s str> + 'a,
620 <I as Input>::Item: AsChar,
621 E: ParseError<I> + ContextError<I>,
622{
623 terminated(schematic, preceded(multispace0, eof))
624 .parse(input)
625 .finish()
626 .map(|r| r.1)
627}