1use smol_str::SmolStr;
13use std::collections::{BTreeSet, HashSet, VecDeque};
14use std::rc::{Rc, Weak};
15
16use crate::expression_tree::{BindingExpression, Expression};
17use crate::langtype::ElementType;
18use crate::namedreference::NamedReference;
19use crate::object_tree::{Component, Document, ElementRc};
20use crate::CompilerConfiguration;
21
22#[cfg(feature = "cpp")]
23pub mod cpp;
24
25#[cfg(feature = "rust")]
26pub mod rust;
27
28#[derive(Clone, Debug, PartialEq)]
29pub enum OutputFormat {
30 #[cfg(feature = "cpp")]
31 Cpp(cpp::Config),
32 #[cfg(feature = "rust")]
33 Rust,
34 Interpreter,
35 Llr,
36}
37
38impl OutputFormat {
39 pub fn guess_from_extension(path: &std::path::Path) -> Option<Self> {
40 match path.extension().and_then(|ext| ext.to_str()) {
41 #[cfg(feature = "cpp")]
42 Some("cpp") | Some("cxx") | Some("h") | Some("hpp") => {
43 Some(Self::Cpp(cpp::Config::default()))
44 }
45 #[cfg(feature = "rust")]
46 Some("rs") => Some(Self::Rust),
47 _ => None,
48 }
49 }
50}
51
52impl std::str::FromStr for OutputFormat {
53 type Err = String;
54 fn from_str(s: &str) -> Result<Self, Self::Err> {
55 match s {
56 #[cfg(feature = "cpp")]
57 "cpp" => Ok(Self::Cpp(cpp::Config::default())),
58 #[cfg(feature = "rust")]
59 "rust" => Ok(Self::Rust),
60 "llr" => Ok(Self::Llr),
61 _ => Err(format!("Unknown output format {s}")),
62 }
63 }
64}
65
66pub fn generate(
67 format: OutputFormat,
68 destination: &mut impl std::io::Write,
69 doc: &Document,
70 compiler_config: &CompilerConfiguration,
71) -> std::io::Result<()> {
72 #![allow(unused_variables)]
73 #![allow(unreachable_code)]
74
75 match format {
76 #[cfg(feature = "cpp")]
77 OutputFormat::Cpp(config) => {
78 let output = cpp::generate(doc, config, compiler_config)?;
79 write!(destination, "{output}")?;
80 }
81 #[cfg(feature = "rust")]
82 OutputFormat::Rust => {
83 let output = rust::generate(doc, compiler_config)?;
84 write!(destination, "{output}")?;
85 }
86 OutputFormat::Interpreter => {
87 return Err(std::io::Error::new(
88 std::io::ErrorKind::Other,
89 "Unsupported output format: The interpreter is not a valid output format yet.",
90 )); }
92 OutputFormat::Llr => {
93 let root = crate::llr::lower_to_item_tree::lower_to_item_tree(doc, compiler_config)?;
94 let mut output = String::new();
95 crate::llr::pretty_print::pretty_print(&root, &mut output).unwrap();
96 write!(destination, "{output}")?;
97 }
98 }
99 Ok(())
100}
101
102pub trait ItemTreeBuilder {
105 type SubComponentState: Clone;
107
108 fn push_repeated_item(
109 &mut self,
110 item: &crate::object_tree::ElementRc,
111 repeater_count: u32,
112 parent_index: u32,
113 component_state: &Self::SubComponentState,
114 );
115 fn push_native_item(
116 &mut self,
117 item: &ElementRc,
118 children_offset: u32,
119 parent_index: u32,
120 component_state: &Self::SubComponentState,
121 );
122 fn enter_component(
125 &mut self,
126 item: &ElementRc,
127 sub_component: &Rc<Component>,
128 children_offset: u32,
129 component_state: &Self::SubComponentState,
130 ) -> Self::SubComponentState;
131 fn enter_component_children(
133 &mut self,
134 item: &ElementRc,
135 repeater_count: u32,
136 component_state: &Self::SubComponentState,
137 sub_component_state: &Self::SubComponentState,
138 );
139}
140
141pub fn build_item_tree<T: ItemTreeBuilder>(
143 root_component: &Rc<Component>,
144 initial_state: &T::SubComponentState,
145 builder: &mut T,
146) {
147 if let Some(sub_component) = root_component.root_element.borrow().sub_component() {
148 assert!(root_component.root_element.borrow().children.is_empty());
149 let sub_compo_state =
150 builder.enter_component(&root_component.root_element, sub_component, 1, initial_state);
151 builder.enter_component_children(
152 &root_component.root_element,
153 0,
154 initial_state,
155 &sub_compo_state,
156 );
157 build_item_tree::<T>(sub_component, &sub_compo_state, builder);
158 } else {
159 let mut repeater_count = 0;
160 visit_item(initial_state, &root_component.root_element, 1, &mut repeater_count, 0, builder);
161
162 visit_children(
163 initial_state,
164 &root_component.root_element.borrow().children,
165 root_component,
166 &root_component.root_element,
167 0,
168 0,
169 1,
170 1,
171 &mut repeater_count,
172 builder,
173 );
174 }
175
176 fn item_sub_tree_size(e: &ElementRc) -> usize {
180 let mut count = e.borrow().children.len();
181 if let Some(sub_component) = e.borrow().sub_component() {
182 count += item_sub_tree_size(&sub_component.root_element);
183 }
184 for i in &e.borrow().children {
185 count += item_sub_tree_size(i);
186 }
187 count
188 }
189
190 fn visit_children<T: ItemTreeBuilder>(
191 state: &T::SubComponentState,
192 children: &[ElementRc],
193 _component: &Rc<Component>,
194 parent_item: &ElementRc,
195 parent_index: u32,
196 relative_parent_index: u32,
197 children_offset: u32,
198 relative_children_offset: u32,
199 repeater_count: &mut u32,
200 builder: &mut T,
201 ) {
202 debug_assert_eq!(
203 relative_parent_index,
204 *parent_item.borrow().item_index.get().unwrap_or(&parent_index)
205 );
206
207 if children.is_empty() {
221 if let Some(nested_subcomponent) = parent_item.borrow().sub_component() {
222 let sub_component_state = builder.enter_component(
223 parent_item,
224 nested_subcomponent,
225 children_offset,
226 state,
227 );
228 visit_children(
229 &sub_component_state,
230 &nested_subcomponent.root_element.borrow().children,
231 nested_subcomponent,
232 &nested_subcomponent.root_element,
233 parent_index,
234 relative_parent_index,
235 children_offset,
236 relative_children_offset,
237 repeater_count,
238 builder,
239 );
240 return;
241 }
242 }
243
244 let mut offset = children_offset + children.len() as u32;
245
246 let mut sub_component_states = VecDeque::new();
247
248 for child in children.iter() {
249 if let Some(sub_component) = child.borrow().sub_component() {
250 let sub_component_state =
251 builder.enter_component(child, sub_component, offset, state);
252 visit_item(
253 &sub_component_state,
254 &sub_component.root_element,
255 offset,
256 repeater_count,
257 parent_index,
258 builder,
259 );
260 sub_component_states.push_back(sub_component_state);
261 } else {
262 visit_item(state, child, offset, repeater_count, parent_index, builder);
263 }
264 offset += item_sub_tree_size(child) as u32;
265 }
266
267 let mut offset = children_offset + children.len() as u32;
268 let mut relative_offset = relative_children_offset + children.len() as u32;
269 let mut index = children_offset;
270 let mut relative_index = relative_children_offset;
271
272 for e in children.iter() {
273 if let Some(sub_component) = e.borrow().sub_component() {
274 let sub_tree_state = sub_component_states.pop_front().unwrap();
275 builder.enter_component_children(e, *repeater_count, state, &sub_tree_state);
276 visit_children(
277 &sub_tree_state,
278 &sub_component.root_element.borrow().children,
279 sub_component,
280 &sub_component.root_element,
281 index,
282 0,
283 offset,
284 1,
285 repeater_count,
286 builder,
287 );
288 } else {
289 visit_children(
290 state,
291 &e.borrow().children,
292 _component,
293 e,
294 index,
295 relative_index,
296 offset,
297 relative_offset,
298 repeater_count,
299 builder,
300 );
301 }
302
303 index += 1;
304 relative_index += 1;
305 let size = item_sub_tree_size(e) as u32;
306 offset += size;
307 relative_offset += size;
308 }
309 }
310
311 fn visit_item<T: ItemTreeBuilder>(
312 component_state: &T::SubComponentState,
313 item: &ElementRc,
314 children_offset: u32,
315 repeater_count: &mut u32,
316 parent_index: u32,
317 builder: &mut T,
318 ) {
319 if item.borrow().repeated.is_some() {
320 builder.push_repeated_item(item, *repeater_count, parent_index, component_state);
321 *repeater_count += 1;
322 } else {
323 let mut item = item.clone();
324 let mut component_state = component_state.clone();
325 while let Some((base, state)) = {
326 let base = item.borrow().sub_component().map(|c| {
327 (
328 c.root_element.clone(),
329 builder.enter_component(&item, c, children_offset, &component_state),
330 )
331 });
332 base
333 } {
334 item = base;
335 component_state = state;
336 }
337 builder.push_native_item(&item, children_offset, parent_index, &component_state)
338 }
339 }
340}
341
342pub fn handle_property_bindings_init(
346 component: &Rc<Component>,
347 mut handle_property: impl FnMut(&ElementRc, &SmolStr, &BindingExpression),
348) {
349 fn handle_property_inner(
350 component: &Weak<Component>,
351 elem: &ElementRc,
352 prop_name: &SmolStr,
353 binding_expression: &BindingExpression,
354 handle_property: &mut impl FnMut(&ElementRc, &SmolStr, &BindingExpression),
355 processed: &mut HashSet<NamedReference>,
356 ) {
357 if elem.borrow().is_component_placeholder {
358 return; }
360 let nr = NamedReference::new(elem, prop_name.clone());
361 if processed.contains(&nr) {
362 return;
363 }
364 processed.insert(nr);
365 if binding_expression.analysis.as_ref().is_some_and(|a| a.is_const) {
366 binding_expression.expression.visit_recursive(&mut |e| {
369 if let Expression::PropertyReference(nr) = e {
370 let elem = nr.element();
371 if Weak::ptr_eq(&elem.borrow().enclosing_component, component) {
372 if let Some(be) = elem.borrow().bindings.get(nr.name()) {
373 handle_property_inner(
374 component,
375 &elem,
376 nr.name(),
377 &be.borrow(),
378 handle_property,
379 processed,
380 );
381 }
382 }
383 }
384 })
385 }
386 handle_property(elem, prop_name, binding_expression);
387 }
388
389 let mut processed = HashSet::new();
390 crate::object_tree::recurse_elem(&component.root_element, &(), &mut |elem: &ElementRc, ()| {
391 for (prop_name, binding_expression) in &elem.borrow().bindings {
392 handle_property_inner(
393 &Rc::downgrade(component),
394 elem,
395 prop_name,
396 &binding_expression.borrow(),
397 &mut handle_property,
398 &mut processed,
399 );
400 }
401 });
402}
403
404pub fn for_each_const_properties(
407 component: &Rc<Component>,
408 mut f: impl FnMut(&ElementRc, &SmolStr),
409) {
410 crate::object_tree::recurse_elem(&component.root_element, &(), &mut |elem: &ElementRc, ()| {
411 if elem.borrow().repeated.is_some() {
412 return;
413 }
414 let mut e = elem.clone();
415 let mut all_prop = BTreeSet::new();
416 loop {
417 all_prop.extend(
418 e.borrow()
419 .property_declarations
420 .iter()
421 .filter(|(_, x)| {
422 x.property_type.is_property_type() &&
423 !matches!( &x.property_type, crate::langtype::Type::Struct(s) if s.name.as_ref().is_some_and(|name| name.ends_with("::StateInfo")))
424 })
425 .map(|(k, _)| k.clone()),
426 );
427 match &e.clone().borrow().base_type {
428 ElementType::Component(c) => {
429 e = c.root_element.clone();
430 }
431 ElementType::Native(n) => {
432 let mut n = n;
433 loop {
434 all_prop.extend(
435 n.properties
436 .iter()
437 .filter(|(k, x)| {
438 x.ty.is_property_type()
439 && !k.starts_with("viewport-")
440 && k.as_str() != "commands"
441 })
442 .map(|(k, _)| k.clone()),
443 );
444 match n.parent.as_ref() {
445 Some(p) => n = p,
446 None => break,
447 }
448 }
449 break;
450 }
451 ElementType::Builtin(_) => {
452 unreachable!("builtin element should have been resolved")
453 }
454 ElementType::Global | ElementType::Error => break,
455 }
456 }
457 for c in all_prop {
458 if NamedReference::new(elem, c.clone()).is_constant() {
459 f(elem, &c);
460 }
461 }
462 });
463}
464
465pub fn to_pascal_case(str: &str) -> String {
467 let mut result = Vec::with_capacity(str.len());
468 let mut next_upper = true;
469 for x in str.as_bytes() {
470 if *x == b'-' {
471 next_upper = true;
472 } else if next_upper {
473 result.push(x.to_ascii_uppercase());
474 next_upper = false;
475 } else {
476 result.push(*x);
477 }
478 }
479 String::from_utf8(result).unwrap()
480}
481
482pub fn to_kebab_case(str: &str) -> String {
484 let mut result = Vec::with_capacity(str.len());
485 for x in str.as_bytes() {
486 if x.is_ascii_uppercase() {
487 if !result.is_empty() {
488 result.push(b'-');
489 }
490 result.push(x.to_ascii_lowercase());
491 } else {
492 result.push(*x);
493 }
494 }
495 String::from_utf8(result).unwrap()
496}
497
498#[test]
499fn case_conversions() {
500 assert_eq!(to_kebab_case("HelloWorld"), "hello-world");
501 assert_eq!(to_pascal_case("hello-world"), "HelloWorld");
502}