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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/.vscode
/target
/plans

# rendering artifacts
*.pdf
Expand Down
109 changes: 58 additions & 51 deletions src/domain/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ impl<'i> Procedure<'i> {
self.elements
.iter()
.flat_map(|element| match element {
Element::Steps(steps) => steps.iter(),
Element::Steps(steps, _) => steps.iter(),
_ => [].iter(),
})
}
Expand All @@ -55,7 +55,7 @@ impl<'i> Procedure<'i> {
self.elements
.iter()
.flat_map(|element| match element {
Element::Description(paragraphs) => paragraphs.iter(),
Element::Description(paragraphs, _) => paragraphs.iter(),
_ => [].iter(),
})
}
Expand Down Expand Up @@ -115,7 +115,7 @@ impl<'i> Scope<'i> {
/// Returns an iterator over responses if this is a ResponseBlock.
pub fn responses(&self) -> impl Iterator<Item = &Response<'i>> {
let slice: &[Response<'i>] = match self {
Scope::ResponseBlock { responses } => responses,
Scope::ResponseBlock { responses, .. } => responses,
_ => &[],
};
slice.iter()
Expand All @@ -127,7 +127,7 @@ impl<'i> Scope<'i> {
Scope::AttributeBlock { attributes, .. } => attributes
.iter()
.filter_map(|attr| match attr {
Attribute::Role(id) => Some(id.0),
Attribute::Role(id, _) => Some(id.value),
_ => None,
})
.collect::<Vec<_>>()
Expand All @@ -142,7 +142,7 @@ impl<'i> Scope<'i> {
Scope::AttributeBlock { attributes, .. } => attributes
.iter()
.filter_map(|attr| match attr {
Attribute::Place(id) => Some(id.0),
Attribute::Place(id, _) => Some(id.value),
_ => None,
})
.collect::<Vec<_>>()
Expand All @@ -156,7 +156,7 @@ impl<'i> Scope<'i> {
match self {
Scope::CodeBlock { expressions, .. } => {
if expressions.len() == 1 {
if let Expression::Tablet(pairs) = &expressions[0] {
if let Expression::Tablet(pairs, _) = &expressions[0] {
return Some(pairs);
}
}
Expand Down Expand Up @@ -232,7 +232,7 @@ impl<'i> Procedure<'i> {
/// Returns the procedure name.
pub fn name(&self) -> &'i str {
self.name
.0
.value
}
}

Expand All @@ -252,10 +252,10 @@ impl<'i> Response<'i> {
/// Returns (expression_text, body_lines) where body_lines captures multiline
/// content separately for distinct styling.
fn render_expression_parts(expr: &Expression) -> (String, Vec<String>) {
if let Expression::Execution(func) = expr {
if let Expression::Execution(func, _) = expr {
let mut body = Vec::new();
for param in &func.parameters {
if let Expression::Multiline(_, lines) = param {
if let Expression::Multiline(_, lines, _) = param {
body.extend(
lines
.iter()
Expand All @@ -268,7 +268,7 @@ fn render_expression_parts(expr: &Expression) -> (String, Vec<String>) {
format!(
"{}(",
func.target
.0
.value
),
body,
);
Expand All @@ -279,29 +279,29 @@ fn render_expression_parts(expr: &Expression) -> (String, Vec<String>) {

fn render_expression(expr: &Expression) -> String {
match expr {
Expression::Repeat(inner) => {
Expression::Repeat(inner, _) => {
format!("repeat {}", render_expression(inner))
}
Expression::Foreach(ids, inner) => {
Expression::Foreach(ids, inner, _) => {
let vars = if ids.len() == 1 {
ids[0]
.0
.value
.to_string()
} else {
format!(
"({})",
ids.iter()
.map(|id| id.0)
.map(|id| id.value)
.collect::<Vec<_>>()
.join(", ")
)
};
format!("foreach {} in {}", vars, render_expression(inner))
}
Expression::Application(inv) => {
Expression::Application(inv, _) => {
let name = match &inv.target {
Target::Local(id) => id.0,
Target::Remote(ext) => ext.0,
Target::Local(id) => id.value,
Target::Remote(ext) => ext.value,
};
if let Some(params) = &inv.parameters {
let args: Vec<_> = params
Expand All @@ -313,7 +313,7 @@ fn render_expression(expr: &Expression) -> String {
format!("<{}>", name)
}
}
Expression::Execution(func) => {
Expression::Execution(func, _) => {
let args: Vec<_> = func
.parameters
.iter()
Expand All @@ -322,16 +322,16 @@ fn render_expression(expr: &Expression) -> String {
format!(
"{}({})",
func.target
.0,
.value,
args.join(", ")
)
}
Expression::Multiline(_, lines) => lines.join("\n"),
Expression::Variable(id) => {
id.0.to_string()
}
Expression::Binding(inner, _) => render_expression(inner),
Expression::String(pieces) => {
Expression::Multiline(_, lines, _) => lines.join("\n"),
Expression::Variable(id, _) => id
.value
.to_string(),
Expression::Binding(inner, _, _) => render_expression(inner),
Expression::String(pieces, _) => {
let mut result = String::new();
for piece in pieces {
match piece {
Expand All @@ -341,9 +341,9 @@ fn render_expression(expr: &Expression) -> String {
}
result
}
Expression::Number(Numeric::Scientific(q)) => q.to_string(),
Expression::Number(Numeric::Integral(n)) => n.to_string(),
Expression::Tablet(_) => String::new(),
Expression::Number(Numeric::Scientific(q), _) => q.to_string(),
Expression::Number(Numeric::Integral(n), _) => n.to_string(),
Expression::Tablet(_, _) => String::new(),
Expression::Separator => String::new(),
}
}
Expand Down Expand Up @@ -411,16 +411,16 @@ impl<'i> Paragraph<'i> {

fn expression_content(expr: &crate::language::Expression<'i>) -> String {
match expr {
crate::language::Expression::Application(invocation) => {
crate::language::Expression::Application(invocation, _) => {
Self::invocation_name(invocation).to_string()
}
crate::language::Expression::Repeat(inner) => {
crate::language::Expression::Repeat(inner, _) => {
format!("repeat {}", Self::expression_content(inner))
}
crate::language::Expression::Foreach(_, inner) => {
crate::language::Expression::Foreach(_, inner, _) => {
format!("foreach {}", Self::expression_content(inner))
}
crate::language::Expression::Binding(inner, _) => Self::expression_content(inner),
crate::language::Expression::Binding(inner, _, _) => Self::expression_content(inner),
_ => String::new(),
}
}
Expand Down Expand Up @@ -458,16 +458,16 @@ impl<'i> Paragraph<'i> {
expr: &crate::language::Expression<'i>,
) {
match expr {
crate::language::Expression::Application(inv) => {
crate::language::Expression::Application(inv, _) => {
targets.push(Self::invocation_name(inv));
}
crate::language::Expression::Repeat(inner) => {
crate::language::Expression::Repeat(inner, _) => {
Self::extract_expression_invocations(targets, inner);
}
crate::language::Expression::Foreach(_, inner) => {
crate::language::Expression::Foreach(_, inner, _) => {
Self::extract_expression_invocations(targets, inner);
}
crate::language::Expression::Binding(inner, _) => {
crate::language::Expression::Binding(inner, _, _) => {
Self::extract_expression_invocations(targets, inner);
}
_ => {}
Expand All @@ -476,8 +476,8 @@ impl<'i> Paragraph<'i> {

fn invocation_name(inv: &crate::language::Invocation<'i>) -> &'i str {
match &inv.target {
crate::language::Target::Local(id) => id.0,
crate::language::Target::Remote(ext) => ext.0,
crate::language::Target::Local(id) => id.value,
crate::language::Target::Remote(ext) => ext.value,
}
}
}
Expand Down Expand Up @@ -543,19 +543,21 @@ impl Prose {

#[cfg(test)]
mod check {
use crate::language::{Descriptive, Expression, Identifier, Invocation, Paragraph, Target};
use crate::language::{
Descriptive, Expression, Identifier, Invocation, Paragraph, Span, Target,
};

fn local<'a>(name: &'a str) -> Invocation<'a> {
Invocation {
target: Target::Local(Identifier(name)),
target: Target::Local(Identifier::new(name)),
parameters: None,
}
}

// Pure text: "Ensure physical and digital safety"
#[test]
fn text_only_paragraph() {
let p = Paragraph(vec![Descriptive::Text(
let p = Paragraph::new(vec![Descriptive::Text(
"Ensure physical and digital safety",
)]);
assert_eq!(p.text(), "Ensure physical and digital safety");
Expand All @@ -568,7 +570,7 @@ mod check {
// Bare invocation: <ensure_safety>
#[test]
fn invocation_only_paragraph() {
let p = Paragraph(vec![Descriptive::Application(local("ensure_safety"))]);
let p = Paragraph::new(vec![Descriptive::Application(local("ensure_safety"))]);
assert_eq!(p.text(), "");
assert_eq!(p.invocations(), vec!["ensure_safety"]);
assert_eq!(p.content(), "ensure_safety");
Expand All @@ -578,7 +580,7 @@ mod check {
// Text is present so content() returns just the text.
#[test]
fn mixed_text_and_invocation() {
let p = Paragraph(vec![
let p = Paragraph::new(vec![
Descriptive::Text("Define Requirements"),
Descriptive::Application(local("define_requirements")),
]);
Expand All @@ -590,9 +592,13 @@ mod check {
// CodeInline with repeat: { repeat <incident_action_cycle> }
#[test]
fn repeat_expression() {
let p = Paragraph(vec![Descriptive::CodeInline(Expression::Repeat(Box::new(
Expression::Application(local("incident_action_cycle")),
)))]);
let p = Paragraph::new(vec![Descriptive::CodeInline(Expression::Repeat(
Box::new(Expression::Application(
local("incident_action_cycle"),
Span::default(),
)),
Span::default(),
))]);
assert_eq!(p.text(), "");
assert_eq!(p.invocations(), vec!["incident_action_cycle"]);
assert_eq!(p.content(), "repeat incident_action_cycle");
Expand All @@ -601,9 +607,9 @@ mod check {
// Binding wrapping an invocation: <observe>(s) ~ e
#[test]
fn binding_with_invocation() {
let p = Paragraph(vec![Descriptive::Binding(
let p = Paragraph::new(vec![Descriptive::Binding(
Box::new(Descriptive::Application(local("observe"))),
vec![Identifier("e")],
vec![Identifier::new("e")],
)]);
assert_eq!(p.text(), "");
assert_eq!(p.invocations(), vec!["observe"]);
Expand All @@ -613,9 +619,10 @@ mod check {
// CodeInline with foreach: { foreach design in designs }
#[test]
fn foreach_expression() {
let p = Paragraph(vec![Descriptive::CodeInline(Expression::Foreach(
vec![Identifier("design")],
Box::new(Expression::Application(local("implement"))),
let p = Paragraph::new(vec![Descriptive::CodeInline(Expression::Foreach(
vec![Identifier::new("design")],
Box::new(Expression::Application(local("implement"), Span::default())),
Span::default(),
))]);
assert_eq!(p.text(), "");
assert_eq!(p.invocations(), vec!["implement"]);
Expand Down
17 changes: 14 additions & 3 deletions src/domain/nasa_esa_iss/adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,9 @@ fn rewrite_expression(expr: &str) -> String {
if let Some(rest) = expr.strip_prefix("foreach ") {
if let Some((var, seq_expr)) = rest.split_once(" in ") {
if let Some((start, end)) = parse_seq(seq_expr) {
let values: Vec<String> = (start..=end).map(|n| n.to_string()).collect();
let values: Vec<String> = (start..=end)
.map(|n| n.to_string())
.collect();
return format!("foreach {} {}", var.trim(), values.join(" "));
}
}
Expand All @@ -123,7 +125,16 @@ fn rewrite_expression(expr: &str) -> String {

/// Parse `seq(A, B)` into a (start, end) pair.
fn parse_seq(s: &str) -> Option<(i64, i64)> {
let inner = s.strip_prefix("seq(")?.strip_suffix(')')?;
let inner = s
.strip_prefix("seq(")?
.strip_suffix(')')?;
let (a, b) = inner.split_once(", ")?;
Some((a.trim().parse().ok()?, b.trim().parse().ok()?))
Some((
a.trim()
.parse()
.ok()?,
b.trim()
.parse()
.ok()?,
))
}
14 changes: 7 additions & 7 deletions src/domain/recipe/adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -254,8 +254,8 @@ fn strip_ordinals(steps: &mut Vec<Step>) {
/// Format an expression value as a human-readable quantity string.
fn format_value(expr: &language::Expression) -> String {
match expr {
language::Expression::Number(language::Numeric::Scientific(q)) => q.to_string(),
language::Expression::Number(language::Numeric::Integral(n)) => n.to_string(),
language::Expression::Number(language::Numeric::Scientific(q), _) => q.to_string(),
language::Expression::Number(language::Numeric::Integral(n), _) => n.to_string(),
_ => String::new(),
}
}
Expand Down Expand Up @@ -304,20 +304,20 @@ fn builtin_from_descriptive(d: &language::Descriptive) -> Option<String> {

fn builtin_from_expression(expr: &language::Expression) -> Option<String> {
match expr {
language::Expression::Application(inv) => builtin_from_invocation(inv),
language::Expression::Execution(func) => builtin_suffix(
language::Expression::Application(inv, _) => builtin_from_invocation(inv),
language::Expression::Execution(func, _) => builtin_suffix(
func.target
.0,
.value,
&func.parameters,
),
language::Expression::Binding(inner, _) => builtin_from_expression(inner),
language::Expression::Binding(inner, _, _) => builtin_from_expression(inner),
_ => None,
}
}

fn builtin_from_invocation(inv: &language::Invocation) -> Option<String> {
let name = match &inv.target {
language::Target::Local(id) => id.0,
language::Target::Local(id) => id.value,
_ => return None,
};
match &inv.parameters {
Expand Down
Loading