cedar_policy_core/parser/
cst_to_ast.rs

1/*
2 * Copyright 2022-2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      https://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17//! Conversions from CST to AST
18//!
19//! This module contains functions to convert ASTNodes containing CST items into
20//! AST items. It works with the parser CST output, where all nodes are optional.
21//!
22//! An important aspect of the transformation is to provide as many errors as
23//! possible to expedite development cycles. To the purpose, an error parameter
24//! must be passed to each function, to collect the potentially multiple errors.
25//! `Option::None` is used to signify that errors were present, and any new
26//! messages will be appended to the error parameter. Messages are not added when
27//! they are assumed to have already been added, like when a sub-conversion fails
28//! or the CST node was `None`, signifying a parse failure with associated message.
29
30// Throughout this module parameters to functions are references to CSTs or
31// owned AST items. This allows the most flexibility and least copying of data.
32// CSTs are almost entirely rewritten to ASTs, so we keep those values intact
33// and only clone the identifiers inside. ASTs here are temporary values until
34// the data passes out of the module, so we deconstruct them freely in the few
35// cases where there is a secondary conversion. This prevents any further
36// cloning.
37
38use super::err::ParseError;
39use super::node::{ASTNode, SourceInfo};
40use super::unescape::{to_pattern, to_unescaped_string};
41use super::{cst, err};
42use crate::ast::{
43    self, ActionConstraint, CallStyle, EntityReference, EntityType, EntityUID, PatternElem,
44    PolicySetError, PrincipalConstraint, PrincipalOrResourceConstraint, ResourceConstraint,
45};
46use itertools::Either;
47use smol_str::SmolStr;
48use std::cmp::Ordering;
49use std::collections::{BTreeMap, HashSet};
50use std::mem;
51use std::sync::Arc;
52
53// shortcut for error parameter
54type Errs<'a> = &'a mut Vec<err::ParseError>;
55
56// for storing extension function names per callstyle
57struct ExtStyles<'a> {
58    functions: HashSet<&'a ast::Name>,
59    methods: HashSet<&'a str>,
60}
61
62// Store extension function call styles
63lazy_static::lazy_static! {
64    static ref EXTENSION_STYLES: ExtStyles<'static> = load_styles();
65}
66fn load_styles() -> ExtStyles<'static> {
67    let mut functions = HashSet::new();
68    let mut methods = HashSet::new();
69    for func in crate::extensions::Extensions::all_available().all_funcs() {
70        match func.style() {
71            CallStyle::FunctionStyle => functions.insert(func.name()),
72            CallStyle::MethodStyle => methods.insert(func.name().basename().as_ref()),
73        };
74    }
75    ExtStyles { functions, methods }
76}
77
78impl ASTNode<Option<cst::Policies>> {
79    /// Iterate over the `Policy` nodes in this `cst::Policies`, with
80    /// corresponding generated `PolicyID`s
81    pub fn with_generated_policyids(
82        &self,
83    ) -> Option<impl Iterator<Item = (ast::PolicyID, &ASTNode<Option<cst::Policy>>)>> {
84        let maybe_policies = self.as_inner();
85        // return right away if there's no data, parse provided error
86        let policies = maybe_policies?;
87
88        Some(
89            policies
90                .0
91                .iter()
92                .enumerate()
93                .map(|(count, node)| (ast::PolicyID::from_string(format!("policy{count}")), node)),
94        )
95    }
96
97    /// convert `cst::Policies` to `ast::PolicySet`
98    pub fn to_policyset(&self, errs: Errs<'_>) -> Option<ast::PolicySet> {
99        let mut pset = ast::PolicySet::new();
100        let mut complete_set = true;
101        for (policy_id, policy) in self.with_generated_policyids()? {
102            // policy may have convert error
103            match policy.to_policy_or_template(policy_id, errs) {
104                Some(Either::Right(template)) => {
105                    if let Err(e) = pset.add_template(template) {
106                        match e {
107                            PolicySetError::Occupied => errs.push(ParseError::ToAST(
108                                "A template with this ID already exists within the policy set"
109                                    .to_string(),
110                            )),
111                        };
112
113                        complete_set = false
114                    }
115                }
116                Some(Either::Left(inline_policy)) => {
117                    if let Err(e) = pset.add_static(inline_policy) {
118                        match e {
119                            PolicySetError::Occupied => errs.push(ParseError::ToAST(
120                                "A policy with this ID already exists within the policy set"
121                                    .to_string(),
122                            )),
123                        };
124
125                        complete_set = false
126                    }
127                }
128                None => complete_set = false,
129            };
130        }
131
132        // fail on any error
133        if complete_set {
134            Some(pset)
135        } else {
136            None
137        }
138    }
139}
140
141impl ASTNode<Option<cst::Policy>> {
142    /// Convert `cst::Policy` to an AST `InlinePolicy` or `Template`
143    pub fn to_policy_or_template(
144        &self,
145        id: ast::PolicyID,
146        errs: Errs<'_>,
147    ) -> Option<Either<ast::StaticPolicy, ast::Template>> {
148        let t = self.to_policy_template(id, errs)?;
149        if t.slots().count() == 0 {
150            // This should always succeed if the slot count is zero
151            ast::StaticPolicy::try_from(t).ok().map(Either::Left)
152        } else {
153            Some(Either::Right(t))
154        }
155    }
156
157    /// Convert `cst::Policy` to an AST `InlinePolicy`. (Will fail if the CST is for a template)
158    pub fn to_policy(&self, id: ast::PolicyID, errs: Errs<'_>) -> Option<ast::StaticPolicy> {
159        let tp = self.to_policy_template(id, errs)?;
160        match ast::StaticPolicy::try_from(tp) {
161            Ok(p) => Some(p),
162            Err(e) => {
163                errs.push(err::ParseError::ToAST(format!("{e}")));
164                None
165            }
166        }
167    }
168
169    /// Convert `cst::Policy` to `ast::Template`. Works for inline policies as
170    /// well, which will become templates with 0 slots
171    pub fn to_policy_template(&self, id: ast::PolicyID, errs: Errs<'_>) -> Option<ast::Template> {
172        let (src, maybe_policy) = self.as_inner_pair();
173        // return right away if there's no data, parse provided error
174        let policy = maybe_policy?;
175
176        let mut failure = false;
177
178        // convert effect
179        let maybe_effect = policy.effect.to_effect(errs);
180
181        // convert annotatons
182        let annotations: BTreeMap<_, _> = policy
183            .annotations
184            .iter()
185            .filter_map(|a| a.to_kv_pair(errs))
186            .collect();
187        if annotations.len() != policy.annotations.len() {
188            failure = true;
189            errs.push(err::ParseError::ToAST(
190                "This policy uses poorly formed or duplicate annotations".to_string(),
191            ));
192        }
193
194        // convert head
195        let (maybe_principal, maybe_action, maybe_resource) = policy.extract_head(errs);
196
197        // convert conditions
198        let conds: Vec<_> = policy
199            .conds
200            .iter()
201            .filter_map(|c| c.to_expr(errs))
202            .collect();
203        if conds.len() != policy.conds.len() {
204            failure = true
205        }
206
207        // all data and errors are generated, so fail or construct result
208        if failure || !errs.is_empty() {
209            return None;
210        };
211        let effect = maybe_effect?;
212        let principal = maybe_principal?;
213        let action = maybe_action?;
214        let resource = maybe_resource?;
215
216        Some(construct_template_policy(
217            id,
218            annotations,
219            effect,
220            principal,
221            action,
222            resource,
223            conds,
224            src.clone(),
225        ))
226    }
227}
228
229impl cst::Policy {
230    /// get the head constraints from the `cst::Policy`
231    pub fn extract_head(
232        &self,
233        errs: Errs<'_>,
234    ) -> (
235        Option<PrincipalConstraint>,
236        Option<ActionConstraint>,
237        Option<ResourceConstraint>,
238    ) {
239        let mut vars = self.variables.iter();
240        let principal = if let Some(head1) = vars.next() {
241            head1.to_principal_constraint(errs)
242        } else {
243            errs.push(err::ParseError::ToAST(
244                "This policy requires the `principal` variable in the head".to_string(),
245            ));
246            None
247        };
248        let action = if let Some(head2) = vars.next() {
249            head2.to_action_constraint(errs)
250        } else {
251            errs.push(err::ParseError::ToAST(
252                "This policy requires the `action` variable in the head".to_string(),
253            ));
254            None
255        };
256        let resource = if let Some(head3) = vars.next() {
257            head3.to_resource_constraint(errs)
258        } else {
259            errs.push(err::ParseError::ToAST(
260                "This policy requires the `resource` variable in the head".to_string(),
261            ));
262            None
263        };
264        if vars.next().is_some() {
265            errs.push(err::ParseError::ToAST(
266                "This policy has extra variables in the head".to_string(),
267            ));
268        }
269        (principal, action, resource)
270    }
271}
272
273impl ASTNode<Option<cst::Annotation>> {
274    /// Get the (k, v) pair for the annotation. Critically, this checks validity
275    /// for the strings and does unescaping
276    pub fn to_kv_pair(&self, errs: Errs<'_>) -> Option<(ast::Id, SmolStr)> {
277        let maybe_anno = self.as_inner();
278        // return right away if there's no data, parse provided error
279        let anno = maybe_anno?;
280
281        let maybe_key = anno.key.to_valid_ident(errs);
282        let maybe_value = anno.value.as_valid_string(errs);
283        let maybe_value = match maybe_value.map(|s| to_unescaped_string(s)).transpose() {
284            Ok(maybe_value) => maybe_value,
285            Err(unescape_errs) => {
286                errs.extend(
287                    unescape_errs
288                        .into_iter()
289                        .map(|e| ParseError::ToAST(e.to_string())),
290                );
291                None
292            }
293        };
294
295        match (maybe_key, maybe_value) {
296            (Some(k), Some(v)) => Some((k, v)),
297            _ => None,
298        }
299    }
300}
301
302impl ASTNode<Option<cst::Ident>> {
303    /// Convert `cst::Ident` to `ast::Id`. Fails for reserved or invalid identifiers
304    pub fn to_valid_ident(&self, errs: Errs<'_>) -> Option<ast::Id> {
305        let maybe_ident = self.as_inner();
306        // return right away if there's no data, parse provided error
307        let ident = maybe_ident?;
308
309        match ident {
310            cst::Ident::If
311            | cst::Ident::True
312            | cst::Ident::False
313            | cst::Ident::Then
314            | cst::Ident::Else
315            | cst::Ident::In
316            | cst::Ident::Has
317            | cst::Ident::Like => {
318                errs.push(err::ParseError::ToAST(format!(
319                    "This identifier is reserved and cannot be used: {ident}"
320                )));
321                None
322            }
323            cst::Ident::Invalid(i) => {
324                errs.push(err::ParseError::ToAST(format!(
325                    "not a valid identifier: {i}"
326                )));
327                None
328            }
329            _ => Some(construct_id(format!("{ident}"))),
330        }
331    }
332
333    /// effect
334    pub(crate) fn to_effect(&self, errs: Errs<'_>) -> Option<ast::Effect> {
335        let maybe_effect = self.as_inner();
336        // return right away if there's no data, parse provided error
337        let effect = maybe_effect?;
338
339        match effect {
340            cst::Ident::Permit => Some(ast::Effect::Permit),
341            cst::Ident::Forbid => Some(ast::Effect::Forbid),
342            _ => {
343                errs.push(err::ParseError::ToAST(format!(
344                    "not a valid policy effect: {effect}"
345                )));
346                None
347            }
348        }
349    }
350    pub(crate) fn to_cond_is_when(&self, errs: Errs<'_>) -> Option<bool> {
351        let maybe_cond = self.as_inner();
352        // return right away if there's no data, parse provided error
353        let cond = maybe_cond?;
354
355        match cond {
356            cst::Ident::When => Some(true),
357            cst::Ident::Unless => Some(false),
358            _ => {
359                errs.push(err::ParseError::ToAST(format!(
360                    "not a valid policy condition: {cond}"
361                )));
362                None
363            }
364        }
365    }
366
367    fn to_var(&self, errs: Errs<'_>) -> Option<ast::Var> {
368        let maybe_ident = self.as_inner();
369        match maybe_ident {
370            Some(cst::Ident::Principal) => Some(ast::Var::Principal),
371            Some(cst::Ident::Action) => Some(ast::Var::Action),
372            Some(cst::Ident::Resource) => Some(ast::Var::Resource),
373            Some(ident) => {
374                errs.push(err::ParseError::ToAST(format!(
375                    "expected an identifier, got {ident}"
376                )));
377                None
378            }
379            None => {
380                errs.push(err::ParseError::ToAST("expected an identifier".to_string()));
381                None
382            }
383        }
384    }
385}
386
387impl ast::Id {
388    fn to_meth(
389        &self,
390        e: ast::Expr,
391        mut args: Vec<ast::Expr>,
392        errs: Errs<'_>,
393        l: SourceInfo,
394    ) -> Option<ast::Expr> {
395        let mut adj_args = args.iter_mut().peekable();
396        match (self.as_ref(), adj_args.next(), adj_args.peek()) {
397            ("contains", Some(a), None) => {
398                // move the value out of the argument, replacing it with a dummy,
399                // after this we can no longer use the original args
400                let arg = mem::replace(a, ast::Expr::val(false));
401                Some(construct_method_contains(e, arg, l))
402            }
403            ("containsAll", Some(a), None) => {
404                let arg = mem::replace(a, ast::Expr::val(false));
405                Some(construct_method_contains_all(e, arg, l))
406            }
407            ("containsAny", Some(a), None) => {
408                let arg = mem::replace(a, ast::Expr::val(false));
409                Some(construct_method_contains_any(e, arg, l))
410            }
411            (name, _, _) => {
412                if EXTENSION_STYLES.methods.contains(&name) {
413                    args.insert(0, e);
414                    // INVARIANT (MethodStyleArgs), we call insert above, so args is non-empty
415                    Some(construct_ext_meth(name.to_string(), args, l))
416                } else {
417                    errs.push(err::ParseError::ToAST(format!(
418                        "expected method name, found {}",
419                        name
420                    )));
421                    None
422                }
423            }
424        }
425    }
426}
427
428#[derive(Debug)]
429enum PrincipalOrResource {
430    Principal(PrincipalConstraint),
431    Resource(ResourceConstraint),
432}
433
434impl ASTNode<Option<cst::VariableDef>> {
435    fn to_principal_constraint(&self, errs: Errs<'_>) -> Option<PrincipalConstraint> {
436        match self.to_principal_or_resource_constraint(errs)? {
437            PrincipalOrResource::Principal(p) => Some(p),
438            PrincipalOrResource::Resource(_) => {
439                errs.push(err::ParseError::ToAST(
440                    "expected principal constraint, found resource constraint".to_string(),
441                ));
442                None
443            }
444        }
445    }
446
447    fn to_resource_constraint(&self, errs: Errs<'_>) -> Option<ResourceConstraint> {
448        match self.to_principal_or_resource_constraint(errs)? {
449            PrincipalOrResource::Principal(_) => {
450                errs.push(err::ParseError::ToAST(
451                    "expected resource constraint, found principal constraint".to_string(),
452                ));
453                None
454            }
455            PrincipalOrResource::Resource(r) => Some(r),
456        }
457    }
458
459    fn to_principal_or_resource_constraint(&self, errs: Errs<'_>) -> Option<PrincipalOrResource> {
460        let maybe_vardef = self.as_inner();
461        // return right away if there's no data, parse provided error
462        let vardef = maybe_vardef?;
463
464        let var = vardef.variable.to_var(errs)?;
465
466        match vardef.variable.to_var(errs) {
467            Some(v) if v == var => Some(()),
468            Some(other) => {
469                errs.push(err::ParseError::ToAST(format!(
470                    "expected {var} found {other}"
471                )));
472                None
473            }
474            None => None,
475        }?;
476
477        if let Some(typename) = vardef.name.as_ref() {
478            typename.to_type_constraint(errs)?;
479        }
480
481        let c = if let Some((op, rel_expr)) = &vardef.ineq {
482            let eref = rel_expr.to_ref_or_slot(errs, var)?;
483            match op {
484                cst::RelOp::Eq => Some(PrincipalOrResourceConstraint::Eq(eref)),
485                cst::RelOp::In => Some(PrincipalOrResourceConstraint::In(eref)),
486                _ => {
487                    errs.push(err::ParseError::ToAST(
488                        "policy head constraints must be `in` or `==`".to_string(),
489                    ));
490                    None
491                }
492            }
493        } else {
494            Some(PrincipalOrResourceConstraint::Any)
495        }?;
496        match var {
497            ast::Var::Principal => {
498                Some(PrincipalOrResource::Principal(PrincipalConstraint::new(c)))
499            }
500            ast::Var::Action => {
501                errs.push(err::ParseError::ToAST("unexpected `action`".to_string()));
502                None
503            }
504            ast::Var::Resource => Some(PrincipalOrResource::Resource(ResourceConstraint::new(c))),
505            ast::Var::Context => {
506                errs.push(err::ParseError::ToAST("unexpected `context`".to_string()));
507                None
508            }
509        }
510    }
511
512    fn to_action_constraint(&self, errs: Errs<'_>) -> Option<ast::ActionConstraint> {
513        let maybe_vardef = self.as_inner();
514        let vardef = maybe_vardef?;
515
516        match vardef.variable.to_var(errs) {
517            Some(ast::Var::Action) => Some(()),
518            Some(other) => {
519                errs.push(err::ParseError::ToAST(format!(
520                    "expected {}, found {other}",
521                    ast::Var::Action
522                )));
523                None
524            }
525            None => None,
526        }?;
527
528        if let Some(typename) = vardef.name.as_ref() {
529            typename.to_type_constraint(errs)?;
530        }
531
532        let action_constraint = if let Some((op, rel_expr)) = &vardef.ineq {
533            let refs = rel_expr.to_refs(errs, ast::Var::Action)?;
534            match (op, refs) {
535                (cst::RelOp::In, OneOrMultipleRefs::Multiple(euids)) => {
536                    Some(ActionConstraint::is_in(euids))
537                }
538                (cst::RelOp::In, OneOrMultipleRefs::Single(euid)) => {
539                    Some(ActionConstraint::is_in([euid]))
540                }
541                (cst::RelOp::Eq, OneOrMultipleRefs::Single(euid)) => {
542                    Some(ActionConstraint::is_eq(euid))
543                }
544                (cst::RelOp::Eq, OneOrMultipleRefs::Multiple(_)) => {
545                    errs.push(err::ParseError::ToAST(
546                        "constraints for `==` must be a single literal euid".to_string(),
547                    ));
548                    None
549                }
550                _ => {
551                    errs.push(err::ParseError::ToAST(
552                        "policy head constraints must be `in` or `==`".to_string(),
553                    ));
554                    None
555                }
556            }
557        } else {
558            Some(ActionConstraint::Any)
559        }?;
560
561        match action_constraint_contains_only_action_types(action_constraint) {
562            Ok(a) => Some(a),
563            Err(mut id_errs) => {
564                errs.append(&mut id_errs);
565                None
566            }
567        }
568    }
569}
570
571fn action_type_error_msg(euid: &EntityUID) -> ParseError {
572    let msg = format!("Expected an EntityUID with the type `Action`. Got: {euid}");
573    ParseError::ToCST(msg)
574}
575
576/// Check that all of the EUIDs in an action constraint have the type `Action`, under an arbitrary namespace
577fn action_constraint_contains_only_action_types(
578    a: ActionConstraint,
579) -> Result<ActionConstraint, Vec<ParseError>> {
580    match a {
581        ActionConstraint::Any => Ok(a),
582        ActionConstraint::In(ref euids) => {
583            let non_actions = euids
584                .iter()
585                .filter(|euid| !euid_has_action_type(euid))
586                .collect::<Vec<_>>();
587            if non_actions.is_empty() {
588                Ok(a)
589            } else {
590                Err(non_actions
591                    .into_iter()
592                    .map(|euid| action_type_error_msg(euid.as_ref()))
593                    .collect())
594            }
595        }
596        ActionConstraint::Eq(ref euid) => {
597            if euid_has_action_type(euid) {
598                Ok(a)
599            } else {
600                Err(vec![action_type_error_msg(euid)])
601            }
602        }
603    }
604}
605
606/// Check if an EUID has the type `Action` under an arbitrary namespace
607fn euid_has_action_type(euid: &EntityUID) -> bool {
608    if let EntityType::Concrete(name) = euid.entity_type() {
609        name.id.as_ref() == "Action"
610    } else {
611        false
612    }
613}
614
615impl ASTNode<Option<cst::Cond>> {
616    /// to expr
617    fn to_expr(&self, errs: Errs<'_>) -> Option<ast::Expr> {
618        let (src, maybe_cond) = self.as_inner_pair();
619        // return right away if there's no data, parse provided error
620        let cond = maybe_cond?;
621
622        let maybe_is_when = cond.cond.to_cond_is_when(errs);
623
624        let maybe_expr = match &cond.expr {
625            Some(expr) => expr.to_expr(errs),
626            None => {
627                errs.push(err::ParseError::ToAST(match cond.cond.as_ref().node {
628                    Some(ident) => format!("{} clause should not be empty", &ident),
629                    None => "bad use of {}".to_string(), // neither a keyword like `when`, nor a body
630                }));
631                None
632            }
633        };
634
635        match (maybe_is_when, maybe_expr) {
636            (Some(true), Some(e)) => Some(e),
637            (Some(false), Some(e)) => Some(construct_expr_not(e, src.clone())),
638            _ => None,
639        }
640    }
641}
642
643impl ASTNode<Option<cst::Str>> {
644    pub(crate) fn as_valid_string(&self, errs: Errs<'_>) -> Option<&SmolStr> {
645        let id = self.as_inner();
646        // return right away if there's no data, parse provided error
647        let id = id?;
648
649        match id {
650            cst::Str::String(s) => Some(s),
651            // at time of comment, all strings are valid
652            cst::Str::Invalid(s) => {
653                errs.push(err::ParseError::ToAST(format!(
654                    "this is an invalid string: {s}"
655                )));
656                None
657            }
658        }
659    }
660}
661
662/// Result type of conversion when we expect an Expr, Var, Name, or String.
663///
664/// During conversion it is useful to keep track of expression that may be used
665/// as function names, record names, or record attributes. This prevents parsing these
666/// terms to a general Expr expression and then immediately unwrapping them.
667pub(crate) enum ExprOrSpecial<'a> {
668    /// Any expression except a variable, name, or string literal
669    Expr(ast::Expr),
670    /// Variables, which act as expressions or names
671    Var(ast::Var, SourceInfo),
672    /// Name that isn't an expr and couldn't be converted to var
673    Name(ast::Name),
674    /// String literal, not yet unescaped
675    /// Must be processed with to_unescaped_string or to_pattern before inclusion in the AST
676    StrLit(&'a SmolStr, SourceInfo),
677}
678
679impl ExprOrSpecial<'_> {
680    fn into_expr(self, errs: Errs<'_>) -> Option<ast::Expr> {
681        match self {
682            Self::Expr(e) => Some(e),
683            Self::Var(v, l) => Some(construct_expr_var(v, l)),
684            Self::Name(n) => {
685                errs.push(err::ParseError::ToAST(format!(
686                    "Arbitrary variables are not supported; did you mean to enclose {n} in quotes to make a string?",
687                )));
688                None
689            }
690            Self::StrLit(s, l) => match to_unescaped_string(s) {
691                Ok(s) => Some(construct_expr_string(s, l)),
692                Err(escape_errs) => {
693                    errs.extend(
694                        escape_errs
695                            .into_iter()
696                            .map(|e| ParseError::ToAST(e.to_string())),
697                    );
698                    None
699                }
700            },
701        }
702    }
703
704    /// Variables, names (with no prefixes), and string literals can all be used as record attributes
705    pub(crate) fn into_valid_attr(self, errs: Errs<'_>) -> Option<SmolStr> {
706        match self {
707            Self::Var(var, _) => Some(construct_string_from_var(var)),
708            Self::Name(name) => name.into_valid_attr(errs),
709            Self::StrLit(s, _) => match to_unescaped_string(s) {
710                Ok(s) => Some(s),
711                Err(escape_errs) => {
712                    errs.extend(
713                        escape_errs
714                            .into_iter()
715                            .map(|e| ParseError::ToAST(e.to_string())),
716                    );
717                    None
718                }
719            },
720            Self::Expr(e) => {
721                errs.push(err::ParseError::ToAST(format!("not a valid string: {e}")));
722                None
723            }
724        }
725    }
726
727    fn into_pattern(self, errs: Errs<'_>) -> Option<Vec<PatternElem>> {
728        match self {
729            Self::StrLit(s, _) => match to_pattern(s) {
730                Ok(pat) => Some(pat),
731                Err(escape_errs) => {
732                    errs.extend(
733                        escape_errs
734                            .into_iter()
735                            .map(|e| ParseError::ToAST(e.to_string())),
736                    );
737                    None
738                }
739            },
740            Self::Var(var, _) => {
741                errs.push(err::ParseError::ToAST(format!(
742                    "not a string literal: {var}"
743                )));
744                None
745            }
746            Self::Name(name) => {
747                errs.push(err::ParseError::ToAST(format!(
748                    "not a string literal: {name}"
749                )));
750                None
751            }
752            Self::Expr(e) => {
753                errs.push(err::ParseError::ToAST(format!("not a string literal: {e}")));
754                None
755            }
756        }
757    }
758    /// to string literal
759    fn into_string_literal(self, errs: Errs<'_>) -> Option<SmolStr> {
760        match self {
761            Self::StrLit(s, _) => match to_unescaped_string(s) {
762                Ok(s) => Some(s),
763                Err(escape_errs) => {
764                    errs.extend(
765                        escape_errs
766                            .into_iter()
767                            .map(|e| ParseError::ToAST(e.to_string())),
768                    );
769                    None
770                }
771            },
772            Self::Var(var, _) => {
773                errs.push(err::ParseError::ToAST(format!(
774                    "not a string literal: {var}"
775                )));
776                None
777            }
778            Self::Name(name) => {
779                errs.push(err::ParseError::ToAST(format!(
780                    "not a string literal: {name}"
781                )));
782                None
783            }
784            Self::Expr(e) => {
785                errs.push(err::ParseError::ToAST(format!("not a string literal: {e}")));
786                None
787            }
788        }
789    }
790}
791
792impl ASTNode<Option<cst::Expr>> {
793    /// to ref
794    fn to_ref(&self, var: ast::Var, errs: Errs<'_>) -> Option<EntityUID> {
795        self.to_ref_or_refs::<SingleEntity>(errs, var).map(|x| x.0)
796    }
797
798    fn to_ref_or_slot(&self, errs: Errs<'_>, var: ast::Var) -> Option<EntityReference> {
799        self.to_ref_or_refs::<EntityReference>(errs, var)
800    }
801
802    fn to_refs(&self, errs: Errs<'_>, var: ast::Var) -> Option<OneOrMultipleRefs> {
803        self.to_ref_or_refs::<OneOrMultipleRefs>(errs, var)
804    }
805
806    fn to_ref_or_refs<T: RefKind>(&self, errs: Errs<'_>, var: ast::Var) -> Option<T> {
807        let maybe_expr = self.as_inner();
808        let expr = &*maybe_expr?.expr;
809        match expr {
810            cst::ExprData::Or(o) => o.to_ref_or_refs::<T>(errs, var),
811            cst::ExprData::If(_, _, _) => {
812                errs.push(err::ParseError::ToAST(format!(
813                    "expected {}, found an if statement",
814                    T::err_string()
815                )));
816                None
817            }
818        }
819    }
820
821    /// convert `cst::Expr` to `ast::Expr`
822    pub fn to_expr(&self, errs: Errs<'_>) -> Option<ast::Expr> {
823        self.to_expr_or_special(errs)?.into_expr(errs)
824    }
825    pub(crate) fn to_expr_or_special(&self, errs: Errs<'_>) -> Option<ExprOrSpecial<'_>> {
826        let (src, maybe_expr) = self.as_inner_pair();
827        // return right away if there's no data, parse provided error
828        let expr = &*maybe_expr?.expr;
829
830        match expr {
831            cst::ExprData::Or(or) => or.to_expr_or_special(errs),
832            cst::ExprData::If(i, t, e) => {
833                let maybe_guard = i.to_expr(errs);
834                let maybe_then = t.to_expr(errs);
835                let maybe_else = e.to_expr(errs);
836
837                match (maybe_guard, maybe_then, maybe_else) {
838                    (Some(i), Some(t), Some(e)) => {
839                        Some(ExprOrSpecial::Expr(construct_expr_if(i, t, e, src.clone())))
840                    }
841                    _ => None,
842                }
843            }
844        }
845    }
846}
847
848/// Type level marker for parsing sets of entity uids or single uids
849/// This presents having either a large level of code duplication
850/// or runtime data.
851trait RefKind: Sized {
852    fn err_string() -> &'static str;
853    fn create_single_ref(e: EntityUID, errs: Errs<'_>) -> Option<Self>;
854    fn create_multiple_refs(es: Vec<EntityUID>, errs: Errs<'_>) -> Option<Self>;
855    fn create_slot(errs: Errs<'_>) -> Option<Self>;
856}
857
858struct SingleEntity(pub EntityUID);
859
860impl RefKind for SingleEntity {
861    fn err_string() -> &'static str {
862        "entity uid"
863    }
864
865    fn create_single_ref(e: EntityUID, _errs: Errs<'_>) -> Option<Self> {
866        Some(SingleEntity(e))
867    }
868
869    fn create_multiple_refs(_es: Vec<EntityUID>, errs: Errs<'_>) -> Option<Self> {
870        errs.push(err::ParseError::ToAST(
871            "expected single entity uid, got a set of entity uids".to_string(),
872        ));
873        None
874    }
875
876    fn create_slot(errs: Errs<'_>) -> Option<Self> {
877        errs.push(err::ParseError::ToAST(
878            "expected a single entity uid, got a template slot".to_string(),
879        ));
880        None
881    }
882}
883
884impl RefKind for EntityReference {
885    fn err_string() -> &'static str {
886        "entity uid or template slot"
887    }
888
889    fn create_slot(_: Errs<'_>) -> Option<Self> {
890        Some(EntityReference::Slot)
891    }
892
893    fn create_single_ref(e: EntityUID, _errs: Errs<'_>) -> Option<Self> {
894        Some(EntityReference::euid(e))
895    }
896
897    fn create_multiple_refs(_es: Vec<EntityUID>, errs: Errs<'_>) -> Option<Self> {
898        errs.push(err::ParseError::ToAST(
899            "expected single entity uid or template slot, got a set of entity uids".to_string(),
900        ));
901        None
902    }
903}
904
905/// Simple utility enum for parsing lists/individual entityuids
906#[derive(Debug)]
907enum OneOrMultipleRefs {
908    Single(EntityUID),
909    Multiple(Vec<EntityUID>),
910}
911
912impl RefKind for OneOrMultipleRefs {
913    fn err_string() -> &'static str {
914        "entity uid, set of entity uids, or template slot"
915    }
916
917    fn create_slot(errs: Errs<'_>) -> Option<Self> {
918        errs.push(err::ParseError::ToAST("Unexpected slot".to_string()));
919        None
920    }
921
922    fn create_single_ref(e: EntityUID, _errs: Errs<'_>) -> Option<Self> {
923        Some(OneOrMultipleRefs::Single(e))
924    }
925
926    fn create_multiple_refs(es: Vec<EntityUID>, _errs: Errs<'_>) -> Option<Self> {
927        Some(OneOrMultipleRefs::Multiple(es))
928    }
929}
930
931impl ASTNode<Option<cst::Or>> {
932    fn to_expr_or_special(&self, errs: Errs<'_>) -> Option<ExprOrSpecial<'_>> {
933        let (src, maybe_or) = self.as_inner_pair();
934        // return right away if there's no data, parse provided error
935        let or = maybe_or?;
936
937        let maybe_first = or.initial.to_expr_or_special(errs);
938        let mut more = or.extended.iter().filter_map(|i| i.to_expr(errs));
939        // getting the second here avoids the possibility of a singleton construction
940        let maybe_second = more.next();
941        // collect() preforms all the conversions, generating any errors
942        let rest: Vec<_> = more.collect();
943
944        match (maybe_first, maybe_second, rest.len(), or.extended.len()) {
945            (f, None, _, 0) => f,
946            (Some(f), Some(s), r, e) if 1 + r == e => f
947                .into_expr(errs)
948                .map(|e| ExprOrSpecial::Expr(construct_expr_or(e, s, rest, src.clone()))),
949            _ => None,
950        }
951    }
952
953    fn to_ref_or_refs<T: RefKind>(&self, errs: Errs<'_>, var: ast::Var) -> Option<T> {
954        let maybe_or = self.as_inner();
955        let or = maybe_or?;
956        match or.extended.len() {
957            0 => or.initial.to_ref_or_refs::<T>(errs, var),
958            _n => {
959                errs.push(err::ParseError::ToAST(format!(
960                    "expected {}, found ||",
961                    T::err_string()
962                )));
963                None
964            }
965        }
966    }
967}
968
969impl ASTNode<Option<cst::And>> {
970    fn to_ref_or_refs<T: RefKind>(&self, errs: Errs<'_>, var: ast::Var) -> Option<T> {
971        let maybe_and = self.as_inner();
972        let and = maybe_and?;
973        match and.extended.len() {
974            0 => and.initial.to_ref_or_refs::<T>(errs, var),
975            _n => {
976                errs.push(err::ParseError::ToAST(format!(
977                    "expected {}, found &&",
978                    T::err_string()
979                )));
980                None
981            }
982        }
983    }
984
985    fn to_expr(&self, errs: Errs<'_>) -> Option<ast::Expr> {
986        self.to_expr_or_special(errs)?.into_expr(errs)
987    }
988    fn to_expr_or_special(&self, errs: Errs<'_>) -> Option<ExprOrSpecial<'_>> {
989        let (src, maybe_and) = self.as_inner_pair();
990        // return right away if there's no data, parse provided error
991        let and = maybe_and?;
992
993        let maybe_first = and.initial.to_expr_or_special(errs);
994        let mut more = and.extended.iter().filter_map(|i| i.to_expr(errs));
995        // getting the second here avoids the possibility of a singleton construction
996        let maybe_second = more.next();
997        // collect() preforms all the conversions, generating any errors
998        let rest: Vec<_> = more.collect();
999
1000        match (maybe_first, maybe_second, rest.len(), and.extended.len()) {
1001            (f, None, _, 0) => f,
1002            (Some(f), Some(s), r, e) if 1 + r == e => f
1003                .into_expr(errs)
1004                .map(|e| ExprOrSpecial::Expr(construct_expr_and(e, s, rest, src.clone()))),
1005            _ => None,
1006        }
1007    }
1008}
1009
1010impl ASTNode<Option<cst::Relation>> {
1011    fn to_ref_or_refs<T: RefKind>(&self, errs: Errs<'_>, var: ast::Var) -> Option<T> {
1012        let maybe_rel = self.as_inner();
1013        match maybe_rel? {
1014            cst::Relation::Common { initial, extended } => match extended.len() {
1015                0 => initial.to_ref_or_refs::<T>(errs, var),
1016                _n => {
1017                    errs.push(err::ParseError::ToAST(format!(
1018                        "expected {}, found binary operation",
1019                        T::err_string()
1020                    )));
1021                    None
1022                }
1023            },
1024            cst::Relation::Has { .. } => {
1025                errs.push(err::ParseError::ToAST(format!(
1026                    "expected {}, found `has` relation",
1027                    T::err_string()
1028                )));
1029                None
1030            }
1031            cst::Relation::Like { .. } => {
1032                errs.push(err::ParseError::ToAST(format!(
1033                    "expected {}, found `like` relation",
1034                    T::err_string()
1035                )));
1036                None
1037            }
1038        }
1039    }
1040
1041    fn to_expr(&self, errs: Errs<'_>) -> Option<ast::Expr> {
1042        self.to_expr_or_special(errs)?.into_expr(errs)
1043    }
1044    fn to_expr_or_special(&self, errs: Errs<'_>) -> Option<ExprOrSpecial<'_>> {
1045        let (src, maybe_rel) = self.as_inner_pair();
1046        // return right away if there's no data, parse provided error
1047        let rel = maybe_rel?;
1048
1049        match rel {
1050            cst::Relation::Common { initial, extended } => {
1051                let maybe_first = initial.to_expr_or_special(errs);
1052                let mut more = extended
1053                    .iter()
1054                    .filter_map(|(op, i)| i.to_expr(errs).map(|e| (op, e)));
1055                // getting the second here avoids the possibility of a singleton construction
1056                let maybe_second = more.next();
1057                // collect() preforms all the conversions, generating any errors
1058                let _rest: Vec<_> = more.collect();
1059
1060                match (maybe_first, maybe_second, extended.len()) {
1061                    (_, _, l) if l > 1 => {
1062                        errs.push(err::ParseError::ToAST(
1063                            "Multiple relational operators (>, ==, in, etc.) without parentheses"
1064                                .to_string(),
1065                        ));
1066                        None
1067                    }
1068                    // error reported and result filtered out
1069                    (_, None, 1) => None,
1070                    (f, None, 0) => f,
1071                    (Some(f), Some((op, s)), _) => f
1072                        .into_expr(errs)
1073                        .map(|e| ExprOrSpecial::Expr(construct_expr_rel(e, *op, s, src.clone()))),
1074                    _ => None,
1075                }
1076            }
1077            cst::Relation::Has { target, field } => {
1078                match (
1079                    target.to_expr(errs),
1080                    field.to_expr_or_special(errs)?.into_valid_attr(errs),
1081                ) {
1082                    (Some(t), Some(s)) => {
1083                        Some(ExprOrSpecial::Expr(construct_expr_has(t, s, src.clone())))
1084                    }
1085                    _ => None,
1086                }
1087            }
1088            cst::Relation::Like { target, pattern } => {
1089                match (
1090                    target.to_expr(errs),
1091                    pattern.to_expr_or_special(errs)?.into_pattern(errs),
1092                ) {
1093                    (Some(t), Some(s)) => {
1094                        Some(ExprOrSpecial::Expr(construct_expr_like(t, s, src.clone())))
1095                    }
1096                    _ => None,
1097                }
1098            }
1099        }
1100    }
1101}
1102
1103impl ASTNode<Option<cst::Add>> {
1104    fn to_ref_or_refs<T: RefKind>(&self, errs: Errs<'_>, var: ast::Var) -> Option<T> {
1105        let maybe_add = self.as_inner();
1106        let add = maybe_add?;
1107        match add.extended.len() {
1108            0 => add.initial.to_ref_or_refs::<T>(errs, var),
1109            _n => {
1110                errs.push(err::ParseError::ToAST(format!(
1111                    "expected {}, found arithmetic",
1112                    T::err_string()
1113                )));
1114                None
1115            }
1116        }
1117    }
1118
1119    fn to_expr(&self, errs: Errs<'_>) -> Option<ast::Expr> {
1120        self.to_expr_or_special(errs)?.into_expr(errs)
1121    }
1122    fn to_expr_or_special(&self, errs: Errs<'_>) -> Option<ExprOrSpecial<'_>> {
1123        let (src, maybe_add) = self.as_inner_pair();
1124        // return right away if there's no data, parse provided error
1125        let add = maybe_add?;
1126
1127        let maybe_first = add.initial.to_expr_or_special(errs);
1128        // collect() performs all the conversions, generating any errors
1129        let more: Vec<(cst::AddOp, _)> = add
1130            .extended
1131            .iter()
1132            .filter_map(|&(op, ref i)| i.to_expr(errs).map(|e| (op, e)))
1133            .collect();
1134        if !more.is_empty() {
1135            Some(ExprOrSpecial::Expr(construct_expr_add(
1136                maybe_first?.into_expr(errs)?,
1137                more,
1138                src.clone(),
1139            )))
1140        } else {
1141            maybe_first
1142        }
1143    }
1144}
1145
1146impl ASTNode<Option<cst::Mult>> {
1147    fn to_ref_or_refs<T: RefKind>(&self, errs: Errs<'_>, var: ast::Var) -> Option<T> {
1148        let maybe_mult = self.as_inner();
1149        let mult = maybe_mult?;
1150        match mult.extended.len() {
1151            0 => mult.initial.to_ref_or_refs::<T>(errs, var),
1152            _n => {
1153                errs.push(err::ParseError::ToAST(format!(
1154                    "expected {}, found arithmetic",
1155                    T::err_string()
1156                )));
1157                None
1158            }
1159        }
1160    }
1161
1162    fn to_expr(&self, errs: Errs<'_>) -> Option<ast::Expr> {
1163        self.to_expr_or_special(errs)?.into_expr(errs)
1164    }
1165    fn to_expr_or_special(&self, errs: Errs<'_>) -> Option<ExprOrSpecial<'_>> {
1166        let (src, maybe_mult) = self.as_inner_pair();
1167        // return right away if there's no data, parse provided error
1168        let mult = maybe_mult?;
1169
1170        let maybe_first = mult.initial.to_expr_or_special(errs);
1171        // collect() preforms all the conversions, generating any errors
1172        let more: Vec<(cst::MultOp, _)> = mult
1173            .extended
1174            .iter()
1175            .filter_map(|&(op, ref i)| i.to_expr(errs).map(|e| (op, e)))
1176            .collect();
1177
1178        if !more.is_empty() {
1179            let first = maybe_first?.into_expr(errs)?;
1180            // enforce that division and remainder/modulo are not supported
1181            for (op, _) in &more {
1182                match op {
1183                    cst::MultOp::Times => {}
1184                    cst::MultOp::Divide => {
1185                        errs.push(ParseError::ToAST("division is not supported".to_string()));
1186                        return None;
1187                    }
1188                    cst::MultOp::Mod => {
1189                        errs.push(ParseError::ToAST(
1190                            "remainder/modulo is not supported".to_string(),
1191                        ));
1192                        return None;
1193                    }
1194                }
1195            }
1196            // split all the operands into constantints and nonconstantints.
1197            // also, remove the opcodes -- from here on we assume they're all
1198            // `Times`, having checked above that this is the case
1199            let (constantints, nonconstantints): (Vec<ast::Expr>, Vec<ast::Expr>) =
1200                std::iter::once(first)
1201                    .chain(more.into_iter().map(|(_, e)| e))
1202                    .partition(|e| {
1203                        matches!(e.expr_kind(), ast::ExprKind::Lit(ast::Literal::Long(_)))
1204                    });
1205            let constantints = constantints
1206                .into_iter()
1207                .map(|e| match e.expr_kind() {
1208                    ast::ExprKind::Lit(ast::Literal::Long(i)) => *i,
1209                    _ => unreachable!(
1210                        "checked it matched ast::ExprKind::Lit(ast::Literal::Long(_)) above"
1211                    ),
1212                })
1213                .collect::<Vec<i64>>();
1214            if nonconstantints.len() > 1 {
1215                // at most one of the operands in `a * b * c * d * ...` can be a nonconstantint
1216                errs.push(err::ParseError::ToAST(
1217                    "Multiplication must be by a constant int".to_string(), // you could see this error for division by a nonconstant as well, but this error message seems like the appropriate one, it will be the common case
1218                ));
1219                None
1220            } else if nonconstantints.is_empty() {
1221                Some(ExprOrSpecial::Expr(construct_expr_mul(
1222                    construct_expr_num(constantints[0], src.clone()),
1223                    constantints[1..].iter().copied(),
1224                    src.clone(),
1225                )))
1226            } else {
1227                let nonconstantint: ast::Expr = nonconstantints
1228                    .into_iter()
1229                    .next()
1230                    .expect("already checked that it's not empty");
1231                Some(ExprOrSpecial::Expr(construct_expr_mul(
1232                    nonconstantint,
1233                    constantints,
1234                    src.clone(),
1235                )))
1236            }
1237        } else {
1238            maybe_first
1239        }
1240    }
1241}
1242
1243impl ASTNode<Option<cst::Unary>> {
1244    fn to_ref_or_refs<T: RefKind>(&self, errs: Errs<'_>, var: ast::Var) -> Option<T> {
1245        let maybe_unary = self.as_inner();
1246        let unary = maybe_unary?;
1247        match &unary.op {
1248            Some(_op) => {
1249                errs.push(err::ParseError::ToAST(
1250                    "expected entity uid found unary operation".to_string(),
1251                ));
1252                None
1253            }
1254            None => unary.item.to_ref_or_refs::<T>(errs, var),
1255        }
1256    }
1257
1258    fn to_expr(&self, errs: Errs<'_>) -> Option<ast::Expr> {
1259        self.to_expr_or_special(errs)?.into_expr(errs)
1260    }
1261    fn to_expr_or_special(&self, errs: Errs<'_>) -> Option<ExprOrSpecial<'_>> {
1262        let (src, maybe_unary) = self.as_inner_pair();
1263        // return right away if there's no data, parse provided error
1264        let unary = maybe_unary?;
1265
1266        // A thunk to delay the evaluation of `item`
1267        let mut maybe_item = || unary.item.to_expr_or_special(errs);
1268
1269        match unary.op {
1270            None => maybe_item(),
1271            Some(cst::NegOp::Bang(0)) => maybe_item(),
1272            Some(cst::NegOp::Dash(0)) => maybe_item(),
1273            Some(cst::NegOp::Bang(n)) => {
1274                let item = maybe_item().and_then(|i| i.into_expr(errs));
1275                if n % 2 == 0 {
1276                    item.map(|i| {
1277                        ExprOrSpecial::Expr(construct_expr_not(
1278                            construct_expr_not(i, src.clone()),
1279                            src.clone(),
1280                        ))
1281                    })
1282                } else {
1283                    // safe to collapse to !
1284                    item.map(|i| ExprOrSpecial::Expr(construct_expr_not(i, src.clone())))
1285                }
1286            }
1287            Some(cst::NegOp::Dash(c)) => {
1288                // Test if there is a negative numeric literal.
1289                // A negative numeric literal should match regex pattern
1290                // `-\d+` which is parsed into a `Unary(_, Member(Primary(Literal(Num(_))), []))`.
1291                // Given a successful match, the number of negation operations
1292                // decreases by one.
1293                let (last, rc) = if let Some(cst::Literal::Num(n)) = unary.item.to_lit() {
1294                    match n.cmp(&(i64::MAX as u64 + 1)) {
1295                        Ordering::Equal => (
1296                            Some(construct_expr_num(i64::MIN, unary.item.info.clone())),
1297                            c - 1,
1298                        ),
1299                        Ordering::Less => (
1300                            Some(construct_expr_num(-(*n as i64), unary.item.info.clone())),
1301                            c - 1,
1302                        ),
1303                        Ordering::Greater => {
1304                            errs.push(err::ParseError::ToAST(
1305                                "Integer constant is too large!".to_string(),
1306                            ));
1307                            (None, 0)
1308                        }
1309                    }
1310                } else {
1311                    // If the operand is not a CST literal, convert it into
1312                    // an expression.
1313                    (maybe_item().and_then(|i| i.into_expr(errs)), c)
1314                };
1315                // Fold the expression into a series of negation operations.
1316                (0..rc)
1317                    .fold(last, |r, _| r.map(|e| (construct_expr_neg(e, src.clone()))))
1318                    .map(ExprOrSpecial::Expr)
1319            }
1320            Some(cst::NegOp::OverBang) => {
1321                errs.push(err::ParseError::ToAST("Too many '!'s".to_string()));
1322                None
1323            }
1324            Some(cst::NegOp::OverDash) => {
1325                errs.push(err::ParseError::ToAST("Too many '-'s".to_string()));
1326                None
1327            }
1328        }
1329    }
1330}
1331
1332/// Temporary converted data, mirroring `cst::MemAccess`
1333enum AstAccessor {
1334    Field(ast::Id),
1335    Call(Vec<ast::Expr>),
1336    Index(SmolStr),
1337}
1338
1339impl ASTNode<Option<cst::Member>> {
1340    // Try to convert `cst::Member` into a `cst::Literal`, i.e.
1341    // match `Member(Primary(Literal(_), []))`.
1342    // It does not match the `Expr` arm of `Primary`, which means expressions
1343    // like `(1)` are not considered as literals on the CST level.
1344    fn to_lit(&self) -> Option<&cst::Literal> {
1345        let m = self.as_ref().node.as_ref()?;
1346        if !m.access.is_empty() {
1347            return None;
1348        }
1349        match m.item.as_ref().node.as_ref()? {
1350            cst::Primary::Literal(l) => l.as_ref().node.as_ref(),
1351            _ => None,
1352        }
1353    }
1354
1355    fn to_ref_or_refs<T: RefKind>(&self, errs: Errs<'_>, var: ast::Var) -> Option<T> {
1356        let maybe_mem = self.as_inner();
1357        let mem = maybe_mem?;
1358        match mem.access.len() {
1359            0 => mem.item.to_ref_or_refs::<T>(errs, var),
1360            _n => {
1361                errs.push(err::ParseError::ToAST(
1362                    "expected entity uid, found member access".to_string(),
1363                ));
1364                None
1365            }
1366        }
1367    }
1368
1369    fn to_expr_or_special(&self, errs: Errs<'_>) -> Option<ExprOrSpecial<'_>> {
1370        let (src, maybe_mem) = self.as_inner_pair();
1371        // return right away if there's no data, parse provided error
1372        let mem = maybe_mem?;
1373
1374        let maybe_prim = mem.item.to_expr_or_special(errs);
1375
1376        // collect() allows all conversions to run and generate errors
1377        let mut accessors: Vec<_> = mem.access.iter().map(|a| a.to_access(errs)).collect();
1378
1379        // we use `head` as our failure indicator going forward
1380        let mut head = maybe_prim;
1381        // we need at least three available items, for example:
1382        // var .call (args) -  which becomes one expr
1383        // so we use slice matching
1384        let mut tail = &mut accessors[..];
1385
1386        // Starting off with a failure and filtering items from the accessor list
1387        // can cause false error messages. We consider this acceptable for now because
1388        // they only occur along side a real error.
1389        // TODO: eliminate the false errors (likely with `Option`s inside `AstAccessor`)
1390        //
1391        // This algorithm is essentially an iterator over the accessor slice, but the
1392        // pattern match should be easier to read, since we have to check multiple elements
1393        // at once. We use `mem::replace` to "deconstruct" the slice as we go, filling it
1394        // with empty data and taking ownership of its contents.
1395        loop {
1396            use AstAccessor::*;
1397            use ExprOrSpecial::*;
1398            match (&mut head, tail) {
1399                // no accessors left - we're done
1400                (_, []) => break head,
1401                // failed method call (presumably) - ignore
1402                (_, [None, Some(Call(_)), rest @ ..]) => {
1403                    head = None;
1404                    tail = rest;
1405                }
1406                // failed access - ignore
1407                (_, [None, rest @ ..]) => {
1408                    head = None;
1409                    tail = rest;
1410                }
1411                // function call
1412                (Some(Name(n)), [Some(Call(a)), rest @ ..]) => {
1413                    // move the vec out of the slice, we won't use the slice after
1414                    let args = std::mem::take(a);
1415                    // replace the object `n` refers to with a default value since it won't be used afterwards
1416                    let nn =
1417                        mem::replace(n, ast::Name::unqualified_name(ast::Id::new_unchecked("")));
1418                    head = nn.into_func(args, errs, src.clone()).map(Expr);
1419                    tail = rest;
1420                }
1421                // variable call - error
1422                (Some(Var(_, _)), [Some(Call(_)), rest @ ..]) => {
1423                    errs.push(err::ParseError::ToAST(
1424                        "Variables cannot be used as functions".to_string(),
1425                    ));
1426                    head = None;
1427                    tail = rest;
1428                }
1429                // arbitrary call - error
1430                (_, [Some(Call(_)), rest @ ..]) => {
1431                    errs.push(err::ParseError::ToAST(
1432                        "All functions are named, this cannot be called".to_string(),
1433                    ));
1434                    head = None;
1435                    tail = rest;
1436                }
1437                // method call on failure - ignore
1438                (None, [Some(Field(_)), Some(Call(_)), rest @ ..]) => {
1439                    tail = rest;
1440                }
1441                // method call on name - error
1442                (Some(Name(_)), [Some(Field(_)), Some(Call(_)), rest @ ..]) => {
1443                    errs.push(err::ParseError::ToAST(
1444                        "This item does not have methods".to_string(),
1445                    ));
1446                    head = None;
1447                    tail = rest;
1448                }
1449                // method call on variable
1450                (Some(Var(v, vl)), [Some(Field(i)), Some(Call(a)), rest @ ..]) => {
1451                    // move var and args out of the slice
1452                    let var = mem::replace(v, ast::Var::Principal);
1453                    let args = std::mem::take(a);
1454                    // move the id out of the slice as well, to avoid cloning the internal string
1455                    let id = mem::replace(i, ast::Id::new_unchecked(""));
1456                    head = id
1457                        .to_meth(construct_expr_var(var, vl.clone()), args, errs, src.clone())
1458                        .map(Expr);
1459                    tail = rest;
1460                }
1461                // method call on arbitrary expression
1462                (Some(Expr(e)), [Some(Field(i)), Some(Call(a)), rest @ ..]) => {
1463                    // move the expr and args out of the slice
1464                    let args = std::mem::take(a);
1465                    let expr = mem::replace(e, ast::Expr::val(false));
1466                    // move the id out of the slice as well, to avoid cloning the internal string
1467                    let id = mem::replace(i, ast::Id::new_unchecked(""));
1468                    head = id.to_meth(expr, args, errs, src.clone()).map(Expr);
1469                    tail = rest;
1470                }
1471                // method call on string literal (same as Expr case)
1472                (Some(StrLit(s, sl)), [Some(Field(i)), Some(Call(a)), rest @ ..]) => {
1473                    let args = std::mem::take(a);
1474                    let id = mem::replace(i, ast::Id::new_unchecked(""));
1475                    let maybe_expr = match to_unescaped_string(s) {
1476                        Ok(s) => Some(construct_expr_string(s, sl.clone())),
1477                        Err(escape_errs) => {
1478                            errs.extend(
1479                                escape_errs
1480                                    .into_iter()
1481                                    .map(|e| ParseError::ToAST(e.to_string())),
1482                            );
1483                            None
1484                        }
1485                    };
1486                    head =
1487                        maybe_expr.and_then(|e| id.to_meth(e, args, errs, src.clone()).map(Expr));
1488                    tail = rest;
1489                }
1490                // access of failure - ignore
1491                (None, [Some(Field(_)) | Some(Index(_)), rest @ ..]) => {
1492                    tail = rest;
1493                }
1494                // access on arbitrary name - error
1495                (Some(Name(_)), [Some(Field(_)) | Some(Index(_)), rest @ ..]) => {
1496                    errs.push(err::ParseError::ToAST(
1497                        "This item is not a data structure".to_string(),
1498                    ));
1499                    head = None;
1500                    tail = rest;
1501                }
1502                // attribute of variable
1503                (Some(Var(v, vl)), [Some(Field(i)), rest @ ..]) => {
1504                    let var = mem::replace(v, ast::Var::Principal);
1505                    let id = mem::replace(i, ast::Id::new_unchecked(""));
1506                    head = Some(Expr(construct_expr_attr(
1507                        construct_expr_var(var, vl.clone()),
1508                        id.to_smolstr(),
1509                        src.clone(),
1510                    )));
1511                    tail = rest;
1512                }
1513                // field of arbitrary expr
1514                (Some(Expr(e)), [Some(Field(i)), rest @ ..]) => {
1515                    let expr = mem::replace(e, ast::Expr::val(false));
1516                    let id = mem::replace(i, ast::Id::new_unchecked(""));
1517                    head = Some(Expr(construct_expr_attr(
1518                        expr,
1519                        id.to_smolstr(),
1520                        src.clone(),
1521                    )));
1522                    tail = rest;
1523                }
1524                // field of string literal (same as Expr case)
1525                (Some(StrLit(s, sl)), [Some(Field(i)), rest @ ..]) => {
1526                    let id = mem::replace(i, ast::Id::new_unchecked(""));
1527                    let maybe_expr = match to_unescaped_string(s) {
1528                        Ok(s) => Some(construct_expr_string(s, sl.clone())),
1529                        Err(escape_errs) => {
1530                            errs.extend(
1531                                escape_errs
1532                                    .into_iter()
1533                                    .map(|e| ParseError::ToAST(e.to_string())),
1534                            );
1535                            None
1536                        }
1537                    };
1538                    head = maybe_expr
1539                        .map(|e| Expr(construct_expr_attr(e, id.to_smolstr(), src.clone())));
1540                    tail = rest;
1541                }
1542                // index into var
1543                (Some(Var(v, vl)), [Some(Index(i)), rest @ ..]) => {
1544                    let var = mem::replace(v, ast::Var::Principal);
1545                    let s = mem::take(i);
1546                    head = Some(Expr(construct_expr_attr(
1547                        construct_expr_var(var, vl.clone()),
1548                        s,
1549                        src.clone(),
1550                    )));
1551                    tail = rest;
1552                }
1553                // index into arbitrary expr
1554                (Some(Expr(e)), [Some(Index(i)), rest @ ..]) => {
1555                    let expr = mem::replace(e, ast::Expr::val(false));
1556                    let s = mem::take(i);
1557                    head = Some(Expr(construct_expr_attr(expr, s, src.clone())));
1558                    tail = rest;
1559                }
1560                // index into string literal (same as Expr case)
1561                (Some(StrLit(s, sl)), [Some(Index(i)), rest @ ..]) => {
1562                    let id = mem::take(i);
1563                    let maybe_expr = match to_unescaped_string(s) {
1564                        Ok(s) => Some(construct_expr_string(s, sl.clone())),
1565                        Err(escape_errs) => {
1566                            errs.extend(
1567                                escape_errs
1568                                    .into_iter()
1569                                    .map(|e| ParseError::ToAST(e.to_string())),
1570                            );
1571                            None
1572                        }
1573                    };
1574                    head = maybe_expr.map(|e| Expr(construct_expr_attr(e, id, src.clone())));
1575                    tail = rest;
1576                }
1577            }
1578        }
1579    }
1580}
1581
1582impl ASTNode<Option<cst::MemAccess>> {
1583    fn to_access(&self, errs: Errs<'_>) -> Option<AstAccessor> {
1584        let maybe_acc = self.as_inner();
1585        // return right away if there's no data, parse provided error
1586        let acc = maybe_acc?;
1587
1588        match acc {
1589            cst::MemAccess::Field(i) => {
1590                let ident = i.to_valid_ident(errs);
1591                ident.map(AstAccessor::Field)
1592            }
1593            cst::MemAccess::Call(args) => {
1594                let conv_args: Vec<_> = args.iter().filter_map(|e| e.to_expr(errs)).collect();
1595                if conv_args.len() == args.len() {
1596                    Some(AstAccessor::Call(conv_args))
1597                } else {
1598                    None
1599                }
1600            }
1601            cst::MemAccess::Index(index) => {
1602                let s = index.to_expr_or_special(errs)?.into_string_literal(errs);
1603                s.map(AstAccessor::Index)
1604            }
1605        }
1606    }
1607}
1608
1609impl ASTNode<Option<cst::Primary>> {
1610    fn to_ref_or_refs<T: RefKind>(&self, errs: Errs<'_>, var: ast::Var) -> Option<T> {
1611        let maybe_prim = self.as_inner();
1612        let prim = maybe_prim?;
1613        let r: Result<Option<T>, String> = match prim {
1614            cst::Primary::Slot(s) => {
1615                let slot = s.as_inner()?;
1616                if slot.matches(var) {
1617                    Ok(T::create_slot(errs))
1618                } else {
1619                    Err(format!(
1620                        "A slot here must be named ?{}, found ?{}",
1621                        var, slot
1622                    ))
1623                }
1624            }
1625            cst::Primary::Literal(_) => {
1626                Err(format!("expected {} found a literal", T::err_string()))
1627            }
1628            cst::Primary::Ref(x) => Ok(T::create_single_ref(x.to_ref(errs)?, errs)),
1629            cst::Primary::Name(_) => Err(format!("expected {} found a name", T::err_string())),
1630            cst::Primary::Expr(x) => Ok(x.to_ref_or_refs::<T>(errs, var)),
1631            cst::Primary::EList(lst) => {
1632                let v: Option<Vec<EntityUID>> =
1633                    lst.iter().map(|expr| expr.to_ref(var, errs)).collect();
1634                Ok(T::create_multiple_refs(v?, errs))
1635            }
1636            cst::Primary::RInits(_) => Err("record initializer".to_string()),
1637        };
1638        match r {
1639            Ok(t) => t,
1640            Err(found) => {
1641                errs.push(err::ParseError::ToAST(format!(
1642                    "expected {}, found {}",
1643                    T::err_string(),
1644                    found
1645                )));
1646                None
1647            }
1648        }
1649    }
1650
1651    pub(crate) fn to_expr(&self, errs: Errs<'_>) -> Option<ast::Expr> {
1652        self.to_expr_or_special(errs)?.into_expr(errs)
1653    }
1654    fn to_expr_or_special(&self, errs: Errs<'_>) -> Option<ExprOrSpecial<'_>> {
1655        let (src, maybe_prim) = self.as_inner_pair();
1656        // return right away if there's no data, parse provided error
1657        let prim = maybe_prim?;
1658
1659        match prim {
1660            cst::Primary::Literal(l) => l.to_expr_or_special(errs),
1661            cst::Primary::Ref(r) => r.to_expr(errs).map(ExprOrSpecial::Expr),
1662            cst::Primary::Slot(s) => s.to_expr(errs).map(ExprOrSpecial::Expr),
1663            #[allow(clippy::manual_map)]
1664            cst::Primary::Name(n) => {
1665                // if `n` isn't a var we don't want errors, we'll get them later
1666                if let Some(v) = n.to_var(&mut Vec::new()) {
1667                    Some(ExprOrSpecial::Var(v, src.clone()))
1668                } else if let Some(n) = n.to_name(errs) {
1669                    Some(ExprOrSpecial::Name(n))
1670                } else {
1671                    None
1672                }
1673            }
1674            cst::Primary::Expr(e) => e.to_expr(errs).map(ExprOrSpecial::Expr),
1675            cst::Primary::EList(es) => {
1676                let list: Vec<_> = es.iter().filter_map(|e| e.to_expr(errs)).collect();
1677                if list.len() == es.len() {
1678                    Some(ExprOrSpecial::Expr(construct_expr_set(list, src.clone())))
1679                } else {
1680                    None
1681                }
1682            }
1683            cst::Primary::RInits(is) => {
1684                let rec: Vec<_> = is.iter().filter_map(|i| i.to_init(errs)).collect();
1685                if rec.len() == is.len() {
1686                    Some(ExprOrSpecial::Expr(construct_expr_record(rec, src.clone())))
1687                } else {
1688                    errs.push(err::ParseError::ToAST(
1689                        "record literal has some invalid attributes".to_string(),
1690                    ));
1691                    None
1692                }
1693            }
1694        }
1695    }
1696
1697    /// convert `cst::Primary` representing a string literal to a `SmolStr`.
1698    /// Fails (and adds to `errs`) if the `Primary` wasn't a string literal.
1699    pub fn to_string_literal(&self, errs: Errs<'_>) -> Option<SmolStr> {
1700        let maybe_prim = self.as_inner();
1701        // return right away if there's no data, parse provided error
1702        let prim = maybe_prim?;
1703
1704        match prim {
1705            cst::Primary::Literal(l) => l.to_expr_or_special(errs)?.into_string_literal(errs),
1706            _ => {
1707                errs.push(err::ParseError::ToAST(format!(
1708                    "{prim} is not a string literal"
1709                )));
1710                None
1711            }
1712        }
1713    }
1714}
1715
1716impl ASTNode<Option<cst::Slot>> {
1717    fn to_expr(&self, _errs: Errs<'_>) -> Option<ast::Expr> {
1718        let (src, s) = self.as_inner_pair();
1719        s.map(|s| {
1720            ast::ExprBuilder::new()
1721                .with_source_info(src.clone())
1722                .slot(match s {
1723                    cst::Slot::Principal => ast::SlotId::principal(),
1724                    cst::Slot::Resource => ast::SlotId::resource(),
1725                })
1726        })
1727    }
1728}
1729
1730impl ASTNode<Option<cst::Name>> {
1731    /// Build type constraints
1732    fn to_type_constraint(&self, errs: Errs<'_>) -> Option<ast::Expr> {
1733        let (src, maybe_name) = self.as_inner_pair();
1734        match maybe_name {
1735            Some(_) => {
1736                errs.push(err::ParseError::ToAST(
1737                    "type constraints are not currently supported".to_string(),
1738                ));
1739                None
1740            }
1741            None => Some(construct_expr_bool(true, src.clone())),
1742        }
1743    }
1744
1745    pub(crate) fn to_name(&self, errs: Errs<'_>) -> Option<ast::Name> {
1746        let maybe_name = self.as_inner();
1747        // return right away if there's no data, parse provided error
1748        let name = maybe_name?;
1749
1750        let path: Vec<_> = name
1751            .path
1752            .iter()
1753            .filter_map(|i| i.to_valid_ident(errs))
1754            .collect();
1755        let maybe_name = name.name.to_valid_ident(errs);
1756
1757        // computation and error generation is complete, so fail or construct
1758        match (maybe_name, path.len()) {
1759            (Some(r), l) if l == name.path.len() => Some(construct_name(path, r)),
1760            _ => None,
1761        }
1762    }
1763    fn to_ident(&self, errs: Errs<'_>) -> Option<&cst::Ident> {
1764        let maybe_name = self.as_inner();
1765        // return right away if there's no data, parse provided error
1766        let name = maybe_name?;
1767
1768        let path: Vec<_> = name
1769            .path
1770            .iter()
1771            .filter_map(|i| i.to_valid_ident(errs))
1772            .collect();
1773        if path.len() > 1 {
1774            errs.push(err::ParseError::ToAST(
1775                "A path is not valid in this context".to_string(),
1776            ));
1777            return None;
1778        }
1779
1780        name.name.as_inner()
1781    }
1782    fn to_var(&self, errs: Errs<'_>) -> Option<ast::Var> {
1783        let name = self.to_ident(errs)?;
1784
1785        match name {
1786            cst::Ident::Principal => Some(ast::Var::Principal),
1787            cst::Ident::Action => Some(ast::Var::Action),
1788            cst::Ident::Resource => Some(ast::Var::Resource),
1789            cst::Ident::Context => Some(ast::Var::Context),
1790            _ => {
1791                errs.push(err::ParseError::ToAST(
1792                    "This is not a variable, use principal, action, resource, or context"
1793                        .to_string(),
1794                ));
1795                None
1796            }
1797        }
1798    }
1799}
1800
1801impl ast::Name {
1802    /// Convert the `Name` into a `String` attribute, which fails if it had any namespaces
1803    fn into_valid_attr(self, errs: Errs<'_>) -> Option<SmolStr> {
1804        if !self.path.is_empty() {
1805            errs.push(err::ParseError::ToAST(
1806                "A name with a path is not a valid attribute".to_string(),
1807            ));
1808            None
1809        } else {
1810            Some(self.id.to_smolstr())
1811        }
1812    }
1813
1814    fn into_func(self, args: Vec<ast::Expr>, errs: Errs<'_>, l: SourceInfo) -> Option<ast::Expr> {
1815        // error on standard methods
1816        if self.path.is_empty() {
1817            let id = self.id.as_ref();
1818            match id {
1819                "contains" | "containsAll" | "containsAny" => {
1820                    errs.push(err::ParseError::ToAST(format!(
1821                        "invalid syntax, use method-style function call like e.{}(...)",
1822                        id
1823                    )));
1824                    return None;
1825                }
1826                _ => {}
1827            }
1828        }
1829        if EXTENSION_STYLES.functions.contains(&self) {
1830            Some(construct_ext_func(self, args, l))
1831        } else {
1832            errs.push(err::ParseError::ToAST(format!(
1833                "invalid syntax, expected function, found {}",
1834                self
1835            )));
1836            None
1837        }
1838    }
1839}
1840
1841impl ASTNode<Option<cst::Ref>> {
1842    /// convert `cst::Ref` to `ast::EntityUID`
1843    pub fn to_ref(&self, errs: Errs<'_>) -> Option<ast::EntityUID> {
1844        let maybe_ref = self.as_inner();
1845        // return right away if there's no data, parse provided error
1846        let refr = maybe_ref?;
1847
1848        match refr {
1849            cst::Ref::Uid { path, eid } => {
1850                let maybe_path = path.to_name(errs);
1851                let maybe_eid = match eid
1852                    .as_valid_string(errs)
1853                    .map(|s| to_unescaped_string(s))
1854                    .transpose()
1855                {
1856                    Ok(opt) => opt,
1857                    Err(escape_errs) => {
1858                        errs.extend(
1859                            escape_errs
1860                                .into_iter()
1861                                .map(|e| ParseError::ToAST(e.to_string())),
1862                        );
1863                        None
1864                    }
1865                };
1866
1867                match (maybe_path, maybe_eid) {
1868                    (Some(p), Some(e)) => Some(construct_refr(p, e)),
1869                    _ => None,
1870                }
1871            }
1872            cst::Ref::Ref { .. } => {
1873                errs.push(err::ParseError::ToAST(
1874                    "arbitrary entity lookups are not currently supported".to_string(),
1875                ));
1876                None
1877            }
1878        }
1879    }
1880    fn to_expr(&self, errs: Errs<'_>) -> Option<ast::Expr> {
1881        self.to_ref(errs)
1882            .map(|euid| construct_expr_ref(euid, self.info.clone()))
1883    }
1884}
1885
1886impl ASTNode<Option<cst::Literal>> {
1887    fn to_expr_or_special(&self, errs: Errs<'_>) -> Option<ExprOrSpecial<'_>> {
1888        let (src, maybe_lit) = self.as_inner_pair();
1889        // return right away if there's no data, parse provided error
1890        let lit = maybe_lit?;
1891
1892        match lit {
1893            cst::Literal::True => Some(ExprOrSpecial::Expr(construct_expr_bool(true, src.clone()))),
1894            cst::Literal::False => {
1895                Some(ExprOrSpecial::Expr(construct_expr_bool(false, src.clone())))
1896            }
1897            cst::Literal::Num(n) => match i64::try_from(*n) {
1898                Ok(i) => Some(ExprOrSpecial::Expr(construct_expr_num(i, src.clone()))),
1899                Err(_) => {
1900                    errs.push(ParseError::ToAST(format!("Literal {n} is too large")));
1901                    None
1902                }
1903            },
1904            cst::Literal::Str(s) => {
1905                let maybe_str = s.as_valid_string(errs);
1906                maybe_str.map(|s| ExprOrSpecial::StrLit(s, src.clone()))
1907            }
1908        }
1909    }
1910}
1911
1912impl ASTNode<Option<cst::RecInit>> {
1913    fn to_init(&self, errs: Errs<'_>) -> Option<(SmolStr, ast::Expr)> {
1914        let (_src, maybe_lit) = self.as_inner_pair();
1915        // return right away if there's no data, parse provided error
1916        let lit = maybe_lit?;
1917
1918        let maybe_attr = lit.0.to_expr_or_special(errs)?.into_valid_attr(errs);
1919        let maybe_value = lit.1.to_expr(errs);
1920
1921        match (maybe_attr, maybe_value) {
1922            (Some(s), Some(v)) => Some((s, v)),
1923            _ => None,
1924        }
1925    }
1926}
1927
1928/// This section (construct_*) exists to handle differences between standard ast constructors and
1929/// the needs or conveniences here. Especially concerning source location data.
1930#[allow(clippy::too_many_arguments)]
1931fn construct_template_policy(
1932    id: ast::PolicyID,
1933    annotations: BTreeMap<ast::Id, SmolStr>,
1934    effect: ast::Effect,
1935    principal: ast::PrincipalConstraint,
1936    action: ast::ActionConstraint,
1937    resource: ast::ResourceConstraint,
1938    conds: Vec<ast::Expr>,
1939    l: SourceInfo,
1940) -> ast::Template {
1941    let construct_template = |non_head_constraint| {
1942        ast::Template::new(
1943            id,
1944            annotations,
1945            effect,
1946            principal,
1947            action,
1948            resource,
1949            non_head_constraint,
1950        )
1951    };
1952    let mut conds_iter = conds.into_iter();
1953    if let Some(first_expr) = conds_iter.next() {
1954        // a left fold of conditions
1955        // e.g., [c1, c2, c3,] --> ((c1 && c2) && c3)
1956        construct_template(match conds_iter.next() {
1957            Some(e) => construct_expr_and(first_expr, e, conds_iter, l),
1958            None => first_expr,
1959        })
1960    } else {
1961        // use `true` to mark the absence of non-head constraints
1962        construct_template(construct_expr_bool(true, l))
1963    }
1964}
1965fn construct_id(s: String) -> ast::Id {
1966    ast::Id::new_unchecked(s)
1967}
1968fn construct_string_from_var(v: ast::Var) -> SmolStr {
1969    match v {
1970        ast::Var::Principal => "principal".into(),
1971        ast::Var::Action => "action".into(),
1972        ast::Var::Resource => "resource".into(),
1973        ast::Var::Context => "context".into(),
1974    }
1975}
1976fn construct_name(path: Vec<ast::Id>, id: ast::Id) -> ast::Name {
1977    ast::Name {
1978        id,
1979        path: Arc::new(path),
1980    }
1981}
1982fn construct_refr(p: ast::Name, n: SmolStr) -> ast::EntityUID {
1983    let eid = ast::Eid::new(n);
1984    ast::EntityUID::from_components(p, eid)
1985}
1986fn construct_expr_ref(r: ast::EntityUID, l: SourceInfo) -> ast::Expr {
1987    ast::ExprBuilder::new().with_source_info(l).val(r)
1988}
1989fn construct_expr_num(n: i64, l: SourceInfo) -> ast::Expr {
1990    ast::ExprBuilder::new().with_source_info(l).val(n)
1991}
1992fn construct_expr_string(s: SmolStr, l: SourceInfo) -> ast::Expr {
1993    ast::ExprBuilder::new().with_source_info(l).val(s)
1994}
1995fn construct_expr_bool(b: bool, l: SourceInfo) -> ast::Expr {
1996    ast::ExprBuilder::new().with_source_info(l).val(b)
1997}
1998fn construct_expr_neg(e: ast::Expr, l: SourceInfo) -> ast::Expr {
1999    ast::ExprBuilder::new().with_source_info(l).neg(e)
2000}
2001fn construct_expr_not(e: ast::Expr, l: SourceInfo) -> ast::Expr {
2002    ast::ExprBuilder::new().with_source_info(l).not(e)
2003}
2004fn construct_expr_var(v: ast::Var, l: SourceInfo) -> ast::Expr {
2005    ast::ExprBuilder::new().with_source_info(l).var(v)
2006}
2007fn construct_expr_if(i: ast::Expr, t: ast::Expr, e: ast::Expr, l: SourceInfo) -> ast::Expr {
2008    ast::ExprBuilder::new().with_source_info(l).ite(i, t, e)
2009}
2010fn construct_expr_or(
2011    f: ast::Expr,
2012    s: ast::Expr,
2013    chained: impl IntoIterator<Item = ast::Expr>,
2014    l: SourceInfo,
2015) -> ast::Expr {
2016    let first = ast::ExprBuilder::new().with_source_info(l.clone()).or(f, s);
2017    chained.into_iter().fold(first, |a, n| {
2018        ast::ExprBuilder::new().with_source_info(l.clone()).or(a, n)
2019    })
2020}
2021fn construct_expr_and(
2022    f: ast::Expr,
2023    s: ast::Expr,
2024    chained: impl IntoIterator<Item = ast::Expr>,
2025    l: SourceInfo,
2026) -> ast::Expr {
2027    let first = ast::ExprBuilder::new()
2028        .with_source_info(l.clone())
2029        .and(f, s);
2030    chained.into_iter().fold(first, |a, n| {
2031        ast::ExprBuilder::new()
2032            .with_source_info(l.clone())
2033            .and(a, n)
2034    })
2035}
2036fn construct_expr_rel(f: ast::Expr, rel: cst::RelOp, s: ast::Expr, l: SourceInfo) -> ast::Expr {
2037    let builder = ast::ExprBuilder::new().with_source_info(l);
2038    match rel {
2039        cst::RelOp::Less => builder.less(f, s),
2040        cst::RelOp::LessEq => builder.lesseq(f, s),
2041        cst::RelOp::GreaterEq => builder.greatereq(f, s),
2042        cst::RelOp::Greater => builder.greater(f, s),
2043        cst::RelOp::NotEq => builder.noteq(f, s),
2044        cst::RelOp::Eq => builder.is_eq(f, s),
2045        cst::RelOp::In => builder.is_in(f, s),
2046    }
2047}
2048/// used for a chain of addition and/or subtraction
2049fn construct_expr_add(
2050    f: ast::Expr,
2051    chained: impl IntoIterator<Item = (cst::AddOp, ast::Expr)>,
2052    l: SourceInfo,
2053) -> ast::Expr {
2054    let mut expr = f;
2055    for (op, next_expr) in chained {
2056        let builder = ast::ExprBuilder::new().with_source_info(l.clone());
2057        expr = match op {
2058            cst::AddOp::Plus => builder.add(expr, next_expr),
2059            cst::AddOp::Minus => builder.sub(expr, next_expr),
2060        };
2061    }
2062    expr
2063}
2064/// used for a chain of multiplication only (no division or mod)
2065fn construct_expr_mul(
2066    f: ast::Expr,
2067    chained: impl IntoIterator<Item = i64>,
2068    l: SourceInfo,
2069) -> ast::Expr {
2070    let mut expr = f;
2071    for next_expr in chained {
2072        expr = ast::ExprBuilder::new()
2073            .with_source_info(l.clone())
2074            .mul(expr, next_expr)
2075    }
2076    expr
2077}
2078fn construct_expr_has(t: ast::Expr, s: SmolStr, l: SourceInfo) -> ast::Expr {
2079    ast::ExprBuilder::new().with_source_info(l).has_attr(t, s)
2080}
2081fn construct_expr_attr(e: ast::Expr, s: SmolStr, l: SourceInfo) -> ast::Expr {
2082    ast::ExprBuilder::new().with_source_info(l).get_attr(e, s)
2083}
2084fn construct_expr_like(e: ast::Expr, s: Vec<PatternElem>, l: SourceInfo) -> ast::Expr {
2085    ast::ExprBuilder::new().with_source_info(l).like(e, s)
2086}
2087fn construct_ext_func(name: ast::Name, args: Vec<ast::Expr>, l: SourceInfo) -> ast::Expr {
2088    // INVARIANT (MethodStyleArgs): CallStyle is not MethodStyle, so any args vector is fine
2089    ast::ExprBuilder::new()
2090        .with_source_info(l)
2091        .call_extension_fn(name, args)
2092}
2093
2094fn construct_method_contains(e0: ast::Expr, e1: ast::Expr, l: SourceInfo) -> ast::Expr {
2095    ast::ExprBuilder::new().with_source_info(l).contains(e0, e1)
2096}
2097fn construct_method_contains_all(e0: ast::Expr, e1: ast::Expr, l: SourceInfo) -> ast::Expr {
2098    ast::ExprBuilder::new()
2099        .with_source_info(l)
2100        .contains_all(e0, e1)
2101}
2102fn construct_method_contains_any(e0: ast::Expr, e1: ast::Expr, l: SourceInfo) -> ast::Expr {
2103    ast::ExprBuilder::new()
2104        .with_source_info(l)
2105        .contains_any(e0, e1)
2106}
2107
2108// INVARIANT (MethodStyleArgs), args must be non-empty
2109fn construct_ext_meth(n: String, args: Vec<ast::Expr>, l: SourceInfo) -> ast::Expr {
2110    let id = ast::Id::new_unchecked(n);
2111    let name = ast::Name::unqualified_name(id);
2112    // INVARIANT (MethodStyleArgs), args must be non-empty
2113    ast::ExprBuilder::new()
2114        .with_source_info(l)
2115        .call_extension_fn(name, args)
2116}
2117fn construct_expr_set(s: Vec<ast::Expr>, l: SourceInfo) -> ast::Expr {
2118    ast::ExprBuilder::new().with_source_info(l).set(s)
2119}
2120fn construct_expr_record(kvs: Vec<(SmolStr, ast::Expr)>, l: SourceInfo) -> ast::Expr {
2121    ast::ExprBuilder::new().with_source_info(l).record(kvs)
2122}
2123
2124#[cfg(test)]
2125mod tests {
2126    use super::*;
2127    use crate::{
2128        ast::Expr,
2129        parser::{err::MultipleParseErrors, *},
2130    };
2131    use std::str::FromStr;
2132
2133    #[test]
2134    fn show_expr1() {
2135        let mut errs = Vec::new();
2136        let expr: ast::Expr = text_to_cst::parse_expr(
2137            r#"
2138            if 7 then 6 > 5 else !5 || "thursday" && ((8) >= "fish")
2139        "#,
2140        )
2141        .expect("failed parser")
2142        .to_expr(&mut errs)
2143        .expect("failed convert");
2144        assert!(errs.is_empty());
2145        // manual check at test defn
2146        println!("{:?}", expr);
2147    }
2148
2149    #[test]
2150    fn show_expr2() {
2151        let mut errs = Vec::new();
2152        let expr: ast::Expr = text_to_cst::parse_expr(
2153            r#"
2154            [2,3,4].foo["hello"]
2155        "#,
2156        )
2157        .expect("failed parser")
2158        .to_expr(&mut errs)
2159        .expect("failed convert");
2160        // manual check at test defn
2161        println!("{:?}", expr);
2162    }
2163
2164    #[test]
2165    fn show_expr3() {
2166        // these exprs are ill-typed, but are allowed by the parser
2167        let mut errs = Vec::new();
2168        let expr = text_to_cst::parse_expr(
2169            r#"
2170            "first".some_ident
2171        "#,
2172        )
2173        .expect("failed parser")
2174        .to_expr(&mut errs)
2175        .expect("failed convert");
2176        match expr.expr_kind() {
2177            ast::ExprKind::GetAttr { attr, .. } => {
2178                assert_eq!(attr, "some_ident");
2179            }
2180            _ => panic!("should be a get expr"),
2181        }
2182
2183        let expr = text_to_cst::parse_expr(
2184            r#"
2185            1.some_ident
2186        "#,
2187        )
2188        .expect("failed parser")
2189        .to_expr(&mut errs)
2190        .expect("failed convert");
2191        match expr.expr_kind() {
2192            ast::ExprKind::GetAttr { attr, .. } => {
2193                assert_eq!(attr, "some_ident");
2194            }
2195            _ => panic!("should be a get expr"),
2196        }
2197
2198        let expr = text_to_cst::parse_expr(
2199            r#"
2200            "first"["some string"]
2201        "#,
2202        )
2203        .expect("failed parser")
2204        .to_expr(&mut errs)
2205        .expect("failed convert");
2206        match expr.expr_kind() {
2207            ast::ExprKind::GetAttr { attr, .. } => {
2208                assert_eq!(attr, "some string");
2209            }
2210            _ => panic!("should be a get expr"),
2211        }
2212    }
2213
2214    #[test]
2215    fn show_expr4() {
2216        let mut errs = Vec::new();
2217        let expr: ast::Expr = text_to_cst::parse_expr(
2218            r#"
2219            {"one":1,"two":2} has one
2220        "#,
2221        )
2222        .expect("failed parser")
2223        .to_expr(&mut errs)
2224        .expect("failed convert");
2225
2226        match expr.expr_kind() {
2227            ast::ExprKind::HasAttr { attr, .. } => {
2228                assert_eq!(attr, "one");
2229            }
2230            _ => panic!("should be a has expr"),
2231        }
2232    }
2233
2234    #[test]
2235    fn show_expr5() {
2236        let mut errs = Vec::new();
2237        let expr = text_to_cst::parse_expr(
2238            r#"
2239            {"one":1,"two":2}.one
2240        "#,
2241        )
2242        .expect("failed parser")
2243        .to_expr(&mut errs)
2244        .expect("failed convert");
2245
2246        match expr.expr_kind() {
2247            ast::ExprKind::GetAttr { attr, .. } => {
2248                assert_eq!(attr, "one");
2249            }
2250            _ => panic!("should be a get expr"),
2251        }
2252
2253        // parses to the same AST expression as above
2254        let expr: ast::Expr = text_to_cst::parse_expr(
2255            r#"
2256            {"one":1,"two":2}["one"]
2257        "#,
2258        )
2259        .expect("failed parser")
2260        .to_expr(&mut errs)
2261        .expect("failed convert");
2262
2263        match expr.expr_kind() {
2264            ast::ExprKind::GetAttr { attr, .. } => {
2265                assert_eq!(attr, "one");
2266            }
2267            _ => panic!("should be a get expr"),
2268        }
2269
2270        // accessing a record with a non-identifier attribute
2271        let mut errs = Vec::new();
2272        let expr: ast::Expr = text_to_cst::parse_expr(
2273            r#"
2274            {"this is a valid map key+.-_%()":1,"two":2}["this is a valid map key+.-_%()"]
2275        "#,
2276        )
2277        .expect("failed parser")
2278        .to_expr(&mut errs)
2279        .expect("failed convert");
2280
2281        match expr.expr_kind() {
2282            ast::ExprKind::GetAttr { attr, .. } => {
2283                assert_eq!(attr, "this is a valid map key+.-_%()");
2284            }
2285            _ => panic!("should be a get expr"),
2286        }
2287    }
2288
2289    #[test]
2290    fn show_expr6_idents() {
2291        let mut errs = Vec::new();
2292        let expr = text_to_cst::parse_expr(
2293            r#"
2294            {if true then a else b:"b"} ||
2295            {if false then a else b:"b"}
2296        "#,
2297        )
2298        .expect("failed parser")
2299        .to_expr(&mut errs);
2300
2301        println!("{:?}", errs);
2302        assert!(expr.is_none());
2303        // a,b,a,b: unsupported variables
2304        // if .. then .. else are invalid attributes
2305        assert!(errs.len() == 6);
2306
2307        errs.clear();
2308        let expr = text_to_cst::parse_expr(
2309            r#"
2310            {principal:"principal"}
2311        "#,
2312        )
2313        .expect("failed parser")
2314        .to_expr(&mut errs)
2315        .expect("failed convert");
2316
2317        println!("{:?}", expr);
2318        match expr.expr_kind() {
2319            ast::ExprKind::Record { .. } => {}
2320            _ => panic!("should be record"),
2321        }
2322
2323        errs.clear();
2324        let expr = text_to_cst::parse_expr(
2325            r#"
2326            {"principal":"principal"}
2327        "#,
2328        )
2329        .expect("failed parser")
2330        .to_expr(&mut errs)
2331        .expect("failed convert");
2332
2333        println!("{:?}", expr);
2334        match expr.expr_kind() {
2335            ast::ExprKind::Record { .. } => {}
2336            _ => panic!("should be record"),
2337        }
2338    }
2339
2340    #[test]
2341    fn reserved_idents1() {
2342        let mut errs = Vec::new();
2343        let parse = text_to_cst::parse_expr(
2344            r#"
2345            The::true::path::to::"enlightenment".false
2346        "#,
2347        )
2348        .expect("failed parse");
2349
2350        let convert = parse.to_expr(&mut errs);
2351        println!("{:?}", errs);
2352        // uses true and false:
2353        assert!(errs.len() == 2);
2354        assert!(convert.is_none());
2355
2356        let mut errs = Vec::new();
2357        let parse = text_to_cst::parse_expr(
2358            r#"
2359            if {if: true}.if then {"if":false}["if"] else {when:true}.permit
2360        "#,
2361        )
2362        .expect("failed parse");
2363
2364        let convert = parse.to_expr(&mut errs);
2365        println!("{:?}", errs);
2366        // uses if twice, one of those triggers an invalid attr
2367        assert!(errs.len() == 3);
2368        assert!(convert.is_none());
2369    }
2370
2371    #[test]
2372    fn reserved_idents2() {
2373        let mut errs = Vec::new();
2374        let parse = text_to_cst::parse_expr(
2375            r#"
2376            if {where: true}.like || {has:false}.in then {"like":false}["in"] else {then:true}.else
2377        "#,
2378        )
2379        .expect("failed parse");
2380
2381        let convert = parse.to_expr(&mut errs);
2382        println!("{:?}", errs);
2383        // uses 5x reserved idents, 2 of those trigger an invalid attr
2384        assert!(errs.len() == 7);
2385        assert!(convert.is_none());
2386    }
2387
2388    #[test]
2389    fn show_policy1() {
2390        let mut errs = Vec::new();
2391        let parse = text_to_cst::parse_policy(
2392            r#"
2393            permit(principal:p,action:a,resource:r)when{w}unless{u}advice{"doit"};
2394        "#,
2395        )
2396        .expect("failed parse");
2397        println!("{:#}", parse.as_inner().expect("internal parse error"));
2398        let convert = parse.to_policy(ast::PolicyID::from_string("id"), &mut errs);
2399        println!("{:?}", errs);
2400        // 3x type constraints, 2x arbitrary vars, advice block
2401        assert!(errs.len() == 6);
2402        assert!(convert.is_none());
2403        // manual check at test defn
2404        println!("{:?}", convert);
2405    }
2406
2407    #[test]
2408    fn show_policy2() {
2409        let mut errs = Vec::new();
2410        let parse = text_to_cst::parse_policy(
2411            r#"
2412            permit(principal,action,resource)when{true};
2413        "#,
2414        )
2415        .expect("failed parse");
2416        println!("{}", parse.as_inner().expect("internal parse error"));
2417        println!("{:?}", parse.as_inner().expect("internal parse error"));
2418        let convert = parse.to_policy(ast::PolicyID::from_string("id"), &mut errs);
2419        assert!(convert.is_some());
2420        // manual check at test defn
2421        println!("{:?}", convert);
2422    }
2423
2424    #[test]
2425    fn show_policy3() {
2426        let mut errs = Vec::new();
2427        let parse = text_to_cst::parse_policy(
2428            r#"
2429            permit(principal in User::"jane",action,resource);
2430        "#,
2431        )
2432        .expect("failed parse");
2433        println!("{}", parse.as_inner().expect("internal parse error"));
2434        println!("{:?}", parse.as_inner().expect("internal parse error"));
2435        let convert = parse
2436            .to_policy(ast::PolicyID::from_string("id"), &mut errs)
2437            .expect("failed convert");
2438        assert!(errs.is_empty());
2439        // manual check at test defn
2440        println!("{:?}", convert);
2441    }
2442
2443    #[test]
2444    fn show_policy4() {
2445        let mut errs = Vec::new();
2446        let parse = text_to_cst::parse_policy(
2447            r#"
2448            forbid(principal in User::"jane",action,resource)unless{
2449                context.group != "friends"
2450            };
2451        "#,
2452        )
2453        .expect("failed parse");
2454        let convert = parse
2455            .to_policy(ast::PolicyID::from_string("id"), &mut errs)
2456            .expect("failed convert");
2457        assert!(errs.is_empty());
2458        // manual check at test defn
2459        println!("\n{:?}", convert);
2460    }
2461
2462    #[test]
2463    fn policy_annotations() {
2464        // common use-case
2465        let mut errs = Vec::new();
2466        let policy = text_to_cst::parse_policy(
2467            r#"
2468            @anno("good annotation")permit(principal,action,resource);
2469        "#,
2470        )
2471        .expect("should parse")
2472        .to_policy(ast::PolicyID::from_string("id"), &mut errs)
2473        .expect("should be valid");
2474        assert_eq!(
2475            policy.annotation(&ast::Id::new_unchecked("anno")),
2476            Some(&"good annotation".into())
2477        );
2478
2479        // duplication is error
2480        let mut errs = Vec::new();
2481        let policy = text_to_cst::parse_policy(
2482            r#"
2483            @anno("good annotation")
2484            @anno2("good annotation")
2485            @anno("oops, duplicate")
2486            permit(principal,action,resource);
2487        "#,
2488        )
2489        .expect("should parse")
2490        .to_policy(ast::PolicyID::from_string("id"), &mut errs);
2491        assert!(policy.is_none());
2492        // annotation duplication (anno)
2493        assert!(errs.len() == 1);
2494
2495        // can have multiple annotations
2496        let mut errs = Vec::new();
2497        let policyset = text_to_cst::parse_policies(
2498            r#"
2499            @anno1("first")
2500            permit(principal,action,resource);
2501
2502            @anno2("second")
2503            permit(principal,action,resource);
2504
2505            @anno3a("third-a")
2506            @anno3b("third-b")
2507            permit(principal,action,resource);
2508        "#,
2509        )
2510        .expect("should parse")
2511        .to_policyset(&mut errs)
2512        .expect("should be valid");
2513        assert_eq!(
2514            policyset
2515                .get(&ast::PolicyID::from_string("policy0"))
2516                .expect("should be a policy")
2517                .annotation(&ast::Id::new_unchecked("anno0")),
2518            None
2519        );
2520        assert_eq!(
2521            policyset
2522                .get(&ast::PolicyID::from_string("policy0"))
2523                .expect("should be a policy")
2524                .annotation(&ast::Id::new_unchecked("anno1")),
2525            Some(&"first".into())
2526        );
2527        assert_eq!(
2528            policyset
2529                .get(&ast::PolicyID::from_string("policy1"))
2530                .expect("should be a policy")
2531                .annotation(&ast::Id::new_unchecked("anno2")),
2532            Some(&"second".into())
2533        );
2534        assert_eq!(
2535            policyset
2536                .get(&ast::PolicyID::from_string("policy2"))
2537                .expect("should be a policy")
2538                .annotation(&ast::Id::new_unchecked("anno3a")),
2539            Some(&"third-a".into())
2540        );
2541        assert_eq!(
2542            policyset
2543                .get(&ast::PolicyID::from_string("policy2"))
2544                .expect("should be a policy")
2545                .annotation(&ast::Id::new_unchecked("anno3b")),
2546            Some(&"third-b".into())
2547        );
2548        assert_eq!(
2549            policyset
2550                .get(&ast::PolicyID::from_string("policy2"))
2551                .expect("should be a policy")
2552                .annotation(&ast::Id::new_unchecked("anno3c")),
2553            None
2554        );
2555        assert_eq!(
2556            policyset
2557                .get(&ast::PolicyID::from_string("policy2"))
2558                .expect("should be a policy")
2559                .annotations()
2560                .count(),
2561            2
2562        );
2563    }
2564
2565    #[test]
2566    fn fail_head1() {
2567        let mut errs = Vec::new();
2568        let parse = text_to_cst::parse_policy(
2569            r#"
2570            permit(
2571                principal in [User::"jane",Group::"friends"],
2572                action,
2573                resource
2574            );
2575        "#,
2576        )
2577        .expect("failed parse");
2578        println!("\n{:#}", parse.as_inner().expect("internal parse error"));
2579        let convert = parse.to_policy(ast::PolicyID::from_string("id"), &mut errs);
2580        println!("{:?}", errs);
2581        assert!(errs.len() == 1);
2582        assert!(convert.is_none());
2583    }
2584
2585    #[test]
2586    fn fail_head2() {
2587        let mut errs = Vec::new();
2588        let parse = text_to_cst::parse_policy(
2589            r#"
2590            permit(
2591                principal in User::"jane",
2592                action == if true then Photo::"view" else Photo::"edit",
2593                resource
2594            );
2595        "#,
2596        )
2597        .expect("failed parse");
2598        println!("{:#}", parse.as_inner().expect("internal parse error"));
2599        let convert = parse.to_policy(ast::PolicyID::from_string("id"), &mut errs);
2600        println!("{:?}", errs);
2601        assert!(errs.len() == 1);
2602        assert!(convert.is_none());
2603    }
2604
2605    #[test]
2606    fn fail_head3() {
2607        let mut errs = Vec::new();
2608        let parse = text_to_cst::parse_policy(
2609            r#"
2610            permit(principal,action,resource,context);
2611        "#,
2612        )
2613        .expect("failed parse");
2614        let convert = parse.to_policy(ast::PolicyID::from_string("id"), &mut errs);
2615        assert!(errs.len() == 1);
2616        assert!(convert.is_none());
2617    }
2618
2619    #[test]
2620    fn method_call2() {
2621        let mut errs = Vec::new();
2622        let e = text_to_cst::parse_expr(
2623            r#"
2624                principal.contains(resource)
2625                "#,
2626        )
2627        // the cst should be acceptable
2628        .expect("parse error")
2629        .to_expr(&mut errs);
2630        // ast should be acceptable
2631        println!("{:?}", errs);
2632        assert!(e.is_some());
2633        assert!(errs.is_empty());
2634
2635        let e = text_to_cst::parse_expr(
2636            r#"
2637            contains(principal,resource)
2638            "#,
2639        )
2640        // cst should be acceptable
2641        .expect("parse error")
2642        .to_expr(&mut errs);
2643        // ast should be error, since "contains" is used inappropriately
2644        println!("{:?}", errs);
2645        assert!(e.is_none());
2646        assert!(errs.len() == 1);
2647    }
2648
2649    #[test]
2650    fn construct_record1() {
2651        let mut errs = Vec::new();
2652        let e = text_to_cst::parse_expr(
2653            r#"
2654                {one:"one"}
2655                "#,
2656        )
2657        // the cst should be acceptable
2658        .expect("parse error")
2659        .to_expr(&mut errs)
2660        .expect("convert fail");
2661        // ast should be acceptable, with record construction
2662        if let ast::ExprKind::Record { .. } = e.expr_kind() {
2663            // good
2664        } else {
2665            panic!("not a record")
2666        }
2667        println!("{e}");
2668
2669        let e = text_to_cst::parse_expr(
2670            r#"
2671                {"one":"one"}
2672                "#,
2673        )
2674        // the cst should be acceptable
2675        .expect("parse error")
2676        .to_expr(&mut errs)
2677        .expect("convert fail");
2678        // ast should be acceptable, with record construction
2679        if let ast::ExprKind::Record { .. } = e.expr_kind() {
2680            // good
2681        } else {
2682            panic!("not a record")
2683        }
2684        println!("{e}");
2685
2686        let e = text_to_cst::parse_expr(
2687            r#"
2688                {"one":"one",two:"two"}
2689                "#,
2690        )
2691        // the cst should be acceptable
2692        .expect("parse error")
2693        .to_expr(&mut errs)
2694        .expect("convert fail");
2695        // ast should be acceptable, with record construction
2696        if let ast::ExprKind::Record { .. } = e.expr_kind() {
2697            // good
2698        } else {
2699            panic!("not a record")
2700        }
2701        println!("{e}");
2702
2703        let e = text_to_cst::parse_expr(
2704            r#"
2705                {one:"one","two":"two"}
2706                "#,
2707        )
2708        // the cst should be acceptable
2709        .expect("parse error")
2710        .to_expr(&mut errs)
2711        .expect("convert fail");
2712        // ast should be acceptable, with record construction
2713        if let ast::ExprKind::Record { .. } = e.expr_kind() {
2714            // good
2715        } else {
2716            panic!("not a record")
2717        }
2718        println!("{e}");
2719
2720        let e = text_to_cst::parse_expr(
2721            r#"
2722                {one:"b\"","b\"":2}
2723                "#,
2724        )
2725        // the cst should be acceptable
2726        .expect("parse error")
2727        .to_expr(&mut errs)
2728        .expect("convert fail");
2729        // ast should be acceptable, with record construction
2730        if let ast::ExprKind::Record { .. } = e.expr_kind() {
2731            // good
2732        } else {
2733            panic!("not a record")
2734        }
2735        println!("{e}");
2736    }
2737
2738    #[test]
2739    fn construct_invalid_get() {
2740        let mut errs = Vec::new();
2741        let e = text_to_cst::parse_expr(
2742            r#"
2743            {"one":1, "two":"two"}[0]
2744        "#,
2745        )
2746        .expect("failed parser")
2747        .to_expr(&mut errs);
2748        // ast should be error: 0 is not a string literal
2749        println!("{:?}", errs);
2750        assert!(e.is_none());
2751        assert!(errs.len() == 1);
2752
2753        let e = text_to_cst::parse_expr(
2754            r#"
2755            {"one":1, "two":"two"}[-1]
2756        "#,
2757        )
2758        .expect("failed parser")
2759        .to_expr(&mut errs);
2760        // ast should be error: -1 is not a string literal
2761        println!("{:?}", errs);
2762        assert!(e.is_none());
2763
2764        let e = text_to_cst::parse_expr(
2765            r#"
2766            {"one":1, "two":"two"}[true]
2767        "#,
2768        )
2769        .expect("failed parser")
2770        .to_expr(&mut errs);
2771        // ast should be error: true is not a string literal
2772        println!("{:?}", errs);
2773        assert!(e.is_none());
2774
2775        let e = text_to_cst::parse_expr(
2776            r#"
2777            {"one":1, "two":"two"}[one]
2778        "#,
2779        )
2780        .expect("failed parser")
2781        .to_expr(&mut errs);
2782        // ast should be error: one is not a string literal
2783        println!("{:?}", errs);
2784        assert!(e.is_none());
2785    }
2786
2787    #[test]
2788    fn construct_has() {
2789        let mut errs = Vec::new();
2790        let expr: ast::Expr = text_to_cst::parse_expr(
2791            r#"
2792            {"one":1,"two":2} has "arbitrary+ _string"
2793        "#,
2794        )
2795        .expect("failed parser")
2796        .to_expr(&mut errs)
2797        .expect("failed convert");
2798
2799        match expr.expr_kind() {
2800            ast::ExprKind::HasAttr { attr, .. } => {
2801                assert_eq!(attr, "arbitrary+ _string");
2802            }
2803            _ => panic!("should be a has expr"),
2804        }
2805
2806        let mut errs = Vec::new();
2807        let e = text_to_cst::parse_expr(
2808            r#"
2809            {"one":1,"two":2} has 1
2810        "#,
2811        )
2812        .expect("failed parser")
2813        .to_expr(&mut errs);
2814        // ast should be error
2815        println!("{:?}", errs);
2816        assert!(e.is_none());
2817        assert!(errs.len() == 1);
2818    }
2819
2820    #[test]
2821    fn construct_like() {
2822        let mut errs = Vec::new();
2823        let expr: ast::Expr = text_to_cst::parse_expr(
2824            r#"
2825            "354 hams" like "*5*"
2826        "#,
2827        )
2828        .expect("failed parser")
2829        .to_expr(&mut errs)
2830        .expect("failed convert");
2831        match expr.expr_kind() {
2832            ast::ExprKind::Like { pattern, .. } => {
2833                assert_eq!(pattern.to_string(), "*5*");
2834            }
2835            _ => panic!("should be a like expr"),
2836        }
2837
2838        let e = text_to_cst::parse_expr(
2839            r#"
2840            "354 hams" like 354
2841        "#,
2842        )
2843        .expect("failed parser")
2844        .to_expr(&mut errs);
2845        // ast should be error
2846        println!("{:?}", errs);
2847        assert!(e.is_none());
2848        assert!(errs.len() == 1);
2849
2850        let mut errs = Vec::new();
2851        let expr: ast::Expr = text_to_cst::parse_expr(
2852            r#"
2853            "string\\with\\backslashes" like "string\\with\\backslashes"
2854        "#,
2855        )
2856        .expect("failed parser")
2857        .to_expr(&mut errs)
2858        .expect("failed convert");
2859        match expr.expr_kind() {
2860            ast::ExprKind::Like { pattern, .. } => {
2861                assert_eq!(pattern.to_string(), r#"string\\with\\backslashes"#);
2862            }
2863            _ => panic!("should be a like expr"),
2864        }
2865
2866        let expr: ast::Expr = text_to_cst::parse_expr(
2867            r#"
2868            "string\\with\\backslashes" like "string\*with\*backslashes"
2869        "#,
2870        )
2871        .expect("failed parser")
2872        .to_expr(&mut errs)
2873        .expect("failed convert");
2874        match expr.expr_kind() {
2875            ast::ExprKind::Like { pattern, .. } => {
2876                assert_eq!(pattern.to_string(), r#"string\*with\*backslashes"#);
2877            }
2878            _ => panic!("should be a like expr"),
2879        }
2880
2881        let e = text_to_cst::parse_expr(
2882            r#"
2883            "string\*with\*escaped\*stars" like "string\*with\*escaped\*stars"
2884        "#,
2885        )
2886        .expect("failed parser")
2887        .to_expr(&mut errs);
2888        // ast should be error, \* is not a valid string character
2889        println!("{:?}", errs);
2890        assert!(e.is_none());
2891        assert!(errs.len() == 3); // 3 invalid escapes in the first argument
2892
2893        let expr: ast::Expr = text_to_cst::parse_expr(
2894            r#"
2895            "string*with*stars" like "string\*with\*stars"
2896        "#,
2897        )
2898        .expect("failed parser")
2899        .to_expr(&mut errs)
2900        .expect("failed convert");
2901        match expr.expr_kind() {
2902            ast::ExprKind::Like { pattern, .. } => {
2903                assert_eq!(pattern.to_string(), "string\\*with\\*stars");
2904            }
2905            _ => panic!("should be a like expr"),
2906        }
2907
2908        let mut errs = Vec::new();
2909        let expr: ast::Expr = text_to_cst::parse_expr(
2910            r#"
2911            "string\\*with\\*backslashes\\*and\\*stars" like "string\\\*with\\\*backslashes\\\*and\\\*stars"
2912        "#,
2913        )
2914        .expect("failed parser")
2915        .to_expr(&mut errs)
2916        .expect("failed convert");
2917        match expr.expr_kind() {
2918            ast::ExprKind::Like { pattern, .. } => {
2919                assert_eq!(
2920                    pattern.to_string(),
2921                    r#"string\\\*with\\\*backslashes\\\*and\\\*stars"#
2922                );
2923            }
2924            _ => panic!("should be a like expr"),
2925        }
2926        // round trip test
2927        let test_pattern = &vec![
2928            PatternElem::Char('h'),
2929            PatternElem::Char('e'),
2930            PatternElem::Char('l'),
2931            PatternElem::Char('l'),
2932            PatternElem::Char('o'),
2933            PatternElem::Char('\\'),
2934            PatternElem::Char('0'),
2935            PatternElem::Char('*'),
2936            PatternElem::Char('\\'),
2937            PatternElem::Char('*'),
2938        ];
2939        let e1 = ast::Expr::like(ast::Expr::val("hello"), test_pattern.clone());
2940        let s1 = format!("{e1}");
2941        // Char('\\') prints to r#"\\"# and Char('*') prints to r#"\*"#.
2942        assert_eq!(s1, r#""hello" like "hello\\0\*\\\*""#);
2943        let e2 = text_to_cst::parse_expr(&s1)
2944            .expect("failed parser")
2945            .to_expr(&mut errs)
2946            .expect("failed convert");
2947        match e2.expr_kind() {
2948            ast::ExprKind::Like { pattern, .. } => {
2949                assert_eq!(pattern.get_elems(), test_pattern);
2950            }
2951            _ => panic!("should be a like expr"),
2952        }
2953        let s2 = format!("{e2}");
2954        assert_eq!(s1, s2);
2955    }
2956
2957    #[test]
2958    fn issue_wf_5046() {
2959        let policy = parse_policy(
2960            Some("WF-5046".into()),
2961            r#"permit(
2962            principal,
2963            action in [Action::"action"],
2964            resource in G::""
2965          ) when {
2966            true && ("" like "/gisterNatives\\*D")
2967          };"#,
2968        );
2969        assert!(policy.is_ok());
2970    }
2971
2972    #[test]
2973    fn entity_access() {
2974        // entities can be accessed using the same notation as records
2975
2976        // ok
2977        let mut errs = Vec::new();
2978        let expr: ast::Expr = text_to_cst::parse_expr(
2979            r#"
2980            User::"jane" has age
2981        "#,
2982        )
2983        .expect("failed parser")
2984        .to_expr(&mut errs)
2985        .expect("failed convert");
2986        match expr.expr_kind() {
2987            ast::ExprKind::HasAttr { attr, .. } => {
2988                assert_eq!(attr, "age");
2989            }
2990            _ => panic!("should be a has expr"),
2991        }
2992
2993        // ok
2994        let mut errs = Vec::new();
2995        let expr: ast::Expr = text_to_cst::parse_expr(
2996            r#"
2997            User::"jane" has "arbitrary+ _string"
2998        "#,
2999        )
3000        .expect("failed parser")
3001        .to_expr(&mut errs)
3002        .expect("failed convert");
3003        match expr.expr_kind() {
3004            ast::ExprKind::HasAttr { attr, .. } => {
3005                assert_eq!(attr, "arbitrary+ _string");
3006            }
3007            _ => panic!("should be a has expr"),
3008        }
3009
3010        // not ok: 1 is not a valid attribute
3011        let mut errs = Vec::new();
3012        let e = text_to_cst::parse_expr(
3013            r#"
3014            User::"jane" has 1
3015        "#,
3016        )
3017        .expect("failed parser")
3018        .to_expr(&mut errs);
3019        assert!(e.is_none());
3020        assert!(errs.len() == 1);
3021
3022        // ok
3023        let mut errs = Vec::new();
3024        let expr: ast::Expr = text_to_cst::parse_expr(
3025            r#"
3026            User::"jane".age
3027        "#,
3028        )
3029        .expect("failed parser")
3030        .to_expr(&mut errs)
3031        .expect("failed convert");
3032        match expr.expr_kind() {
3033            ast::ExprKind::GetAttr { attr, .. } => {
3034                assert_eq!(attr, "age");
3035            }
3036            _ => panic!("should be a get expr"),
3037        }
3038
3039        // ok
3040        let mut errs = Vec::new();
3041        let expr: ast::Expr = text_to_cst::parse_expr(
3042            r#"
3043            User::"jane"["arbitrary+ _string"]
3044        "#,
3045        )
3046        .expect("failed parser")
3047        .to_expr(&mut errs)
3048        .expect("failed convert");
3049        match expr.expr_kind() {
3050            ast::ExprKind::GetAttr { attr, .. } => {
3051                assert_eq!(attr, "arbitrary+ _string");
3052            }
3053            _ => panic!("should be a get expr"),
3054        }
3055
3056        // not ok: age is not a string literal
3057        let mut errs = Vec::new();
3058        let e = text_to_cst::parse_expr(
3059            r#"
3060            User::"jane"[age]
3061        "#,
3062        )
3063        .expect("failed parser")
3064        .to_expr(&mut errs);
3065        assert!(e.is_none());
3066        assert!(errs.len() == 1);
3067    }
3068
3069    #[test]
3070    fn relational_ops1() {
3071        let mut errs = Vec::new();
3072        let e = text_to_cst::parse_expr(
3073            r#"
3074                3 >= 2 >= 1
3075                "#,
3076        )
3077        // the cst should be acceptable
3078        .expect("parse error")
3079        .to_expr(&mut errs);
3080        // conversion should fail, too many relational ops
3081        assert!(e.is_none());
3082
3083        let e = text_to_cst::parse_expr(
3084            r#"
3085                    3 >= ("dad" in "dad")
3086                    "#,
3087        )
3088        // the cst should be acceptable
3089        .expect("parse error")
3090        .to_expr(&mut errs);
3091        // conversion should succeed, only one relational op
3092        assert!(e.is_some());
3093
3094        let e = text_to_cst::parse_expr(
3095            r#"
3096                (3 >= 2) == true
3097                "#,
3098        )
3099        // the cst should be acceptable
3100        .expect("parse error")
3101        .to_expr(&mut errs);
3102        // conversion should succeed, parentheses provided
3103        assert!(e.is_some());
3104
3105        let e = text_to_cst::parse_expr(
3106            r#"
3107                if 4 < 3 then 4 != 3 else 4 == 3 < 4
3108                "#,
3109        )
3110        // the cst should be acceptable
3111        .expect("parse error")
3112        .to_expr(&mut errs);
3113        // conversion should fail, too many relational ops
3114        assert!(e.is_none());
3115    }
3116
3117    #[test]
3118    fn arithmetic() {
3119        let mut errs = Vec::new();
3120        let e = text_to_cst::parse_expr(r#" 2 + 4 "#)
3121            // the cst should be acceptable
3122            .expect("parse error")
3123            .to_expr(&mut errs);
3124        // conversion should succeed
3125        assert!(e.is_some());
3126
3127        let e = text_to_cst::parse_expr(r#" 2 + -5 "#)
3128            // the cst should be acceptable
3129            .expect("parse error")
3130            .to_expr(&mut errs);
3131        // conversion should succeed
3132        assert!(e.is_some());
3133
3134        let e = text_to_cst::parse_expr(r#" 2 - 5 "#)
3135            // the cst should be acceptable
3136            .expect("parse error")
3137            .to_expr(&mut errs);
3138        // conversion should succeed
3139        assert!(e.is_some());
3140
3141        let e = text_to_cst::parse_expr(r#" 2 * 5 "#)
3142            // the cst should be acceptable
3143            .expect("parse error")
3144            .to_expr(&mut errs);
3145        // conversion should succeed
3146        assert!(e.is_some());
3147
3148        let e = text_to_cst::parse_expr(r#" 2 * -5 "#)
3149            // the cst should be acceptable
3150            .expect("parse error")
3151            .to_expr(&mut errs);
3152        // conversion should succeed
3153        assert!(e.is_some());
3154
3155        let e = text_to_cst::parse_expr(r#" context.size * 4 "#)
3156            // the cst should be acceptable
3157            .expect("parse error")
3158            .to_expr(&mut errs);
3159        // conversion should succeed
3160        assert!(e.is_some());
3161
3162        let e = text_to_cst::parse_expr(r#" 4 * context.size "#)
3163            // the cst should be acceptable
3164            .expect("parse error")
3165            .to_expr(&mut errs);
3166        // conversion should succeed
3167        assert!(e.is_some());
3168
3169        let e = text_to_cst::parse_expr(r#" context.size * context.scale "#)
3170            // the cst should be acceptable
3171            .expect("parse error")
3172            .to_expr(&mut errs);
3173        // conversion should fail: only multiplication by a constant is allowed
3174        assert!(e.is_none());
3175
3176        let e = text_to_cst::parse_expr(r#" 5 + 10 + 90 "#)
3177            // the cst should be acceptable
3178            .expect("parse error")
3179            .to_expr(&mut errs);
3180        // conversion should succeed
3181        assert!(e.is_some());
3182
3183        let e = text_to_cst::parse_expr(r#" 5 + 10 - 90 * -2 "#)
3184            // the cst should be acceptable
3185            .expect("parse error")
3186            .to_expr(&mut errs);
3187        // conversion should succeed
3188        assert!(e.is_some());
3189
3190        let e = text_to_cst::parse_expr(r#" 5 + 10 * 90 - 2 "#)
3191            // the cst should be acceptable
3192            .expect("parse error")
3193            .to_expr(&mut errs);
3194        // conversion should succeed
3195        assert!(e.is_some());
3196
3197        let e = text_to_cst::parse_expr(r#" 5 - 10 - 90 - 2 "#)
3198            // the cst should be acceptable
3199            .expect("parse error")
3200            .to_expr(&mut errs);
3201        // conversion should succeed
3202        assert!(e.is_some());
3203
3204        let e = text_to_cst::parse_expr(r#" 5 * context.size * 10 "#)
3205            // the cst should be acceptable
3206            .expect("parse error")
3207            .to_expr(&mut errs);
3208        // conversion should succeed
3209        assert!(e.is_some());
3210
3211        let e = text_to_cst::parse_expr(r#" context.size * 3 * context.scale "#)
3212            // the cst should be acceptable
3213            .expect("parse error")
3214            .to_expr(&mut errs);
3215        // conversion should fail: only multiplication by a constant is allowed
3216        assert!(e.is_none());
3217    }
3218
3219    const CORRECT_TEMPLATES: [&str; 7] = [
3220        r#"permit(principal == ?principal, action == Action::"action", resource == ?resource);"#,
3221        r#"permit(principal in ?principal, action == Action::"action", resource in ?resource);"#,
3222        r#"permit(principal in ?principal, action == Action::"action", resource in ?resource);"#,
3223        r#"permit(principal in p::"principal", action == Action::"action", resource in ?resource);"#,
3224        r#"permit(principal == p::"principal", action == Action::"action", resource in ?resource);"#,
3225        r#"permit(principal in ?principal, action == Action::"action", resource in r::"resource");"#,
3226        r#"permit(principal in ?principal, action == Action::"action", resource == r::"resource");"#,
3227    ];
3228
3229    #[test]
3230    fn template_tests() {
3231        for src in CORRECT_TEMPLATES {
3232            let mut errs = Vec::new();
3233            let e = text_to_cst::parse_policy(src)
3234                .expect("parse_error")
3235                .to_policy_template(ast::PolicyID::from_string("i0"), &mut errs);
3236            if e.is_none() {
3237                panic!("Failed to create a policy template: {:?}", errs);
3238            }
3239        }
3240    }
3241
3242    const WRONG_VAR_TEMPLATES: [&str; 16] = [
3243        r#"permit(principal == ?resource, action, resource);"#,
3244        r#"permit(principal in ?resource, action, resource);"#,
3245        r#"permit(principal, action, resource == ?principal);"#,
3246        r#"permit(principal, action, resource in ?principal);"#,
3247        r#"permit(principal, action == ?principal, resource);"#,
3248        r#"permit(principal, action in ?principal, resource);"#,
3249        r#"permit(principal, action == ?resource, resource);"#,
3250        r#"permit(principal, action in ?resource, resource);"#,
3251        r#"forbid(principal == ?resource, action, resource);"#,
3252        r#"forbid(principal in ?resource, action, resource);"#,
3253        r#"forbid(principal, action, resource == ?principal);"#,
3254        r#"forbid(principal, action, resource in ?principal);"#,
3255        r#"forbid(principal, action == ?principal, resource);"#,
3256        r#"forbid(principal, action in ?principal, resource);"#,
3257        r#"forbid(principal, action == ?resource, resource);"#,
3258        r#"forbid(principal, action in ?resource, resource);"#,
3259    ];
3260
3261    #[test]
3262    fn test_wrong_template_var() {
3263        for src in WRONG_VAR_TEMPLATES {
3264            let mut errs = vec![];
3265            let e = text_to_cst::parse_policy(src)
3266                .expect("Parse Error")
3267                .to_policy_template(ast::PolicyID::from_string("id0"), &mut errs);
3268            assert!(e.is_none());
3269        }
3270    }
3271
3272    #[test]
3273    fn var_type() {
3274        let mut errs = Vec::new();
3275        let e = text_to_cst::parse_policy(
3276            r#"
3277                permit(principal,action,resource);
3278                "#,
3279        )
3280        // the cst should be acceptable
3281        .expect("parse error")
3282        .to_policy(ast::PolicyID::from_string("0"), &mut errs);
3283        // conversion should succeed, it's just permit all
3284        assert!(e.is_some());
3285        let e = text_to_cst::parse_policy(
3286            r#"
3287                permit(principal:User,action,resource);
3288                "#,
3289        )
3290        // the cst should be acceptable
3291        .expect("parse error")
3292        .to_policy(ast::PolicyID::from_string("1"), &mut errs);
3293        // conversion should fail, variable types are not supported
3294        assert!(e.is_none());
3295    }
3296    #[test]
3297    fn string_escapes() {
3298        // test strings with valid escapes
3299        // convert a string `s` to `<double-quote> <escaped-form-of-s> <double-quote>`
3300        // and test if the resulting string literal AST contains exactly `s`
3301        // for instance, "\u{1F408}"" is converted into r#""\u{1F408}""#,
3302        // the latter should be parsed into `Literal(String("🐈"))` and
3303        // `🐈` is represented by '\u{1F408}'
3304        let test_valid = |s: &str| {
3305            let r = parse_literal(&format!("\"{}\"", s.escape_default()));
3306            assert!(r.is_ok());
3307            assert_eq!(r.unwrap(), ast::Literal::String(s.into()));
3308        };
3309        test_valid("\t");
3310        test_valid("\0");
3311        test_valid("👍");
3312        test_valid("🐈");
3313        test_valid("\u{1F408}");
3314        test_valid("abc\tde\\fg");
3315        test_valid("aaa\u{1F408}bcd👍👍👍");
3316        // test string with invalid escapes
3317        let test_invalid = |s: &str, en: usize| {
3318            let r = parse_literal(&format!("\"{}\"", s));
3319            assert!(r.is_err());
3320            assert!(r.unwrap_err().len() == en);
3321        };
3322        // invalid escape `\a`
3323        test_invalid("\\a", 1);
3324        // invalid escape `\b`
3325        test_invalid("\\b", 1);
3326        // invalid escape `\p`
3327        test_invalid("\\\\aa\\p", 1);
3328        // invalid escape `\a` and empty unicode escape
3329        test_invalid(r#"\aaa\u{}"#, 2);
3330    }
3331
3332    fn expect_action_error(test: &str, euid_strs: Vec<&str>) {
3333        let euids = euid_strs
3334            .into_iter()
3335            .map(|euid_str| {
3336                EntityUID::from_str(euid_str).expect("Test was provided with invalid euid")
3337            })
3338            .collect::<Vec<_>>();
3339        let p = parse_policyset(test);
3340        match p {
3341            Ok(pset) => panic!("Policy: {pset}, shouln't have parsed!"),
3342            Err(es) => {
3343                if es.len() != euids.len() {
3344                    panic!(
3345                        "Parse should have produced exactly {} parse errors, produced: {:?}",
3346                        euids.len(),
3347                        es
3348                    );
3349                } else {
3350                    for euid in euids {
3351                        let err = action_type_error_msg(&euid);
3352                        assert!(es.contains(&err));
3353                    }
3354                }
3355            }
3356        }
3357    }
3358
3359    #[test]
3360    fn action_checker() {
3361        let euid = EntityUID::from_str("Action::\"view\"").unwrap();
3362        assert!(euid_has_action_type(&euid));
3363        let euid = EntityUID::from_str("Foo::Action::\"view\"").unwrap();
3364        assert!(euid_has_action_type(&euid));
3365        let euid = EntityUID::from_str("Foo::\"view\"").unwrap();
3366        assert!(!euid_has_action_type(&euid));
3367        let euid = EntityUID::from_str("Action::Foo::\"view\"").unwrap();
3368        assert!(!euid_has_action_type(&euid));
3369    }
3370
3371    #[test]
3372    fn action_must_be_action() {
3373        parse_policyset(r#"permit(principal, action == Action::"view", resource);"#)
3374            .expect("Valid policy failed to parse");
3375        parse_policyset(r#"permit(principal, action == Foo::Action::"view", resource);"#)
3376            .expect("Valid policy failed to parse");
3377        parse_policyset(r#"permit(principal, action in Action::"view", resource);"#)
3378            .expect("Valid policy failed to parse");
3379        parse_policyset(r#"permit(principal, action in Foo::Action::"view", resource);"#)
3380            .expect("Valid policy failed to parse");
3381        parse_policyset(r#"permit(principal, action in [Foo::Action::"view"], resource);"#)
3382            .expect("Valid policy failed to parse");
3383        parse_policyset(
3384            r#"permit(principal, action in [Foo::Action::"view", Action::"view"], resource);"#,
3385        )
3386        .expect("Valid policy failed to parse");
3387        expect_action_error(
3388            r#"permit(principal, action == Foo::"view", resource);"#,
3389            vec!["Foo::\"view\""],
3390        );
3391        expect_action_error(
3392            r#"permit(principal, action == Action::Foo::"view", resource);"#,
3393            vec!["Action::Foo::\"view\""],
3394        );
3395        expect_action_error(
3396            r#"permit(principal, action == Bar::Action::Foo::"view", resource);"#,
3397            vec!["Bar::Action::Foo::\"view\""],
3398        );
3399        expect_action_error(
3400            r#"permit(principal, action in Bar::Action::Foo::"view", resource);"#,
3401            vec!["Bar::Action::Foo::\"view\""],
3402        );
3403        expect_action_error(
3404            r#"permit(principal, action in [Bar::Action::Foo::"view"], resource);"#,
3405            vec!["Bar::Action::Foo::\"view\""],
3406        );
3407        expect_action_error(
3408            r#"permit(principal, action in [Bar::Action::Foo::"view", Action::"check"], resource);"#,
3409            vec!["Bar::Action::Foo::\"view\""],
3410        );
3411        expect_action_error(
3412            r#"permit(principal, action in [Bar::Action::Foo::"view", Foo::"delete", Action::"check"], resource);"#,
3413            vec!["Bar::Action::Foo::\"view\"", "Foo::\"delete\""],
3414        );
3415    }
3416
3417    #[test]
3418    fn method_style() {
3419        let policy = parse_policyset(
3420            r#"permit(principal, action, resource)
3421            when { contains(true) < 1 };"#,
3422        );
3423        assert!(
3424            policy.is_err()
3425                && matches!(
3426                    policy.as_ref().unwrap_err().as_slice(),
3427                    [err::ParseError::ToAST(_)]
3428                ),
3429            "builtin functions must be called in method-style"
3430        );
3431    }
3432
3433    #[test]
3434    fn test_mul() {
3435        for (es, expr) in [
3436            ("--2*3", Expr::mul(Expr::neg(Expr::val(-2)), 3)),
3437            (
3438                "1 * 2 * false",
3439                Expr::mul(Expr::mul(Expr::val(false), 1), 2),
3440            ),
3441            (
3442                "0 * 1 * principal",
3443                Expr::mul(Expr::mul(Expr::var(ast::Var::Principal), 0), 1),
3444            ),
3445            (
3446                "0 * (-1) * principal",
3447                Expr::mul(Expr::mul(Expr::var(ast::Var::Principal), 0), -1),
3448            ),
3449        ] {
3450            let mut errs = Vec::new();
3451            let e = text_to_cst::parse_expr(es)
3452                .expect("should construct a CST")
3453                .to_expr(&mut errs)
3454                .expect("should convert to AST");
3455            assert!(
3456                e.eq_shape(&expr),
3457                "{:?} and {:?} should have the same shape.",
3458                e,
3459                expr
3460            );
3461        }
3462
3463        for es in [
3464            r#"false * "bob""#,
3465            "principal * (1 + 2)",
3466            "principal * -(-1)",
3467            // --1 is parsed as Expr::neg(Expr::val(-1)) and thus is not
3468            // considered as a constant.
3469            "principal * --1",
3470        ] {
3471            let mut errs = Vec::new();
3472            let e = text_to_cst::parse_expr(es)
3473                .expect("should construct a CST")
3474                .to_expr(&mut errs);
3475            assert!(e.is_none());
3476        }
3477    }
3478
3479    #[test]
3480    fn test_not() {
3481        for (es, expr) in [
3482            (
3483                "!1 + 2 == 3",
3484                Expr::is_eq(
3485                    Expr::add(Expr::not(Expr::val(1)), Expr::val(2)),
3486                    Expr::val(3),
3487                ),
3488            ),
3489            (
3490                "!!1 + 2 == 3",
3491                Expr::is_eq(
3492                    Expr::add(Expr::not(Expr::not(Expr::val(1))), Expr::val(2)),
3493                    Expr::val(3),
3494                ),
3495            ),
3496            (
3497                "!!!1 + 2 == 3",
3498                Expr::is_eq(
3499                    Expr::add(Expr::not(Expr::val(1)), Expr::val(2)),
3500                    Expr::val(3),
3501                ),
3502            ),
3503            (
3504                "!!!!1 + 2 == 3",
3505                Expr::is_eq(
3506                    Expr::add(Expr::not(Expr::not(Expr::val(1))), Expr::val(2)),
3507                    Expr::val(3),
3508                ),
3509            ),
3510            (
3511                "!!(-1) + 2 == 3",
3512                Expr::is_eq(
3513                    Expr::add(Expr::not(Expr::not(Expr::val(-1))), Expr::val(2)),
3514                    Expr::val(3),
3515                ),
3516            ),
3517        ] {
3518            let mut errs = Vec::new();
3519            let e = text_to_cst::parse_expr(es)
3520                .expect("should construct a CST")
3521                .to_expr(&mut errs)
3522                .expect("should convert to AST");
3523            assert!(
3524                e.eq_shape(&expr),
3525                "{:?} and {:?} should have the same shape.",
3526                e,
3527                expr
3528            );
3529        }
3530    }
3531
3532    #[test]
3533    fn test_neg() {
3534        for (es, expr) in [
3535            ("-(1 + 2)", Expr::neg(Expr::add(Expr::val(1), Expr::val(2)))),
3536            ("1-(2)", Expr::sub(Expr::val(1), Expr::val(2))),
3537            ("1-2", Expr::sub(Expr::val(1), Expr::val(2))),
3538            ("(-1)", Expr::val(-1)),
3539            ("-(-1)", Expr::neg(Expr::val(-1))),
3540            ("--1", Expr::neg(Expr::val(-1))),
3541            ("--(--1)", Expr::neg(Expr::neg(Expr::neg(Expr::val(-1))))),
3542            ("2--1", Expr::sub(Expr::val(2), Expr::val(-1))),
3543            ("-9223372036854775808", Expr::val(-(9223372036854775808))),
3544            // Evaluating this expression leads to overflows but the parser
3545            // won't reject it.
3546            (
3547                "--9223372036854775808",
3548                Expr::neg(Expr::val(-9223372036854775808)),
3549            ),
3550            (
3551                "-(9223372036854775807)",
3552                Expr::neg(Expr::val(9223372036854775807)),
3553            ),
3554        ] {
3555            let mut errs = Vec::new();
3556            let e = text_to_cst::parse_expr(es)
3557                .expect("should construct a CST")
3558                .to_expr(&mut errs)
3559                .expect("should convert to AST");
3560            assert!(
3561                e.eq_shape(&expr),
3562                "{:?} and {:?} should have the same shape.",
3563                e,
3564                expr
3565            );
3566        }
3567
3568        for (es, em) in [
3569            ("-9223372036854775809", "Integer constant is too large"),
3570            // Contrary to Rust, this expression is not valid because the
3571            // parser treats it as a negation operation whereas the operand
3572            // (9223372036854775808) is too large.
3573            (
3574                "-(9223372036854775808)",
3575                "Literal 9223372036854775808 is too large",
3576            ),
3577        ] {
3578            let mut errs = Vec::new();
3579            let e = text_to_cst::parse_expr(es)
3580                .expect("should construct a CST")
3581                .to_expr(&mut errs);
3582            assert!(e.is_none());
3583            assert!(MultipleParseErrors(&errs).to_string().contains(em));
3584        }
3585    }
3586}