added better error messages inspired by rustc/cargo

but not as good
This commit is contained in:
Mark
2023-10-23 21:48:15 +02:00
parent 62ed8fc2bd
commit ea95a16c30
30 changed files with 557 additions and 260 deletions

View File

@@ -8,15 +8,17 @@ pub mod types;
pub fn parse(src: &mut Source) -> Result<Box<dyn program::parsed::MersStatement>, ()> {
let pos_in_src = src.get_pos();
let statements = statements::parse_multiple(src, "")?;
let block = Block {
pos_in_src,
statements: statements::parse_multiple(src, "")?,
pos_in_src: (pos_in_src, src.get_pos()).into(),
statements,
};
Ok(Box::new(block))
}
pub struct Source {
src_raw_len: usize,
src_og: String,
src: String,
/// (start, content) of each comment, including start/end (//, \n, /* and */)
comments: Vec<(usize, String)>,
@@ -83,6 +85,7 @@ impl Source {
}
Self {
src_raw_len: source.len(),
src_og: source,
src,
comments,
i: 0,
@@ -90,6 +93,13 @@ impl Source {
}
}
pub fn src(&self) -> &String {
&self.src
}
pub fn comments(&self) -> &Vec<(usize, String)> {
&self.comments
}
pub fn skip_whitespace(&mut self) {
if let Some(i) = self.src[self.i..].char_indices().find_map(|(i, ch)| {
if !ch.is_whitespace() {
@@ -172,6 +182,17 @@ impl Source {
&self.sections
}
pub fn get_line_start(&self, pos: usize) -> usize {
self.src[0..pos].rfind("\n").map(|i| i + 1).unwrap_or(0)
}
pub fn get_line_end(&self, pos: usize) -> usize {
// TODO: If the newline is preceded by `\r`s, remove those too since they are part of the newline `\r\n` sequence
self.src[pos..]
.find("\n")
.map(|i| i + pos)
.unwrap_or(self.src.len())
}
pub fn format(&self, insertions: &Vec<(usize, bool, String)>) -> String {
let mut o = String::with_capacity(self.src_raw_len);
let mut insertions = insertions.iter().peekable();
@@ -244,6 +265,9 @@ impl SectionMarker {
#[derive(Clone, Copy, Debug)]
pub struct SourcePos(usize);
impl SourcePos {
pub fn pos(&self) -> usize {
self.0
}
fn diff(&self, rhs: &Self) -> usize {
rhs.0 - self.0
}

View File

@@ -16,28 +16,31 @@ pub fn parse(src: &mut Source) -> Result<Option<Box<dyn program::parsed::MersSta
":=" => {
let pos_in_src = src.get_pos();
src.next_word();
let source = parse(src)?.expect("todo");
first = Box::new(program::parsed::init_to::InitTo {
pos_in_src,
pos_in_src: (pos_in_src, src.get_pos()).into(),
target: first,
source: parse(src)?.expect("todo"),
source,
});
}
"=" => {
let pos_in_src = src.get_pos();
src.next_word();
let source = parse(src)?.expect("todo");
first = Box::new(program::parsed::assign_to::AssignTo {
pos_in_src,
pos_in_src: (pos_in_src, src.get_pos()).into(),
target: first,
source: parse(src)?.expect("todo"),
source,
});
}
"->" => {
let pos_in_src = src.get_pos();
src.next_word();
let run = parse(src)?.expect("err: bad eof, fn needs some statement");
first = Box::new(program::parsed::function::Function {
pos_in_src,
pos_in_src: (pos_in_src, src.get_pos()).into(),
arg: first,
run: parse(src)?.expect("err: bad eof, fn needs some statement"),
run,
});
}
_ => loop {
@@ -47,7 +50,7 @@ pub fn parse(src: &mut Source) -> Result<Option<Box<dyn program::parsed::MersSta
src.next_char();
let chained = parse_no_chain(src)?.expect("err: EOF instead of chain");
first = Box::new(program::parsed::chain::Chain {
pos_in_src,
pos_in_src: (pos_in_src, src.get_pos()).into(),
first,
chained,
});
@@ -87,17 +90,19 @@ pub fn parse_no_chain(
Some('{') => {
let pos_in_src = src.get_pos();
src.next_char();
let statements = parse_multiple(src, "}")?;
return Ok(Some(Box::new(program::parsed::block::Block {
pos_in_src,
statements: parse_multiple(src, "}")?,
pos_in_src: (pos_in_src, src.get_pos()).into(),
statements,
})));
}
Some('(') => {
let pos_in_src = src.get_pos();
src.next_char();
let elems = parse_multiple(src, ")")?;
return Ok(Some(Box::new(program::parsed::tuple::Tuple {
pos_in_src,
elems: parse_multiple(src, ")")?,
pos_in_src: (pos_in_src, src.get_pos()).into(),
elems,
})));
}
Some('"') => {
@@ -127,7 +132,7 @@ pub fn parse_no_chain(
}
}
return Ok(Some(Box::new(program::parsed::value::Value {
pos_in_src,
pos_in_src: (pos_in_src, src.get_pos()).into(),
data: Data::new(crate::data::string::String(s)),
})));
}
@@ -137,28 +142,31 @@ pub fn parse_no_chain(
Ok(Some(match src.next_word() {
"if" => {
src.section_begin("if".to_string());
let condition = parse(src)?.expect("err: EOF instead of condition");
let on_true = parse(src)?.expect("err: EOF instead of on_true");
let on_false = {
src.skip_whitespace();
if src.peek_word() == "else" {
src.section_begin("else".to_string());
src.next_word();
Some(parse(src)?.expect("err: EOF instead of on_false after else"))
} else {
None
}
};
Box::new(program::parsed::r#if::If {
pos_in_src,
condition: parse(src)?.expect("err: EOF instead of condition"),
on_true: parse(src)?.expect("err: EOF instead of on_true"),
on_false: {
src.skip_whitespace();
if src.peek_word() == "else" {
src.section_begin("else".to_string());
src.next_word();
Some(parse(src)?.expect("err: EOF instead of on_false after else"))
} else {
None
}
},
pos_in_src: (pos_in_src, src.get_pos()).into(),
condition,
on_true,
on_false,
})
}
"true" => Box::new(program::parsed::value::Value {
pos_in_src,
pos_in_src: (pos_in_src, src.get_pos()).into(),
data: Data::new(crate::data::bool::Bool(true)),
}),
"false" => Box::new(program::parsed::value::Value {
pos_in_src,
pos_in_src: (pos_in_src, src.get_pos()).into(),
data: Data::new(crate::data::bool::Bool(false)),
}),
"" => return Ok(None),
@@ -171,32 +179,32 @@ pub fn parse_no_chain(
src.next_char();
if let Ok(num) = format!("{o}.{}", src.next_word()).parse() {
Box::new(program::parsed::value::Value {
pos_in_src,
pos_in_src: (pos_in_src, src.get_pos()).into(),
data: Data::new(crate::data::float::Float(num)),
})
} else {
src.set_pos(here);
Box::new(program::parsed::value::Value {
pos_in_src,
pos_in_src: (pos_in_src, src.get_pos()).into(),
data: Data::new(crate::data::int::Int(n)),
})
}
} else {
Box::new(program::parsed::value::Value {
pos_in_src,
pos_in_src: (pos_in_src, src.get_pos()).into(),
data: Data::new(crate::data::int::Int(n)),
})
}
} else {
if let Some('&') = o.chars().next() {
Box::new(program::parsed::variable::Variable {
pos_in_src,
pos_in_src: (pos_in_src, src.get_pos()).into(),
is_ref: true,
var: o[1..].to_string(),
})
} else {
Box::new(program::parsed::variable::Variable {
pos_in_src,
pos_in_src: (pos_in_src, src.get_pos()).into(),
is_ref: false,
var: o.to_string(),
})