Skip to main content

pax_language/
lib.rs

1#[cfg(feature = "parser")]
2use pest::error::Error;
3#[cfg(feature = "parser")]
4pub use pest::iterators::{Pair, Pairs};
5
6#[cfg(feature = "parser")]
7pub use pest::pratt_parser::{Assoc, Op, PrattParser};
8#[cfg(feature = "parser")]
9pub use pest::{Parser, Span};
10#[cfg(feature = "parser")]
11pub use pest_derive::Parser;
12
13#[cfg(feature = "parser")]
14mod parsing;
15#[cfg(feature = "parser")]
16pub use parsing::get_pax_pratt_parser;
17#[cfg(feature = "parser")]
18pub mod formatting;
19
20#[cfg(feature = "parser")]
21pub mod deserializer;
22pub mod interpreter;
23#[cfg(feature = "parser")]
24pub use deserializer::from_pax;
25#[cfg(feature = "parser")]
26pub mod helpers;
27
28#[cfg(feature = "parser")]
29pub use interpreter::parse_pax_expression;
30pub use interpreter::{computable::Computable, property_resolution::DependencyCollector};
31
32/// Pest parser generated from the Pax grammar.
33#[cfg(feature = "parser")]
34#[derive(Parser)]
35#[grammar = "pax.pest"]
36pub struct PaxParser;
37
38#[cfg(feature = "parser")]
39fn renamed_rules(rule: &Rule) -> String {
40    match rule {
41        Rule::template_list_value => "template list entry".to_string(),
42        Rule::EOI => "end of file".to_string(),
43        Rule::WHITESPACE => " ".to_string(),
44        Rule::comment => "comment".to_string(),
45        Rule::pax_component_definition =>  "component".to_string(),
46        Rule::root_tag_pair => "component tag, comment".to_string(),
47        Rule::any_tag_pair => "component tag, comment".to_string(),
48        Rule::open_tag => "opening component tag".to_string(),
49        Rule::closing_tag => "closing component tag".to_string(),
50        Rule::self_closing_tag => "component tag".to_string(),
51        Rule::matched_tag => "component tag".to_string(),
52        Rule::inner_nodes => "literal value, expression, component tag".to_string(),
53        Rule::identifier => "identifier".to_string(),
54        Rule::pascal_identifier => "identifier".to_string(),
55        Rule::event_id => "@HANDLER_NAME".to_string(),
56        Rule::attribute_key_value_pair => "setting key-value pair".to_string(),
57        Rule::class_attribute => "class attribute".to_string(),
58        Rule::class_value => "string, string list, or expression".to_string(),
59        Rule::class_string_list => "class string list".to_string(),
60        Rule::attribute_transition_binding => "transition binding".to_string(),
61        Rule::attribute_event_binding => "handler binding".to_string(),
62        Rule::transition_id => "@in or @out".to_string(),
63        Rule::transition_binding_value => "timeline name or inline @timeline block".to_string(),
64        Rule::transition_inline_timeline_value => "inline transition timeline".to_string(),
65        Rule::transition_inline_timeline_body => "inline transition timeline body".to_string(),
66        Rule::double_binding => "two-way binding".to_string(),
67        Rule::any_template_value => "literal value, literal object, expression, identifier, @gradient".to_string(),
68        Rule::node_inner_content => "literal value or expression".to_string(),
69        Rule::string => "double quoted string".to_string(),
70        Rule::inner => "string".to_string(),
71        Rule::char => "char".to_string(),
72        Rule::settings_block_declaration => "settings block".to_string(),
73        Rule::settings_block_element => "settings block element".to_string(),
74        Rule::settings_conditional => "settings conditional".to_string(),
75        Rule::settings_conditional_body => "settings conditional body".to_string(),
76        Rule::settings_if_branch => "settings if branch".to_string(),
77        Rule::settings_else_if_branch => "settings else if branch".to_string(),
78        Rule::settings_else_branch => "settings else branch".to_string(),
79        Rule::selector_block => "selector block".to_string(),
80        Rule::literal_object => "literal object".to_string(),
81        Rule::selector => "selector (e.g. .CLASS_NAME or #ID_NAME)".to_string(),
82        Rule::settings_key_value_pair => "setting key-value pair".to_string(),
83        Rule::settings_event_binding => "handler binding".to_string(),
84        Rule::settings_key => "setting key (e.g. PROPERTY_NAME: )".to_string(),
85        Rule::settings_value => "literal value, literal object, {expression}, @gradient".to_string(),
86        Rule::timeline_block_declaration => "timeline block".to_string(),
87        Rule::timeline_block_setting => "timeline block setting".to_string(),
88        Rule::timeline_block_setting_value => "timeline block setting value".to_string(),
89        Rule::timeline_selector_block => "timeline selector block".to_string(),
90        Rule::timeline_selector_body => "timeline selector body".to_string(),
91        Rule::timeline_property_key_value_pair => "timeline property key-value pair".to_string(),
92        Rule::timeline_inline_value => "@timeline block".to_string(),
93        Rule::timeline_track => "timeline track".to_string(),
94        Rule::timeline_keyframe => "timeline keyframe".to_string(),
95        Rule::timeline_keyframe_value => "timeline keyframe value".to_string(),
96        Rule::timeline_marker => "timeline marker (e.g. 24, 250ms, 5f, or 50%)".to_string(),
97        Rule::timeline_percent => "percent marker (e.g. 50%)".to_string(),
98        Rule::timeline_duration => "duration marker (e.g. 250ms, 1s, or 5f)".to_string(),
99        Rule::timeline_duration_unit => "duration unit (ms, s, f)".to_string(),
100        Rule::timeline_easing_curve => "timeline easing curve".to_string(),
101        Rule::timeline_target => "timeline target".to_string(),
102        Rule::timeline_local_target => "self or this".to_string(),
103        Rule::timeline_symbol => "identifier".to_string(),
104        Rule::gradient_inline_value => "@gradient block".to_string(),
105        Rule::gradient_body => "gradient body".to_string(),
106        Rule::gradient_shape_block => "gradient shape block".to_string(),
107            Rule::gradient_shape_key => "linear, radial, or mesh".to_string(),
108        Rule::gradient_shape_settings => "gradient shape settings".to_string(),
109        Rule::gradient_shape_setting => "gradient shape setting".to_string(),
110        Rule::gradient_shape_setting_value => "gradient shape setting value".to_string(),
111        Rule::gradient_stop => "gradient stop".to_string(),
112        Rule::gradient_stop_marker => "gradient stop marker".to_string(),
113        Rule::gradient_stop_value => "gradient stop color".to_string(),
114        Rule::literal_function => "function name".to_string(),
115        Rule::silent_comma => ",".to_string(),
116        Rule::function_list => "function list (e.g. [handle_click, handle_click_again] )".to_string(),
117        Rule::literal_value => "literal value".to_string(),
118        Rule::literal_boolean => "boolean".to_string(),
119        Rule::literal_number_with_unit => "number with unit (e.g. 10px, 10%, 10deg, 10rad, 10ms, 1s, 5f )".to_string(),
120        Rule::literal_number => "number".to_string(),
121        Rule::literal_number_integer => "integer".to_string(),
122        Rule::literal_number_float => "float".to_string(),
123        Rule::literal_number_unit => "unit (px, %, rad, deg, ms, s, f)".to_string(),
124        Rule::literal_tuple => "tuple".to_string(),
125        Rule::literal_tuple_access => "tuple access".to_string(),
126        Rule::literal_enum_value => "enum".to_string(),
127        Rule::literal_enum_args_list => "enum args list".to_string(),
128        Rule::literal_color => "color space function (e.g. rgb(255,255,255) ), color constant (e.g. SLATE )".to_string(),
129        Rule::literal_color_space_func => "color space function (e.g. rgb(), hsl(), rgba(), hsla())".to_string(),
130        Rule::literal_color_channel => "integers 0-255, 0-100%, or arbitrary numeric deg/rad ".to_string(),
131        Rule::xo_color_space_func => "color space function (e.g. rgb(), hsl(), rgba(), hsla())".to_string(),
132        Rule::literal_color_const => "color constant (see docs.pax.dev/colors)".to_string(),
133        Rule::expression_body => "expression".to_string(),
134        Rule::expression_ternary => "ternary expression".to_string(),
135        Rule::expression_coalesce => "null coalescing expression".to_string(),
136        Rule::expression_binary => "binary expression".to_string(),
137        Rule::expression_wrapped => "{ expression }".to_string(),
138        Rule::expression_grouped => "( expression )".to_string(),
139        Rule::expression_grouped_unit => "unit suffix for grouped expression".to_string(),
140        Rule::xo_primary => "(expression), color space function, enum, function call, range, tuple, list, literal, identifier".to_string(),
141        Rule::xo_prefix => "- , !".to_string(),
142        Rule::xo_neg => "-".to_string(),
143        Rule::xo_bool_not => "!".to_string(),
144        Rule::xo_infix => "+, -, *, /, %%, ^, ==, !=, <, <=, >, >=, &&, ||, ??, ?, :".to_string(),
145        Rule::xo_add => "+".to_string(),
146        Rule::xo_bool_and => "&&".to_string(),
147        Rule::xo_bool_or => "||".to_string(),
148        Rule::xo_div => "/".to_string(),
149        Rule::xo_exp => "^".to_string(),
150        Rule::xo_mod => "%%".to_string(),
151        Rule::xo_mul => "*".to_string(),
152        Rule::xo_rel_eq => "==".to_string(),
153        Rule::xo_rel_gt => ">".to_string(),
154        Rule::xo_rel_gte => ">=".to_string(),
155        Rule::xo_rel_lt => "<".to_string(),
156        Rule::xo_rel_lte => "<=".to_string(),
157        Rule::xo_rel_neq => "!=".to_string(),
158        Rule::xo_sub => "-".to_string(),
159        Rule::xo_null_coalesce => "??".to_string(),
160        Rule::xo_tern_then => "?".to_string(),
161        Rule::xo_tern_else => ":".to_string(),
162        Rule::xo_range => "range (e.g. 0..5 or i..j)".to_string(),
163        Rule::xo_range_exclusive => "..".to_string(),
164        Rule::xo_literal => "literal value".to_string(),
165        Rule::xo_object => "literal object".to_string(),
166        Rule::xo_object_settings_key_value_pair => "setting key-value pair".to_string(),
167        Rule::xo_symbol => "identifier".to_string(),
168        Rule::xo_tuple => "tuple (e.g. (1,2) )".to_string(),
169        Rule::xo_list => "list (e.g. [1,2] )".to_string(),
170        Rule::xo_enum_or_function_call => "enum, function call".to_string(),
171        Rule::xo_enum_or_function_args_list => "args list".to_string(),
172        Rule::statement_control_flow => "if, for, slot".to_string(),
173        Rule::statement_if => "if".to_string(),
174        Rule::statement_if_branch => "if branch".to_string(),
175        Rule::statement_else_if_branch => "else if branch".to_string(),
176        Rule::statement_else_branch => "else branch".to_string(),
177        Rule::statement_for => "for".to_string(),
178        Rule::statement_slot => "slot".to_string(),
179        Rule::statement_for_predicate_declaration => "for predicate (e.g. i, (elem,i) )".to_string(),
180        Rule::statement_for_source => "for source (e.g. 0..5 )".to_string(),
181        Rule::statement_for_key => "for key expression (e.g. key item.id )".to_string(),
182        Rule::literal_list => "list".to_string(),
183        Rule::literal_option => "option".to_string(),
184        Rule::literal_some => "Some".to_string(),
185        Rule::literal_none => "None".to_string(),
186        Rule::literal_list_access => "list access".to_string(),
187    }
188}
189
190/// Parse a string against a single Pax grammar rule, returning a human-readable error string.
191#[cfg(feature = "parser")]
192pub fn parse_pax_str(expected_rule: Rule, input: &str) -> Result<Pair<'_, Rule>, String> {
193    let pairs = PaxParser::parse(expected_rule, input);
194    match pairs {
195        Ok(mut pairs) => {
196            let pair = pairs.next().unwrap();
197            Ok(pair)
198        }
199        Err(err) => {
200            let named_error = err.renamed_rules(renamed_rules);
201            Err(format!("{named_error}"))
202        }
203    }
204}
205
206/// Parse a string against a Pax grammar rule, preserving the structured pest error.
207#[cfg(feature = "parser")]
208pub fn parse_pax_err(expected_rule: Rule, input: &str) -> Result<Pair<'_, Rule>, Error<Rule>> {
209    let pairs = PaxParser::parse(expected_rule, input);
210    match pairs {
211        Ok(mut pairs) => {
212            let pair = pairs.next().unwrap();
213            Ok(pair)
214        }
215        Err(err) => {
216            let named_error = err.renamed_rules(renamed_rules);
217            Err(named_error)
218        }
219    }
220}
221
222/// Parse a string into pest pairs for a Pax grammar rule.
223#[cfg(feature = "parser")]
224pub fn parse_pax_pairs(expected_rule: Rule, input: &str) -> Result<Pairs<'_, Rule>, Error<Rule>> {
225    let pairs = PaxParser::parse(expected_rule, input);
226    match pairs {
227        Ok(pairs) => Ok(pairs),
228        Err(err) => {
229            let named_error = err.renamed_rules(renamed_rules);
230            Err(named_error)
231        }
232    }
233}