Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
324 changes: 150 additions & 174 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "technique"
version = "0.3.6"
version = "0.4.0"
edition = "2021"
description = "A domain specific language for procedures."
authors = [ "Andrew Cowie" ]
Expand Down
32 changes: 32 additions & 0 deletions examples/minimal/ExampleOfEverything.tq
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
% technique v1
! MIT; © 2024 ACME, Inc
& checklist

making_coffee : Ingredients -> Coffee

# Heading

Ask these questions: are you really sure you want a coffee?

Assuming you do, then:

1. { repeat <making_coffee>(e) ~ cups }
a. First task
b. Second another task
'Yes' | 'No'
2. Do things { exec(
```bash
./stuff
```
) }

another_example(e) : Input -> Output
{
[
"answer" = 42
"Question Time" = "What is the question?"
"timestamp" = now()
"message" = e
"interpolation" = "The value is { e }"
]
}
20 changes: 10 additions & 10 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,12 @@ fn main() {
.action(ArgAction::Version))
.subcommand(
Command::new("check")
.about("Syntax- and type-check the given procedure")
.about("Validate the syntax, structure, and types in the given Technique document.")
.arg(
Arg::new("watch")
.long("watch")
.action(clap::ArgAction::SetTrue)
.help("Watch the given procedure file and recompile if changes are detected."),
.help("Watch the given file containing a Technique document and recompile if changes are detected."),
)
.arg(
Arg::new("output")
Expand All @@ -75,12 +75,12 @@ fn main() {
.arg(
Arg::new("filename")
.required(true)
.help("The file containing the code for the procedure you want to type-check."),
.help("The file containing the code for the Technique you want to type-check."),
),
)
.subcommand(
Command::new("format")
.about("Code format the given procedure")
.about("Format the code in the given Technique document.")
.arg(
Arg::new("raw-control-chars")
.short('R')
Expand All @@ -100,29 +100,29 @@ fn main() {
.arg(
Arg::new("filename")
.required(true)
.help("The file containing the code for the procedure you want to format."),
.help("The file containing the code for the Technique you want to format."),
),
)
.subcommand(
Command::new("render")
.about("Render the Technique procedure into a printable PDF")
.long_about("Render the Technique procedure into a printable \
.about("Render the Technique document into a printable PDF.")
.long_about("Render the Technique document into a printable \
PDF. By default this will highlight the source of the \
input file for the purposes of reviewing the raw \
procedure.")
procedure in code form.")
.arg(
Arg::new("output")
.short('o')
.long("output")
.value_parser(["typst", "none"])
.default_value("none")
.action(ArgAction::Set)
.help("Output format: pdf (default) or typst markup.")
.help("Which kind of diagnostic output to print when rendering.")
)
.arg(
Arg::new("filename")
.required(true)
.help("The file containing the code for the procedure you want to print."),
.help("The file containing the code for the Technique you want to print."),
),
)
.get_matches();
Expand Down
1 change: 1 addition & 0 deletions tests/broken/BadDeclaration.tq
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
making_coffee : Ingredients Coffee
10 changes: 10 additions & 0 deletions tests/broken/IllegalUnitSymbol.tq
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
% technique v1

result :
{
[
"speed" = 3.0 × 10⁸ m_s
"weight" = 84.0 +/- 3.6 kg
"height" = 1.93 * 10^2 cm
]
}
1 change: 1 addition & 0 deletions tests/broken/MagicLine.tq
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
% technique v0
12 changes: 12 additions & 0 deletions tests/broken/UnclosedInterpolation.tq
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
% technique v1

another_example(e) : Input -> Output
{
[
"answer" = 42
"Question Time" = "What is the question?"
"timestamp" = now()
"message" = e
"interpolation" = "The value is { e "
]
}
10 changes: 5 additions & 5 deletions tests/formatting/golden.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,13 @@ mod examples {

#[test]
fn ensure_identical_output() {
// Read all .t files from examples/prototype/
let dir = Path::new("examples/golden");
// Read all .tq files from examples/prototype/
let dir = Path::new("tests/golden/");

// Ensure the directory exists
assert!(dir.exists(), "examples directory missing");

// Get all .t files in the directory
// Get all .tq files in the directory
let entries = fs::read_dir(dir).expect("Failed to read examples directory");

let mut files = Vec::new();
Expand All @@ -76,14 +76,14 @@ mod examples {
if path
.extension()
.and_then(|s| s.to_str())
== Some("t")
== Some("tq")
{
files.push(path);
}
}

// Ensure we found some test files
assert!(!files.is_empty(), "No .t files found in examples directory");
assert!(!files.is_empty(), "No .tq files found in examples directory");

let mut failures = Vec::new();

Expand Down
File renamed without changes.
32 changes: 32 additions & 0 deletions tests/golden/ExampleOfEverything.tq
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
% technique v1
! MIT; © 2024 ACME, Inc
& checklist

making_coffee : Ingredients -> Coffee

# Heading

Ask these questions: are you really sure you want a coffee?

Assuming you do, then:

1. { repeat <making_coffee>(e) ~ cups }
a. First task
b. Second another task
'Yes' | 'No'
2. Do things { exec(
```bash
./stuff
```
) }

another_example(e) : Input -> Output
{
[
"answer" = 42
"Question Time" = "What is the question?"
"timestamp" = now()
"message" = e
"interpolation" = "The value is { e }"
]
}
File renamed without changes.
1 change: 1 addition & 0 deletions tests/parsing/mod.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
mod errors;
mod parser;
mod samples;
101 changes: 101 additions & 0 deletions tests/parsing/samples.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
#[cfg(test)]
mod samples {
use std::fs;
use std::path::Path;

use technique::parsing;

#[test]
fn ensure_samples_parse() {
let dir = Path::new("tests/samples/");

assert!(dir.exists(), "samples directory missing");

let entries = fs::read_dir(dir).expect("Failed to read samples directory");

let mut files = Vec::new();
for entry in entries {
let entry = entry.expect("Failed to read directory entry");
let path = entry.path();

if path
.extension()
.and_then(|s| s.to_str())
== Some("tq")
{
files.push(path);
}
}

assert!(!files.is_empty(), "No .tq files found in samples directory");

let mut failures = Vec::new();

for file in &files {
let content = parsing::load(&file)
.unwrap_or_else(|e| panic!("Failed to load file {:?}: {:?}", file, e));

match parsing::parse(&file, &content) {
Ok(_) => {}
Err(e) => {
println!("File {:?} failed to parse: {:?}", file, e);
failures.push(file.clone());
}
}
}

if !failures.is_empty() {
panic!(
"Sample files should parse successfully, but {} files failed",
failures.len()
);
}
}

#[test]
fn ensure_broken_fail() {
let dir = Path::new("tests/broken/");

assert!(dir.exists(), "broken directory missing");

let entries = fs::read_dir(dir).expect("Failed to read broken directory");

let mut files = Vec::new();
for entry in entries {
let entry = entry.expect("Failed to read directory entry");
let path = entry.path();

if path
.extension()
.and_then(|s| s.to_str())
== Some("tq")
{
files.push(path);
}
}

assert!(!files.is_empty(), "No .tq files found in broken directory");

let mut unexpected_successes = Vec::new();

for file in &files {
let content = parsing::load(&file)
.unwrap_or_else(|e| panic!("Failed to load file {:?}: {:?}", file, e));

match parsing::parse(&file, &content) {
Ok(_) => {
println!("File {:?} unexpectedly parsed successfully", file);
unexpected_successes.push(file.clone());
}
Err(_) => {}
}
}

if !unexpected_successes.is_empty() {
panic!(
"Broken files should not to parse successfully, but {} files passed",
unexpected_successes.len()
);
}
}
}
10 changes: 10 additions & 0 deletions tests/samples/TabletOfQuantity.tq
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
% technique v1

result :
{
[
"speed" = 3.0 × 10⁸ m/s
"weight" = 84.0 +/- 3.6 kg
"height" = 1.93 * 10^2 cm
]
}