1use super::Instance;
19
20use crate::{Buildable, Builder, IndexGraph};
21
22use std::error;
23use std::fmt;
24use std::fs;
25use std::io;
26
27use super::EdgeAttr;
28use std::collections::HashMap;
29
30use peg;
31
32pub type ParseError = peg::error::ParseError<peg::str::LineCol>;
34
35peg::parser! {
37
38 grammar steinlib_parser() for str {
39 pub(super) rule doc() -> Vec<Section>
40 = ws()* i("33D32945") ws()+ i("STP") ws()+ i("File,") ws()+ i("STP") ws()+ i("Format") ws()+ i("Version") ws()+ i("1.") "0"+ nl()+
41 secs:(
42 comment_section() /
43 graph_section() /
44 terminals_section() /
45 coordinates_section() /
46 drawing_section()
47 ) ** (nl()*)
48 nl()*
49 i("EOF") nl()* ![_]
50 { secs }
51
52 pub(super) rule comment_section() -> Section
53 = i("SECTION") ws()+ i("Comment") nl()+ h:comment_entry() ** (nl()*) nl()* i("END") nl()
54 {
55 Section::Comment(h.into_iter().collect())
56 }
57
58 rule comment_entry() -> (String, String)
59 = ws()* key:$(['a'..='z' | 'A' ..= 'Z']+) ws()+ val:(quoted() / word()) nl()
60 {
61 (key.to_string(), val)
62 }
63
64 pub(super) rule graph_section() -> Section
65 = i("SECTION") ws()+ i("Graph") nl()+
66 g:(edge_line() / arc_line() / node_count() / edge_count() / arc_count()) ** (nl()*) nl()*
67 i("END") nl() { Section::Graph(g) }
68
69 rule edge_line() -> GraphLine
70 = i("E") ws()+ u:int() ws()+ v:int() ws()+ w:float() nl() { GraphLine::Edge(u, v, w) }
71
72 rule arc_line() -> GraphLine
73 = i("A") ws()+ u:int() ws()+ v:int() ws()+ w:float() nl() { GraphLine::Arc(u, v, w) }
74
75 rule node_count() -> GraphLine
76 = i("Nodes") ws()+ n:int() nl() { GraphLine::NumNodes(n) }
77
78 rule edge_count() -> GraphLine
79 = i("Edges") ws()+ n:int() nl() { GraphLine::NumEdges(n) }
80
81 rule arc_count() -> GraphLine
82 = i("Arcs") ws()+ n:int() nl() { GraphLine::NumArcs(n) }
83
84
85 pub(super) rule terminals_section() -> Section
86 = i("SECTION") ws()+ i("Terminals")
87 nl()+
88 i("Terminals") ws()+ n:int()
89 nl()+
90 t:terminal() ** (nl()*)
91 nl()*
92 i("END") nl()
93 { Section::Terminals(n, t) }
94
95 rule terminal() -> usize
96 = i("T") ws()* n:int() nl() { n }
97
98
99 pub(super) rule coordinates_section() -> Section
100 = i("SECTION") ws()+ i("Coordinates") nl()
101 coords:coordinate() ** (nl()*)
102 nl()*
103 i("END") nl()
104 { Section::Coordinates(coords) }
105
106 rule coordinate() -> (usize,Vec<f64>)
107 = ws()* beg:position!() i("D")+ end:position!() ws()+ u:int() ws()+ nums:float() **<{end-beg}> (ws()+) nl() { (u,nums) }
108
109
110 pub(super) rule drawing_section() -> Section
111 = i("SECTION") ws()+ i("Drawing") nl()
112 d:arc_drawing() ** (nl()*)
113 nl()*
114 i("END") nl()
115 { Section::Drawing(d) }
116
117 rule arc_drawing() -> (usize, usize, Vec<EdgeAttr>)
118 = ['a' | 'A' | 'e' | 'E'] ws()+ u:int() ws()+ v:int() ws()+ attrs:arc_attrs() ** (ws()+) nl()
119 { (u, v, attrs) }
120
121 rule arc_attrs() -> EdgeAttr
122 = a:$(i("bl") / i("br"))
123 {
124 match a {
125 "bl" | "bL" | "Bl" | "BL" => EdgeAttr::BendLeft,
126 _ => EdgeAttr::BendRight
127 }
128 }
129
130 rule int() -> usize
131 = n:$(['-' | '+']? digit()+) { n.parse().unwrap() }
132
133 rule float() -> f64
134 = n:$(['-' | '+']? (digit()+ "."? digit()* / "." digit()+) (['e' | 'E'] ['-' | '+']? digit()+)?) { n.parse().unwrap() }
135
136 rule digit()
137 = ['0' ..= '9']
138
139 rule quoted() -> String
140 = "\"" s:$(( "\\" [_] / !['\\' | '"'] [_] )*) "\"" { s.to_string() }
141
142 rule word() -> String
143 = s:$((![' ' | '\t' | '\r' | '\n'] [_])+) { s.to_string() }
144
145 rule nl()
146 = quiet!{ws()* ("#" (!['\n'] [_])*)? "\n" ws()*}
147
148 rule ws()
149 = quiet!{[' ' | '\t' | '\r']}
150
151 rule i(literal: &'static str)
152 = input:$([_]*<{literal.len()}>)
153 {? if input.eq_ignore_ascii_case(literal) { Ok(()) } else { Err(literal) } }
154 }
155}
156
157#[derive(Debug)]
158pub enum SteinlibError {
159 Io(io::Error),
160 Parse(ParseError),
161 NoMixed,
162 UnmatchedDimension { dim: usize },
163 UnmatchedTerminalCount { got: usize, expected: usize },
164 InvalidLoop { node: usize },
165 Format { msg: String },
166}
167
168impl From<io::Error> for SteinlibError {
169 fn from(err: io::Error) -> Self {
170 SteinlibError::Io(err)
171 }
172}
173
174impl From<ParseError> for SteinlibError {
175 fn from(err: ParseError) -> Self {
176 SteinlibError::Parse(err)
177 }
178}
179
180impl fmt::Display for SteinlibError {
181 fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
182 use self::SteinlibError::*;
183 match self {
184 Io(err) => err.fmt(fmt),
185 Parse(err) => err.fmt(fmt),
186 NoMixed => write!(fmt, "Mixed graphs are not supported"),
187 UnmatchedDimension { dim } => write!(
188 fmt,
189 "Dimension of coordinates for node {} differs from previous dimension",
190 dim
191 ),
192 UnmatchedTerminalCount { got, expected } => write!(
193 fmt,
194 "Unexpected number of terminals (expected: {}, got: {})",
195 expected, got
196 ),
197 InvalidLoop { node } => write!(fmt, "Loops are not allowed (got ({},{}))", node, node),
198 Format { msg } => write!(fmt, "Format error: {}", msg),
199 }
200 }
201}
202
203impl error::Error for SteinlibError {
204 fn cause(&self) -> Option<&dyn error::Error> {
205 match self {
206 SteinlibError::Io(err) => Some(err),
207 SteinlibError::Parse(err) => Some(err),
208 _ => None,
209 }
210 }
211}
212
213#[derive(Debug, PartialEq)]
214pub enum GraphLine {
215 Edge(usize, usize, f64),
216 Arc(usize, usize, f64),
217 NumNodes(usize),
218 NumEdges(usize),
219 NumArcs(usize),
220}
221
222#[derive(Debug, PartialEq)]
223pub enum Section {
224 Comment(HashMap<String, String>),
225 Graph(Vec<GraphLine>),
226 Coordinates(Vec<(usize, Vec<f64>)>),
227 Terminals(usize, Vec<usize>),
228 Drawing(Vec<(usize, usize, Vec<EdgeAttr>)>),
229}
230
231pub fn read<G>(fname: &str) -> Result<Instance<G>, SteinlibError>
232where
233 G: for<'a> IndexGraph + Buildable,
234{
235 let mut f = fs::File::open(fname)?;
236 read_from_buf(&mut f)
237}
238
239pub fn read_from_buf<G, R>(buf: &mut R) -> Result<Instance<G>, SteinlibError>
243where
244 R: io::Read,
245 G: IndexGraph + Buildable,
246{
247 let data = {
248 let mut s = String::new();
249 buf.read_to_string(&mut s)?;
250 steinlib_parser::doc(&s)?
251 };
252
253 let mut b = G::Builder::new();
254
255 let mut nodes = vec![];
256 let mut nodecoords = vec![];
257 let mut nedges = 0;
258 let mut narcs = 0;
259 let mut weights = vec![];
260 let mut edgeattrs = vec![];
261 let mut edges = HashMap::new();
262
263 for sec in data {
264 match sec {
265 Section::Comment(_) => (),
266 Section::Terminals(n, terms) => {
267 if n != terms.len() {
268 return Err(SteinlibError::UnmatchedTerminalCount {
269 got: terms.len(),
270 expected: n,
271 });
272 }
273 }
274 Section::Graph(graph_lines) => {
275 let mut cntarcs = 0;
276 let mut cntedges = 0;
277 for line in graph_lines {
278 match line {
279 GraphLine::NumNodes(n) => {
280 if !nodes.is_empty() {
281 return Err(SteinlibError::Format {
282 msg: "Duplicate 'Nodes' keyword".to_string(),
283 });
284 }
285 nodes.extend_from_slice(&b.add_nodes(n));
286 nodecoords.resize(nodes.len(), vec![]);
287 }
288 GraphLine::NumEdges(m) => {
289 if nedges > 0 {
290 return Err(SteinlibError::Format {
291 msg: "Duplicate 'Edges' keyword".to_string(),
292 });
293 } else if narcs > 0 {
294 return Err(SteinlibError::NoMixed);
295 }
296 nedges = m;
297 b.reserve(0, m);
298 }
299 GraphLine::NumArcs(m) => {
300 if narcs > 0 {
301 return Err(SteinlibError::Format {
302 msg: "Duplicate 'Arcs' keyword".to_string(),
303 });
304 } else if nedges > 0 {
305 return Err(SteinlibError::NoMixed);
306 }
307 narcs = m;
308 b.reserve(0, m);
309 }
310 GraphLine::Edge(u, v, w) => {
311 if nedges == 0 {
312 return Err(SteinlibError::Format {
313 msg: "Midding 'Edges' line before first edge".to_string(),
314 });
315 } else if nedges <= cntedges {
316 return Err(SteinlibError::Format {
317 msg: format!("Too many edges (expected: {})", nedges),
318 });
319 } else if u == v {
320 return Err(SteinlibError::InvalidLoop { node: u });
321 } else if u < 1 || u > nodes.len() {
322 return Err(SteinlibError::Format {
323 msg: format!("Invalid node in edge: {} (must be in 1..{})", u, nodes.len()),
324 });
325 } else if v < 1 || v > nodes.len() {
326 return Err(SteinlibError::Format {
327 msg: format!("Invalid node in edge: {} (must be in 1..{})", v, nodes.len()),
328 });
329 }
330 b.add_edge(nodes[u - 1], nodes[v - 1]);
331 weights.push(w);
332 edges.insert((u, v), cntedges);
333 cntedges += 1;
334 }
335 GraphLine::Arc(u, v, w) => {
336 if narcs == 0 {
337 return Err(SteinlibError::Format {
338 msg: "Midding 'Arcs' line before first arc".to_string(),
339 });
340 } else if narcs <= cntarcs {
341 return Err(SteinlibError::Format {
342 msg: format!("Too many arcs (expected: {})", narcs),
343 });
344 } else if u == v {
345 return Err(SteinlibError::InvalidLoop { node: u });
346 } else if u < 1 || u > nodes.len() {
347 return Err(SteinlibError::Format {
348 msg: format!("Invalid node in arc: {} (must be in 1..{})", u, narcs),
349 });
350 } else if v < 1 || v > nodes.len() {
351 return Err(SteinlibError::Format {
352 msg: format!("Invalid node in arc: {} (must be in 1..{})", v, narcs),
353 });
354 }
355 b.add_edge(nodes[u - 1], nodes[v - 1]);
356 weights.push(w);
357 edges.insert((u, v), cntedges);
358 cntarcs += 1;
359 }
360 }
361 }
362
363 if nedges != cntedges {
364 return Err(SteinlibError::Format {
365 msg: format!("Wrong number of edges (found: {}, expected: {})", cntedges, nedges),
366 });
367 }
368 if narcs != cntarcs {
369 return Err(SteinlibError::Format {
370 msg: format!("Wrong number of arcs (found: {}, expected: {})", cntarcs, narcs),
371 });
372 }
373 }
374 Section::Coordinates(coords) => {
375 let mut dim = 0;
376 for (u, coord) in coords {
377 if u < 1 || u > nodes.len() {
378 return Err(SteinlibError::Format {
379 msg: format!("Invalid node in coordinate: {} (must be in 1..{})", u, narcs),
380 });
381 }
382 if dim > 0 && coord.len() != dim {
383 return Err(SteinlibError::UnmatchedDimension { dim: u });
384 }
385 dim = coord.len();
386 nodecoords[u - 1] = coord;
387 }
388 }
389 Section::Drawing(drawings) => {
390 edgeattrs.resize(nedges, vec![]);
391 for (u, v, attrs) in drawings {
392 if let Some(&e) = edges.get(&(u, v)) {
393 edgeattrs[e] = attrs;
394 } else {
395 return Err(SteinlibError::Format {
396 msg: format!("Unknown edge in drawing: ({},{})", u, v),
397 });
398 }
399 }
400 }
401 };
402 }
403
404 Ok(Instance {
405 graph: b.into_graph(),
406 weights,
407 coordinates: nodecoords,
408 edgeattrs,
409 })
410}
411
412#[cfg(test)]
413mod tests {
414
415 use super::steinlib_parser::doc as steinlib;
416 use super::steinlib_parser::{
417 comment_section, coordinates_section, drawing_section, graph_section, terminals_section,
418 };
419 use super::{read_from_buf, GraphLine, Section};
420 use crate::steinlib::EdgeAttr;
421 use crate::traits::{FiniteDigraph, FiniteGraph, IndexGraph};
422 use crate::LinkedListGraph;
423
424 #[test]
425 fn test_graph_section() {
426 let r = graph_section("SECTION Graph\n NODES 5\n ARCS 2\n E 1 2 3 # Kante bla\n A 4 5 6\nEND\n");
427 assert_eq!(
428 r,
429 Ok(Section::Graph(vec![
430 GraphLine::NumNodes(5),
431 GraphLine::NumArcs(2),
432 GraphLine::Edge(1, 2, 3.0),
433 GraphLine::Arc(4, 5, 6.0),
434 ]))
435 );
436 }
437
438 #[test]
439 fn test_coordinates_section() {
440 let r = coordinates_section("SECTION coordinates\n D 1 5\n DD 2 2 3\n \n DDD 3 1 2 3 # Kante bla\nEND\n");
441 assert_eq!(
442 r,
443 Ok(Section::Coordinates(vec![
444 (1, vec![5.0]),
445 (2, vec![2.0, 3.0]),
446 (3, vec![1.0, 2.0, 3.0]),
447 ]))
448 );
449 }
450
451 #[test]
452 fn test_comment_section() {
453 let r = comment_section("SECTION comment\n Name \"blaa\" \n\n Creator \"Me the king\"\nEND\n");
454 assert_eq!(
455 r,
456 Ok(Section::Comment(
457 [
458 ("Name".to_string(), "blaa".to_string()),
459 ("Creator".to_string(), "Me the king".to_string())
460 ]
461 .iter()
462 .cloned()
463 .collect()
464 ))
465 );
466 }
467
468 #[test]
469 fn test_terminals_section() {
470 let r = terminals_section("SECTION Terminals\n TERMINALS 2 \n\n T 1 \n T 2 \nEND\n");
471 assert_eq!(r, Ok(Section::Terminals(2, vec![1, 2])));
472 }
473
474 #[test]
475 fn test_drawing_section() {
476 let r = drawing_section("SECTION Drawing\n E 1 2 bl \n E 3 4 br \nEND\n");
477 assert_eq!(
478 r,
479 Ok(Section::Drawing(vec![
480 (1, 2, vec![EdgeAttr::BendLeft]),
481 (3, 4, vec![EdgeAttr::BendRight]),
482 ]))
483 )
484 }
485
486 #[test]
487 fn test_parser() {
488 let file = "33D32945 STP File, STP Format Version 1.0
489 SECTION Comment
490 Name \"Odd Wheel\"
491 Creator \"T. Koch, A. Martin and S. Voss\"
492 Remark \"Example used to describe the STP data format\"
493 END
494 SECTION Graph
495 Nodes 7
496 Edges 9
497 E 1 2 1
498 E 1 4 1
499 E 1 6 1
500 E 2 3 1
501 E 3 4 1
502 E 4 5 1
503 E 5 6 1
504 E 6 7 1
505 E 7 2 1
506 END
507
508
509 SECTION Terminals
510 Terminals 4
511 T 1
512 T 3
513 T 5
514 T 7
515 END
516
517 SECTION Coordinates
518 DD 1 80 50
519 DD 2 30 50
520 DD 3 55 5
521 DD 4 105 5
522 DD 5 130 50
523 DD 6 105 95
524 DD 7 55 95
525 END
526
527 SECTION Drawing
528 E 1 2 bl
529 E 6 7 BR
530 END
531
532 EOF";
533
534 let r = steinlib(file);
535 assert_eq!(
536 r,
537 Ok(vec![
538 Section::Comment(
539 [
540 ("Name".to_string(), "Odd Wheel".to_string()),
541 ("Creator".to_string(), "T. Koch, A. Martin and S. Voss".to_string()),
542 (
543 "Remark".to_string(),
544 "Example used to describe the STP data format".to_string(),
545 ),
546 ]
547 .iter()
548 .cloned()
549 .collect(),
550 ),
551 Section::Graph(vec![
552 GraphLine::NumNodes(7),
553 GraphLine::NumEdges(9),
554 GraphLine::Edge(1, 2, 1.0),
555 GraphLine::Edge(1, 4, 1.0),
556 GraphLine::Edge(1, 6, 1.0),
557 GraphLine::Edge(2, 3, 1.0),
558 GraphLine::Edge(3, 4, 1.0),
559 GraphLine::Edge(4, 5, 1.0),
560 GraphLine::Edge(5, 6, 1.0),
561 GraphLine::Edge(6, 7, 1.0),
562 GraphLine::Edge(7, 2, 1.0),
563 ]),
564 Section::Terminals(4, vec![1, 3, 5, 7]),
565 Section::Coordinates(vec![
566 (1, vec![80.0, 50.0]),
567 (2, vec![30.0, 50.0]),
568 (3, vec![55.0, 5.0]),
569 (4, vec![105.0, 5.0]),
570 (5, vec![130.0, 50.0]),
571 (6, vec![105.0, 95.0]),
572 (7, vec![55.0, 95.0]),
573 ]),
574 Section::Drawing(vec![
575 (1, 2, vec![EdgeAttr::BendLeft]),
576 (6, 7, vec![EdgeAttr::BendRight]),
577 ]),
578 ])
579 )
580 }
581
582 #[test]
583 fn test_read() {
584 let mut file: &[u8] = &b"33D32945 STP File, STP Format Version 1.0
585 SECTION Comment
586 Name \"Odd Wheel\"
587 Creator \"T. Koch, A. Martin and S. Voss\"
588 Remark \"Example used to describe the STP data format\"
589 END
590 SECTION Graph
591 Nodes 7
592 Edges 9
593 E 1 2 1
594 E 1 4 2
595 E 1 6 3
596 E 2 3 4
597 E 3 4 5
598 E 4 5 6
599 E 5 6 7
600 E 6 7 8
601 E 7 2 9
602 END
603
604
605 SECTION Terminals
606 Terminals 4
607 T 1
608 T 3
609 T 5
610 T 7
611 END
612
613 SECTION Coordinates
614 DD 1 80 50
615 DD 2 30 50
616 DD 3 55 5
617 DD 4 105 5
618 DD 5 130 50
619 DD 6 105 95
620 DD 7 55 95
621 END
622
623 SECTION Drawing
624 E 1 2 bl
625 E 6 7 BR
626 END
627
628 EOF"[..];
629
630 let instance = read_from_buf::<LinkedListGraph, _>(&mut file).unwrap();
631 let g = instance.graph;
632 let weights = instance.weights;
633 let coords = instance.coordinates;
634 let edgeattrs = instance.edgeattrs;
635
636 assert_eq!(g.num_nodes(), 7);
637 assert_eq!(g.num_edges(), 9);
638
639 let edges: Vec<_> = g
640 .edges()
641 .map(|e| (g.node_id(g.src(e)) + 1, g.node_id(g.snk(e)) + 1, weights[g.edge_id(e)]))
642 .collect();
643 assert_eq!(
644 edges,
645 vec![
646 (1, 2, 1.0),
647 (1, 4, 2.0),
648 (1, 6, 3.0),
649 (2, 3, 4.0),
650 (3, 4, 5.0),
651 (4, 5, 6.0),
652 (5, 6, 7.0),
653 (6, 7, 8.0),
654 (7, 2, 9.0),
655 ]
656 );
657
658 assert_eq!(
659 coords,
660 vec![
661 vec![80.0, 50.0],
662 vec![30.0, 50.0],
663 vec![55.0, 5.0],
664 vec![105.0, 5.0],
665 vec![130.0, 50.0],
666 vec![105.0, 95.0],
667 vec![55.0, 95.0],
668 ]
669 );
670
671 let edgeattrs: Vec<_> = g
672 .edges()
673 .map(|e| {
674 (
675 g.node_id(g.src(e)) + 1,
676 g.node_id(g.snk(e)) + 1,
677 edgeattrs[g.edge_id(e)].clone(),
678 )
679 })
680 .filter(|&(_, _, ref attrs)| !attrs.is_empty())
681 .collect();
682
683 assert_eq!(
684 edgeattrs,
685 vec![(1, 2, vec![EdgeAttr::BendLeft]), (6, 7, vec![EdgeAttr::BendRight])]
686 )
687 }
688}