diff --git a/rudder-lang/src/ir/enums.rs b/rudder-lang/src/ir/enums.rs index c921c6e5a6f..de1aa20efef 100644 --- a/rudder-lang/src/ir/enums.rs +++ b/rudder-lang/src/ir/enums.rs @@ -5,6 +5,7 @@ use super::{ context::Type, context::VarContext, enum_tree::{EnumItem, EnumTree}, + value::Value, }; use crate::{error::*, parser::*}; use std::collections::{HashMap, HashSet}; @@ -14,6 +15,77 @@ use toml::Value as TomlValue; // TODO named item tests // TODO aliases tests +#[derive(Debug, PartialEq, Clone)] +pub enum Expression<'src> { + Enum(EnumExpression<'src>), + Variable(VariableExpression<'src>), +} + +#[derive(Debug, PartialEq, Clone)] +pub enum Variable<'src> { + Value(Value<'src>), + Identifier(Token<'src>), +} + +/// An expression that can be defined using variables and raw rudderlang types +#[derive(Debug, PartialEq, Clone)] +pub struct VariableExpression<'src> { + pub source: Token<'src>, + pub expression: VariableExpressionPart<'src>, +} + +#[derive(Debug, PartialEq, Clone)] +pub enum VariableExpressionPart<'src> { + // list and struct + // implicit: NotIncludes = Not(Includes) + Includes(Box>, Box>), + + // integer and float + // includes Smaller (reversed left/right) + GreaterOrEqual(Box>, Box>), + Greater(Box>, Box>), + + // list struct integer float string + // explicit & implicit: NotEqual = Not(Equal) + Equal( + Box>, + Box>, + ), + And( + Box>, + Box>, + ), + Or( + Box>, + Box>, + ), + Not(Box>), +} + +impl<'src> VariableExpressionPart<'src> { + /// List all variables that are used in an expression, + /// and put them into the 'variables' hashset + /// this is recursive mutable, pass it an empty hashset at first call + /// Only used by evaluate. + fn list_variables_tree( + &self, + variables: &mut HashMap<(Token<'src>, Token<'src>), HashSet>>, // (variable, tree) -> item list + ) { + unimplemented!() + // match self { + // VariableExpressionPart::Not(v) => v.list_variables_tree(variables), + // VariableExpressionPart::Or(v1, v2) => { + // v1.list_variables_tree(variables); + // v2.list_variables_tree(variables); + // } + // VariableExpressionPart::And(v1, v2) => { + // v1.list_variables_tree(variables); + // v2.list_variables_tree(variables); + // } + // } + } +} + /// EnumList Is a singleton containing all enum definitions /// It also has a direct pointer from an item name to its enum type #[derive(Debug)] @@ -624,6 +696,7 @@ impl<'it, 'src> Iterator for CrossIterator<'it, 'src> { None } } + /// A boolean expression that can be defined using enums #[derive(Debug, PartialEq, Clone)] pub struct EnumExpression<'src> { diff --git a/rudder-lang/src/ir/resource.rs b/rudder-lang/src/ir/resource.rs index 2df182fd09a..ddd18f8d5a1 100644 --- a/rudder-lang/src/ir/resource.rs +++ b/rudder-lang/src/ir/resource.rs @@ -533,10 +533,9 @@ impl<'src> Statement<'src> { PStatement::Noop => Statement::Noop, PStatement::Case(case, v) => Statement::Case( case, - map_vec_results(v.into_iter(), |(exp, sts)| { - let expr = enum_list.canonify_expression(context, exp)?; - Ok(( - expr, + map_vec_results(v.into_iter(), |(exp, sts)| match exp { + PExpression::Enum(e) => Ok(( + enum_list.canonify_expression(context, e)?, map_vec_results(sts.into_iter(), |st| { Statement::from_pstatement( context, @@ -546,7 +545,8 @@ impl<'src> Statement<'src> { enum_list, ) })?, - )) + )), + PExpression::Variable(v) => unimplemented!(), })?, ), }) diff --git a/rudder-lang/src/ir/value.rs b/rudder-lang/src/ir/value.rs index 9e2cebfcb32..ee96b0cf2ea 100644 --- a/rudder-lang/src/ir/value.rs +++ b/rudder-lang/src/ir/value.rs @@ -4,7 +4,7 @@ use super::{ context::Type, context::VarContext, - enums::{EnumExpression, EnumList}, + enums::{EnumExpression, EnumList, Expression, VariableExpression}, }; use crate::{error::*, parser::*}; use std::{collections::HashMap, convert::TryFrom}; @@ -190,13 +190,10 @@ impl<'src> Value<'src> { pub struct ComplexValue<'src> { source: Token<'src>, // nested complex values not supported - cases: Vec<(EnumExpression<'src>, Option>)>, + cases: Vec<(Expression<'src>, Option>)>, } impl<'src> ComplexValue<'src> { - pub fn new( - source: Token<'src>, - cases: Vec<(EnumExpression<'src>, Option>)>, - ) -> Self { + pub fn new(source: Token<'src>, cases: Vec<(Expression<'src>, Option>)>) -> Self { Self { source, cases } } @@ -206,7 +203,12 @@ impl<'src> ComplexValue<'src> { pvalue: PComplexValue<'src>, ) -> Result { let cases = map_vec_results(pvalue.cases.into_iter(), |(case, value)| { - let case = enum_list.canonify_expression(context, case)?; + let case = match case { + PExpression::Enum(expr) => { + Expression::Enum(enum_list.canonify_expression(context, expr)?) + } + PExpression::Variable(expr) => unimplemented!(), + }; let value = match value { None => None, Some(v) => Some(Value::from_pvalue(enum_list, context, v)?), @@ -247,8 +249,28 @@ impl<'src> ComplexValue<'src> { } pub fn verify(&self, enum_list: &EnumList<'src>) -> Result<()> { + let mut enum_cases: Vec<(EnumExpression, Option)> = Vec::new(); + let mut variable_cases: Vec<(VariableExpression, Option)> = Vec::new(); + for (expr, val) in self.cases.clone() { + match expr { + Expression::Enum(e) => enum_cases.push((e, val)), + Expression::Variable(e) => variable_cases.push((e, val)), + }; + } // check enums - let mut errors = enum_list.evaluate(&self.cases, "TODO".into()); + let enum_cases = self + .cases + .iter() + .filter_map(|(expr, val)| { + if let Expression::Enum(e) = expr { + Some((e.clone(), val.clone())) + } else { + None + } + }) + .collect::)>>(); + let variable_cases = unimplemented!(); + let mut errors = enum_list.evaluate(&enum_cases, "TODO".into()); // check type let init_val = self .first_value() diff --git a/rudder-lang/src/parser.rs b/rudder-lang/src/parser.rs index f5a7db9702f..db4a0edf37c 100644 --- a/rudder-lang/src/parser.rs +++ b/rudder-lang/src/parser.rs @@ -214,6 +214,18 @@ fn penum_alias(i: PInput) -> PResult { )(i) } +#[derive(Debug, PartialEq, Clone)] +pub enum PExpression<'src> { + Enum(PEnumExpression<'src>), + Variable(PVariableExpression<'src>), +} +fn pexpression(i: PInput) -> PResult { + alt(( + map(pvariable_expression, PExpression::Variable), + map(penum_expression, PExpression::Enum), + ))(i) +} + /// An enum expression is used as a condition in a case expression. /// This is a boolean expression based on enum comparison. /// A comparison check if the variable is of the right type and contains @@ -320,7 +332,7 @@ fn enum_atom(i: PInput) -> PResult { { var: pvariable_identifier; _x: etag("=~"); - value: or_fail(enum_range_or_item, || PErrorKind::InvalidEnumExpression); + value: or_fail(enum_range_or_item, || PErrorKind::Invalid("enum expression")); } => { match value { RangeOrItem::Range(name,left,right,dots) => PEnumExpressionPart::RangeCompare(Some(var), name, left, right, dots), @@ -332,7 +344,7 @@ fn enum_atom(i: PInput) -> PResult { { var: pvariable_identifier; _x: etag("!~"); - value: or_fail(enum_range_or_item, || PErrorKind::InvalidEnumExpression); + value: or_fail(enum_range_or_item, || PErrorKind::Invalid("enum expression")); } => { match value { RangeOrItem::Range(name,left,right,dots) => PEnumExpressionPart::Not(Box::new(PEnumExpressionPart::RangeCompare(Some(var), name, left, right, dots))), @@ -355,7 +367,7 @@ fn enum_or_expression(i: PInput) -> PResult { _x: etag("|"); right: or_fail( alt((enum_or_expression, enum_and_expression, enum_not_expression, enum_atom)), - || PErrorKind::InvalidEnumExpression); + || PErrorKind::Invalid("enum expression")); } => PEnumExpressionPart::Or(Box::new(left), Box::new(right)) )(i) } @@ -366,7 +378,7 @@ fn enum_and_expression(i: PInput) -> PResult { _x: etag("&"); right: or_fail( alt((enum_and_expression, enum_not_expression, enum_atom)), - || PErrorKind::InvalidEnumExpression); + || PErrorKind::Invalid("enum expression")); } => PEnumExpressionPart::And(Box::new(left), Box::new(right)) )(i) } @@ -374,11 +386,189 @@ fn enum_not_expression(i: PInput) -> PResult { wsequence!( { _x: etag("!"); - value: or_fail(enum_atom, || PErrorKind::InvalidEnumExpression); + value: or_fail(enum_atom, || PErrorKind::Invalid("enum expression")); } => PEnumExpressionPart::Not(Box::new(value)) )(i) } +#[derive(Debug, PartialEq, Clone)] +pub struct PVariableExpression<'src> { + pub source: Token<'src>, + pub expression: PVariableExpressionPart<'src>, +} +fn pvariable_expression(i: PInput) -> PResult { + pexpression_part(i).map(|(rest, expression)| { + println!("VARIABLE EXPRESSION: {:#?}", expression); + let source = get_parsed_context(i, i, rest); + (rest, PVariableExpression { source, expression }) + }) +} + +#[derive(Debug, PartialEq, Clone)] +pub enum PVariable<'src> { + // PEnumExpression must not be handled because + PValue(PValue<'src>), + PIdentifier(Token<'src>), +} +fn pexpr_variable(i: PInput) -> PResult { + alt(( + map(pidentifier, |identifier| { + println!("IDENTIFIER: {:#?}", identifier); + PVariable::PIdentifier(identifier) + }), + // Be careful of ordering here + map(punescaped_string, |(x, y)| { + println!("UNESCSTR: {:#?}", y); + PVariable::PValue(PValue::String(x, y)) + }), + map(pescaped_string, |(x, y)| { + println!("ESCSTR: {:#?}", y); + PVariable::PValue(PValue::String(x, y)) + }), + map(pinteger, |(x, y)| PVariable::PValue(PValue::Integer(x, y))), + map(pfloat, |(x, y)| PVariable::PValue(PValue::Float(x, y))), + map(plist, |list| PVariable::PValue(PValue::List(list))), + map(pstruct, |map| PVariable::PValue(PValue::Struct(map))), + ))(i) +} + +#[derive(Debug, PartialEq, Clone)] +#[allow(clippy::large_enum_variant)] +pub enum PVariableExpressionPart<'src> { + // list and struct + // implicit: NotIncludes = Not(Includes) + Includes(Box>, Box>), + + // integer and float + // includes Smaller (reversed left/right) + GreaterOrEqual(Box>, Box>), + Greater(Box>, Box>), + + // list struct integer float string + // explicit & implicit: NotEqual = Not(Equal) + Equal( + Box>, + Box>, + ), + And( + Box>, + Box>, + ), + Or( + Box>, + Box>, + ), + Not(Box>), +} +fn pexpression_part(i: PInput) -> PResult { + alt(( + // or_expression, + // and_expression, + // not_expression, + // equal_expression, + not_equal_expression, + atom, + ))(i) +} +fn atom(i: PInput) -> PResult { + println!("ATOM EPXR : {:.20}", i.fragment()); + alt(( + delimited_parser("(", pexpression_part, ")"), + wsequence!( + { + left: pexpr_variable; + _x: etag(">"); + right: or_fail(pexpr_variable, || PErrorKind::Invalid("expression (greater)")); + } => PVariableExpressionPart::Greater(Box::new(left), Box::new(right)) + ), + wsequence!( + { + left: pexpr_variable; + _x: etag(">="); + right: or_fail(pexpr_variable, || PErrorKind::Invalid("expression (greater or equal)")); + } => PVariableExpressionPart::GreaterOrEqual(Box::new(left), Box::new(right)) + ), + wsequence!( + { + left: pexpr_variable; + _x: etag("<"); + right: or_fail(pexpr_variable, || PErrorKind::Invalid("expression (less)")); + } => PVariableExpressionPart::Greater(Box::new(right), Box::new(left)) + ), + wsequence!( + { + left: pexpr_variable; + _x: etag("<="); + right: or_fail(pexpr_variable, || PErrorKind::Invalid("expression (less or equal)")); + } => PVariableExpressionPart::GreaterOrEqual(Box::new(right), Box::new(left)) + ), + wsequence!( + { + parent: pexpr_variable; + _x: etag("["); + child: or_fail(pexpr_variable, || PErrorKind::Invalid("expression (includes)")); + _x: etag("]"); + } => PVariableExpressionPart::Includes(Box::new(parent), Box::new(child)) + ), + ))(i) +} +fn equal_expression(i: PInput) -> PResult { + wsequence!( + { + left: alt((and_expression, not_expression, or_expression, atom)); + _x: etag("=="); + right: or_fail( + alt((or_expression, and_expression, not_expression, atom)), + || PErrorKind::Invalid("expression (equal)")); + } => PVariableExpressionPart::Equal(Box::new(left), Box::new(right)) + )(i) +} +fn not_equal_expression(i: PInput) -> PResult { + println!("NOT EQ EPXR"); + wsequence!( + { + left: alt((and_expression, not_expression, or_expression, atom)); + _x: etag("!="); + right: or_fail( + alt((or_expression, and_expression, not_expression, atom)), + || PErrorKind::Invalid("expression (not equal)")); + } => PVariableExpressionPart::Not(Box::new(PVariableExpressionPart::Equal(Box::new(left), Box::new(right)))) + )(i) +} +fn or_expression(i: PInput) -> PResult { + println!("OR EPXR"); + wsequence!( + { + left: alt((and_expression, not_expression, atom)); + _x: etag("|"); + right: or_fail( + alt((or_expression, and_expression, not_expression, atom)), + || PErrorKind::Invalid("expression (or)")); + } => PVariableExpressionPart::Or(Box::new(left), Box::new(right)) + )(i) +} +fn and_expression(i: PInput) -> PResult { + wsequence!( + { + left: alt((not_expression, atom)); + _x: etag("&"); + right: or_fail( + alt((and_expression, not_expression, atom)), + || PErrorKind::Invalid("expression (and)")); + } => PVariableExpressionPart::And(Box::new(left), Box::new(right)) + )(i) +} +fn not_expression(i: PInput) -> PResult { + println!("NOT"); + wsequence!( + { + _not: etag("!"); + _eq: not(etag("=")); + value: or_fail(atom, || PErrorKind::Invalid("expression (not)")); + } => PVariableExpressionPart::Not(Box::new(value)) + )(i) +} + /// An unescaped string is a literal string delimited by '"""'. /// The token is here to keep position fn punescaped_string(i: PInput) -> PResult<(Token, String)> { @@ -514,20 +704,6 @@ fn pstruct(i: PInput) -> PResult> { )(i) } -/// Alternative version of pstruct based on "." rather than braces. -/// Used for the agent -/// A struct is stored in a HashMap -// fn pstruct_agent(i: PInput) -> PResult> { -// wsequence!( -// { -// values: separated_list( -// sp(etag(".")), -// pvalue -// ); -// } => values.into_iter().map(|(k,v)| (k.1,v)).collect() -// )(i) -// } - /// A PType is the type a variable or a parameter can take. /// Its only purpose is to be a PValue construction helper use std::marker::PhantomData; @@ -551,6 +727,7 @@ pub enum PValue<'src> { Float(Token<'src>, f64), Integer(Token<'src>, i64), Boolean(Token<'src>, bool), + // Expression(PExpression<'src>), EnumExpression(PEnumExpression<'src>), Struct(HashMap>), List(Vec>), @@ -562,6 +739,7 @@ fn pvalue(i: PInput) -> PResult { map(pescaped_string, |(x, y)| PValue::String(x, y)), map(pinteger, |(x, y)| PValue::Integer(x, y)), map(pfloat, |(x, y)| PValue::Float(x, y)), + // map(pexpression, PValue::Expression), map(penum_expression, PValue::EnumExpression), map(plist, PValue::List), map(pstruct, PValue::Struct), @@ -586,23 +764,23 @@ fn ptype(i: PInput) -> PResult { pub struct PComplexValue<'src> { pub source: Token<'src>, // nested complex values not supported - pub cases: Vec<(PEnumExpression<'src>, Option>)>, + pub cases: Vec<(PExpression<'src>, Option>)>, } /// A single case in a case switch -fn pvalue_case(i: PInput) -> PResult<(PEnumExpression, Option)> { +fn pvalue_case(i: PInput) -> PResult<(PExpression, Option)> { alt(( map(etag("nodefault"), |t| { ( - PEnumExpression { + PExpression::Enum(PEnumExpression { source: Token::from(t), expression: PEnumExpressionPart::NoDefault(Token::from(t)), - }, + }), None, ) }), wsequence!( { - expr: or_fail(penum_expression, || PErrorKind::ExpectedKeyword("enum expression")); + expr: or_fail(pexpression, || PErrorKind::ExpectedKeyword("enum expression")); _x: ftag("=>"); value: or_fail(pvalue, || PErrorKind::ExpectedToken("case statement")); } => (expr,Some(value)) @@ -637,10 +815,10 @@ fn pcomplex_value(i: PInput) -> PResult { PComplexValue { source, cases: vec![( - PEnumExpression { + PExpression::Enum(PEnumExpression { source: "".into(), expression: PEnumExpressionPart::Default("".into()), - }, + }), Some(res), )], }, @@ -980,10 +1158,7 @@ pub enum PStatement<'src> { ConditionVariableDefinition(PCondVariableDef<'src>), StateDeclaration(PStateDeclaration<'src>), // case keyword, list (condition , then) - Case( - Token<'src>, - Vec<(PEnumExpression<'src>, Vec>)>, - ), // keep the pinput since it will be reparsed later + Case(Token<'src>, Vec<(PExpression<'src>, Vec>)>), // keep the pinput since it will be reparsed later // Stop engine with a final message Fail(PValue<'src>), // Inform the user of something @@ -996,20 +1171,20 @@ pub enum PStatement<'src> { Noop, } /// A single case in a case switch -fn pcase(i: PInput) -> PResult<(PEnumExpression, Vec)> { +fn pcase(i: PInput) -> PResult<(PExpression, Vec)> { alt(( map(etag("nodefault"), |t| { ( - PEnumExpression { + PExpression::Enum(PEnumExpression { source: Token::from(t), expression: PEnumExpressionPart::NoDefault(Token::from(t)), - }, + }), vec![PStatement::Noop], ) }), wsequence!( { - expr: or_fail(penum_expression, || PErrorKind::ExpectedKeyword("enum expression")); + expr: or_fail(pexpression, || PErrorKind::ExpectedKeyword("enum expression")); _x: ftag("=>"); stmt: or_fail(alt(( map(pstatement, |x| vec![x]), @@ -1046,9 +1221,9 @@ fn pstatement(i: PInput) -> PResult { { metadata: pmetadata_list; // metadata is invalid here, check it after the 'if' tag below case: estag("if"); - expr: or_fail(penum_expression, || PErrorKind::ExpectedKeyword("enum expression")); + expr: or_fail(pexpression, || PErrorKind::Invalid("expression")); _x: ftag("=>"); - stmt: or_fail(pstatement, || PErrorKind::ExpectedKeyword("statement")); + stmt: or_fail(pstatement, || PErrorKind::Invalid("statement")); } => { // Propagate metadata to the single statement let statement = match stmt { @@ -1058,11 +1233,17 @@ fn pstatement(i: PInput) -> PResult { }, x => x, }; - PStatement::Case(case.into(), vec![ - ( expr, vec![statement] ), - ( PEnumExpression { source:"default".into(), expression: PEnumExpressionPart::Default("default".into()) }, - vec![PStatement::Noop]) - ] ) + let case_exprs = match expr { + PExpression::Enum(_) => vec![ + ( expr, vec![statement] ), + ( + PExpression::Enum(PEnumExpression { source:"default".into(), expression: PEnumExpressionPart::Default("default".into()) }), + vec![PStatement::Noop] + ) + ], + PExpression::Variable(_) => vec![( expr, vec![statement] )], + }; + PStatement::Case(case.into(), case_exprs) } ), // Flow statements diff --git a/rudder-lang/src/parser/baseparsers.rs b/rudder-lang/src/parser/baseparsers.rs index 44650a55222..e7f68f2ee9b 100644 --- a/rudder-lang/src/parser/baseparsers.rs +++ b/rudder-lang/src/parser/baseparsers.rs @@ -155,7 +155,7 @@ macro_rules! generic_sequence { }}; } -/// extract the parsed data once something hase been parsed +/// extract the parsed data once something has been parsed pub fn get_parsed_context<'src>( input: PInput<'src>, start: PInput<'src>, diff --git a/rudder-lang/src/parser/error.rs b/rudder-lang/src/parser/error.rs index f63af3cbae9..f09d810422a 100644 --- a/rudder-lang/src/parser/error.rs +++ b/rudder-lang/src/parser/error.rs @@ -25,7 +25,7 @@ pub enum PErrorKind { NomTest(String), // cannot be use outside of tests ExpectedKeyword(&'static str), // anywhere (keyword type) ExpectedToken(&'static str), // anywhere (expected token) - InvalidEnumExpression, // in enum expression + Invalid(&'static str), // expression / statement is invalid InvalidEscapeSequence, // in string definition InvalidFormat, // in header InvalidName(I), // in identifier expressions (type of expression) @@ -108,7 +108,7 @@ impl<'src> fmt::Display for PError> { PErrorKind::NomTest(msg) => format!("Testing only error message, this should never happen {}.\nPlease fill a BUG with context on when this happened!", msg), PErrorKind::ExpectedKeyword(s) => format!("The following keyword was expected: '{}'.", s.bright_magenta()), PErrorKind::ExpectedToken(s) => format!("The following token was expected '{}'.", s.bright_magenta()), - PErrorKind::InvalidEnumExpression => "This enum expression is invalid".to_string(), + PErrorKind::Invalid(s) => format!("This {} is invalid", s), PErrorKind::InvalidEscapeSequence => "This escape sequence cannot be used in a string".to_string(), PErrorKind::InvalidFormat => "Invalid header format, it must contain a single line '@format=x' where x is an integer. Shebang accepted.".to_string(), PErrorKind::InvalidName(i) => format!("The identifier is invalid in a {}.", i.fragment().bright_magenta()), diff --git a/rudder-lang/src/parser/tests.rs b/rudder-lang/src/parser/tests.rs index 47002df3d37..c9999896011 100644 --- a/rudder-lang/src/parser/tests.rs +++ b/rudder-lang/src/parser/tests.rs @@ -34,7 +34,7 @@ fn map_err(err: PError) -> (&str, PErrorKind<&str>) { PErrorKind::NomTest(e) => PErrorKind::NomTest(e), PErrorKind::ExpectedKeyword(i) => PErrorKind::ExpectedKeyword(i), PErrorKind::ExpectedToken(i) => PErrorKind::ExpectedToken(i), - PErrorKind::InvalidEnumExpression => PErrorKind::InvalidEnumExpression, + PErrorKind::Invalid(s) => PErrorKind::Invalid(s), PErrorKind::InvalidEscapeSequence => PErrorKind::InvalidEscapeSequence, PErrorKind::InvalidFormat => PErrorKind::InvalidFormat, PErrorKind::InvalidName(i) => PErrorKind::InvalidName(*i.fragment()), @@ -564,7 +564,7 @@ fn test_penum_expression() { ); assert_eq!( map_res(penum_expression, "a=~"), - Err(("a=~", PErrorKind::InvalidEnumExpression)) + Err(("a=~", PErrorKind::Invalid("enum expression"))) ); assert_eq!( map_res(penum_expression, "a=~b|(c=~d"), @@ -775,10 +775,10 @@ fn test_pcomplex_value() { PComplexValue { source: src.into(), cases: vec![( - PEnumExpression { + PExpression::Enum(PEnumExpression { source: "".into(), expression: PEnumExpressionPart::Default("".into()), - }, + }), Some(PValue::String( "\"\"\"".into(), "This is a string".to_string() @@ -796,17 +796,17 @@ fn test_pcomplex_value() { source: src.into(), cases: vec![ ( - PEnumExpression { + PExpression::Enum(PEnumExpression { source: "debian9 ".into(), expression: PEnumExpressionPart::Compare(None, None, "debian9".into()), - }, + }), Some(PValue::String("\"".into(), "str".to_string())) ), ( - PEnumExpression { + PExpression::Enum(PEnumExpression { source: "debian10 ".into(), expression: PEnumExpressionPart::Compare(None, None, "debian10".into()), - }, + }), Some(PValue::String("\"".into(), "str10".to_string())) ), ] @@ -1338,11 +1338,11 @@ fn test_pstatement() { "case".into(), vec![ ( - map_res(penum_expression, "ubuntu ").unwrap().1, + map_res(pexpression, "ubuntu ").unwrap().1, vec![map_res(pstatement, "f().g()").unwrap().1] ), ( - map_res(penum_expression, "debian ").unwrap().1, + map_res(pexpression, "debian ").unwrap().1, vec![map_res(pstatement, "a().b() ").unwrap().1] ), ]