Remove parsing logic

This commit is contained in:
Joscha 2021-11-17 21:55:40 +01:00
parent d8ec2e6c3a
commit f6c249393b
7 changed files with 1 additions and 286 deletions

29
Cargo.lock generated
View file

@ -68,16 +68,6 @@ dependencies = [
"vec_map",
]
[[package]]
name = "codespan-reporting"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
dependencies = [
"termcolor",
"unicode-width",
]
[[package]]
name = "heck"
version = "0.3.3"
@ -210,15 +200,6 @@ dependencies = [
"unicode-xid",
]
[[package]]
name = "termcolor"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
dependencies = [
"winapi-util",
]
[[package]]
name = "textwrap"
version = "0.11.0"
@ -265,7 +246,6 @@ version = "0.1.0"
dependencies = [
"anyhow",
"chrono",
"codespan-reporting",
"structopt",
"thiserror",
]
@ -322,15 +302,6 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
"winapi",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"

View file

@ -6,6 +6,5 @@ edition = "2018"
[dependencies]
anyhow = "1.0.45"
chrono = "0.4.19"
codespan-reporting = "0.11.1"
structopt = "0.3.25"
thiserror = "1.0.30"

View file

@ -1,14 +1,8 @@
use std::path::PathBuf;
use std::process;
use structopt::StructOpt;
use crate::parse::Parser;
use crate::source::SourceFiles;
mod commands;
mod parse;
mod source;
#[derive(Debug, StructOpt)]
pub struct Opt {
@ -18,26 +12,5 @@ pub struct Opt {
fn main() {
let opt = Opt::from_args();
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(parse::parse_commands) {
Ok(result) => result,
Err(es) => {
files.emit_all(&es);
process::exit(1);
}
};
println!("{:#?}", commands);
println!("{:#?}", opt);
}

View file

@ -1,5 +0,0 @@
mod commands;
mod parser;
pub use commands::parse as parse_commands;
pub use parser::{ParseError, ParseResult, Parser};

View file

@ -1,34 +0,0 @@
use crate::commands::Command;
use super::{ParseResult, Parser};
pub fn parse(p: &mut Parser<'_>) -> ParseResult<Vec<Command>> {
let mut commands = vec![];
skip_empty_lines(p);
while !p.at_eof() {
// Commands consume all their trailing lines, including empty ones
commands.push(parse_command(p)?);
}
Ok(commands)
}
fn skip_empty_lines(p: &mut Parser<'_>) {
while p.peek_line().chars().all(|c| c.is_whitespace()) {
p.take_line();
}
}
fn parse_command(p: &mut Parser<'_>) -> ParseResult<Command> {
let rest = p.peek_rest();
if rest.starts_with("TASK") {
todo!() // TODO Implement parsing TASK command
} else if rest.starts_with("NOTE") {
todo!() // TODO Implement parsing NOTE command
} else if rest.starts_with("BIRTHDAY") {
todo!() // TODO Implement parsing BIRTHDAY command
} else {
p.critical(p.at(), "Expected command")
}
}

View file

@ -1,107 +0,0 @@
use std::cmp::min;
use codespan_reporting::diagnostic::{Diagnostic, Label};
use crate::source::{SourceFile, SourceSpan};
// TODO Add warnings for things like trailing whitespace
#[derive(Debug)]
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())])
}
}
pub 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![],
}
}
pub fn peek(&self, amount: usize) -> &'a str {
// self.offset is always a valid start since take() ensures it is in the
// range 0..self.content.len()
let end = min(self.content.len(), self.offset + amount);
&self.content[self.offset..end]
}
pub fn peek_rest(&self) -> &'a str {
// self.offset is always a valid start since take() ensures it is in the
// range 0..self.content.len()
&self.content[self.offset..]
}
pub fn peek_line(&self) -> &'a str {
if let Some(i) = self.peek_rest().find('\n') {
self.peek(i)
} else {
self.peek_rest()
}
}
pub fn at(&self) -> usize {
self.offset
}
pub fn at_eof(&self) -> bool {
self.offset >= self.content.len()
}
pub fn take(&mut self, amount: usize) {
// Ensure the offset always stays in the range 0..self.content.len()
self.offset = min(self.content.len(), self.offset + amount);
}
pub fn take_line(&mut self) {
self.take(self.peek_line().len() + 1);
}
fn error(&self, at: usize, error: impl ToString) -> ParseError {
ParseError(self.file.span(at..at), error.to_string())
}
pub fn uncritical(&mut self, at: usize, error: impl ToString) {
self.errors.push(self.error(at, error));
}
pub fn critical<T>(&self, at: usize, error: impl ToString) -> ParseResult<T> {
Err(self.error(at, error))
}
pub fn parse<T>(
&mut self,
f: impl FnOnce(&mut Self) -> ParseResult<T>,
) -> Result<T, Vec<ParseError>> {
match f(self) {
Ok(result) => {
if self.errors.is_empty() {
return Ok(result);
}
}
Err(error) => {
self.errors.push(error);
}
};
Err(self.errors.split_off(0))
}
}

View file

@ -1,82 +0,0 @@
use std::collections::HashMap;
use std::ops::Range;
use std::path::{Path, PathBuf};
use std::{fs, io};
use codespan_reporting::diagnostic::Diagnostic;
use codespan_reporting::files::SimpleFiles;
use codespan_reporting::term;
use codespan_reporting::term::termcolor::{ColorChoice, StandardStream};
#[derive(Debug)]
pub struct SourceFiles {
files: SimpleFiles<String, String>,
files_by_path: HashMap<PathBuf, usize>,
}
impl SourceFiles {
pub fn new() -> Self {
Self {
files: SimpleFiles::new(),
files_by_path: HashMap::new(),
}
}
pub fn load(&mut self, path: &Path) -> io::Result<(SourceFile, &str)> {
let canonical_path = path.canonicalize()?;
let file_id = if let Some(&file_id) = self.files_by_path.get(&canonical_path) {
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 content = self.files.get(file_id).unwrap().source() as &str;
Ok((SourceFile(file_id), content))
}
pub fn emit_all<'a, T>(&self, diagnostics: &'a [T])
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)
.expect("failed to print errors");
}
}
}
#[derive(Clone, Copy, Debug)]
pub struct SourceFile(usize);
impl SourceFile {
pub fn span(&self, range: Range<usize>) -> SourceSpan {
SourceSpan {
file_id: self.0,
start: range.start,
end: range.end,
}
}
}
#[derive(Clone, Copy, Debug)]
pub struct SourceSpan {
file_id: usize,
start: usize,
end: usize,
}
impl SourceSpan {
pub fn file_id(&self) -> usize {
self.file_id
}
pub fn range(&self) -> Range<usize> {
self.start..self.end
}
}