diff --git a/src/main.rs b/src/main.rs index 9143364..03308b1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -31,7 +31,7 @@ fn main() { let mut parser = Parser::new(file, content); - let commands = match parser.parse() { + let commands = match parser.parse(parser::commands::parse) { Ok(result) => result, Err(es) => { files.emit_all(&es); diff --git a/src/parser.rs b/src/parser.rs index 0fc1366..18aefc9 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,10 +1,12 @@ use std::cmp::min; -use std::process::Command; use codespan_reporting::diagnostic::{Diagnostic, Label}; use crate::source::{SourceFile, SourceSpan}; +pub mod commands; +mod task; + // TODO Add warnings for things like trailing whitespace #[derive(Debug)] @@ -38,40 +40,64 @@ impl<'a> Parser<'a> { } } - fn peek(&self, amount: usize) -> &'a str { + pub fn peek(&self, amount: usize) -> &'a str { + // self.offset is always a valid start since take() ensures it is in the + // range 0..self.content.len() let end = min(self.content.len(), self.offset + amount); &self.content[self.offset..end] } - fn peek_rest(&self) -> &'a str { + pub fn peek_rest(&self) -> &'a str { + // self.offset is always a valid start since take() ensures it is in the + // range 0..self.content.len() &self.content[self.offset..] } - fn at_eof(&self) -> bool { + pub fn peek_line(&self) -> &'a str { + if let Some(i) = self.peek_rest().find('\n') { + self.peek(i) + } else { + self.peek_rest() + } + } + + pub fn at(&self) -> usize { + self.offset + } + + pub fn at_eof(&self) -> bool { self.offset >= self.content.len() } - fn take(&mut self, amount: usize) { - self.offset += amount; + pub fn take(&mut self, amount: usize) { + // Ensure the offset always stays in the range 0..self.content.len() + self.offset = min(self.content.len(), self.offset + amount); + } + + pub fn take_line(&mut self) { + self.take(self.peek_line().len() + 1); } fn error(&self, at: usize, error: impl ToString) -> ParseError { ParseError(self.file.span(at..at), error.to_string()) } - fn uncritical(&mut self, at: usize, error: impl ToString) { + pub fn uncritical(&mut self, at: usize, error: impl ToString) { self.errors.push(self.error(at, error)); } - fn critical(&self, at: usize, error: impl ToString) -> ParseResult<()> { + pub fn critical(&self, at: usize, error: impl ToString) -> ParseResult { Err(self.error(at, error)) } - pub fn parse(&mut self) -> Result, Vec> { - match self.parse_commands() { - Ok(commands) => { + pub fn parse( + &mut self, + f: impl FnOnce(&mut Self) -> ParseResult, + ) -> Result> { + match f(self) { + Ok(result) => { if self.errors.is_empty() { - return Ok(commands); + return Ok(result); } } Err(error) => { @@ -82,39 +108,38 @@ impl<'a> Parser<'a> { Err(self.errors.split_off(0)) } - fn parse_commands(&mut self) -> ParseResult> { - let mut commands = vec![]; + // fn parse_commands(&mut self) -> ParseResult> { + // let mut commands = vec![]; - self.skip_empty_lines(); - while !self.at_eof() { - commands.push(self.parse_command()?); - } + // self.skip_empty_lines(); + // while !self.at_eof() { + // commands.push(self.parse_command()?); + // } - if !self.at_eof() { - self.uncritical(self.offset, "expected EOF"); - } + // if !self.at_eof() { + // self.uncritical(self.offset, "Expected EOF"); + // } - Ok(commands) - } + // Ok(commands) + // } - fn skip_empty_lines(&mut self) { - loop { - if let Some(i) = self.peek_rest().find('\n') { - if self.peek(i).chars().all(|c| c.is_whitespace()) { - self.take(i + 1); // Include the newline - } else { - break; - } - } else if self.peek_rest().chars().all(|c| c.is_whitespace()) { - self.take(self.peek_rest().len()); - } else { - break; - } - } - } + // fn skip_empty_lines(&mut self) { + // while self.peek_line().chars().all(|c| c.is_whitespace()) { + // self.take_line(); + // } + // } - fn parse_command(&mut self) -> ParseResult { - self.critical(self.offset, "idk, ded")?; - todo!() - } + // fn parse_command(&mut self) -> ParseResult { + // let rest = self.peek_rest(); + // if rest.starts_with("TASK") { + // let task = self.parse_task()?; + // Ok(Command::Task(task)) + // } else if rest.starts_with("NOTE") { + // todo!() // TODO Implement parsing NOTE command + // } else if rest.starts_with("BIRTHDAY") { + // todo!() // TODO Implement parsing BIRTHDAY command + // } else { + // self.critical(self.offset, "Expected command") + // } + // } } diff --git a/src/parser/commands.rs b/src/parser/commands.rs new file mode 100644 index 0000000..e3766e9 --- /dev/null +++ b/src/parser/commands.rs @@ -0,0 +1,7 @@ +use crate::commands::Command; + +use super::{ParseResult, Parser}; + +pub fn parse(p: &mut Parser<'_>) -> ParseResult> { + todo!() +} diff --git a/src/parser/task.rs b/src/parser/task.rs new file mode 100644 index 0000000..a2f2e23 --- /dev/null +++ b/src/parser/task.rs @@ -0,0 +1,7 @@ +use crate::commands::Task; + +use super::{ParseResult, Parser}; + +fn parse_task(p: &mut Parser<'_>) -> ParseResult { + todo!() +} diff --git a/src/source.rs b/src/source.rs index 4674a9b..407c7d6 100644 --- a/src/source.rs +++ b/src/source.rs @@ -45,7 +45,8 @@ impl SourceFiles { for diagnostic in diagnostics { let diagnostic: Diagnostic = diagnostic.into(); - term::emit(&mut stderr.lock(), &config, &self.files, &diagnostic); + term::emit(&mut stderr.lock(), &config, &self.files, &diagnostic) + .expect("failed to print errors"); } } }