Skip to content

Commit

Permalink
Parenthesized expressions, even better parser errors (#30)
Browse files Browse the repository at this point in the history
* Implement Serialize+Deserialize for AST

* Positive parser tests

* Major parser refactor, more tests, removed TOML support

* Remove vestigial TOML code

* Fix winnow deprecation warning
  • Loading branch information
simonask authored Jan 29, 2025
1 parent 97633a2 commit 06ea1ae
Show file tree
Hide file tree
Showing 75 changed files with 2,678 additions and 3,079 deletions.
66 changes: 49 additions & 17 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ ahash = "0.8.11"
werk-runner.path = "werk-runner"
werk-parser.path = "werk-parser"
werk-fs.path = "werk-fs"
winnow = "0.6.20"
winnow = "^0.6.25"
thiserror = "2.0"
tracing = "0.1.40"
indexmap = { version = "2.6.0", features = ["serde"] }
Expand All @@ -17,6 +17,7 @@ serde = { version = "1.0.215", features = ["derive"] }
smol = "2.0.2"
toml_edit = "0.22.22"
futures = "0.3.31"
annotate-snippets = "0.11.5"

[workspace.lints.clippy]
pedantic = { level = "warn", priority = -1 }
Expand Down
1 change: 0 additions & 1 deletion TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

- [ ] Consider using pattern syntax for glob patterns instead of standard glob
syntax.
- [ ] Forward doc comments from TOML to `--list`.
- [ ] Autoclean: Match files in the output directory against available recipes
and delete them if they are older than `.werk-cache`.

Expand Down
26 changes: 0 additions & 26 deletions examples/expressions.toml

This file was deleted.

2 changes: 1 addition & 1 deletion tests/cases/map.werk
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ let result = input
# map string
let input = "a";
let result = input
| map "hello {}"
| map ("hello {}" | assert-eq "hello a")
| assert-eq "hello a"
72 changes: 15 additions & 57 deletions tests/mock_io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ pub struct TestBuilder<'a> {
pub defines: Vec<(String, String)>,
pub default_filesystem: bool,
pub create_workspace_dir: bool,
pub werkfile: Option<TestSource<'a>>,
pub werkfile: &'a str,
}

pub fn native_path<I: IntoIterator<Item: AsRef<OsStr>>>(
Expand All @@ -59,7 +59,7 @@ impl Default for TestBuilder<'_> {
defines: Vec::new(),
default_filesystem: true,
create_workspace_dir: true,
werkfile: None,
werkfile: "",
}
}
}
Expand All @@ -80,17 +80,13 @@ impl<'a> TestBuilder<'a> {
self
}

pub fn werkfile(&mut self, werkfile: impl Into<TestSource<'a>>) -> &mut Self {
self.werkfile = Some(werkfile.into());
pub fn werkfile(&mut self, werkfile: &'a str) -> &mut Self {
self.werkfile = werkfile;
self
}

pub fn build(&self) -> Result<Test<'a>, werk_parser::Error> {
let ast = match self.werkfile {
Some(TestSource::Werk(source)) => Some(werk_parser::parse_werk(source)?),
Some(TestSource::Toml(toml)) => Some(werk_parser::parse_toml(toml)?),
None => None,
};
let ast = werk_parser::parse_werk(self.werkfile)?;

let mut io = MockIo::default();
io.initialize_default_env();
Expand All @@ -111,73 +107,35 @@ impl<'a> TestBuilder<'a> {
}
}

pub enum TestSource<'a> {
Werk(&'a str),
Toml(&'a toml_edit::ImDocument<&'a str>),
}

impl<'a> From<&'a str> for TestSource<'a> {
fn from(s: &'a str) -> Self {
TestSource::Werk(s)
}
}

impl<'a> From<&'a toml_edit::ImDocument<&'a str>> for TestSource<'a> {
fn from(t: &'a toml_edit::ImDocument<&'a str>) -> Self {
TestSource::Toml(t)
}
}

pub struct Test<'a> {
pub io: Arc<MockIo>,
pub render: Arc<MockRender>,
pub workspace_dir: Absolute<std::path::PathBuf>,
pub output_dir: Absolute<std::path::PathBuf>,
pub ast: Option<werk_parser::Document<'a>>,
pub ast: werk_parser::Document<'a>,
}

impl<'a> Test<'a> {
pub fn new(werk_source: &'a str) -> Result<Self, werk_parser::Error> {
TestBuilder::default().werkfile(werk_source).build()
}

pub fn reload(
&mut self,
werkfile: impl Into<TestSource<'a>>,
) -> Result<(), werk_parser::Error> {
self.ast = match werkfile.into() {
TestSource::Werk(source) => Some(werk_parser::parse_werk(source)?),
TestSource::Toml(toml) => Some(werk_parser::parse_toml(toml)?),
};
pub fn reload(&mut self, werkfile: &'a str) -> Result<(), werk_parser::Error> {
self.ast = werk_parser::parse_werk(werkfile)?;
Ok(())
}

pub fn new_toml(toml: &'a toml_edit::ImDocument<&'a str>) -> Result<Self, werk_parser::Error> {
TestBuilder::default().werkfile(toml).build()
}

pub fn reload_toml(
&mut self,
toml: &'a toml_edit::ImDocument<&'a str>,
) -> Result<(), werk_parser::Error> {
self.reload(toml)
}

pub fn create_workspace(
&self,
defines: &[(&str, &str)],
) -> Result<werk_runner::Workspace<'_>, werk_runner::Error> {
if let Some(ref ast) = self.ast {
werk_runner::Workspace::new(
ast,
&*self.io,
&*self.render,
test_workspace_dir().to_path_buf(),
&test_workspace_settings(defines),
)
} else {
panic!("no werkfile loaded!")
}
werk_runner::Workspace::new(
&self.ast,
&*self.io,
&*self.render,
test_workspace_dir().to_path_buf(),
&test_workspace_settings(defines),
)
}
}

Expand Down
20 changes: 17 additions & 3 deletions tests/test_cases.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::sync::OnceLock;

use tests::mock_io::*;
use werk_parser::ast;
use werk_parser::{ast, LocatedError};
use werk_runner::{Metadata, Runner};

struct PragmaRegexes {
Expand Down Expand Up @@ -30,7 +30,7 @@ async fn evaluate_check(file: &std::path::Path) -> Result<(), anyhow::Error> {
let path = std::path::Path::new(file);
let test = Test::new(&source)
.map_err(|err| anyhow::Error::msg(err.with_location(path, &source).to_string()))?;
let ast = test.ast.as_ref().unwrap();
let ast = &test.ast;

// Interpret pragmas in the trailing comment of the werkfile.
let trailing_whitespace = ast.get_whitespace(ast.root.ws_trailing).trim().lines();
Expand Down Expand Up @@ -68,7 +68,21 @@ async fn evaluate_check(file: &std::path::Path) -> Result<(), anyhow::Error> {
}
}

let workspace = test.create_workspace(&[])?;
let workspace = match test.create_workspace(&[]) {
Ok(workspace) => workspace,
Err(werk_runner::Error::Eval(error)) => {
eprintln!(
"{}",
LocatedError {
error,
file_name: file,
source_code: &source
}
);
panic!("evaluation failed")
}
Err(err) => panic!("unexpected error: {:?}", err),
};

// Invoke the runner if there is a default target.
if let Some(ast::ConfigStmt {
Expand Down
Loading

0 comments on commit 06ea1ae

Please sign in to comment.