tin_lang/
graph.rs

1//! Graph rendering tools for the internal representation of Tin code.
2//!
3//! Use this module to diagnose errors encountered in a piece of code.  The graph representation
4//! aims to provide all of the information available to the Tin compiler.
5use std::borrow;
6use std::fmt;
7
8use dot;
9use specs;
10
11use ir;
12use ir::component::element;
13use ir::component::layout;
14use ir::component::symbol;
15use ir::component::ty;
16
17/// A graph representation of IR.
18pub struct Graph<'a> {
19    entities: specs::Entities<'a>,
20    elements: specs::ReadStorage<'a, element::Element>,
21    layouts: specs::ReadStorage<'a, layout::Layout>,
22    symbols: specs::ReadStorage<'a, symbol::Symbol>,
23    types: specs::ReadStorage<'a, ty::Type>,
24}
25
26/// A node in the IR graph.
27#[derive(Clone, Copy, Debug)]
28pub struct Node(specs::Entity);
29
30/// An edge in the IR graph.
31#[derive(Clone, Copy, Debug)]
32pub struct Edge<'a> {
33    source: Node,
34    target: Node,
35    label: Label<'a>,
36}
37
38#[derive(Clone, Copy, Debug)]
39enum Label<'a> {
40    RecordField(&'a str),
41    TupleField(usize),
42    VariableInitializer,
43    SelectField(&'a str),
44    AppliedFunction,
45    AppliedParameter(usize),
46    ParameterSignature,
47    ClosureCaptureDefinition(&'a str),
48    ClosureCaptureUsage(usize),
49    ClosureParameter(usize),
50    ClosureStatement(usize),
51    ClosureSignature,
52    ClosureResult,
53    ModuleDefinition(&'a str),
54    UnOperand,
55    BiLhs,
56    BiRhs,
57}
58
59struct PrettyTy<T>(T);
60
61impl<'a> Graph<'a> {
62    /// Creates a new IR graph based on the supplied intermediate representation.
63    pub(crate) fn new(ir: &'a ir::Ir) -> Graph<'a> {
64        let world = &ir.world;
65        let entities = world.entities();
66        let elements = world.read_storage();
67        let layouts = world.read_storage();
68        let symbols = world.read_storage();
69        let types = world.read_storage();
70
71        Graph {
72            entities,
73            elements,
74            layouts,
75            symbols,
76            types,
77        }
78    }
79}
80
81impl<'a> dot::GraphWalk<'a, Node, Edge<'a>> for Graph<'a> {
82    fn nodes(&'a self) -> borrow::Cow<'a, [Node]> {
83        use specs::Join;
84
85        borrow::Cow::Owned(
86            self.entities
87                .join()
88                .filter(|e| self.elements.contains(*e))
89                .map(Node)
90                .collect::<Vec<_>>(),
91        )
92    }
93
94    fn edges(&'a self) -> borrow::Cow<'a, [Edge<'a>]> {
95        use specs::Join;
96
97        let mut edges = Vec::new();
98
99        for entity in self.entities.join() {
100            if let Some(element) = self.elements.get(entity) {
101                match element {
102                    element::Element::NumberValue(_) => {}
103                    element::Element::StringValue(_) => {}
104                    element::Element::Tuple(element::Tuple { fields }) => {
105                        for (idx, field) in fields.iter().enumerate() {
106                            edges.push(Edge {
107                                source: Node(entity),
108                                target: Node(*field),
109                                label: Label::TupleField(idx),
110                            });
111                        }
112                    }
113                    element::Element::Record(element::Record { fields }) => {
114                        for (name, field) in fields {
115                            edges.push(Edge {
116                                source: Node(entity),
117                                target: Node(*field),
118                                label: Label::RecordField(name),
119                            });
120                        }
121                    }
122                    element::Element::UnOp(element::UnOp { operand, .. }) => {
123                        edges.push(Edge {
124                            source: Node(entity),
125                            target: Node(*operand),
126                            label: Label::UnOperand,
127                        });
128                    }
129                    element::Element::BiOp(element::BiOp { lhs, rhs, .. }) => {
130                        edges.push(Edge {
131                            source: Node(entity),
132                            target: Node(*lhs),
133                            label: Label::BiLhs,
134                        });
135                        edges.push(Edge {
136                            source: Node(entity),
137                            target: Node(*rhs),
138                            label: Label::BiRhs,
139                        });
140                    }
141                    element::Element::Variable(element::Variable { initializer, .. }) => edges
142                        .push(Edge {
143                            source: Node(entity),
144                            target: Node(*initializer),
145                            label: Label::VariableInitializer,
146                        }),
147                    element::Element::Select(element::Select { record, field }) => {
148                        edges.push(Edge {
149                            source: Node(entity),
150                            target: Node(*record),
151                            label: Label::SelectField(field),
152                        });
153                    }
154                    element::Element::Apply(element::Apply {
155                        function,
156                        parameters,
157                    }) => {
158                        edges.push(Edge {
159                            source: Node(entity),
160                            target: Node(*function),
161                            label: Label::AppliedFunction,
162                        });
163                        for (idx, parameter) in parameters.iter().enumerate() {
164                            edges.push(Edge {
165                                source: Node(entity),
166                                target: Node(*parameter),
167                                label: Label::AppliedParameter(idx),
168                            });
169                        }
170                    }
171                    element::Element::Parameter(element::Parameter { signature, .. }) => {
172                        if let Some(signature) = signature {
173                            edges.push(Edge {
174                                source: Node(entity),
175                                target: Node(*signature),
176                                label: Label::ParameterSignature,
177                            });
178                        }
179                    }
180                    element::Element::Capture(element::Capture { ref name, captured }) => edges
181                        .push(Edge {
182                            source: Node(entity),
183                            target: Node(*captured),
184                            label: Label::ClosureCaptureDefinition(name),
185                        }),
186                    element::Element::Closure(element::Closure {
187                        captures,
188                        parameters,
189                        statements,
190                        signature,
191                        result,
192                    }) => {
193                        for (idx, capture) in captures.iter().enumerate() {
194                            edges.push(Edge {
195                                source: Node(entity),
196                                target: Node(*capture),
197                                label: Label::ClosureCaptureUsage(idx),
198                            });
199                        }
200                        for (idx, parameter) in parameters.iter().enumerate() {
201                            edges.push(Edge {
202                                source: Node(entity),
203                                target: Node(*parameter),
204                                label: Label::ClosureParameter(idx),
205                            });
206                        }
207                        for (idx, statement) in statements.iter().enumerate() {
208                            edges.push(Edge {
209                                source: Node(entity),
210                                target: Node(*statement),
211                                label: Label::ClosureStatement(idx),
212                            });
213                        }
214                        if let Some(signature) = signature {
215                            edges.push(Edge {
216                                source: Node(entity),
217                                target: Node(*signature),
218                                label: Label::ClosureSignature,
219                            });
220                        }
221                        edges.push(Edge {
222                            source: Node(entity),
223                            target: Node(*result),
224                            label: Label::ClosureResult,
225                        });
226                    }
227                    element::Element::Module(element::Module { variables }) => {
228                        for (name, variable) in variables {
229                            edges.push(Edge {
230                                source: Node(entity),
231                                target: Node(*variable),
232                                label: Label::ModuleDefinition(name),
233                            });
234                        }
235                    }
236                }
237            }
238        }
239
240        borrow::Cow::Owned(edges)
241    }
242
243    fn source(&'a self, edge: &Edge) -> Node {
244        edge.source
245    }
246
247    fn target(&'a self, edge: &Edge) -> Node {
248        edge.target
249    }
250}
251
252impl<'a> dot::Labeller<'a, Node, Edge<'a>> for Graph<'a> {
253    fn graph_id(&'a self) -> dot::Id<'a> {
254        dot::Id::new("ir").unwrap()
255    }
256
257    fn node_id(&'a self, n: &Node) -> dot::Id<'a> {
258        dot::Id::new(format!("n{}", n.0.id())).unwrap()
259    }
260
261    fn node_shape(&'a self, _n: &Node) -> Option<dot::LabelText<'a>> {
262        Some(dot::LabelText::LabelStr("record".into()))
263    }
264
265    fn node_label(&'a self, n: &Node) -> dot::LabelText<'a> {
266        use std::fmt::Write;
267
268        let mut result = format!("({}) ", n.0.id());
269
270        if let Some(element) = self.elements.get(n.0) {
271            match element {
272                element::Element::NumberValue(n) => write!(result, "num <b>{:?}</b>", n).unwrap(),
273                element::Element::StringValue(element::StringValue(s)) => {
274                    write!(result, "str <b>{:?}</b>", s).unwrap()
275                }
276                element::Element::Tuple(element::Tuple { fields }) => {
277                    write!(result, "tuple <br/> <b>{:?}</b> fields", fields.len()).unwrap()
278                }
279                element::Element::Record(element::Record { fields }) => {
280                    write!(result, "record <br/> <b>{:?}</b> fields", fields.len()).unwrap()
281                }
282                element::Element::UnOp(element::UnOp { operator, .. }) => {
283                    write!(result, "un op <b>{}</b>", operator).unwrap()
284                }
285                element::Element::BiOp(element::BiOp { operator, .. }) => {
286                    write!(result, "bi op <b>{}</b>", operator).unwrap()
287                }
288                element::Element::Variable(element::Variable { name, .. }) => {
289                    write!(result, "variable <b>{:?}</b>", name).unwrap()
290                }
291                element::Element::Select(element::Select { .. }) => {
292                    write!(result, "select").unwrap()
293                }
294                element::Element::Apply(element::Apply { parameters, .. }) => {
295                    write!(result, "apply <br/> <b>{:?}</b> params", parameters.len()).unwrap()
296                }
297                element::Element::Parameter(element::Parameter { name, .. }) => {
298                    write!(result, "param <b>{:?}</b>", name).unwrap()
299                }
300                element::Element::Capture(element::Capture { name, .. }) => {
301                    write!(result, "capture <b>{:?}</b>", name).unwrap()
302                }
303                element::Element::Closure(element::Closure {
304                    captures,
305                    parameters,
306                    ..
307                }) => write!(
308                    result,
309                    "closure <br/> <b>{:?}</b> parameters <br/> <b>{:?}</b> captures",
310                    parameters.len(),
311                    captures.len()
312                )
313                .unwrap(),
314
315                element::Element::Module(element::Module { variables }) => write!(
316                    result,
317                    "module <br/> <b>{:?}</b> variables",
318                    variables.len()
319                )
320                .unwrap(),
321            }
322        } else {
323            write!(result, "(unknown)").unwrap();
324        };
325
326        if let Some(ty) = self.types.get(n.0) {
327            write!(result, "<br/> <font color=\"blue\">{}</font>", PrettyTy(ty)).unwrap();
328        }
329
330        if let Some(layout) = self.layouts.get(n.0) {
331            write!(result, "<br/> <font color=\"brown\">{}</font>", layout).unwrap();
332        }
333
334        if let Some(symbol) = self.symbols.get(n.0) {
335            if symbol.is_empty() {
336                write!(result, "<br/> <font color=\"purple\">(root)</font>").unwrap();
337            } else {
338                write!(result, "<br/> <font color=\"purple\">{}</font>", symbol).unwrap();
339            }
340        }
341
342        dot::LabelText::HtmlStr(result.into())
343    }
344
345    fn edge_label(&'a self, e: &Edge<'a>) -> dot::LabelText<'a> {
346        match e.label {
347            Label::RecordField(ref name) => {
348                dot::LabelText::HtmlStr(format!("field <b>{}</b>", name).into())
349            }
350            Label::TupleField(idx) => {
351                dot::LabelText::HtmlStr(format!("field <b>{}</b>", idx).into())
352            }
353            Label::VariableInitializer => dot::LabelText::LabelStr("initializer".into()),
354            Label::SelectField(ref name) => {
355                dot::LabelText::HtmlStr(format!("select <b>{}</b>", name).into())
356            }
357            Label::AppliedFunction => dot::LabelText::LabelStr("func".into()),
358            Label::AppliedParameter(idx) => {
359                dot::LabelText::HtmlStr(format!("param <b>{}</b>", idx).into())
360            }
361            Label::ParameterSignature => dot::LabelText::LabelStr("sig".into()),
362            Label::ClosureCaptureDefinition(ref name) => {
363                dot::LabelText::HtmlStr(format!("capture definition <b>{}</b>", name).into())
364            }
365            Label::ClosureCaptureUsage(idx) => {
366                dot::LabelText::HtmlStr(format!("capture usage <b>{}</b>", idx).into())
367            }
368            Label::ClosureParameter(idx) => {
369                dot::LabelText::HtmlStr(format!("param <b>{}</b>", idx).into())
370            }
371            Label::ClosureStatement(idx) => {
372                dot::LabelText::HtmlStr(format!("stmt <b>{}</b>", idx).into())
373            }
374            Label::ClosureResult => dot::LabelText::HtmlStr("result".into()),
375            Label::ClosureSignature => dot::LabelText::LabelStr("sig".into()),
376            Label::ModuleDefinition(ref name) => {
377                dot::LabelText::HtmlStr(format!("def <b>{}</b>", name).into())
378            }
379            Label::UnOperand => dot::LabelText::LabelStr("operand".into()),
380            Label::BiLhs => dot::LabelText::LabelStr("lhs".into()),
381            Label::BiRhs => dot::LabelText::LabelStr("rhs".into()),
382        }
383    }
384
385    fn edge_style(&'a self, e: &Edge<'a>) -> dot::Style {
386        match e.label {
387            Label::RecordField(_) => dot::Style::None,
388            Label::TupleField(_) => dot::Style::None,
389            Label::VariableInitializer => dot::Style::None,
390            Label::SelectField(_) => dot::Style::None,
391            Label::AppliedFunction => dot::Style::None,
392            Label::AppliedParameter(_) => dot::Style::None,
393            Label::ParameterSignature => dot::Style::Dotted,
394            Label::ClosureCaptureDefinition(_) => dot::Style::Dashed,
395            Label::ClosureCaptureUsage(_) => dot::Style::None,
396            Label::ClosureParameter(_) => dot::Style::None,
397            Label::ClosureStatement(_) => dot::Style::Dashed,
398            Label::ClosureResult => dot::Style::None,
399            Label::ClosureSignature => dot::Style::Dotted,
400            Label::ModuleDefinition(_) => dot::Style::None,
401            Label::UnOperand => dot::Style::None,
402            Label::BiLhs => dot::Style::None,
403            Label::BiRhs => dot::Style::None,
404        }
405    }
406}
407
408impl<'a> fmt::Debug for Graph<'a> {
409    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
410        f.debug_struct("Graph").finish()
411    }
412}
413
414impl<'a> fmt::Display for PrettyTy<&'a ty::Type> {
415    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
416        match self.0 {
417            ty::Type::Boolean => write!(f, "bool"),
418            ty::Type::Number(ref number) => PrettyTy(number).fmt(f),
419            ty::Type::String => write!(f, "str"),
420            ty::Type::Tuple(ref tuple) => PrettyTy(tuple).fmt(f),
421            ty::Type::Record(ref record) => PrettyTy(record).fmt(f),
422            ty::Type::Function(ref function) => PrettyTy(function).fmt(f),
423            ty::Type::Conflict(ref conflict) => PrettyTy(conflict).fmt(f),
424            ty::Type::Any => write!(f, "any"),
425        }
426    }
427}
428
429impl<'a> fmt::Display for PrettyTy<&'a ty::Number> {
430    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
431        match self.0 {
432            ty::Number::U8 => write!(f, "u8"),
433            ty::Number::U16 => write!(f, "u16"),
434            ty::Number::U32 => write!(f, "u32"),
435            ty::Number::U64 => write!(f, "u64"),
436            ty::Number::I8 => write!(f, "i8"),
437            ty::Number::I16 => write!(f, "i16"),
438            ty::Number::I32 => write!(f, "i32"),
439            ty::Number::I64 => write!(f, "i64"),
440            ty::Number::F32 => write!(f, "f32"),
441            ty::Number::F64 => write!(f, "f64"),
442        }
443    }
444}
445
446impl<'a> fmt::Display for PrettyTy<&'a ty::Tuple> {
447    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
448        write!(f, "(")?;
449        let mut needs_sep = false;
450        for ty in &self.0.fields {
451            if needs_sep {
452                write!(f, ",")?;
453            }
454            PrettyTy(ty).fmt(f)?;
455            needs_sep = true;
456        }
457        write!(f, ")")?;
458        Ok(())
459    }
460}
461
462impl<'a> fmt::Display for PrettyTy<&'a ty::Record> {
463    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
464        write!(f, "\\{{")?;
465        let mut needs_sep = false;
466        for (id, ty) in &self.0.fields {
467            if needs_sep {
468                write!(f, ",")?;
469            }
470            write!(f, "{}:", id)?;
471            PrettyTy(ty).fmt(f)?;
472            needs_sep = true;
473        }
474        write!(f, "\\}}")?;
475        Ok(())
476    }
477}
478
479impl<'a> fmt::Display for PrettyTy<&'a ty::Function> {
480    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
481        write!(f, "\\|")?;
482        let mut needs_sep = false;
483        for ty in &self.0.parameters {
484            if needs_sep {
485                write!(f, ",")?;
486            }
487            PrettyTy(ty).fmt(f)?;
488            needs_sep = true;
489        }
490        write!(f, "\\|:")?;
491        PrettyTy(&*self.0.result).fmt(f)?;
492        Ok(())
493    }
494}
495
496impl<'a> fmt::Display for PrettyTy<&'a ty::Conflict> {
497    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
498        PrettyTy(&self.0.expected).fmt(f)?;
499        write!(f, "!=")?;
500        PrettyTy(&*self.0.actual).fmt(f)?;
501        Ok(())
502    }
503}
504
505impl<'a> fmt::Display for PrettyTy<&'a ty::ExpectedType> {
506    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
507        match *self.0 {
508            ty::ExpectedType::Specific(ref ty) => PrettyTy(&**ty).fmt(f),
509            ty::ExpectedType::ScalarClass(ref class) => PrettyTy(class).fmt(f),
510        }
511    }
512}
513
514impl<'a> fmt::Display for PrettyTy<&'a ty::ScalarClass> {
515    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
516        match *self.0 {
517            ty::ScalarClass::Boolean => f.write_str("(any bool type)"),
518            ty::ScalarClass::Integral(ty::IntegralScalarClass::Unsigned) => {
519                f.write_str("(any unsigned integer type)")
520            }
521            ty::ScalarClass::Integral(ty::IntegralScalarClass::Signed) => {
522                f.write_str("(any signed integer type)")
523            }
524            ty::ScalarClass::Integral(ty::IntegralScalarClass::Any) => {
525                f.write_str("(any integer type)")
526            }
527            ty::ScalarClass::Fractional => f.write_str("(any floating point type)"),
528            ty::ScalarClass::Complex => f.write_str("(any complex type)"),
529            ty::ScalarClass::Undefined => f.write_str("(any non-scalar type)"),
530        }
531    }
532}