use proc_macro2::Span;
use proc_macro2::TokenStream;
use quote::quote_spanned;
use crate::parser::args::IdentOrLiteral;
use crate::parser::location::Location;
use crate::parser::location::Located;
#[non_exhaustive]
#[derive(Debug)]
pub enum ParseArgError {
MacroLineParse {
location: Location,
message: String,
},
}
#[non_exhaustive]
#[derive(Debug)]
pub enum ArgError {
MultipleModulePrefixDefinition(Vec<Location>),
MultipleUserDataDefinition(Vec<Location>),
MultipleErrorDefinition(Vec<Location>),
MultipleTokenTypeDefinition(Vec<Location>),
MultipleEofDefinition(Vec<Location>),
MultipleStartDefinition(Vec<Location>),
MultiplePrecDefinition(Vec<Location>),
MultipleDPrecDefinition(Vec<Location>),
StartNotDefined,
EofNotDefined,
TokenTypeNotDefined,
MultipleNameDefinition(String, Vec<Location>),
ReservedName(Vec<Located<String>>),
}
#[non_exhaustive]
#[derive(Debug)]
pub enum ConflictError {
ShiftReduceConflict {
term: String,
reduce_rule: (usize, rusty_lr_core::rule::ProductionRule<String, String>),
shift_rules: Vec<(usize, rusty_lr_core::rule::ShiftedRule<String, String>)>,
},
ReduceReduceConflict {
lookahead: String,
rule1: (usize, rusty_lr_core::rule::ProductionRule<String, String>),
rule2: (usize, rusty_lr_core::rule::ProductionRule<String, String>),
},
}
#[non_exhaustive]
#[derive(Debug)]
pub enum ParseError {
MultipleReduceDefinition(
Vec<Located<rusty_lr_core::rule::ReduceType>>,
),
InvalidTerminalRange{ location: Location, start: (Located<String>, usize), end: (Located<String>, usize) },
StartNonTerminalNotDefined(Location),
TerminalNotDefined(Location),
UnsupportedLiteralType(Location),
InvalidLiteralRange(Location),
TokenInLiteralMode(Vec<Location>),
MultiplePrecedenceOrderDefinition (Vec<Location>),
PrecedenceNotDefined(IdentOrLiteral),
NonTerminalPrecedenceNotDefined(Located<usize>),
RuleTypeDefinedButActionNotDefined {
nonterm: Location,
rule: Location
},
OnlyTerminalSet(Location),
NonTerminalNotDefined(Location),
OnlyUsizeLiteral(Location),
BisonVariableZero(Location),
BisonVariableOutOfRange {
location: Location,
name: String,
max: usize,
},
TypeInferenceFailed(Location),
}
#[allow(unused)]
impl ArgError {
pub fn to_compile_error(&self, span_manager: &crate::parser::location::SpanManager) -> TokenStream {
let mut output = TokenStream::new();
let message = self.short_message();
for loc in self.locations() {
for span in span_manager.get_spans_in_location(&loc) {
output.extend(quote_spanned! {
span=>
compile_error!(#message);
});
}
}
output
}
pub fn locations(&self) -> Vec<Location> {
match self {
ArgError::MultipleModulePrefixDefinition(locs)
| ArgError::MultipleUserDataDefinition(locs)
| ArgError::MultipleErrorDefinition(locs)
| ArgError::MultipleTokenTypeDefinition(locs)
| ArgError::MultipleEofDefinition(locs)
| ArgError::MultipleStartDefinition(locs)
| ArgError::MultiplePrecDefinition(locs)
| ArgError::MultipleDPrecDefinition(locs) => locs.clone(),
ArgError::MultipleNameDefinition(_, locs) => locs.clone(),
ArgError::ReservedName(names) => names.iter().map(|name| name.location()).collect(),
_ => vec![Location::default()],
}
}
pub fn short_message(&self) -> String {
match self {
ArgError::MultipleModulePrefixDefinition(_) => {
"Multiple %moduleprefix definition".into()
}
ArgError::MultipleUserDataDefinition(_) => "Multiple %userdata definition".into(),
ArgError::MultipleErrorDefinition(_) => "Multiple %error definition".into(),
ArgError::MultipleTokenTypeDefinition(_) => "Multiple %tokentype definition".into(),
ArgError::MultipleEofDefinition(_) => "Multiple %eof definition".into(),
ArgError::MultipleStartDefinition(_) => "Multiple %start definition".into(),
ArgError::MultiplePrecDefinition(_) => "Multiple %prec definition".into(),
ArgError::MultipleDPrecDefinition(_) => "Multiple %dprec definition".into(),
ArgError::StartNotDefined => "Start rule not defined\n>>> %start <rule_name>;".into(),
ArgError::EofNotDefined => "Eof not defined\n>>> %eof <eof_token_value>;".into(),
ArgError::TokenTypeNotDefined => {
"Token type not defined\n>>> %tokentype <token_type_name>;".into()
}
ArgError::MultipleNameDefinition(name, _) => {
format!("Duplicated name for terminal or non-terminal: {}", name)
}
ArgError::ReservedName(names) =>
"This name is reserved and cannot be used".into(),
}
}
}
#[allow(unused)]
impl ParseArgError {
pub fn to_compile_error(&self, span_manager: &crate::parser::location::SpanManager) -> TokenStream {
let mut output = TokenStream::new();
let message = self.short_message();
let location = self.location();
for span in span_manager.get_spans_in_location(&location) {
output.extend(
quote_spanned! {
span=>
compile_error!(#message);
}
);
}
output
}
pub fn location(&self) -> Location {
match self {
ParseArgError::MacroLineParse { location, message } => *location,
}
}
pub fn short_message(&self) -> String {
match self {
ParseArgError::MacroLineParse { message, .. } => message.clone(),
}
}
}
#[allow(unused)]
impl ParseError {
pub fn to_compile_error(&self, span_manager: &crate::parser::location::SpanManager) -> TokenStream {
let mut output = TokenStream::new();
let message = self.short_message();
for loc in self.locations() {
for span in span_manager.get_spans_in_location(&loc) {
output.extend(quote_spanned! {
span=>
compile_error!(#message);
});
}
}
output
}
pub fn locations(&self) -> Vec<Location> {
match self {
ParseError::MultipleReduceDefinition(locations) => locations.iter().map(Located::location).collect(),
ParseError::InvalidTerminalRange { location: range, start, end } => vec![range.clone()],
ParseError::StartNonTerminalNotDefined(loc) => vec![*loc],
ParseError::TerminalNotDefined(loc) => vec![*loc],
ParseError::UnsupportedLiteralType(loc) => vec![*loc],
ParseError::InvalidLiteralRange(loc) => vec![*loc],
ParseError::TokenInLiteralMode(locs) => locs.clone(),
ParseError::MultiplePrecedenceOrderDefinition(locations) => locations.clone(),
ParseError::PrecedenceNotDefined(name) => vec![name.location()],
ParseError::NonTerminalPrecedenceNotDefined(loc) => vec![loc.location()],
ParseError::RuleTypeDefinedButActionNotDefined { nonterm, rule} => vec![*nonterm, *rule],
ParseError::OnlyTerminalSet(location) => vec![*location],
ParseError::NonTerminalNotDefined(loc) => vec![*loc],
ParseError::OnlyUsizeLiteral(loc) => vec![*loc],
ParseError::BisonVariableZero(loc) => vec![*loc],
ParseError::BisonVariableOutOfRange { location, .. } => vec![*location],
ParseError::TypeInferenceFailed(location) => vec![*location],
}
}
pub fn short_message(&self) -> String {
match self {
ParseError::MultipleReduceDefinition(_) => {
"Different reduce type (%left and %right) applied to the same terminal symbol".to_string()
}
ParseError::InvalidTerminalRange { location: range, start, end } => {
format!(
"Invalid terminal range: [{}({}) - {}({})]",
start.0, start.1, end.0, end.1
)
}
ParseError::StartNonTerminalNotDefined(_) => {
"Name given to %start not defined".to_string()
}
ParseError::TerminalNotDefined(_) => {
"Unknown terminal symbol name".to_string()
}
ParseError::UnsupportedLiteralType(_) => {
format!("This literal type is not supported. Use string literal (e.g. 'a', \"abc\", b'a' or b\"abc\") instead")
}
ParseError::InvalidLiteralRange(_) =>
"Invalid literal range: [first, last] with first > last".to_string(),
ParseError::TokenInLiteralMode(_) => {
"%token with %tokentype `char` or `u8` is not supported. Use character literal (e.g. 'a' or b'a') instead"
.to_string()
}
ParseError::MultiplePrecedenceOrderDefinition(_) =>
"Multiple precedence order definition for the same token".to_string(),
ParseError::PrecedenceNotDefined(_) => {
"Precedence not defined for the given token".to_string()
}
ParseError::NonTerminalPrecedenceNotDefined(_) => {
"All production rules in this non-terminal must have %prec defined".into()
}
ParseError::RuleTypeDefinedButActionNotDefined { .. } => {
"ReduceAction must be defined for this rule".into()
}
ParseError::OnlyTerminalSet(_) => "Only terminal or terminal set is allowed".into(),
ParseError::NonTerminalNotDefined(_) => {
"Unknown non-terminal symbol name".to_string()
}
ParseError::OnlyUsizeLiteral(_) => "Only 'usize' literal is allowed for %dprec".into(),
ParseError::BisonVariableZero(_) => "bison variable $0 is not supported".into(),
ParseError::BisonVariableOutOfRange { name, max, .. } => {
format!("bison variable {} is out of range (max: {})", name, max)
}
ParseError::TypeInferenceFailed(_) => {
"type inference failed: circular dependency or no identity action found".to_string()
}
}
}
}
#[allow(unused)]
impl ConflictError {
pub fn to_compile_error(&self) -> TokenStream {
let span = self.span();
let message = self.short_message();
quote_spanned! {
span=>
compile_error!(#message);
}
}
pub fn span(&self) -> Span {
match self {
ConflictError::ShiftReduceConflict {
term,
reduce_rule: (ruleid, rule),
shift_rules,
} => Span::call_site(),
ConflictError::ReduceReduceConflict {
lookahead,
rule1: (ruleid1, rule1),
rule2: (ruleid2, rule2),
} => Span::call_site(),
}
}
pub fn short_message(&self) -> String {
match self {
ConflictError::ShiftReduceConflict {
term,
reduce_rule: (ruleid, rule),
shift_rules,
} => {
format!(
"Shift-Reduce conflict with terminal symbol: {}\n>>> Reduce: {}\n>>> Shifts: {}",
term,
rule,
shift_rules
.iter()
.map(|(ruleid, rule)| format!("{}", rule))
.collect::<Vec<_>>()
.join("\n>>>")
)
}
ConflictError::ReduceReduceConflict {
lookahead,
rule1: (ruleid1, rule1),
rule2: (ruleid2, rule2),
} => {
format!(
"Reduce-Reduce conflict with lookahead symbol: {}\n>>> Rule1: {}\n>>> Rule2: {}",
lookahead, rule1, rule2
)
}
}
}
}