diff --git a/resources/optional_labels.lp b/resources/optional_labels.lp index 003c11c..c00ff9b 100644 --- a/resources/optional_labels.lp +++ b/resources/optional_labels.lp @@ -3,7 +3,7 @@ Minimize x + 10 y Subject To -r01: x + y >= 1 +-x + 2y >= 1 Binaries x y z a diff --git a/src/lp_file_format.pest b/src/lp_file_format.pest index f7d3eb7..89aa808 100644 --- a/src/lp_file_format.pest +++ b/src/lp_file_format.pest @@ -97,7 +97,7 @@ LTE = { "<=" } EQ = { "=" } CMP = _{ GTE | GT | LTE | LT | EQ } CONSTRAINT_EXPR = { OPERATOR? ~ FLOAT? ~ VARIABLE | OPERATOR? ~ FLOAT } -CONSTRAINT_NAME = ${ VALID_CHARS{1, 255} } +CONSTRAINT_NAME = ${ VALID_CHARS{1, 255} ~ COLON } CONSTRAINT = { NEWLINE* ~ (CONSTRAINT_NAME ~ COLON*)? ~ CONSTRAINT_EXPR ~ (NEWLINE? ~ OPERATOR ~ CONSTRAINT_EXPR)* ~ NEWLINE? ~ CMP ~ FLOAT } diff --git a/src/lp_parts.rs b/src/lp_parts.rs index 93f2c9a..6335c80 100644 --- a/src/lp_parts.rs +++ b/src/lp_parts.rs @@ -23,9 +23,14 @@ fn compose_objective(pair: Pair<'_, Rule>, gen: &mut ShortCodeGenerator) - } #[allow(clippy::unwrap_used)] -fn compose_constraint(pair: Pair<'_, Rule>) -> anyhow::Result { - let mut parts = pair.into_inner(); - let name = parts.next().unwrap().as_str().to_string(); +fn compose_constraint(pair: Pair<'_, Rule>, gen: &mut ShortCodeGenerator) -> anyhow::Result { + let mut parts = pair.into_inner().peekable(); + // Constraint name can be omitted in LP files, so we need to handle that case + let name = if parts.peek().unwrap().as_rule() == Rule::CONSTRAINT_NAME { + parts.next().unwrap().as_str().to_string() + } else { + format!("con_{}", gen.next_string()) + }; let mut coefficients: Vec<_> = vec![]; while let Some(p) = parts.peek() { if p.as_rule().is_cmp() { @@ -33,7 +38,8 @@ fn compose_constraint(pair: Pair<'_, Rule>) -> anyhow::Result { } coefficients.push(parts.next().unwrap()); } - let coefficients: anyhow::Result> = coefficients.into_iter().map(|p| p.into_inner().try_into()).collect(); + let coefficients: anyhow::Result> = + coefficients.into_iter().filter(|p| !matches!(p.as_rule(), Rule::PLUS | Rule::MINUS)).map(|p| p.into_inner().try_into()).collect(); let sense = parts.next().unwrap().as_str().to_string(); let rhs = parts.next().unwrap().as_str().parse()?; Ok(Constraint::Standard { name, coefficients: coefficients?, sense, rhs }) @@ -105,7 +111,8 @@ pub fn compose(pair: Pair<'_, Rule>, mut parsed: LPProblem, gen: &mut ShortCodeG } // Problem Constraints Rule::CONSTRAINTS => { - let constraints: anyhow::Result> = pair.into_inner().map(|inner_pair| compose_constraint(inner_pair)).collect(); + let constraints: anyhow::Result> = + pair.into_inner().map(|inner_pair| compose_constraint(inner_pair, gen)).collect(); parsed.add_constraints(constraints?); } Rule::SOS => { diff --git a/tests/test_from_file.rs b/tests/test_from_file.rs index b6c854e..daf1572 100644 --- a/tests/test_from_file.rs +++ b/tests/test_from_file.rs @@ -11,23 +11,23 @@ macro_rules! generate_test { #[test] fn $test_name() { let result = read_file_from_resources($file).unwrap(); - // dbg!(&result); + dbg!(&result); assert_eq!(result.problem_sense, Sense::$sense); - assert_eq!(result.objectives.len(), $obj_len); - assert_eq!(result.constraints.len(), $con_len); - assert_eq!(result.variables.len(), $var_len); + assert_eq!(result.objectives.len(), $obj_len, "Failed Objective Count"); + assert_eq!(result.constraints.len(), $con_len, "Failed Constraint Count"); + assert_eq!(result.variables.len(), $var_len, "Failed Variable Count"); } }; ($test_name:ident, $file:expr, $name:expr, $sense:ident, $obj_len:expr, $con_len:expr, $var_len:expr) => { #[test] fn $test_name() { let result = read_file_from_resources($file).unwrap(); - // dbg!(&result); + dbg!(&result); assert_eq!($name, result.problem_name); assert_eq!(result.problem_sense, Sense::$sense); - assert_eq!(result.objectives.len(), $obj_len); - assert_eq!(result.constraints.len(), $con_len); - assert_eq!(result.variables.len(), $var_len); + assert_eq!(result.objectives.len(), $obj_len, "Failed Objective Count"); + assert_eq!(result.constraints.len(), $con_len, "Failed Constraint Count"); + assert_eq!(result.variables.len(), $var_len, "Failed Variable Count"); } }; }