From 3efb1a26e0e6c4ad9c3e7f9cf37cd29d20f47225 Mon Sep 17 00:00:00 2001 From: Joscha Date: Sun, 7 Nov 2021 21:59:20 +0000 Subject: [PATCH] Start implementing parser --- README.md | 12 ++++++ src/main.rs | 1 + src/parser.rs | 107 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 120 insertions(+) create mode 100644 src/parser.rs diff --git a/README.md b/README.md index 279fd14..f8d645c 100644 --- a/README.md +++ b/README.md @@ -112,4 +112,16 @@ BDATE 1987-05-14 BIRTHDAY Martha BDATE ?-09-21 + +NOTE Physics lecture +DATE wed 14:00 -- 15:30 +DATE 2021-05-07 14:00 -- 15:30 +FROM 2021-04-14 +UNTIL 2021-07-28 +EXCEPT 2021-05-06 + This is a description of the event. It might mention further information + that doesn't fit into the title. + + It may even contain multiple paragraphs, separated by an arbitrary amount + of empty lines, as long as every non-empty line is indented. ``` diff --git a/src/main.rs b/src/main.rs index 7590a92..48a0c19 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,6 +3,7 @@ use std::path::PathBuf; use structopt::StructOpt; mod commands; +mod parser; mod source; #[derive(Debug, StructOpt)] diff --git a/src/parser.rs b/src/parser.rs new file mode 100644 index 0000000..f3da2ce --- /dev/null +++ b/src/parser.rs @@ -0,0 +1,107 @@ +use std::cmp::min; +use std::process::Command; + +use crate::source::{SourceFile, SourceSpan}; + +#[derive(Debug)] +pub struct ParseError(SourceSpan, String); + +type ParseResult = Result; + +#[derive(Debug)] +pub struct Parser<'a> { + file: SourceFile, + content: &'a str, + offset: usize, + errors: Vec, +} + +impl<'a> Parser<'a> { + pub fn new(file: SourceFile, content: &'a str) -> Self { + Self { + file, + content, + offset: 0, + errors: vec![], + } + } + + fn peek(&self, amount: usize) -> &'a str { + let end = min(self.content.len(), self.offset + amount); + &self.content[self.offset..end] + } + + fn peek_rest(&self) -> &'a str { + &self.content[self.offset..] + } + + fn at_eof(&self) -> bool { + self.offset >= self.content.len() + } + + fn take(&mut self, amount: usize) { + self.offset += amount; + } + + 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) { + self.errors.push(self.error(at, error)); + } + + 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) => { + if self.errors.is_empty() { + return Ok(commands); + } + } + Err(error) => { + self.errors.push(error); + } + }; + + Err(self.errors.split_off(0)) + } + + fn parse_commands(&mut self) -> ParseResult> { + let mut commands = vec![]; + + self.skip_empty_lines(); + while !self.at_eof() { + commands.push(self.parse_command()?); + } + + if !self.at_eof() { + self.uncritical(self.offset, "expected EOF"); + } + + 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 parse_command(&mut self) -> ParseResult { + todo!() + } +}