Add some basic error handling.

TODO: This, better
This commit is contained in:
John 2024-04-06 06:06:43 -05:00
parent d1d8c45bdb
commit 8f8cfd4cb5
2 changed files with 138 additions and 66 deletions

View File

@ -1,5 +1,4 @@
use std::{iter::Peekable, str::CharIndices}; use std::{iter::Peekable, str::CharIndices};
use unicode_ident::*; use unicode_ident::*;
/// Rule = ident '=' Either? ';' ; /// Rule = ident '=' Either? ';' ;
@ -94,6 +93,9 @@ impl<'a> Parser<'a> {
tail: 0, tail: 0,
} }
} }
pub fn error(&mut self, kind: ErrorKind) -> error::Error {
error::Error::new(self.head, self.tail, kind)
}
pub fn start(&mut self) -> &mut Self { pub fn start(&mut self) -> &mut Self {
self.space(); self.space();
self.head = self.tail; self.head = self.tail;
@ -105,8 +107,11 @@ impl<'a> Parser<'a> {
} = self; } = self;
&text[head..tail] &text[head..tail]
} }
pub fn peek(&mut self) -> Option<char> { pub fn peek(&mut self) -> Result<char> {
self.chars.peek().map(|(_, c)| *c) self.chars
.peek()
.map(|(_, c)| *c)
.ok_or_else(|| self.error(ErrorKind::EndOfInput))
} }
pub fn take(&mut self) -> Option<(usize, char)> { pub fn take(&mut self) -> Option<(usize, char)> {
let out = self.chars.next(); let out = self.chars.next();
@ -116,14 +121,19 @@ impl<'a> Parser<'a> {
}; };
out out
} }
pub fn take_one(&mut self, f: fn(char) -> bool) -> Option<&mut Self> { pub fn take_one(&mut self, f: fn(char) -> bool) -> Result<&mut Self> {
self.chars.peek().filter(|(_, c)| f(*c)).is_some().then(|| { let Some(&(_, c)) = self.chars.peek() else {
Err(self.error(ErrorKind::EndOfInput))?
};
if f(c) {
self.take(); self.take();
self Ok(self)
}) } else {
Err(self.error(ErrorKind::Unexpected(c)))
}
} }
pub fn take_many(&mut self, f: fn(char) -> bool) -> &mut Self { pub fn take_many(&mut self, f: fn(char) -> bool) -> &mut Self {
while self.take_one(f).is_some() {} while self.take_one(f).is_ok() {}
self self
} }
pub fn space(&mut self) -> &mut Self { pub fn space(&mut self) -> &mut Self {
@ -132,9 +142,9 @@ impl<'a> Parser<'a> {
} }
impl<'a> Parser<'a> { impl<'a> Parser<'a> {
pub fn rule(&mut self) -> Option<Rule<'a>> { pub fn rule(&mut self) -> Result<Rule<'a>> {
let out = Rule { let out = Rule {
comment: self.comment(), comment: self.comment().ok(),
name: self.ident()?, name: self.ident()?,
body: { body: {
self.space() self.space()
@ -143,102 +153,91 @@ impl<'a> Parser<'a> {
.unwrap_or_default() .unwrap_or_default()
}, },
}; };
if self.space().take_one(|c| ';' == c).is_none() { self.space().take_one(|c| ';' == c).map(|_| out)
panic!("Rule should end in ';': {}..{}", self.head, self.tail)
} }
Some(out) pub fn either(&mut self) -> Result<RuleKind<'a>> {
}
pub fn either(&mut self) -> Option<RuleKind<'a>> {
let mut out = vec![self.follow()?]; let mut out = vec![self.follow()?];
while self.space().take_one(|c| '|' == c).is_some() { while self.space().take_one(|c| '|' == c).is_ok() {
out.push(self.follow()?) out.push(self.follow()?)
} }
match out.len() { Ok(match out.len() {
1 => out.pop(), 1 => out.pop().expect("pop should succeed when length is 1"),
_ => Some(RuleKind::Either(out)), _ => RuleKind::Either(out),
})
} }
} pub fn follow(&mut self) -> Result<RuleKind<'a>> {
pub fn follow(&mut self) -> Option<RuleKind<'a>> {
let mut out = vec![]; let mut out = vec![];
while let Some(rule) = self.repeat() { while let Ok(rule) = self.repeat() {
out.push(rule) out.push(rule)
} }
match out.len() { Ok(match out.len() {
1 => out.pop(), 1 => out.pop().expect("pop should succeed when length is 1"),
_ => Some(RuleKind::Follow(out)), _ => RuleKind::Follow(out),
})
} }
} pub fn repeat(&mut self) -> Result<RuleKind<'a>> {
pub fn repeat(&mut self) -> Option<RuleKind<'a>> {
let out = self.not()?; let out = self.not()?;
let out = match self.space().peek() { let out = match self.space().peek() {
Some('*') => RuleKind::Any(out.into()), Ok('*') => RuleKind::Any(out.into()),
Some('+') => RuleKind::Many(out.into()), Ok('+') => RuleKind::Many(out.into()),
Some('?') => RuleKind::Maybe(out.into()), Ok('?') => RuleKind::Maybe(out.into()),
_ => return Some(out), _ => return Ok(out),
}; };
self.take(); self.take();
Some(out) Ok(out)
} }
pub fn not(&mut self) -> Option<RuleKind<'a>> { pub fn not(&mut self) -> Result<RuleKind<'a>> {
match self.space().take_one(|c| '!' == c) { match self.space().take_one(|c| '!' == c) {
Some(_) => Some(RuleKind::Not(self.prime()?.into())), Ok(_) => Ok(RuleKind::Not(self.prime()?.into())),
_ => self.prime(), _ => self.prime(),
} }
} }
pub fn prime(&mut self) -> Option<RuleKind<'a>> { pub fn prime(&mut self) -> Result<RuleKind<'a>> {
Some(match self.space().peek()? { Ok(match self.space().peek()? {
'(' => return self.group(), '(' => return self.group(),
'"' => RuleKind::Str(self.str()?), '"' => RuleKind::Str(self.str()?),
'\'' => RuleKind::Chr(self.chr()?), '\'' => RuleKind::Chr(self.chr()?),
_ => RuleKind::Ident(self.ident()?), _ => RuleKind::Ident(self.ident()?),
}) })
} }
pub fn group(&mut self) -> Option<RuleKind<'a>> { pub fn group(&mut self) -> Result<RuleKind<'a>> {
self.take_one(|c| '(' == c)?; self.take_one(|c| '(' == c)?;
let out = self.either()?; let out = self.either()?;
if self.take_one(|c| ')' == c).is_none() { self.take_one(|c| ')' == c)
panic!("Groups should have terminating ')': {}", self.tail) .map(|_| RuleKind::Group(out.into()))
} }
Some(RuleKind::Group(out.into())) pub fn ident(&mut self) -> Result<&'a str> {
}
pub fn ident(&mut self) -> Option<&'a str> {
self.start().take_one(is_xid_start)?; self.start().take_one(is_xid_start)?;
self.take_many(is_xid_continue); self.take_many(is_xid_continue);
Some(self.fragment()) Ok(self.fragment())
} }
pub fn chr(&mut self) -> Option<&'a str> { pub fn chr(&mut self) -> Result<&'a str> {
self.space().take_one(|c| '\'' == c)?; self.space().take_one(|c| '\'' == c)?;
self.start().take_many(|c| '\'' != c); self.start().take_many(|c| '\'' != c);
let out = self.fragment(); let out = self.fragment();
if self.take_one(|c| '\'' == c).is_none() { self.take_one(|c| '\'' == c).map(|_| out)
panic!("chr should have terminating '\'': {}", self.tail)
} }
Some(out) pub fn str(&mut self) -> Result<&'a str> {
}
pub fn str(&mut self) -> Option<&'a str> {
self.space().take_one(|c| '\"' == c)?; self.space().take_one(|c| '\"' == c)?;
self.start().take_many(|c| '\"' != c); self.start().take_many(|c| '\"' != c);
let out = self.fragment(); let out = self.fragment();
if self.take_one(|c| '\"' == c).is_none() { self.take_one(|c| '"' == c).map(|_| out)
panic!("str should have terminating '\"': {}", self.tail)
}
Some(out)
} }
pub fn comment(&mut self) -> Option<&'a str> { pub fn comment(&mut self) -> Result<&'a str> {
let start = self.tail; let start = self.tail;
while self.space().take_one(|c| '(' == c).is_some() { while self.space().take_one(|c| '(' == c).is_ok() {
self.take_one(|c| '*' == c)?; self.take_one(|c| '*' == c)?;
while let Some(c) = self.peek() { loop {
match c { match self.peek()? {
'*' => { '*' => {
self.take_one(|c| '*' == c)?; self.take_one(|c| '*' == c)?;
if self.take_one(|c| ')' == c).is_some() { if self.take_one(|c| ')' == c).is_ok() {
break; break;
} }
} }
'(' => { '(' => {
self.comment(); let _ = self.comment();
} }
_ => { _ => {
self.take(); self.take();
@ -247,6 +246,69 @@ impl<'a> Parser<'a> {
} }
} }
let out = &self.text[start..self.tail]; let out = &self.text[start..self.tail];
(out.len() > 1).then_some(out) if out.len() < 2 {
Ok(out)
} else {
Err(self.error(ErrorKind::NoComment))
}
}
}
use error::{ErrorKind, Result};
pub mod error {
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug)]
pub struct Error {
place: (usize, usize),
kind: ErrorKind,
}
impl Error {
pub fn new(start: usize, end: usize, kind: ErrorKind) -> Self {
Self {
place: (start, end),
kind,
}
}
pub fn is_end(&self) -> bool {
matches!(self.kind, ErrorKind::EndOfInput)
}
}
impl std::error::Error for Error {}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self {
place: (start, end),
kind,
} = self;
write!(f, "[{start}..{end}]: {kind} at {end}")
}
}
#[derive(Debug)]
pub enum ErrorKind {
Unterminated(Terminable),
Unexpected(char),
EndOfInput,
NoComment,
}
impl std::fmt::Display for ErrorKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ErrorKind::Unterminated(t) => write!(f, "unterminated {t:?}"),
ErrorKind::Unexpected(c) => write!(f, "unexpected character {c}"),
ErrorKind::EndOfInput => write!(f, "end of input"),
ErrorKind::NoComment => write!(f, "no comment"),
}
}
}
#[derive(Debug)]
pub enum Terminable {
Comment,
Group,
Str,
Chr,
} }
} }

View File

@ -6,17 +6,27 @@ fn main() -> Result<(), Box<dyn Error>> {
for file in std::env::args().skip(1) { for file in std::env::args().skip(1) {
let file = std::fs::read_to_string(file)?; let file = std::fs::read_to_string(file)?;
let mut p = Parser::new(&file); let mut p = Parser::new(&file);
while let Some(rule) = p.rule() { parse(&mut p);
println!("{rule}");
}
} }
for line in std::io::stdin().lines() { for line in std::io::stdin().lines() {
let line = line?; let line = line?;
let mut p = Parser::new(&line); let mut p = Parser::new(&line);
while let Some(rule) = p.rule() { parse(&mut p);
println!("{} = {{ {} }}", rule.name, rule.body);
}
} }
Ok(()) Ok(())
} }
fn parse(p: &mut Parser) {
loop {
match p.rule() {
Ok(rule) => {
println!("{rule}");
continue;
}
Err(e) if e.is_end() => {}
Err(e) => eprintln!("Error {e}"),
}
break;
}
}