1use crate::ast::stmt::{BinaryOpKind, Expr, Literal};
12use crate::intern::Interner;
13
14fn op_word(op: BinaryOpKind) -> &'static str {
16 use BinaryOpKind::*;
17 match op {
18 Add => "plus",
19 Subtract => "minus",
20 Multiply => "times",
21 Divide | ExactDivide => "divided by",
22 FloorDivide => "floor-divided by",
23 Pow => "to the power of",
24 Modulo => "modulo",
25 Eq => "is",
26 NotEq => "is not",
27 Lt => "is less than",
28 Gt => "is greater than",
29 LtEq => "is at most",
30 GtEq => "is at least",
31 And => "and",
32 Or => "or",
33 Concat => "combined with",
34 SeqConcat => "followed by",
35 ApproxEq => "is approximately",
36 BitXor => "xor",
37 BitAnd => "bitand",
38 BitOr => "bitor",
39 Shl => "shifted left by",
40 Shr => "shifted right by",
41 }
42}
43
44fn render_literal(lit: &Literal, interner: &Interner) -> String {
46 match lit {
47 Literal::Number(n) => n.to_string(),
48 Literal::Float(f) => f.to_string(),
49 Literal::Text(s) => format!("{:?}", interner.resolve(*s)),
50 Literal::Boolean(b) => b.to_string(),
51 Literal::Nothing => "nothing".to_string(),
52 Literal::Char(c) => format!("'{c}'"),
53 Literal::Duration(ns) => format!("{ns}ns"),
54 _ => "…".to_string(),
55 }
56}
57
58pub fn render_expr(expr: &Expr<'_>, interner: &Interner) -> String {
61 match expr {
62 Expr::Literal(lit) => render_literal(lit, interner),
63 Expr::Identifier(sym) => interner.resolve(*sym).to_string(),
64 Expr::BinaryOp { op, left, right } => {
65 format!(
66 "{} {} {}",
67 render_expr(left, interner),
68 op_word(*op),
69 render_expr(right, interner)
70 )
71 }
72 Expr::Not { operand } => format!("not {}", render_expr(operand, interner)),
73 Expr::Call { function, args } => {
74 let rendered: Vec<String> = args.iter().map(|a| render_expr(a, interner)).collect();
75 format!("{}({})", interner.resolve(*function), rendered.join(", "))
76 }
77 Expr::Index { collection, index } => {
78 format!(
79 "item {} of {}",
80 render_expr(index, interner),
81 render_expr(collection, interner)
82 )
83 }
84 Expr::Length { collection } => {
85 format!("the length of {}", render_expr(collection, interner))
86 }
87 Expr::Contains { collection, value } => {
88 format!(
89 "{} contains {}",
90 render_expr(collection, interner),
91 render_expr(value, interner)
92 )
93 }
94 Expr::FieldAccess { object, field } => {
95 format!(
96 "the {} of {}",
97 interner.resolve(*field),
98 render_expr(object, interner)
99 )
100 }
101 Expr::List(items) => {
102 let rendered: Vec<String> = items.iter().map(|i| render_expr(i, interner)).collect();
103 format!("[{}]", rendered.join(", "))
104 }
105 _ => "…".to_string(),
106 }
107}
108
109pub fn subexpressions<'a, 'e>(expr: &'e Expr<'a>, out: &mut Vec<&'e Expr<'a>>) {
114 match expr {
115 Expr::Literal(_) => {}
116 Expr::Identifier(_) => out.push(expr),
117 Expr::BinaryOp { left, right, .. } => {
118 subexpressions(left, out);
119 subexpressions(right, out);
120 }
121 Expr::Not { operand } => subexpressions(operand, out),
122 Expr::Call { args, .. } => {
123 out.push(expr);
124 for a in args {
125 subexpressions(a, out);
126 }
127 }
128 Expr::Index { collection, index } => {
129 out.push(expr);
130 subexpressions(collection, out);
131 subexpressions(index, out);
132 }
133 Expr::Length { collection } => {
134 out.push(expr);
135 subexpressions(collection, out);
136 }
137 Expr::FieldAccess { object, .. } => {
138 out.push(expr);
139 subexpressions(object, out);
140 }
141 Expr::Contains { collection, value } => {
142 subexpressions(collection, out);
143 subexpressions(value, out);
144 }
145 Expr::List(items) => {
146 for i in items {
147 subexpressions(i, out);
148 }
149 }
150 _ => {}
151 }
152}