Skip to content
3 changes: 2 additions & 1 deletion examples/minimal/ExampleOfEverything.tq
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Ask these questions: are you really sure you want a coffee?

Assuming you do, then:

1. { repeat <making_coffee>(e) ~ cups }
1. { repeat <making_coffee>(e) }
a. First task
b. Second another task
'Yes' | 'No'
Expand All @@ -19,6 +19,7 @@ Assuming you do, then:
./stuff
```
) }
3. Write everything down ~ paper

another_example(e) : Input -> Output
{
Expand Down
32 changes: 27 additions & 5 deletions src/domain/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,13 +146,26 @@ impl<'i> Scope<'i> {
}
}

/// Returns the tablet pairs if this is a CodeBlock containing a Tablet.
pub fn tablet(&self) -> Option<&[Pair<'i>]> {
/// Returns the tablet pairs if this is a CodeBlock containing a single
/// list whose elements are all labelled values.
pub fn tablet(&self) -> Option<Vec<&Pair<'i>>> {
match self {
Scope::CodeBlock { expressions, .. } => {
if expressions.len() == 1 {
if let Expression::Tablet(pairs, _) = &expressions[0] {
return Some(pairs);
if let Expression::List(elements, _) = &expressions[0] {
let pairs: Vec<&Pair<'i>> = elements
.iter()
.filter_map(|element| {
if let Expression::Pair(pair, _) = element {
Some(pair.as_ref())
} else {
None
}
})
.collect();
if !pairs.is_empty() && pairs.len() == elements.len() {
return Some(pairs);
}
}
}
None
Expand Down Expand Up @@ -338,7 +351,16 @@ fn render_expression(expr: &Expression) -> String {
}
Expression::Number(Numeric::Scientific(q), _) => q.to_string(),
Expression::Number(Numeric::Integral(n), _) => n.to_string(),
Expression::Tablet(_, _) => String::new(),
Expression::Pair(pair, _) => {
format!("\"{}\" = {}", pair.label, render_expression(&pair.value))
}
Expression::List(elements, _) => {
let items: Vec<_> = elements
.iter()
.map(render_expression)
.collect();
format!("[{}]", items.join(", "))
}
Expression::Separator => String::new(),
}
}
Expand Down
100 changes: 77 additions & 23 deletions src/formatting/formatter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,31 @@ fn render_fragments<'i>(fragments: &[(Syntax, Cow<'i, str>)], renderer: &dyn Ren
result
}

/// A list reads as a tablet when it is non-empty and every element is a
/// labelled value. Such lists are laid out and treated as blocks rather than
/// inline.
fn is_tablet_list(elements: &[Expression]) -> bool {
!elements.is_empty()
&& elements
.iter()
.all(|element| {
if let Expression::Pair(_, _) = element {
true
} else {
false
}
})
}

/// True when an expression is a tablet-shaped list (see `is_tablet_list`).
fn is_tablet_list_expr(expr: &Expression) -> bool {
if let Expression::List(elements, _) = expr {
is_tablet_list(elements)
} else {
false
}
}

struct Formatter<'i> {
fragments: Vec<(Syntax, Cow<'i, str>)>,
nesting: u8,
Expand Down Expand Up @@ -329,8 +354,12 @@ impl<'i> Formatter<'i> {
}

fn render_inline_code(&self, expr: &'i Expression) -> Vec<(Syntax, Cow<'i, str>)> {
if is_tablet_list_expr(expr) {
// Not inline; caller handles the block layout specially.
return Vec::new();
}
match expr {
Expression::Tablet(_, _) | Expression::Multiline(_, _, _) => {
Expression::Multiline(_, _, _) => {
// These are not inline, caller should handle specially
Vec::new()
}
Expand Down Expand Up @@ -657,7 +686,7 @@ impl<'i> Formatter<'i> {
line.add_breakable(syntax, text);
}
Descriptive::CodeInline(expr) => match expr {
Expression::Tablet(_, _) => {
_ if is_tablet_list_expr(expr) => {
line.flush();
self.add_fragment_reference(Syntax::Structure, "{");
self.append_char('\n');
Expand Down Expand Up @@ -884,11 +913,7 @@ impl<'i> Formatter<'i> {
let inline = if has_separator {
true
} else if expressions.len() == 1 {
if let Expression::Tablet(_, _) = &expressions[0] {
false
} else {
true
}
!is_tablet_list_expr(&expressions[0])
} else {
false
};
Expand Down Expand Up @@ -1075,7 +1100,8 @@ impl<'i> Formatter<'i> {
self.add_fragment_reference(Syntax::Neutral, " ");
self.append_variables(variables);
}
Expression::Tablet(pairs, _) => self.append_tablet(pairs),
Expression::Pair(pair, _) => self.append_pair(pair),
Expression::List(elements, _) => self.append_list(elements),
Expression::Separator => {}
}
}
Expand Down Expand Up @@ -1205,25 +1231,53 @@ impl<'i> Formatter<'i> {
self.add_fragment_reference(Syntax::Structure, ")");
}

fn append_tablet(&mut self, pairs: &'i Vec<Pair>) {
self.add_fragment_reference(Syntax::Structure, "[");
self.append_char('\n');
fn append_pair(&mut self, pair: &'i Pair) {
self.add_fragment_reference(Syntax::Quote, "\"");
self.add_fragment_reference(Syntax::Label, pair.label);
self.add_fragment_reference(Syntax::Quote, "\"");
self.add_fragment_reference(Syntax::Neutral, " ");
self.add_fragment_reference(Syntax::Structure, "=");
self.add_fragment_reference(Syntax::Neutral, " ");
self.append_expression(&pair.value);
}

self.increase(4);
for pair in pairs {
self.indent();
self.add_fragment_reference(Syntax::Quote, "\"");
self.add_fragment_reference(Syntax::Label, pair.label);
self.add_fragment_reference(Syntax::Quote, "\"");
self.add_fragment_reference(Syntax::Neutral, " ");
self.add_fragment_reference(Syntax::Structure, "=");
self.add_fragment_reference(Syntax::Neutral, " ");
self.append_expression(&pair.value);
/// A list whose elements are all labelled (a tablet) is laid out one
/// element per line; any other list, and the empty list, is inline.
fn append_list(&mut self, elements: &'i Vec<Expression>) {
if elements.is_empty() {
self.add_fragment_reference(Syntax::Structure, "[]");
return;
}

if is_tablet_list(elements) {
self.add_fragment_reference(Syntax::Structure, "[");
self.append_char('\n');

self.increase(4);
for element in elements {
self.indent();
self.append_expression(element);
self.append_char('\n');
}
self.decrease(4);

self.indent();
self.add_fragment_reference(Syntax::Structure, "]");
return;
}
self.decrease(4);

self.indent();
self.add_fragment_reference(Syntax::Structure, "[");
for (i, element) in elements
.iter()
.enumerate()
{
if i > 0 {
self.add_fragment_reference(Syntax::Structure, ",");
}
self.add_fragment_reference(Syntax::Neutral, " ");
self.append_expression(element);
}
self.add_fragment_reference(Syntax::Neutral, " ");
self.add_fragment_reference(Syntax::Structure, "]");
}
}
Expand Down
6 changes: 4 additions & 2 deletions src/language/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,8 @@ pub enum Expression<'i> {
Application(Invocation<'i>, Span),
Execution(Function<'i>, Span),
Binding(Box<Expression<'i>>, Vec<Identifier<'i>>, Span),
Tablet(Vec<Pair<'i>>, Span),
Pair(Box<Pair<'i>>, Span),
List(Vec<Expression<'i>>, Span),
Separator,
}

Expand All @@ -438,7 +439,8 @@ impl PartialEq for Expression<'_> {
(Expression::Binding(a1, a2, _), Expression::Binding(b1, b2, _)) => {
a1 == b1 && a2 == b2
}
(Expression::Tablet(a, _), Expression::Tablet(b, _)) => a == b,
(Expression::Pair(a, _), Expression::Pair(b, _)) => a == b,
(Expression::List(a, _), Expression::List(b, _)) => a == b,
(Expression::Separator, Expression::Separator) => true,
_ => false,
}
Expand Down
13 changes: 12 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -616,6 +616,17 @@ fn main() {

debug!(filename);

let arguments: Vec<String> = submatches
.get_many::<String>("arguments")
.map(|values| {
values
.cloned()
.collect()
})
.unwrap_or_default();

debug!(?arguments);

let filename = Path::new(filename);
let content = match parsing::load(&filename) {
Ok(data) => data,
Expand Down Expand Up @@ -670,7 +681,7 @@ fn main() {
}
};

match runner::start(filename, &program) {
match runner::start(filename, &program, &arguments) {
Ok((run_id, Outcome::Quit)) => {
eprintln!("paused; resume with `technique resume {}`", run_id.render());
std::process::exit(0);
Expand Down
Loading