use crate::ast::{
BinaryOp, Direction, ExprKind, Expression, InequalityOp, IntegralBounds, LogicalOp,
MathConstant, MathFloat, SetOp, SetRelation, UnaryOp,
};
use crate::error::{ParseError, ParseOutput, ParseResult, Span};
use crate::parser::tokenizer::{tokenize, SpannedToken, Token};
use crate::ParserConfig;
mod arithmetic;
mod calculus_fns;
mod derivatives;
mod expression;
mod primary;
mod set_ops;
fn split_order_prefix(s: &str) -> (u32, &str) {
let digit_end = s.find(|c: char| !c.is_ascii_digit()).unwrap_or(s.len());
if digit_end == 0 {
(0, s)
} else {
let order = s[..digit_end].parse::<u32>().unwrap_or(0);
(order, &s[digit_end..])
}
}
fn split_order_suffix(s: &str) -> (u32, &str) {
let alpha_end = s
.find(|c: char| !c.is_ascii_alphabetic())
.unwrap_or(s.len());
if alpha_end == s.len() {
(0, s)
} else {
let order = s[alpha_end..].parse::<u32>().unwrap_or(0);
(order, &s[..alpha_end])
}
}
#[cfg(test)]
mod tests;
pub fn parse(input: &str) -> ParseResult<Expression> {
parse_with_config(input, &ParserConfig::default())
}
pub fn parse_with_config(input: &str, config: &ParserConfig) -> ParseResult<Expression> {
let tokens = tokenize(input)?;
let parser = TextParser::new(tokens, config.clone(), false);
parser.parse_strict()
}
pub fn parse_lenient(input: &str) -> ParseOutput {
parse_lenient_with_config(input, &ParserConfig::default())
}
pub fn parse_equation_system(input: &str) -> ParseResult<Vec<Expression>> {
parse_equation_system_with_config(input, &ParserConfig::default())
}
pub(crate) fn parse_equation_system_with_config(
input: &str,
config: &ParserConfig,
) -> ParseResult<Vec<Expression>> {
input
.split(';')
.map(str::trim)
.filter(|s| !s.is_empty())
.map(|part| parse_with_config(part, config))
.collect()
}
pub fn parse_lenient_with_config(input: &str, config: &ParserConfig) -> ParseOutput {
let tokens = match tokenize(input) {
Ok(tokens) => tokens,
Err(err) => {
return ParseOutput {
expression: None,
errors: vec![err],
}
}
};
let parser = TextParser::new(tokens, config.clone(), true);
parser.parse_lenient()
}
struct TextParser {
tokens: Vec<SpannedToken>,
pos: usize,
config: ParserConfig,
collected_errors: Vec<ParseError>,
}
impl TextParser {
fn new(tokens: Vec<SpannedToken>, config: ParserConfig, _lenient: bool) -> Self {
Self {
tokens,
pos: 0,
config,
collected_errors: Vec::new(),
}
}
fn peek(&self) -> Option<&SpannedToken> {
self.tokens.get(self.pos)
}
fn peek_ahead(&self, offset: usize) -> Option<&SpannedToken> {
self.tokens.get(self.pos + offset)
}
fn next(&mut self) -> Option<SpannedToken> {
let token = self.tokens.get(self.pos).cloned();
if token.is_some() {
self.pos += 1;
}
token
}
fn current_span(&self) -> Span {
self.peek().map(|token| token.span).unwrap_or_else(|| {
if let Some(last_token) = self.tokens.last() {
Span::at(last_token.span.end)
} else {
Span::start()
}
})
}
fn check(&self, expected: &Token) -> bool {
self.peek()
.map(|token| &token.value == expected)
.unwrap_or(false)
}
fn should_insert_implicit_mult(&self, _left: &Expression) -> bool {
if !self.config.implicit_multiplication {
return false;
}
let next_token = match self.peek() {
Some(token) => &token.value,
None => return false,
};
matches!(next_token, Token::Identifier(_) | Token::LParen)
}
fn consume(&mut self, expected: Token) -> ParseResult<Span> {
if let Some(token) = self.next() {
if token.value == expected {
Ok(token.span)
} else {
Err(ParseError::unexpected_token(
vec![format!("{}", expected)],
format!("{}", token.value),
Some(token.span),
))
}
} else {
Err(ParseError::unexpected_eof(
vec![format!("{}", expected)],
Some(self.current_span()),
))
}
}
fn is_sync_token(token: &Token) -> bool {
matches!(
token,
Token::RParen
| Token::Plus
| Token::Minus
| Token::Equals
| Token::Semicolon
| Token::Comma
)
}
fn synchronize(&mut self) {
while let Some(token) = self.peek() {
if Self::is_sync_token(&token.value) {
return;
}
self.next();
}
}
fn parse_strict(mut self) -> ParseResult<Expression> {
let expr = self.parse_expression()?;
if let Some(token) = self.peek() {
return Err(ParseError::unexpected_token(
vec!["end of input"],
format!("{}", token.value),
Some(token.span),
));
}
Ok(expr)
}
fn parse_lenient(mut self) -> ParseOutput {
let mut parts: Vec<Expression> = Vec::new();
while self.peek().is_some() {
match self.parse_expression() {
Ok(expr) => {
parts.push(expr);
if let Some(token) = self.peek() {
self.collected_errors.push(ParseError::unexpected_token(
vec!["end of input or operator"],
format!("{}", token.value),
Some(token.span),
));
self.synchronize();
if let Some(t) = self.peek() {
if matches!(t.value, Token::RParen) {
self.next();
}
}
}
}
Err(err) => {
self.collected_errors.push(err);
self.synchronize();
if let Some(t) = self.peek() {
if matches!(t.value, Token::RParen) {
self.next();
}
}
}
}
}
let expression = match parts.len() {
0 => None,
1 => Some(parts.remove(0)),
_ => Some(parts.remove(0)),
};
ParseOutput {
expression,
errors: self.collected_errors,
}
}
}