Hook up parser

This commit is contained in:
Joscha 2021-11-07 22:39:50 +00:00
parent 3efb1a26e0
commit 09f83930b4
4 changed files with 120 additions and 21 deletions

44
example.today Normal file
View file

@ -0,0 +1,44 @@
TASK foo
NOTE Spielerunde
DATE sun 22:00 -- 24:00
DATE sun 22:00 -- 00:00
DATE sun 22:00 -- +2h
DATE (wd = sun) 22:00 -- 24:00
DATE 2021-11-07 22:00 -- 24:00; +w
DATE 2021-11-07 22:00 -- +2h; +w
NOTE daily
DATE *
DATE (true)
DATE 2021-11-07; +d
NOTE on weekends
DATE (wd = sat | wd = sun)
NOTE weekends
DATE sat -- sun
DATE 2021-11-06 -- 2021-11-07; +w
DATE 2021-11-06 -- +d; +w
DATE (wd = sat) -- +d
NOTE last of each month
DATE (m = 1) -d
BIRTHDAY Max
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.

View file

@ -1,7 +1,11 @@
use std::path::PathBuf; use std::path::PathBuf;
use std::process;
use structopt::StructOpt; use structopt::StructOpt;
use crate::parser::Parser;
use crate::source::SourceFiles;
mod commands; mod commands;
mod parser; mod parser;
mod source; mod source;
@ -14,5 +18,26 @@ pub struct Opt {
fn main() { fn main() {
let opt = Opt::from_args(); let opt = Opt::from_args();
println!("{:#?}", opt);
let mut files = SourceFiles::new();
let (file, content) = match files.load(&opt.file) {
Ok(result) => result,
Err(e) => {
eprintln!("Failed to load file: {}", e);
process::exit(1);
}
};
let mut parser = Parser::new(file, content);
let commands = match parser.parse() {
Ok(result) => result,
Err(es) => {
files.emit_all(&es);
process::exit(1);
}
};
println!("{:#?}", commands);
} }

View file

@ -1,11 +1,23 @@
use std::cmp::min; use std::cmp::min;
use std::process::Command; use std::process::Command;
use codespan_reporting::diagnostic::{Diagnostic, Label};
use crate::source::{SourceFile, SourceSpan}; use crate::source::{SourceFile, SourceSpan};
// TODO Add warnings for things like trailing whitespace
#[derive(Debug)] #[derive(Debug)]
pub struct ParseError(SourceSpan, String); pub struct ParseError(SourceSpan, String);
impl From<&ParseError> for Diagnostic<usize> {
fn from(e: &ParseError) -> Self {
Self::error()
.with_message(&e.1)
.with_labels(vec![Label::primary(e.0.file_id(), e.0.range())])
}
}
type ParseResult<T> = Result<T, ParseError>; type ParseResult<T> = Result<T, ParseError>;
#[derive(Debug)] #[derive(Debug)]
@ -102,6 +114,7 @@ impl<'a> Parser<'a> {
} }
fn parse_command(&mut self) -> ParseResult<Command> { fn parse_command(&mut self) -> ParseResult<Command> {
self.critical(self.offset, "idk, ded")?;
todo!() todo!()
} }
} }

View file

@ -3,16 +3,12 @@ use std::ops::Range;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::{fs, io}; use std::{fs, io};
use codespan_reporting::diagnostic::Diagnostic;
use codespan_reporting::files::SimpleFiles; use codespan_reporting::files::SimpleFiles;
use codespan_reporting::term;
use codespan_reporting::term::termcolor::{ColorChoice, StandardStream};
#[derive(Debug, thiserror::Error)] #[derive(Debug)]
pub enum LoadFileError {
#[error("file already loaded")]
FileAlreadyLoaded,
#[error("error loading file: {0}")]
IoError(#[from] io::Error),
}
pub struct SourceFiles { pub struct SourceFiles {
files: SimpleFiles<String, String>, files: SimpleFiles<String, String>,
files_by_path: HashMap<PathBuf, usize>, files_by_path: HashMap<PathBuf, usize>,
@ -26,20 +22,31 @@ impl SourceFiles {
} }
} }
pub fn load(&mut self, path: &Path) -> Result<SourceFile, LoadFileError> { pub fn load(&mut self, path: &Path) -> io::Result<(SourceFile, &str)> {
let canonical_path = path.canonicalize()?; let canonical_path = path.canonicalize()?;
if self.files_by_path.contains_key(&canonical_path) { let file_id = if let Some(&file_id) = self.files_by_path.get(&canonical_path) {
return Err(LoadFileError::FileAlreadyLoaded); file_id
} } else {
let name = path.as_os_str().to_string_lossy().into_owned();
let content = fs::read_to_string(path)?;
self.files.add(name, content)
};
let name = path.as_os_str().to_string_lossy().into_owned(); let content = self.files.get(file_id).unwrap().source() as &str;
let content = fs::read_to_string(path)?; Ok((SourceFile(file_id), content))
let file_id = self.files.add(name, content);
Ok(SourceFile(file_id))
} }
pub fn content_of(&self, file: SourceFile) -> Option<&str> { pub fn emit_all<'a, T>(&self, diagnostics: &'a [T])
self.files.get(file.0).ok().map(|sf| sf.source() as &str) where
&'a T: Into<Diagnostic<usize>>,
{
let stderr = StandardStream::stderr(ColorChoice::Auto);
let config = term::Config::default();
for diagnostic in diagnostics {
let diagnostic: Diagnostic<usize> = diagnostic.into();
term::emit(&mut stderr.lock(), &config, &self.files, &diagnostic);
}
} }
} }
@ -49,7 +56,7 @@ pub struct SourceFile(usize);
impl SourceFile { impl SourceFile {
pub fn span(&self, range: Range<usize>) -> SourceSpan { pub fn span(&self, range: Range<usize>) -> SourceSpan {
SourceSpan { SourceSpan {
file: self.0, file_id: self.0,
start: range.start, start: range.start,
end: range.end, end: range.end,
} }
@ -58,7 +65,17 @@ impl SourceFile {
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub struct SourceSpan { pub struct SourceSpan {
file: usize, file_id: usize,
start: usize, start: usize,
end: usize, end: usize,
} }
impl SourceSpan {
pub fn file_id(&self) -> usize {
self.file_id
}
pub fn range(&self) -> Range<usize> {
self.start..self.end
}
}