diff --git a/Cargo.toml b/Cargo.toml index 0c7b37f..694d191 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,10 +11,3 @@ keywords = ["logic"] categories = ["data-structures", "science"] license = "MIT" edition = "2018" - -[dependencies] -nom = {version = ">=6.0.0-alpha1, <6.1", optional = true} - -[features] -default = ["parse"] -parse = ["nom"] diff --git a/src/lib.rs b/src/lib.rs index 3aea966..1f131f1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,5 @@ mod ast; pub mod format; -#[cfg(feature = "parse")] pub mod parse; mod utils; diff --git a/src/parse.rs b/src/parse.rs index 7d32868..99c7584 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -1,4 +1,4 @@ -mod formulas; +/*mod formulas; mod helpers; mod literals; mod names; @@ -9,4 +9,10 @@ pub(crate) use literals::{boolean, integer, special_integer, string}; pub use names::function_or_predicate_name; pub(crate) use names::variable_name; pub use terms::term; -pub use formulas::formula; +pub use formulas::formula;*/ + +pub mod error; +pub mod formulas; +pub mod tokens; + +pub use error::Error; diff --git a/src/parse/error.rs b/src/parse/error.rs new file mode 100644 index 0000000..fbf13e4 --- /dev/null +++ b/src/parse/error.rs @@ -0,0 +1,129 @@ +pub type Source = Box; + +pub struct Location +{ + start: usize, + end: Option, +} + +impl Location +{ + pub fn new(start: usize, end: Option) -> Self + { + Self + { + start, + end, + } + } +} + +pub enum Kind +{ + UnmatchedParenthesis, + CharacterNotAllowed(char), + ParseNumber(String), + MixedImplicationDirections(Location), +} + +pub struct Error +{ + pub kind: Kind, + pub location: Location, + pub source: Option, +} + +impl Error +{ + pub(crate) fn new(kind: Kind, location: Location) -> Self + { + Self + { + kind, + location, + source: None, + } + } + + pub(crate) fn with>(mut self, source: S) -> Self + { + self.source = Some(source.into()); + self + } + + pub(crate) fn new_unmatched_parenthesis(location: Location) -> Self + { + Self::new(Kind::UnmatchedParenthesis, location) + } + + pub(crate) fn new_character_not_allowed(character: char, location: Location) -> Self + { + Self::new(Kind::CharacterNotAllowed(character), location) + } + + pub(crate) fn new_parse_number, S: Into>(input: I, location: Location, + source: S) + -> Self + { + Self::new(Kind::ParseNumber(input.into()), location).with(source) + } + + pub(crate) fn new_mixed_implication_directions(location_1: Location, location_2: Location) + -> Self + { + Self::new(Kind::MixedImplicationDirections(location_2), location_1) + } +} + +impl std::fmt::Debug for Error +{ + fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result + { + if let Some(end) = self.location.end + { + write!(formatter, "parsing error at {}:{}: ", self.location.start, end)?; + } + else + { + write!(formatter, "parsing error at {}: ", self.location.start)?; + } + + match &self.kind + { + Kind::UnmatchedParenthesis => write!(formatter, "unmatched parenthesis")?, + Kind::CharacterNotAllowed(character) => + write!(formatter, "character not allowed: ‘{}’", character)?, + Kind::ParseNumber(input) => write!(formatter, "could not “{}” as number", input)?, + // TODO: print second location properly + Kind::MixedImplicationDirections(_location_2) => + write!(formatter, "-> and <- implications may not be mixed within the same scope")?, + } + + if let Some(source) = &self.source + { + write!(formatter, "\nerror source: {}", source)?; + } + + Ok(()) + } +} + +impl std::fmt::Display for Error +{ + fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result + { + write!(formatter, "{:?}", self) + } +} + +impl std::error::Error for Error +{ + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> + { + match &self.source + { + Some(source) => Some(source.as_ref()), + None => None, + } + } +} diff --git a/src/parse/formulas.rs b/src/parse/formulas.rs index b1f1699..e0acb04 100644 --- a/src/parse/formulas.rs +++ b/src/parse/formulas.rs @@ -1,740 +1,386 @@ -use nom:: -{ - IResult, - branch::alt, - bytes::complete::tag, - character::complete::multispace0, - combinator::{cut, map, map_res, peek}, - multi::{many1, separated_list1}, - sequence::{delimited, pair, preceded, terminated, tuple}, -}; +use super::tokens::*; -use super::{boolean, word_boundary}; - -pub fn predicate<'i, 'v, D>(i: &'i str, d: &D, v: &'v crate::VariableDeclarationStackLayer) - -> IResult<&'i str, crate::Predicate> -where - D: crate::FindOrCreateFunctionDeclaration + crate::FindOrCreatePredicateDeclaration +pub fn parse_formula(input: &str) -> Result { - map - ( - |i| crate::parse::terms::function_or_predicate(i, d, v), - |(name, arguments)| + let formula_str = FormulaStr::new(input); + formula_str.parse(0)?; + + // TODO: implement correctly + Ok(crate::Formula::true_()) +} + +#[derive(Clone, Copy, Eq, PartialEq)] +enum FormulaInfixOperator +{ + And, + IfAndOnlyIf, + ImpliesLeftToRight, + ImpliesRightToLeft, + Or, +} + +impl FormulaInfixOperator +{ + fn level(&self) -> usize + { + match self { - let arguments = match arguments + Self::And => 1, + Self::Or => 2, + Self::ImpliesLeftToRight + | Self::ImpliesRightToLeft => 3, + Self::IfAndOnlyIf => 4, + } + } +} + +impl std::fmt::Debug for FormulaInfixOperator +{ + fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result + { + match &self + { + Self::And => write!(formatter, "and"), + Self::IfAndOnlyIf => write!(formatter, "<->"), + Self::ImpliesLeftToRight => write!(formatter, "->"), + Self::ImpliesRightToLeft => write!(formatter, "<-"), + Self::Or => write!(formatter, "or"), + } + } +} + +struct FormulaStr<'i> +{ + input: &'i str, +} + +impl<'i> FormulaStr<'i> +{ + pub fn new(input: &'i str) -> Self + { + Self + { + input, + } + } + + pub fn top_level_infix_operator(&self) + -> Result, crate::parse::Error> + { + let mut top_level_infix_operator = None; + + for infix_operator in self.iter_infix_operators() + { + let (_, _, infix_operator) = infix_operator?; + + top_level_infix_operator = match top_level_infix_operator { - Some(arguments) => arguments, - None => vec![], + None => Some(infix_operator), + Some(top_level_infix_operator) => + { + if (infix_operator == FormulaInfixOperator::ImpliesLeftToRight + && top_level_infix_operator == FormulaInfixOperator::ImpliesRightToLeft) + || (infix_operator == FormulaInfixOperator::ImpliesRightToLeft + && top_level_infix_operator == FormulaInfixOperator::ImpliesLeftToRight) + { + return Err(crate::parse::Error::new_mixed_implication_directions( + crate::parse::error::Location::new(0, Some(0)), + crate::parse::error::Location::new(0, Some(0)))); + } + + if infix_operator.level() > top_level_infix_operator.level() + { + Some(infix_operator) + } + else + { + Some(top_level_infix_operator) + } + }, + } + } + + Ok(top_level_infix_operator) + } + + pub fn iter_infix_operators(&self) -> FormulaInfixOperatorIterator<'i> + { + FormulaInfixOperatorIterator::new(self.input) + } + + pub fn split_at_infix_operator(&self, infix_operator: FormulaInfixOperator) + -> SplitFormulaAtInfixOperator<'i> + { + SplitFormulaAtInfixOperator::new(self, infix_operator) + } + + pub fn parse(&self, level: usize) -> Result<(), crate::parse::Error> + { + let indentation = " ".repeat(level); + println!("{}- parsing: {}", indentation, self.input); + + let input = self.input.trim_start(); + + match self.top_level_infix_operator()? + { + None => + { + if let Some((identifier, _)) = identifier(input) + { + match identifier + { + "exists" => println!("{} parsing “exists” expression from: {}", indentation, input), + "forall" => println!("{} parsing “forall” expression from: {}", indentation, input), + _ => (), + } + } + + println!("{} can’t break down any further: {}", indentation, input) + }, + Some(top_level_infix_operator) => + { + println!("{} parsing “{:?}” expression from: {}", indentation, + top_level_infix_operator, input); + + for subformula in self.split_at_infix_operator(top_level_infix_operator) + { + FormulaStr::new(subformula?).parse(level + 1)?; + } + }, + } + + Ok(()) + } +} + +struct FormulaInfixOperatorIterator<'i> +{ + original_input: &'i str, + input: &'i str, +} + +impl<'i> FormulaInfixOperatorIterator<'i> +{ + pub fn new(input: &'i str) -> Self + { + Self + { + original_input: input, + input, + } + } +} + +impl<'i> std::iter::Iterator for FormulaInfixOperatorIterator<'i> +{ + type Item = Result<(&'i str, &'i str, FormulaInfixOperator), crate::parse::Error>; + + fn next(&mut self) -> Option + { + loop + { + self.input = self.input.trim_start(); + + let first_character = match self.input.chars().next() + { + None => return None, + Some(first_character) => first_character, }; - let declaration = d.find_or_create_predicate_declaration(name, arguments.len()); - - crate::Predicate::new(declaration, arguments) - }, - )(i) -} - -fn not<'i, 'v, D>(i: &'i str, d: &D, v: &'v crate::VariableDeclarationStackLayer) - -> IResult<&'i str, crate::Formula> -where - D: crate::FindOrCreateFunctionDeclaration + crate::FindOrCreatePredicateDeclaration -{ - map - ( - preceded - ( - terminated - ( - tag("not"), - multispace0, - ), - |i| formula_precedence_2(i, d, v), - ), - |x| crate::Formula::not(Box::new(x)), - )(i) -} - -fn and<'i, 'v, D>(i: &'i str, d: &D, v: &'v crate::VariableDeclarationStackLayer) - -> IResult<&'i str, crate::Formulas> -where - D: crate::FindOrCreateFunctionDeclaration + crate::FindOrCreatePredicateDeclaration -{ - map_res - ( - separated_list1 - ( - delimited - ( - multispace0, - terminated - ( - tag("and"), - word_boundary, - ), - multispace0, - ), - |i| formula_precedence_2(i, d, v), - ), - |arguments| -> Result<_, (_, _)> - { - if arguments.len() >= 2 + if self.input.starts_with(")") { - Ok(arguments.into_iter().collect()) + return Some(Err(crate::parse::Error::new_unmatched_parenthesis( + crate::parse::error::Location::new(0, Some(1))))); } - else + + match parenthesized_expression(self.input) { - // TODO: return more appropriate error type - Err(nom::error::make_error(i, nom::error::ErrorKind::Many1)) + Ok(Some((_, remaining_input))) => + { + self.input = remaining_input; + continue; + }, + Ok(None) => (), + Err(error) => return Some(Err(error)), + } + + match number(self.input) + { + Ok(Some((_, remaining_input))) => + { + self.input = remaining_input; + continue; + } + Ok(None) => (), + Err(error) => return Some(Err(error)), + } + + let index_left = self.input.as_ptr() as usize - self.original_input.as_ptr() as usize; + let input_left = self.original_input.split_at(index_left).0.trim_end(); + + if let Some((identifier, remaining_input)) = identifier(self.input) + { + self.input = remaining_input; + + match identifier + { + "and" => + return Some(Ok((input_left, remaining_input, FormulaInfixOperator::And))), + "or" => + return Some(Ok((input_left, remaining_input, FormulaInfixOperator::Or))), + _ => continue, + } + } + + if let Some((symbol, remaining_input)) = symbol(self.input) + { + self.input = remaining_input; + + match symbol + { + Symbol::ArrowLeft => return Some(Ok((input_left, remaining_input, + FormulaInfixOperator::ImpliesRightToLeft))), + Symbol::ArrowLeftAndRight => return Some(Ok((input_left, remaining_input, + FormulaInfixOperator::IfAndOnlyIf))), + Symbol::ArrowRight => return Some(Ok((input_left, remaining_input, + FormulaInfixOperator::ImpliesLeftToRight))), + _ => continue, + } + } + + return Some(Err(crate::parse::Error::new_character_not_allowed(first_character, + crate::parse::error::Location::new(0, Some(0))))); + } + } +} + +struct SplitFormulaAtInfixOperator<'i> +{ + infix_operator_iterator: FormulaInfixOperatorIterator<'i>, + infix_operator: FormulaInfixOperator, + previous_index: usize, +} + +impl<'i> SplitFormulaAtInfixOperator<'i> +{ + pub fn new(input: &FormulaStr<'i>, infix_operator: FormulaInfixOperator) + -> Self + { + Self + { + infix_operator_iterator: input.iter_infix_operators(), + infix_operator, + previous_index: 0, + } + } +} + +impl<'i> std::iter::Iterator for SplitFormulaAtInfixOperator<'i> +{ + type Item = Result<&'i str, crate::parse::Error>; + + fn next(&mut self) -> Option + { + loop + { + let (input_left, input_right, infix_operator) = + match self.infix_operator_iterator.next() + { + Some(Err(error)) => return Some(Err(error)), + Some(Ok(infix_operator_iterator_next)) => infix_operator_iterator_next, + None => break, + }; + + if infix_operator == self.infix_operator + { + // TODO: refactor + let index = input_left.as_ptr() as usize + + input_left.len() + - self.infix_operator_iterator.original_input.as_ptr() as usize; + let split_input = &self.infix_operator_iterator + .original_input[self.previous_index..index].trim(); + self.previous_index = input_right.as_ptr() as usize + - self.infix_operator_iterator.original_input.as_ptr() as usize; + + return Some(Ok(split_input)); } } - )(i) -} -fn or<'i, 'v, D>(i: &'i str, d: &D, v: &'v crate::VariableDeclarationStackLayer) - -> IResult<&'i str, crate::Formulas> -where - D: crate::FindOrCreateFunctionDeclaration + crate::FindOrCreatePredicateDeclaration -{ - map_res - ( - separated_list1 - ( - delimited - ( - multispace0, - terminated - ( - tag("or"), - word_boundary, - ), - multispace0, - ), - |i| formula_precedence_3(i, d, v), - ), - |arguments| -> Result<_, (_, _)> + let remaining_input = self.infix_operator_iterator + .original_input[self.previous_index..].trim(); + + if remaining_input.is_empty() { - if arguments.len() >= 2 - { - Ok(arguments.into_iter().collect()) - } - else - { - // TODO: return more appropriate error type - Err(nom::error::make_error(i, nom::error::ErrorKind::Many1)) - } + None } - )(i) -} - -fn implies_left_to_right<'i, 'v, D>(i: &'i str, d: &D, v: &'v crate::VariableDeclarationStackLayer) - -> IResult<&'i str, crate::Formula> -where - D: crate::FindOrCreateFunctionDeclaration + crate::FindOrCreatePredicateDeclaration -{ - map - ( - pair - ( - many1 - ( - terminated - ( - |i| formula_precedence_4(i, d, v), - delimited - ( - multispace0, - tag("->"), - multispace0, - ), - ) - ), - |i| formula_precedence_4(i, d, v), - ), - |(arguments, last_argument)| arguments.into_iter().rev().fold(last_argument, - |accumulator, argument| - crate::Formula::implies(crate::ImplicationDirection::LeftToRight, - Box::new(argument), Box::new(accumulator))) - )(i) -} - -fn implies_right_to_left<'i, 'v, D>(i: &'i str, d: &D, v: &'v crate::VariableDeclarationStackLayer) - -> IResult<&'i str, crate::Formula> -where - D: crate::FindOrCreateFunctionDeclaration + crate::FindOrCreatePredicateDeclaration -{ - map - ( - pair - ( - |i| formula_precedence_4(i, d, v), - many1 - ( - preceded - ( - delimited - ( - multispace0, - tag("<-"), - multispace0, - ), - |i| formula_precedence_4(i, d, v), - ) - ), - ), - |(first_argument, arguments)| arguments.into_iter().fold(first_argument, - |accumulator, argument| - crate::Formula::implies(crate::ImplicationDirection::RightToLeft, - Box::new(argument), Box::new(accumulator))) - )(i) -} - -fn if_and_only_if<'i, 'v, D>(i: &'i str, d: &D, v: &'v crate::VariableDeclarationStackLayer) - -> IResult<&'i str, crate::Formulas> -where - D: crate::FindOrCreateFunctionDeclaration + crate::FindOrCreatePredicateDeclaration -{ - map_res - ( - separated_list1 - ( - delimited - ( - multispace0, - tag("<->"), - multispace0, - ), - |i| formula_precedence_5(i, d, v), - ), - |arguments| -> Result<_, (_, _)> + else { - if arguments.len() >= 2 - { - Ok(arguments.into_iter().collect()) - } - else - { - // TODO: return more appropriate error type - Err(nom::error::make_error(i, nom::error::ErrorKind::Many1)) - } + self.previous_index = self.infix_operator_iterator.original_input.len(); + + Some(Ok(remaining_input)) } - )(i) -} - -fn quantified_formula<'i, 'b, 'v, D>(i: &'i str, d: &D, v: &'v crate::VariableDeclarationStackLayer, - keyword: &'b str) - -> IResult<&'i str, crate::QuantifiedFormula> -where - D: crate::FindOrCreateFunctionDeclaration + crate::FindOrCreatePredicateDeclaration -{ - preceded - ( - terminated - ( - tag(keyword), - word_boundary, - ), - cut - ( - |i| - { - let (i, variable_declarations) = - map - ( - delimited - ( - multispace0, - separated_list1 - ( - delimited - ( - multispace0, - tag(","), - multispace0, - ), - map - ( - crate::parse::terms::variable_declaration, - std::rc::Rc::new, - ), - ), - multispace0, - ), - std::rc::Rc::new, - )(i)?; - - let bound_variable_declarations = crate::VariableDeclarationStackLayer::bound(v, - std::rc::Rc::clone(&variable_declarations)); - - let (i, argument) = formula_precedence_2(i, d, &bound_variable_declarations)?; - - Ok((i, crate::QuantifiedFormula::new(variable_declarations, Box::new(argument)))) - } - ), - )(i) -} - -fn compare<'i, 'v, D>(i: &'i str, d: &D, v: &'v crate::VariableDeclarationStackLayer) - -> IResult<&'i str, crate::Compare> -where - D: crate::FindOrCreateFunctionDeclaration + crate::FindOrCreatePredicateDeclaration -{ - map - ( - tuple - (( - |i| crate::parse::term(i, d, v), - delimited - ( - multispace0, - terminated - ( - alt - (( - map - ( - tag("<="), - |_| crate::ComparisonOperator::LessOrEqual, - ), - map - ( - tag(">="), - |_| crate::ComparisonOperator::GreaterOrEqual, - ), - map - ( - tag("<"), - |_| crate::ComparisonOperator::Less, - ), - map - ( - tag(">"), - |_| crate::ComparisonOperator::Greater, - ), - map - ( - tag("!="), - |_| crate::ComparisonOperator::NotEqual, - ), - map - ( - tag("="), - |_| crate::ComparisonOperator::Equal, - ), - )), - peek(nom::combinator::not(tag("-"))), - ), - multispace0, - ), - |i| crate::parse::term(i, d, v), - )), - |(left, operator, right)| - { - crate::Compare::new(operator, Box::new(left), Box::new(right)) - } - )(i) -} - -fn exists<'i, 'v, D>(i: &'i str, d: &D, v: &'v crate::VariableDeclarationStackLayer) - -> IResult<&'i str, crate::QuantifiedFormula> -where - D: crate::FindOrCreateFunctionDeclaration + crate::FindOrCreatePredicateDeclaration -{ - quantified_formula(i, d, v, "exists") -} - -fn for_all<'i, 'v, D>(i: &'i str, d: &D, v: &'v crate::VariableDeclarationStackLayer) - -> IResult<&'i str, crate::QuantifiedFormula> -where - D: crate::FindOrCreateFunctionDeclaration + crate::FindOrCreatePredicateDeclaration -{ - quantified_formula(i, d, v, "forall") -} - -fn formula_parenthesized<'i, 'v, D>(i: &'i str, d: &D, v: &'v crate::VariableDeclarationStackLayer) - -> IResult<&'i str, crate::Formula> -where - D: crate::FindOrCreateFunctionDeclaration + crate::FindOrCreatePredicateDeclaration -{ - delimited - ( - terminated - ( - tag("("), - multispace0, - ), - |i| formula(i, d, v), - preceded - ( - multispace0, - tag(")"), - ), - )(i) -} - -fn formula_precedence_0<'i, 'v, D>(i: &'i str, d: &D, v: &'v crate::VariableDeclarationStackLayer) - -> IResult<&'i str, crate::Formula> -where - D: crate::FindOrCreateFunctionDeclaration + crate::FindOrCreatePredicateDeclaration -{ - alt - (( - map - ( - boolean, - crate::Formula::Boolean, - ), - map - ( - |i| compare(i, d, v), - crate::Formula::Compare, - ), - map - ( - |i| predicate(i, d, v), - crate::Formula::Predicate, - ), - |i| formula_parenthesized(i, d, v), - ))(i) -} - -fn formula_precedence_1<'i, 'v, D>(i: &'i str, d: &D, v: &'v crate::VariableDeclarationStackLayer) - -> IResult<&'i str, crate::Formula> -where - D: crate::FindOrCreateFunctionDeclaration + crate::FindOrCreatePredicateDeclaration -{ - alt - (( - map - ( - |i| exists(i, d, v), - crate::Formula::Exists, - ), - map - ( - |i| for_all(i, d, v), - crate::Formula::ForAll, - ), - |i| formula_precedence_0(i, d, v), - ))(i) -} - -fn formula_precedence_2<'i, 'v, D>(i: &'i str, d: &D, v: &'v crate::VariableDeclarationStackLayer) - -> IResult<&'i str, crate::Formula> -where - D: crate::FindOrCreateFunctionDeclaration + crate::FindOrCreatePredicateDeclaration -{ - alt - (( - |i| not(i, d, v), - |i| formula_precedence_1(i, d, v), - ))(i) -} - -fn formula_precedence_3<'i, 'v, D>(i: &'i str, d: &D, v: &'v crate::VariableDeclarationStackLayer) - -> IResult<&'i str, crate::Formula> -where - D: crate::FindOrCreateFunctionDeclaration + crate::FindOrCreatePredicateDeclaration -{ - alt - (( - map - ( - |i| and(i, d, v), - crate::Formula::And, - ), - |i| formula_precedence_2(i, d, v), - ))(i) -} - -fn formula_precedence_4<'i, 'v, D>(i: &'i str, d: &D, v: &'v crate::VariableDeclarationStackLayer) - -> IResult<&'i str, crate::Formula> -where - D: crate::FindOrCreateFunctionDeclaration + crate::FindOrCreatePredicateDeclaration -{ - alt - (( - map - ( - |i| or(i, d, v), - crate::Formula::Or, - ), - |i| formula_precedence_3(i, d, v), - ))(i) -} - -fn formula_precedence_5<'i, 'v, D>(i: &'i str, d: &D, v: &'v crate::VariableDeclarationStackLayer) - -> IResult<&'i str, crate::Formula> -where - D: crate::FindOrCreateFunctionDeclaration + crate::FindOrCreatePredicateDeclaration -{ - alt - (( - |i| implies_left_to_right(i, d, v), - |i| implies_right_to_left(i, d, v), - |i| formula_precedence_4(i, d, v), - ))(i) -} - -fn formula_precedence_6<'i, 'v, D>(i: &'i str, d: &D, v: &'v crate::VariableDeclarationStackLayer) - -> IResult<&'i str, crate::Formula> -where - D: crate::FindOrCreateFunctionDeclaration + crate::FindOrCreatePredicateDeclaration -{ - alt - (( - map - ( - |i| if_and_only_if(i, d, v), - crate::Formula::IfAndOnlyIf, - ), - |i| formula_precedence_5(i, d, v), - ))(i) -} - -pub fn formula<'i, 'v, D>(i: &'i str, d: &D, v: &'v crate::VariableDeclarationStackLayer) - -> IResult<&'i str, crate::Formula> -where - D: crate::FindOrCreateFunctionDeclaration + crate::FindOrCreatePredicateDeclaration -{ - formula_precedence_6(i, d, v) + } } #[cfg(test)] mod tests { - use crate::{Formula, ImplicationDirection, Term, VariableDeclarationStackLayer}; - use crate::parse::formulas as original; - use crate::utils::*; - - fn formula(i: &str) -> Formula - { - original::formula(i, &Declarations::new(), &VariableDeclarationStackLayer::free()) - .unwrap().1 - } - - fn formula_remainder(i: &str) -> &str - { - original::formula(i, &Declarations::new(), &VariableDeclarationStackLayer::free()) - .unwrap().0 - } - - fn format_formula(i: &str) -> String - { - format!("{}", formula(i)) - } + use super::*; #[test] - fn parse_boolean() + fn tokenize_formula_infix_operators() { - assert_eq!(formula("true"), Formula::true_()); - assert_eq!(formula("false"), Formula::false_()); - } + let f = FormulaStr::new("((forall X exists Y (p(X) -> q(Y)) and false) or p) -> false"); + assert_eq!(f.top_level_infix_operator().unwrap(), + Some(FormulaInfixOperator::ImpliesLeftToRight)); + let mut i = f.iter_infix_operators(); + assert_eq!(i.next().unwrap().unwrap().2, FormulaInfixOperator::ImpliesLeftToRight); + assert!(i.next().is_none()); - #[test] - fn parse_precedence() - { - assert_eq!(format_formula("a -> b -> c <-> d -> e -> f"), "a -> b -> c <-> d -> e -> f"); - assert_eq!(format_formula("(a -> b -> c) <-> (d -> e -> f)"), "a -> b -> c <-> d -> e -> f"); - assert_eq!(format_formula("a <- b <- c <-> d <- e <- f"), "a <- b <- c <-> d <- e <- f"); - assert_eq!(format_formula("(a <- b <- c) <-> (d <- e <- f)"), "a <- b <- c <-> d <- e <- f"); - } + let f = FormulaStr::new("forall X exists Y (p(X) -> q(Y)) and false or p -> false"); + assert_eq!(f.top_level_infix_operator().unwrap(), + Some(FormulaInfixOperator::ImpliesLeftToRight)); + let mut i = f.iter_infix_operators(); + assert_eq!(i.next().unwrap().unwrap().2, FormulaInfixOperator::And); + assert_eq!(i.next().unwrap().unwrap().2, FormulaInfixOperator::Or); + assert_eq!(i.next().unwrap().unwrap().2, FormulaInfixOperator::ImpliesLeftToRight); + assert!(i.next().is_none()); - #[test] - fn parse_compare() - { - assert_eq!(format_formula("X>=0."), "X >= 0"); - assert_eq!(format_formula("N>=0."), "N >= 0"); - assert_eq!(format_formula("n<0."), "n < 0"); - assert_eq!(format_formula("n>=0."), "n >= 0"); - assert_eq!(format_formula("p(0)>=q."), "p(0) >= q"); - } + let f = FormulaStr::new(" p -> forall X exists Y (p(X) -> q(Y)) and false or p -> false "); + assert_eq!(f.top_level_infix_operator().unwrap(), + Some(FormulaInfixOperator::ImpliesLeftToRight)); + let mut i = f.split_at_infix_operator(FormulaInfixOperator::ImpliesLeftToRight); + assert_eq!(i.next().unwrap().unwrap(), "p"); + assert_eq!(i.next().unwrap().unwrap(), "forall X exists Y (p(X) -> q(Y)) and false or p"); + assert_eq!(i.next().unwrap().unwrap(), "false"); + assert!(i.next().is_none()); - #[test] - fn parse_exists() - { - let formula_as_exists = |i| match formula(i) - { - Formula::Exists(exists) => exists, - _ => panic!("expected existentially quantified formula"), - }; + let f = FormulaStr::new(" p -> forall X exists Y (p(X) -> q(Y)) and false or p -> false "); + assert_eq!(f.top_level_infix_operator().unwrap(), + Some(FormulaInfixOperator::ImpliesLeftToRight)); + let mut i = f.split_at_infix_operator(FormulaInfixOperator::And); + assert_eq!(i.next().unwrap().unwrap(), "p -> forall X exists Y (p(X) -> q(Y))"); + assert_eq!(i.next().unwrap().unwrap(), "false or p -> false"); + assert!(i.next().is_none()); - let as_predicate = |x| match x - { - Formula::Predicate(arguments) => arguments, - _ => panic!("expected predicate"), - }; + let f = FormulaStr::new(" p and forall X exists Y (p(X) -> q(Y)) and false or p or false "); + assert_eq!(f.top_level_infix_operator().unwrap(), Some(FormulaInfixOperator::Or)); + let mut i = f.split_at_infix_operator(FormulaInfixOperator::Or); + assert_eq!(i.next().unwrap().unwrap(), "p and forall X exists Y (p(X) -> q(Y)) and false"); + assert_eq!(i.next().unwrap().unwrap(), "p"); + assert_eq!(i.next().unwrap().unwrap(), "false"); + assert!(i.next().is_none()); - assert_eq!(format_formula("exists X , Y , Z ( p )"), "exists X, Y, Z p"); - assert_eq!(formula_as_exists("exists X , Y , Z ( p )").parameters.len(), 3); - assert_eq!(as_predicate(*formula_as_exists("exists X , Y , Z ( p )").argument) - .declaration.name, "p"); - } + let f = FormulaStr::new(" (p and q) "); + assert!(f.top_level_infix_operator().unwrap().is_none()); + let mut i = f.split_at_infix_operator(FormulaInfixOperator::And); + assert_eq!(i.next().unwrap().unwrap(), "(p and q)"); + assert!(i.next().is_none()); - #[test] - fn parse_and() - { - let formula_as_and = |i| match formula(i) - { - Formula::And(arguments) => arguments, - _ => panic!("expected conjunction"), - }; + assert!(FormulaStr::new(" a -> b -> c ").parse(0).is_ok()); + assert!(FormulaStr::new(" a -> b <- c ").parse(0).is_err()); - let as_predicate = |x| match x - { - Formula::Predicate(arguments) => arguments, - _ => panic!("expected predicate"), - }; - - assert_eq!(format_formula("(true and false)"), "true and false"); - assert_eq!(formula_as_and("(true and false)").len(), 2); - assert_eq!(formula_as_and("(true and false)").remove(0), Formula::true_()); - assert_eq!(formula_as_and("(true and false)").remove(1), Formula::false_()); - // The order of elements is retained - assert_ne!(formula("(true and false)"), formula("false and true")); - assert_eq!(format_formula("(p and q and r and s)"), "p and q and r and s"); - assert_eq!( - as_predicate(formula_as_and("(p and q and r and s)").remove(0)).declaration.name, "p"); - assert_eq!( - as_predicate(formula_as_and("(p and q and r and s)").remove(3)).declaration.name, "s"); - - let formula = |i| original::formula(i, &Declarations::new(), - &VariableDeclarationStackLayer::free()); - - // Malformed formulas shouldn’t be accepted - assert!(formula("and").is_err()); - assert!(formula("and p").is_err()); - assert_eq!(formula_remainder("p and"), " and"); - assert_eq!(formula_remainder("p andq"), " andq"); - assert_eq!(formula_remainder("p and q and"), " and"); - assert_eq!(formula_remainder("p and q andq"), " andq"); - assert!(formula("(p and) q").is_err()); - assert_eq!(formula_remainder("p (and q)"), " (and q)"); - } - - #[test] - fn parse_or() - { - let formula_as_or = |i| match formula(i) - { - Formula::Or(arguments) => arguments, - _ => panic!("expected disjunction"), - }; - - let as_predicate = |x| match x - { - Formula::Predicate(arguments) => arguments, - _ => panic!("expected predicate"), - }; - - assert_eq!(format_formula("(true or false)"), "true or false"); - assert_eq!(formula_as_or("(true or false)").len(), 2); - assert_eq!(formula_as_or("(true or false)").remove(0), Formula::true_()); - assert_eq!(formula_as_or("(true or false)").remove(1), Formula::false_()); - // The order of elements is retained - assert_ne!(formula("(true or false)"), formula("false or true)")); - assert_eq!(format_formula("(p or q or r or s)"), "p or q or r or s"); - assert_eq!( - as_predicate(formula_as_or("(p or q or r or s)").remove(0)).declaration.name, "p"); - assert_eq!( - as_predicate(formula_as_or("(p or q or r or s)").remove(3)).declaration.name, "s"); - - let formula = |i| original::formula(i, &Declarations::new(), - &VariableDeclarationStackLayer::free()); - - // Malformed formulas shouldn’t be accepted - assert!(formula("or").is_err()); - assert!(formula("or p").is_err()); - assert_eq!(formula_remainder("p or"), " or"); - assert_eq!(formula_remainder("p orq"), " orq"); - assert_eq!(formula_remainder("p or q or"), " or"); - assert_eq!(formula_remainder("p or q orq"), " orq"); - assert!(formula("(p or) q").is_err()); - assert_eq!(formula_remainder("p (or q)"), " (or q)"); - } - - #[test] - fn parse_implies() - { - let formula_as_implies = |i| match formula(i) - { - Formula::Implies(implies) => implies, - _ => panic!("expected implication"), - }; - - let as_predicate = |x| match x - { - Formula::Predicate(arguments) => arguments, - _ => panic!("expected predicate"), - }; - - assert_eq!(as_predicate(*formula_as_implies("a -> b").antecedent).declaration.name, "a"); - assert_eq!(as_predicate(*formula_as_implies("a -> b").implication).declaration.name, "b"); - assert_eq!(formula_as_implies("a -> b").direction, ImplicationDirection::LeftToRight); - - assert_eq!(as_predicate(*formula_as_implies("a <- b").antecedent).declaration.name, "b"); - assert_eq!(as_predicate(*formula_as_implies("a <- b").implication).declaration.name, "a"); - assert_eq!(formula_as_implies("a <- b").direction, ImplicationDirection::RightToLeft); - - assert_eq!(format_formula("(a -> b -> c)"), "a -> b -> c"); - assert_eq!(format_formula("(a -> (b -> c))"), "a -> b -> c"); - assert_eq!(format_formula("((a -> b) -> c)"), "(a -> b) -> c"); - } - - #[test] - fn parse_predicate() - { - let predicate = |i| original::predicate(i, &Declarations::new(), - &VariableDeclarationStackLayer::free()).unwrap().1; - let predicate_remainder = |i| original::predicate(i, &Declarations::new(), - &VariableDeclarationStackLayer::free()).unwrap().0; - - assert_eq!(predicate("s").declaration.name, "s"); - assert_eq!(predicate("s").declaration.arity, 0); - assert_eq!(predicate_remainder("s"), ""); - assert_eq!(predicate("s ( )").declaration.name, "s"); - assert_eq!(predicate("s ( )").declaration.arity, 0); - assert_eq!(predicate_remainder("s ( )"), ""); - assert_eq!(predicate("s ( 1 , 2 , 3 )").declaration.name, "s"); - assert_eq!(predicate("s ( 1 , 2 , 3 )").declaration.arity, 3); - assert_eq!(predicate("s ( 1 , 2 , 3 )").arguments.remove(0), Term::integer(1)); - assert_eq!(predicate("s ( 1 , 2 , 3 )").arguments.remove(1), Term::integer(2)); - assert_eq!(predicate("s ( 1 , 2 , 3 )").arguments.remove(2), Term::integer(3)); - assert_eq!(predicate_remainder("s ( 1 , 2 , 3 )"), ""); - assert_eq!(predicate_remainder("s ( 1 , 2 , 3 )"), ""); - assert_eq!(predicate("s ( ), rest").declaration.name, "s"); - assert_eq!(predicate("s ( ), rest").declaration.arity, 0); - assert_eq!(predicate_remainder("s ( ), rest"), ", rest"); - assert_eq!(predicate("s ( 1 , 2 , 3 ), rest").declaration.name, "s"); - assert_eq!(predicate("s ( 1 , 2 , 3 ), rest").declaration.arity, 3); - assert_eq!(predicate_remainder("s ( 1 , 2 , 3 ), rest"), ", rest"); - } - - #[test] - fn parse_exists_primitive() - { - let exists = |i| original::exists(i, &Declarations::new(), - &VariableDeclarationStackLayer::free()); - - assert_eq!(exists("exists X (p(X, Y, X, Y)), rest") - .map(|(i, x)| (i, x.parameters.len())), - Ok((", rest", 1))); - assert_eq!(exists("exists X p(X, Y, X, Y), rest") - .map(|(i, x)| (i, x.parameters.len())), - Ok((", rest", 1))); - assert!(exists("exists (p(X, Y, X, Y)), rest").is_err()); - assert!(exists("exists X, rest").is_err()); - assert!(exists("exists X (), rest").is_err()); - assert!(exists("exists X (, true), rest").is_err()); - assert!(exists("exists X (true, ), rest").is_err()); - assert!(exists("exists X (true false), rest").is_err()); - assert!(exists("exists X (true), rest").is_ok()); - assert!(exists("exists X p(X), rest").is_ok()); - } - - #[test] - fn parse_formula() - { - // TODO: refactor - formula("exists X, Y (p(X, Y, X, Y) and X < Y and q(X) <-> r(X)), rest"); + assert!(!FormulaStr::new(" p -> forall X exists Y (p(X) -> q(Y)) and false or p -> false ") + .parse(0).is_ok()); } } diff --git a/src/parse/helpers.rs b/src/parse/helpers.rs deleted file mode 100644 index ab1441f..0000000 --- a/src/parse/helpers.rs +++ /dev/null @@ -1,87 +0,0 @@ -use nom:: -{ - IResult, - branch::alt, - bytes::complete::take_while_m_n, - combinator::{map, peek, rest_len, verify}, -}; - -fn is_character_word_boundary(c: char) -> bool -{ - if c.is_whitespace() - { - return true; - } - - match c - { - '(' - | ')' - | '>' - | '<' - | '=' - | ',' - | '+' - | '-' - | '*' - | '/' - | '%' - | '|' - | '#' - | '.' - => true, - _ => false, - } -} - -pub(crate) fn word_boundary(i: &str) -> IResult<&str, ()> -{ - peek - ( - alt - (( - // Accept word boundary characters - map - ( - take_while_m_n(1, 1, is_character_word_boundary), - |_| (), - ), - // Accept end of file - map - ( - verify - ( - rest_len, - |rest_length| *rest_length == 0usize, - ), - |_| (), - ), - )) - )(i) -} - -#[cfg(test)] -mod tests -{ - use crate::parse::*; - - #[test] - fn detect_word_boundaries() - { - assert_eq!(word_boundary(" rest"), Ok((" rest", ()))); - assert_eq!(word_boundary("(rest"), Ok(("(rest", ()))); - assert_eq!(word_boundary(")rest"), Ok((")rest", ()))); - assert_eq!(word_boundary(",rest"), Ok((",rest", ()))); - assert_eq!(word_boundary("+rest"), Ok(("+rest", ()))); - assert_eq!(word_boundary("-rest"), Ok(("-rest", ()))); - assert_eq!(word_boundary("*rest"), Ok(("*rest", ()))); - assert_eq!(word_boundary("/rest"), Ok(("/rest", ()))); - assert_eq!(word_boundary("%rest"), Ok(("%rest", ()))); - assert_eq!(word_boundary("|rest"), Ok(("|rest", ()))); - assert_eq!(word_boundary("rest"), Ok((">rest", ()))); - assert_eq!(word_boundary("=rest"), Ok(("=rest", ()))); - assert!(word_boundary("0").is_err()); - assert!(word_boundary("rest").is_err()); - } -} diff --git a/src/parse/literals.rs b/src/parse/literals.rs deleted file mode 100644 index afb201e..0000000 --- a/src/parse/literals.rs +++ /dev/null @@ -1,249 +0,0 @@ -use nom:: -{ - IResult, - branch::alt, - bytes::complete::{escaped_transform, tag}, - character::complete::{digit1, none_of}, - combinator::{map, map_res, opt, recognize}, - sequence::{delimited, pair, terminated}, -}; - -use super::word_boundary; - -fn true_(i: &str) -> IResult<&str, bool> -{ - map - ( - terminated - ( - tag("true"), - word_boundary, - ), - |_| true, - )(i) -} - -fn false_(i: &str) -> IResult<&str, bool> -{ - map - ( - terminated - ( - tag("false"), - word_boundary, - ), - |_| false, - )(i) -} - -pub fn boolean(i: &str) -> IResult<&str, bool> -{ - alt - (( - true_, - false_, - ))(i) -} - -pub fn integer(i: &str) -> IResult<&str, i32> -{ - map_res - ( - recognize - ( - terminated - ( - pair - ( - opt - ( - alt - (( - tag("-"), - tag("+"), - )) - ), - digit1, - ), - word_boundary, - ) - ), - std::str::FromStr::from_str, - )(i) -} - -fn infimum(i: &str) -> IResult<&str, crate::SpecialInteger> -{ - map - ( - terminated - ( - tag("#inf"), - word_boundary, - ), - |_| crate::SpecialInteger::Infimum, - )(i) -} - -fn supremum(i: &str) -> IResult<&str, crate::SpecialInteger> -{ - map - ( - terminated - ( - tag("#sup"), - word_boundary, - ), - |_| crate::SpecialInteger::Supremum, - )(i) -} - -pub fn special_integer(i: &str) -> IResult<&str, crate::SpecialInteger> -{ - alt - (( - infimum, - supremum, - ))(i) -} - -pub fn string(i: &str) -> IResult<&str, String> -{ - map - ( - terminated - ( - delimited - ( - tag("\""), - escaped_transform - ( - none_of("\"\\"), - '\\', - alt - (( - tag("\""), - tag("\\"), - map - ( - tag("n"), - |_| "\n", - ), - map - ( - tag("t"), - |_| "\t", - ), - )), - ), - tag("\""), - ), - word_boundary, - ), - String::from, - )(i) -} - -#[cfg(test)] -mod tests -{ - use crate::SpecialInteger; - use crate::parse::*; - - #[test] - fn parse_boolean() - { - assert_eq!(boolean("true"), Ok(("", true))); - assert_eq!(boolean("false"), Ok(("", false))); - assert_eq!(boolean("true false"), Ok((" false", true))); - assert_eq!(boolean("false true"), Ok((" true", false))); - assert_eq!(boolean("true,"), Ok((",", true))); - assert_eq!(boolean("false,"), Ok((",", false))); - assert!(boolean("truefalse").is_err()); - assert!(boolean("falsetrue").is_err()); - assert!(boolean("truea").is_err()); - assert!(boolean("falsea").is_err()); - assert!(boolean("a").is_err()); - assert!(boolean("-").is_err()); - assert!(boolean(" ").is_err()); - } - - #[test] - fn parse_integer() - { - assert_eq!(integer("0"), Ok(("", 0))); - assert_eq!(integer("10000"), Ok(("", 10000))); - assert_eq!(integer("+10000"), Ok(("", 10000))); - assert_eq!(integer("-10000"), Ok(("", -10000))); - assert_eq!(integer("0 42"), Ok((" 42", 0))); - assert_eq!(integer("10000 42"), Ok((" 42", 10000))); - assert_eq!(integer("+10000 42"), Ok((" 42", 10000))); - assert_eq!(integer("-10000 42"), Ok((" 42", -10000))); - assert_eq!(integer("10000,"), Ok((",", 10000))); - assert_eq!(integer("+10000,"), Ok((",", 10000))); - assert_eq!(integer("-10000,"), Ok((",", -10000))); - assert!(integer("10000a").is_err()); - assert!(integer("+10000a").is_err()); - assert!(integer("-10000a").is_err()); - assert_eq!(integer("1.5"), Ok((".5", 1))); - assert!(integer("a").is_err()); - assert!(integer("-").is_err()); - assert!(integer(" ").is_err()); - } - - #[test] - fn parse_special_integer() - { - assert_eq!(special_integer("#inf"), Ok(("", SpecialInteger::Infimum))); - assert_eq!(special_integer("#sup"), Ok(("", SpecialInteger::Supremum))); - assert_eq!(special_integer("#inf #sup"), Ok((" #sup", SpecialInteger::Infimum))); - assert_eq!(special_integer("#sup #inf"), Ok((" #inf", SpecialInteger::Supremum))); - assert_eq!(special_integer("#inf,"), Ok((",", SpecialInteger::Infimum))); - assert_eq!(special_integer("#sup,"), Ok((",", SpecialInteger::Supremum))); - assert!(special_integer("#inf0").is_err()); - assert!(special_integer("#sup0").is_err()); - assert!(special_integer("#infimum").is_err()); - assert!(special_integer("#supremum").is_err()); - assert!(special_integer("inf").is_err()); - assert!(special_integer("sup").is_err()); - assert!(special_integer("0").is_err()); - assert!(special_integer("10000").is_err()); - assert!(special_integer("-10000").is_err()); - assert!(special_integer("-").is_err()); - assert!(special_integer("+").is_err()); - assert!(special_integer("a").is_err()); - assert!(special_integer(" ").is_err()); - } - - #[test] - fn parse_string() - { - assert_eq!(string("\"test 123\""), Ok(("", "test 123".to_string()))); - assert_eq!(string("\"123 test\""), Ok(("", "123 test".to_string()))); - assert_eq!(string("\" test 123 \""), Ok(("", " test 123 ".to_string()))); - assert_eq!(string("\"test 123\" \"rest"), Ok((" \"rest", "test 123".to_string()))); - assert_eq!(string("\"test 123\", \"rest"), Ok((", \"rest", "test 123".to_string()))); - assert_eq!(string("\"test\n123\""), Ok(("", "test\n123".to_string()))); - assert_eq!(string("\"test\\\"123\""), Ok(("", "test\"123".to_string()))); - assert_eq!(string("\"test\\\"123\\\"\""), Ok(("", "test\"123\"".to_string()))); - assert_eq!(string("\"\\\"test 123\\\"\""), Ok(("", "\"test 123\"".to_string()))); - assert_eq!(string("\"test\\\\123\""), Ok(("", "test\\123".to_string()))); - assert_eq!(string("\"test\\\\123\\\\\""), Ok(("", "test\\123\\".to_string()))); - assert_eq!(string("\"\\\\test 123\\\\\""), Ok(("", "\\test 123\\".to_string()))); - assert_eq!(string("\"test\\n123\""), Ok(("", "test\n123".to_string()))); - assert_eq!(string("\"test\\n123\\n\""), Ok(("", "test\n123\n".to_string()))); - assert_eq!(string("\"\\ntest 123\\n\""), Ok(("", "\ntest 123\n".to_string()))); - assert_eq!(string("\"test\\t123\""), Ok(("", "test\t123".to_string()))); - assert_eq!(string("\"test\\t123\\t\""), Ok(("", "test\t123\t".to_string()))); - assert_eq!(string("\"\\ttest 123\\t\""), Ok(("", "\ttest 123\t".to_string()))); - assert_eq!(string("\"test 🙂 123\""), Ok(("", "test 🙂 123".to_string()))); - assert_eq!(string("\"🙂test 123\""), Ok(("", "🙂test 123".to_string()))); - assert_eq!(string("\"test 123🙂\""), Ok(("", "test 123🙂".to_string()))); - assert!(string("\"test 123\"a").is_err()); - assert!(string("\"test\\i123\"").is_err()); - assert!(string("\"test").is_err()); - assert!(string("test").is_err()); - assert!(string("-").is_err()); - assert!(string(" ").is_err()); - } -} diff --git a/src/parse/names.rs b/src/parse/names.rs deleted file mode 100644 index d91ce12..0000000 --- a/src/parse/names.rs +++ /dev/null @@ -1,159 +0,0 @@ -use nom:: -{ - IResult, - bytes::complete::{take_while, take_while_m_n}, - combinator::recognize, - sequence::{pair, terminated}, -}; - -use super::word_boundary; - -fn is_function_name_character_first(c: char) -> bool -{ - c.is_alphabetic() && c.is_lowercase() -} - -fn is_function_name_character_body(c: char) -> bool -{ - c.is_alphanumeric() || c == '_' -} - -fn is_variable_name_character_first(c: char) -> bool -{ - c.is_alphabetic() && c.is_uppercase() -} - -fn is_variable_name_character_body(c: char) -> bool -{ - c.is_alphanumeric() || c == '_' -} - -pub fn function_or_predicate_name(i: &str) -> IResult<&str, &str> -{ - let (i, name) = - recognize - ( - terminated - ( - pair - ( - take_while_m_n(1, 1, is_function_name_character_first), - take_while(is_function_name_character_body), - ), - word_boundary, - ) - )(i)?; - - match name - { - "and" - | "exists" - | "false" - | "forall" - | "not" - | "or" - | "true" - => Err(nom::Err::Error((i, nom::error::ErrorKind::Verify))), - name => Ok((i, name)), - } -} - -pub fn variable_name(i: &str) -> IResult<&str, &str> -{ - recognize - ( - terminated - ( - pair - ( - take_while_m_n(1, 1, is_variable_name_character_first), - take_while(is_variable_name_character_body), - ), - word_boundary, - ) - )(i) -} - -#[cfg(test)] -mod tests -{ - use crate::parse::*; - - #[test] - fn parse_function_or_predicate_name() - { - assert_eq!(function_or_predicate_name("p rest"), Ok((" rest", "p"))); - assert_eq!(function_or_predicate_name("f rest"), Ok((" rest", "f"))); - assert_eq!(function_or_predicate_name("p, rest"), Ok((", rest", "p"))); - assert_eq!(function_or_predicate_name("f, rest"), Ok((", rest", "f"))); - assert_eq!(function_or_predicate_name("name_123 rest"), Ok((" rest", "name_123"))); - assert!(function_or_predicate_name("0 rest").is_err()); - assert!(function_or_predicate_name("123_asd rest").is_err()); - assert!(function_or_predicate_name("P rest").is_err()); - assert!(function_or_predicate_name("Predicate_123 rest").is_err()); - assert!(function_or_predicate_name("_ rest").is_err()); - assert!(function_or_predicate_name("_predicate_123 rest").is_err()); - assert!(function_or_predicate_name("(p").is_err()); - assert!(function_or_predicate_name(")p").is_err()); - assert!(function_or_predicate_name(">p").is_err()); - assert!(function_or_predicate_name("X").is_err()); - assert!(variable_name("(i: &'i str, d: &D, v: &'v crate::VariableDeclarationStackLayer) - -> IResult<&'i str, crate::Term> -where - D: crate::FindOrCreateFunctionDeclaration + crate::FindOrCreatePredicateDeclaration -{ - map - ( - preceded - ( - terminated - ( - tag("-"), - multispace0, - ), - |i| term_precedence_1(i, d, v), - ), - |x| match x - { - crate::Term::Integer(value) => crate::Term::integer(-value), - crate::Term::UnaryOperation( - crate::UnaryOperation{operator: crate::UnaryOperator::Negative, argument}) - => *argument, - _ => crate::Term::negative(Box::new(x)), - } - )(i) -} - -fn absolute_value<'i, 'v, D>(i: &'i str, d: &D, v: &'v crate::VariableDeclarationStackLayer) - -> IResult<&'i str, crate::Term> -where - D: crate::FindOrCreateFunctionDeclaration + crate::FindOrCreatePredicateDeclaration -{ - map - ( - delimited - ( - terminated - ( - tag("|"), - multispace0, - ), - |i| term(i, d, v), - preceded - ( - multispace0, - tag("|"), - ), - ), - |x| crate::Term::absolute_value(Box::new(x)), - )(i) -} - -pub(crate) fn function_or_predicate<'i, 'v, D>(i: &'i str, d: &D, - v: &'v crate::VariableDeclarationStackLayer) - -> IResult<&'i str, (&'i str, Option)> -where - D: crate::FindOrCreateFunctionDeclaration + crate::FindOrCreatePredicateDeclaration -{ - pair - ( - function_or_predicate_name, - opt - ( - delimited - ( - delimited - ( - multispace0, - tag("("), - multispace0, - ), - separated_list0 - ( - delimited - ( - multispace0, - tag(","), - multispace0, - ), - |i| term(i, d, v), - ), - preceded - ( - multispace0, - tag(")"), - ), - ) - ), - )(i) -} - -fn function<'i, 'v, D>(i: &'i str, d: &D, v: &'v crate::VariableDeclarationStackLayer) - -> IResult<&'i str, crate::Function> -where - D: crate::FindOrCreateFunctionDeclaration + crate::FindOrCreatePredicateDeclaration -{ - map - ( - |i| function_or_predicate(i, d, v), - |(name, arguments)| - { - let arguments = match arguments - { - Some(arguments) => arguments, - None => vec![], - }; - - let declaration = d.find_or_create_function_declaration(name, arguments.len()); - - crate::Function::new(declaration, arguments) - }, - )(i) -} - -pub(crate) fn variable_declaration(i: &str) -> IResult<&str, crate::VariableDeclaration> -{ - map - ( - variable_name, - |name| crate::VariableDeclaration::new(name.to_string()) - )(i) -} - -fn variable<'i, 'v>(i: &'i str, v: &'v crate::VariableDeclarationStackLayer) - -> IResult<&'i str, crate::Variable> -{ - map - ( - variable_name, - |name| - { - let declaration = v.find_or_create(name); - - crate::Variable::new(declaration) - }, - )(i) -} - -fn term_parenthesized<'i, 'v, D>(i: &'i str, d: &D, v: &'v crate::VariableDeclarationStackLayer) - -> IResult<&'i str, crate::Term> -where - D: crate::FindOrCreateFunctionDeclaration + crate::FindOrCreatePredicateDeclaration -{ - delimited - ( - terminated - ( - tag("("), - multispace0, - ), - |i| term(i, d, v), - preceded - ( - multispace0, - tag(")"), - ), - )(i) -} - -fn term_precedence_0<'i, 'v, D>(i: &'i str, d: &D, v: &'v crate::VariableDeclarationStackLayer) - -> IResult<&'i str, crate::Term> -where - D: crate::FindOrCreateFunctionDeclaration + crate::FindOrCreatePredicateDeclaration -{ - alt - (( - map - ( - boolean, - crate::Term::Boolean, - ), - map - ( - special_integer, - crate::Term::SpecialInteger, - ), - map - ( - integer, - crate::Term::Integer, - ), - map - ( - |i| function(i, d, v), - crate::Term::Function, - ), - map - ( - string, - crate::Term::String, - ), - map - ( - |i| variable(i, v), - crate::Term::Variable, - ), - |i| absolute_value(i, d, v), - |i| term_parenthesized(i, d, v), - ))(i) -} - -fn term_precedence_1<'i, 'v, D>(i: &'i str, d: &D, v: &'v crate::VariableDeclarationStackLayer) - -> IResult<&'i str, crate::Term> -where - D: crate::FindOrCreateFunctionDeclaration + crate::FindOrCreatePredicateDeclaration -{ - alt - (( - |i| negative(i, d, v), - |i| term_precedence_0(i, d, v), - ))(i) -} - -fn term_precedence_2<'i, 'v, D>(i: &'i str, d: &D, v: &'v crate::VariableDeclarationStackLayer) - -> IResult<&'i str, crate::Term> -where - D: crate::FindOrCreateFunctionDeclaration + crate::FindOrCreatePredicateDeclaration -{ - alt - (( - map - ( - pair - ( - many1 - ( - terminated - ( - |i| term_precedence_1(i, d, v), - delimited - ( - multispace0, - tag("**"), - multispace0, - ), - ) - ), - |i| term_precedence_1(i, d, v), - ), - |(arguments, last_argument)| arguments.into_iter().rev().fold(last_argument, - |accumulator, argument| - crate::Term::exponentiate(Box::new(argument), Box::new(accumulator))), - ), - |i| term_precedence_1(i, d, v), - ))(i) -} - -fn term_precedence_3<'i, 'v, D>(i: &'i str, d: &D, v: &'v crate::VariableDeclarationStackLayer) - -> IResult<&'i str, crate::Term> -where - D: crate::FindOrCreateFunctionDeclaration + crate::FindOrCreatePredicateDeclaration -{ - alt - (( - map - ( - pair - ( - |i| term_precedence_2(i, d, v), - many1 - ( - pair - ( - delimited - ( - multispace0, - alt - (( - tag("*"), - tag("/"), - tag("%"), - )), - multispace0, - ), - |i| term_precedence_2(i, d, v), - ) - ), - ), - |(first_argument, arguments)| arguments.into_iter().fold(first_argument, - |accumulator, (operator, argument)| - match operator - { - "*" => crate::Term::multiply(Box::new(accumulator), Box::new(argument)), - "/" => crate::Term::divide(Box::new(accumulator), Box::new(argument)), - "%" => crate::Term::modulo(Box::new(accumulator), Box::new(argument)), - // TODO: handle appropriately - _ => panic!("test"), - }) - ), - |i| term_precedence_2(i, d, v), - ))(i) -} - -fn term_precedence_4<'i, 'v, D>(i: &'i str, d: &D, v: &'v crate::VariableDeclarationStackLayer) - -> IResult<&'i str, crate::Term> -where - D: crate::FindOrCreateFunctionDeclaration + crate::FindOrCreatePredicateDeclaration -{ - alt - (( - map - ( - pair - ( - |i| term_precedence_3(i, d, v), - many1 - ( - pair - ( - delimited - ( - multispace0, - alt - (( - tag("+"), - tag("-"), - )), - multispace0, - ), - |i| term_precedence_3(i, d, v), - ) - ), - ), - |(first_argument, arguments)| arguments.into_iter().fold(first_argument, - |accumulator, (operator, argument)| - match operator - { - "+" => crate::Term::add(Box::new(accumulator), Box::new(argument)), - "-" => crate::Term::subtract(Box::new(accumulator), Box::new(argument)), - // TODO: handle appropriately - _ => panic!("test"), - }) - ), - |i| term_precedence_3(i, d, v), - ))(i) -} - -pub fn term<'i, 'v, D>(i: &'i str, d: &D, v: &'v crate::VariableDeclarationStackLayer) - -> IResult<&'i str, crate::Term> -where - D: crate::FindOrCreateFunctionDeclaration + crate::FindOrCreatePredicateDeclaration -{ - term_precedence_4(i, d, v) -} - -#[cfg(test)] -mod tests -{ - use crate::{Term, VariableDeclaration, VariableDeclarationStackLayer}; - use crate::parse::terms as original; - use crate::utils::*; - - fn term(i: &str) -> Term - { - original::term(i, &Declarations::new(), &VariableDeclarationStackLayer::free()).unwrap().1 - } - - fn format_term(i: &str) -> String - { - format!("{}", term(i)) - } - - #[test] - fn parse_parenthesized() - { - assert_eq!(format_term("(1)"), format_term("1")); - assert_eq!(format_term("((1))"), format_term("1")); - assert_eq!(format_term("(-1)"), format_term("-1")); - assert_eq!(format_term("((-1))"), format_term("-1")); - assert_eq!(format_term("(-(1))"), format_term("-1")); - assert_eq!(format_term("-((1))"), format_term("-1")); - assert_eq!(format_term("(-(-1))"), format_term("1")); - assert_eq!(format_term("-((-1))"), format_term("1")); - assert_eq!(format_term("-(-(1))"), format_term("1")); - assert_eq!(format_term("-(-(-1))"), format_term("-1")); - assert_eq!(format_term("(a)"), format_term("a")); - assert_eq!(format_term("((a))"), format_term("a")); - assert_eq!(format_term("(X)"), format_term("X")); - assert_eq!(format_term("((X))"), format_term("X")); - assert_eq!(format_term("(\"test\")"), format_term("\"test\"")); - assert_eq!(format_term("((\"test\"))"), format_term("\"test\"")); - assert_eq!(format_term("(a ** b)"), format_term("a ** b")); - assert_eq!(format_term("(a * b)"), format_term("a * b")); - assert_eq!(format_term("(a / b)"), format_term("a / b")); - assert_eq!(format_term("(a % b)"), format_term("a % b")); - assert_eq!(format_term("(a + b)"), format_term("a + b")); - assert_eq!(format_term("(a - b)"), format_term("a - b")); - assert_eq!(format_term("((a ** b))"), format_term("a ** b")); - assert_eq!(format_term("((a * b))"), format_term("a * b")); - assert_eq!(format_term("((a / b))"), format_term("a / b")); - assert_eq!(format_term("((a % b))"), format_term("a % b")); - assert_eq!(format_term("((a + b))"), format_term("a + b")); - assert_eq!(format_term("((a - b))"), format_term("a - b")); - assert_eq!(format_term("(f(a, b))"), format_term("f(a, b)")); - assert_eq!(format_term("((f(a, b)))"), format_term("f(a, b)")); - assert_eq!(format_term("f((a), (b))"), format_term("f(a, b)")); - assert_eq!(format_term("f(|-a|)"), format_term("f(|-a|)")); - assert_eq!(format_term("f((|-a|))"), format_term("f(|-a|)")); - assert_eq!(format_term("f((-a))"), format_term("f(-a)")); - assert_eq!(format_term("f(((-a)))"), format_term("f(-a)")); - assert_eq!(format_term("f((a ** b))"), format_term("f(a ** b)")); - assert_eq!(format_term("f((a * b))"), format_term("f(a * b)")); - assert_eq!(format_term("f((a / b))"), format_term("f(a / b)")); - assert_eq!(format_term("f((a % b))"), format_term("f(a % b)")); - assert_eq!(format_term("f((a + b))"), format_term("f(a + b)")); - assert_eq!(format_term("f((a - b))"), format_term("f(a - b)")); - assert_eq!(format_term("f(((a ** b)))"), format_term("f(a ** b)")); - assert_eq!(format_term("f(((a * b)))"), format_term("f(a * b)")); - assert_eq!(format_term("f(((a / b)))"), format_term("f(a / b)")); - assert_eq!(format_term("f(((a % b)))"), format_term("f(a % b)")); - assert_eq!(format_term("f(((a + b)))"), format_term("f(a + b)")); - assert_eq!(format_term("f(((a - b)))"), format_term("f(a - b)")); - assert_eq!(format_term("(|a ** b|)"), format_term("|a ** b|")); - assert_eq!(format_term("|(a ** b)|"), format_term("|a ** b|")); - assert_eq!(format_term("(|(a ** b)|)"), format_term("|a ** b|")); - assert_eq!(format_term("(|a * b|)"), format_term("|a * b|")); - assert_eq!(format_term("|(a * b)|"), format_term("|a * b|")); - assert_eq!(format_term("(|(a * b)|)"), format_term("|a * b|")); - assert_eq!(format_term("(|a / b|)"), format_term("|a / b|")); - assert_eq!(format_term("|(a / b)|"), format_term("|a / b|")); - assert_eq!(format_term("(|(a / b)|)"), format_term("|a / b|")); - assert_eq!(format_term("(|a % b|)"), format_term("|a % b|")); - assert_eq!(format_term("|(a % b)|"), format_term("|a % b|")); - assert_eq!(format_term("(|(a % b)|)"), format_term("|a % b|")); - } - - #[test] - fn parse_boolean() - { - assert_eq!(term("true"), Term::true_()); - assert_eq!(term("false"), Term::false_()); - } - - #[test] - fn parse_integer() - { - assert_eq!(term("0"), Term::integer(0)); - assert_eq!(term("10000"), Term::integer(10000)); - assert_eq!(term("+10000"), Term::integer(10000)); - assert_eq!(term("-10000"), Term::integer(-10000)); - } - - #[test] - fn parse_special_integer() - { - assert_eq!(term("#inf"), Term::infimum()); - assert_eq!(term("#sup"), Term::supremum()); - } - - #[test] - fn parse_string() - { - // TODO: fix - //assert_eq!(term("\"\""), Term::string("".to_string())); - assert_eq!(term("\"a\""), Term::string("a".to_string())); - assert_eq!(term("\"#\""), Term::string("#".to_string())); - assert_eq!(term("\" \""), Term::string(" ".to_string())); - assert_eq!(term("\" \""), Term::string(" ".to_string())); - assert_eq!(term("\"test test\""), Term::string("test test".to_string())); - assert_eq!(term("\"123 456\""), Term::string("123 456".to_string())); - assert_eq!(term("\"-#? -#?\""), Term::string("-#? -#?".to_string())); - assert_eq!(term("\"\\ntest\\n123\\n\""), Term::string("\ntest\n123\n".to_string())); - assert_eq!(term("\"\\ttest\\t123\\t\""), Term::string("\ttest\t123\t".to_string())); - assert_eq!(term("\"\\\\test\\\\123\\\\\""), Term::string("\\test\\123\\".to_string())); - assert_eq!(term("\"🙂test🙂123🙂\""), Term::string("🙂test🙂123🙂".to_string())); - } - - #[test] - fn parse_function() - { - let term_as_function = |i| match term(i) - { - Term::Function(function) => function, - _ => panic!("expected function"), - }; - - assert_eq!(term_as_function("s").declaration.name, "s"); - assert_eq!(term_as_function("s").declaration.arity, 0); - assert!(term_as_function("s").arguments.is_empty()); - assert_eq!(term_as_function("s()").declaration.name, "s"); - assert_eq!(term_as_function("s()").declaration.arity, 0); - assert!(term_as_function("s()").arguments.is_empty()); - assert_eq!(term_as_function("s(1, 2, 3)").declaration.name, "s"); - assert_eq!(term_as_function("s(1, 2, 3)").declaration.arity, 3); - assert_eq!(term_as_function("s(1, 2, 3)").arguments.len(), 3); - assert_eq!(term_as_function("s(1, 2, 3)").arguments.remove(0), Term::integer(1)); - assert_eq!(term_as_function("s(1, 2, 3)").arguments.remove(2), Term::integer(3)); - - assert_eq!(format_term("n"), "n"); - } - - #[test] - fn parse_variable() - { - let term_as_variable = |i| match term(i) - { - Term::Variable(variable) => variable, - _ => panic!("expected variable"), - }; - - assert_eq!(term_as_variable("X").declaration.name, "X"); - assert_eq!(term_as_variable("Variable_123").declaration.name, "Variable_123"); - } - - #[test] - fn parse_unary() - { - assert_eq!(format_term("|a|"), "|a|"); - assert_eq!(format_term("||a||"), "||a||"); - assert_eq!(format_term("|a - b|"), "|a - b|"); - assert_eq!(format_term("|a| - b"), "|a| - b"); - assert_eq!(format_term("a - |b|"), "a - |b|"); - assert_eq!(format_term("||a| - b|"), "||a| - b|"); - assert_eq!(format_term("|a - |b||"), "|a - |b||"); - assert_eq!(format_term("||a| - |b||"), "||a| - |b||"); - assert_eq!(format_term("||a| - |b| - |c||"), "||a| - |b| - |c||"); - assert_eq!(format_term("||a - b| - |c - d||"), "||a - b| - |c - d||"); - assert_eq!(format_term("-a"), "-a"); - assert_eq!(format_term("--a"), "a"); - assert_eq!(format_term("---a"), "-a"); - assert_eq!(format_term("-(a + b)"), "-(a + b)"); - assert_eq!(format_term("-|a + b|"), "-|a + b|"); - assert_eq!(format_term("--|a + b|"), "|a + b|"); - assert_eq!(format_term("---|a + b|"), "-|a + b|"); - assert_eq!(term("5"), Term::integer(5)); - assert_eq!(term("-5"), Term::integer(-5)); - assert_eq!(term("--5"), Term::integer(5)); - assert_eq!(term("---5"), Term::integer(-5)); - assert_eq!(term("0"), Term::integer(0)); - assert_eq!(term("-0"), Term::integer(0)); - assert_eq!(term("--0"), Term::integer(0)); - assert_eq!(term("---0"), Term::integer(0)); - } - - #[test] - fn parse_exponentiate() - { - assert_eq!(term("1 ** (2 ** (3 ** (4 ** 5)))"), term("1 ** 2 ** 3 ** 4 ** 5")); - assert_eq!(format_term("1 ** 2 ** 3 ** 4 ** 5"), "1 ** 2 ** 3 ** 4 ** 5"); - assert_eq!(term("1 ** (2 ** (3 ** (4 ** 5)))"), term("1 ** 2 ** 3 ** 4 ** 5")); - // As exponentiation is right-associative, these parentheses cannot be omitted - assert_ne!(term("(((1 ** 2) ** 3) ** 4) ** 5"), term("1 ** 2 ** 3 ** 4 ** 5")); - assert_eq!(format_term("(((1 ** 2) ** 3) ** 4) ** 5"), "(((1 ** 2) ** 3) ** 4) ** 5"); - } - - #[test] - fn parse_multiplicative() - { - assert_eq!(format_term("(a * b) * (c * d)"), "a * b * c * d"); - assert_eq!(format_term("(a * b) * (c / d)"), "a * b * c / d"); - assert_eq!(format_term("(a * b) * (c % d)"), "a * b * (c % d)"); - assert_eq!(format_term("(a / b) * (c * d)"), "a / b * c * d"); - assert_eq!(format_term("(a / b) * (c / d)"), "a / b * c / d"); - assert_eq!(format_term("(a / b) * (c % d)"), "a / b * (c % d)"); - assert_eq!(format_term("(a % b) * (c * d)"), "a % b * c * d"); - assert_eq!(format_term("(a % b) * (c / d)"), "a % b * c / d"); - assert_eq!(format_term("(a % b) * (c % d)"), "a % b * (c % d)"); - assert_eq!(format_term("(a * b) / (c * d)"), "a * b / (c * d)"); - assert_eq!(format_term("(a * b) / (c / d)"), "a * b / (c / d)"); - assert_eq!(format_term("(a * b) / (c % d)"), "a * b / (c % d)"); - assert_eq!(format_term("(a / b) / (c * d)"), "a / b / (c * d)"); - assert_eq!(format_term("(a / b) / (c / d)"), "a / b / (c / d)"); - assert_eq!(format_term("(a / b) / (c % d)"), "a / b / (c % d)"); - assert_eq!(format_term("(a % b) / (c * d)"), "a % b / (c * d)"); - assert_eq!(format_term("(a % b) / (c / d)"), "a % b / (c / d)"); - assert_eq!(format_term("(a % b) / (c % d)"), "a % b / (c % d)"); - assert_eq!(format_term("(a * b) % (c * d)"), "a * b % (c * d)"); - assert_eq!(format_term("(a * b) % (c / d)"), "a * b % (c / d)"); - assert_eq!(format_term("(a * b) % (c % d)"), "a * b % (c % d)"); - assert_eq!(format_term("(a / b) % (c * d)"), "a / b % (c * d)"); - assert_eq!(format_term("(a / b) % (c / d)"), "a / b % (c / d)"); - assert_eq!(format_term("(a / b) % (c % d)"), "a / b % (c % d)"); - assert_eq!(format_term("(a % b) % (c * d)"), "a % b % (c * d)"); - assert_eq!(format_term("(a % b) % (c / d)"), "a % b % (c / d)"); - assert_eq!(format_term("(a % b) % (c % d)"), "a % b % (c % d)"); - - // TODO: test malformed expressions - } - - #[test] - fn parse_additive() - { - assert_eq!(format_term("(a + b) + (c + d)"), "a + b + c + d"); - assert_eq!(format_term("(a + b) + (c - d)"), "a + b + c - d"); - assert_eq!(format_term("(a - b) + (c + d)"), "a - b + c + d"); - assert_eq!(format_term("(a - b) + (c - d)"), "a - b + c - d"); - assert_eq!(format_term("(a + b) - (c + d)"), "a + b - (c + d)"); - assert_eq!(format_term("(a + b) - (c - d)"), "a + b - (c - d)"); - assert_eq!(format_term("(a - b) - (c + d)"), "a - b - (c + d)"); - assert_eq!(format_term("(a - b) - (c - d)"), "a - b - (c - d)"); - } - - #[test] - fn parse_precedence() - { - assert_eq!(term("-a + b"), term("(-a) + b")); - assert_eq!(term("-a + -b"), term("(-a) + (-b)")); - assert_eq!(term("-a + -b"), term("-(a) + -(b)")); - assert_eq!(format_term("-(a + b)"), "-(a + b)"); - assert_eq!(term("-a - b"), term("(-a) - b")); - assert_eq!(term("-a - -b"), term("(-a) - (-b)")); - assert_eq!(term("-a - -b"), term("-(a) - -(b)")); - assert_eq!(term("-a * b"), term("(-a) * b")); - assert_eq!(term("-a * -b"), term("(-a) * (-b)")); - assert_eq!(term("-a * -b"), term("-(a) * -(b)")); - assert_eq!(term("-a / b"), term("(-a) / b")); - assert_eq!(term("-a / -b"), term("(-a) / (-b)")); - assert_eq!(term("-a / -b"), term("-(a) / -(b)")); - assert_eq!(term("-a % b"), term("(-a) % b")); - assert_eq!(term("-a % -b"), term("(-a) % (-b)")); - assert_eq!(term("-a % -b"), term("-(a) % -(b)")); - assert_eq!(term("-a ** b"), term("(-a) ** b")); - assert_eq!(term("-a ** -b"), term("(-a) ** (-b)")); - assert_eq!(term("-a ** -b"), term("-(a) ** -(b)")); - assert_eq!(format_term("-(a + b)"), "-(a + b)"); - assert_eq!(format_term("-(a + b)"), "-(a + b)"); - assert_eq!(format_term("-(a + b)"), "-(a + b)"); - assert_eq!(format_term("-(a + b)"), "-(a + b)"); - assert_eq!(format_term("-(a + b)"), "-(a + b)"); - assert_eq!(term("a + (b * c) + d"), term("(a + (b * c)) + d")); - assert_eq!(term("a + (b / c) + d"), term("(a + (b / c)) + d")); - assert_eq!(term("a + (b % c) + d"), term("(a + (b % c)) + d")); - assert_eq!(term("a - (b * c) - d"), term("(a - (b * c)) - d")); - assert_eq!(term("a - (b / c) - d"), term("(a - (b / c)) - d")); - assert_eq!(term("a - (b % c) - d"), term("(a - (b % c)) - d")); - assert_eq!(format_term("(a + b) * (c + d)"), "(a + b) * (c + d)"); - assert_eq!(format_term("(a + b) / (c + d)"), "(a + b) / (c + d)"); - assert_eq!(format_term("(a + b) % (c + d)"), "(a + b) % (c + d)"); - assert_eq!(format_term("(a - b) * (c - d)"), "(a - b) * (c - d)"); - assert_eq!(format_term("(a - b) / (c - d)"), "(a - b) / (c - d)"); - assert_eq!(format_term("(a - b) % (c - d)"), "(a - b) % (c - d)"); - assert_eq!(term("a ** b ** c + d ** e ** f"), term("(a ** b ** c) + (d ** e ** f)")); - assert_eq!(term("a ** (b ** c + d) ** e ** f"), term("a ** ((b ** c + d) ** (e ** f))")); - assert_eq!(term("a ** b ** (c + d) ** e ** f"), term("a ** (b ** ((c + d) ** (e ** f)))")); - assert_eq!(term("a ** b ** (c + d ** e) ** f"), term("a ** (b ** ((c + d ** e) ** f))")); - assert_eq!(term("a ** b ** c - d ** e ** f"), term("(a ** b ** c) - (d ** e ** f)")); - assert_eq!(term("a ** (b ** c - d) ** e ** f"), term("a ** ((b ** c - d) ** (e ** f))")); - assert_eq!(term("a ** b ** (c - d) ** e ** f"), term("a ** (b ** ((c - d) ** (e ** f)))")); - assert_eq!(term("a ** b ** (c - d ** e) ** f"), term("a ** (b ** ((c - d ** e) ** f))")); - assert_eq!(term("a ** b ** c * d ** e ** f"), term("(a ** b ** c) * (d ** e ** f)")); - assert_eq!(term("a ** (b ** c * d) ** e ** f"), term("a ** ((b ** c * d) ** (e ** f))")); - assert_eq!(term("a ** b ** (c * d) ** e ** f"), term("a ** (b ** ((c * d) ** (e ** f)))")); - assert_eq!(term("a ** b ** (c * d ** e) ** f"), term("a ** (b ** ((c * d ** e) ** f))")); - assert_eq!(term("a ** b ** c / d ** e ** f"), term("(a ** b ** c) / (d ** e ** f)")); - assert_eq!(term("a ** (b ** c / d) ** e ** f"), term("a ** ((b ** c / d) ** (e ** f))")); - assert_eq!(term("a ** b ** (c / d) ** e ** f"), term("a ** (b ** ((c / d) ** (e ** f)))")); - assert_eq!(term("a ** b ** (c / d ** e) ** f"), term("a ** (b ** ((c / d ** e) ** f))")); - assert_eq!(term("a ** b ** c % d ** e ** f"), term("(a ** b ** c) % (d ** e ** f)")); - assert_eq!(term("a ** (b ** c % d) ** e ** f"), term("a ** ((b ** c % d) ** (e ** f))")); - assert_eq!(term("a ** b ** (c % d) ** e ** f"), term("a ** (b ** ((c % d) ** (e ** f)))")); - assert_eq!(term("a ** b ** (c % d ** e) ** f"), term("a ** (b ** ((c % d ** e) ** f))")); - } - - #[test] - fn parse_bounds() - { - let term = |i| original::term(i, &Declarations::new(), - &VariableDeclarationStackLayer::free()).unwrap().0; - - assert_eq!(term("1 ** 2 ** 3, rest"), ", rest"); - assert_eq!(term("1 * 2 * 3, rest"), ", rest"); - assert_eq!(term("1 / 2 / 3, rest"), ", rest"); - assert_eq!(term("1 % 2 % 3, rest"), ", rest"); - assert_eq!(term("1 + 2 + 3, rest"), ", rest"); - assert_eq!(term("1 - 2 - 3, rest"), ", rest"); - assert_eq!(term("1, rest"), ", rest"); - assert_eq!(term("-1, rest"), ", rest"); - assert_eq!(term("--1, rest"), ", rest"); - assert_eq!(term("|1|, rest"), ", rest"); - assert_eq!(term("|1| + |-2|, rest"), ", rest"); - assert_eq!(term("||-2||, rest"), ", rest"); - assert_eq!(term("|-|-2||, rest"), ", rest"); - assert_eq!(term("(1), rest"), ", rest"); - assert_eq!(term("a, rest"), ", rest"); - assert_eq!(term("1, rest"), ", rest"); - assert_eq!(term("true, rest"), ", rest"); - assert_eq!(term("false, rest"), ", rest"); - assert_eq!(term("#inf, rest"), ", rest"); - assert_eq!(term("#sup, rest"), ", rest"); - assert_eq!(term("f(1, 2), rest"), ", rest"); - assert_eq!(term("g(1 ** 2, 3 * 4, #inf), rest"), ", rest"); - assert_eq!(term("\"test\", rest"), ", rest"); - assert_eq!(term("X, rest"), ", rest"); - assert_eq!(term("Variable, rest"), ", rest"); - assert_eq!(term("f(\"test\", Variable), rest"), ", rest"); - } - - #[test] - fn parse_whitespace() - { - assert_eq!(format_term("(a+b)*(c+d)"), "(a + b) * (c + d)"); - assert_eq!(format_term("( a + b ) * ( c + d )"), "(a + b) * (c + d)"); - assert_eq!(format_term("( a + b ) * ( c + d )"), "(a + b) * (c + d)"); - assert_eq!(format_term("(\ta\t+\tb\t)\t*\t(\tc\t+\td\t)"), "(a + b) * (c + d)"); - assert_eq!(format_term("(\na\n+\nb\n)\n*\n(\nc\n+\nd\n)"), "(a + b) * (c + d)"); - assert_eq!(format_term("( \t a \t + \t b \t ) \t * \t ( \t c \t + \t d \t )"), "(a + b) * (c + d)"); - assert_eq!(format_term("( \n a \n + \n b \n ) \n * \n ( \n c \n + \n d \n )"), "(a + b) * (c + d)"); - assert_eq!(format_term("f(\ta\t+\tb\t,\tc\t+\td\t)"), "f(a + b, c + d)"); - assert_eq!(format_term("f(\na\n+\nb\n,\nc\n+\nd\n)"), "f(a + b, c + d)"); - assert_eq!(format_term("f( \t a \t + \t b \t , \t c \t + \t d \t)"), "f(a + b, c + d)"); - assert_eq!(format_term("f( \n a \n + \n b \n , \n c \n + \n d \n)"), "f(a + b, c + d)"); - // TODO: test other operators - } - - #[test] - fn parse_function_primitive() - { - let function = |i| original::function(i, &Declarations::new(), - &VariableDeclarationStackLayer::free()).unwrap().1; - let function_remainder = |i| original::function(i, &Declarations::new(), - &VariableDeclarationStackLayer::free()).unwrap().0; - - assert_eq!(function("s").declaration.name, "s"); - assert_eq!(function("s").declaration.arity, 0); - assert_eq!(function_remainder("s"), ""); - assert_eq!(function("s ( )").declaration.name, "s"); - assert_eq!(function("s ( )").declaration.arity, 0); - assert_eq!(function_remainder("s ( )"), ""); - assert_eq!(function("s ( 1 , 2 , 3 )").declaration.name, "s"); - assert_eq!(function("s ( 1 , 2 , 3 )").declaration.arity, 3); - assert_eq!(function("s ( 1 , 2 , 3 )").arguments.remove(0), Term::integer(1)); - assert_eq!(function("s ( 1 , 2 , 3 )").arguments.remove(1), Term::integer(2)); - assert_eq!(function("s ( 1 , 2 , 3 )").arguments.remove(2), Term::integer(3)); - assert_eq!(function_remainder("s ( 1 , 2 , 3 )"), ""); - assert_eq!(function("s ( ), rest").declaration.name, "s"); - assert_eq!(function("s ( ), rest").declaration.arity, 0); - assert_eq!(function_remainder("s ( ), rest"), ", rest"); - assert_eq!(function("s ( 1 , 2 , 3 ), rest").declaration.name, "s"); - assert_eq!(function("s ( 1 , 2 , 3 ), rest").declaration.arity, 3); - assert_eq!(function_remainder("s ( 1 , 2 , 3 ), rest"), ", rest"); - } - - #[test] - fn parse_variable_declaration() - { - let variable_declaration = |i| original::variable_declaration(i).unwrap().1; - let variable_declaration_remainder = |i| original::variable_declaration(i).unwrap().0; - - assert_eq!(variable_declaration("X Rest").name, "X"); - assert_eq!(variable_declaration_remainder("X Rest"), " Rest"); - assert_eq!(variable_declaration("X, Rest").name, "X"); - assert_eq!(variable_declaration_remainder("X, Rest"), ", Rest"); - // Variable declarations parsed at different locations should not be considered equal - assert_ne!(variable_declaration("X"), variable_declaration("X")); - assert_eq!(variable_declaration("Variable_123 Rest").name, "Variable_123"); - assert_eq!(variable_declaration_remainder("Variable_123 Rest"), " Rest"); - - let variable_declaration = original::variable_declaration; - - assert!(variable_declaration("0 Rest").is_err()); - assert!(variable_declaration("123_Asd Rest").is_err()); - assert!(variable_declaration("x Rest").is_err()); - assert!(variable_declaration("variable_123 Rest").is_err()); - assert!(variable_declaration("_ Rest").is_err()); - assert!(variable_declaration("_variable_123 Rest").is_err()); - assert!(variable_declaration(" ").is_err()); - } - - #[test] - fn parse_variable_primitive() - { - let variable = |i| original::variable(i, &VariableDeclarationStackLayer::free()).unwrap().1; - let variable_remainder = |i| original::variable(i, - &VariableDeclarationStackLayer::free()).unwrap().0; - - assert_eq!(variable("X Rest").declaration.name, "X"); - assert_eq!(variable_remainder("X Rest"), " Rest"); - assert_eq!(variable("X, Rest").declaration.name, "X"); - assert_eq!(variable_remainder("X, Rest"), ", Rest"); - assert_eq!(variable("Variable_123 Rest").declaration.name, "Variable_123"); - assert_eq!(variable_remainder("Variable_123 Rest"), " Rest"); - - let variable = |i| original::variable(i, &VariableDeclarationStackLayer::free()); - - assert!(variable("0 Rest").is_err()); - assert!(variable("123_Asd Rest").is_err()); - assert!(variable("x Rest").is_err()); - assert!(variable("variable_123 Rest").is_err()); - assert!(variable("_ Rest").is_err()); - assert!(variable("_variable_123 Rest").is_err()); - assert!(variable(" ").is_err()); - - let new_variable_declarations = |names: &[&str]| std::rc::Rc::new(names.iter() - .map(|name| std::rc::Rc::new(VariableDeclaration::new(name.to_string()))) - .collect()); - - let layer_1 = new_variable_declarations(&["A", "B", "X"]); - let layer_2 = new_variable_declarations(&["C", "D", "X"]); - let layer_3 = new_variable_declarations(&["E", "F", "Y"]); - let layer_4 = new_variable_declarations(&["G", "H", "X"]); - - let v_0 = VariableDeclarationStackLayer::free(); - let variable = |i| original::variable(i, &v_0).unwrap().1; - let number_of_free_variable_declarations = - || v_0.free_variable_declarations_do(|x| x.len()); - - let x1 = variable("X"); - assert_eq!(number_of_free_variable_declarations(), 1); - let x2 = variable("X"); - assert_eq!(number_of_free_variable_declarations(), 1); - assert_eq!(x1.declaration, x2.declaration); - let y1 = variable("Y"); - assert_eq!(number_of_free_variable_declarations(), 2); - assert_ne!(x1.declaration, y1.declaration); - assert_ne!(x2.declaration, y1.declaration); - - let v_1 = VariableDeclarationStackLayer::bound(&v_0, layer_1); - let variable = |i| original::variable(i, &v_1).unwrap().1; - - let x3 = variable("X"); - assert_eq!(number_of_free_variable_declarations(), 2); - assert_ne!(x1.declaration, x3.declaration); - let x4 = variable("X"); - assert_eq!(number_of_free_variable_declarations(), 2); - assert_eq!(x3.declaration, x4.declaration); - let a1 = variable("A"); - assert_eq!(number_of_free_variable_declarations(), 2); - assert_ne!(x3.declaration, a1.declaration); - let y2 = variable("Y"); - assert_eq!(number_of_free_variable_declarations(), 2); - assert_eq!(y1.declaration, y2.declaration); - - let v_2 = VariableDeclarationStackLayer::bound(&v_1, layer_2); - let variable = |i| original::variable(i, &v_2).unwrap().1; - - let x5 = variable("X"); - assert_eq!(number_of_free_variable_declarations(), 2); - assert_ne!(x1.declaration, x5.declaration); - assert_ne!(x3.declaration, x5.declaration); - let x6 = variable("X"); - assert_eq!(number_of_free_variable_declarations(), 2); - assert_eq!(x5.declaration, x6.declaration); - let a2 = variable("A"); - assert_eq!(number_of_free_variable_declarations(), 2); - assert_eq!(a1.declaration, a2.declaration); - - let v_3 = VariableDeclarationStackLayer::bound(&v_2, layer_3); - let variable = |i| original::variable(i, &v_3).unwrap().1; - - let x7 = variable("X"); - assert_eq!(number_of_free_variable_declarations(), 2); - assert_eq!(x5.declaration, x7.declaration); - let y3 = variable("Y"); - assert_eq!(number_of_free_variable_declarations(), 2); - assert_ne!(y2.declaration, y3.declaration); - - let v_4 = VariableDeclarationStackLayer::bound(&v_3, layer_4); - let variable = |i| original::variable(i, &v_4).unwrap().1; - - let x8 = variable("X"); - assert_eq!(number_of_free_variable_declarations(), 2); - assert_ne!(x7.declaration, x8.declaration); - let y4 = variable("Y"); - assert_eq!(number_of_free_variable_declarations(), 2); - assert_eq!(y3.declaration, y4.declaration); - let _ = variable("I"); - assert_eq!(number_of_free_variable_declarations(), 3); - } -} diff --git a/src/parse/tokens.rs b/src/parse/tokens.rs new file mode 100644 index 0000000..0586cca --- /dev/null +++ b/src/parse/tokens.rs @@ -0,0 +1,302 @@ +#[derive(Clone, Copy, Eq, PartialEq)] +pub(crate) enum Symbol +{ + ArrowLeft, + ArrowLeftAndRight, + ArrowRight, + Comma, + Division, + Equal, + Exponentiation, + Greater, + GreaterOrEqual, + Less, + LessOrEqual, + Minus, + Multiplication, + NotEqual, + Plus, + VerticalBar, +} + +impl std::fmt::Debug for Symbol +{ + fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result + { + match &self + { + Self::ArrowLeft => write!(formatter, "<-"), + Self::ArrowLeftAndRight => write!(formatter, "<->"), + Self::ArrowRight => write!(formatter, "->"), + Self::Comma => write!(formatter, ","), + Self::Division => write!(formatter, "/"), + Self::Equal => write!(formatter, "="), + Self::Exponentiation => write!(formatter, "**"), + Self::Greater => write!(formatter, ">"), + Self::GreaterOrEqual => write!(formatter, ">="), + Self::Less => write!(formatter, "<"), + Self::LessOrEqual => write!(formatter, "<="), + Self::Minus => write!(formatter, "-"), + Self::Multiplication => write!(formatter, "*"), + Self::NotEqual => write!(formatter, "!="), + Self::Plus => write!(formatter, "+"), + Self::VerticalBar => write!(formatter, "|"), + } + } +} + +fn is_identifier_start_character(character: char) -> bool +{ + // TODO: support leading underscores + character.is_ascii_alphabetic() +} + +fn is_identifier_body_character(character: char) -> bool +{ + match character + { + '_' => true, + _ if character.is_ascii_alphanumeric() => true, + _ => false, + } +} + +pub(crate) fn identifier(input: &str) -> Option<(&str, &str)> +{ + let mut characters = input.char_indices(); + + let (_, character) = match characters.next() + { + Some(characters_next) => characters_next, + None => return None, + }; + + if !is_identifier_start_character(character) + { + return None; + } + + loop + { + match characters.next() + { + None => return Some((input, characters.as_str())), + Some((character_index, character)) => + { + if !is_identifier_body_character(character) + { + return Some(input.split_at(character_index)); + } + }, + } + } +} + +fn number_string(input: &str) -> Option<(&str, &str)> +{ + let mut characters = input.char_indices(); + + let (_, character) = match characters.next() + { + Some(characters_next) => characters_next, + None => return None, + }; + + if !character.is_ascii_digit() + { + return None; + } + + loop + { + match characters.next() + { + None => return Some((input, characters.as_str())), + Some((character_index, character)) => + { + if !character.is_ascii_digit() + { + return Some(input.split_at(character_index)); + } + }, + } + } +} + +pub(crate) fn number(input: &str) -> Result, crate::parse::Error> +{ + let (number_string, remaining_input) = match number_string(input) + { + Some(number_string) => number_string, + None => return Ok(None), + }; + + let number = number_string.parse() + .map_err(|error| crate::parse::Error::new_parse_number(input, + crate::parse::error::Location::new(0, Some(0)), error))?; + + Ok(Some((number, remaining_input))) +} + +pub(crate) fn symbol(input: &str) -> Option<(Symbol, &str)> +{ + let mut characters = input.char_indices(); + + let (_, character) = match characters.next() + { + Some(characters_next) => characters_next, + None => return None, + }; + + let remaining_input = characters.as_str(); + + match character + { + ',' => Some((Symbol::Comma, remaining_input)), + // <->, <-, <=, < + '=' => Some((Symbol::Equal, remaining_input)), + // != + '!' => match characters.next() + { + Some((_, '=')) => Some((Symbol::NotEqual, characters.as_str())), + _ => None, + }, + '<' => match characters.next() + { + Some((_, '-')) => + { + let remaining_input = characters.as_str(); + + match characters.next() + { + Some((_, '>')) => Some((Symbol::ArrowLeftAndRight, characters.as_str())), + _ => Some((Symbol::ArrowLeft, remaining_input)), + } + }, + Some((_, '=')) => Some((Symbol::LessOrEqual, characters.as_str())), + _ => Some((Symbol::Less, remaining_input)), + }, + // >=, > + '>' => match characters.next() + { + Some((_, '=')) => Some((Symbol::GreaterOrEqual, characters.as_str())), + _ => Some((Symbol::Greater, remaining_input)), + }, + '+' => Some((Symbol::Plus, remaining_input)), + // ->, - + '-' => match characters.next() + { + Some((_, '>')) => Some((Symbol::ArrowRight, characters.as_str())), + _ => Some((Symbol::Minus, remaining_input)), + }, + // **, * + '*' => match characters.next() + { + Some((_, '*')) => Some((Symbol::Exponentiation, characters.as_str())), + _ => Some((Symbol::Multiplication, remaining_input)), + }, + '/' => Some((Symbol::Division, remaining_input)), + '|' => Some((Symbol::VerticalBar, remaining_input)), + _ => None, + } +} + +pub(crate) fn parenthesized_expression(input: &str) + -> Result, crate::parse::Error> +{ + let mut characters = input.chars(); + + let (first_character, remaining_input) = match characters.next() + { + Some(first_character) => (first_character, characters.as_str()), + None => return Ok(None), + }; + + if first_character != '(' + { + return Ok(None); + } + + let mut characters = remaining_input.char_indices(); + let mut number_of_open_parentheses = 1; + + while let Some((character_index, character)) = characters.next() + { + match character + { + '(' => number_of_open_parentheses += 1, + ')' => number_of_open_parentheses -= 1, + _ => (), + } + + if number_of_open_parentheses == 0 + { + let position_of_closing_parenthesis = character_index; + let (parenthesized_expression, _) = + remaining_input.split_at(position_of_closing_parenthesis); + let remaining_input = characters.as_str(); + + return Ok(Some((parenthesized_expression, remaining_input))); + } + } + + Err(crate::parse::Error::new_unmatched_parenthesis( + crate::parse::error::Location::new(0, Some(1)))) +} + +#[cfg(test)] +mod tests +{ + use super::*; + + #[test] + fn tokenize_primitives() + { + assert_eq!(parenthesized_expression("(foo bar baz) test").unwrap(), + Some(("foo bar baz", " test"))); + assert!(parenthesized_expression("( | asd#0231(asd|asd) test").is_err()); + assert_eq!(parenthesized_expression("( | asd#0231(asd|asd) ) test").unwrap(), + Some((" | asd#0231(asd|asd) ", " test"))); + assert_eq!(parenthesized_expression("( | a)sd#0231(asd|asd) test").unwrap(), + Some((" | a", "sd#0231(asd|asd) test"))); + + assert_eq!(number("1234, ").unwrap(), Some((1234, ", "))); + assert_eq!(number("1234.5, ").unwrap(), Some((1234, ".5, "))); + assert_eq!(number("-1234, ").unwrap(), None); + assert_eq!(number("a1234, ").unwrap(), None); + + assert_eq!(symbol("<-"), Some((Symbol::ArrowLeft, ""))); + assert_eq!(symbol("<->"), Some((Symbol::ArrowLeftAndRight, ""))); + assert_eq!(symbol("->"), Some((Symbol::ArrowRight, ""))); + assert_eq!(symbol(","), Some((Symbol::Comma, ""))); + assert_eq!(symbol("/"), Some((Symbol::Division, ""))); + assert_eq!(symbol("="), Some((Symbol::Equal, ""))); + assert_eq!(symbol("**"), Some((Symbol::Exponentiation, ""))); + assert_eq!(symbol(">"), Some((Symbol::Greater, ""))); + assert_eq!(symbol(">="), Some((Symbol::GreaterOrEqual, ""))); + assert_eq!(symbol("<"), Some((Symbol::Less, ""))); + assert_eq!(symbol("<="), Some((Symbol::LessOrEqual, ""))); + assert_eq!(symbol("-"), Some((Symbol::Minus, ""))); + assert_eq!(symbol("*"), Some((Symbol::Multiplication, ""))); + assert_eq!(symbol("!="), Some((Symbol::NotEqual, ""))); + assert_eq!(symbol("+"), Some((Symbol::Plus, ""))); + assert_eq!(symbol("|"), Some((Symbol::VerticalBar, ""))); + + assert_eq!(symbol("<-a"), Some((Symbol::ArrowLeft, "a"))); + assert_eq!(symbol("<->a"), Some((Symbol::ArrowLeftAndRight, "a"))); + assert_eq!(symbol("->a"), Some((Symbol::ArrowRight, "a"))); + assert_eq!(symbol(",a"), Some((Symbol::Comma, "a"))); + assert_eq!(symbol("/a"), Some((Symbol::Division, "a"))); + assert_eq!(symbol("=a"), Some((Symbol::Equal, "a"))); + assert_eq!(symbol("**a"), Some((Symbol::Exponentiation, "a"))); + assert_eq!(symbol(">a"), Some((Symbol::Greater, "a"))); + assert_eq!(symbol(">=a"), Some((Symbol::GreaterOrEqual, "a"))); + assert_eq!(symbol("