1use crate::lexer::{Lexer, ScanState, Token, TokenKind};
6type AttrMap2 = std::collections::BTreeMap<String, graphviz_pure_core::AttrSym>;
7
8use graphviz_pure_core::{
9 AttrMap, BoundingBox, EdgeId, Graph, GraphKind, GraphvizError, NodeId, Port, Result,
10 SubgraphData, SubgraphId,
11};
12
13const MAX_SUBGRAPH_DEPTH: usize = 256;
14
15#[derive(Debug, Clone)]
16enum SimpleItem {
17 Nodes(Vec<(NodeId, Port)>),
18 Subgraph(Vec<(NodeId, Port)>),
19}
20
21impl SimpleItem {
22 fn into_endpoints(self) -> Vec<(NodeId, Port)> {
23 match self {
24 Self::Nodes(v) | Self::Subgraph(v) => v,
25 }
26 }
27}
28
29#[derive(Debug, Clone, Default, PartialEq, Eq)]
32pub struct GlobalAttrs {
33 pub graph: AttrMap,
34 pub node: AttrMap,
35 pub edge: AttrMap,
36}
37
38pub struct Parser {
39 tokens: Vec<Token>,
40 pos: usize,
41 graph: Graph,
42 scope_stack: Vec<SubgraphId>,
43 globals: GlobalAttrs,
44 rbrace_line: usize,
46 attr_order: Vec<String>,
49 anon_ctr: u64,
51 sub_parent: Vec<Option<usize>>,
53}
54
55#[derive(Clone, Copy, PartialEq, Eq)]
57enum DictKind {
58 Graph,
59 Node,
60 Edge,
61}
62
63impl Parser {
64 fn new(input: &str, globals: &GlobalAttrs) -> Result<Self> {
65 let mut lexer = Lexer::new(input);
66 let tokens = lexer.tokenize_all()?;
67 Ok(Self {
68 tokens,
69 pos: 0,
70 graph: Graph::new("", GraphKind::Undirected, false),
71 scope_stack: Vec::new(),
72 globals: globals.clone(),
73 rbrace_line: 0,
74 attr_order: Vec::new(),
75 anon_ctr: 1,
76 sub_parent: Vec::new(),
77 })
78 }
79
80 pub fn parse_str(input: &str) -> Result<Graph> {
82 let mut parser = Self::new(input, &GlobalAttrs::default())?;
83 parser.parse_graph()?;
84 Ok(parser.graph)
85 }
86
87 pub fn parse_all(input: &str, globals: &GlobalAttrs) -> Result<Vec<Graph>> {
89 match Self::parse_all_partial(input, globals) {
90 (graphs, None) => Ok(graphs),
91 (_, Some(e)) => Err(e),
92 }
93 }
94
95 pub fn parse_all_partial(
103 input: &str,
104 globals: &GlobalAttrs,
105 ) -> (Vec<Graph>, Option<GraphvizError>) {
106 let mut graphs = Vec::new();
107 let mut rest = input;
108 loop {
109 let mut parser = match Self::new(rest, globals) {
110 Ok(p) => p,
111 Err(e) => return (graphs, Some(e)),
112 };
113 if parser.peek().kind == TokenKind::Eof {
114 return (graphs, None);
115 }
116 if let Err(e) = parser.parse_graph() {
117 return (graphs, Some(e));
118 }
119 graphs.push(parser.graph);
120 match rest
122 .match_indices('\n')
123 .nth(parser.rbrace_line.saturating_sub(1))
124 {
125 Some((i, _)) => rest = &rest[i + 1..],
126 None => return (graphs, None),
127 }
128 }
129 }
130
131 fn peek(&self) -> &Token {
132 self.tokens
133 .get(self.pos)
134 .unwrap_or_else(|| self.tokens.last().expect("tokens always ends with Eof"))
135 }
136
137 fn peek_n(&self, offset: usize) -> &Token {
138 self.tokens
139 .get(self.pos + offset)
140 .unwrap_or_else(|| self.tokens.last().expect("tokens always ends with Eof"))
141 }
142
143 fn advance(&mut self) -> Token {
144 let tok = self.peek().clone();
145 if self.pos + 1 < self.tokens.len() {
146 self.pos += 1;
147 }
148 tok
149 }
150
151 fn expect(&mut self, expected: &TokenKind) -> Result<Token> {
152 if &self.peek().kind == expected {
153 Ok(self.advance())
154 } else {
155 Err(syntax_error(self.peek()))
156 }
157 }
158
159 fn is_atom_token(kind: &TokenKind) -> bool {
160 matches!(
161 kind,
162 TokenKind::Ident(_) | TokenKind::Quoted(_) | TokenKind::Html(_)
163 )
164 }
165
166 fn parse_atom(&mut self) -> Result<String> {
172 let tok = self.peek().clone();
173 let mut is_html = false;
174 let mut val = match tok.kind {
175 TokenKind::Ident(s) | TokenKind::Quoted(s) => {
176 self.advance();
177 s
178 }
179 TokenKind::Html(s) => {
180 self.advance();
181 is_html = true;
182 s
183 }
184 _ => return Err(syntax_error(&tok)),
185 };
186
187 while self.peek().kind == TokenKind::Plus {
189 self.advance();
190 let next_tok = self.peek().clone();
191 match next_tok.kind {
192 TokenKind::Ident(s) | TokenKind::Quoted(s) | TokenKind::Html(s) => {
193 self.advance();
194 is_html = false;
195 val.push_str(&s);
196 }
197 _ => return Err(syntax_error(&next_tok)),
198 }
199 }
200
201 if is_html {
202 self.graph.html_strings.insert(val.clone());
203 }
204 Ok(val)
205 }
206
207 fn atom_span_len(&self, start_offset: usize) -> Option<usize> {
210 if !Self::is_atom_token(&self.peek_n(start_offset).kind) {
211 return None;
212 }
213 let mut len = 1;
214 while self.peek_n(start_offset + len).kind == TokenKind::Plus {
215 if Self::is_atom_token(&self.peek_n(start_offset + len + 1).kind) {
216 len += 2;
217 } else {
218 break;
219 }
220 }
221 Some(len)
222 }
223
224 fn parse_graph(&mut self) -> Result<()> {
225 let strict = if self.peek().kind == TokenKind::Strict {
226 self.advance();
227 true
228 } else {
229 false
230 };
231
232 let tok = self.peek().clone();
233 let kind = match tok.kind {
234 TokenKind::Graph => {
235 self.advance();
236 GraphKind::Undirected
237 }
238 TokenKind::Digraph => {
239 self.advance();
240 GraphKind::Directed
241 }
242 _ => return Err(syntax_error(&tok)),
243 };
244
245 let name = if Self::is_atom_token(&self.peek().kind) {
246 self.parse_atom()?
247 } else {
248 String::new()
249 };
250
251 self.anon_ctr = if name.is_empty() { 3 } else { 1 };
252 self.graph = Graph::new(name, kind, strict);
253 self.graph.graph_attrs.extend(self.globals.graph.clone());
254 self.graph.node_defaults.extend(self.globals.node.clone());
255 self.graph.edge_defaults.extend(self.globals.edge.clone());
256 self.sub_parent.clear();
257 self.graph.dicts = vec![graphviz_pure_core::AttrDicts::default()];
258 self.graph.dicts[0].node.insert(
260 "label".to_string(),
261 graphviz_pure_core::AttrSym {
262 defval: "\\N".to_string(),
263 print: false,
264 id: 0,
265 },
266 );
267 for (kind, map) in [
269 (DictKind::Graph, self.globals.graph.clone()),
270 (DictKind::Node, self.globals.node.clone()),
271 (DictKind::Edge, self.globals.edge.clone()),
272 ] {
273 for (k, v) in map {
274 let d = self.dict_mut(0, kind);
275 let id = d.get(&k).map_or(d.len(), |s| s.id);
276 d.insert(
277 k,
278 graphviz_pure_core::AttrSym {
279 defval: v,
280 print: false,
281 id,
282 },
283 );
284 }
285 }
286
287 self.expect(&TokenKind::LBrace)?;
288 self.parse_stmt_list()?;
289 self.rbrace_line = self.expect(&TokenKind::RBrace)?.line;
290 let declared: Vec<(String, String)> = self.graph.dicts[0]
293 .graph
294 .iter()
295 .map(|(k, v)| (k.clone(), v.defval.clone()))
296 .collect();
297 for (k, v) in declared {
298 self.graph.graph_attrs.entry(k).or_insert(v);
299 }
300 while self.peek().kind == TokenKind::Semi {
302 self.advance();
303 }
304
305 Ok(())
308 }
309
310 fn parse_stmt_list(&mut self) -> Result<()> {
311 while self.peek().kind != TokenKind::RBrace && self.peek().kind != TokenKind::Eof {
312 if self.peek().kind == TokenKind::Semi {
313 self.advance();
314 continue;
315 }
316 self.parse_stmt()?;
317 if self.peek().kind == TokenKind::Semi {
318 self.advance();
319 }
320 }
321 Ok(())
322 }
323
324 fn parse_stmt(&mut self) -> Result<()> {
325 match self.peek().kind {
326 TokenKind::Graph | TokenKind::Node | TokenKind::Edge => self.parse_attr_stmt(),
327 TokenKind::Subgraph | TokenKind::LBrace => self.parse_compound(),
328 _ if Self::is_atom_token(&self.peek().kind) => {
329 if let Some(span) = self.atom_span_len(0)
330 && self.peek_n(span).kind == TokenKind::Equals
331 {
332 let key = self.parse_atom()?;
333 self.expect(&TokenKind::Equals)?;
334 let val = self.parse_atom()?;
335 if !self.fixed_at_root(DictKind::Graph, &key) {
336 self.attr_stmt(DictKind::Graph, &key, &val);
337 self.current_graph_attrs_mut().insert(key, val);
338 } else {
339 if let Some(sym) = self.dict_mut(0, DictKind::Graph).get_mut(&key) {
340 sym.print = true;
341 }
342 }
343 return Ok(());
344 }
345 self.parse_compound()
346 }
347 _ => Err(syntax_error(self.peek())),
348 }
349 }
350
351 fn parse_attr_stmt(&mut self) -> Result<()> {
352 let attr_tok = self.advance();
353 if let Some(span) = self.atom_span_len(0)
355 && self.peek_n(span).kind == TokenKind::Equals
356 {
357 let _macro_name = self.parse_atom()?;
358 self.expect(&TokenKind::Equals)?;
359 }
360
361 if self.peek().kind != TokenKind::LBracket {
362 return Err(syntax_error(self.peek()));
363 }
364
365 let attrs = self.parse_opt_attr_list()?;
366 let kind = match attr_tok.kind {
367 TokenKind::Graph => DictKind::Graph,
368 TokenKind::Node => DictKind::Node,
369 _ => DictKind::Edge,
370 };
371 let mut kept = AttrMap::new();
373 for (k, v) in attrs {
374 if self.fixed_at_root(kind, &k) {
375 if let Some(sym) = self.dict_mut(0, kind).get_mut(&k) {
376 sym.print = true;
377 }
378 } else {
379 kept.insert(k, v);
380 }
381 }
382 let attrs = kept;
383 for k in std::mem::take(&mut self.attr_order) {
384 if let Some(v) = attrs.get(&k) {
385 self.attr_stmt(kind, &k, v);
386 }
387 }
388 match attr_tok.kind {
389 TokenKind::Graph => self.current_graph_attrs_mut().extend(attrs),
390 TokenKind::Node => self.current_node_defaults_mut().extend(attrs),
391 TokenKind::Edge => self.current_edge_defaults_mut().extend(attrs),
392 _ => unreachable!(),
393 }
394 Ok(())
395 }
396
397 fn parse_opt_attr_list(&mut self) -> Result<AttrMap> {
398 let mut attrs = AttrMap::new();
399 self.attr_order.clear();
400 while self.peek().kind == TokenKind::LBracket {
401 self.advance(); while self.peek().kind != TokenKind::RBracket && self.peek().kind != TokenKind::Eof {
404 let key = self.parse_atom()?;
405 self.expect(&TokenKind::Equals)?;
406 let val = self.parse_atom()?;
407 if !attrs.contains_key(&key) {
408 self.attr_order.push(key.clone());
409 }
410 attrs.insert(key, val);
411 if matches!(self.peek().kind, TokenKind::Comma | TokenKind::Semi) {
412 self.advance();
413 }
414 }
415 self.expect(&TokenKind::RBracket)?;
416 }
417 Ok(attrs)
418 }
419
420 fn parse_compound(&mut self) -> Result<()> {
421 let first = self.parse_simple()?;
422
423 if matches!(self.peek().kind, TokenKind::Arrow | TokenKind::DashDash) {
424 let mut chain = vec![first.into_endpoints()];
425 while matches!(self.peek().kind, TokenKind::Arrow | TokenKind::DashDash) {
426 let op_tok = self.advance();
427 match (&op_tok.kind, self.graph.kind) {
430 (TokenKind::Arrow, GraphKind::Undirected)
431 | (TokenKind::DashDash, GraphKind::Directed) => {
432 return Err(syntax_error(&op_tok));
433 }
434 _ => {}
435 }
436 let next_item = self.parse_simple()?;
437 chain.push(next_item.into_endpoints());
438 }
439
440 let edge_attrs = self.parse_opt_attr_list()?;
441 for k in std::mem::take(&mut self.attr_order) {
442 if k != "key" {
443 self.bind_attr(DictKind::Edge, &k);
444 }
445 }
446 for pair in chain.windows(2) {
447 let tails = &pair[0];
448 let heads = &pair[1];
449 for (tail_id, tail_port) in tails {
450 for (head_id, head_port) in heads {
451 self.create_edge(
452 *tail_id,
453 tail_port.clone(),
454 *head_id,
455 head_port.clone(),
456 &edge_attrs,
457 );
458 }
459 }
460 }
461 } else {
462 let attrs = self.parse_opt_attr_list()?;
463 if matches!(first, SimpleItem::Nodes(_)) {
464 for k in std::mem::take(&mut self.attr_order) {
465 self.bind_attr(DictKind::Node, &k);
466 }
467 }
468 if let SimpleItem::Nodes(nodes) = first
469 && !attrs.is_empty()
470 {
471 for (node_id, _port) in nodes {
472 self.graph.nodes[node_id.0 as usize]
473 .attrs
474 .extend(attrs.clone());
475 }
476 }
477 }
478
479 Ok(())
480 }
481
482 fn parse_simple(&mut self) -> Result<SimpleItem> {
483 if matches!(self.peek().kind, TokenKind::Subgraph | TokenKind::LBrace) {
484 let subg_id = self.parse_subgraph()?;
485 let mut ids: Vec<NodeId> = Vec::new();
488 let mut stack = vec![subg_id];
489 while let Some(s) = stack.pop() {
490 let sg = &self.graph.subgraphs[s.0 as usize];
491 ids.extend(sg.nodes.iter().copied());
492 stack.extend(sg.subgraphs.iter().copied());
493 }
494 ids.sort_by_key(|n| n.0);
495 ids.dedup();
496 let endpoints = ids.into_iter().map(|nid| (nid, Port::default())).collect();
497 Ok(SimpleItem::Subgraph(endpoints))
498 } else {
499 let mut nodes = vec![self.parse_node_ref()?];
500 while self.peek().kind == TokenKind::Comma {
501 self.advance();
502 nodes.push(self.parse_node_ref()?);
503 }
504 Ok(SimpleItem::Nodes(nodes))
505 }
506 }
507
508 fn parse_node_ref(&mut self) -> Result<(NodeId, Port)> {
509 let name = self.parse_atom()?;
510 let mut port = Port::default();
511 if self.peek().kind == TokenKind::Colon {
512 self.advance();
513 let p1 = self.parse_atom()?;
514 if self.peek().kind == TokenKind::Colon {
515 self.advance();
516 let p2 = self.parse_atom()?;
517 port.name = Some(p1);
518 port.compass = Some(p2);
519 } else {
520 port.name = Some(p1);
521 }
522 }
523 let node_id = self.ensure_scoped_node(&name);
524 Ok((node_id, port))
525 }
526
527 fn parse_subgraph(&mut self) -> Result<SubgraphId> {
528 let name = if self.peek().kind == TokenKind::Subgraph {
529 self.advance();
530 if Self::is_atom_token(&self.peek().kind) {
531 self.parse_atom()?
532 } else {
533 String::new()
534 }
535 } else {
536 String::new()
537 };
538
539 if self.scope_stack.len() >= MAX_SUBGRAPH_DEPTH {
540 let tok = self.peek();
541 return Err(GraphvizError::SyntaxError {
542 line: tok.c_line,
543 col: tok.col,
544 message: format!("subgraphs nested more than {MAX_SUBGRAPH_DEPTH} deep"),
545 file: None,
546 });
547 }
548
549 let subg_id = self.open_subgraph(name.clone());
550
551 if self.peek().kind == TokenKind::LBrace {
552 self.scope_stack.push(subg_id);
553 self.advance(); self.parse_stmt_list()?;
555 self.expect(&TokenKind::RBrace)?;
556 self.scope_stack.pop();
557 } else if name.is_empty() {
558 return Err(syntax_error(self.peek()));
559 }
560
561 Ok(subg_id)
562 }
563
564 fn open_subgraph(&mut self, name: String) -> SubgraphId {
565 let current_parent = self.scope_stack.last().copied();
566 if !name.is_empty()
567 && let Some(existing_id) = self.graph.subgraphs.iter().find_map(|s| {
568 if s.name != name {
569 return None;
570 }
571 let has_same_parent = match current_parent {
572 Some(pid) => self.graph.subgraphs[pid.0 as usize]
573 .subgraphs
574 .contains(&s.id),
575 None => !self
576 .graph
577 .subgraphs
578 .iter()
579 .any(|p| p.subgraphs.contains(&s.id)),
580 };
581 has_same_parent.then_some(s.id)
582 })
583 {
584 return existing_id;
585 }
586
587 let id = SubgraphId(self.graph.subgraphs.len() as u32);
588 if name.is_empty() {
589 self.graph.anon_subgraph_ids.insert(id.0, self.anon_ctr);
590 self.anon_ctr += 2;
591 }
592 let is_cluster = name.to_ascii_lowercase().starts_with("cluster");
593 let graph_attrs = self.current_graph_attrs().clone();
594 let node_defaults = self.current_node_defaults().clone();
595 let edge_defaults = self.current_edge_defaults().clone();
596
597 self.sub_parent
598 .push(self.scope_stack.last().map(|p| p.0 as usize));
599 self.graph
600 .dicts
601 .push(graphviz_pure_core::AttrDicts::default());
602 if let Some(&parent_id) = self.scope_stack.last() {
603 let parent_subgraphs = &mut self.graph.subgraphs[parent_id.0 as usize].subgraphs;
604 if !parent_subgraphs.contains(&id) {
605 parent_subgraphs.push(id);
606 }
607 }
608
609 self.graph.subgraphs.push(SubgraphData {
610 id,
611 name,
612 is_cluster,
613 graph_attrs,
614 node_defaults,
615 edge_defaults,
616 nodes: Vec::new(),
617 edges: Vec::new(),
618 subgraphs: Vec::new(),
619 bb: BoundingBox::default(),
620 });
621
622 id
623 }
624
625 fn fixed_at_root(&self, kind: DictKind, name: &str) -> bool {
629 self.scope_dict() == 0
630 && match kind {
631 DictKind::Graph => self.globals.graph.contains_key(name),
632 DictKind::Node => self.globals.node.contains_key(name),
633 DictKind::Edge => self.globals.edge.contains_key(name),
634 }
635 }
636
637 fn scope_dict(&self) -> usize {
638 self.scope_stack.last().map_or(0, |s| s.0 as usize + 1)
639 }
640
641 fn dict_mut(&mut self, gi: usize, kind: DictKind) -> &mut AttrMap2 {
642 let d = &mut self.graph.dicts[gi];
643 match kind {
644 DictKind::Graph => &mut d.graph,
645 DictKind::Node => &mut d.node,
646 DictKind::Edge => &mut d.edge,
647 }
648 }
649
650 fn dict(&self, gi: usize, kind: DictKind) -> &AttrMap2 {
651 let d = &self.graph.dicts[gi];
652 match kind {
653 DictKind::Graph => &d.graph,
654 DictKind::Node => &d.node,
655 DictKind::Edge => &d.edge,
656 }
657 }
658
659 fn lookup_view(&self, gi: usize, kind: DictKind, name: &str) -> bool {
661 let mut cur = Some(gi);
662 while let Some(g) = cur {
663 if self.dict(g, kind).contains_key(name) {
664 return true;
665 }
666 cur = if g == 0 {
667 None
668 } else {
669 Some(self.sub_parent[g - 1].map_or(0, |p| p + 1))
670 };
671 }
672 false
673 }
674
675 fn bind_attr(&mut self, kind: DictKind, name: &str) {
677 let gi = self.scope_dict();
678 if !self.lookup_view(gi, kind, name) {
679 let d = self.dict_mut(0, kind);
680 let id = d.len();
681 d.insert(
682 name.to_string(),
683 graphviz_pure_core::AttrSym {
684 id,
685 ..Default::default()
686 },
687 );
688 }
689 }
690
691 fn attr_stmt(&mut self, kind: DictKind, name: &str, value: &str) {
693 self.bind_attr(kind, name);
694 let gi = self.scope_dict();
695 if self.dict(gi, kind).contains_key(name) {
696 if kind == DictKind::Graph {
697 let children: Vec<usize> = if gi == 0 {
699 (0..self.sub_parent.len())
700 .filter(|&i| self.sub_parent[i].is_none())
701 .collect()
702 } else {
703 self.graph.subgraphs[gi - 1]
704 .subgraphs
705 .iter()
706 .map(|c| c.0 as usize)
707 .collect()
708 };
709 for c in children {
710 if !self.graph.dicts[c + 1].graph.contains_key(name) {
711 let v = self.graph.subgraphs[c]
712 .graph_attrs
713 .get(name)
714 .cloned()
715 .unwrap_or_default();
716 self.graph.dicts[c + 1].graph.insert(
717 name.to_string(),
718 graphviz_pure_core::AttrSym {
719 defval: v,
720 ..Default::default()
721 },
722 );
723 }
724 }
725 }
726 self.dict_mut(gi, kind).get_mut(name).unwrap().defval = value.to_string();
727 } else {
728 self.dict_mut(gi, kind).insert(
729 name.to_string(),
730 graphviz_pure_core::AttrSym {
731 defval: value.to_string(),
732 ..Default::default()
733 },
734 );
735 }
736 if gi == 0 {
737 self.dict_mut(0, kind).get_mut(name).unwrap().print = true;
738 }
739 }
740
741 fn ensure_scoped_node(&mut self, name: &str) -> NodeId {
742 let is_new = !self.graph.node_lookup.contains_key(name);
743 let node_id = self.graph.ensure_node(name);
744 if is_new {
745 let mut attrs = self.current_node_defaults().clone();
746 attrs
747 .entry("label".to_string())
748 .or_insert_with(|| "\\N".to_string());
749 self.graph.nodes[node_id.0 as usize].attrs = attrs;
750 }
751 self.record_node_in_active_subgraphs(node_id);
752 node_id
753 }
754
755 fn create_edge(
756 &mut self,
757 tail: NodeId,
758 mut tail_port: Port,
759 head: NodeId,
760 mut head_port: Port,
761 edge_attrs: &AttrMap,
762 ) -> EdgeId {
763 let mut merged_attrs = self.current_edge_defaults().clone();
764 merged_attrs.extend(edge_attrs.clone());
765
766 let undirected = self.graph.kind == graphviz_pure_core::GraphKind::Undirected;
769 let exists = self.graph.strict
770 && self.graph.edges.iter().any(|e| {
771 (e.tail == tail && e.head == head)
772 || (undirected && e.tail == head && e.head == tail)
773 });
774 if exists {
775 merged_attrs = edge_attrs.clone();
776 }
777 let before = self.graph.edges.len();
778 let edge_id = self.graph.add_edge(tail, head, merged_attrs.clone());
779 let is_new = self.graph.edges.len() > before;
780 if is_new {
781 self.anon_ctr += 2;
782 }
783 if let Some(edge) = self.graph.edges.get(edge_id.0 as usize) {
784 if edge.tail != edge.head && edge.head == tail {
787 std::mem::swap(&mut tail_port, &mut head_port);
788 }
789 for (key, endpoint) in [("tailport", &tail_port), ("headport", &head_port)] {
794 if *endpoint != Port::default() {
795 self.bind_attr(DictKind::Edge, key);
796 }
797 }
798 }
799 if let Some(edge) = self.graph.edges.get_mut(edge_id.0 as usize) {
800 for (key, endpoint) in [("tailport", &tail_port), ("headport", &head_port)] {
805 if *endpoint != Port::default() && !edge_attrs.contains_key(key) {
806 merged_attrs.insert(key.to_string(), port_to_str(endpoint));
807 }
808 }
809 edge.attrs.extend(merged_attrs);
810 edge.tail_port = edge
811 .attrs
812 .get("tailport")
813 .map(|s| parse_port_str(s))
814 .unwrap_or_default();
815 edge.head_port = edge
816 .attrs
817 .get("headport")
818 .map(|s| parse_port_str(s))
819 .unwrap_or_default();
820 }
821
822 self.record_node_in_active_subgraphs(tail);
823 self.record_node_in_active_subgraphs(head);
824 self.record_edge_in_active_subgraphs(edge_id);
825 edge_id
826 }
827
828 fn record_node_in_active_subgraphs(&mut self, node_id: NodeId) {
829 for &subg_id in &self.scope_stack {
830 let nodes = &mut self.graph.subgraphs[subg_id.0 as usize].nodes;
831 if !nodes.contains(&node_id) {
832 nodes.push(node_id);
833 }
834 }
835 }
836
837 fn record_edge_in_active_subgraphs(&mut self, edge_id: EdgeId) {
838 for &subg_id in &self.scope_stack {
839 let edges = &mut self.graph.subgraphs[subg_id.0 as usize].edges;
840 if !edges.contains(&edge_id) {
841 edges.push(edge_id);
842 }
843 }
844 }
845
846 fn current_graph_attrs(&self) -> &AttrMap {
847 if let Some(&subg_id) = self.scope_stack.last() {
848 &self.graph.subgraphs[subg_id.0 as usize].graph_attrs
849 } else {
850 &self.graph.graph_attrs
851 }
852 }
853
854 fn current_graph_attrs_mut(&mut self) -> &mut AttrMap {
855 if let Some(&subg_id) = self.scope_stack.last() {
856 &mut self.graph.subgraphs[subg_id.0 as usize].graph_attrs
857 } else {
858 &mut self.graph.graph_attrs
859 }
860 }
861
862 fn current_node_defaults(&self) -> &AttrMap {
863 if let Some(&subg_id) = self.scope_stack.last() {
864 &self.graph.subgraphs[subg_id.0 as usize].node_defaults
865 } else {
866 &self.graph.node_defaults
867 }
868 }
869
870 fn current_node_defaults_mut(&mut self) -> &mut AttrMap {
871 if let Some(&subg_id) = self.scope_stack.last() {
872 &mut self.graph.subgraphs[subg_id.0 as usize].node_defaults
873 } else {
874 &mut self.graph.node_defaults
875 }
876 }
877
878 fn current_edge_defaults(&self) -> &AttrMap {
879 if let Some(&subg_id) = self.scope_stack.last() {
880 &self.graph.subgraphs[subg_id.0 as usize].edge_defaults
881 } else {
882 &self.graph.edge_defaults
883 }
884 }
885
886 fn current_edge_defaults_mut(&mut self) -> &mut AttrMap {
887 if let Some(&subg_id) = self.scope_stack.last() {
888 &mut self.graph.subgraphs[subg_id.0 as usize].edge_defaults
889 } else {
890 &mut self.graph.edge_defaults
891 }
892 }
893}
894
895const YY_BUF_SIZE: usize = 16384;
897
898fn syntax_error(tok: &Token) -> GraphvizError {
902 let mut message = format!("syntax error in line {}", tok.c_line);
903 if !tok.text.is_empty() {
904 message.push_str(&format!(" near '{}'", tok.text));
905 } else {
906 let first_80 = |s: &str| {
908 let bytes = graphviz_pure_core::charset::encode(s);
909 graphviz_pure_core::charset::decode_slice(&bytes[..bytes.len().min(80)])
910 };
911 match &tok.state {
912 ScanState::QString(sbuf) => {
913 message.push_str(&format!(
914 " scanning a quoted string (missing endquote? longer than {YY_BUF_SIZE}?)"
915 ));
916 if !sbuf.is_empty() {
917 message.push_str(&format!("\nString starting:\"{}", first_80(sbuf)));
918 }
919 }
920 ScanState::HString(sbuf) => {
921 message.push_str(&format!(
922 " scanning a HTML string (missing '>'? bad nesting? longer than {YY_BUF_SIZE}?)"
923 ));
924 if !sbuf.is_empty() {
925 message.push_str(&format!("\nString starting:<{}", first_80(sbuf)));
926 }
927 }
928 ScanState::Comment => message.push_str(&format!(
929 " scanning a /*...*/ comment (missing '*/? longer than {YY_BUF_SIZE}?)"
930 )),
931 ScanState::Initial => {}
932 }
933 }
934 GraphvizError::SyntaxError {
935 line: tok.c_line,
936 col: tok.col,
937 message,
938 file: tok.file.as_deref().map(str::to_string),
939 }
940}
941
942fn port_to_str(p: &Port) -> String {
944 match (&p.name, &p.compass) {
945 (Some(n), Some(c)) => format!("{n}:{c}"),
946 (Some(n), None) => n.clone(),
947 (None, Some(c)) => c.clone(),
948 (None, None) => String::new(),
949 }
950}
951
952fn parse_port_str(s: &str) -> Port {
953 if s.is_empty() {
954 return Port::default();
955 }
956 if let Some((p1, p2)) = s.split_once(':') {
957 Port {
958 name: Some(p1.to_string()),
959 compass: Some(p2.to_string()),
960 }
961 } else {
962 Port {
963 name: Some(s.to_string()),
964 compass: None,
965 }
966 }
967}