Skip to main content

pax_language/interpreter/
property_resolution.rs

1use std::collections::HashMap;
2
3use pax_runtime_api::{LocalProperty, PaxValue, Variable};
4
5use super::{
6    PaxAccessor, PaxExpression, PaxInfix, PaxNullCoalesce, PaxPostfix, PaxPrefix, PaxPrimary,
7    PaxTernary,
8};
9
10/// Trait for resolving identifiers to values
11/// This is implemented by RuntimePropertyStackFrame
12pub trait IdentifierResolver {
13    fn resolve(&self, name: String) -> Result<Variable, String>;
14}
15
16/// Walk an expression tree and report the symbols that should dirty-watch it.
17pub trait DependencyCollector {
18    fn collect_dependencies(&self) -> Vec<String>;
19}
20
21impl DependencyCollector for PaxExpression {
22    fn collect_dependencies(&self) -> Vec<String> {
23        match self {
24            PaxExpression::Primary(p) => p.collect_dependencies(),
25            PaxExpression::Prefix(p) => p.collect_dependencies(),
26            PaxExpression::Infix(p) => p.collect_dependencies(),
27            PaxExpression::Postfix(p) => p.collect_dependencies(),
28            PaxExpression::Ternary(p) => p.collect_dependencies(),
29            PaxExpression::NullCoalesce(p) => p.collect_dependencies(),
30        }
31    }
32}
33
34impl DependencyCollector for PaxPrimary {
35    fn collect_dependencies(&self) -> Vec<String> {
36        let mut ret = match self {
37            PaxPrimary::Literal(_) => vec![],
38            PaxPrimary::Grouped(expr, _) => expr.collect_dependencies(),
39            PaxPrimary::Identifier(i, accessors) => {
40                let mut deps = vec![i.name.clone()];
41                for accessor in accessors {
42                    if let PaxAccessor::List(index) = accessor {
43                        deps.extend(index.collect_dependencies());
44                    }
45                }
46                deps
47            }
48            PaxPrimary::Object(o) => o
49                .iter()
50                .flat_map(|(_, v)| v.collect_dependencies())
51                .collect(),
52            PaxPrimary::FunctionOrEnum(_, _, args) => {
53                args.iter().flat_map(|a| a.collect_dependencies()).collect()
54            }
55            PaxPrimary::Range(start, end) => {
56                let mut deps = start.collect_dependencies();
57                deps.extend(end.collect_dependencies());
58                deps
59            }
60            PaxPrimary::Tuple(t) => t.iter().flat_map(|e| e.collect_dependencies()).collect(),
61            PaxPrimary::List(l) => l.iter().flat_map(|e| e.collect_dependencies()).collect(),
62        };
63        // Keep first occurrence order: these dependencies are serialized into
64        // generated manifests and baked programs, whose identity must be stable.
65        let mut seen = std::collections::HashSet::new();
66        ret.retain(|dependency| seen.insert(dependency.clone()));
67        ret
68    }
69}
70
71impl DependencyCollector for PaxPrefix {
72    fn collect_dependencies(&self) -> Vec<String> {
73        self.rhs.collect_dependencies()
74    }
75}
76
77impl DependencyCollector for PaxInfix {
78    fn collect_dependencies(&self) -> Vec<String> {
79        let mut deps = self.lhs.collect_dependencies();
80        deps.extend(self.rhs.collect_dependencies());
81        deps
82    }
83}
84
85impl DependencyCollector for PaxPostfix {
86    fn collect_dependencies(&self) -> Vec<String> {
87        self.lhs.collect_dependencies()
88    }
89}
90
91impl DependencyCollector for PaxTernary {
92    fn collect_dependencies(&self) -> Vec<String> {
93        let mut deps = self.condition.collect_dependencies();
94        deps.extend(self.then_branch.collect_dependencies());
95        deps.extend(self.else_branch.collect_dependencies());
96        deps
97    }
98}
99
100impl DependencyCollector for PaxNullCoalesce {
101    fn collect_dependencies(&self) -> Vec<String> {
102        let mut deps = self.lhs.collect_dependencies();
103        deps.extend(self.rhs.collect_dependencies());
104        deps
105    }
106}
107
108impl IdentifierResolver for HashMap<String, PaxValue> {
109    fn resolve(&self, name: String) -> Result<Variable, String> {
110        self.get(&name)
111            // A value map supplies local snapshots, not shared application state.
112            // Resolving it must not require a PropertyGraph attachment.
113            .map(|v| Variable::new_from_typed_property(LocalProperty::new(v.clone())))
114            .ok_or(format!("Identifier not found: {}", name))
115    }
116}