1use super::api::ProjectionAPI;
2use super::types::{
3 AvailableAction, CandidateKind, CompletenessSummary, NodeDetail, NodeEditProjection,
4 NotEditableReason, ProjectedNode, SlotEntry,
5};
6use crate::constraint::{ArithOp, Constraint, ExpectedType, Expression};
7use crate::operation::AstEngine;
8use crate::structure::{NodeId, NodeKind, Reference};
9
10impl ProjectionAPI for AstEngine {
11 fn nodes(&self) -> Vec<ProjectedNode> {
12 let mut result = Vec::new();
13 let root_id = self.structure.root();
14 let mut stack = Vec::new();
15
16 if let Some(root) = self.structure.get(root_id) {
17 if is_hidden_root(root.kind()) {
18 for &child_id in children_of(root.kind()).iter().rev() {
19 stack.push((child_id, 0));
20 }
21 } else {
22 stack.push((root_id, 0));
23 }
24 }
25
26 while let Some((node_id, depth)) = stack.pop() {
27 if let Some(node) = self.structure.get(node_id) {
28 let label = make_label(node.kind());
29 let is_hole = matches!(node.kind(), NodeKind::Hole { .. });
30
31 result.push(ProjectedNode {
32 id: node_id,
33 label,
34 depth,
35 is_hole,
36 edit: node_edit_projection(self, node_id),
37 });
38
39 let children = children_of(node.kind());
41 for &child_id in children.iter().rev() {
42 stack.push((child_id, depth + 1));
43 }
44 }
45 }
46
47 result
48 }
49
50 fn children(&self, node: NodeId) -> Vec<SlotEntry> {
51 if let Some(node) = self.structure.get(node) {
52 slot_entries_for(node.kind())
53 } else {
54 Vec::new()
55 }
56 }
57
58 fn inspect(&self, node: NodeId) -> Option<NodeDetail> {
59 let node = self.structure.get(node)?;
60 let kind_label = make_label(node.kind());
61
62 let constraint_ids = self.constraints.for_node(node.id());
63 let constraints = constraint_ids
64 .iter()
65 .filter_map(|&id| self.constraints.get(id))
66 .map(format_constraint)
67 .collect();
68
69 Some(NodeDetail {
70 id: node.id(),
71 kind_label,
72 constraints,
73 })
74 }
75
76 fn hole_candidates(&self, hole: NodeId) -> Vec<CandidateKind> {
77 if let Some(node) = self.structure.get(hole) {
78 if matches!(node.kind(), NodeKind::Hole { .. }) {
79 vec![
80 CandidateKind::IntroduceScalar {
81 suggested_names: vec!["N".into(), "M".into(), "K".into()],
82 },
83 CandidateKind::IntroduceArray {
84 suggested_names: vec!["A".into(), "B".into()],
85 },
86 CandidateKind::IntroduceMatrix,
87 CandidateKind::IntroduceSection,
88 ]
89 } else {
90 Vec::new()
91 }
92 } else {
93 Vec::new()
94 }
95 }
96
97 fn available_actions(&self) -> Vec<AvailableAction> {
98 let mut actions = Vec::new();
99
100 for node in self.structure.iter() {
101 let node_id = node.id();
102
103 match node.kind() {
104 NodeKind::Hole { .. } => {
105 actions.push(AvailableAction {
106 target: node_id,
107 description: "Fill hole".to_owned(),
108 });
109 }
110 _ => {
111 if node_id != self.structure.root() {
113 actions.push(AvailableAction {
114 target: node_id,
115 description: "Replace node".to_owned(),
116 });
117
118 let constraints = self.constraints.for_node(node_id);
120 if constraints.is_empty() {
121 actions.push(AvailableAction {
122 target: node_id,
123 description: "Remove from parent".to_owned(),
124 });
125 }
126 }
127 }
128 }
129 }
130
131 actions
132 }
133
134 fn why_not_editable(&self, node: NodeId) -> Option<NotEditableReason> {
135 if node == self.structure.root() {
136 return Some(NotEditableReason::IsRoot);
137 }
138
139 let constraint_ids = self.constraints.for_node(node);
140 if !constraint_ids.is_empty() {
141 return Some(NotEditableReason::HasDependents {
142 dependents: vec![node], });
144 }
145
146 None
147 }
148
149 fn completeness(&self) -> CompletenessSummary {
150 let mut total_nodes: usize = 0;
151 let mut total_holes: usize = 0;
152
153 for node in self.structure.iter() {
154 total_nodes += 1;
155 if matches!(node.kind(), NodeKind::Hole { .. }) {
156 total_holes += 1;
157 }
158 }
159
160 let filled_slots = total_nodes.saturating_sub(total_holes);
161 let unsatisfied_constraints = 0; let is_complete = total_holes == 0;
163
164 CompletenessSummary {
165 total_holes,
166 filled_slots,
167 unsatisfied_constraints,
168 is_complete,
169 }
170 }
171}
172
173fn node_edit_projection(engine: &AstEngine, node_id: NodeId) -> Option<NodeEditProjection> {
174 let node = engine.structure.get(node_id)?;
175 let value_type = expected_type(engine, node_id)
176 .map_or("number", |typ| match typ {
177 ExpectedType::Int => "number",
178 ExpectedType::Str => "string",
179 ExpectedType::Char => "char",
180 })
181 .to_owned();
182
183 match node.kind() {
184 NodeKind::Scalar { name } => Some(NodeEditProjection {
185 kind: "scalar".to_owned(),
186 name: name.as_str().to_owned(),
187 value_type,
188 length_expr: None,
189 allowed_kinds: vec!["scalar".to_owned(), "array".to_owned()],
190 allowed_types: vec!["number".to_owned(), "char".to_owned(), "string".to_owned()],
191 }),
192 NodeKind::Array { name, length } => Some(NodeEditProjection {
193 kind: "array".to_owned(),
194 name: name.as_str().to_owned(),
195 value_type,
196 length_expr: Some(format_expr_with_names(length, engine)),
197 allowed_kinds: vec!["scalar".to_owned(), "array".to_owned()],
198 allowed_types: vec!["number".to_owned(), "char".to_owned(), "string".to_owned()],
199 }),
200 _ => None,
201 }
202}
203
204fn expected_type(engine: &AstEngine, node_id: NodeId) -> Option<ExpectedType> {
205 let ids = engine.constraints.for_node(node_id);
206 for cid in &ids {
207 if let Some(Constraint::TypeDecl { expected, .. }) = engine.constraints.get(*cid) {
208 return Some(expected.clone());
209 }
210 }
211 None
212}
213
214fn format_expr_with_names(expr: &Expression, engine: &AstEngine) -> String {
215 match expr {
216 Expression::Lit(n) => n.to_string(),
217 Expression::Var(r) => ref_to_name(r, engine),
218 Expression::BinOp { op, lhs, rhs } => {
219 let symbol = match op {
220 ArithOp::Add => "+",
221 ArithOp::Sub => "-",
222 ArithOp::Mul => "*",
223 ArithOp::Div => "/",
224 };
225 format!(
226 "{}{}{}",
227 format_expr_with_names(lhs, engine),
228 symbol,
229 format_expr_with_names(rhs, engine)
230 )
231 }
232 Expression::Pow { base, exp } => {
233 format!(
234 "{}^{}",
235 format_expr_with_names(base, engine),
236 format_expr_with_names(exp, engine)
237 )
238 }
239 Expression::FnCall { name, args } => {
240 let arg_strs: Vec<_> = args
241 .iter()
242 .map(|arg| format_expr_with_names(arg, engine))
243 .collect();
244 format!("{}({})", name.as_str(), arg_strs.join(","))
245 }
246 }
247}
248
249fn ref_to_name(reference: &Reference, engine: &AstEngine) -> String {
250 match reference {
251 Reference::VariableRef(node_id) => engine.structure.get(*node_id).map_or_else(
252 || format!("?node({node_id:?})"),
253 |node| match node.kind() {
254 NodeKind::Scalar { name }
255 | NodeKind::Array { name, .. }
256 | NodeKind::Matrix { name, .. } => name.as_str().to_owned(),
257 other => format!("{other:?}"),
258 },
259 ),
260 Reference::Unresolved(ident) => ident.as_str().to_owned(),
261 Reference::IndexedRef { .. } => format!("{reference:?}"),
262 }
263}
264
265pub(super) fn make_label(kind: &NodeKind) -> String {
267 match kind {
268 NodeKind::Scalar { name } => name.as_str().to_owned(),
269 NodeKind::Array { name, .. } => format!("{}[]", name.as_str()),
270 NodeKind::Matrix { name, .. } => format!("{}[][]", name.as_str()),
271 NodeKind::Tuple { .. } => "Tuple".to_owned(),
272 NodeKind::Repeat { .. } => "Repeat".to_owned(),
273 NodeKind::Section { .. } => "Section".to_owned(),
274 NodeKind::Sequence { .. } => "Sequence".to_owned(),
275 NodeKind::Choice { .. } => "Choice".to_owned(),
276 NodeKind::Hole { .. } => "[ ]".to_owned(),
277 }
278}
279
280fn is_hidden_root(kind: &NodeKind) -> bool {
281 matches!(kind, NodeKind::Sequence { .. })
282}
283
284fn children_of(kind: &NodeKind) -> Vec<NodeId> {
286 match kind {
287 NodeKind::Sequence { children } => children.clone(),
288 NodeKind::Section { header, body } => {
289 header.iter().copied().chain(body.iter().copied()).collect()
290 }
291 NodeKind::Repeat { body, .. } => body.clone(),
292 NodeKind::Tuple { elements } => elements.clone(),
293 NodeKind::Choice { variants, .. } => variants
294 .iter()
295 .flat_map(|(_, v)| v.iter())
296 .copied()
297 .collect(),
298 _ => Vec::new(),
299 }
300}
301
302fn slot_entries_for(kind: &NodeKind) -> Vec<SlotEntry> {
304 match kind {
305 NodeKind::Sequence { children } => children
306 .iter()
307 .map(|&child| SlotEntry {
308 name: "children".to_owned(),
309 child,
310 })
311 .collect(),
312 NodeKind::Section { header, body } => {
313 let mut entries = Vec::new();
314 if let Some(header_id) = header {
315 entries.push(SlotEntry {
316 name: "header".to_owned(),
317 child: *header_id,
318 });
319 }
320 for &body_id in body {
321 entries.push(SlotEntry {
322 name: "body".to_owned(),
323 child: body_id,
324 });
325 }
326 entries
327 }
328 NodeKind::Repeat { body, .. } => body
329 .iter()
330 .map(|&child| SlotEntry {
331 name: "body".to_owned(),
332 child,
333 })
334 .collect(),
335 NodeKind::Tuple { elements } => elements
336 .iter()
337 .map(|&child| SlotEntry {
338 name: "elements".to_owned(),
339 child,
340 })
341 .collect(),
342 NodeKind::Choice { variants, .. } => variants
343 .iter()
344 .flat_map(|(_, v)| v.iter())
345 .map(|&child| SlotEntry {
346 name: "variant".to_owned(),
347 child,
348 })
349 .collect(),
350 _ => Vec::new(),
351 }
352}
353
354fn format_constraint(c: &Constraint) -> String {
356 match c {
357 Constraint::Range { lower, upper, .. } => {
358 format!("Range: {} to {}", format_expr(lower), format_expr(upper))
359 }
360 Constraint::TypeDecl { expected, .. } => format!("Type: {expected:?}"),
361 Constraint::LengthRelation { length, .. } => format!("Length: {}", format_expr(length)),
362 Constraint::Relation { lhs, op, rhs } => {
363 format!(
364 "Relation: {} {:?} {}",
365 format_expr(lhs),
366 op,
367 format_expr(rhs)
368 )
369 }
370 Constraint::Distinct { unit, .. } => format!("Distinct: {unit:?}"),
371 Constraint::Property { tag, .. } => format!("Property: {tag:?}"),
372 Constraint::SumBound { upper, .. } => format!("SumBound: {}", format_expr(upper)),
373 Constraint::Sorted { order, .. } => format!("Sorted: {order:?}"),
374 Constraint::Guarantee { description, .. } => format!("Guarantee: {description}"),
375 Constraint::CharSet { charset, .. } => format!("CharSet: {charset:?}"),
376 Constraint::StringLength { min, max, .. } => {
377 format!("StringLength: {} to {}", format_expr(min), format_expr(max))
378 }
379 Constraint::RenderHint { hint, .. } => format!("RenderHint: {hint:?}"),
380 }
381}
382
383fn format_expr(e: &Expression) -> String {
385 match e {
386 Expression::Lit(n) => n.to_string(),
387 Expression::Var(r) => format!("{r:?}"),
388 Expression::BinOp { op, lhs, rhs } => {
389 format!("({} {:?} {})", format_expr(lhs), op, format_expr(rhs))
390 }
391 Expression::Pow { base, exp } => {
392 format!("({} ^ {})", format_expr(base), format_expr(exp))
393 }
394 Expression::FnCall { name, args } => {
395 let arg_strs: Vec<_> = args.iter().map(format_expr).collect();
396 format!("{}({})", name.as_str(), arg_strs.join(", "))
397 }
398 }
399}