Start implementing parser

This commit is contained in:
Joscha 2021-11-07 21:59:20 +00:00
parent 5e2beaedde
commit 3efb1a26e0
3 changed files with 120 additions and 0 deletions

View file

@ -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.
``` ```

View file

@ -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
View 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!()
}
}