Skip to content

Commit

Permalink
Allow arbitrary expressions as default arguments (#400)
Browse files Browse the repository at this point in the history
  • Loading branch information
casey authored Apr 12, 2019
1 parent 12f9428 commit fe0a6c2
Show file tree
Hide file tree
Showing 16 changed files with 505 additions and 170 deletions.
4 changes: 2 additions & 2 deletions GRAMMAR.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,15 +63,15 @@ value : NAME '(' arguments? ')'
| RAW_STRING
| BACKTICK
| NAME
| '(' expression ')'
arguments : expression ',' arguments
| expression ','?
recipe : '@'? NAME parameter* ('+' parameter)? ':' dependencies? body?
parameter : NAME
| NAME '=' STRING
| NAME '=' RAW_STRING
| NAME '=' value
dependencies : NAME+
Expand Down
13 changes: 12 additions & 1 deletion README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -480,7 +480,9 @@ cd my-awesome-project && make
Parameters may have default values:

```make
test target tests='all':
default = 'all'

test target tests=default:
@echo 'Testing {{target}}:{{tests}}...'
./test --tests {{tests}} {{target}}
```
Expand All @@ -501,6 +503,15 @@ Testing server:unit...
./test --tests unit server
```

Default values may be arbitrary expressions, but concatenations must be parenthesized:

```make
arch = "wasm"

test triple=(arch + "-unknown-unknown"):
./test {{triple}}
```

The last parameter of a recipe may be variadic, indicated with a `+` before the argument name:

```make
Expand Down
1 change: 1 addition & 0 deletions src/alias.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::common::*;

#[derive(Debug)]
pub struct Alias<'a> {
pub name: &'a str,
pub target: &'a str,
Expand Down
5 changes: 3 additions & 2 deletions src/assignment_evaluator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ impl<'a, 'b> AssignmentEvaluator<'a, 'b> {
Ok(())
}

fn evaluate_expression(
pub fn evaluate_expression(
&mut self,
expression: &Expression<'a>,
arguments: &BTreeMap<&str, Cow<str>>,
Expand Down Expand Up @@ -120,7 +120,7 @@ impl<'a, 'b> AssignmentEvaluator<'a, 'b> {
};
evaluate_function(token, name, &context, &call_arguments)
}
Expression::String { ref cooked_string } => Ok(cooked_string.cooked.clone()),
Expression::String { ref cooked_string } => Ok(cooked_string.cooked.to_string()),
Expression::Backtick { raw, ref token } => {
if self.dry_run {
Ok(format!("`{}`", raw))
Expand All @@ -131,6 +131,7 @@ impl<'a, 'b> AssignmentEvaluator<'a, 'b> {
Expression::Concatination { ref lhs, ref rhs } => {
Ok(self.evaluate_expression(lhs, arguments)? + &self.evaluate_expression(rhs, arguments)?)
}
Expression::Group { ref expression } => self.evaluate_expression(&expression, arguments),
}
}

Expand Down
3 changes: 2 additions & 1 deletion src/assignment_resolver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ impl<'a: 'b, 'b> AssignmentResolver<'a, 'b> {
}

fn resolve_expression(&mut self, expression: &Expression<'a>) -> CompilationResult<'a, ()> {
match *expression {
match expression {
Expression::Variable { name, ref token } => {
if self.evaluated.contains(name) {
return Ok(());
Expand All @@ -83,6 +83,7 @@ impl<'a: 'b, 'b> AssignmentResolver<'a, 'b> {
self.resolve_expression(rhs)?;
}
Expression::String { .. } | Expression::Backtick { .. } => {}
Expression::Group { expression } => self.resolve_expression(expression)?,
}
Ok(())
}
Expand Down
18 changes: 15 additions & 3 deletions src/cooked_string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::common::*;
#[derive(PartialEq, Debug)]
pub struct CookedString<'a> {
pub raw: &'a str,
pub cooked: String,
pub cooked: Cow<'a, str>,
}

impl<'a> CookedString<'a> {
Expand All @@ -12,7 +12,7 @@ impl<'a> CookedString<'a> {

if let TokenKind::RawString = token.kind {
Ok(CookedString {
cooked: raw.to_string(),
cooked: Cow::Borrowed(raw),
raw,
})
} else if let TokenKind::StringToken = token.kind {
Expand Down Expand Up @@ -41,11 +41,23 @@ impl<'a> CookedString<'a> {
}
cooked.push(c);
}
Ok(CookedString { raw, cooked })
Ok(CookedString {
raw,
cooked: Cow::Owned(cooked),
})
} else {
Err(token.error(CompilationErrorKind::Internal {
message: "cook_string() called on non-string token".to_string(),
}))
}
}
}

impl<'a> Display for CookedString<'a> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self.cooked {
Cow::Borrowed(raw) => write!(f, "'{}'", raw),
Cow::Owned(_) => write!(f, "\"{}\"", self.raw),
}
}
}
38 changes: 24 additions & 14 deletions src/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ pub enum Expression<'a> {
name: &'a str,
token: Token<'a>,
},
Group {
expression: Box<Expression<'a>>,
},
}

impl<'a> Expression<'a> {
Expand All @@ -39,7 +42,7 @@ impl<'a> Display for Expression<'a> {
match *self {
Expression::Backtick { raw, .. } => write!(f, "`{}`", raw)?,
Expression::Concatination { ref lhs, ref rhs } => write!(f, "{} + {}", lhs, rhs)?,
Expression::String { ref cooked_string } => write!(f, "\"{}\"", cooked_string.raw)?,
Expression::String { ref cooked_string } => write!(f, "{}", cooked_string)?,
Expression::Variable { name, .. } => write!(f, "{}", name)?,
Expression::Call {
name,
Expand All @@ -56,6 +59,7 @@ impl<'a> Display for Expression<'a> {
}
write!(f, ")")?;
}
Expression::Group { ref expression } => write!(f, "({})", expression)?,
}
Ok(())
}
Expand All @@ -71,15 +75,19 @@ impl<'a> Iterator for Variables<'a> {
fn next(&mut self) -> Option<&'a Token<'a>> {
match self.stack.pop() {
None
| Some(&Expression::String { .. })
| Some(&Expression::Backtick { .. })
| Some(&Expression::Call { .. }) => None,
Some(&Expression::Variable { ref token, .. }) => Some(token),
Some(&Expression::Concatination { ref lhs, ref rhs }) => {
| Some(Expression::String { .. })
| Some(Expression::Backtick { .. })
| Some(Expression::Call { .. }) => None,
Some(Expression::Variable { token, .. }) => Some(token),
Some(Expression::Concatination { lhs, rhs }) => {
self.stack.push(lhs);
self.stack.push(rhs);
self.next()
}
Some(Expression::Group { expression }) => {
self.stack.push(expression);
self.next()
}
}
}
}
Expand All @@ -94,19 +102,21 @@ impl<'a> Iterator for Functions<'a> {
fn next(&mut self) -> Option<Self::Item> {
match self.stack.pop() {
None
| Some(&Expression::String { .. })
| Some(&Expression::Backtick { .. })
| Some(&Expression::Variable { .. }) => None,
Some(&Expression::Call {
ref token,
ref arguments,
..
| Some(Expression::String { .. })
| Some(Expression::Backtick { .. })
| Some(Expression::Variable { .. }) => None,
Some(Expression::Call {
token, arguments, ..
}) => Some((token, arguments.len())),
Some(&Expression::Concatination { ref lhs, ref rhs }) => {
Some(Expression::Concatination { lhs, rhs }) => {
self.stack.push(lhs);
self.stack.push(rhs);
self.next()
}
Some(Expression::Group { expression }) => {
self.stack.push(expression);
self.next()
}
}
}
}
1 change: 1 addition & 0 deletions src/justfile.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::common::*;

#[derive(Debug)]
pub struct Justfile<'a> {
pub recipes: BTreeMap<&'a str, Recipe<'a>>,
pub assignments: BTreeMap<&'a str, Expression<'a>>,
Expand Down
6 changes: 6 additions & 0 deletions src/lexer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -617,6 +617,12 @@ c: b
"#$#$.",
}

summary_test! {
multiple_recipes,
"a:\n foo\nb:",
"N:$>^_$<N:.",
}

error_test! {
name: tokenize_space_then_tab,
input: "a:
Expand Down
8 changes: 2 additions & 6 deletions src/parameter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::common::*;

#[derive(PartialEq, Debug)]
pub struct Parameter<'a> {
pub default: Option<String>,
pub default: Option<Expression<'a>>,
pub name: &'a str,
pub token: Token<'a>,
pub variadic: bool,
Expand All @@ -16,11 +16,7 @@ impl<'a> Display for Parameter<'a> {
}
write!(f, "{}", color.parameter().paint(self.name))?;
if let Some(ref default) = self.default {
let escaped = default
.chars()
.flat_map(char::escape_default)
.collect::<String>();;
write!(f, r#"='{}'"#, color.string().paint(&escaped))?;
write!(f, "={}", color.string().paint(&default.to_string()))?;
}
Ok(())
}
Expand Down
Loading

0 comments on commit fe0a6c2

Please sign in to comment.