use crate::parse;
use voltaic_ast::BinaryOp;
use voltaic_ast::Program;
use voltaic_diag::Code;
use voltaic_diag::Span;
use voltaic_lexer::Keyword;
use voltaic_lexer::LexError;
use voltaic_lexer::Token;
use voltaic_lexer::TokenKind;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ParseError {
pub code: Code,
pub message: String,
pub span: Span,
pub help: Option<String>,
}
impl ParseError {
pub(crate) fn expected(message: impl Into<String>, span: Span) -> Self {
ParseError {
code: Code::ExpectedToken,
message: message.into(),
span,
help: None,
}
}
pub(crate) fn r#with(code: Code, message: impl Into<String>, span: Span) -> Self {
ParseError {
code,
message: message.into(),
span,
help: None,
}
}
pub(crate) fn with_help(
code: Code,
message: impl Into<String>,
help: impl Into<String>,
span: Span,
) -> Self {
ParseError {
code,
message: message.into(),
span,
help: Some(help.into()),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ParseFailure {
Lex(LexError),
Parse(Vec<ParseError>),
}
#[allow(unused_imports)]
use ParseFailure::*;
pub const MAX_NESTING: usize = 16;
pub(crate) fn nesting_depth(toks: &[Token]) -> usize {
let mut depth: usize = 0;
let mut deepest: usize = 0;
for token in toks {
match token.kind {
TokenKind::LParen | TokenKind::LBracket | TokenKind::LBrace => {
depth += 1;
deepest = deepest.max(depth);
}
TokenKind::RParen | TokenKind::RBracket | TokenKind::RBrace => {
depth = depth.saturating_sub(1);
}
_ => (),
};
}
deepest
}
pub(crate) fn above_bitor() -> u8 {
let found = binary_power(&TokenKind::Or).expect("`|` is in the precedence table");
found.1 + 1
}
pub(crate) fn binary_power(kind: &TokenKind) -> Option<(BinaryOp, u8, u8)> {
let op = match kind {
TokenKind::Pipe => BinaryOp::Pipeline,
TokenKind::Coalesce => BinaryOp::Coalesce,
TokenKind::Kw(Keyword::Catch) => BinaryOp::Catch,
TokenKind::DotDot => BinaryOp::Range,
TokenKind::DotDotEq => BinaryOp::RangeInclusive,
TokenKind::OrOr => BinaryOp::Or,
TokenKind::AndAnd => BinaryOp::And,
TokenKind::EqEq => BinaryOp::Eq,
TokenKind::Ne => BinaryOp::Ne,
TokenKind::Lt => BinaryOp::Lt,
TokenKind::Gt => BinaryOp::Gt,
TokenKind::Le => BinaryOp::Le,
TokenKind::Ge => BinaryOp::Ge,
TokenKind::Or => BinaryOp::BitOr,
TokenKind::Caret => BinaryOp::BitXor,
TokenKind::Amp => BinaryOp::BitAnd,
TokenKind::Shl => BinaryOp::Shl,
TokenKind::Shr => BinaryOp::Shr,
TokenKind::Plus => BinaryOp::Add,
TokenKind::Minus => BinaryOp::Sub,
TokenKind::Star => BinaryOp::Mul,
TokenKind::Slash => BinaryOp::Div,
TokenKind::Percent => BinaryOp::Rem,
TokenKind::Compose => BinaryOp::Compose,
TokenKind::ComposeBack => BinaryOp::ComposeBack,
_ => {
return None;
}
};
let left = op.power();
Some((op, left, left + 1))
}
#[cfg(test)]
fn power(kind: TokenKind) -> u8 {
binary_power(&kind).expect("should have a precedence").1
}
pub(crate) const PREFIX_POWER: u8 = 25;
pub fn parse_source(src: &str) -> Result<Program, ParseFailure> {
let toks = voltaic_lexer::lex(src).map_err(ParseFailure::Lex)?;
parse(&toks, src.len()).map_err(ParseFailure::Parse)
}
#[cfg(test)]
mod __rdx_tests {
use super::*;
#[test]
fn precedence_follows_rust() {
assert!(power(TokenKind::Star) > power(TokenKind::Plus));
assert!(power(TokenKind::Plus) > power(TokenKind::Shl));
assert!(power(TokenKind::Shl) > power(TokenKind::Amp));
assert!(power(TokenKind::Amp) > power(TokenKind::Caret));
assert!(power(TokenKind::Caret) > power(TokenKind::Or));
assert!(power(TokenKind::Or) > power(TokenKind::EqEq));
assert!(power(TokenKind::EqEq) > power(TokenKind::AndAnd));
assert!(power(TokenKind::AndAnd) > power(TokenKind::OrOr));
}
#[test]
fn the_new_operators_sit_where_doc_03_places_them() {
assert!(power(TokenKind::Coalesce) < power(TokenKind::OrOr));
assert!(power(TokenKind::Pipe) < power(TokenKind::Coalesce));
}
#[test]
fn unary_and_postfix_bind_tighter_than_any_binary() {
assert!(crate::support::PREFIX_POWER > power(TokenKind::Star));
}
#[test]
fn binary_operators_are_left_associative() {
for kind in [TokenKind::Plus, TokenKind::Star, TokenKind::Pipe] {
let rung = binary_power(&kind).expect("should have a precedence");
assert_eq!(rung.2, rung.1 + 1, "{:?} should be left-associative", kind);
}
}
#[test]
fn a_non_operator_has_no_precedence() {
assert!(binary_power(&TokenKind::Ident("x".into())).is_none());
assert!(binary_power(&TokenKind::Newline).is_none());
}
#[test]
fn the_powers_come_from_the_operator() {
for kind in [
TokenKind::Pipe,
TokenKind::Coalesce,
TokenKind::OrOr,
TokenKind::EqEq,
TokenKind::Plus,
TokenKind::Percent,
] {
let rung = binary_power(&kind).expect("should have a precedence");
assert_eq!(
rung.1,
rung.0.power(),
"{:?} reports a power of its own",
kind
);
assert_eq!(rung.2, rung.0.power() + 1);
}
}
#[test]
fn maps_tokens_to_their_operators() {
assert_eq!(
binary_power(&TokenKind::Plus).map(|pair| pair.0),
Some(BinaryOp::Add)
);
assert_eq!(
binary_power(&TokenKind::DotDot).map(|pair| pair.0),
Some(BinaryOp::Range),
"`..` is exclusive (doc 03 ยง4.2)"
);
}
}