use cas_error::Error;
use crate::{
parser::{
ast::{expr::Expr, helper::{SquareDelimited, Surrounded}},
error::{EmptyRadixLiteral, InvalidRadixBase, InvalidRadixDigit, UnexpectedToken},
fmt::Latex,
token::{
Boolean,
CloseParen,
Name,
Int,
OpenParen,
OpenSquare,
Semicolon,
Quote,
},
Parse,
Parser,
ParseResult,
},
tokenizer::TokenKind,
return_if_ok,
};
use std::{collections::HashSet, fmt, ops::Range};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct LitInt {
pub value: String,
pub span: Range<usize>,
}
impl std::fmt::Display for LitInt {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.value)
}
}
impl Latex for LitInt {
fn fmt_latex(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.value)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct LitFloat {
pub value: String,
pub span: Range<usize>,
}
impl std::fmt::Display for LitFloat {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.value)
}
}
impl Latex for LitFloat {
fn fmt_latex(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.value)
}
}
fn parse_ascii_number(input: &mut Parser) -> Result<Literal, Vec<Error>> {
struct Num {
value: String,
span: Range<usize>,
has_digits: bool,
has_decimal: bool,
}
let mut result: Option<Num> = None;
input.advance_past_whitespace();
while let Ok(token) = input.next_token_raw() {
match token.kind {
TokenKind::Int => {
let mut has_decimal = false;
result = result.map_or_else(|| {
Some(Num {
value: token.lexeme.to_owned(),
span: token.span.clone(),
has_digits: true,
has_decimal: false,
})
}, |mut num| {
num.value.push_str(token.lexeme);
num.span.end = token.span.end;
num.has_digits = true;
has_decimal = num.has_decimal;
Some(num)
});
if has_decimal {
break;
}
},
TokenKind::Dot => {
result = result.map_or_else(|| {
Some(Num {
value: ".".to_owned(),
span: token.span.clone(),
has_digits: false,
has_decimal: true,
})
}, |mut num| {
num.value.push('.');
num.span.end = token.span.end;
num.has_decimal = true;
Some(num)
});
},
_ => {
input.prev();
break;
},
}
}
let num = result.ok_or_else(|| {
let mut input_ahead = input.clone();
match input_ahead.next_token() {
Ok(token) => vec![Error::new(vec![token.span], UnexpectedToken {
expected: &[TokenKind::Int],
found: token.kind,
})],
Err(e) => vec![e],
}
})?;
if !num.has_digits {
return Err(vec![Error::new(vec![num.span.clone()], UnexpectedToken {
expected: &[TokenKind::Int],
found: TokenKind::Dot,
})]);
}
if num.has_decimal {
Ok(Literal::Float(LitFloat {
value: num.value,
span: num.span,
}))
} else {
Ok(Literal::Integer(LitInt {
value: num.value,
span: num.span,
}))
}
}
pub const DIGITS: [char; 64] = [
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'+', '/',
];
#[derive(Debug, Clone, PartialEq, Eq)]
struct RadixWord {
pub value: String,
pub span: Range<usize>,
}
impl RadixWord {
fn parse(input: &mut Parser) -> Self {
let mut value = String::new();
let mut span = 0..0;
while let Ok(token) = input.next_token_raw() {
match token.kind {
TokenKind::Add
| TokenKind::Name
| TokenKind::Int
| TokenKind::Div => value.push_str(token.lexeme),
_ => {
input.prev();
break;
},
}
if span.start == 0 {
span.start = token.span.start;
}
span.end = token.span.end;
}
Self {
value,
span,
}
}
}
fn validate_radix_base(num: &Int) -> ParseResult<u8> {
match num.lexeme.parse() {
Ok(base) if (2..=64).contains(&base) => ParseResult::Ok(base),
Ok(base) if base < 2 => ParseResult::Recoverable(
64, vec![Error::new(vec![num.span.clone()], InvalidRadixBase { too_large: false })],
),
_ => ParseResult::Recoverable(
64,
vec![Error::new(vec![num.span.clone()], InvalidRadixBase { too_large: true })],
),
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct LitRadix {
pub base: u8,
pub value: String,
pub span: Range<usize>,
}
impl<'source> Parse<'source> for LitRadix {
fn std_parse(
input: &mut Parser<'source>,
recoverable_errors: &mut Vec<Error>
) -> Result<Self, Vec<Error>> {
let num = input.try_parse().forward_errors(recoverable_errors)?;
let quote = input.try_parse::<Quote>().forward_errors(recoverable_errors)?;
let base = validate_radix_base(&num).forward_errors(recoverable_errors)?;
let word = RadixWord::parse(input);
if word.value.is_empty() {
recoverable_errors.push(Error::new(vec![quote.span], EmptyRadixLiteral {
radix: base,
allowed: &DIGITS[..base as usize],
}));
}
let allowed_digits = &DIGITS[..base as usize];
let mut bad_digits = HashSet::new();
let mut bad_digit_spans: Vec<Range<usize>> = Vec::new();
for (i, c) in word.value.chars().enumerate() {
if !allowed_digits.contains(&c) {
let char_start = word.span.start + i;
if let Some(last_span) = bad_digit_spans.last_mut() {
if last_span.end == char_start {
last_span.end += 1;
} else {
bad_digit_spans.push(char_start..char_start + 1);
}
} else {
bad_digit_spans.push(char_start..char_start + 1);
}
bad_digits.insert(c);
continue;
}
}
if !bad_digit_spans.is_empty() {
recoverable_errors.push(Error::new(bad_digit_spans, InvalidRadixDigit {
radix: base,
allowed: allowed_digits,
digits: bad_digits,
last_op_digit: {
if let Some(ch) = word.value.chars().last() {
['+', '/'].into_iter()
.find(|&op| op == ch)
.map(|op| (op, word.span.end - 1..word.span.end))
} else {
None
}
},
}));
}
Ok(Self {
base,
value: word.value,
span: num.span.start..word.span.end,
})
}
}
impl std::fmt::Display for LitRadix {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}'{}", self.base, self.value)
}
}
impl Latex for LitRadix {
fn fmt_latex(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}'{}", self.base, self.value)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct LitBool {
pub value: bool,
pub span: Range<usize>,
}
impl<'source> Parse<'source> for LitBool {
fn std_parse(
input: &mut Parser<'source>,
recoverable_errors: &mut Vec<Error>
) -> Result<Self, Vec<Error>> {
input.try_parse::<Boolean>()
.map(|boolean| Self {
value: match boolean.lexeme {
"true" => true,
"false" => false,
_ => unreachable!(),
},
span: boolean.span,
})
.forward_errors(recoverable_errors)
}
}
impl std::fmt::Display for LitBool {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.value)
}
}
impl Latex for LitBool {
fn fmt_latex(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.value)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct LitSym {
pub name: String,
pub span: Range<usize>,
}
impl<'source> Parse<'source> for LitSym {
fn std_parse(
input: &mut Parser<'source>,
recoverable_errors: &mut Vec<Error>
) -> Result<Self, Vec<Error>> {
input.try_parse::<Name>()
.map(|name| Self {
name: name.lexeme.to_owned(),
span: name.span,
})
.forward_errors(recoverable_errors)
}
}
impl std::fmt::Display for LitSym {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.name)
}
}
impl Latex for LitSym {
fn fmt_latex(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.name.as_str() {
"tau" | "pi" | "phi" | "theta" => write!(f, "\\{} ", self.name),
_ => write!(f, "{}", self.name),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct LitUnit {
pub span: Range<usize>,
}
impl<'source> Parse<'source> for LitUnit {
fn std_parse(
input: &mut Parser<'source>,
recoverable_errors: &mut Vec<Error>
) -> Result<Self, Vec<Error>> {
let open = input.try_parse::<OpenParen>().forward_errors(recoverable_errors)?;
let close = input.try_parse::<CloseParen>().forward_errors(recoverable_errors)?;
Ok(Self {
span: open.span.start..close.span.end,
})
}
}
impl std::fmt::Display for LitUnit {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "()")
}
}
impl Latex for LitUnit {
fn fmt_latex(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "()")
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct LitList {
pub values: Vec<Expr>,
pub span: Range<usize>,
}
impl<'source> Parse<'source> for LitList {
fn std_parse(
input: &mut Parser<'source>,
recoverable_errors: &mut Vec<Error>
) -> Result<Self, Vec<Error>> {
let surrounded = input.try_parse::<SquareDelimited<_>>().forward_errors(recoverable_errors)?;
Ok(Self {
values: surrounded.value.values,
span: surrounded.open.span.start..surrounded.close.span.end,
})
}
}
impl std::fmt::Display for LitList {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "[")?;
for (i, value) in self.values.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}", value)?;
}
write!(f, "]")
}
}
impl Latex for LitList {
fn fmt_latex(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "[")?;
for (i, value) in self.values.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
value.fmt_latex(f)?;
}
write!(f, "]")
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct LitListRepeat {
pub value: Box<Expr>,
pub count: Box<Expr>,
pub span: Range<usize>,
}
impl<'source> Parse<'source> for LitListRepeat {
fn std_parse(
input: &mut Parser<'source>,
recoverable_errors: &mut Vec<Error>
) -> Result<Self, Vec<Error>> {
#[derive(Debug)]
struct LitListRepeatInner {
value: Expr,
count: Expr,
}
impl<'source> Parse<'source> for LitListRepeatInner {
fn std_parse(
input: &mut Parser<'source>,
recoverable_errors: &mut Vec<Error>
) -> Result<Self, Vec<Error>> {
let value = input.try_parse().forward_errors(recoverable_errors)?;
input.try_parse::<Semicolon>().forward_errors(recoverable_errors)?;
let count = input.try_parse().forward_errors(recoverable_errors)?;
Ok(Self { value, count })
}
}
let inner = input.try_parse::<Surrounded<OpenSquare, LitListRepeatInner>>().forward_errors(recoverable_errors)?;
Ok(Self {
value: Box::new(inner.value.value),
count: Box::new(inner.value.count),
span: inner.open.span.start..inner.close.span.end,
})
}
}
impl std::fmt::Display for LitListRepeat {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "[{}; {}]", self.value, self.count)
}
}
impl Latex for LitListRepeat {
fn fmt_latex(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "[{}; {}]", self.value, self.count)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Literal {
Integer(LitInt),
Float(LitFloat),
Radix(LitRadix),
Boolean(LitBool),
Symbol(LitSym),
Unit(LitUnit),
List(LitList),
ListRepeat(LitListRepeat),
}
impl Literal {
pub fn span(&self) -> Range<usize> {
match self {
Literal::Integer(int) => int.span.clone(),
Literal::Float(float) => float.span.clone(),
Literal::Radix(radix) => radix.span.clone(),
Literal::Boolean(boolean) => boolean.span.clone(),
Literal::Symbol(name) => name.span.clone(),
Literal::Unit(unit) => unit.span.clone(),
Literal::List(list) => list.span.clone(),
Literal::ListRepeat(repeat) => repeat.span.clone(),
}
}
}
impl<'source> Parse<'source> for Literal {
fn std_parse(
input: &mut Parser<'source>,
recoverable_errors: &mut Vec<Error>
) -> Result<Self, Vec<Error>> {
let _ = return_if_ok!(input.try_parse().map(Literal::Boolean).forward_errors(recoverable_errors));
let _ = return_if_ok!(input.try_parse().map(Literal::Radix).forward_errors(recoverable_errors));
let _ = return_if_ok!(parse_ascii_number(input));
let _ = return_if_ok!(input.try_parse().map(Literal::Symbol).forward_errors(recoverable_errors));
let _ = return_if_ok!(input.try_parse().map(Literal::Unit).forward_errors(recoverable_errors));
let _ = return_if_ok!(input.try_parse().map(Literal::List).forward_errors(recoverable_errors));
input.try_parse().map(Literal::ListRepeat).forward_errors(recoverable_errors)
}
}
impl std::fmt::Display for Literal {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Literal::Integer(int) => int.fmt(f),
Literal::Float(float) => float.fmt(f),
Literal::Radix(radix) => radix.fmt(f),
Literal::Boolean(boolean) => boolean.fmt(f),
Literal::Symbol(name) => name.fmt(f),
Literal::Unit(unit) => unit.fmt(f),
Literal::List(list) => list.fmt(f),
Literal::ListRepeat(repeat) => repeat.fmt(f),
}
}
}
impl Latex for Literal {
fn fmt_latex(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Literal::Integer(int) => int.fmt_latex(f),
Literal::Float(float) => float.fmt_latex(f),
Literal::Radix(radix) => radix.fmt_latex(f),
Literal::Boolean(boolean) => boolean.fmt_latex(f),
Literal::Symbol(name) => name.fmt_latex(f),
Literal::Unit(unit) => unit.fmt_latex(f),
Literal::List(list) => list.fmt_latex(f),
Literal::ListRepeat(repeat) => repeat.fmt_latex(f),
}
}
}