Start implementing parser
This commit is contained in:
parent
5e2beaedde
commit
3efb1a26e0
3 changed files with 120 additions and 0 deletions
12
README.md
12
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.
|
||||
```
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ use std::path::PathBuf;
|
|||
use structopt::StructOpt;
|
||||
|
||||
mod commands;
|
||||
mod parser;
|
||||
mod source;
|
||||
|
||||
#[derive(Debug, StructOpt)]
|
||||
|
|
|
|||
107
src/parser.rs
Normal file
107
src/parser.rs
Normal file
|
|
@ -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<T> = Result<T, ParseError>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Parser<'a> {
|
||||
file: SourceFile,
|
||||
content: &'a str,
|
||||
offset: usize,
|
||||
errors: Vec<ParseError>,
|
||||
}
|
||||
|
||||
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<Command>, Vec<ParseError>> {
|
||||
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<Vec<Command>> {
|
||||
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<Command> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue