Arguments done!

This commit is contained in:
Casey Rodarmor 2016-10-29 00:55:47 -07:00
parent ac5433248e
commit b57b84e550
3 changed files with 78 additions and 46 deletions

14
notes
View File

@ -1,20 +1,16 @@
notes
-----
- arguments:
. change evaluate_expression to evaluate_lines
. fast errors when arguments are not passed
. don't assume that argument count is correct
. don't unwrap errors in evaluate line
. sub arguments into recipes
- save result of commands in variables: `hello`
- set variables from the command line: j --set build linux
- set variables from the command line:
. j --set build linux
. j build=linux
- before release:
- where can users get help?
- irc, email, github, mailing list
- rewrite grammar.txt
- start with an example justfile
- then installation instructions

View File

@ -135,31 +135,24 @@ fn error_from_signal(recipe: &str, exit_status: process::ExitStatus) -> RunError
impl<'a> Recipe<'a> {
fn run(&self, arguments: &[&'a str], scope: &BTreeMap<&'a str, String>) -> Result<(), RunError<'a>> {
let mut evaluated_lines = vec![];
for fragments in &self.lines {
let mut line = String::new();
for fragment in fragments.iter() {
match *fragment {
Fragment::Text{ref text} => line += text.lexeme,
Fragment::Expression{value: Some(ref value), ..} => {
line += &value;
}
Fragment::Expression{ref expression, value: None} => {
let mut arg_map = BTreeMap::new();
for (i, argument) in arguments.iter().enumerate() {
arg_map.insert(*self.arguments.get(i).unwrap(), *argument);
}
line += &evaluate_expression(
expression,
&scope,
&BTreeMap::new(),
&BTreeMap::new(),
&arg_map,
).unwrap().unwrap();
}
}
let mut arg_map = BTreeMap::new();
for (i, argument) in arguments.iter().enumerate() {
arg_map.insert(*self.arguments.get(i).unwrap(), Some(*argument));
}
let evaluated_lines;
match evaluate_lines(&self.lines, &scope, &arg_map) {
Err(error) => {
return Err(RunError::InternalError {
message: format!("deferred evaluation failed {}", error),
});
}
evaluated_lines.push(line);
Ok(None) => {
return Err(RunError::InternalError {
message: "deferred evaluation returned None".to_string(),
});
}
Ok(Some(lines)) => evaluated_lines = lines,
}
if self.shebang {
@ -377,26 +370,42 @@ fn evaluate<'a>(
Ok(evaluated)
}
fn evaluate_expression<'a: 'b, 'b> (
expression: &Expression<'a>,
scope: &BTreeMap<&'a str, String>,
assignments: &'b BTreeMap<&'a str, Expression<'a>>,
assignment_tokens: &'b BTreeMap<&'a str, Token<'a>>,
arguments: &BTreeMap<&str, &str>,
) -> Result<Option<String>, Error<'a>> {
fn evaluate_lines<'a>(
lines: &Vec<Vec<Fragment<'a>>>,
scope: &BTreeMap<&'a str, String>,
arguments: &BTreeMap<&str, Option<&str>>
) -> Result<Option<Vec<String>>, Error<'a>> {
let mut evaluator = Evaluator{
seen: HashSet::new(),
stack: vec![],
evaluated: &mut BTreeMap::new(),
scope: scope,
assignments: assignments,
assignment_tokens: assignment_tokens,
assignments: &BTreeMap::new(),
assignment_tokens: &BTreeMap::new(),
};
let mut argument_options = BTreeMap::new();
for (name, value) in arguments.iter() {
argument_options.insert(*name, Some(*value));
let mut evaluated_lines = vec![];
for fragments in lines {
let mut line = String::new();
for fragment in fragments.iter() {
match *fragment {
Fragment::Text{ref text} => line += text.lexeme,
Fragment::Expression{value: Some(ref value), ..} => {
line += &value;
}
Fragment::Expression{ref expression, value: None} => {
if let Some(value) = try!(evaluator.evaluate_expression(expression, &arguments)) {
line += &value;
} else {
return Ok(None);
}
}
}
}
evaluated_lines.push(line);
}
evaluator.evaluate_expression(expression, &argument_options)
Ok(Some(evaluated_lines))
}
struct Evaluator<'a: 'b, 'b> {
@ -777,6 +786,7 @@ enum RunError<'a> {
UnknownFailure{recipe: &'a str},
IoError{recipe: &'a str, io_error: io::Error},
TmpdirIoError{recipe: &'a str, io_error: io::Error},
InternalError{message: String},
}
impl<'a> Display for RunError<'a> {
@ -815,6 +825,9 @@ impl<'a> Display for RunError<'a> {
},
RunError::TmpdirIoError{recipe, ref io_error} =>
try!(write!(f, "Recipe \"{}\" could not be run because of an IO error while trying to create a temporary directory or write a file to that directory`:\n{}", recipe, io_error)),
RunError::InternalError{ref message} => {
try!(writeln!(f, "internal error, this may indicate a bug in j: {}\n consider filing an issue: https://github.com/casey/j/issues/new", message));
}
}
Ok(())
}

View File

@ -733,3 +733,26 @@ a return code:
}
}
#[test]
fn missing_args() {
match parse_success("a b c d:").run(&["a", "b", "c"]).unwrap_err() {
super::RunError::ArgumentCountMismatch{recipe, found, expected} => {
assert_eq!(recipe, "a");
assert_eq!(found, 2);
assert_eq!(expected, 3);
},
other => panic!("expected an code run error, but got: {}", other),
}
}
#[test]
fn missing_default() {
match parse_success("a b c d:\n echo {{b}}{{c}}{{d}}").run(&["a"]).unwrap_err() {
super::RunError::ArgumentCountMismatch{recipe, found, expected} => {
assert_eq!(recipe, "a");
assert_eq!(found, 0);
assert_eq!(expected, 3);
},
other => panic!("expected an code run error, but got: {}", other),
}
}