Skip to content

Commit

Permalink
Merge pull request #7 from tlaplus-community/locate-parse-error
Browse files Browse the repository at this point in the history
Output probable line of first syntax error
  • Loading branch information
ahelwer authored Nov 8, 2023
2 parents 653a483 + cedbe5c commit d73115e
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 14 deletions.
12 changes: 6 additions & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "tlauc"
description = "Rewrites TLA⁺ specs to use Unicode symbols instead of ASCII, and vice-versa"
version = "0.1.4"
version = "0.1.5"
authors = ["Andrew Helwer <2n8rn1w1f@mozmail.com>"]
repository = "https://github.com/tlaplus-community/tlauc"
license = "MIT"
Expand All @@ -12,12 +12,12 @@ edition = "2018"
exclude = ["tests", ".github", ".gitignore", ".gitmodules"]

[dependencies]
anyhow = "1.0.68"
anyhow = "1.0.75"
clap = { version = "4.0.32", features = ["derive"] }
csv = "1.1.6"
serde = { version = "1.0.149", features = ["derive"] }
tree-sitter = "0.20.9"
tree-sitter-tlaplus = "1.0.1"
csv = "1.3.0"
serde = { version = "1.0.192", features = ["derive"] }
tree-sitter = "0.20.10"
tree-sitter-tlaplus = "1.0.4"

[dev-dependencies]
glob = "0.3.0"
Expand Down
60 changes: 55 additions & 5 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::strmeasure::*;

use serde::{Deserialize, Deserializer};
use std::ops::Range;
use tree_sitter::{Parser, Query, QueryCursor, Tree, TreeCursor};
use tree_sitter::{Node, Parser, Query, QueryCursor, Tree, TreeCursor};

pub enum Mode {
AsciiToUnicode,
Expand All @@ -12,7 +12,10 @@ pub enum Mode {

#[derive(Debug)]
pub enum TlaError {
InputFileParseError(Tree),
InputFileParseError {
parse_tree: Tree,
first_error_line: Option<usize>,
},
OutputFileParseError {
output_tree: Tree,
output: String,
Expand All @@ -35,7 +38,12 @@ pub fn rewrite(input: &str, mode: &Mode, force: bool) -> Result<String, TlaError
// Parse input TLA⁺ file and construct data structures to hold information about it
let input_tree = parser.parse(input, None).unwrap();
if !force && input_tree.root_node().has_error() {
return Err(TlaError::InputFileParseError(input_tree));
let first_error_line =
find_first_error_line(&input_tree).map_or_else(|e| Some(e), |_| None);
return Err(TlaError::InputFileParseError {
parse_tree: input_tree,
first_error_line,
});
}

let mut tla_lines = TlaLine::construct_from(input);
Expand Down Expand Up @@ -73,6 +81,41 @@ pub fn rewrite(input: &str, mode: &Mode, force: bool) -> Result<String, TlaError
Ok(output)
}

fn find_first_error_line(tree: &Tree) -> Result<(), usize> {
traverse_parse_tree(tree, |n| {
if n.is_error() || n.is_missing() {
Err(n.start_position().row + 1)
} else {
Ok(())
}
})
}

fn traverse_parse_tree<T>(tree: &Tree, m: fn(Node) -> Result<(), T>) -> Result<(), T> {
let mut cursor: TreeCursor = tree.walk();
loop {
// Every time a new node is found the control flow passes here
m(cursor.node())?;
// Descend as far as possible
if !cursor.goto_first_child() {
loop {
// Attempt to go to sibling
if cursor.goto_next_sibling() {
// If sibling exists, break out into descent loop
break;
} else {
// If sibling does not exist, go to parent, then
// parent's sibling in next loop iteration
if !cursor.goto_parent() {
// If parent does not exist, we are done
return Ok(());
}
}
}
}
}
}

fn compare_parse_trees(input_tree: &Tree, output_tree: &Tree) -> Result<(), String> {
let mut input_cursor: TreeCursor = input_tree.walk();
let mut output_cursor: TreeCursor = output_tree.walk();
Expand Down Expand Up @@ -510,8 +553,15 @@ mod tests {
fn unwrap_conversion(input: Result<String, TlaError>) -> String {
match input {
Ok(converted) => converted,
Err(TlaError::InputFileParseError(tree)) => {
panic!("{}", tree.root_node().to_sexp())
Err(TlaError::InputFileParseError {
parse_tree,
first_error_line,
}) => {
panic!(
"{}\n{}",
first_error_line.unwrap_or(0),
parse_tree.root_node().to_sexp()
)
}
Err(TlaError::OutputFileParseError {
output_tree,
Expand Down
7 changes: 6 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,12 @@ fn convert(config: &Config, mode: Mode) -> Result<()> {
output_file.write_all(output.as_bytes()).context(format!("Failed to write to output file [{}]", &config.output))?;
Ok(())
},
Err(TlaError::InputFileParseError(_)) => Err(anyhow!("Failed to correctly parse input TLA⁺ file; use --force flag to bypass this check.")),
Err(TlaError::InputFileParseError { first_error_line, .. }) => {
let line_msg = first_error_line.map_or(
"Could not identify line of first syntax error.".to_string(),
|line| format!("First syntax error might occur on line {}.", line));
Err(anyhow!("Failed to correctly parse input TLA⁺ file; use --force flag to bypass this check.\n".to_string() + &line_msg))
}
Err(TlaError::OutputFileParseError{..}) => Err(anyhow!("Failed to correctly parse converted TLA⁺ output; this is a bug, please report it to the maintainer! Use --force to bypass this check (not recommended).")),
Err(TlaError::InvalidTranslationError { input_tree: _, output_tree: _, output: _, first_diff }) => {
let err_msg = "Converted TLA⁺ parse tree differs from original; this is a bug, please report it to the maintainer! Use --force to bypass this check (not recommended).";
Expand Down
2 changes: 1 addition & 1 deletion tests/corpus
Submodule corpus updated 143 files
2 changes: 1 addition & 1 deletion tests/corpus_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ mod corpus_tests {
fn unwrap_conversion(input: Result<String, TlaError>, path: &PathBuf) -> String {
match input {
Ok(converted) => converted,
Err(TlaError::InputFileParseError(_)) => {
Err(TlaError::InputFileParseError { .. }) => {
panic!("Failed to parse input file [{:?}]", path)
}
Err(TlaError::OutputFileParseError { .. }) => {
Expand Down

0 comments on commit d73115e

Please sign in to comment.