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 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` - 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: - before release:
- where can users get help?
- irc, email, github, mailing list
- rewrite grammar.txt - rewrite grammar.txt
- start with an example justfile - start with an example justfile
- then installation instructions - then installation instructions

View File

@ -135,31 +135,24 @@ fn error_from_signal(recipe: &str, exit_status: process::ExitStatus) -> RunError
impl<'a> Recipe<'a> { impl<'a> Recipe<'a> {
fn run(&self, arguments: &[&'a str], scope: &BTreeMap<&'a str, String>) -> Result<(), RunError<'a>> { fn run(&self, arguments: &[&'a str], scope: &BTreeMap<&'a str, String>) -> Result<(), RunError<'a>> {
let mut evaluated_lines = vec![]; let mut arg_map = BTreeMap::new();
for fragments in &self.lines { for (i, argument) in arguments.iter().enumerate() {
let mut line = String::new(); arg_map.insert(*self.arguments.get(i).unwrap(), Some(*argument));
for fragment in fragments.iter() { }
match *fragment {
Fragment::Text{ref text} => line += text.lexeme, let evaluated_lines;
Fragment::Expression{value: Some(ref value), ..} => { match evaluate_lines(&self.lines, &scope, &arg_map) {
line += &value; Err(error) => {
} return Err(RunError::InternalError {
Fragment::Expression{ref expression, value: None} => { message: format!("deferred evaluation failed {}", error),
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();
}
}
} }
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 { if self.shebang {
@ -377,26 +370,42 @@ fn evaluate<'a>(
Ok(evaluated) Ok(evaluated)
} }
fn evaluate_expression<'a: 'b, 'b> ( fn evaluate_lines<'a>(
expression: &Expression<'a>, lines: &Vec<Vec<Fragment<'a>>>,
scope: &BTreeMap<&'a str, String>, scope: &BTreeMap<&'a str, String>,
assignments: &'b BTreeMap<&'a str, Expression<'a>>, arguments: &BTreeMap<&str, Option<&str>>
assignment_tokens: &'b BTreeMap<&'a str, Token<'a>>, ) -> Result<Option<Vec<String>>, Error<'a>> {
arguments: &BTreeMap<&str, &str>,
) -> Result<Option<String>, Error<'a>> {
let mut evaluator = Evaluator{ let mut evaluator = Evaluator{
seen: HashSet::new(), seen: HashSet::new(),
stack: vec![], stack: vec![],
evaluated: &mut BTreeMap::new(), evaluated: &mut BTreeMap::new(),
scope: scope, scope: scope,
assignments: assignments, assignments: &BTreeMap::new(),
assignment_tokens: assignment_tokens, assignment_tokens: &BTreeMap::new(),
}; };
let mut argument_options = BTreeMap::new();
for (name, value) in arguments.iter() { let mut evaluated_lines = vec![];
argument_options.insert(*name, Some(*value)); 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> { struct Evaluator<'a: 'b, 'b> {
@ -777,6 +786,7 @@ enum RunError<'a> {
UnknownFailure{recipe: &'a str}, UnknownFailure{recipe: &'a str},
IoError{recipe: &'a str, io_error: io::Error}, IoError{recipe: &'a str, io_error: io::Error},
TmpdirIoError{recipe: &'a str, io_error: io::Error}, TmpdirIoError{recipe: &'a str, io_error: io::Error},
InternalError{message: String},
} }
impl<'a> Display for RunError<'a> { impl<'a> Display for RunError<'a> {
@ -815,6 +825,9 @@ impl<'a> Display for RunError<'a> {
}, },
RunError::TmpdirIoError{recipe, ref io_error} => 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)), 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(()) 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),
}
}