1use std::sync::Arc;
33
34use num_bigint::BigInt;
35use num_rational::Ratio;
36use smallvec::SmallVec;
37
38use crate::api::context::Context;
39use crate::api::expr::{BoolEx, Ex};
40use crate::base::arena::{
41 Arena, FN_AIRYAI, FN_AIRYAIPRIME, FN_AIRYBI, FN_AIRYBIPRIME, FN_ASSOC_LAGUERRE,
42 FN_ASSOC_LEGENDRE, FN_BETAINC, FN_BETAINC_REGULARIZED, FN_CHI, FN_DIRICHLET_ETA, FN_ELLIPTIC_E,
43 FN_ELLIPTIC_F, FN_ELLIPTIC_K, FN_ELLIPTIC_PI, FN_ERFCINV, FN_ERFI, FN_ERFINV, FN_EXPINT,
44 FN_FRESNELC, FN_FRESNELS, FN_GEGENBAUER, FN_JACOBI, FN_LOWERGAMMA, FN_POLYLOG, FN_SHI,
45 FN_UPPERGAMMA,
46};
47use crate::base::node::{ExprId, ExprNode};
48
49#[derive(Debug, Clone)]
51pub struct ParseError {
52 pub message: String,
54 pub position: usize,
56}
57
58impl std::fmt::Display for ParseError {
59 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
60 write!(
61 f,
62 "parse error at position {}: {}",
63 self.position, self.message
64 )
65 }
66}
67
68impl std::error::Error for ParseError {}
69
70pub fn parse(ctx: &Context, input: &str) -> Result<Ex, ParseError> {
78 let id = parse_with_mode(ctx, input, Mode::STRICT)?;
79 Ok(Ex::from_raw_parts(ctx.id, Arc::clone(&ctx.inner), id))
82}
83
84pub fn parse_bool(ctx: &Context, input: &str) -> Result<BoolEx, ParseError> {
114 let id = parse_with_mode(ctx, input, Mode::RELATIONS)?;
115 Ok(BoolEx::from_raw_parts(ctx.id, Arc::clone(&ctx.inner), id))
116}
117
118pub fn parse_implicit(ctx: &Context, input: &str) -> Result<Ex, ParseError> {
170 let id = parse_with_mode(ctx, input, Mode::IMPLICIT)?;
171 Ok(Ex::from_raw_parts(ctx.id, Arc::clone(&ctx.inner), id))
172}
173
174fn parse_with_mode(ctx: &Context, input: &str, mode: Mode) -> Result<ExprId, ParseError> {
178 let mut parser = Parser::new(input, mode);
179 ctx.with_arena_mut(|arena| {
180 let result = parser.parse_expr(arena, 0)?;
181 if parser.current != Token::Eof {
183 return Err(ParseError {
184 message: format!("unexpected token {:?} after expression", parser.current),
185 position: parser.lexer.pos,
186 });
187 }
188 if mode.relations && !is_bool_node(arena, result) {
189 return Err(ParseError {
190 message: format!(
191 "expected a relation or Boolean expression, got the numeric expression '{}'",
192 arena.display(result)
193 ),
194 position: parser.lexer.pos,
195 });
196 }
197 Ok(result)
198 })
199}
200
201impl Context {
202 pub fn parse_bool(
226 &self,
227 input: &str,
228 ) -> Result<crate::api::expr::BoolEx, crate::base::errors::SymplexError> {
229 parse_bool(self, input).map_err(|e| crate::base::errors::SymplexError::ComputationFailed {
230 operation: "parse_bool",
231 reason: e.to_string(),
232 })
233 }
234
235 pub fn parse_implicit(
258 &self,
259 input: &str,
260 ) -> Result<crate::api::expr::Ex, crate::base::errors::SymplexError> {
261 parse_implicit(self, input).map_err(|e| {
262 crate::base::errors::SymplexError::ComputationFailed {
263 operation: "parse_implicit",
264 reason: e.to_string(),
265 }
266 })
267 }
268}
269
270#[derive(Clone, Copy)]
272struct Mode {
273 relations: bool,
275 implicit_app: bool,
277}
278
279impl Mode {
280 const STRICT: Mode = Mode {
281 relations: false,
282 implicit_app: false,
283 };
284 const RELATIONS: Mode = Mode {
285 relations: true,
286 implicit_app: false,
287 };
288 const IMPLICIT: Mode = Mode {
289 relations: false,
290 implicit_app: true,
291 };
292}
293
294fn is_bool_node(arena: &Arena, id: ExprId) -> bool {
296 matches!(
297 arena.node(id),
298 ExprNode::BoolTrue
299 | ExprNode::BoolFalse
300 | ExprNode::Gt(_, _)
301 | ExprNode::Ge(_, _)
302 | ExprNode::Eq_(_, _)
303 | ExprNode::Ne(_, _)
304 | ExprNode::And(_)
305 | ExprNode::Or(_)
306 | ExprNode::Not(_)
307 )
308}
309
310const KNOWN_FUNCTIONS: &[&str] = &[
314 "sin",
316 "cos",
317 "tan",
318 "exp",
319 "ln",
320 "log",
321 "sqrt",
322 "cbrt",
323 "abs",
324 "asin",
325 "arcsin",
326 "acos",
327 "arccos",
328 "atan",
329 "arctan",
330 "sinh",
331 "cosh",
332 "tanh",
333 "asinh",
334 "arcsinh",
335 "acosh",
336 "arccosh",
337 "atanh",
338 "arctanh",
339 "sign",
340 "sgn",
341 "floor",
342 "ceil",
343 "ceiling",
344 "gamma",
345 "erf",
346 "erfc",
347 "heaviside",
348 "diracdelta",
349 "dirac_delta",
350 "lambertw",
351 "w",
352 "factorial",
353 "digamma",
354 "loggamma",
355 "cot",
356 "sec",
357 "csc",
358 "coth",
359 "sech",
360 "csch",
361 "acot",
362 "arccot",
363 "re",
364 "im",
365 "conjugate",
366 "conj",
367 "arg",
368 "si",
369 "ci",
370 "ei",
371 "li",
372 "zeta",
373 "erfi",
374 "erfinv",
375 "erfcinv",
376 "e1",
377 "shi",
378 "chi",
379 "fresnels",
380 "fresnelc",
381 "dirichlet_eta",
382 "airyai",
383 "airybi",
384 "airyaiprime",
385 "airybiprime",
386 "elliptic_k",
387 "elliptic_e",
388 "rootof",
390 "conditionset",
391 "integral",
392 "atan2",
393 "polygamma",
394 "kroneckerdelta",
395 "kronecker_delta",
396 "binomial",
397 "c",
398 "beta",
399 "b",
400 "besselj",
401 "bessely",
402 "besseli",
403 "besselk",
404 "expint",
405 "lowergamma",
406 "uppergamma",
407 "polylog",
408 "elliptic_f",
409 "elliptic_pi",
410 "limit",
412 "laplacetransform",
413 "inverselaplacetransform",
414 "residue",
415 "dsolve",
416 "gegenbauer",
417 "assoc_legendre",
418 "assoc_laguerre",
419 "series",
421 "jacobi",
422 "betainc",
423 "betainc_regularized",
424 "min",
426 "max",
427 "sum",
428 "product",
429];
430
431fn is_known_function(name_lower: &str) -> bool {
432 KNOWN_FUNCTIONS.contains(&name_lower)
433}
434
435fn is_implicit_unary_function(name_lower: &str) -> bool {
439 matches!(
440 name_lower,
441 "sin"
442 | "cos"
443 | "tan"
444 | "cot"
445 | "sec"
446 | "csc"
447 | "sinh"
448 | "cosh"
449 | "tanh"
450 | "coth"
451 | "sech"
452 | "csch"
453 | "asin"
454 | "acos"
455 | "atan"
456 | "acot"
457 | "arcsin"
458 | "arccos"
459 | "arctan"
460 | "arccot"
461 | "asinh"
462 | "acosh"
463 | "atanh"
464 | "arcsinh"
465 | "arccosh"
466 | "arctanh"
467 | "exp"
468 | "ln"
469 | "log"
470 | "sqrt"
471 | "cbrt"
472 | "abs"
473 | "floor"
474 | "ceil"
475 | "ceiling"
476 | "sign"
477 | "sgn"
478 | "gamma"
479 | "erf"
480 | "erfc"
481 | "factorial"
482 )
483}
484
485fn constant_of(arena: &Arena, name: &str) -> Option<ExprId> {
487 Some(match name {
488 "pi" | "Pi" | "PI" => arena.pi,
489 "e" | "E" => arena.e_const,
490 "I" | "i" => arena.i_unit,
491 "inf" | "oo" | "Inf" => arena.infinity,
492 "zoo" => arena.complex_infinity,
493 "nan" => arena.nan,
494 "EulerGamma" | "euler_gamma" => arena.euler_gamma,
495 "Catalan" => arena.catalan,
496 "GoldenRatio" | "golden_ratio" => arena.golden_ratio,
497 _ => return None,
498 })
499}
500
501#[derive(Debug, Clone, PartialEq)]
506enum Token {
507 Int(BigInt),
508 Rational(Ratio<BigInt>),
509 Ident(String),
510 Plus,
511 Minus,
512 Star,
513 Slash,
514 Caret,
515 LParen,
516 RParen,
517 Comma,
518 Bang,
520 Eq,
522 DotDot,
524 Lt,
526 Le,
528 Gt,
530 Ge,
532 EqEq,
534 Ne,
536 Amp,
538 Pipe,
540 Tilde,
542 Eof,
543}
544
545struct Lexer<'a> {
546 input: &'a str,
547 pos: usize,
548}
549
550impl<'a> Lexer<'a> {
551 fn new(input: &'a str) -> Self {
552 Lexer { input, pos: 0 }
553 }
554
555 fn skip_whitespace(&mut self) {
556 while self.pos < self.input.len() && self.input.as_bytes()[self.pos].is_ascii_whitespace() {
557 self.pos += 1;
558 }
559 }
560
561 fn peek_is(&self, b: u8) -> bool {
563 self.input.as_bytes().get(self.pos) == Some(&b)
564 }
565
566 fn next_token(&mut self) -> Result<Token, ParseError> {
567 self.skip_whitespace();
568 if self.pos >= self.input.len() {
569 return Ok(Token::Eof);
570 }
571
572 let b = self.input.as_bytes()[self.pos];
573 match b {
574 b'+' => {
575 self.pos += 1;
576 Ok(Token::Plus)
577 }
578 b'-' => {
579 self.pos += 1;
580 Ok(Token::Minus)
581 }
582 b'*' => {
583 self.pos += 1;
584 if self.pos < self.input.len() && self.input.as_bytes()[self.pos] == b'*' {
586 self.pos += 1;
587 Ok(Token::Caret) } else {
589 Ok(Token::Star)
590 }
591 }
592 b'/' => {
593 self.pos += 1;
594 Ok(Token::Slash)
595 }
596 b'^' => {
597 self.pos += 1;
598 Ok(Token::Caret)
599 }
600 b'(' => {
601 self.pos += 1;
602 Ok(Token::LParen)
603 }
604 b')' => {
605 self.pos += 1;
606 Ok(Token::RParen)
607 }
608 b',' => {
609 self.pos += 1;
610 Ok(Token::Comma)
611 }
612 b'!' => {
613 self.pos += 1;
614 if self.peek_is(b'=') {
615 self.pos += 1;
616 Ok(Token::Ne)
617 } else {
618 Ok(Token::Bang)
619 }
620 }
621 b'=' => {
622 self.pos += 1;
623 if self.peek_is(b'=') {
624 self.pos += 1;
625 Ok(Token::EqEq)
626 } else {
627 Ok(Token::Eq)
628 }
629 }
630 b'<' => {
631 self.pos += 1;
632 if self.peek_is(b'=') {
633 self.pos += 1;
634 Ok(Token::Le)
635 } else {
636 Ok(Token::Lt)
637 }
638 }
639 b'>' => {
640 self.pos += 1;
641 if self.peek_is(b'=') {
642 self.pos += 1;
643 Ok(Token::Ge)
644 } else {
645 Ok(Token::Gt)
646 }
647 }
648 b'&' => {
649 self.pos += 1;
650 if self.peek_is(b'&') {
651 self.pos += 1;
652 }
653 Ok(Token::Amp)
654 }
655 b'|' => {
656 self.pos += 1;
657 if self.peek_is(b'|') {
658 self.pos += 1;
659 }
660 Ok(Token::Pipe)
661 }
662 b'~' => {
663 self.pos += 1;
664 Ok(Token::Tilde)
665 }
666 b'.' if self.pos + 1 < self.input.len()
667 && self.input.as_bytes()[self.pos + 1] == b'.' =>
668 {
669 self.pos += 2;
670 Ok(Token::DotDot)
671 }
672 b'0'..=b'9' => {
673 let start = self.pos;
674 while self.pos < self.input.len()
675 && self.input.as_bytes()[self.pos].is_ascii_digit()
676 {
677 self.pos += 1;
678 }
679 if self.pos < self.input.len()
681 && self.input.as_bytes()[self.pos] == b'.'
682 && self.pos + 1 < self.input.len()
683 && self.input.as_bytes()[self.pos + 1].is_ascii_digit()
684 {
685 self.pos += 1; let frac_start = self.pos;
687 while self.pos < self.input.len()
688 && self.input.as_bytes()[self.pos].is_ascii_digit()
689 {
690 self.pos += 1;
691 }
692 let decimal_places = self.pos - frac_start;
693 let full_str = self.input[start..self.pos].replace('.', "");
694 let numer = full_str.parse::<BigInt>().map_err(|e| ParseError {
695 message: format!(
696 "invalid number '{}': {}",
697 &self.input[start..self.pos],
698 e
699 ),
700 position: start,
701 })?;
702 let mut denom = BigInt::from(1);
703 for _ in 0..decimal_places {
704 denom *= 10;
705 }
706 let ratio = Ratio::new(numer, denom);
707 return Ok(Token::Rational(ratio));
708 }
709 let s = &self.input[start..self.pos];
710 let n = s.parse::<BigInt>().map_err(|e| ParseError {
711 message: format!("invalid integer '{}': {}", s, e),
712 position: start,
713 })?;
714 Ok(Token::Int(n))
715 }
716 b'a'..=b'z' | b'A'..=b'Z' | b'_' => {
717 let start = self.pos;
718 while self.pos < self.input.len() {
719 let c = self.input.as_bytes()[self.pos];
720 if c.is_ascii_alphanumeric() || c == b'_' {
721 self.pos += 1;
722 } else {
723 break;
724 }
725 }
726 let s = self.input[start..self.pos].to_string();
727 Ok(Token::Ident(s))
728 }
729 _ => Err(ParseError {
730 message: format!("unexpected character '{}'", b as char),
731 position: self.pos,
732 }),
733 }
734 }
735}
736
737const BP_OR: (u8, u8) = (1, 2);
745const BP_AND: (u8, u8) = (3, 4);
746const BP_REL: (u8, u8) = (5, 6);
747const BP_ADD: (u8, u8) = (7, 8);
748const BP_MUL: (u8, u8) = (9, 10);
749const BP_NEG: u8 = 11;
752const BP_POW: (u8, u8) = (14, 13);
754const BP_NOT: u8 = BP_REL.0;
757
758#[derive(Clone, Copy)]
760enum Infix {
761 Add,
762 Sub,
763 Mul,
764 Div,
765 Pow,
766 Rel(RelOp),
767 And,
768 Or,
769}
770
771#[derive(Clone, Copy)]
772enum RelOp {
773 Lt,
774 Le,
775 Gt,
776 Ge,
777 Eq,
778 Ne,
779}
780
781impl RelOp {
782 fn of(token: &Token) -> Option<RelOp> {
783 Some(match token {
784 Token::Lt => RelOp::Lt,
785 Token::Le => RelOp::Le,
786 Token::Gt => RelOp::Gt,
787 Token::Ge => RelOp::Ge,
788 Token::EqEq => RelOp::Eq,
789 Token::Ne => RelOp::Ne,
790 _ => return None,
791 })
792 }
793
794 fn text(self) -> &'static str {
795 match self {
796 RelOp::Lt => "<",
797 RelOp::Le => "<=",
798 RelOp::Gt => ">",
799 RelOp::Ge => ">=",
800 RelOp::Eq => "==",
801 RelOp::Ne => "!=",
802 }
803 }
804}
805
806fn starts_operand(token: &Token) -> bool {
808 matches!(
809 token,
810 Token::Int(_) | Token::Rational(_) | Token::Ident(_) | Token::LParen
811 )
812}
813
814struct Parser<'a> {
815 lexer: Lexer<'a>,
816 current: Token,
817 depth: usize,
818 mode: Mode,
819 app_arg: bool,
822}
823
824impl<'a> Parser<'a> {
825 fn new(input: &'a str, mode: Mode) -> Self {
826 let mut lexer = Lexer::new(input);
827 let current = lexer.next_token().unwrap_or(Token::Eof);
828 Parser {
829 lexer,
830 current,
831 depth: 0,
832 mode,
833 app_arg: false,
834 }
835 }
836
837 fn error(&self, message: String) -> ParseError {
838 ParseError {
839 message,
840 position: self.lexer.pos,
841 }
842 }
843
844 fn outside_app_arg<T>(
846 &mut self,
847 f: impl FnOnce(&mut Self) -> Result<T, ParseError>,
848 ) -> Result<T, ParseError> {
849 let saved = std::mem::replace(&mut self.app_arg, false);
850 let result = f(self);
851 self.app_arg = saved;
852 result
853 }
854
855 fn numeric_operands(
857 &self,
858 arena: &Arena,
859 op: &str,
860 lhs: ExprId,
861 rhs: ExprId,
862 ) -> Result<(), ParseError> {
863 for id in [lhs, rhs] {
864 if is_bool_node(arena, id) {
865 return Err(self.error(format!(
866 "'{op}' needs numeric operands, but '{}' is a Boolean expression",
867 arena.display(id)
868 )));
869 }
870 }
871 Ok(())
872 }
873
874 fn boolean_operand(&self, arena: &Arena, op: &str, id: ExprId) -> Result<(), ParseError> {
876 if is_bool_node(arena, id) {
877 Ok(())
878 } else {
879 Err(self.error(format!(
880 "'{op}' needs Boolean operands (relations), but '{}' is numeric",
881 arena.display(id)
882 )))
883 }
884 }
885
886 fn combine(
888 &self,
889 arena: &mut Arena,
890 op: Infix,
891 lhs: ExprId,
892 rhs: ExprId,
893 ) -> Result<ExprId, ParseError> {
894 match op {
895 Infix::Add => {
896 self.numeric_operands(arena, "+", lhs, rhs)?;
897 Ok(arena.add(&[lhs, rhs]))
898 }
899 Infix::Sub => {
900 self.numeric_operands(arena, "-", lhs, rhs)?;
901 Ok(arena.sub(lhs, rhs))
902 }
903 Infix::Mul => {
904 self.numeric_operands(arena, "*", lhs, rhs)?;
905 Ok(arena.mul(&[lhs, rhs]))
906 }
907 Infix::Div => {
908 self.numeric_operands(arena, "/", lhs, rhs)?;
909 Ok(arena.div(lhs, rhs))
910 }
911 Infix::Pow => {
912 self.numeric_operands(arena, "^", lhs, rhs)?;
913 Ok(arena.pow(lhs, rhs))
914 }
915 Infix::Rel(rel) => {
916 self.numeric_operands(arena, rel.text(), lhs, rhs)?;
917 if RelOp::of(&self.current).is_some() {
918 return Err(self.error(
919 "chained comparisons are not supported; write 'a < b & b < c'".into(),
920 ));
921 }
922 Ok(match rel {
923 RelOp::Lt => arena.gt(rhs, lhs),
924 RelOp::Le => arena.ge(rhs, lhs),
925 RelOp::Gt => arena.gt(lhs, rhs),
926 RelOp::Ge => arena.ge(lhs, rhs),
927 RelOp::Eq => arena.eq_(lhs, rhs),
928 RelOp::Ne => arena.ne_(lhs, rhs),
929 })
930 }
931 Infix::And => {
932 self.boolean_operand(arena, "&", lhs)?;
933 self.boolean_operand(arena, "&", rhs)?;
934 let mut items: SmallVec<[ExprId; 4]> = match arena.node(lhs) {
936 ExprNode::And(children) => children.iter().copied().collect(),
937 _ => SmallVec::from_slice(&[lhs]),
938 };
939 items.push(rhs);
940 Ok(arena.and(&items))
941 }
942 Infix::Or => {
943 self.boolean_operand(arena, "|", lhs)?;
944 self.boolean_operand(arena, "|", rhs)?;
945 let mut items: SmallVec<[ExprId; 4]> = match arena.node(lhs) {
946 ExprNode::Or(children) => children.iter().copied().collect(),
947 _ => SmallVec::from_slice(&[lhs]),
948 };
949 items.push(rhs);
950 Ok(arena.or(&items))
951 }
952 }
953 }
954
955 fn advance(&mut self) -> Result<Token, ParseError> {
956 let old = std::mem::replace(&mut self.current, Token::Eof);
957 self.current = self.lexer.next_token()?;
958 Ok(old)
959 }
960
961 fn expect(&mut self, expected: &Token) -> Result<(), ParseError> {
962 if &self.current == expected {
963 self.advance()?;
964 Ok(())
965 } else {
966 Err(ParseError {
967 message: format!("expected {:?}, got {:?}", expected, self.current),
968 position: self.lexer.pos,
969 })
970 }
971 }
972
973 fn parse_expr(&mut self, arena: &mut Arena, min_bp: u8) -> Result<ExprId, ParseError> {
975 self.depth += 1;
976 if self.depth > 128 {
977 return Err(ParseError {
978 message: "expression nesting too deep (max 128 levels)".into(),
979 position: self.lexer.pos,
980 });
981 }
982
983 let mut lhs = self.parse_prefix(arena)?;
985
986 loop {
988 if self.current == Token::Bang {
991 self.advance()?;
992 lhs = arena.intern(ExprNode::Factorial(lhs));
993 continue;
994 }
995 let relations = self.mode.relations;
996 let (op, (l_bp, r_bp), implicit) = match &self.current {
997 Token::Plus => (Infix::Add, BP_ADD, false),
998 Token::Minus => (Infix::Sub, BP_ADD, false),
999 Token::Star => (Infix::Mul, BP_MUL, false),
1000 Token::Slash => (Infix::Div, BP_MUL, false),
1001 Token::Caret => (Infix::Pow, BP_POW, false),
1002 Token::Lt | Token::Le | Token::Gt | Token::Ge | Token::EqEq | Token::Ne
1003 if relations =>
1004 {
1005 match RelOp::of(&self.current) {
1006 Some(rel) => (Infix::Rel(rel), BP_REL, false),
1007 None => break,
1008 }
1009 }
1010 Token::Amp if relations => (Infix::And, BP_AND, false),
1011 Token::Pipe if relations => (Infix::Or, BP_OR, false),
1012 Token::Ident(name) if relations && name == "and" => (Infix::And, BP_AND, false),
1013 Token::Ident(name) if relations && name == "or" => (Infix::Or, BP_OR, false),
1014 Token::Ident(name)
1017 if self.app_arg && is_implicit_unary_function(&name.to_ascii_lowercase()) =>
1018 {
1019 break;
1020 }
1021 Token::Int(_) | Token::Rational(_) | Token::Ident(_) | Token::LParen => {
1024 (Infix::Mul, BP_MUL, true)
1025 }
1026 _ => break,
1027 };
1028
1029 if l_bp < min_bp {
1030 break;
1031 }
1032
1033 if !implicit {
1034 self.advance()?;
1035 }
1036 let rhs = self.parse_expr(arena, r_bp)?;
1037 lhs = self.combine(arena, op, lhs, rhs)?;
1038 }
1039
1040 self.depth -= 1;
1041 Ok(lhs)
1042 }
1043
1044 fn parse_prefix(&mut self, arena: &mut Arena) -> Result<ExprId, ParseError> {
1046 match self.current.clone() {
1047 Token::Int(n) => {
1048 self.advance()?;
1049 Ok(arena.big_int(n))
1050 }
1051 Token::Rational(ratio) => {
1052 self.advance()?;
1053 let nid = arena.intern_num(ratio);
1054 Ok(arena.intern(ExprNode::Num(nid)))
1055 }
1056 Token::Ident(name) => {
1057 self.advance()?;
1058 let name_lower = name.to_ascii_lowercase();
1059 let is_call = !self.mode.implicit_app
1065 || (is_known_function(&name_lower) && name_lower.len() > 1);
1066 if self.current == Token::LParen && is_call {
1067 return self.outside_app_arg(|p| p.parse_function_call(arena, &name));
1068 }
1069 if self.mode.relations {
1070 match name.as_str() {
1071 "True" | "true" => return Ok(arena.bool_true()),
1072 "False" | "false" => return Ok(arena.bool_false()),
1073 "not" => return self.parse_not(arena),
1074 _ => {}
1075 }
1076 }
1077 if self.mode.implicit_app && is_implicit_unary_function(&name_lower) {
1078 if !starts_operand(&self.current) {
1081 return Err(self.error(format!(
1082 "function '{name}' needs an argument (write '{name}(x)' or '{name} x')"
1083 )));
1084 }
1085 let saved = std::mem::replace(&mut self.app_arg, true);
1086 let arg = self.parse_expr(arena, BP_MUL.0);
1087 self.app_arg = saved;
1088 return self.call_1(arena, &name, &name_lower, arg?);
1089 }
1090 match constant_of(arena, &name) {
1091 Some(c) => Ok(c),
1092 None => Ok(arena.symbol(&name)),
1093 }
1094 }
1095 Token::Minus => {
1096 self.advance()?;
1097 let operand = self.parse_expr(arena, BP_NEG)?;
1098 if is_bool_node(arena, operand) {
1099 return Err(self.error(format!(
1100 "'-' needs a numeric operand, but '{}' is a Boolean expression",
1101 arena.display(operand)
1102 )));
1103 }
1104 Ok(arena.neg(operand))
1105 }
1106 Token::Tilde | Token::Bang if self.mode.relations => {
1107 self.advance()?;
1108 self.parse_not(arena)
1109 }
1110 Token::LParen => {
1111 self.advance()?;
1112 let inner = self.outside_app_arg(|p| p.parse_expr(arena, 0))?;
1113 self.expect(&Token::RParen)?;
1114 Ok(inner)
1115 }
1116 other => Err(ParseError {
1117 message: format!("expected expression, got {:?}", other),
1118 position: self.lexer.pos,
1119 }),
1120 }
1121 }
1122
1123 fn parse_not(&mut self, arena: &mut Arena) -> Result<ExprId, ParseError> {
1126 let operand = self.parse_expr(arena, BP_NOT)?;
1127 self.boolean_operand(arena, "not", operand)?;
1128 Ok(arena.not(operand))
1129 }
1130
1131 const MAX_FN_ARGS: usize = 32;
1133
1134 fn parse_function_call(&mut self, arena: &mut Arena, name: &str) -> Result<ExprId, ParseError> {
1135 self.expect(&Token::LParen)?;
1136
1137 if self.current == Token::RParen {
1138 self.advance()?;
1139 return Err(ParseError {
1140 message: format!("function '{}' requires an argument", name),
1141 position: self.lexer.pos,
1142 });
1143 }
1144
1145 let name_lower = name.to_ascii_lowercase();
1147 let mut args: Vec<ExprId> = vec![self.parse_expr(arena, 0)?];
1148
1149 if matches!(name_lower.as_str(), "sum" | "product") && self.current == Token::Comma {
1151 self.advance()?;
1152 let var = self.parse_expr(arena, 0)?;
1153 if self.current == Token::Eq {
1154 self.advance()?;
1155 let lo = self.parse_expr(arena, 0)?;
1156 self.expect(&Token::DotDot)?;
1157 let hi = self.parse_expr(arena, 0)?;
1158 self.expect(&Token::RParen)?;
1159 return self.make_sum_product(arena, name, &name_lower, args[0], var, lo, hi);
1160 }
1161 args.push(var);
1162 }
1163
1164 while self.current == Token::Comma {
1165 self.advance()?;
1166 if args.len() >= Self::MAX_FN_ARGS {
1167 return Err(ParseError {
1168 message: format!(
1169 "function '{}' has too many arguments (max {})",
1170 name,
1171 Self::MAX_FN_ARGS
1172 ),
1173 position: self.lexer.pos,
1174 });
1175 }
1176 args.push(self.parse_expr(arena, 0)?);
1177 }
1178 self.expect(&Token::RParen)?;
1179
1180 if self.mode.relations
1183 && let Some(result) = self.call_boolean(arena, name, &name_lower, &args)?
1184 {
1185 return Ok(result);
1186 }
1187
1188 if matches!(name_lower.as_str(), "min" | "max") {
1190 if args.len() < 2 {
1191 return Err(ParseError {
1192 message: format!("function '{}' requires at least 2 arguments", name),
1193 position: self.lexer.pos,
1194 });
1195 }
1196 let ids: SmallVec<[ExprId; 4]> = args.iter().copied().collect();
1197 return Ok(arena.intern(if name_lower == "min" {
1198 ExprNode::Min(ids)
1199 } else {
1200 ExprNode::Max(ids)
1201 }));
1202 }
1203
1204 match args.len() {
1205 1 => self.call_1(arena, name, &name_lower, args[0]),
1206 2 => self.call_2(arena, name, &name_lower, args[0], args[1]),
1207 3 => self.call_3(arena, name, &name_lower, args[0], args[1], args[2]),
1208 4 => self.call_4(arena, name, &name_lower, args[0], args[1], args[2], args[3]),
1209 n => Err(ParseError {
1210 message: format!(
1211 "unknown {n}-argument function '{}'. Only min and max take more than 4 arguments",
1212 name
1213 ),
1214 position: self.lexer.pos,
1215 }),
1216 }
1217 }
1218
1219 fn call_boolean(
1223 &self,
1224 arena: &mut Arena,
1225 name: &str,
1226 name_lower: &str,
1227 args: &[ExprId],
1228 ) -> Result<Option<ExprId>, ParseError> {
1229 let rel = match name_lower {
1230 "eq" => Some(RelOp::Eq),
1231 "ne" => Some(RelOp::Ne),
1232 "lt" => Some(RelOp::Lt),
1233 "le" => Some(RelOp::Le),
1234 "gt" => Some(RelOp::Gt),
1235 "ge" => Some(RelOp::Ge),
1236 _ => None,
1237 };
1238 if let Some(rel) = rel {
1239 let [lhs, rhs] = args else {
1240 return Err(self.error(format!(
1241 "'{name}' takes exactly 2 arguments, got {}",
1242 args.len()
1243 )));
1244 };
1245 self.numeric_operands(arena, rel.text(), *lhs, *rhs)?;
1246 return Ok(Some(match rel {
1247 RelOp::Lt => arena.gt(*rhs, *lhs),
1248 RelOp::Le => arena.ge(*rhs, *lhs),
1249 RelOp::Gt => arena.gt(*lhs, *rhs),
1250 RelOp::Ge => arena.ge(*lhs, *rhs),
1251 RelOp::Eq => arena.eq_(*lhs, *rhs),
1252 RelOp::Ne => arena.ne_(*lhs, *rhs),
1253 }));
1254 }
1255 match name_lower {
1256 "and" | "or" => {
1257 for &a in args {
1258 self.boolean_operand(arena, name, a)?;
1259 }
1260 Ok(Some(if name_lower == "and" {
1261 arena.and(args)
1262 } else {
1263 arena.or(args)
1264 }))
1265 }
1266 "not" => {
1267 let [a] = args else {
1268 return Err(self.error(format!(
1269 "'{name}' takes exactly 1 argument, got {}",
1270 args.len()
1271 )));
1272 };
1273 self.boolean_operand(arena, name, *a)?;
1274 Ok(Some(arena.not(*a)))
1275 }
1276 _ => Ok(None),
1277 }
1278 }
1279
1280 #[allow(clippy::too_many_arguments)]
1282 fn make_sum_product(
1283 &self,
1284 arena: &mut Arena,
1285 name: &str,
1286 name_lower: &str,
1287 body: ExprId,
1288 var: ExprId,
1289 lo: ExprId,
1290 hi: ExprId,
1291 ) -> Result<ExprId, ParseError> {
1292 if !matches!(arena.node(var), ExprNode::Symbol(_)) {
1293 return Err(ParseError {
1294 message: format!(
1295 "the index of '{}' must be a symbol, got '{}'",
1296 name,
1297 arena.display(var)
1298 ),
1299 position: self.lexer.pos,
1300 });
1301 }
1302 Ok(arena.intern(if name_lower == "sum" {
1303 ExprNode::Sum(body, var, lo, hi)
1304 } else {
1305 ExprNode::Product_(body, var, lo, hi)
1306 }))
1307 }
1308
1309 #[allow(clippy::too_many_arguments)]
1310 fn call_4(
1311 &self,
1312 arena: &mut Arena,
1313 name: &str,
1314 name_lower: &str,
1315 arg: ExprId,
1316 arg2: ExprId,
1317 arg3: ExprId,
1318 arg4: ExprId,
1319 ) -> Result<ExprId, ParseError> {
1320 match name_lower {
1321 "series" => Ok(arena.intern(ExprNode::Series(arg, arg2, arg3, arg4))),
1322 "integral" => Ok(arena.definite_integral(arg, arg2, arg3, arg4)),
1325 "sum" | "product" => {
1327 self.make_sum_product(arena, name, name_lower, arg, arg2, arg3, arg4)
1328 }
1329 "jacobi" => Ok(apply_named(arena, FN_JACOBI, &[arg, arg2, arg3, arg4])),
1330 "betainc" => Ok(apply_named(arena, FN_BETAINC, &[arg, arg2, arg3, arg4])),
1332 "betainc_regularized" => Ok(apply_named(
1333 arena,
1334 FN_BETAINC_REGULARIZED,
1335 &[arg, arg2, arg3, arg4],
1336 )),
1337 _ => Err(ParseError {
1338 message: format!(
1339 "unknown 4-argument function '{}'. Supported: Series, Sum, Product, Integral, \
1340 jacobi, betainc, betainc_regularized",
1341 name
1342 ),
1343 position: self.lexer.pos,
1344 }),
1345 }
1346 }
1347
1348 fn call_3(
1349 &self,
1350 arena: &mut Arena,
1351 name: &str,
1352 name_lower: &str,
1353 arg: ExprId,
1354 arg2: ExprId,
1355 arg3: ExprId,
1356 ) -> Result<ExprId, ParseError> {
1357 match name_lower {
1358 "limit" => Ok(arena.intern(ExprNode::Limit(arg, arg2, arg3))),
1359 "laplacetransform" => Ok(arena.intern(ExprNode::LaplaceTransform(arg, arg2, arg3))),
1360 "inverselaplacetransform" => {
1361 Ok(arena.intern(ExprNode::InverseLaplaceTransform(arg, arg2, arg3)))
1362 }
1363 "residue" => Ok(arena.intern(ExprNode::Residue(arg, arg2, arg3))),
1364 "dsolve" => Ok(arena.intern(ExprNode::DSolve(arg, arg2, arg3))),
1365 "gegenbauer" => Ok(apply_named(arena, FN_GEGENBAUER, &[arg, arg2, arg3])),
1367 "assoc_legendre" => Ok(apply_named(arena, FN_ASSOC_LEGENDRE, &[arg, arg2, arg3])),
1368 "assoc_laguerre" => Ok(apply_named(arena, FN_ASSOC_LAGUERRE, &[arg, arg2, arg3])),
1369 _ => Err(ParseError {
1370 message: format!(
1371 "unknown 3-argument function '{}'. Supported: Limit, LaplaceTransform, \
1372 InverseLaplaceTransform, Residue, DSolve, min, max, gegenbauer, \
1373 assoc_legendre, assoc_laguerre",
1374 name
1375 ),
1376 position: self.lexer.pos,
1377 }),
1378 }
1379 }
1380
1381 fn call_2(
1382 &self,
1383 arena: &mut Arena,
1384 name: &str,
1385 name_lower: &str,
1386 arg: ExprId,
1387 arg2: ExprId,
1388 ) -> Result<ExprId, ParseError> {
1389 match name_lower {
1390 "log" => {
1391 let ln_x = arena.ln(arg);
1393 let ln_base = arena.ln(arg2);
1394 Ok(arena.div(ln_x, ln_base))
1395 }
1396 "rootof" => Ok(arena.intern(ExprNode::RootOf(arg, arg2))),
1397 "conditionset" => Ok(arena.intern(ExprNode::ConditionSet(arg, arg2))),
1398 "integral" => Ok(arena.intern(ExprNode::Integral(arg, arg2))),
1400 "atan2" => Ok(arena.atan2(arg, arg2)),
1401 "polygamma" => Ok(arena.polygamma(arg, arg2)),
1402 "kroneckerdelta" | "kronecker_delta" => Ok(arena.kronecker_delta(arg, arg2)),
1403 "binomial" | "c" => Ok(arena.binomial(arg, arg2)),
1406 "beta" | "b" => Ok(arena.beta(arg, arg2)),
1407 "besselj" => Ok(arena.besselj(arg, arg2)),
1409 "bessely" => Ok(arena.bessely(arg, arg2)),
1410 "besseli" => Ok(arena.besseli(arg, arg2)),
1411 "besselk" => Ok(arena.besselk(arg, arg2)),
1412 "expint" => Ok(apply_named(arena, FN_EXPINT, &[arg, arg2])),
1414 "lowergamma" => Ok(apply_named(arena, FN_LOWERGAMMA, &[arg, arg2])),
1415 "uppergamma" => Ok(apply_named(arena, FN_UPPERGAMMA, &[arg, arg2])),
1416 "polylog" => Ok(apply_named(arena, FN_POLYLOG, &[arg, arg2])),
1417 "elliptic_f" => Ok(apply_named(arena, FN_ELLIPTIC_F, &[arg, arg2])),
1418 "elliptic_pi" => Ok(apply_named(arena, FN_ELLIPTIC_PI, &[arg, arg2])),
1419 _ => Err(ParseError {
1420 message: format!(
1421 "unknown 2-argument function '{}'. Supported: log, atan2, polygamma, \
1422 binomial, beta, besselj, bessely, besseli, besselk, expint, lowergamma, \
1423 uppergamma, polylog, elliptic_f, elliptic_pi, min, max, KroneckerDelta, \
1424 RootOf, ConditionSet, Integral",
1425 name
1426 ),
1427 position: self.lexer.pos,
1428 }),
1429 }
1430 }
1431
1432 fn call_1(
1433 &self,
1434 arena: &mut Arena,
1435 name: &str,
1436 name_lower: &str,
1437 arg: ExprId,
1438 ) -> Result<ExprId, ParseError> {
1439 match name_lower {
1440 "sin" => Ok(arena.sin(arg)),
1441 "cos" => Ok(arena.cos(arg)),
1442 "tan" => Ok(arena.tan(arg)),
1443 "exp" => Ok(arena.exp(arg)),
1444 "ln" | "log" => Ok(arena.ln(arg)),
1445 "sqrt" => Ok(arena.sqrt(arg)),
1446 "cbrt" => Ok(arena.cbrt(arg)),
1447 "abs" => Ok(arena.abs(arg)),
1448 "asin" | "arcsin" => Ok(arena.asin(arg)),
1449 "acos" | "arccos" => Ok(arena.acos(arg)),
1450 "atan" | "arctan" => Ok(arena.atan(arg)),
1451 "sinh" => Ok(arena.sinh(arg)),
1452 "cosh" => Ok(arena.cosh(arg)),
1453 "tanh" => Ok(arena.tanh(arg)),
1454 "asinh" | "arcsinh" => Ok(arena.asinh(arg)),
1455 "acosh" | "arccosh" => Ok(arena.acosh(arg)),
1456 "atanh" | "arctanh" => Ok(arena.atanh(arg)),
1457 "sign" | "sgn" => Ok(arena.sign(arg)),
1458 "floor" => Ok(arena.floor(arg)),
1459 "ceil" | "ceiling" => Ok(arena.ceiling(arg)),
1460 "gamma" => Ok(arena.intern(crate::base::node::ExprNode::Gamma(arg))),
1461 "erf" => Ok(arena.intern(crate::base::node::ExprNode::Erf(arg))),
1462 "erfc" => Ok(arena.intern(crate::base::node::ExprNode::Erfc(arg))),
1463 "heaviside" => Ok(arena.intern(crate::base::node::ExprNode::Heaviside(arg))),
1464 "diracdelta" | "dirac_delta" => {
1465 Ok(arena.intern(crate::base::node::ExprNode::DiracDelta(arg)))
1466 }
1467 "lambertw" | "w" => Ok(arena.intern(crate::base::node::ExprNode::LambertW(arg))),
1468 "factorial" => Ok(arena.intern(crate::base::node::ExprNode::Factorial(arg))),
1469 "digamma" => Ok(arena.intern(crate::base::node::ExprNode::Digamma(arg))),
1470 "loggamma" => Ok(arena.intern(crate::base::node::ExprNode::LogGamma(arg))),
1471 "cot" => {
1474 let c = arena.cos(arg);
1475 let s = arena.sin(arg);
1476 Ok(arena.div(c, s))
1477 }
1478 "sec" => {
1479 let c = arena.cos(arg);
1480 Ok(arena.div(arena.one, c))
1481 }
1482 "csc" => {
1483 let s = arena.sin(arg);
1484 Ok(arena.div(arena.one, s))
1485 }
1486 "coth" => {
1487 let c = arena.cosh(arg);
1488 let s = arena.sinh(arg);
1489 Ok(arena.div(c, s))
1490 }
1491 "sech" => {
1492 let c = arena.cosh(arg);
1493 Ok(arena.div(arena.one, c))
1494 }
1495 "csch" => {
1496 let s = arena.sinh(arg);
1497 Ok(arena.div(arena.one, s))
1498 }
1499 "acot" | "arccot" => {
1500 let inv = arena.div(arena.one, arg);
1501 Ok(arena.atan(inv))
1502 }
1503 "re" => Ok(arena.re(arg)),
1505 "im" => Ok(arena.im(arg)),
1506 "conjugate" | "conj" => Ok(arena.conjugate(arg)),
1507 "arg" => Ok(arena.arg(arg)),
1508 "si" => Ok(arena.si(arg)),
1510 "ci" => Ok(arena.ci(arg)),
1511 "ei" => Ok(arena.ei(arg)),
1512 "li" => Ok(arena.li(arg)),
1513 "zeta" => Ok(arena.zeta(arg)),
1514 "erfi" => Ok(apply_named(arena, FN_ERFI, &[arg])),
1516 "erfinv" => Ok(apply_named(arena, FN_ERFINV, &[arg])),
1517 "erfcinv" => Ok(apply_named(arena, FN_ERFCINV, &[arg])),
1518 "e1" => Ok(apply_named(arena, FN_EXPINT, &[arena.one, arg])),
1519 "shi" => Ok(apply_named(arena, FN_SHI, &[arg])),
1520 "chi" => Ok(apply_named(arena, FN_CHI, &[arg])),
1521 "fresnels" => Ok(apply_named(arena, FN_FRESNELS, &[arg])),
1522 "fresnelc" => Ok(apply_named(arena, FN_FRESNELC, &[arg])),
1523 "dirichlet_eta" => Ok(apply_named(arena, FN_DIRICHLET_ETA, &[arg])),
1524 "airyai" => Ok(apply_named(arena, FN_AIRYAI, &[arg])),
1525 "airybi" => Ok(apply_named(arena, FN_AIRYBI, &[arg])),
1526 "airyaiprime" => Ok(apply_named(arena, FN_AIRYAIPRIME, &[arg])),
1527 "airybiprime" => Ok(apply_named(arena, FN_AIRYBIPRIME, &[arg])),
1528 "elliptic_k" => Ok(apply_named(arena, FN_ELLIPTIC_K, &[arg])),
1529 "elliptic_e" => Ok(apply_named(arena, FN_ELLIPTIC_E, &[arg])),
1530 _ => Err(ParseError {
1531 message: format!(
1532 "unknown function '{}'. Supported: sin, cos, tan, cot, sec, csc, exp, ln, log, \
1533 sqrt, cbrt, abs, asin, acos, atan, acot, sinh, cosh, tanh, coth, sech, csch, \
1534 asinh, acosh, atanh, sign, floor, ceil, gamma, erf, erfc, heaviside, \
1535 diracdelta, lambertw, factorial, digamma, loggamma, re, im, conjugate, arg, \
1536 Si, Ci, Ei, li, zeta, polygamma, binomial, beta, besselj, bessely, besseli, \
1537 besselk, erfi, erfinv, erfcinv, E1, expint, Shi, Chi, fresnels, fresnelc, \
1538 lowergamma, uppergamma, polylog, dirichlet_eta, airyai, airybi, \
1539 airyaiprime, airybiprime, elliptic_k, elliptic_e, elliptic_f, elliptic_pi, \
1540 gegenbauer, jacobi, assoc_legendre, assoc_laguerre, betainc, \
1541 betainc_regularized, min, max, \
1542 KroneckerDelta, Limit, RootOf, ConditionSet, LaplaceTransform, \
1543 InverseLaplaceTransform, Residue, DSolve, Series, Sum, Product, Integral",
1544 name
1545 ),
1546 position: self.lexer.pos,
1547 }),
1548 }
1549 }
1550}
1551
1552fn apply_named(arena: &mut Arena, name: &str, args: &[ExprId]) -> ExprId {
1555 let sid = arena.symbols.intern(name);
1556 arena.intern(ExprNode::Apply(sid, args.iter().copied().collect()))
1557}
1558
1559#[cfg(test)]
1564mod tests {
1565 use super::*;
1566 use crate::api::context::Context;
1567
1568 fn parse_and_display(input: &str) -> String {
1569 let ctx = Context::new();
1570 let ex = parse(&ctx, input).unwrap();
1571 format!("{ex}")
1572 }
1573
1574 #[test]
1575 fn parse_integer() {
1576 assert_eq!(parse_and_display("42"), "42");
1577 }
1578
1579 #[test]
1580 fn parse_symbol() {
1581 assert_eq!(parse_and_display("x"), "x");
1582 }
1583
1584 #[test]
1585 fn parse_addition() {
1586 assert_eq!(parse_and_display("x + y"), "x + y");
1587 }
1588
1589 #[test]
1590 fn parse_polynomial() {
1591 let s = parse_and_display("x^2 + 2*x + 1");
1592 assert!(s.contains("x^2") && s.contains("2*x"), "got: {s}");
1593 }
1594
1595 #[test]
1596 fn parse_function_sin() {
1597 assert_eq!(parse_and_display("sin(x)"), "sin(x)");
1598 }
1599
1600 #[test]
1601 fn parse_nested_functions() {
1602 assert_eq!(parse_and_display("sin(cos(x))"), "sin(cos(x))");
1603 }
1604
1605 #[test]
1606 fn parse_negation() {
1607 let s = parse_and_display("-x");
1608 assert!(s.contains("x") && s.starts_with('-'), "got: {s}");
1609 }
1610
1611 #[test]
1612 fn parse_power_right_assoc() {
1613 let s = parse_and_display("x^2^3");
1615 assert!(s.contains("x"), "got: {s}");
1616 }
1617
1618 #[test]
1619 fn parse_constant_pi() {
1620 assert_eq!(parse_and_display("pi"), "pi");
1621 }
1622
1623 #[test]
1624 fn parse_precedence() {
1625 let s = parse_and_display("2 + 3*x");
1627 assert!(s.contains("3*x"), "got: {s}");
1628 }
1629
1630 #[test]
1631 fn parse_parens() {
1632 let s = parse_and_display("(x + 1)^2");
1633 assert!(
1634 s.contains("(1 + x)^2") || s.contains("(x + 1)^2"),
1635 "got: {s}"
1636 );
1637 }
1638
1639 #[test]
1640 fn parse_division() {
1641 let s = parse_and_display("x / y");
1642 assert!(
1644 s.contains("1/y") || s.contains("x*1/y") || s.contains("x/y") || s.contains("y^(-1)"),
1645 "got: {s}"
1646 );
1647 }
1648
1649 #[test]
1650 fn parse_empty_string_error() {
1651 let ctx = Context::new();
1652 assert!(parse(&ctx, "").is_err());
1653 }
1654
1655 #[test]
1656 fn parse_unknown_function_error() {
1657 let ctx = Context::new();
1658 assert!(parse(&ctx, "foo(x)").is_err());
1659 }
1660
1661 #[test]
1662 fn parse_unary_minus_precedence() {
1663 let s = parse_and_display("-x^2");
1665 assert!(s.contains("x^2"), "got: {s}");
1667 }
1668
1669 #[test]
1670 fn parse_subtraction() {
1671 let s = parse_and_display("x - y");
1672 assert!(s.contains("x") && s.contains("y"), "got: {s}");
1674 }
1675
1676 #[test]
1677 fn parse_multiple_operations() {
1678 let s = parse_and_display("2*x + 3*y - z");
1679 assert!(
1680 s.contains("2*x") && s.contains("3*y") && s.contains("z"),
1681 "got: {s}"
1682 );
1683 }
1684
1685 #[test]
1686 fn parse_constant_e() {
1687 assert_eq!(parse_and_display("e"), "E");
1688 }
1689
1690 #[test]
1691 fn parse_exp_function() {
1692 let s = parse_and_display("exp(x)");
1693 assert!(s.contains("x"), "got: {s}");
1695 }
1696
1697 #[test]
1698 fn parse_sqrt_function() {
1699 let s = parse_and_display("sqrt(x)");
1700 assert!(s.contains("x"), "got: {s}");
1702 }
1703
1704 #[test]
1705 fn parse_complex_nested() {
1706 let s = parse_and_display("sin(x^2 + 1)");
1707 assert!(s.contains("sin"), "got: {s}");
1708 }
1709
1710 #[test]
1711 fn parse_trailing_garbage_error() {
1712 let ctx = Context::new();
1713 assert!(parse(&ctx, "x )").is_err());
1716 }
1717
1718 #[test]
1719 fn parse_unmatched_paren_error() {
1720 let ctx = Context::new();
1721 assert!(parse(&ctx, "(x + 1").is_err());
1722 }
1723
1724 #[test]
1725 fn parse_unexpected_char_error() {
1726 let ctx = Context::new();
1727 assert!(parse(&ctx, "x & y").is_err());
1728 }
1729
1730 #[test]
1731 fn parse_empty_function_call_error() {
1732 let ctx = Context::new();
1733 assert!(parse(&ctx, "sin()").is_err());
1734 }
1735
1736 #[test]
1737 fn parse_abs_function() {
1738 let s = parse_and_display("abs(x)");
1739 assert!(s.contains("x"), "got: {s}");
1740 }
1741
1742 #[test]
1743 fn parse_ln_function() {
1744 let s = parse_and_display("ln(x)");
1745 assert!(s.contains("x"), "got: {s}");
1746 }
1747
1748 #[test]
1749 fn parse_log_alias() {
1750 let s = parse_and_display("log(x)");
1751 assert!(s.contains("x"), "got: {s}");
1752 }
1753
1754 #[test]
1755 fn parse_constant_infinity() {
1756 let s = parse_and_display("inf");
1757 assert!(
1758 s.contains("oo") || s.contains("inf") || s.contains("∞"),
1759 "got: {s}"
1760 );
1761 }
1762
1763 #[test]
1764 fn parse_deeply_nested_parens() {
1765 let s = parse_and_display("((((x))))");
1766 assert_eq!(s, "x");
1767 }
1768
1769 #[test]
1770 fn parse_chained_additions() {
1771 let s = parse_and_display("a + b + c + d");
1772 assert!(
1773 s.contains("a") && s.contains("b") && s.contains("c") && s.contains("d"),
1774 "got: {s}"
1775 );
1776 }
1777
1778 #[test]
1779 fn parse_chained_multiplications() {
1780 let s = parse_and_display("a * b * c");
1781 assert!(
1782 s.contains("a") && s.contains("b") && s.contains("c"),
1783 "got: {s}"
1784 );
1785 }
1786
1787 #[test]
1788 fn parse_mixed_precedence() {
1789 let s = parse_and_display("a + b * c");
1791 assert!(s.contains("b*c") || s.contains("c*b"), "got: {s}");
1792 }
1793
1794 #[test]
1799 fn parse_float() {
1800 let ctx = Context::new();
1801 let result = parse(&ctx, "3.14").unwrap();
1802 let s = format!("{result}");
1803 assert!(
1805 s.contains("157") || s.contains("3.14") || s.contains("314"),
1806 "got: {s}"
1807 );
1808 }
1809
1810 #[test]
1811 fn parse_implicit_mul_number_var() {
1812 let ctx = Context::new();
1813 let result = parse(&ctx, "2x").unwrap();
1814 let s = format!("{result}");
1815 assert!(s.contains("2") && s.contains("x"), "2x should be 2*x: {s}");
1816 }
1817
1818 #[test]
1819 fn parse_implicit_mul_var_paren() {
1820 let ctx = Context::new();
1823 assert!(parse(&ctx, "x(x+1)").is_err());
1824 }
1825
1826 #[test]
1827 fn parse_constant_pi_variants() {
1828 assert_eq!(parse_and_display("pi"), "pi");
1829 assert_eq!(parse_and_display("Pi"), "pi");
1830 assert_eq!(parse_and_display("PI"), "pi");
1831 }
1832
1833 #[test]
1834 fn parse_constant_i_unit() {
1835 let ctx = Context::new();
1836 let result = parse(&ctx, "I").unwrap();
1837 assert_eq!(format!("{result}"), "I");
1838 }
1839
1840 #[test]
1841 fn parse_constant_i_lowercase() {
1842 let ctx = Context::new();
1843 let result = parse(&ctx, "i").unwrap();
1844 assert_eq!(format!("{result}"), "I");
1845 }
1846
1847 #[test]
1848 fn parse_euler_formula() {
1849 let ctx = Context::new();
1850 let result = parse(&ctx, "exp(I*pi)").unwrap();
1851 let s = format!("{result}");
1852 assert!(s.contains("I") && s.contains("pi"), "got: {s}");
1853 }
1854
1855 #[test]
1856 fn parse_float_times_var() {
1857 let ctx = Context::new();
1858 let result = parse(&ctx, "2.5*x").unwrap();
1859 let s = format!("{result}");
1860 assert!(
1861 s.contains("x") && (s.contains("5/2") || s.contains("2.5") || s.contains("5*1/2")),
1862 "got: {s}"
1863 );
1864 }
1865
1866 #[test]
1867 fn parse_log_two_args() {
1868 let ctx = Context::new();
1869 let result = parse(&ctx, "log(x, 2)").unwrap();
1871 let s = format!("{result}");
1872 assert!(s.contains("x"), "log(x,2) should parse: {s}");
1873 }
1874
1875 #[test]
1876 fn parse_implicit_mul_number_paren() {
1877 let ctx = Context::new();
1878 let result = parse(&ctx, "3(x+1)").unwrap();
1879 let s = format!("{result}");
1880 assert!(
1881 s.contains("3") && s.contains("x"),
1882 "3(x+1) should be 3*(x+1): {s}"
1883 );
1884 }
1885
1886 #[test]
1887 fn parse_implicit_mul_paren_paren() {
1888 let ctx = Context::new();
1889 let result = parse(&ctx, "(a)(b)").unwrap();
1890 let s = format!("{result}");
1891 assert!(
1892 s.contains("a") && s.contains("b"),
1893 "(a)(b) should be a*b: {s}"
1894 );
1895 }
1896
1897 #[test]
1898 fn parse_implicit_mul_coeff_pi() {
1899 let ctx = Context::new();
1900 let result = parse(&ctx, "2pi").unwrap();
1901 let s = format!("{result}");
1902 assert!(
1903 s.contains("2") && s.contains("pi"),
1904 "2pi should be 2*pi: {s}"
1905 );
1906 }
1907
1908 #[test]
1913 fn parse_large_integer() {
1914 let ctx = Context::new();
1915 let result = parse(&ctx, "99999999999999999999999999999").unwrap();
1916 let s = format!("{result}");
1917 assert_eq!(s, "99999999999999999999999999999");
1918 }
1919
1920 #[test]
1921 fn parse_integer_beyond_i64_max() {
1922 let ctx = Context::new();
1923 let result = parse(&ctx, "9223372036854775808").unwrap();
1925 let s = format!("{result}");
1926 assert_eq!(
1927 s, "9223372036854775808",
1928 "should handle integers > i64::MAX"
1929 );
1930 }
1931
1932 #[test]
1933 fn parse_integer_beyond_i128_max() {
1934 let ctx = Context::new();
1935 let big = "123456789012345678901234567890123456789012345678901234567890";
1937 let result = parse(&ctx, big).unwrap();
1938 let s = format!("{result}");
1939 assert_eq!(s, big, "should handle integers > i128::MAX");
1940 }
1941
1942 #[test]
1943 fn parse_large_integer_arithmetic() {
1944 let ctx = Context::new();
1945 let result = parse(&ctx, "1000000000000000000000000000000^2").unwrap();
1947 let evaled = result.eval();
1948 let s = format!("{evaled}");
1949 assert_eq!(
1950 s, "1000000000000000000000000000000000000000000000000000000000000",
1951 "large integer exponentiation should be exact"
1952 );
1953 }
1954
1955 #[test]
1956 fn parse_negative_large_integer() {
1957 let ctx = Context::new();
1958 let result = parse(&ctx, "-99999999999999999999999999999").unwrap();
1959 let s = format!("{result}");
1960 assert_eq!(s, "-99999999999999999999999999999");
1961 }
1962
1963 #[test]
1968 fn parse_many_decimal_places() {
1969 let ctx = Context::new();
1970 let result = parse(&ctx, "1.0000000000000000000001");
1972 assert!(
1973 result.is_ok(),
1974 "parsing many decimal places should not panic"
1975 );
1976 }
1977
1978 #[test]
1979 fn parse_decimal_exact_rational_simple() {
1980 let ctx = Context::new();
1981 let result = parse(&ctx, "0.5").unwrap();
1983 let s = format!("{result}");
1984 assert_eq!(s, "1/2", "0.5 should parse as exact rational 1/2, got: {s}");
1985 }
1986
1987 #[test]
1988 fn parse_decimal_exact_rational_quarter() {
1989 let ctx = Context::new();
1990 let result = parse(&ctx, "0.25").unwrap();
1992 let s = format!("{result}");
1993 assert_eq!(
1994 s, "1/4",
1995 "0.25 should parse as exact rational 1/4, got: {s}"
1996 );
1997 }
1998
1999 #[test]
2000 fn parse_decimal_exact_rational_third_approx() {
2001 let ctx = Context::new();
2002 let result = parse(&ctx, "0.333").unwrap();
2004 let s = format!("{result}");
2005 assert_eq!(
2006 s, "333/1000",
2007 "0.333 should parse as exact rational 333/1000, got: {s}"
2008 );
2009 }
2010
2011 #[test]
2012 fn parse_decimal_preserves_all_digits() {
2013 let ctx = Context::new();
2014 let input = "1.00000000000000000000000000000000001";
2019 let result = parse(&ctx, input).unwrap();
2020 let big_denom = parse(&ctx, "100000000000000000000000000000000000").unwrap();
2022 let product = &result * &big_denom;
2023 let s = format!("{}", product.eval());
2024 assert_eq!(
2025 s, "100000000000000000000000000000000001",
2026 "1.00000000000000000000000000000000001 * 10^35 should be exact, got: {s}"
2027 );
2028 }
2029
2030 #[test]
2031 fn parse_decimal_20_places_exact() {
2032 let ctx = Context::new();
2033 let result = parse(&ctx, "3.14159265358979323846").unwrap();
2038 let s = format!("{result}");
2039 assert_eq!(
2040 s, "157079632679489661923/50000000000000000000",
2041 "20-digit decimal should be exact reduced rational"
2042 );
2043 }
2044
2045 #[test]
2046 fn parse_decimal_20_places_multiply_back() {
2047 let ctx = Context::new();
2048 let result = parse(&ctx, "3.14159265358979323846").unwrap();
2051 let denom = parse(&ctx, "50000000000000000000").unwrap();
2052 let product = (&result * &denom).eval();
2053 let s = format!("{product}");
2054 assert_eq!(
2055 s, "157079632679489661923",
2056 "rational * denominator should recover exact numerator"
2057 );
2058 }
2059
2060 #[test]
2061 fn parse_decimal_50_places_exact() {
2062 let ctx = Context::new();
2063 let input = "3.14159265358979323846264338327950288419716939937510";
2066 let result = parse(&ctx, input).unwrap();
2067 let big = parse(&ctx, "100000000000000000000000000000000000000000000000000").unwrap();
2070 let product = (&result * &big).eval();
2071 let s = format!("{product}");
2072 assert_eq!(
2073 s, "314159265358979323846264338327950288419716939937510",
2074 "50-digit decimal * 10^50 must recover exact integer (bc-verified)"
2075 );
2076 }
2077
2078 #[test]
2079 fn parse_decimal_large_integer_part_and_fraction_exact() {
2080 let ctx = Context::new();
2081 let result = parse(
2085 &ctx,
2086 "123456789012345678901234567890.123456789012345678901234567890",
2087 )
2088 .unwrap();
2089 let denom = parse(&ctx, "1000000000000000000000000000000").unwrap(); let product = (&result * &denom).eval();
2091 let s = format!("{product}");
2092 assert_eq!(
2093 s, "123456789012345678901234567890123456789012345678901234567890",
2094 "large decimal * 10^30 must recover exact integer (bc-verified)"
2095 );
2096 }
2097
2098 #[test]
2099 fn parse_decimal_used_in_arithmetic() {
2100 let ctx = Context::new();
2101 let result = parse(&ctx, "0.1 + 0.2").unwrap();
2103 let s = format!("{result}");
2104 assert_eq!(s, "3/10", "0.1 + 0.2 should be exactly 3/10, got: {s}");
2105 }
2106
2107 #[test]
2108 fn parse_decimal_multiplication_exact() {
2109 let ctx = Context::new();
2110 let result = parse(&ctx, "0.1 * 0.1").unwrap();
2112 let s = format!("{result}");
2113 assert_eq!(s, "1/100", "0.1 * 0.1 should be exactly 1/100, got: {s}");
2114 }
2115
2116 #[test]
2117 fn parse_decimal_vs_fraction_equivalence() {
2118 let ctx = Context::new();
2119 let x = ctx.symbol("x");
2121 let from_decimal = parse(&ctx, "2.5 * x").unwrap();
2122 let five_halves = ctx.rational(5, 2);
2123 let from_fraction = &five_halves * &x;
2124 assert_eq!(
2125 from_decimal, from_fraction,
2126 "2.5*x and (5/2)*x should be identical expressions"
2127 );
2128 }
2129
2130 #[test]
2135 fn parse_zero_integer() {
2136 let ctx = Context::new();
2137 let result = parse(&ctx, "0").unwrap();
2138 let s = format!("{result}");
2139 assert_eq!(s, "0");
2140 }
2141
2142 #[test]
2143 fn parse_zero_decimal() {
2144 let ctx = Context::new();
2145 let result = parse(&ctx, "0.0").unwrap();
2146 let s = format!("{result}");
2147 assert_eq!(s, "0", "0.0 should parse as 0, got: {s}");
2148 }
2149
2150 #[test]
2151 fn parse_leading_zeros_integer() {
2152 let ctx = Context::new();
2153 let result = parse(&ctx, "007").unwrap();
2154 let s = format!("{result}");
2155 assert_eq!(s, "7", "007 should parse as 7, got: {s}");
2156 }
2157
2158 #[test]
2159 fn parse_leading_zeros_decimal() {
2160 let ctx = Context::new();
2161 let result = parse(&ctx, "0.00100").unwrap();
2162 let s = format!("{result}");
2163 assert_eq!(s, "1/1000", "0.00100 should parse as 1/1000, got: {s}");
2164 }
2165
2166 #[test]
2167 fn parse_deep_nesting_limit() {
2168 let ctx = Context::new();
2169 let deep = "(".repeat(300) + "1" + &")".repeat(300);
2170 let result = parse(&ctx, &deep);
2171 assert!(
2172 result.is_err(),
2173 "deeply nested input should return error, not stack overflow"
2174 );
2175 }
2176
2177 #[test]
2178 fn parse_moderate_nesting_ok() {
2179 let ctx = Context::new();
2180 let expr = "(".repeat(50) + "x + 1" + &")".repeat(50);
2182 let result = parse(&ctx, &expr);
2183 assert!(result.is_ok(), "50 levels of nesting should be fine");
2184 }
2185
2186 #[test]
2187 fn parse_decimal_with_variable_exact_coeff() {
2188 let ctx = Context::new();
2189 let result = parse(&ctx, "3.14 * x").unwrap();
2191 let s = format!("{result}");
2192 assert_eq!(
2193 s, "157/50*x",
2194 "3.14*x should have exact coefficient 157/50, got: {s}"
2195 );
2196 }
2197
2198 #[test]
2199 fn parse_integer_one() {
2200 let ctx = Context::new();
2201 let result = parse(&ctx, "1").unwrap();
2202 let s = format!("{result}");
2203 assert_eq!(s, "1");
2204 }
2205
2206 #[test]
2207 fn parse_negative_decimal() {
2208 let ctx = Context::new();
2209 let result = parse(&ctx, "-0.5").unwrap();
2211 let s = format!("{result}");
2212 assert_eq!(s, "-1/2", "-0.5 should parse as -1/2, got: {s}");
2213 }
2214
2215 #[test]
2220 fn parse_beyond_i64_arithmetic() {
2221 let ctx = Context::new();
2222 let result = parse(&ctx, "9223372036854775808 + 1").unwrap();
2224 let s = format!("{result}");
2225 assert_eq!(
2226 s, "9223372036854775809",
2227 "i64::MAX+1 + 1 should be exact (bc-verified)"
2228 );
2229 }
2230
2231 #[test]
2232 fn parse_beyond_i128() {
2233 let ctx = Context::new();
2234 let result = parse(&ctx, "170141183460469231731687303715884105728").unwrap();
2237 let s = format!("{result}");
2238 assert_eq!(
2239 s, "170141183460469231731687303715884105728",
2240 "i128::MAX+1 should parse and display exactly"
2241 );
2242 }
2243
2244 #[test]
2245 fn parse_huge_integer_squared() {
2246 let ctx = Context::new();
2247 let base = parse(&ctx, "99999999999999999999999999999").unwrap();
2250 let squared = base.powi(2).eval();
2251 let s = format!("{squared}");
2252 assert_eq!(
2253 s, "9999999999999999999999999999800000000000000000000000000001",
2254 "(10^29 - 1)^2 should be exact (bc-verified)"
2255 );
2256 }
2257
2258 #[test]
2259 fn parse_tiny_number_exact() {
2260 let ctx = Context::new();
2261 let tiny = parse(&ctx, "0.000000000000000000000000000000000001").unwrap();
2265 let big = parse(&ctx, "1000000000000000000000000000000000000").unwrap();
2266 let product = (&tiny * &big).eval();
2267 let s = format!("{product}");
2268 assert_eq!(s, "1", "10^-36 * 10^36 should be exactly 1 (bc-verified)");
2269 }
2270
2271 #[test]
2272 fn parse_tiny_number_display() {
2273 let ctx = Context::new();
2274 let tiny = parse(&ctx, "0.000000000000000000000000000000000001").unwrap();
2276 let s = format!("{tiny}");
2277 assert_eq!(
2278 s, "1/1000000000000000000000000000000000000",
2279 "10^-36 should display as exact fraction (bc-verified)"
2280 );
2281 }
2282
2283 #[test]
2284 fn parse_large_integer_addition() {
2285 let ctx = Context::new();
2286 let result = parse(&ctx, "123456789012345678901234567890 + 1").unwrap();
2288 let s = format!("{result}");
2289 assert_eq!(
2290 s, "123456789012345678901234567891",
2291 "large integer + 1 should be exact (bc-verified)"
2292 );
2293 }
2294
2295 #[test]
2300 fn parse_sympy_power_operator() {
2301 assert_eq!(parse_and_display("x**2"), parse_and_display("x^2"));
2302 }
2303
2304 #[test]
2305 fn parse_sympy_double_star_in_expression() {
2306 assert_eq!(
2307 parse_and_display("3*x**2 + 2"),
2308 parse_and_display("3*x^2 + 2")
2309 );
2310 }
2311
2312 #[test]
2313 fn parse_sympy_abs_capital() {
2314 assert_eq!(parse_and_display("Abs(x)"), parse_and_display("abs(x)"));
2315 }
2316
2317 #[test]
2318 fn parse_sympy_full_expression() {
2319 let ctx = Context::new();
2320 let result = parse(&ctx, "x**2*log(x)/2 - x**2/4").unwrap();
2322 let s = format!("{result}");
2323 assert!(
2324 s.contains("ln") && s.contains("x"),
2325 "should parse SymPy integrate output: {s}"
2326 );
2327 }
2328
2329 #[test]
2330 fn parse_sympy_trig_identity() {
2331 let ctx = Context::new();
2332 let result = parse(&ctx, "sin(x)**2 + cos(x)**2").unwrap();
2333 let simplified = result.simplify();
2335 assert_eq!(format!("{simplified}"), "1");
2336 }
2337
2338 #[test]
2341 fn parse_named_constants() {
2342 let ctx = Context::new();
2343 assert_eq!(parse(&ctx, "EulerGamma").unwrap(), ctx.euler_gamma());
2344 assert_eq!(parse(&ctx, "Catalan").unwrap(), ctx.catalan());
2345 assert_eq!(parse(&ctx, "GoldenRatio").unwrap(), ctx.golden_ratio());
2346 assert_eq!(parse(&ctx, "zoo").unwrap(), ctx.complex_infinity());
2347 assert_eq!(
2348 parse_and_display("EulerGamma + Catalan"),
2349 "EulerGamma + Catalan"
2350 );
2351 }
2352
2353 #[test]
2354 fn parse_complex_functions() {
2355 let ctx = Context::new();
2356 let z = ctx.symbol("z");
2357 assert_eq!(parse(&ctx, "re(z)").unwrap(), z.re());
2358 assert_eq!(parse(&ctx, "im(z)").unwrap(), z.im());
2359 assert_eq!(parse(&ctx, "conjugate(z)").unwrap(), z.conjugate());
2360 assert_eq!(parse(&ctx, "conj(z)").unwrap(), z.conjugate());
2361 assert_eq!(parse(&ctx, "arg(z)").unwrap(), z.arg());
2362 assert_eq!(parse_and_display("Re(3 + 4*I)"), "3");
2363 assert_eq!(parse_and_display("im(3 + 4*I)"), "4");
2364 }
2365
2366 #[test]
2367 fn parse_special_functions() {
2368 let ctx = Context::new();
2369 let x = ctx.symbol("x");
2370 let n = ctx.symbol("n");
2371 assert_eq!(parse(&ctx, "Si(x)").unwrap(), x.si());
2372 assert_eq!(parse(&ctx, "Ci(x)").unwrap(), x.ci());
2373 assert_eq!(parse(&ctx, "Ei(x)").unwrap(), x.ei());
2374 assert_eq!(parse(&ctx, "li(x)").unwrap(), x.li());
2375 assert_eq!(parse(&ctx, "zeta(x)").unwrap(), x.zeta());
2376 assert_eq!(parse(&ctx, "polygamma(n, x)").unwrap(), x.polygamma(&n));
2377 assert_eq!(
2378 parse(&ctx, "KroneckerDelta(n, x)").unwrap(),
2379 x.kronecker_delta(&n)
2380 );
2381 assert_eq!(
2382 parse(&ctx, "kronecker_delta(n, x)").unwrap(),
2383 x.kronecker_delta(&n)
2384 );
2385 assert_eq!(parse_and_display("zeta(2)"), "1/6*pi^2");
2386 assert_eq!(parse_and_display("Si(0)"), "0");
2387 assert_eq!(parse_and_display("atan2(1, 1)"), "atan2(1, 1)");
2388 }
2389
2390 #[test]
2393 fn parse_bool_relations_and_connectives() {
2394 let ctx = Context::new();
2395 let (x, y) = (ctx.symbol("x"), ctx.symbol("y"));
2396 let zero = ctx.int(0);
2397 let one = ctx.int(1);
2398 assert_eq!(parse_bool(&ctx, "x > 0").unwrap(), x.gt(&zero));
2399 assert_eq!(parse_bool(&ctx, "x >= 0").unwrap(), x.ge(&zero));
2400 assert_eq!(parse_bool(&ctx, "x < 1").unwrap(), x.lt(&one));
2401 assert_eq!(parse_bool(&ctx, "x <= 1").unwrap(), x.le(&one));
2402 assert_eq!(parse_bool(&ctx, "x == 1").unwrap(), x.eq_expr(&one));
2403 assert_eq!(parse_bool(&ctx, "x != 1").unwrap(), x.ne_expr(&one));
2404 assert_eq!(
2405 parse_bool(&ctx, "x > 0 & x < 1").unwrap(),
2406 x.gt(&zero).and(&x.lt(&one))
2407 );
2408 assert_eq!(
2409 parse_bool(&ctx, "x > 0 && x < 1").unwrap(),
2410 parse_bool(&ctx, "x > 0 and x < 1").unwrap()
2411 );
2412 assert_eq!(
2413 parse_bool(&ctx, "x > 0 | y > 0").unwrap(),
2414 x.gt(&zero).or(&y.gt(&zero))
2415 );
2416 assert_eq!(
2417 parse_bool(&ctx, "x > 0 || y > 0").unwrap(),
2418 parse_bool(&ctx, "x > 0 or y > 0").unwrap()
2419 );
2420 assert_eq!(parse_bool(&ctx, "~(x > 0)").unwrap(), x.gt(&zero).not());
2421 assert_eq!(parse_bool(&ctx, "!(x > 0)").unwrap(), x.gt(&zero).not());
2422 assert_eq!(parse_bool(&ctx, "not x > 0").unwrap(), x.gt(&zero).not());
2423 assert_eq!(parse_bool(&ctx, "True").unwrap().to_string(), "True");
2424 assert_eq!(parse_bool(&ctx, "false").unwrap().to_string(), "False");
2425 }
2426
2427 #[test]
2428 fn parse_bool_precedence() {
2429 let ctx = Context::new();
2430 let (x, y) = (ctx.symbol("x"), ctx.symbol("y"));
2431 let zero = ctx.int(0);
2432 let one = ctx.int(1);
2433 assert_eq!(
2435 parse_bool(&ctx, "x > 0 & x < 1 | y == 0").unwrap(),
2436 x.gt(&zero).and(&x.lt(&one)).or(&y.eq_expr(&zero))
2437 );
2438 assert_eq!(
2439 parse_bool(&ctx, "x > 0 | x < 1 & y == 0").unwrap(),
2440 x.gt(&zero).or(&x.lt(&one).and(&y.eq_expr(&zero)))
2441 );
2442 assert_eq!(
2444 parse_bool(&ctx, "x + 1 > 2*y").unwrap(),
2445 (&x + 1).gt(&(2 * &y))
2446 );
2447 assert_eq!(
2449 parse_bool(&ctx, "not x > 0 & y > 0").unwrap(),
2450 x.gt(&zero).not().and(&y.gt(&zero))
2451 );
2452 assert_eq!(
2454 parse_bool(&ctx, "x > 0 & y > 0 & x < 1")
2455 .unwrap()
2456 .to_string(),
2457 "x > 0 & y > 0 & 1 > x"
2458 );
2459 assert_eq!(
2461 parse_bool(&ctx, "(x > 0 | y > 0) & x < 1").unwrap(),
2462 x.gt(&zero).or(&y.gt(&zero)).and(&x.lt(&one))
2463 );
2464 }
2465
2466 #[test]
2467 fn parse_bool_function_forms() {
2468 let ctx = Context::new();
2469 let (x, y) = (ctx.symbol("x"), ctx.symbol("y"));
2470 let one = ctx.int(1);
2471 assert_eq!(parse_bool(&ctx, "Eq(x, 1)").unwrap(), x.eq_expr(&one));
2472 assert_eq!(parse_bool(&ctx, "Ne(x, 1)").unwrap(), x.ne_expr(&one));
2473 assert_eq!(parse_bool(&ctx, "Lt(x, 1)").unwrap(), x.lt(&one));
2474 assert_eq!(parse_bool(&ctx, "Le(x, 1)").unwrap(), x.le(&one));
2475 assert_eq!(parse_bool(&ctx, "Gt(x, 1)").unwrap(), x.gt(&one));
2476 assert_eq!(parse_bool(&ctx, "Ge(x, 1)").unwrap(), x.ge(&one));
2477 assert_eq!(
2478 parse_bool(&ctx, "And(x > 1, y > 1)").unwrap(),
2479 x.gt(&one).and(&y.gt(&one))
2480 );
2481 assert_eq!(
2482 parse_bool(&ctx, "Or(x > 1, y > 1)").unwrap(),
2483 x.gt(&one).or(&y.gt(&one))
2484 );
2485 assert_eq!(parse_bool(&ctx, "Not(x > 1)").unwrap(), x.gt(&one).not());
2486 }
2487
2488 #[test]
2489 fn parse_bool_errors() {
2490 let ctx = Context::new();
2491 assert!(parse_bool(&ctx, "x + 1").is_err());
2493 assert!(parse_bool(&ctx, "0 < x < 1").is_err());
2495 assert!(parse_bool(&ctx, "(x > 0) + 1").is_err());
2497 assert!(parse_bool(&ctx, "x & y").is_err());
2498 assert!(parse_bool(&ctx, "not x").is_err());
2499 assert!(parse_bool(&ctx, "(x > 0) > 1").is_err());
2500 assert!(parse_bool(&ctx, "-(x > 0)").is_err());
2501 assert!(parse_bool(&ctx, "x > 0 &").is_err());
2503 }
2504
2505 #[test]
2506 fn parse_strict_rejects_relations_and_keeps_keywords_as_symbols() {
2507 let ctx = Context::new();
2508 assert!(parse(&ctx, "x > 0").is_err());
2509 assert!(parse(&ctx, "x & y").is_err());
2510 assert!(parse(&ctx, "~x").is_err());
2511 assert!(parse(&ctx, "x != 1").is_err());
2512 assert_eq!(parse(&ctx, "and").unwrap(), ctx.symbol("and"));
2514 assert_eq!(parse(&ctx, "True").unwrap(), ctx.symbol("True"));
2515 assert!(parse(&ctx, "Sum(k, k=1..3)").is_ok());
2517 }
2518
2519 #[test]
2520 fn parse_implicit_application() {
2521 let ctx = Context::new();
2522 let (x, y) = (ctx.symbol("x"), ctx.symbol("y"));
2523 assert_eq!(parse_implicit(&ctx, "sin x").unwrap(), x.sin());
2524 assert_eq!(parse_implicit(&ctx, "2 sin x").unwrap(), 2 * &x.sin());
2525 assert_eq!(parse_implicit(&ctx, "sin 2x").unwrap(), (2 * &x).sin());
2526 assert_eq!(parse_implicit(&ctx, "sin x^2").unwrap(), x.powi(2).sin());
2527 assert_eq!(
2528 parse_implicit(&ctx, "sin x cos y").unwrap(),
2529 &x.sin() * &y.cos()
2530 );
2531 assert_eq!(parse_implicit(&ctx, "sin x + 1").unwrap(), &x.sin() + 1);
2532 assert_eq!(parse_implicit(&ctx, "sin x/2").unwrap(), (&x / 2).sin());
2533 assert_eq!(parse_implicit(&ctx, "sin x y").unwrap(), (&x * &y).sin());
2534 assert_eq!(parse_implicit(&ctx, "exp (x) y").unwrap(), &x.exp() * &y);
2535 assert_eq!(parse_implicit(&ctx, "sqrt 2").unwrap(), ctx.int(2).sqrt());
2536 assert_eq!(parse_implicit(&ctx, "ln x^2").unwrap(), x.powi(2).ln());
2537 assert_eq!(
2539 parse_implicit(&ctx, "sin(x cos y)").unwrap(),
2540 (&x * &y.cos()).sin()
2541 );
2542 }
2543
2544 #[test]
2545 fn parse_implicit_products() {
2546 let ctx = Context::new();
2547 let (x, y, z) = (ctx.symbol("x"), ctx.symbol("y"), ctx.symbol("z"));
2548 assert_eq!(
2549 parse_implicit(&ctx, "2x + 3(y-1)").unwrap(),
2550 2 * &x + 3 * (&y - 1)
2551 );
2552 assert_eq!(parse_implicit(&ctx, "x y z").unwrap(), &x * &y * &z);
2553 assert_eq!(parse_implicit(&ctx, "x(x+1)").unwrap(), &x * (&x + 1));
2554 assert_eq!(
2555 parse_implicit(&ctx, "(x+1)(x-1)").unwrap(),
2556 (&x + 1) * (&x - 1)
2557 );
2558 let f = ctx.symbol("f");
2561 assert_eq!(parse_implicit(&ctx, "f(x)").unwrap(), &f * &x);
2562 let c = ctx.symbol("c");
2563 assert_eq!(parse_implicit(&ctx, "c(x+1)").unwrap(), &c * (&x + 1));
2564 assert_eq!(
2565 parse_implicit(&ctx, "binomial(x, 2)").unwrap(),
2566 parse(&ctx, "C(x, 2)").unwrap()
2567 );
2568 assert_eq!(parse_implicit(&ctx, "pi(x)").unwrap(), ctx.pi() * &x);
2569 assert_eq!(parse_implicit(&ctx, "2 pi x").unwrap(), 2 * ctx.pi() * &x);
2570 }
2571
2572 #[test]
2573 fn parse_implicit_errors_and_limits() {
2574 let ctx = Context::new();
2575 assert!(parse_implicit(&ctx, "sin + 1").is_err());
2577 assert!(parse_implicit(&ctx, "sin").is_err());
2578 let (re, x) = (ctx.symbol("re"), ctx.symbol("x"));
2580 assert_eq!(parse_implicit(&ctx, "re x").unwrap(), &re * &x);
2581 assert_eq!(parse_implicit(&ctx, "re(x)").unwrap(), x.re());
2582 assert!(parse_implicit(&ctx, "x > 0").is_err());
2584 }
2585
2586 #[test]
2587 fn known_functions_table_matches_call_tables() {
2588 let ctx = Context::new();
2590 for name in KNOWN_FUNCTIONS {
2591 let ok = [
2592 format!("{name}(x)"),
2593 format!("{name}(x, y)"),
2594 format!("{name}(x, y, z)"),
2595 format!("{name}(x, y, z, w)"),
2596 ]
2597 .iter()
2598 .any(|s| parse(&ctx, s).is_ok());
2599 assert!(ok, "`{name}` is listed but no arity parses");
2600 }
2601 for name in KNOWN_FUNCTIONS {
2602 assert!(
2603 !is_implicit_unary_function(name) || parse(&ctx, &format!("{name}(x)")).is_ok(),
2604 "`{name}` is applied implicitly but is not a unary function"
2605 );
2606 }
2607 }
2608}