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
|
BIRTHDAY Martha
|
||||||
BDATE ?-09-21
|
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;
|
use structopt::StructOpt;
|
||||||
|
|
||||||
mod commands;
|
mod commands;
|
||||||
|
mod parser;
|
||||||
mod source;
|
mod source;
|
||||||
|
|
||||||
#[derive(Debug, StructOpt)]
|
#[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