1use crate::*;
2use std::fmt::Write;
3
4pub type TermId = usize;
5
6#[allow(rustdoc::private_intra_doc_links)]
7#[derive(Clone, PartialEq, Eq, Hash, Debug)]
12pub enum Term {
13 Lit(Literal),
14 Var(String),
15 App(String, Vec<TermId>),
16}
17
18#[derive(Clone, PartialEq, Eq, Debug, Default)]
20pub struct TermDag {
21 nodes: IndexSet<Term>,
23}
24
25#[macro_export]
26macro_rules! match_term_app {
27 ($e:expr; $body:tt) => {
28 match $e {
29 Term::App(head, args) => {
30 match (head.as_str(), args.as_slice())
31 $body
32 }
33 _ => panic!("not an app")
34 }
35 }
36}
37
38impl TermDag {
39 pub fn size(&self) -> usize {
41 self.nodes.len()
42 }
43
44 pub fn lookup(&self, node: &Term) -> TermId {
48 self.nodes.get_index_of(node).unwrap()
49 }
50
51 pub fn get(&self, id: TermId) -> &Term {
55 self.nodes.get_index(id).unwrap()
56 }
57
58 pub fn app(&mut self, sym: String, children: Vec<Term>) -> Term {
63 let node = Term::App(sym, children.iter().map(|c| self.lookup(c)).collect());
64
65 self.add_node(&node);
66
67 node
68 }
69
70 pub fn lit(&mut self, lit: Literal) -> Term {
73 let node = Term::Lit(lit);
74
75 self.add_node(&node);
76
77 node
78 }
79
80 pub fn var(&mut self, sym: String) -> Term {
83 let node = Term::Var(sym);
84
85 self.add_node(&node);
86
87 node
88 }
89
90 fn add_node(&mut self, node: &Term) {
91 if self.nodes.get(node).is_none() {
92 self.nodes.insert(node.clone());
93 }
94 }
95
96 pub fn expr_to_term(&mut self, expr: &GenericExpr<String, String>) -> Term {
102 let res = match expr {
103 GenericExpr::Lit(_, lit) => Term::Lit(lit.clone()),
104 GenericExpr::Var(_, v) => Term::Var(v.to_owned()),
105 GenericExpr::Call(_, op, args) => {
106 let args = args
107 .iter()
108 .map(|a| {
109 let term = self.expr_to_term(a);
110 self.lookup(&term)
111 })
112 .collect();
113 Term::App(op.clone(), args)
114 }
115 };
116 self.add_node(&res);
117 res
118 }
119
120 pub fn term_to_expr(&self, term: &Term, span: Span) -> Expr {
124 match term {
125 Term::Lit(lit) => Expr::Lit(span, lit.clone()),
126 Term::Var(v) => Expr::Var(span, v.clone()),
127 Term::App(op, args) => {
128 let args: Vec<_> = args
129 .iter()
130 .map(|a| self.term_to_expr(self.get(*a), span.clone()))
131 .collect();
132 Expr::Call(span, op.clone(), args)
133 }
134 }
135 }
136
137 pub fn to_string(&self, term: &Term) -> String {
141 let mut result = String::new();
142 let mut ranges = HashMap::<TermId, (usize, usize)>::default();
144 let id = self.lookup(term);
145 let mut stack = vec![(id, false, None)];
148 while let Some((id, space_before, mut start_index)) = stack.pop() {
149 if space_before {
150 result.push(' ');
151 }
152
153 if let Some((start, end)) = ranges.get(&id) {
154 result.extend_from_within(*start..*end);
155 continue;
156 }
157
158 match self.nodes[id].clone() {
159 Term::App(name, children) => {
160 if start_index.is_some() {
161 result.push(')');
162 } else {
163 stack.push((id, false, Some(result.len())));
164 write!(&mut result, "({}", name).unwrap();
165 for c in children.iter().rev() {
166 stack.push((*c, true, None));
167 }
168 }
169 }
170 Term::Lit(lit) => {
171 start_index = Some(result.len());
172 write!(&mut result, "{lit}").unwrap();
173 }
174 Term::Var(v) => {
175 start_index = Some(result.len());
176 write!(&mut result, "{v}").unwrap();
177 }
178 }
179
180 if let Some(start_index) = start_index {
181 ranges.insert(id, (start_index, result.len()));
182 }
183 }
184
185 result
186 }
187}
188
189#[cfg(test)]
190mod tests {
191 use super::*;
192 use crate::{ast::*, span};
193
194 fn parse_term(s: &str) -> (TermDag, Term) {
195 let e = Parser::default().get_expr_from_string(None, s).unwrap();
196 let mut td = TermDag::default();
197 let t = td.expr_to_term(&e);
198 (td, t)
199 }
200
201 #[test]
202 fn test_to_from_expr() {
203 let s = r#"(f (g x y) x y (g x y))"#;
204 let e = Parser::default().get_expr_from_string(None, s).unwrap();
205 let mut td = TermDag::default();
206 assert_eq!(td.size(), 0);
207 let t = td.expr_to_term(&e);
208 assert_eq!(td.size(), 4);
209 assert_eq!(
214 td.nodes.as_slice().iter().cloned().collect::<Vec<_>>(),
215 vec![
216 Term::Var("x".into()),
217 Term::Var("y".into()),
218 Term::App("g".into(), vec![0, 1]),
219 Term::App("f".into(), vec![2, 0, 1, 2]),
220 ]
221 );
222 let e2 = td.term_to_expr(&t, span!());
224 assert_eq!(format!("{e}"), format!("{e2}")); }
228
229 #[test]
230 fn test_match_term_app() {
231 let s = r#"(f (g x y) x y (g x y))"#;
232 let (td, t) = parse_term(s);
233 match_term_app!(t; {
234 ("f", [_, x, _, _]) => {
235 let span = span!();
236 assert_eq!(
237 td.term_to_expr(td.get(*x), span.clone()),
238 crate::ast::GenericExpr::Var(span, "x".to_owned())
239 )
240 }
241 (head, _) => panic!("unexpected head {}, in {}:{}:{}", head, file!(), line!(), column!())
242 })
243 }
244
245 #[test]
246 fn test_to_string() {
247 let s = r#"(f (g x y) x y (g x y))"#;
248 let (td, t) = parse_term(s);
249 assert_eq!(td.to_string(&t), s);
250 }
251
252 #[test]
253 fn test_lookup() {
254 let s = r#"(f (g x y) x y (g x y))"#;
255 let (td, t) = parse_term(s);
256 assert_eq!(td.lookup(&t), td.size() - 1);
257 }
258
259 #[test]
260 fn test_app_var_lit() {
261 let s = r#"(f (g x y) x 7 (g x y))"#;
262 let (mut td, t) = parse_term(s);
263 let x = td.var("x".into());
264 let y = td.var("y".into());
265 let seven = td.lit(7.into());
266 let g = td.app("g".into(), vec![x.clone(), y.clone()]);
267 let t2 = td.app("f".into(), vec![g.clone(), x, seven, g]);
268 assert_eq!(t, t2);
269 }
270}