Use BTreeMap and BTreeSet as Map and Set
This commit is contained in:
parent
93a3b3533b
commit
f925520101
118
src/lib.rs
118
src/lib.rs
@ -16,9 +16,9 @@ extern crate tempdir;
|
||||
use std::io::prelude::*;
|
||||
|
||||
use std::{fs, fmt, process, io};
|
||||
use std::collections::{BTreeMap, HashSet};
|
||||
use std::fmt::Display;
|
||||
use regex::Regex;
|
||||
use std::collections::{BTreeMap as Map, BTreeSet as Set};
|
||||
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
|
||||
@ -188,17 +188,17 @@ impl<'a> Recipe<'a> {
|
||||
fn run(
|
||||
&self,
|
||||
arguments: &[&'a str],
|
||||
scope: &BTreeMap<&'a str, String>,
|
||||
scope: &Map<&'a str, String>,
|
||||
dry_run: bool,
|
||||
) -> Result<(), RunError<'a>> {
|
||||
let argument_map = arguments .iter().enumerate()
|
||||
.map(|(i, argument)| (self.arguments[i], *argument)).collect();
|
||||
|
||||
let mut evaluator = Evaluator {
|
||||
evaluated: BTreeMap::new(),
|
||||
evaluated: Map::new(),
|
||||
scope: scope,
|
||||
assignments: &BTreeMap::new(),
|
||||
overrides: &BTreeMap::new(),
|
||||
assignments: &Map::new(),
|
||||
overrides: &Map::new(),
|
||||
};
|
||||
|
||||
if self.shebang {
|
||||
@ -339,14 +339,14 @@ impl<'a> Display for Recipe<'a> {
|
||||
}
|
||||
|
||||
fn resolve_recipes<'a>(
|
||||
recipes: &BTreeMap<&'a str, Recipe<'a>>,
|
||||
assignments: &BTreeMap<&'a str, Expression<'a>>,
|
||||
recipes: &Map<&'a str, Recipe<'a>>,
|
||||
assignments: &Map<&'a str, Expression<'a>>,
|
||||
text: &'a str,
|
||||
) -> Result<(), Error<'a>> {
|
||||
let mut resolver = Resolver {
|
||||
seen: HashSet::new(),
|
||||
seen: Set::new(),
|
||||
stack: vec![],
|
||||
resolved: HashSet::new(),
|
||||
resolved: Set::new(),
|
||||
recipes: recipes,
|
||||
};
|
||||
|
||||
@ -393,9 +393,9 @@ fn resolve_recipes<'a>(
|
||||
|
||||
struct Resolver<'a: 'b, 'b> {
|
||||
stack: Vec<&'a str>,
|
||||
seen: HashSet<&'a str>,
|
||||
resolved: HashSet<&'a str>,
|
||||
recipes: &'b BTreeMap<&'a str, Recipe<'a>>,
|
||||
seen: Set<&'a str>,
|
||||
resolved: Set<&'a str>,
|
||||
recipes: &'b Map<&'a str, Recipe<'a>>,
|
||||
}
|
||||
|
||||
impl<'a, 'b> Resolver<'a, 'b> {
|
||||
@ -433,16 +433,16 @@ impl<'a, 'b> Resolver<'a, 'b> {
|
||||
}
|
||||
|
||||
fn resolve_assignments<'a>(
|
||||
assignments: &BTreeMap<&'a str, Expression<'a>>,
|
||||
assignment_tokens: &BTreeMap<&'a str, Token<'a>>,
|
||||
assignments: &Map<&'a str, Expression<'a>>,
|
||||
assignment_tokens: &Map<&'a str, Token<'a>>,
|
||||
) -> Result<(), Error<'a>> {
|
||||
|
||||
let mut resolver = AssignmentResolver {
|
||||
assignments: assignments,
|
||||
assignment_tokens: assignment_tokens,
|
||||
stack: vec![],
|
||||
seen: HashSet::new(),
|
||||
evaluated: HashSet::new(),
|
||||
seen: Set::new(),
|
||||
evaluated: Set::new(),
|
||||
};
|
||||
|
||||
for name in assignments.keys() {
|
||||
@ -453,11 +453,11 @@ fn resolve_assignments<'a>(
|
||||
}
|
||||
|
||||
struct AssignmentResolver<'a: 'b, 'b> {
|
||||
assignments: &'b BTreeMap<&'a str, Expression<'a>>,
|
||||
assignment_tokens: &'b BTreeMap<&'a str, Token<'a>>,
|
||||
assignments: &'b Map<&'a str, Expression<'a>>,
|
||||
assignment_tokens: &'b Map<&'a str, Token<'a>>,
|
||||
stack: Vec<&'a str>,
|
||||
seen: HashSet<&'a str>,
|
||||
evaluated: HashSet<&'a str>,
|
||||
seen: Set<&'a str>,
|
||||
evaluated: Set<&'a str>,
|
||||
}
|
||||
|
||||
impl<'a: 'b, 'b> AssignmentResolver<'a, 'b> {
|
||||
@ -507,12 +507,12 @@ impl<'a: 'b, 'b> AssignmentResolver<'a, 'b> {
|
||||
}
|
||||
|
||||
fn evaluate_assignments<'a>(
|
||||
assignments: &BTreeMap<&'a str, Expression<'a>>,
|
||||
overrides: &BTreeMap<&str, &str>,
|
||||
) -> Result<BTreeMap<&'a str, String>, RunError<'a>> {
|
||||
assignments: &Map<&'a str, Expression<'a>>,
|
||||
overrides: &Map<&str, &str>,
|
||||
) -> Result<Map<&'a str, String>, RunError<'a>> {
|
||||
let mut evaluator = Evaluator {
|
||||
evaluated: BTreeMap::new(),
|
||||
scope: &BTreeMap::new(),
|
||||
evaluated: Map::new(),
|
||||
scope: &Map::new(),
|
||||
assignments: assignments,
|
||||
overrides: overrides,
|
||||
};
|
||||
@ -525,17 +525,17 @@ fn evaluate_assignments<'a>(
|
||||
}
|
||||
|
||||
struct Evaluator<'a: 'b, 'b> {
|
||||
evaluated: BTreeMap<&'a str, String>,
|
||||
scope: &'b BTreeMap<&'a str, String>,
|
||||
assignments: &'b BTreeMap<&'a str, Expression<'a>>,
|
||||
overrides: &'b BTreeMap<&'b str, &'b str>,
|
||||
evaluated: Map<&'a str, String>,
|
||||
scope: &'b Map<&'a str, String>,
|
||||
assignments: &'b Map<&'a str, Expression<'a>>,
|
||||
overrides: &'b Map<&'b str, &'b str>,
|
||||
}
|
||||
|
||||
impl<'a, 'b> Evaluator<'a, 'b> {
|
||||
fn evaluate_line(
|
||||
&mut self,
|
||||
line: &[Fragment<'a>],
|
||||
arguments: &BTreeMap<&str, &str>
|
||||
arguments: &Map<&str, &str>
|
||||
) -> Result<String, RunError<'a>> {
|
||||
let mut evaluated = String::new();
|
||||
for fragment in line {
|
||||
@ -558,7 +558,7 @@ impl<'a, 'b> Evaluator<'a, 'b> {
|
||||
if let Some(value) = self.overrides.get(name) {
|
||||
self.evaluated.insert(name, value.to_string());
|
||||
} else {
|
||||
let value = try!(self.evaluate_expression(expression, &BTreeMap::new()));
|
||||
let value = try!(self.evaluate_expression(expression, &Map::new()));
|
||||
self.evaluated.insert(name, value);
|
||||
}
|
||||
} else {
|
||||
@ -573,7 +573,7 @@ impl<'a, 'b> Evaluator<'a, 'b> {
|
||||
fn evaluate_expression(
|
||||
&mut self,
|
||||
expression: &Expression<'a>,
|
||||
arguments: &BTreeMap<&str, &str>
|
||||
arguments: &Map<&str, &str>
|
||||
) -> Result<String, RunError<'a>> {
|
||||
Ok(match *expression {
|
||||
Expression::Variable{name, ..} => {
|
||||
@ -616,7 +616,6 @@ struct Error<'a> {
|
||||
#[derive(Debug, PartialEq)]
|
||||
enum ErrorKind<'a> {
|
||||
ArgumentShadowsVariable{argument: &'a str},
|
||||
BadName{name: &'a str},
|
||||
CircularRecipeDependency{recipe: &'a str, circle: Vec<&'a str>},
|
||||
CircularVariableDependency{variable: &'a str, circle: Vec<&'a str>},
|
||||
DependencyHasArguments{recipe: &'a str, dependency: &'a str},
|
||||
@ -698,9 +697,6 @@ impl<'a> Display for Error<'a> {
|
||||
try!(write!(f, "error: "));
|
||||
|
||||
match self.kind {
|
||||
ErrorKind::BadName{name} => {
|
||||
try!(writeln!(f, "name `{}` did not match /[a-z](-?[a-z0-9])*/", name));
|
||||
}
|
||||
ErrorKind::CircularRecipeDependency{recipe, ref circle} => {
|
||||
if circle.len() == 2 {
|
||||
try!(write!(f, "recipe `{}` depends on itself", recipe));
|
||||
@ -792,8 +788,8 @@ impl<'a> Display for Error<'a> {
|
||||
}
|
||||
|
||||
struct Justfile<'a> {
|
||||
recipes: BTreeMap<&'a str, Recipe<'a>>,
|
||||
assignments: BTreeMap<&'a str, Expression<'a>>,
|
||||
recipes: Map<&'a str, Recipe<'a>>,
|
||||
assignments: Map<&'a str, Expression<'a>>,
|
||||
}
|
||||
|
||||
impl<'a, 'b> Justfile<'a> where 'a: 'b {
|
||||
@ -821,7 +817,7 @@ impl<'a, 'b> Justfile<'a> where 'a: 'b {
|
||||
|
||||
fn run(
|
||||
&'a self,
|
||||
overrides: &BTreeMap<&'a str, &'a str>,
|
||||
overrides: &Map<&'a str, &'a str>,
|
||||
arguments: &[&'a str],
|
||||
dry_run: bool,
|
||||
evaluate: bool,
|
||||
@ -842,7 +838,7 @@ impl<'a, 'b> Justfile<'a> where 'a: 'b {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut ran = HashSet::new();
|
||||
let mut ran = Set::new();
|
||||
|
||||
for (i, argument) in arguments.iter().enumerate() {
|
||||
if let Some(recipe) = self.recipes.get(argument) {
|
||||
@ -885,8 +881,8 @@ impl<'a, 'b> Justfile<'a> where 'a: 'b {
|
||||
&'c self,
|
||||
recipe: &Recipe<'a>,
|
||||
arguments: &[&'a str],
|
||||
scope: &BTreeMap<&'c str, String>,
|
||||
ran: &mut HashSet<&'a str>,
|
||||
scope: &Map<&'c str, String>,
|
||||
ran: &mut Set<&'a str>,
|
||||
dry_run: bool,
|
||||
) -> Result<(), RunError> {
|
||||
for dependency_name in &recipe.dependencies {
|
||||
@ -1103,17 +1099,17 @@ fn token(pattern: &str) -> Regex {
|
||||
|
||||
fn tokenize(text: &str) -> Result<Vec<Token>, Error> {
|
||||
lazy_static! {
|
||||
static ref BACKTICK: Regex = token(r"`[^`\n\r]*`" );
|
||||
static ref COLON: Regex = token(r":" );
|
||||
static ref COMMENT: Regex = token(r"#([^!].*)?$" );
|
||||
static ref EOF: Regex = token(r"(?-m)$" );
|
||||
static ref EOL: Regex = token(r"\n|\r\n" );
|
||||
static ref EQUALS: Regex = token(r"=" );
|
||||
static ref INTERPOLATION_END: Regex = token(r"[}][}]" );
|
||||
static ref INTERPOLATION_START_TOKEN: Regex = token(r"[{][{]" );
|
||||
static ref NAME: Regex = token(r"([a-zA-Z0-9_-]+)" );
|
||||
static ref PLUS: Regex = token(r"[+]" );
|
||||
static ref STRING: Regex = token("\"" );
|
||||
static ref BACKTICK: Regex = token(r"`[^`\n\r]*`" );
|
||||
static ref COLON: Regex = token(r":" );
|
||||
static ref COMMENT: Regex = token(r"#([^!].*)?$" );
|
||||
static ref EOF: Regex = token(r"(?-m)$" );
|
||||
static ref EOL: Regex = token(r"\n|\r\n" );
|
||||
static ref EQUALS: Regex = token(r"=" );
|
||||
static ref INTERPOLATION_END: Regex = token(r"[}][}]" );
|
||||
static ref INTERPOLATION_START_TOKEN: Regex = token(r"[{][{]" );
|
||||
static ref NAME: Regex = token(r"([a-zA-Z_-][a-zA-Z0-9_-]*)");
|
||||
static ref PLUS: Regex = token(r"[+]" );
|
||||
static ref STRING: Regex = token("\"" );
|
||||
static ref INDENT: Regex = re(r"^([ \t]*)[^ \t\n\r]" );
|
||||
static ref INTERPOLATION_START: Regex = re(r"^[{][{]" );
|
||||
static ref LEADING_TEXT: Regex = re(r"^(?m)(.+?)[{][{]" );
|
||||
@ -1347,15 +1343,6 @@ fn tokenize(text: &str) -> Result<Vec<Token>, Error> {
|
||||
fn parse(text: &str) -> Result<Justfile, Error> {
|
||||
let tokens = try!(tokenize(text));
|
||||
let filtered: Vec<_> = tokens.into_iter().filter(|token| token.kind != Comment).collect();
|
||||
if let Some(token) = filtered.iter().find(|token| {
|
||||
lazy_static! {
|
||||
static ref GOOD_NAME: Regex = re("^[a-z](-?[a-z0-9])*$");
|
||||
}
|
||||
token.kind == Name && !GOOD_NAME.is_match(token.lexeme)
|
||||
}) {
|
||||
return Err(token.error(ErrorKind::BadName{name: token.lexeme}));
|
||||
}
|
||||
|
||||
let parser = Parser{
|
||||
text: text,
|
||||
tokens: filtered.into_iter().peekable()
|
||||
@ -1563,9 +1550,10 @@ impl<'a> Parser<'a> {
|
||||
}
|
||||
|
||||
fn file(mut self) -> Result<Justfile<'a>, Error<'a>> {
|
||||
let mut recipes = BTreeMap::<&str, Recipe>::new();
|
||||
let mut assignments = BTreeMap::<&str, Expression>::new();
|
||||
let mut assignment_tokens = BTreeMap::<&str, Token<'a>>::new();
|
||||
let mut recipes = Map::<&str, Recipe>::new();
|
||||
let mut assignments = Map::<&str, Expression>::new();
|
||||
let mut assignment_tokens = Map::<&str, Token<'a>>::new();
|
||||
let mut exports = Set::<&str>::new();
|
||||
|
||||
loop {
|
||||
match self.tokens.next() {
|
||||
|
61
src/unit.rs
61
src/unit.rs
@ -2,7 +2,7 @@ extern crate tempdir;
|
||||
|
||||
use super::{Token, Error, ErrorKind, Justfile, RunError};
|
||||
use super::TokenKind::*;
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::BTreeMap as Map;
|
||||
|
||||
fn tokenize_success(text: &str, expected_summary: &str) {
|
||||
let tokens = super::tokenize(text).unwrap();
|
||||
@ -579,7 +579,7 @@ fn conjoin_and() {
|
||||
|
||||
#[test]
|
||||
fn unknown_recipes() {
|
||||
match parse_success("a:\nb:\nc:").run(&BTreeMap::new(), &["a", "x", "y", "z"], false, false).unwrap_err() {
|
||||
match parse_success("a:\nb:\nc:").run(&Map::new(), &["a", "x", "y", "z"], false, false).unwrap_err() {
|
||||
RunError::UnknownRecipes{recipes} => assert_eq!(recipes, &["x", "y", "z"]),
|
||||
other => panic!("expected an unknown recipe error, but got: {}", other),
|
||||
}
|
||||
@ -603,49 +603,6 @@ fn extra_whitespace() {
|
||||
parse_success("a:\n #!\n print(1)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bad_recipe_names() {
|
||||
// We are extra strict with names. Although the tokenizer
|
||||
// will tokenize anything that matches /[a-zA-Z0-9_-]+/
|
||||
// as a name, we throw an error if names do not match
|
||||
// / [a-z](-?[a-z])* /. This is to support future expansion
|
||||
// of justfile and command line syntax.
|
||||
fn bad_name(text: &str, name: &str, index: usize, line: usize, column: usize) {
|
||||
parse_error(text, Error {
|
||||
text: text,
|
||||
index: index,
|
||||
line: line,
|
||||
column: column,
|
||||
width: Some(name.len()),
|
||||
kind: ErrorKind::BadName{name: name}
|
||||
});
|
||||
}
|
||||
|
||||
bad_name("-a", "-a", 0, 0, 0);
|
||||
bad_name("_a", "_a", 0, 0, 0);
|
||||
bad_name("a-", "a-", 0, 0, 0);
|
||||
bad_name("a_", "a_", 0, 0, 0);
|
||||
bad_name("a__a", "a__a", 0, 0, 0);
|
||||
bad_name("a--a", "a--a", 0, 0, 0);
|
||||
bad_name("a: a--", "a--", 3, 0, 3);
|
||||
bad_name("a: 9a", "9a", 3, 0, 3);
|
||||
bad_name("a: 9a", "9a", 3, 0, 3);
|
||||
bad_name("a:\nZ:", "Z", 3, 1, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bad_interpolation_variable_name() {
|
||||
let text = "a:\n echo {{hello--hello}}";
|
||||
parse_error(text, Error {
|
||||
text: text,
|
||||
index: 11,
|
||||
line: 1,
|
||||
column: 8,
|
||||
width: Some(12),
|
||||
kind: ErrorKind::BadName{name: "hello--hello"}
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn interpolation_outside_of_recipe() {
|
||||
let text = "{{";
|
||||
@ -747,7 +704,7 @@ a:
|
||||
x
|
||||
";
|
||||
|
||||
match parse_success(text).run(&BTreeMap::new(), &["a"], false, false).unwrap_err() {
|
||||
match parse_success(text).run(&Map::new(), &["a"], false, false).unwrap_err() {
|
||||
RunError::Code{recipe, code} => {
|
||||
assert_eq!(recipe, "a");
|
||||
assert_eq!(code, 200);
|
||||
@ -758,7 +715,7 @@ a:
|
||||
|
||||
#[test]
|
||||
fn code_error() {
|
||||
match parse_success("fail:\n @function x { return 100; }; x").run(&BTreeMap::new(), &["fail"], false, false).unwrap_err() {
|
||||
match parse_success("fail:\n @function x { return 100; }; x").run(&Map::new(), &["fail"], false, false).unwrap_err() {
|
||||
RunError::Code{recipe, code} => {
|
||||
assert_eq!(recipe, "fail");
|
||||
assert_eq!(code, 100);
|
||||
@ -773,7 +730,7 @@ fn run_args() {
|
||||
a return code:
|
||||
@function x { {{return}} {{code + "0"}}; }; x"#;
|
||||
|
||||
match parse_success(text).run(&BTreeMap::new(), &["a", "return", "15"], false, false).unwrap_err() {
|
||||
match parse_success(text).run(&Map::new(), &["a", "return", "15"], false, false).unwrap_err() {
|
||||
RunError::Code{recipe, code} => {
|
||||
assert_eq!(recipe, "a");
|
||||
assert_eq!(code, 150);
|
||||
@ -784,7 +741,7 @@ a return code:
|
||||
|
||||
#[test]
|
||||
fn missing_args() {
|
||||
match parse_success("a b c d:").run(&BTreeMap::new(), &["a", "b", "c"], false, false).unwrap_err() {
|
||||
match parse_success("a b c d:").run(&Map::new(), &["a", "b", "c"], false, false).unwrap_err() {
|
||||
RunError::ArgumentCountMismatch{recipe, found, expected} => {
|
||||
assert_eq!(recipe, "a");
|
||||
assert_eq!(found, 2);
|
||||
@ -796,7 +753,7 @@ fn missing_args() {
|
||||
|
||||
#[test]
|
||||
fn missing_default() {
|
||||
match parse_success("a b c d:\n echo {{b}}{{c}}{{d}}").run(&BTreeMap::new(), &["a"], false, false).unwrap_err() {
|
||||
match parse_success("a b c d:\n echo {{b}}{{c}}{{d}}").run(&Map::new(), &["a"], false, false).unwrap_err() {
|
||||
RunError::ArgumentCountMismatch{recipe, found, expected} => {
|
||||
assert_eq!(recipe, "a");
|
||||
assert_eq!(found, 0);
|
||||
@ -808,7 +765,7 @@ fn missing_default() {
|
||||
|
||||
#[test]
|
||||
fn backtick_code() {
|
||||
match parse_success("a:\n echo {{`function f { return 100; }; f`}}").run(&BTreeMap::new(), &["a"], false, false).unwrap_err() {
|
||||
match parse_success("a:\n echo {{`function f { return 100; }; f`}}").run(&Map::new(), &["a"], false, false).unwrap_err() {
|
||||
RunError::BacktickCode{code, token} => {
|
||||
assert_eq!(code, 100);
|
||||
assert_eq!(token.lexeme, "`function f { return 100; }; f`");
|
||||
@ -819,7 +776,7 @@ fn backtick_code() {
|
||||
|
||||
#[test]
|
||||
fn unknown_overrides() {
|
||||
let mut overrides = BTreeMap::new();
|
||||
let mut overrides = Map::new();
|
||||
overrides.insert("foo", "bar");
|
||||
overrides.insert("baz", "bob");
|
||||
match parse_success("a:\n echo {{`function f { return 100; }; f`}}")
|
||||
|
Loading…
Reference in New Issue
Block a user