Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
047091c
Add --until option to check command
istathar May 5, 2026
90a7164
Output native, normal to "terminal" or silent
istathar May 5, 2026
8e1c228
Apply code formatter
istathar May 5, 2026
d9937a1
Introduce types for Intermediate Representation
istathar May 5, 2026
dc0472f
Stubs for translation phase
istathar May 5, 2026
25bb07a
Scaffolding for translation phase errors
istathar May 6, 2026
454d2c5
Introduce Operation type for Intermediate Representation
istathar May 6, 2026
a797b13
Preliminary collection of procedure names
istathar May 6, 2026
bdee9a5
Merge branch 'main' into 'run'
istathar May 7, 2026
1f24a77
Update TranslationErrors with better detail
istathar May 7, 2026
9e37b36
Rename intermediate to Subroutine
istathar May 7, 2026
7bd4e9d
Translate section scopes
istathar May 7, 2026
7f87173
Translate dependent and parallel steps
istathar May 7, 2026
0e30588
Supress warning temporarily
istathar May 7, 2026
41f2aa5
Lift attributes onto enclosing Steps
istathar May 7, 2026
52f5a79
Attach response blocks to enclosing Steps
istathar May 7, 2026
fc45d86
Refactor into stateful Translator
istathar May 7, 2026
8aa82e9
Translate expressions, invocations, and bindings
istathar May 7, 2026
7f942c5
Translate code blocks
istathar May 7, 2026
b5d59f2
Refine translation type names
istathar May 7, 2026
903d286
Resolve procedure invocations or error
istathar May 7, 2026
62ea999
Translate descriptives
istathar May 7, 2026
91696b3
Translate responses
istathar May 7, 2026
79f4b43
Common function for finding test samples
istathar May 7, 2026
6543c50
Test translation with known-good samples
istathar May 7, 2026
4128956
Organize known good and known bad samples by phase
istathar May 7, 2026
f1ca93f
Add known bad translation samples
istathar May 7, 2026
c2852a4
Print line and column position for translation phase errors
istathar May 7, 2026
c467463
Refine errors around interleaved descriptions
istathar May 8, 2026
d8483b4
Fix multiple Expressions in CodeBlock
istathar May 8, 2026
2f7a407
Move Intermediate Representation types to program::
istathar May 8, 2026
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
2 changes: 1 addition & 1 deletion Cargo.lock

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

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.5.5"
version = "0.5.6"
edition = "2021"
description = "A domain specific language for procedures."
authors = [ "Andrew Cowie" ]
Expand Down
2 changes: 1 addition & 1 deletion src/language/quantity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
use crate::regex::*;
use std::fmt::{self, Display};

#[derive(Debug, PartialEq, Eq)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Quantity<'i> {
pub mantissa: Decimal,
pub uncertainty: Option<Decimal>,
Expand Down
4 changes: 2 additions & 2 deletions src/language/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ impl<'i> Procedure<'i> {
}
}

#[derive(Eq, Debug)]
#[derive(Clone, Copy, Eq, Debug)]
pub struct Identifier<'i> {
pub value: &'i str,
pub span: Span,
Expand Down Expand Up @@ -445,7 +445,7 @@ impl PartialEq for Expression<'_> {
}
}

#[derive(Debug, PartialEq, Eq)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Numeric<'i> {
Integral(i64),
Scientific(Quantity<'i>),
Expand Down
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,7 @@ pub mod formatting;
pub mod highlighting;
pub mod language;
pub mod parsing;
pub mod program;
pub(crate) mod regex;
pub mod templating;
pub mod translation;
81 changes: 76 additions & 5 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,26 @@ use technique::formatting::{self, Identity};
use technique::highlighting::{self, Terminal};
use technique::parsing;
use technique::templating::{self, Checklist, NasaEsaIss, Procedure, Recipe, Source};
use technique::translation;

mod editor;
mod output;
mod problem;

#[derive(Eq, Debug, PartialEq)]
#[allow(dead_code)]
enum Output {
Terminal,
Native,
Silent,
}

#[derive(Eq, Debug, PartialEq)]
enum Phase {
Parsing,
Translation,
}

fn main() {
const VERSION: &str = concat!("v", env!("CARGO_PKG_VERSION"));

Expand Down Expand Up @@ -72,6 +81,18 @@ fn main() {
.action(ArgAction::Set)
.help("Which kind of diagnostic output to print when checking.")
)
.arg(
Arg::new("until")
.long("until")
.value_name("phase")
.value_parser(["parsing", "translation"])
.default_value("parsing")
.action(ArgAction::Set)
.help("Stop compilation after the given phase is complete so that the result can be inspected. \
Use this in conjunction with the --output option. The phases are: \
parsing, where the input is parsed from the surface language to an internal abstract syntax tree; then \
translation, which resolves names, checks references, and ensures the input is valid Technique.")
)
.arg(
Arg::new("filename")
.required(true)
Expand Down Expand Up @@ -173,12 +194,23 @@ fn main() {
.unwrap();
let output = match output.as_str() {
"native" => Output::Native,
"none" => Output::Silent,
"none" => Output::Terminal,
_ => panic!("Unrecognized --output value"),
};

debug!(?output);

let until = submatches
.get_one::<String>("until")
.unwrap();
let until = match until.as_str() {
"parsing" => Phase::Parsing,
"translation" => Phase::Translation,
_ => panic!("Unrecognized --until value"),
};

debug!(?until);

let filename = submatches
.get_one::<String>("filename")
.unwrap(); // argument are required by definition so always present
Expand Down Expand Up @@ -213,12 +245,51 @@ fn main() {
}
};

// TODO continue with validation of the returned technique
if let Phase::Parsing = until {
match output {
Output::Terminal => {
eprintln!("{}", "ok".bright_green());
}
Output::Native => {
println!("{:#?}", technique);
}
Output::Silent => {}
}
std::process::exit(0);
}

eprintln!("{}", "ok".bright_green());
let program = match translation::translate(&technique) {
Ok(program) => program,
Err(errors) => {
for (i, error) in errors
.iter()
.enumerate()
{
if i > 0 {
eprintln!();
}
eprintln!(
"{}",
problem::concise_translation_error(
&error, &filename, &content, &Terminal
)
);
}
std::process::exit(1);
}
};

if let Output::Native = output {
println!("{:#?}", technique);
if let Phase::Translation = until {
match output {
Output::Terminal => {
eprintln!("{}", "ok".bright_green());
}
Output::Native => {
println!("{:#?}", program);
}
Output::Silent => {}
}
std::process::exit(0);
}
}
Some(("format", submatches)) => {
Expand Down
2 changes: 1 addition & 1 deletion src/parsing/checks/verify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1566,7 +1566,7 @@ III. Implementation

#[test]
fn spans_are_populated() {
let source = std::fs::read_to_string("tests/samples/KnownSpanLengths.tq").unwrap();
let source = std::fs::read_to_string("tests/samples/parsing/KnownSpanLengths.tq").unwrap();

let mut input = Parser::new();
input.initialize(&source);
Expand Down
34 changes: 32 additions & 2 deletions src/problem/format.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
use super::messages::generate_error_message;
use super::messages::{generate_error_message, generate_translation_error};
use owo_colors::OwoColorize;
use std::path::Path;
use technique::{formatting::Render, language::LoadingError, parsing::ParsingError};
use technique::{
formatting::Render, language::LoadingError, parsing::ParsingError,
translation::TranslationError,
};

/// Format a parsing error with full details including source code context
pub fn full_parsing_error<'i>(
Expand Down Expand Up @@ -89,6 +92,33 @@ pub fn concise_parsing_error<'i>(
)
}

/// Format a translation error with concise single-line output.
pub fn concise_translation_error<'i>(
error: &TranslationError<'i>,
filename: &'i Path,
source: &'i str,
renderer: &impl Render,
) -> String {
let (problem, _) = generate_translation_error(error, renderer);
let input = generate_filename(filename);
let offset = error
.span()
.offset;
let i = calculate_line_number(source, offset);
let j = calculate_column_number(source, offset);
let line = i + 1;
let column = j + 1;

format!(
"{}: {}:{}:{} {}",
"error".bright_red(),
input,
line,
column,
problem.bold(),
)
}

/// Format a LoadingError with concise single-line output
pub fn concise_loading_error<'i>(error: &LoadingError<'i>) -> String {
format!(
Expand Down
35 changes: 34 additions & 1 deletion src/problem/messages.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use crate::problem::Present;
use technique::{formatting::Render, language::*, parsing::ParsingError};
use technique::{
formatting::Render, language::*, parsing::ParsingError, translation::TranslationError,
};

/// Generate problem and detail messages for parsing errors using AST construction
pub fn generate_error_message<'i>(error: &ParsingError, renderer: &dyn Render) -> (String, String) {
Expand Down Expand Up @@ -1015,3 +1017,34 @@ Hyphens, underscores, spaces, or subscripts are not valid in unit symbols.
}
}
}

/// Generate problem and detail messages for translation errors.
pub fn generate_translation_error<'i>(
error: &TranslationError<'i>,
_renderer: &dyn Render,
) -> (String, String) {
match error {
TranslationError::DuplicateProcedure(Identifier { value: name, .. }) => (
format!("Duplicate procedure name '{}'", name),
"A procedure with this name has already been declared in this document.".to_string(),
),
TranslationError::DuplicateTitle {
procedure: Identifier { value: name, .. },
..
} => (
format!("Duplicate title in procedure '{}'", name),
"A procedure can have at most one title.".to_string(),
),
TranslationError::InterleavedDescription {
procedure: Identifier { value: name, .. },
..
} => (
format!("Description out of place in procedure '{}'", name),
"A procedure's free-text description must appear immediately after the title and before any steps or code blocks.".to_string(),
),
TranslationError::UnresolvedProcedure(Identifier { value: name, .. }) => (
format!("Unresolved procedure '{}'", name),
"A `<name>` invocation must refer to a procedure declared in this document. Built-in functions use the `name(...)` form (without angle brackets).".to_string(),
),
}
}
6 changes: 6 additions & 0 deletions src/program/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
//! Intermediate Representation suitable for an interpreter.

mod types;

// Re-export all public symbols
pub use types::*;
Loading