use super::manifest::{
ComponentDefinition, ControlFlowRepeatPredicateDefinition, ExpressionSpec,
ExpressionSpecInvocation, PaxManifest, PropertyDefinition, SettingsSelectorBlockDefinition,
TemplateNodeDefinition, ValueDefinition,
};
use std::collections::HashMap;
use std::ops::{IndexMut, RangeFrom};
use std::slice::IterMut;
use crate::manifest::{PropertyDefinitionFlags, TypeDefinition, TypeTable};
use crate::parsing::escape_identifier;
use itertools::Itertools;
use lazy_static::lazy_static;
pub fn compile_all_expressions<'a>(manifest: &'a mut PaxManifest) {
let mut swap_expression_specs: HashMap<usize, ExpressionSpec> = HashMap::new();
let mut all_expression_specs: HashMap<usize, ExpressionSpec> = HashMap::new();
let mut new_components = manifest.components.clone();
let mut uid_track = 0;
new_components
.values_mut()
.for_each(|component_def: &mut ComponentDefinition| {
let mut new_component_def = component_def.clone();
let read_only_component_def = component_def.clone();
if let Some(ref mut template) = new_component_def.template {
let mut active_node_def = TemplateNodeDefinition::default();
std::mem::swap(&mut active_node_def, template.index_mut(0));
let mut ctx = ExpressionCompilationContext {
template,
active_node_def,
scope_stack: vec![component_def
.get_property_definitions(&manifest.type_table)
.iter()
.map(|pd| (pd.name.clone(), pd.clone()))
.collect()],
uid_gen: uid_track..,
all_components: manifest.components.clone(),
expression_specs: &mut swap_expression_specs,
component_def: &read_only_component_def,
type_table: &manifest.type_table,
};
ctx = recurse_compile_expressions(ctx);
uid_track = ctx.uid_gen.next().unwrap();
all_expression_specs.extend(ctx.expression_specs.to_owned());
std::mem::swap(&mut ctx.active_node_def, template.index_mut(0));
}
std::mem::swap(component_def, &mut new_component_def);
});
manifest.components = new_components;
manifest.expression_specs = Some(swap_expression_specs);
}
fn pull_matched_identifiers_from_inline(
inline_settings: &Option<Vec<(String, ValueDefinition)>>,
s: String,
) -> Vec<String> {
let mut ret = Vec::new();
if let Some(val) = inline_settings {
for (_, matched) in val.iter().filter(|avd| avd.0 == s.as_str()) {
match matched {
ValueDefinition::Identifier(s, _) => ret.push(s.clone()),
_ => {}
};
}
}
ret
}
fn pull_settings_with_selector(
settings: &Option<Vec<SettingsSelectorBlockDefinition>>,
selector: String,
) -> Option<Vec<(String, ValueDefinition)>> {
if let Some(val) = settings {
let merged_settings: Vec<(String, ValueDefinition)> = val
.iter()
.filter(|block| block.selector == selector)
.map(|block| block.value_block.settings_key_value_pairs.clone())
.flatten()
.clone()
.collect();
if merged_settings.len() > 0 {
Some(merged_settings)
} else {
None
}
} else {
None
}
}
fn merge_inline_settings_with_settings_block(
inline_settings: &Option<Vec<(String, ValueDefinition)>>,
settings_block: &Option<Vec<SettingsSelectorBlockDefinition>>,
) -> Option<Vec<(String, ValueDefinition)>> {
let ids = pull_matched_identifiers_from_inline(&inline_settings, "id".to_string());
let mut id_settings = Vec::new();
if ids.len() == 1 {
if let Some(settings) = pull_settings_with_selector(&settings_block, format!("#{}", ids[0]))
{
id_settings.extend(settings.clone());
}
} else if ids.len() > 1 {
panic!("Specified more than one id inline!");
}
let classes = pull_matched_identifiers_from_inline(&inline_settings, "class".to_string());
let mut class_settings = Vec::new();
for class in classes {
if let Some(settings) = pull_settings_with_selector(&settings_block, format!(".{}", class))
{
class_settings.extend(settings.clone());
}
}
let mut map = HashMap::new();
for (key, value) in class_settings.into_iter() {
map.insert(key, value);
}
for (key, value) in id_settings.into_iter() {
map.insert(key, value);
}
if let Some(inline) = inline_settings.clone() {
for (key, value) in inline.into_iter() {
map.insert(key, value);
}
}
let merged: Vec<(String, ValueDefinition)> = map.into_iter().collect();
if merged.len() > 0 {
Some(merged)
} else {
None
}
}
fn recurse_compile_literal_block<'a>(
settings_pairs: IterMut<(String, ValueDefinition)>,
ctx: &mut ExpressionCompilationContext,
current_property_definitions: Vec<PropertyDefinition>,
type_id: String,
) {
settings_pairs.for_each(|pair| {
match &mut pair.1 {
ValueDefinition::LiteralValue(_) => {
}
ValueDefinition::EventBindingTarget(_) => {
}
ValueDefinition::Block(block) => {
let type_def = (current_property_definitions
.iter()
.find(|property_def| property_def.name == pair.0))
.expect(&format!(
"Property `{}` not found on `{}`",
&pair.0, type_id
))
.get_type_definition(ctx.type_table);
recurse_compile_literal_block(
block.settings_key_value_pairs.iter_mut(),
ctx,
type_def.property_definitions.clone(),
type_def.type_id_escaped.clone(),
);
}
ValueDefinition::Expression(input, manifest_id) => {
let id = ctx.uid_gen.next().unwrap();
let (output_statement, invocations) = compile_paxel_to_ril(&input, &ctx);
let builtin_types = HashMap::from([
("transform", "Transform2D".to_string()),
("width", "Size".to_string()),
("height", "Size".to_string()),
("x", "Size".to_string()),
("y", "Size".to_string()),
("anchor_x", "Size".to_string()),
("anchor_y", "Size".to_string()),
("skew_x", "Numeric".to_string()),
("skew_y", "Numeric".to_string()),
("scale_x", "Size".to_string()),
("scale_y", "Size".to_string()),
("rotate", "Rotation".to_string()),
]);
let pascalized_return_type = if let Some(type_string) = builtin_types.get(&*pair.0)
{
type_string.to_string()
} else {
(current_property_definitions
.iter()
.find(|property_def| property_def.name == pair.0)
.expect(&format!(
"Property `{}` not found on component `{}`",
&pair.0, type_id
))
.get_type_definition(ctx.type_table)
.type_id_escaped)
.clone()
};
let mut whitespace_removed_input = input.clone();
whitespace_removed_input.retain(|c| !c.is_whitespace());
ctx.expression_specs.insert(
id,
ExpressionSpec {
id,
pascalized_return_type,
invocations,
output_statement,
input_statement: whitespace_removed_input,
is_repeat_source_iterable_expression: false,
repeat_source_iterable_type_id_escaped: "".to_string(),
},
);
let mut manifest_id_insert = Some(id);
std::mem::swap(manifest_id, &mut manifest_id_insert);
}
ValueDefinition::Identifier(identifier, manifest_id) => {
if pair.0 == "id" || pair.0 == "class" {
} else {
let id = ctx.uid_gen.next().unwrap();
let mut manifest_id_insert: Option<usize> = Some(id);
std::mem::swap(manifest_id, &mut manifest_id_insert);
let (output_statement, invocations) = compile_paxel_to_ril(&identifier, &ctx);
let pascalized_return_type = (&ctx
.component_def
.get_property_definitions(ctx.type_table)
.iter()
.find(|property_def| property_def.name == pair.0)
.unwrap()
.get_type_definition(ctx.type_table)
.type_id_escaped)
.clone();
ctx.expression_specs.insert(
id,
ExpressionSpec {
id,
pascalized_return_type,
invocations,
output_statement,
input_statement: identifier.clone(),
is_repeat_source_iterable_expression: false,
repeat_source_iterable_type_id_escaped: "".to_string(),
},
);
}
}
_ => {
unreachable!()
}
}
})
}
fn recurse_compile_expressions<'a>(
mut ctx: ExpressionCompilationContext<'a>,
) -> ExpressionCompilationContext<'a> {
let incremented = false;
let cloned_settings_block = ctx.component_def.settings.clone();
let cloned_inline_settings = ctx.active_node_def.settings.clone();
let mut merged_settings =
merge_inline_settings_with_settings_block(&cloned_inline_settings, &cloned_settings_block);
let mut cloned_control_flow_settings = ctx.active_node_def.control_flow_settings.clone();
if let Some(ref mut inline_settings) = merged_settings {
let type_id = ctx.active_node_def.type_id.clone();
let pascal_identifier;
let property_def;
{
let active_node_component = ctx.all_components.get(&type_id)
.expect(&format!("No known component with identifier {}. Try importing or defining a component named {}", &type_id, &type_id));
pascal_identifier = active_node_component.pascal_identifier.clone();
property_def = active_node_component.get_property_definitions(&mut ctx.type_table);
}
recurse_compile_literal_block(
inline_settings.iter_mut(),
&mut ctx,
property_def.clone(),
pascal_identifier,
);
} else if let Some(ref mut cfa) = cloned_control_flow_settings {
if let Some(ref mut repeat_source_definition) = &mut cfa.repeat_source_definition {
let id = ctx.uid_gen.next().unwrap();
repeat_source_definition.vtable_id = Some(id);
let repeat_source_definition = cfa.repeat_source_definition.as_ref().unwrap();
let is_repeat_source_range = repeat_source_definition.range_expression_paxel.is_some();
let is_repeat_source_iterable = repeat_source_definition.symbolic_binding.is_some();
let (paxel, return_type) = if let Some(range_expression_paxel) =
&repeat_source_definition.range_expression_paxel
{
(
range_expression_paxel.to_string(),
TypeDefinition::builtin_range_isize(),
)
} else if let Some(symbolic_binding) = &repeat_source_definition.symbolic_binding {
let inner_iterable_type_id = ctx
.resolve_symbol_as_prop_def(symbolic_binding)
.unwrap()
.last()
.unwrap()
.get_inner_iterable_type_definition(ctx.type_table)
.unwrap()
.type_id
.clone();
(
symbolic_binding.to_string(),
TypeDefinition::builtin_vec_rc_properties_coproduct(inner_iterable_type_id),
)
} else {
unreachable!()
};
let repeat_source_iterable_type_id_escaped =
if let Some(iiti) = return_type.inner_iterable_type_id {
escape_identifier(iiti.clone())
} else {
"".to_string()
};
let (output_statement, invocations) = compile_paxel_to_ril(&paxel, &ctx);
match cfa.repeat_predicate_definition.as_ref().unwrap() {
ControlFlowRepeatPredicateDefinition::ElemId(elem_id) => {
let property_definition = PropertyDefinition {
name: format!("{}", elem_id),
flags: PropertyDefinitionFlags {
is_binding_repeat_i: false,
is_binding_repeat_elem: true,
is_repeat_source_range,
is_repeat_source_iterable,
is_property_wrapped: true,
},
type_id: "isize".to_string(),
};
let scope = HashMap::from([
(elem_id.clone(), property_definition),
]);
ctx.scope_stack.push(scope);
}
ControlFlowRepeatPredicateDefinition::ElemIdIndexId(elem_id, index_id) => {
let iterable_type =
if let Some(_) = &repeat_source_definition.range_expression_paxel {
TypeDefinition::primitive("isize")
} else if let Some(symbolic_binding) =
&repeat_source_definition.symbolic_binding
{
let pd = ctx
.resolve_symbol_as_prop_def(symbolic_binding)
.expect(&format!("Property not found: {}", symbolic_binding))
.last()
.unwrap()
.clone();
pd.get_inner_iterable_type_definition(ctx.type_table)
.unwrap()
.clone()
} else {
unreachable!()
};
let elem_property_definition = PropertyDefinition {
name: format!("{}", elem_id),
type_id: iterable_type.type_id,
flags: PropertyDefinitionFlags {
is_binding_repeat_elem: true,
is_binding_repeat_i: false,
is_repeat_source_range,
is_repeat_source_iterable,
is_property_wrapped: true,
},
};
let mut i_property_definition =
PropertyDefinition::primitive_with_name("usize", index_id);
i_property_definition.flags = PropertyDefinitionFlags {
is_binding_repeat_i: true,
is_binding_repeat_elem: false,
is_repeat_source_range,
is_repeat_source_iterable,
is_property_wrapped: true,
};
ctx.scope_stack.push(HashMap::from([
(elem_id.clone(), elem_property_definition),
(index_id.clone(), i_property_definition),
]));
}
};
let mut whitespace_removed_input = paxel.clone();
whitespace_removed_input.retain(|c| !c.is_whitespace());
ctx.expression_specs.insert(
id,
ExpressionSpec {
id,
pascalized_return_type: return_type.type_id_escaped,
invocations,
output_statement,
input_statement: whitespace_removed_input,
is_repeat_source_iterable_expression: is_repeat_source_iterable,
repeat_source_iterable_type_id_escaped,
},
);
} else if let Some(condition_expression_paxel) = &cfa.condition_expression_paxel {
let (output_statement, invocations) =
compile_paxel_to_ril(&condition_expression_paxel, &ctx);
let id = ctx.uid_gen.next().unwrap();
cfa.condition_expression_vtable_id = Some(id);
let mut whitespace_removed_input = condition_expression_paxel.clone();
whitespace_removed_input.retain(|c| !c.is_whitespace());
ctx.expression_specs.insert(
id,
ExpressionSpec {
id,
pascalized_return_type: "bool".to_string(),
invocations,
output_statement,
input_statement: whitespace_removed_input,
is_repeat_source_iterable_expression: false,
repeat_source_iterable_type_id_escaped: "".to_string(),
},
);
} else if let Some(slot_index_expression_paxel) = &cfa.slot_index_expression_paxel {
let (output_statement, invocations) =
compile_paxel_to_ril(&slot_index_expression_paxel, &ctx);
let id = ctx.uid_gen.next().unwrap();
cfa.slot_index_expression_vtable_id = Some(id);
let mut whitespace_removed_input = slot_index_expression_paxel.clone();
whitespace_removed_input.retain(|c| !c.is_whitespace());
ctx.expression_specs.insert(
id,
ExpressionSpec {
id,
pascalized_return_type: "Numeric".to_string(),
invocations,
output_statement,
input_statement: whitespace_removed_input,
is_repeat_source_iterable_expression: false,
repeat_source_iterable_type_id_escaped: "".to_string(),
},
);
} else {
unreachable!("encountered invalid control flow definition")
}
std::mem::swap(
&mut cloned_control_flow_settings,
&mut ctx.active_node_def.control_flow_settings,
);
}
std::mem::swap(&mut merged_settings, &mut ctx.active_node_def.settings);
for id in ctx.active_node_def.child_ids.clone().iter() {
let mut active_node_def = TemplateNodeDefinition::default();
let mut old_active_node_def = TemplateNodeDefinition::default();
std::mem::swap(&mut active_node_def, ctx.template.get_mut(*id).unwrap());
std::mem::swap(&mut old_active_node_def, &mut ctx.active_node_def);
ctx.active_node_def = active_node_def;
ctx = recurse_compile_expressions(ctx);
std::mem::swap(&mut ctx.active_node_def, ctx.template.get_mut(*id).unwrap());
std::mem::swap(&mut old_active_node_def, &mut ctx.active_node_def);
}
if incremented {
ctx.scope_stack.pop();
}
ctx
}
fn resolve_symbol_as_invocation(
sym: &str,
ctx: &ExpressionCompilationContext,
) -> ExpressionSpecInvocation {
if BUILTIN_MAP.contains_key(sym) {
unimplemented!("Built-ins like $bounds are not yet supported")
} else {
let prop_def_chain = ctx
.resolve_symbol_as_prop_def(&sym)
.expect(&format!("symbol not found: {}", &sym));
let nested_prop_def = prop_def_chain.last().unwrap();
let is_nested_numeric = ExpressionSpecInvocation::is_numeric(&nested_prop_def.type_id);
let split_symbols = clean_and_split_symbols(&sym);
let escaped_identifier = escape_identifier(split_symbols.join("."));
let mut split_symbols = split_symbols.into_iter();
let root_identifier = split_symbols.next().unwrap().to_string();
let root_prop_def = prop_def_chain.first().unwrap();
let properties_coproduct_type = ctx.component_def.type_id_escaped.clone();
let iterable_type_id_escaped = if root_prop_def.flags.is_binding_repeat_elem {
escape_identifier(root_prop_def.type_id.clone())
} else if root_prop_def.flags.is_binding_repeat_i {
"usize".to_string()
} else {
"".to_string()
};
let mut found_depth: Option<usize> = None;
let mut current_depth = 0;
let mut found_val: Option<PropertyDefinition> = None;
while let None = found_depth {
let map = ctx
.scope_stack
.get((ctx.scope_stack.len() - 1) - current_depth)
.unwrap();
if let Some(val) = map.get(&root_identifier) {
found_depth = Some(current_depth);
found_val = Some(val.clone());
} else {
current_depth += 1;
}
}
let stack_offset = found_depth.unwrap();
let found_val = found_val.expect(&format!("Property not found {}", sym));
let property_flags = found_val.flags;
let property_properties_coproduct_type = &root_prop_def
.get_type_definition(ctx.type_table)
.type_id
.split("::")
.last()
.unwrap();
let mut nested_symbol_tail_literal = "".to_string();
prop_def_chain.iter().enumerate().for_each(|(i, elem)| {
if i > 0 && i < prop_def_chain.len() {
nested_symbol_tail_literal += &if elem.flags.is_property_wrapped {
format!(".{}.get()", elem.name)
} else {
format!(".{}", elem.name)
};
}
});
if nested_symbol_tail_literal != "" {
nested_symbol_tail_literal += ".clone()"
}
ExpressionSpecInvocation {
root_identifier,
is_numeric: ExpressionSpecInvocation::is_numeric(&property_properties_coproduct_type),
is_primitive_nonnumeric: ExpressionSpecInvocation::is_primitive_nonnumeric(
&property_properties_coproduct_type,
),
escaped_identifier,
stack_offset,
iterable_type_id_escaped,
properties_coproduct_type,
property_flags,
nested_symbol_tail_literal,
is_nested_numeric,
}
}
}
fn compile_paxel_to_ril<'a>(
paxel: &str,
ctx: &ExpressionCompilationContext<'a>,
) -> (String, Vec<ExpressionSpecInvocation>) {
let (output_string, symbolic_ids) = crate::parsing::run_pratt_parser(paxel);
let invocations = symbolic_ids
.iter()
.map(|sym| resolve_symbol_as_invocation(&sym.trim(), ctx))
.unique_by(|esi| esi.escaped_identifier.clone())
.sorted_by(|esi0, esi1| esi0.escaped_identifier.cmp(&esi1.escaped_identifier))
.collect();
(output_string, invocations)
}
pub struct ExpressionCompilationContext<'a> {
pub component_def: &'a ComponentDefinition,
pub template: &'a mut Vec<TemplateNodeDefinition>,
pub scope_stack: Vec<HashMap<String, PropertyDefinition>>,
pub uid_gen: RangeFrom<usize>,
pub expression_specs: &'a mut HashMap<usize, ExpressionSpec>,
pub active_node_def: TemplateNodeDefinition,
pub all_components: HashMap<String, ComponentDefinition>,
pub type_table: &'a TypeTable,
}
lazy_static! {
static ref BUILTIN_MAP : HashMap<&'static str, ()> = HashMap::from([
("$container",())
]);
}
pub fn clean_and_split_symbols(possibly_nested_symbols: &str) -> Vec<String> {
let entire_symbol = if possibly_nested_symbols.starts_with("self.") {
possibly_nested_symbols.replacen("self.", "", 1)
} else if possibly_nested_symbols.starts_with("this.") {
possibly_nested_symbols.replacen("this.", "", 1)
} else {
possibly_nested_symbols.to_string()
};
entire_symbol
.split(".")
.map(|atomic_symbol| atomic_symbol.to_string())
.collect::<Vec<_>>()
}
impl<'a> ExpressionCompilationContext<'a> {
pub fn resolve_symbol_as_prop_def(&self, symbol: &str) -> Option<Vec<PropertyDefinition>> {
let split_symbols = clean_and_split_symbols(symbol);
let mut split_symbols = split_symbols.iter();
let root_symbol = split_symbols.next().unwrap();
let root_symbol_pd = if BUILTIN_MAP.contains_key(root_symbol.as_str()) {
None } else {
let mut found = false;
let mut exhausted = false;
let mut iter = self.scope_stack.iter();
let mut current_frame = iter.next();
let mut ret: Option<PropertyDefinition> = None;
while !found && !exhausted {
if let Some(frame) = current_frame {
if let Some(pv) = frame.get(root_symbol) {
ret = Some(pv.clone());
found = true;
}
current_frame = iter.next();
} else {
exhausted = true;
}
}
ret
};
match root_symbol_pd {
Some(root_symbol_pd) => {
let mut ret = vec![root_symbol_pd];
while let Some(atomic_symbol) = split_symbols.next() {
let td = ret.last().unwrap().get_type_definition(self.type_table);
ret.push(
td.property_definitions
.iter()
.find(|pd| pd.name == *atomic_symbol)
.expect(&format!(
"Unable to resolve nested symbol `{}` while evaluating `{}`.",
atomic_symbol, symbol
))
.clone(),
);
}
Some(ret)
}
None => None,
}
}
}