Simplify error handling, finish up LR conflict message generation
This commit is contained in:
parent
d0c3e26e84
commit
ba96e4961b
6 changed files with 117 additions and 39 deletions
|
|
@ -455,9 +455,9 @@ impl<'a> ParseTableBuilder<'a> {
|
|||
self.symbol_name(&conflicting_lookahead)
|
||||
)
|
||||
.unwrap();
|
||||
write!(&mut msg, "Possible interpretations:\n").unwrap();
|
||||
write!(&mut msg, "Possible interpretations:\n\n").unwrap();
|
||||
for (i, item) in conflicting_items.iter().enumerate() {
|
||||
write!(&mut msg, "\n {}:", i).unwrap();
|
||||
write!(&mut msg, " {}:", i + 1).unwrap();
|
||||
|
||||
for preceding_symbol in preceding_symbols
|
||||
.iter()
|
||||
|
|
@ -501,11 +501,89 @@ impl<'a> ParseTableBuilder<'a> {
|
|||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
write!(&mut msg, "\n").unwrap();
|
||||
}
|
||||
|
||||
// TODO - generate suggested resolutions
|
||||
let mut resolution_count = 0;
|
||||
write!(&mut msg, "\nPossible resolutions:\n\n").unwrap();
|
||||
let shift_items = conflicting_items
|
||||
.iter()
|
||||
.filter(|i| !i.is_done())
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
if shift_items.len() > 0 {
|
||||
resolution_count += 1;
|
||||
write!(
|
||||
&mut msg,
|
||||
" {}: Specify a higher precedence in",
|
||||
resolution_count
|
||||
)
|
||||
.unwrap();
|
||||
for (i, item) in shift_items.iter().enumerate() {
|
||||
if i > 0 {
|
||||
write!(&mut msg, " and").unwrap();
|
||||
}
|
||||
write!(
|
||||
&mut msg,
|
||||
" `{}`",
|
||||
self.symbol_name(&Symbol::non_terminal(item.variable_index as usize))
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
write!(&mut msg, " than in the other rules.\n").unwrap();
|
||||
}
|
||||
|
||||
Err(Error::ConflictError(msg))
|
||||
if considered_associativity {
|
||||
resolution_count += 1;
|
||||
write!(
|
||||
&mut msg,
|
||||
" {}: Specify a left or right associativity in ",
|
||||
resolution_count
|
||||
)
|
||||
.unwrap();
|
||||
for (i, item) in conflicting_items.iter().filter(|i| i.is_done()).enumerate() {
|
||||
if i > 0 {
|
||||
write!(&mut msg, " and ").unwrap();
|
||||
}
|
||||
write!(
|
||||
&mut msg,
|
||||
"{}",
|
||||
self.symbol_name(&Symbol::non_terminal(item.variable_index as usize))
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
for item in &conflicting_items {
|
||||
if item.is_done() {
|
||||
resolution_count += 1;
|
||||
write!(
|
||||
&mut msg,
|
||||
" {}: Specify a higher precedence in `{}` than in the other rules.\n",
|
||||
resolution_count,
|
||||
self.symbol_name(&Symbol::non_terminal(item.variable_index as usize))
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
resolution_count += 1;
|
||||
write!(
|
||||
&mut msg,
|
||||
" {}: Add a conflict for these rules: ",
|
||||
resolution_count
|
||||
)
|
||||
.unwrap();
|
||||
for (i, symbol) in actual_conflict.iter().enumerate() {
|
||||
if i > 0 {
|
||||
write!(&mut msg, ", ").unwrap();
|
||||
}
|
||||
write!(&mut msg, "{}", self.symbol_name(symbol)).unwrap();
|
||||
}
|
||||
write!(&mut msg, "\n").unwrap();
|
||||
|
||||
Err(Error(msg))
|
||||
}
|
||||
|
||||
fn get_auxiliary_node_info(
|
||||
|
|
@ -517,8 +595,11 @@ impl<'a> ParseTableBuilder<'a> {
|
|||
.entries
|
||||
.keys()
|
||||
.filter_map(|item| {
|
||||
if item.symbol() == Some(symbol) {
|
||||
None
|
||||
let variable_index = item.variable_index as usize;
|
||||
if item.symbol() == Some(symbol)
|
||||
&& !self.syntax_grammar.variables[variable_index].is_auxiliary()
|
||||
{
|
||||
Some(Symbol::non_terminal(variable_index))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
|
|
|||
17
src/error.rs
17
src/error.rs
|
|
@ -1,25 +1,24 @@
|
|||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
GrammarError(String),
|
||||
SymbolError(String),
|
||||
RegexError(String),
|
||||
ConflictError(String),
|
||||
}
|
||||
pub struct Error(pub String);
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
impl Error {
|
||||
pub fn grammar(message: &str) -> Self {
|
||||
Error::GrammarError(message.to_string())
|
||||
Error(format!("Grammar error: {}", message))
|
||||
}
|
||||
|
||||
pub fn regex(message: &str) -> Self {
|
||||
Error::RegexError(message.to_string())
|
||||
Error(format!("Regex error: {}", message))
|
||||
}
|
||||
|
||||
pub fn undefined_symbol(name: &str) -> Self {
|
||||
Error(format!("Undefined symbol `{}`", name))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<serde_json::Error> for Error {
|
||||
fn from(error: serde_json::Error) -> Self {
|
||||
Error::GrammarError(error.to_string())
|
||||
Error(error.to_string())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
11
src/main.rs
11
src/main.rs
|
|
@ -11,7 +11,7 @@ use clap::{App, Arg, SubCommand};
|
|||
use std::env;
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
use std::process::{Command, Stdio};
|
||||
use std::process::{exit, Command, Stdio};
|
||||
|
||||
mod build_tables;
|
||||
mod error;
|
||||
|
|
@ -25,7 +25,14 @@ mod render;
|
|||
mod rules;
|
||||
mod tables;
|
||||
|
||||
fn main() -> error::Result<()> {
|
||||
fn main() {
|
||||
if let Err(e) = run() {
|
||||
eprintln!("{}", e.0);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
fn run() -> error::Result<()> {
|
||||
let matches = App::new("tree-sitter")
|
||||
.version("0.1")
|
||||
.author("Max Brunsfeld <maxbrunsfeld@gmail.com>")
|
||||
|
|
|
|||
|
|
@ -64,12 +64,7 @@ pub(crate) fn expand_tokens(mut grammar: ExtractedLexicalGrammar) -> Result<Lexi
|
|||
let last_state_id = builder.nfa.last_state_id();
|
||||
builder
|
||||
.expand_rule(&variable.rule, last_state_id)
|
||||
.map_err(|e| match e {
|
||||
Error::RegexError(msg) => {
|
||||
Error::RegexError(format!("Rule {} {}", variable.name, msg))
|
||||
}
|
||||
_ => e,
|
||||
})?;
|
||||
.map_err(|Error(msg)| Error(format!("Rule {} {}", variable.name, msg)))?;
|
||||
|
||||
if !is_immediate_token {
|
||||
builder.is_sep = true;
|
||||
|
|
@ -97,7 +92,7 @@ impl NfaBuilder {
|
|||
Rule::Pattern(s) => {
|
||||
let ast = parse::Parser::new()
|
||||
.parse(&s)
|
||||
.map_err(|e| Error::GrammarError(e.to_string()))?;
|
||||
.map_err(|e| Error(e.to_string()))?;
|
||||
self.expand_regex(&ast, next_state_id)
|
||||
}
|
||||
Rule::String(s) => {
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ pub(super) fn extract_tokens(
|
|||
if let Rule::Symbol(symbol) = rule {
|
||||
let new_symbol = symbol_replacer.replace_symbol(symbol);
|
||||
if new_symbol.is_non_terminal() {
|
||||
return Err(Error::GrammarError(format!(
|
||||
return Err(Error(format!(
|
||||
"Non-token symbol '{}' cannot be used as an extra token",
|
||||
&variables[new_symbol.index].name
|
||||
)));
|
||||
|
|
@ -110,7 +110,7 @@ pub(super) fn extract_tokens(
|
|||
let rule = symbol_replacer.replace_symbols_in_rule(&external_token.rule);
|
||||
if let Rule::Symbol(symbol) = rule {
|
||||
if symbol.is_non_terminal() {
|
||||
return Err(Error::GrammarError(format!(
|
||||
return Err(Error(format!(
|
||||
"Rule '{}' cannot be used as both an external token and a non-terminal rule",
|
||||
&variables[symbol.index].name,
|
||||
)));
|
||||
|
|
@ -130,7 +130,7 @@ pub(super) fn extract_tokens(
|
|||
})
|
||||
}
|
||||
} else {
|
||||
return Err(Error::GrammarError(format!(
|
||||
return Err(Error(format!(
|
||||
"Non-symbol rules cannot be used as external tokens"
|
||||
)));
|
||||
}
|
||||
|
|
@ -140,7 +140,7 @@ pub(super) fn extract_tokens(
|
|||
if let Some(token) = grammar.word_token {
|
||||
let token = symbol_replacer.replace_symbol(token);
|
||||
if token.is_non_terminal() {
|
||||
return Err(Error::GrammarError(format!(
|
||||
return Err(Error(format!(
|
||||
"Non-terminal symbol '{}' cannot be used as the word token",
|
||||
&variables[token.index].name
|
||||
)));
|
||||
|
|
@ -475,7 +475,7 @@ mod test {
|
|||
grammar.extra_tokens = vec![Rule::non_terminal(1)];
|
||||
|
||||
match extract_tokens(grammar) {
|
||||
Err(Error::GrammarError(s)) => {
|
||||
Err(Error(s)) => {
|
||||
assert_eq!(
|
||||
s,
|
||||
"Non-token symbol 'rule_1' cannot be used as an extra token"
|
||||
|
|
@ -503,7 +503,7 @@ mod test {
|
|||
grammar.external_tokens = vec![Variable::named("rule_1", Rule::non_terminal(1))];
|
||||
|
||||
match extract_tokens(grammar) {
|
||||
Err(Error::GrammarError(s)) => {
|
||||
Err(Error(s)) => {
|
||||
assert_eq!(s, "Rule 'rule_1' cannot be used as both an external token and a non-terminal rule");
|
||||
}
|
||||
_ => {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ pub(super) fn intern_symbols(grammar: &InputGrammar) -> Result<InternedGrammar>
|
|||
let interner = Interner { grammar };
|
||||
|
||||
if variable_type_for_name(&grammar.variables[0].name) == VariableType::Hidden {
|
||||
return Err(Error::GrammarError(
|
||||
return Err(Error(
|
||||
"Grammar's start rule must be visible".to_string(),
|
||||
));
|
||||
}
|
||||
|
|
@ -44,7 +44,7 @@ pub(super) fn intern_symbols(grammar: &InputGrammar) -> Result<InternedGrammar>
|
|||
interned_conflict.push(
|
||||
interner
|
||||
.intern_name(&name)
|
||||
.ok_or_else(|| symbol_error(name))?,
|
||||
.ok_or_else(|| Error::undefined_symbol(name))?,
|
||||
);
|
||||
}
|
||||
expected_conflicts.push(interned_conflict);
|
||||
|
|
@ -62,7 +62,7 @@ pub(super) fn intern_symbols(grammar: &InputGrammar) -> Result<InternedGrammar>
|
|||
word_token = Some(
|
||||
interner
|
||||
.intern_name(&name)
|
||||
.ok_or_else(|| symbol_error(&name))?,
|
||||
.ok_or_else(|| Error::undefined_symbol(&name))?,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -107,7 +107,7 @@ impl<'a> Interner<'a> {
|
|||
if let Some(symbol) = self.intern_name(&name) {
|
||||
Ok(Rule::Symbol(symbol))
|
||||
} else {
|
||||
Err(symbol_error(name))
|
||||
Err(Error::undefined_symbol(name))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -134,10 +134,6 @@ impl<'a> Interner<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn symbol_error(name: &str) -> Error {
|
||||
Error::SymbolError(format!("Undefined symbol '{}'", name))
|
||||
}
|
||||
|
||||
fn variable_type_for_name(name: &str) -> VariableType {
|
||||
if name.starts_with("_") {
|
||||
VariableType::Hidden
|
||||
|
|
@ -223,7 +219,7 @@ mod tests {
|
|||
let result = intern_symbols(&build_grammar(vec![Variable::named("x", Rule::named("y"))]));
|
||||
|
||||
match result {
|
||||
Err(Error::SymbolError(message)) => assert_eq!(message, "Undefined symbol 'y'"),
|
||||
Err(Error(message)) => assert_eq!(message, "Undefined symbol 'y'"),
|
||||
_ => panic!("Expected an error but got none"),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue