parent
caf2570bb2
commit
1856646a9c
116
src/lib.rs
116
src/lib.rs
@ -424,7 +424,7 @@ fn resolve_recipes<'a>(
|
|||||||
recipes: &Map<&'a str, Recipe<'a>>,
|
recipes: &Map<&'a str, Recipe<'a>>,
|
||||||
assignments: &Map<&'a str, Expression<'a>>,
|
assignments: &Map<&'a str, Expression<'a>>,
|
||||||
text: &'a str,
|
text: &'a str,
|
||||||
) -> Result<(), Error<'a>> {
|
) -> Result<(), CompileError<'a>> {
|
||||||
let mut resolver = Resolver {
|
let mut resolver = Resolver {
|
||||||
seen: Set::new(),
|
seen: Set::new(),
|
||||||
stack: vec![],
|
stack: vec![],
|
||||||
@ -455,7 +455,7 @@ fn resolve_recipes<'a>(
|
|||||||
// of the struct, and the second being the lifetime of the tokens
|
// of the struct, and the second being the lifetime of the tokens
|
||||||
// that it contains
|
// that it contains
|
||||||
let error = variable.error(ErrorKind::UndefinedVariable{variable: name});
|
let error = variable.error(ErrorKind::UndefinedVariable{variable: name});
|
||||||
return Err(Error {
|
return Err(CompileError {
|
||||||
text: text,
|
text: text,
|
||||||
index: error.index,
|
index: error.index,
|
||||||
line: error.line,
|
line: error.line,
|
||||||
@ -483,7 +483,7 @@ struct Resolver<'a: 'b, 'b> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'b> Resolver<'a, 'b> {
|
impl<'a, 'b> Resolver<'a, 'b> {
|
||||||
fn resolve(&mut self, recipe: &Recipe<'a>) -> Result<(), Error<'a>> {
|
fn resolve(&mut self, recipe: &Recipe<'a>) -> Result<(), CompileError<'a>> {
|
||||||
if self.resolved.contains(recipe.name) {
|
if self.resolved.contains(recipe.name) {
|
||||||
return Ok(())
|
return Ok(())
|
||||||
}
|
}
|
||||||
@ -519,7 +519,7 @@ impl<'a, 'b> Resolver<'a, 'b> {
|
|||||||
fn resolve_assignments<'a>(
|
fn resolve_assignments<'a>(
|
||||||
assignments: &Map<&'a str, Expression<'a>>,
|
assignments: &Map<&'a str, Expression<'a>>,
|
||||||
assignment_tokens: &Map<&'a str, Token<'a>>,
|
assignment_tokens: &Map<&'a str, Token<'a>>,
|
||||||
) -> Result<(), Error<'a>> {
|
) -> Result<(), CompileError<'a>> {
|
||||||
|
|
||||||
let mut resolver = AssignmentResolver {
|
let mut resolver = AssignmentResolver {
|
||||||
assignments: assignments,
|
assignments: assignments,
|
||||||
@ -545,7 +545,7 @@ struct AssignmentResolver<'a: 'b, 'b> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a: 'b, 'b> AssignmentResolver<'a, 'b> {
|
impl<'a: 'b, 'b> AssignmentResolver<'a, 'b> {
|
||||||
fn resolve_assignment(&mut self, name: &'a str) -> Result<(), Error<'a>> {
|
fn resolve_assignment(&mut self, name: &'a str) -> Result<(), CompileError<'a>> {
|
||||||
if self.evaluated.contains(name) {
|
if self.evaluated.contains(name) {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
@ -562,7 +562,7 @@ impl<'a: 'b, 'b> AssignmentResolver<'a, 'b> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_expression(&mut self, expression: &Expression<'a>) -> Result<(), Error<'a>> {
|
fn resolve_expression(&mut self, expression: &Expression<'a>) -> Result<(), CompileError<'a>> {
|
||||||
match *expression {
|
match *expression {
|
||||||
Expression::Variable{name, ref token} => {
|
Expression::Variable{name, ref token} => {
|
||||||
if self.evaluated.contains(name) {
|
if self.evaluated.contains(name) {
|
||||||
@ -695,7 +695,7 @@ impl<'a, 'b> Evaluator<'a, 'b> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
struct Error<'a> {
|
struct CompileError<'a> {
|
||||||
text: &'a str,
|
text: &'a str,
|
||||||
index: usize,
|
index: usize,
|
||||||
line: usize,
|
line: usize,
|
||||||
@ -728,8 +728,8 @@ enum ErrorKind<'a> {
|
|||||||
UnterminatedString,
|
UnterminatedString,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn internal_error(message: String) -> Error<'static> {
|
fn internal_error(message: String) -> CompileError<'static> {
|
||||||
Error {
|
CompileError {
|
||||||
text: "",
|
text: "",
|
||||||
index: 0,
|
index: 0,
|
||||||
line: 0,
|
line: 0,
|
||||||
@ -773,7 +773,7 @@ struct CookedString<'a> {
|
|||||||
cooked: String,
|
cooked: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cook_string<'a>(token: &Token<'a>) -> Result<CookedString<'a>, Error<'a>> {
|
fn cook_string<'a>(token: &Token<'a>) -> Result<CookedString<'a>, CompileError<'a>> {
|
||||||
let raw = &token.lexeme[1..token.lexeme.len()-1];
|
let raw = &token.lexeme[1..token.lexeme.len()-1];
|
||||||
|
|
||||||
if let RawString = token.kind {
|
if let RawString = token.kind {
|
||||||
@ -929,15 +929,16 @@ fn maybe_bold(colors: bool) -> ansi_term::Style {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Display for Error<'a> {
|
impl<'a> Display for CompileError<'a> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||||
|
use ErrorKind::*;
|
||||||
let red = maybe_red(f.alternate());
|
let red = maybe_red(f.alternate());
|
||||||
let bold = maybe_bold(f.alternate());
|
let bold = maybe_bold(f.alternate());
|
||||||
|
|
||||||
write!(f, "{} {}", red.paint("error:"), bold.prefix())?;
|
write!(f, "{} {}", red.paint("error:"), bold.prefix())?;
|
||||||
|
|
||||||
match self.kind {
|
match self.kind {
|
||||||
ErrorKind::CircularRecipeDependency{recipe, ref circle} => {
|
CircularRecipeDependency{recipe, ref circle} => {
|
||||||
if circle.len() == 2 {
|
if circle.len() == 2 {
|
||||||
write!(f, "recipe `{}` depends on itself", recipe)?;
|
write!(f, "recipe `{}` depends on itself", recipe)?;
|
||||||
} else {
|
} else {
|
||||||
@ -945,7 +946,7 @@ impl<'a> Display for Error<'a> {
|
|||||||
recipe, circle.join(" -> "))?;
|
recipe, circle.join(" -> "))?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ErrorKind::CircularVariableDependency{variable, ref circle} => {
|
CircularVariableDependency{variable, ref circle} => {
|
||||||
if circle.len() == 2 {
|
if circle.len() == 2 {
|
||||||
writeln!(f, "variable `{}` depends on its own value: `{}`",
|
writeln!(f, "variable `{}` depends on its own value: `{}`",
|
||||||
variable, circle.join(" -> "))?;
|
variable, circle.join(" -> "))?;
|
||||||
@ -954,68 +955,68 @@ impl<'a> Display for Error<'a> {
|
|||||||
variable, circle.join(" -> "))?;
|
variable, circle.join(" -> "))?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ErrorKind::InvalidEscapeSequence{character} => {
|
InvalidEscapeSequence{character} => {
|
||||||
writeln!(f, "`\\{}` is not a valid escape sequence",
|
writeln!(f, "`\\{}` is not a valid escape sequence",
|
||||||
character.escape_default().collect::<String>())?;
|
character.escape_default().collect::<String>())?;
|
||||||
}
|
}
|
||||||
ErrorKind::DuplicateParameter{recipe, parameter} => {
|
DuplicateParameter{recipe, parameter} => {
|
||||||
writeln!(f, "recipe `{}` has duplicate parameter `{}`", recipe, parameter)?;
|
writeln!(f, "recipe `{}` has duplicate parameter `{}`", recipe, parameter)?;
|
||||||
}
|
}
|
||||||
ErrorKind::DuplicateVariable{variable} => {
|
DuplicateVariable{variable} => {
|
||||||
writeln!(f, "variable `{}` is has multiple definitions", variable)?;
|
writeln!(f, "variable `{}` is has multiple definitions", variable)?;
|
||||||
}
|
}
|
||||||
ErrorKind::UnexpectedToken{ref expected, found} => {
|
UnexpectedToken{ref expected, found} => {
|
||||||
writeln!(f, "expected {} but found {}", Or(expected), found)?;
|
writeln!(f, "expected {} but found {}", Or(expected), found)?;
|
||||||
}
|
}
|
||||||
ErrorKind::DuplicateDependency{recipe, dependency} => {
|
DuplicateDependency{recipe, dependency} => {
|
||||||
writeln!(f, "recipe `{}` has duplicate dependency `{}`", recipe, dependency)?;
|
writeln!(f, "recipe `{}` has duplicate dependency `{}`", recipe, dependency)?;
|
||||||
}
|
}
|
||||||
ErrorKind::DuplicateRecipe{recipe, first} => {
|
DuplicateRecipe{recipe, first} => {
|
||||||
writeln!(f, "recipe `{}` first defined on line {} is redefined on line {}",
|
writeln!(f, "recipe `{}` first defined on line {} is redefined on line {}",
|
||||||
recipe, first, self.line)?;
|
recipe, first, self.line)?;
|
||||||
}
|
}
|
||||||
ErrorKind::DependencyHasParameters{recipe, dependency} => {
|
DependencyHasParameters{recipe, dependency} => {
|
||||||
writeln!(f, "recipe `{}` depends on `{}` which requires arguments. \
|
writeln!(f, "recipe `{}` depends on `{}` which requires arguments. \
|
||||||
dependencies may not require arguments", recipe, dependency)?;
|
dependencies may not require arguments", recipe, dependency)?;
|
||||||
}
|
}
|
||||||
ErrorKind::ParameterShadowsVariable{parameter} => {
|
ParameterShadowsVariable{parameter} => {
|
||||||
writeln!(f, "parameter `{}` shadows variable of the same name", parameter)?;
|
writeln!(f, "parameter `{}` shadows variable of the same name", parameter)?;
|
||||||
}
|
}
|
||||||
ErrorKind::RequiredParameterFollowsDefaultParameter{parameter} => {
|
RequiredParameterFollowsDefaultParameter{parameter} => {
|
||||||
writeln!(f, "non-default parameter `{}` follows default parameter", parameter)?;
|
writeln!(f, "non-default parameter `{}` follows default parameter", parameter)?;
|
||||||
}
|
}
|
||||||
ErrorKind::MixedLeadingWhitespace{whitespace} => {
|
MixedLeadingWhitespace{whitespace} => {
|
||||||
writeln!(f,
|
writeln!(f,
|
||||||
"found a mix of tabs and spaces in leading whitespace: `{}`\n\
|
"found a mix of tabs and spaces in leading whitespace: `{}`\n\
|
||||||
leading whitespace may consist of tabs or spaces, but not both",
|
leading whitespace may consist of tabs or spaces, but not both",
|
||||||
show_whitespace(whitespace)
|
show_whitespace(whitespace)
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
ErrorKind::ExtraLeadingWhitespace => {
|
ExtraLeadingWhitespace => {
|
||||||
writeln!(f, "recipe line has extra leading whitespace")?;
|
writeln!(f, "recipe line has extra leading whitespace")?;
|
||||||
}
|
}
|
||||||
ErrorKind::InconsistentLeadingWhitespace{expected, found} => {
|
InconsistentLeadingWhitespace{expected, found} => {
|
||||||
writeln!(f,
|
writeln!(f,
|
||||||
"inconsistant leading whitespace: recipe started with `{}` but found line with `{}`:",
|
"inconsistant leading whitespace: recipe started with `{}` but found line with `{}`:",
|
||||||
show_whitespace(expected), show_whitespace(found)
|
show_whitespace(expected), show_whitespace(found)
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
ErrorKind::OuterShebang => {
|
OuterShebang => {
|
||||||
writeln!(f, "a shebang `#!` is reserved syntax outside of recipes")?;
|
writeln!(f, "a shebang `#!` is reserved syntax outside of recipes")?;
|
||||||
}
|
}
|
||||||
ErrorKind::UnknownDependency{recipe, unknown} => {
|
UnknownDependency{recipe, unknown} => {
|
||||||
writeln!(f, "recipe `{}` has unknown dependency `{}`", recipe, unknown)?;
|
writeln!(f, "recipe `{}` has unknown dependency `{}`", recipe, unknown)?;
|
||||||
}
|
}
|
||||||
ErrorKind::UndefinedVariable{variable} => {
|
UndefinedVariable{variable} => {
|
||||||
writeln!(f, "variable `{}` not defined", variable)?;
|
writeln!(f, "variable `{}` not defined", variable)?;
|
||||||
}
|
}
|
||||||
ErrorKind::UnknownStartOfToken => {
|
UnknownStartOfToken => {
|
||||||
writeln!(f, "unknown start of token:")?;
|
writeln!(f, "unknown start of token:")?;
|
||||||
}
|
}
|
||||||
ErrorKind::UnterminatedString => {
|
UnterminatedString => {
|
||||||
writeln!(f, "unterminated string")?;
|
writeln!(f, "unterminated string")?;
|
||||||
}
|
}
|
||||||
ErrorKind::InternalError{ref message} => {
|
InternalError{ref message} => {
|
||||||
writeln!(f, "internal error, this may indicate a bug in just: {}\n\
|
writeln!(f, "internal error, this may indicate a bug in just: {}\n\
|
||||||
consider filing an issue: https://github.com/casey/just/issues/new",
|
consider filing an issue: https://github.com/casey/just/issues/new",
|
||||||
message)?;
|
message)?;
|
||||||
@ -1213,6 +1214,7 @@ enum RunError<'a> {
|
|||||||
|
|
||||||
impl<'a> Display for RunError<'a> {
|
impl<'a> Display for RunError<'a> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||||
|
use RunError::*;
|
||||||
let red = maybe_red(f.alternate());
|
let red = maybe_red(f.alternate());
|
||||||
let bold = maybe_bold(f.alternate());
|
let bold = maybe_bold(f.alternate());
|
||||||
write!(f, "{} {}", red.paint("error:"), bold.prefix())?;
|
write!(f, "{} {}", red.paint("error:"), bold.prefix())?;
|
||||||
@ -1220,23 +1222,23 @@ impl<'a> Display for RunError<'a> {
|
|||||||
let mut error_token = None;
|
let mut error_token = None;
|
||||||
|
|
||||||
match *self {
|
match *self {
|
||||||
RunError::UnknownRecipes{ref recipes, ref suggestion} => {
|
UnknownRecipes{ref recipes, ref suggestion} => {
|
||||||
write!(f, "Justfile does not contain recipe{} {}.",
|
write!(f, "Justfile does not contain recipe{} {}.",
|
||||||
maybe_s(recipes.len()), Or(&ticks(&recipes)))?;
|
maybe_s(recipes.len()), Or(&ticks(&recipes)))?;
|
||||||
if let Some(suggestion) = *suggestion {
|
if let Some(suggestion) = *suggestion {
|
||||||
write!(f, "\nDid you mean `{}`?", suggestion)?;
|
write!(f, "\nDid you mean `{}`?", suggestion)?;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
RunError::UnknownOverrides{ref overrides} => {
|
UnknownOverrides{ref overrides} => {
|
||||||
write!(f, "Variable{} {} overridden on the command line but not present in justfile",
|
write!(f, "Variable{} {} overridden on the command line but not present in justfile",
|
||||||
maybe_s(overrides.len()),
|
maybe_s(overrides.len()),
|
||||||
And(&overrides.iter().map(Tick).collect::<Vec<_>>()))?;
|
And(&overrides.iter().map(Tick).collect::<Vec<_>>()))?;
|
||||||
},
|
},
|
||||||
RunError::NonLeadingRecipeWithParameters{recipe} => {
|
NonLeadingRecipeWithParameters{recipe} => {
|
||||||
write!(f, "Recipe `{}` takes arguments and so must be the first and only recipe \
|
write!(f, "Recipe `{}` takes arguments and so must be the first and only recipe \
|
||||||
specified on the command line", recipe)?;
|
specified on the command line", recipe)?;
|
||||||
},
|
},
|
||||||
RunError::ArgumentCountMismatch{recipe, found, min, max} => {
|
ArgumentCountMismatch{recipe, found, min, max} => {
|
||||||
if min == max {
|
if min == max {
|
||||||
let expected = min;
|
let expected = min;
|
||||||
write!(f, "Recipe `{}` got {} argument{} but {}takes {}",
|
write!(f, "Recipe `{}` got {} argument{} but {}takes {}",
|
||||||
@ -1250,16 +1252,16 @@ impl<'a> Display for RunError<'a> {
|
|||||||
recipe, found, maybe_s(found), max)?;
|
recipe, found, maybe_s(found), max)?;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
RunError::Code{recipe, code} => {
|
Code{recipe, code} => {
|
||||||
write!(f, "Recipe `{}` failed with exit code {}", recipe, code)?;
|
write!(f, "Recipe `{}` failed with exit code {}", recipe, code)?;
|
||||||
},
|
},
|
||||||
RunError::Signal{recipe, signal} => {
|
Signal{recipe, signal} => {
|
||||||
write!(f, "Recipe `{}` wast terminated by signal {}", recipe, signal)?;
|
write!(f, "Recipe `{}` wast terminated by signal {}", recipe, signal)?;
|
||||||
}
|
}
|
||||||
RunError::UnknownFailure{recipe} => {
|
UnknownFailure{recipe} => {
|
||||||
write!(f, "Recipe `{}` failed for an unknown reason", recipe)?;
|
write!(f, "Recipe `{}` failed for an unknown reason", recipe)?;
|
||||||
},
|
},
|
||||||
RunError::IoError{recipe, ref io_error} => {
|
IoError{recipe, ref io_error} => {
|
||||||
match io_error.kind() {
|
match io_error.kind() {
|
||||||
io::ErrorKind::NotFound => write!(f,
|
io::ErrorKind::NotFound => write!(f,
|
||||||
"Recipe `{}` could not be run because just could not find `sh` the command:\n{}",
|
"Recipe `{}` could not be run because just could not find `sh` the command:\n{}",
|
||||||
@ -1271,23 +1273,23 @@ impl<'a> Display for RunError<'a> {
|
|||||||
launching `sh`:\n{}", recipe, io_error),
|
launching `sh`:\n{}", recipe, io_error),
|
||||||
}?;
|
}?;
|
||||||
},
|
},
|
||||||
RunError::TmpdirIoError{recipe, ref io_error} =>
|
TmpdirIoError{recipe, ref io_error} =>
|
||||||
write!(f, "Recipe `{}` could not be run because of an IO error while trying \
|
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{}",
|
to create a temporary directory or write a file to that directory`:\n{}",
|
||||||
recipe, io_error)?,
|
recipe, io_error)?,
|
||||||
RunError::BacktickCode{code, ref token} => {
|
BacktickCode{code, ref token} => {
|
||||||
write!(f, "backtick failed with exit code {}\n", code)?;
|
write!(f, "backtick failed with exit code {}\n", code)?;
|
||||||
error_token = Some(token);
|
error_token = Some(token);
|
||||||
}
|
}
|
||||||
RunError::BacktickSignal{ref token, signal} => {
|
BacktickSignal{ref token, signal} => {
|
||||||
write!(f, "backtick was terminated by signal {}", signal)?;
|
write!(f, "backtick was terminated by signal {}", signal)?;
|
||||||
error_token = Some(token);
|
error_token = Some(token);
|
||||||
}
|
}
|
||||||
RunError::BacktickUnknownFailure{ref token} => {
|
BacktickUnknownFailure{ref token} => {
|
||||||
write!(f, "backtick failed for an uknown reason")?;
|
write!(f, "backtick failed for an uknown reason")?;
|
||||||
error_token = Some(token);
|
error_token = Some(token);
|
||||||
}
|
}
|
||||||
RunError::BacktickIoError{ref token, ref io_error} => {
|
BacktickIoError{ref token, ref io_error} => {
|
||||||
match io_error.kind() {
|
match io_error.kind() {
|
||||||
io::ErrorKind::NotFound => write!(
|
io::ErrorKind::NotFound => write!(
|
||||||
f, "backtick could not be run because just could not find `sh` the command:\n{}",
|
f, "backtick could not be run because just could not find `sh` the command:\n{}",
|
||||||
@ -1299,11 +1301,11 @@ impl<'a> Display for RunError<'a> {
|
|||||||
}?;
|
}?;
|
||||||
error_token = Some(token);
|
error_token = Some(token);
|
||||||
}
|
}
|
||||||
RunError::BacktickUtf8Error{ref token, ref utf8_error} => {
|
BacktickUtf8Error{ref token, ref utf8_error} => {
|
||||||
write!(f, "backtick succeeded but stdout was not utf8: {}", utf8_error)?;
|
write!(f, "backtick succeeded but stdout was not utf8: {}", utf8_error)?;
|
||||||
error_token = Some(token);
|
error_token = Some(token);
|
||||||
}
|
}
|
||||||
RunError::InternalError{ref message} => {
|
InternalError{ref message} => {
|
||||||
write!(f, "internal error, this may indicate a bug in just: {} \
|
write!(f, "internal error, this may indicate a bug in just: {} \
|
||||||
consider filing an issue: https://github.com/casey/just/issues/new",
|
consider filing an issue: https://github.com/casey/just/issues/new",
|
||||||
message)?;
|
message)?;
|
||||||
@ -1332,8 +1334,8 @@ struct Token<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Token<'a> {
|
impl<'a> Token<'a> {
|
||||||
fn error(&self, kind: ErrorKind<'a>) -> Error<'a> {
|
fn error(&self, kind: ErrorKind<'a>) -> CompileError<'a> {
|
||||||
Error {
|
CompileError {
|
||||||
text: self.text,
|
text: self.text,
|
||||||
index: self.index + self.prefix.len(),
|
index: self.index + self.prefix.len(),
|
||||||
line: self.line,
|
line: self.line,
|
||||||
@ -1397,7 +1399,7 @@ fn token(pattern: &str) -> Regex {
|
|||||||
re(&s)
|
re(&s)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tokenize(text: &str) -> Result<Vec<Token>, Error> {
|
fn tokenize(text: &str) -> Result<Vec<Token>, CompileError> {
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref BACKTICK: Regex = token(r"`[^`\n\r]*`" );
|
static ref BACKTICK: Regex = token(r"`[^`\n\r]*`" );
|
||||||
static ref COLON: Regex = token(r":" );
|
static ref COLON: Regex = token(r":" );
|
||||||
@ -1439,7 +1441,7 @@ fn tokenize(text: &str) -> Result<Vec<Token>, Error> {
|
|||||||
|
|
||||||
macro_rules! error {
|
macro_rules! error {
|
||||||
($kind:expr) => {{
|
($kind:expr) => {{
|
||||||
Err(Error {
|
Err(CompileError {
|
||||||
text: text,
|
text: text,
|
||||||
index: index,
|
index: index,
|
||||||
line: line,
|
line: line,
|
||||||
@ -1644,7 +1646,7 @@ fn tokenize(text: &str) -> Result<Vec<Token>, Error> {
|
|||||||
Ok(tokens)
|
Ok(tokens)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse(text: &str) -> Result<Justfile, Error> {
|
fn parse(text: &str) -> Result<Justfile, CompileError> {
|
||||||
let tokens = tokenize(text)?;
|
let tokens = tokenize(text)?;
|
||||||
let filtered: Vec<_> = tokens.into_iter().filter(|token| token.kind != Comment).collect();
|
let filtered: Vec<_> = tokens.into_iter().filter(|token| token.kind != Comment).collect();
|
||||||
let parser = Parser {
|
let parser = Parser {
|
||||||
@ -1716,14 +1718,14 @@ impl<'a> Parser<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unexpected_token(&self, found: &Token<'a>, expected: &[TokenKind]) -> Error<'a> {
|
fn unexpected_token(&self, found: &Token<'a>, expected: &[TokenKind]) -> CompileError<'a> {
|
||||||
found.error(ErrorKind::UnexpectedToken {
|
found.error(ErrorKind::UnexpectedToken {
|
||||||
expected: expected.to_vec(),
|
expected: expected.to_vec(),
|
||||||
found: found.kind,
|
found: found.kind,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn recipe(&mut self, name: Token<'a>) -> Result<(), Error<'a>> {
|
fn recipe(&mut self, name: Token<'a>) -> Result<(), CompileError<'a>> {
|
||||||
if let Some(recipe) = self.recipes.get(name.lexeme) {
|
if let Some(recipe) = self.recipes.get(name.lexeme) {
|
||||||
return Err(name.error(ErrorKind::DuplicateRecipe {
|
return Err(name.error(ErrorKind::DuplicateRecipe {
|
||||||
recipe: recipe.name,
|
recipe: recipe.name,
|
||||||
@ -1851,7 +1853,7 @@ impl<'a> Parser<'a> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expression(&mut self, interpolation: bool) -> Result<Expression<'a>, Error<'a>> {
|
fn expression(&mut self, interpolation: bool) -> Result<Expression<'a>, CompileError<'a>> {
|
||||||
let first = self.tokens.next().unwrap();
|
let first = self.tokens.next().unwrap();
|
||||||
let lhs = match first.kind {
|
let lhs = match first.kind {
|
||||||
Name => Expression::Variable {name: first.lexeme, token: first},
|
Name => Expression::Variable {name: first.lexeme, token: first},
|
||||||
@ -1881,7 +1883,7 @@ impl<'a> Parser<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn assignment(&mut self, name: Token<'a>, export: bool) -> Result<(), Error<'a>> {
|
fn assignment(&mut self, name: Token<'a>, export: bool) -> Result<(), CompileError<'a>> {
|
||||||
if self.assignments.contains_key(name.lexeme) {
|
if self.assignments.contains_key(name.lexeme) {
|
||||||
return Err(name.error(ErrorKind::DuplicateVariable {variable: name.lexeme}));
|
return Err(name.error(ErrorKind::DuplicateVariable {variable: name.lexeme}));
|
||||||
}
|
}
|
||||||
@ -1894,7 +1896,7 @@ impl<'a> Parser<'a> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn file(mut self) -> Result<Justfile<'a>, Error<'a>> {
|
fn file(mut self) -> Result<Justfile<'a>, CompileError<'a>> {
|
||||||
loop {
|
loop {
|
||||||
match self.tokens.next() {
|
match self.tokens.next() {
|
||||||
Some(token) => match token.kind {
|
Some(token) => match token.kind {
|
||||||
@ -1918,7 +1920,7 @@ impl<'a> Parser<'a> {
|
|||||||
})),
|
})),
|
||||||
_ => return return Err(self.unexpected_token(&token, &[Name])),
|
_ => return return Err(self.unexpected_token(&token, &[Name])),
|
||||||
},
|
},
|
||||||
None => return Err(Error {
|
None => return Err(CompileError {
|
||||||
text: self.text,
|
text: self.text,
|
||||||
index: 0,
|
index: 0,
|
||||||
line: 0,
|
line: 0,
|
||||||
|
68
src/unit.rs
68
src/unit.rs
@ -1,7 +1,7 @@
|
|||||||
extern crate tempdir;
|
extern crate tempdir;
|
||||||
extern crate brev;
|
extern crate brev;
|
||||||
|
|
||||||
use super::{Token, Error, ErrorKind, Justfile, RunError, RunOptions};
|
use super::{Token, CompileError, ErrorKind, Justfile, RunError, RunOptions};
|
||||||
use super::TokenKind::*;
|
use super::TokenKind::*;
|
||||||
|
|
||||||
fn tokenize_success(text: &str, expected_summary: &str) {
|
fn tokenize_success(text: &str, expected_summary: &str) {
|
||||||
@ -19,7 +19,7 @@ fn tokenize_success(text: &str, expected_summary: &str) {
|
|||||||
assert_eq!(text, roundtrip);
|
assert_eq!(text, roundtrip);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tokenize_error(text: &str, expected: Error) {
|
fn tokenize_error(text: &str, expected: CompileError) {
|
||||||
if let Err(error) = super::tokenize(text) {
|
if let Err(error) = super::tokenize(text) {
|
||||||
assert_eq!(error.text, expected.text);
|
assert_eq!(error.text, expected.text);
|
||||||
assert_eq!(error.index, expected.index);
|
assert_eq!(error.index, expected.index);
|
||||||
@ -72,7 +72,7 @@ fn parse_summary(input: &str, output: &str) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_error(text: &str, expected: Error) {
|
fn parse_error(text: &str, expected: CompileError) {
|
||||||
if let Err(error) = super::parse(text) {
|
if let Err(error) = super::parse(text) {
|
||||||
assert_eq!(error.text, expected.text);
|
assert_eq!(error.text, expected.text);
|
||||||
assert_eq!(error.index, expected.index);
|
assert_eq!(error.index, expected.index);
|
||||||
@ -192,7 +192,7 @@ fn tokenize_space_then_tab() {
|
|||||||
1
|
1
|
||||||
\t2
|
\t2
|
||||||
";
|
";
|
||||||
tokenize_error(text, Error {
|
tokenize_error(text, CompileError {
|
||||||
text: text,
|
text: text,
|
||||||
index: 9,
|
index: 9,
|
||||||
line: 3,
|
line: 3,
|
||||||
@ -209,7 +209,7 @@ fn tokenize_tabs_then_tab_space() {
|
|||||||
\t\t 1
|
\t\t 1
|
||||||
\t 2
|
\t 2
|
||||||
";
|
";
|
||||||
tokenize_error(text, Error {
|
tokenize_error(text, CompileError {
|
||||||
text: text,
|
text: text,
|
||||||
index: 12,
|
index: 12,
|
||||||
line: 3,
|
line: 3,
|
||||||
@ -222,7 +222,7 @@ fn tokenize_tabs_then_tab_space() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn tokenize_outer_shebang() {
|
fn tokenize_outer_shebang() {
|
||||||
let text = "#!/usr/bin/env bash";
|
let text = "#!/usr/bin/env bash";
|
||||||
tokenize_error(text, Error {
|
tokenize_error(text, CompileError {
|
||||||
text: text,
|
text: text,
|
||||||
index: 0,
|
index: 0,
|
||||||
line: 0,
|
line: 0,
|
||||||
@ -235,7 +235,7 @@ fn tokenize_outer_shebang() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn tokenize_unknown() {
|
fn tokenize_unknown() {
|
||||||
let text = "~";
|
let text = "~";
|
||||||
tokenize_error(text, Error {
|
tokenize_error(text, CompileError {
|
||||||
text: text,
|
text: text,
|
||||||
index: 0,
|
index: 0,
|
||||||
line: 0,
|
line: 0,
|
||||||
@ -382,7 +382,7 @@ r#"a:
|
|||||||
#[test]
|
#[test]
|
||||||
fn missing_colon() {
|
fn missing_colon() {
|
||||||
let text = "a b c\nd e f";
|
let text = "a b c\nd e f";
|
||||||
parse_error(text, Error {
|
parse_error(text, CompileError {
|
||||||
text: text,
|
text: text,
|
||||||
index: 5,
|
index: 5,
|
||||||
line: 0,
|
line: 0,
|
||||||
@ -395,7 +395,7 @@ fn missing_colon() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn missing_default_eol() {
|
fn missing_default_eol() {
|
||||||
let text = "hello arg=\n";
|
let text = "hello arg=\n";
|
||||||
parse_error(text, Error {
|
parse_error(text, CompileError {
|
||||||
text: text,
|
text: text,
|
||||||
index: 10,
|
index: 10,
|
||||||
line: 0,
|
line: 0,
|
||||||
@ -408,7 +408,7 @@ fn missing_default_eol() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn missing_default_eof() {
|
fn missing_default_eof() {
|
||||||
let text = "hello arg=";
|
let text = "hello arg=";
|
||||||
parse_error(text, Error {
|
parse_error(text, CompileError {
|
||||||
text: text,
|
text: text,
|
||||||
index: 10,
|
index: 10,
|
||||||
line: 0,
|
line: 0,
|
||||||
@ -421,7 +421,7 @@ fn missing_default_eof() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn missing_default_colon() {
|
fn missing_default_colon() {
|
||||||
let text = "hello arg=:";
|
let text = "hello arg=:";
|
||||||
parse_error(text, Error {
|
parse_error(text, CompileError {
|
||||||
text: text,
|
text: text,
|
||||||
index: 10,
|
index: 10,
|
||||||
line: 0,
|
line: 0,
|
||||||
@ -434,7 +434,7 @@ fn missing_default_colon() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn missing_default_backtick() {
|
fn missing_default_backtick() {
|
||||||
let text = "hello arg=`hello`";
|
let text = "hello arg=`hello`";
|
||||||
parse_error(text, Error {
|
parse_error(text, CompileError {
|
||||||
text: text,
|
text: text,
|
||||||
index: 10,
|
index: 10,
|
||||||
line: 0,
|
line: 0,
|
||||||
@ -447,7 +447,7 @@ fn missing_default_backtick() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn required_after_default() {
|
fn required_after_default() {
|
||||||
let text = "hello arg='foo' bar:";
|
let text = "hello arg='foo' bar:";
|
||||||
parse_error(text, Error {
|
parse_error(text, CompileError {
|
||||||
text: text,
|
text: text,
|
||||||
index: 16,
|
index: 16,
|
||||||
line: 0,
|
line: 0,
|
||||||
@ -460,7 +460,7 @@ fn required_after_default() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn missing_eol() {
|
fn missing_eol() {
|
||||||
let text = "a b c: z =";
|
let text = "a b c: z =";
|
||||||
parse_error(text, Error {
|
parse_error(text, CompileError {
|
||||||
text: text,
|
text: text,
|
||||||
index: 9,
|
index: 9,
|
||||||
line: 0,
|
line: 0,
|
||||||
@ -478,7 +478,7 @@ fn eof_test() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn duplicate_parameter() {
|
fn duplicate_parameter() {
|
||||||
let text = "a b b:";
|
let text = "a b b:";
|
||||||
parse_error(text, Error {
|
parse_error(text, CompileError {
|
||||||
text: text,
|
text: text,
|
||||||
index: 4,
|
index: 4,
|
||||||
line: 0,
|
line: 0,
|
||||||
@ -491,7 +491,7 @@ fn duplicate_parameter() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn parameter_shadows_varible() {
|
fn parameter_shadows_varible() {
|
||||||
let text = "foo = \"h\"\na foo:";
|
let text = "foo = \"h\"\na foo:";
|
||||||
parse_error(text, Error {
|
parse_error(text, CompileError {
|
||||||
text: text,
|
text: text,
|
||||||
index: 12,
|
index: 12,
|
||||||
line: 1,
|
line: 1,
|
||||||
@ -504,7 +504,7 @@ fn parameter_shadows_varible() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn dependency_has_parameters() {
|
fn dependency_has_parameters() {
|
||||||
let text = "foo arg:\nb: foo";
|
let text = "foo arg:\nb: foo";
|
||||||
parse_error(text, Error {
|
parse_error(text, CompileError {
|
||||||
text: text,
|
text: text,
|
||||||
index: 12,
|
index: 12,
|
||||||
line: 1,
|
line: 1,
|
||||||
@ -517,7 +517,7 @@ fn dependency_has_parameters() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn duplicate_dependency() {
|
fn duplicate_dependency() {
|
||||||
let text = "a b c: b c z z";
|
let text = "a b c: b c z z";
|
||||||
parse_error(text, Error {
|
parse_error(text, CompileError {
|
||||||
text: text,
|
text: text,
|
||||||
index: 13,
|
index: 13,
|
||||||
line: 0,
|
line: 0,
|
||||||
@ -530,7 +530,7 @@ fn duplicate_dependency() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn duplicate_recipe() {
|
fn duplicate_recipe() {
|
||||||
let text = "a:\nb:\na:";
|
let text = "a:\nb:\na:";
|
||||||
parse_error(text, Error {
|
parse_error(text, CompileError {
|
||||||
text: text,
|
text: text,
|
||||||
index: 6,
|
index: 6,
|
||||||
line: 2,
|
line: 2,
|
||||||
@ -543,7 +543,7 @@ fn duplicate_recipe() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn circular_recipe_dependency() {
|
fn circular_recipe_dependency() {
|
||||||
let text = "a: b\nb: a";
|
let text = "a: b\nb: a";
|
||||||
parse_error(text, Error {
|
parse_error(text, CompileError {
|
||||||
text: text,
|
text: text,
|
||||||
index: 8,
|
index: 8,
|
||||||
line: 1,
|
line: 1,
|
||||||
@ -556,7 +556,7 @@ fn circular_recipe_dependency() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn circular_variable_dependency() {
|
fn circular_variable_dependency() {
|
||||||
let text = "a = b\nb = a";
|
let text = "a = b\nb = a";
|
||||||
parse_error(text, Error {
|
parse_error(text, CompileError {
|
||||||
text: text,
|
text: text,
|
||||||
index: 0,
|
index: 0,
|
||||||
line: 0,
|
line: 0,
|
||||||
@ -569,7 +569,7 @@ fn circular_variable_dependency() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn duplicate_variable() {
|
fn duplicate_variable() {
|
||||||
let text = "a = \"0\"\na = \"0\"";
|
let text = "a = \"0\"\na = \"0\"";
|
||||||
parse_error(text, Error {
|
parse_error(text, CompileError {
|
||||||
text: text,
|
text: text,
|
||||||
index: 8,
|
index: 8,
|
||||||
line: 1,
|
line: 1,
|
||||||
@ -582,7 +582,7 @@ fn duplicate_variable() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn unterminated_string() {
|
fn unterminated_string() {
|
||||||
let text = r#"a = ""#;
|
let text = r#"a = ""#;
|
||||||
parse_error(text, Error {
|
parse_error(text, CompileError {
|
||||||
text: text,
|
text: text,
|
||||||
index: 3,
|
index: 3,
|
||||||
line: 0,
|
line: 0,
|
||||||
@ -595,7 +595,7 @@ fn unterminated_string() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn unterminated_string_with_escapes() {
|
fn unterminated_string_with_escapes() {
|
||||||
let text = r#"a = "\n\t\r\"\\"#;
|
let text = r#"a = "\n\t\r\"\\"#;
|
||||||
parse_error(text, Error {
|
parse_error(text, CompileError {
|
||||||
text: text,
|
text: text,
|
||||||
index: 3,
|
index: 3,
|
||||||
line: 0,
|
line: 0,
|
||||||
@ -634,7 +634,7 @@ fn parameters() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn self_recipe_dependency() {
|
fn self_recipe_dependency() {
|
||||||
let text = "a: a";
|
let text = "a: a";
|
||||||
parse_error(text, Error {
|
parse_error(text, CompileError {
|
||||||
text: text,
|
text: text,
|
||||||
index: 3,
|
index: 3,
|
||||||
line: 0,
|
line: 0,
|
||||||
@ -647,7 +647,7 @@ fn self_recipe_dependency() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn self_variable_dependency() {
|
fn self_variable_dependency() {
|
||||||
let text = "a = a";
|
let text = "a = a";
|
||||||
parse_error(text, Error {
|
parse_error(text, CompileError {
|
||||||
text: text,
|
text: text,
|
||||||
index: 0,
|
index: 0,
|
||||||
line: 0,
|
line: 0,
|
||||||
@ -660,7 +660,7 @@ fn self_variable_dependency() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn unknown_dependency() {
|
fn unknown_dependency() {
|
||||||
let text = "a: b";
|
let text = "a: b";
|
||||||
parse_error(text, Error {
|
parse_error(text, CompileError {
|
||||||
text: text,
|
text: text,
|
||||||
index: 3,
|
index: 3,
|
||||||
line: 0,
|
line: 0,
|
||||||
@ -673,7 +673,7 @@ fn unknown_dependency() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn mixed_leading_whitespace() {
|
fn mixed_leading_whitespace() {
|
||||||
let text = "a:\n\t echo hello";
|
let text = "a:\n\t echo hello";
|
||||||
parse_error(text, Error {
|
parse_error(text, CompileError {
|
||||||
text: text,
|
text: text,
|
||||||
index: 3,
|
index: 3,
|
||||||
line: 1,
|
line: 1,
|
||||||
@ -724,7 +724,7 @@ fn extra_whitespace() {
|
|||||||
// we might want to make extra leading whitespace a line continuation in the future,
|
// we might want to make extra leading whitespace a line continuation in the future,
|
||||||
// so make it a error for now
|
// so make it a error for now
|
||||||
let text = "a:\n blah\n blarg";
|
let text = "a:\n blah\n blarg";
|
||||||
parse_error(text, Error {
|
parse_error(text, CompileError {
|
||||||
text: text,
|
text: text,
|
||||||
index: 10,
|
index: 10,
|
||||||
line: 2,
|
line: 2,
|
||||||
@ -740,7 +740,7 @@ fn extra_whitespace() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn interpolation_outside_of_recipe() {
|
fn interpolation_outside_of_recipe() {
|
||||||
let text = "{{";
|
let text = "{{";
|
||||||
parse_error(text, Error {
|
parse_error(text, CompileError {
|
||||||
text: text,
|
text: text,
|
||||||
index: 0,
|
index: 0,
|
||||||
line: 0,
|
line: 0,
|
||||||
@ -753,7 +753,7 @@ fn interpolation_outside_of_recipe() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn unclosed_interpolation_delimiter() {
|
fn unclosed_interpolation_delimiter() {
|
||||||
let text = "a:\n echo {{ foo";
|
let text = "a:\n echo {{ foo";
|
||||||
parse_error(text, Error {
|
parse_error(text, CompileError {
|
||||||
text: text,
|
text: text,
|
||||||
index: 15,
|
index: 15,
|
||||||
line: 1,
|
line: 1,
|
||||||
@ -766,7 +766,7 @@ fn unclosed_interpolation_delimiter() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn unknown_expression_variable() {
|
fn unknown_expression_variable() {
|
||||||
let text = "x = yy";
|
let text = "x = yy";
|
||||||
parse_error(text, Error {
|
parse_error(text, CompileError {
|
||||||
text: text,
|
text: text,
|
||||||
index: 4,
|
index: 4,
|
||||||
line: 0,
|
line: 0,
|
||||||
@ -779,7 +779,7 @@ fn unknown_expression_variable() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn unknown_interpolation_variable() {
|
fn unknown_interpolation_variable() {
|
||||||
let text = "x:\n {{ hello}}";
|
let text = "x:\n {{ hello}}";
|
||||||
parse_error(text, Error {
|
parse_error(text, CompileError {
|
||||||
text: text,
|
text: text,
|
||||||
index: 9,
|
index: 9,
|
||||||
line: 1,
|
line: 1,
|
||||||
@ -792,7 +792,7 @@ fn unknown_interpolation_variable() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn unknown_second_interpolation_variable() {
|
fn unknown_second_interpolation_variable() {
|
||||||
let text = "wtf=\"x\"\nx:\n echo\n foo {{wtf}} {{ lol }}";
|
let text = "wtf=\"x\"\nx:\n echo\n foo {{wtf}} {{ lol }}";
|
||||||
parse_error(text, Error {
|
parse_error(text, CompileError {
|
||||||
text: text,
|
text: text,
|
||||||
index: 33,
|
index: 33,
|
||||||
line: 3,
|
line: 3,
|
||||||
|
Loading…
Reference in New Issue
Block a user