Working on backticks. They are evaluated as strings for now.
This commit is contained in:
parent
b57b84e550
commit
70d1df5b9c
7
notes
7
notes
@ -1,7 +1,12 @@
|
|||||||
notes
|
notes
|
||||||
-----
|
-----
|
||||||
|
|
||||||
- save result of commands in variables: `hello`
|
- save result of commands in variables
|
||||||
|
. backticks: `echo hello`
|
||||||
|
. backtick evaluation returns None inside of recipe bodies on initial evaluation
|
||||||
|
. when you run the recipe, then you execute it
|
||||||
|
. eval shebang recipes all at once, but plain recipes line by line
|
||||||
|
. we want to avoid executing backticks before we need them
|
||||||
|
|
||||||
- set variables from the command line:
|
- set variables from the command line:
|
||||||
. j --set build linux
|
. j --set build linux
|
||||||
|
83
src/lib.rs
83
src/lib.rs
@ -77,6 +77,7 @@ enum Fragment<'a> {
|
|||||||
enum Expression<'a> {
|
enum Expression<'a> {
|
||||||
Variable{name: &'a str, token: Token<'a>},
|
Variable{name: &'a str, token: Token<'a>},
|
||||||
String{raw: &'a str, cooked: String},
|
String{raw: &'a str, cooked: String},
|
||||||
|
Backtick{raw: &'a str},
|
||||||
Concatination{lhs: Box<Expression<'a>>, rhs: Box<Expression<'a>>},
|
Concatination{lhs: Box<Expression<'a>>, rhs: Box<Expression<'a>>},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,7 +98,7 @@ impl<'a> Iterator for Variables<'a> {
|
|||||||
|
|
||||||
fn next(&mut self) -> Option<&'a Token<'a>> {
|
fn next(&mut self) -> Option<&'a Token<'a>> {
|
||||||
match self.stack.pop() {
|
match self.stack.pop() {
|
||||||
None | Some(&Expression::String{..}) => None,
|
None | Some(&Expression::String{..}) | Some(&Expression::Backtick{..}) => None,
|
||||||
Some(&Expression::Variable{ref token,..}) => Some(token),
|
Some(&Expression::Variable{ref token,..}) => Some(token),
|
||||||
Some(&Expression::Concatination{ref lhs, ref rhs}) => {
|
Some(&Expression::Concatination{ref lhs, ref rhs}) => {
|
||||||
self.stack.push(lhs);
|
self.stack.push(lhs);
|
||||||
@ -111,9 +112,10 @@ impl<'a> Iterator for Variables<'a> {
|
|||||||
impl<'a> Display for Expression<'a> {
|
impl<'a> Display for Expression<'a> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||||
match *self {
|
match *self {
|
||||||
Expression::Variable {name, .. } => try!(write!(f, "{}", name)),
|
Expression::Backtick {raw, .. } => try!(write!(f, "`{}`", raw)),
|
||||||
Expression::String {raw, .. } => try!(write!(f, "\"{}\"", raw)),
|
|
||||||
Expression::Concatination{ref lhs, ref rhs} => try!(write!(f, "{} + {}", lhs, rhs)),
|
Expression::Concatination{ref lhs, ref rhs} => try!(write!(f, "{} + {}", lhs, rhs)),
|
||||||
|
Expression::String {raw, .. } => try!(write!(f, "\"{}\"", raw)),
|
||||||
|
Expression::Variable {name, .. } => try!(write!(f, "{}", name)),
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -486,6 +488,9 @@ impl<'a, 'b> Evaluator<'a, 'b> {
|
|||||||
Expression::String{ref cooked, ..} => {
|
Expression::String{ref cooked, ..} => {
|
||||||
Some(cooked.clone())
|
Some(cooked.clone())
|
||||||
}
|
}
|
||||||
|
Expression::Backtick{raw, ..} => {
|
||||||
|
Some(raw.to_string())
|
||||||
|
}
|
||||||
Expression::Concatination{ref lhs, ref rhs} => {
|
Expression::Concatination{ref lhs, ref rhs} => {
|
||||||
let lhs = try!(self.evaluate_expression(lhs, arguments));
|
let lhs = try!(self.evaluate_expression(lhs, arguments));
|
||||||
let rhs = try!(self.evaluate_expression(rhs, arguments));
|
let rhs = try!(self.evaluate_expression(rhs, arguments));
|
||||||
@ -515,11 +520,11 @@ enum ErrorKind<'a> {
|
|||||||
BadName{name: &'a str},
|
BadName{name: &'a str},
|
||||||
CircularRecipeDependency{recipe: &'a str, circle: Vec<&'a str>},
|
CircularRecipeDependency{recipe: &'a str, circle: Vec<&'a str>},
|
||||||
CircularVariableDependency{variable: &'a str, circle: Vec<&'a str>},
|
CircularVariableDependency{variable: &'a str, circle: Vec<&'a str>},
|
||||||
|
DependencyHasArguments{recipe: &'a str, dependency: &'a str},
|
||||||
DuplicateArgument{recipe: &'a str, argument: &'a str},
|
DuplicateArgument{recipe: &'a str, argument: &'a str},
|
||||||
DuplicateDependency{recipe: &'a str, dependency: &'a str},
|
DuplicateDependency{recipe: &'a str, dependency: &'a str},
|
||||||
DuplicateRecipe{recipe: &'a str, first: usize},
|
DuplicateRecipe{recipe: &'a str, first: usize},
|
||||||
DuplicateVariable{variable: &'a str},
|
DuplicateVariable{variable: &'a str},
|
||||||
DependencyHasArguments{recipe: &'a str, dependency: &'a str},
|
|
||||||
ExtraLeadingWhitespace,
|
ExtraLeadingWhitespace,
|
||||||
InconsistentLeadingWhitespace{expected: &'a str, found: &'a str},
|
InconsistentLeadingWhitespace{expected: &'a str, found: &'a str},
|
||||||
InternalError{message: String},
|
InternalError{message: String},
|
||||||
@ -778,15 +783,15 @@ impl<'a> Display for Justfile<'a> {
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum RunError<'a> {
|
enum RunError<'a> {
|
||||||
UnknownRecipes{recipes: Vec<&'a str>},
|
|
||||||
NonLeadingRecipeWithArguments{recipe: &'a str},
|
|
||||||
ArgumentCountMismatch{recipe: &'a str, found: usize, expected: usize},
|
ArgumentCountMismatch{recipe: &'a str, found: usize, expected: usize},
|
||||||
Signal{recipe: &'a str, signal: i32},
|
|
||||||
Code{recipe: &'a str, code: i32},
|
Code{recipe: &'a str, code: i32},
|
||||||
UnknownFailure{recipe: &'a str},
|
|
||||||
IoError{recipe: &'a str, io_error: io::Error},
|
|
||||||
TmpdirIoError{recipe: &'a str, io_error: io::Error},
|
|
||||||
InternalError{message: String},
|
InternalError{message: String},
|
||||||
|
IoError{recipe: &'a str, io_error: io::Error},
|
||||||
|
NonLeadingRecipeWithArguments{recipe: &'a str},
|
||||||
|
Signal{recipe: &'a str, signal: i32},
|
||||||
|
TmpdirIoError{recipe: &'a str, io_error: io::Error},
|
||||||
|
UnknownFailure{recipe: &'a str},
|
||||||
|
UnknownRecipes{recipes: Vec<&'a str>},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Display for RunError<'a> {
|
impl<'a> Display for RunError<'a> {
|
||||||
@ -859,39 +864,41 @@ impl<'a> Token<'a> {
|
|||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone, Copy)]
|
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||||
enum TokenKind {
|
enum TokenKind {
|
||||||
Name,
|
Backtick,
|
||||||
Colon,
|
Colon,
|
||||||
StringToken,
|
|
||||||
Plus,
|
|
||||||
Equals,
|
|
||||||
Comment,
|
Comment,
|
||||||
Indent,
|
|
||||||
Dedent,
|
Dedent,
|
||||||
InterpolationStart,
|
|
||||||
InterpolationEnd,
|
|
||||||
Text,
|
|
||||||
Line,
|
|
||||||
Eol,
|
|
||||||
Eof,
|
Eof,
|
||||||
|
Eol,
|
||||||
|
Equals,
|
||||||
|
Indent,
|
||||||
|
InterpolationEnd,
|
||||||
|
InterpolationStart,
|
||||||
|
Line,
|
||||||
|
Name,
|
||||||
|
Plus,
|
||||||
|
StringToken,
|
||||||
|
Text,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for TokenKind {
|
impl Display for TokenKind {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||||
try!(write!(f, "{}", match *self {
|
try!(write!(f, "{}", match *self {
|
||||||
Name => "name",
|
Backtick => "backtick",
|
||||||
Colon => "\":\"",
|
Colon => "\":\"",
|
||||||
Plus => "\"+\"",
|
Comment => "comment",
|
||||||
|
Dedent => "dedent",
|
||||||
|
Eof => "end of file",
|
||||||
|
Eol => "end of line",
|
||||||
Equals => "\"=\"",
|
Equals => "\"=\"",
|
||||||
|
Indent => "indent",
|
||||||
|
InterpolationEnd => "}}",
|
||||||
|
InterpolationStart => "{{",
|
||||||
|
Line => "command",
|
||||||
|
Name => "name",
|
||||||
|
Plus => "\"+\"",
|
||||||
StringToken => "string",
|
StringToken => "string",
|
||||||
Text => "command text",
|
Text => "command text",
|
||||||
InterpolationStart => "{{",
|
|
||||||
InterpolationEnd => "}}",
|
|
||||||
Comment => "comment",
|
|
||||||
Line => "command",
|
|
||||||
Indent => "indent",
|
|
||||||
Dedent => "dedent",
|
|
||||||
Eol => "end of line",
|
|
||||||
Eof => "end of file",
|
|
||||||
}));
|
}));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -909,20 +916,21 @@ fn token(pattern: &str) -> Regex {
|
|||||||
|
|
||||||
fn tokenize(text: &str) -> Result<Vec<Token>, Error> {
|
fn tokenize(text: &str) -> Result<Vec<Token>, Error> {
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref EOF: Regex = token(r"(?-m)$" );
|
static ref BACKTICK: Regex = token(r"`[^`\n\r]*`" );
|
||||||
static ref NAME: Regex = token(r"([a-zA-Z0-9_-]+)" );
|
|
||||||
static ref COLON: Regex = token(r":" );
|
static ref COLON: Regex = token(r":" );
|
||||||
static ref EQUALS: Regex = token(r"=" );
|
|
||||||
static ref PLUS: Regex = token(r"[+]" );
|
|
||||||
static ref COMMENT: Regex = token(r"#([^!].*)?$" );
|
static ref COMMENT: Regex = token(r"#([^!].*)?$" );
|
||||||
static ref STRING: Regex = token("\"" );
|
static ref EOF: Regex = token(r"(?-m)$" );
|
||||||
static ref EOL: Regex = token(r"\n|\r\n" );
|
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_END: Regex = token(r"[}][}]" );
|
||||||
static ref INTERPOLATION_START_TOKEN: Regex = token(r"[{][{]" );
|
static ref INTERPOLATION_START_TOKEN: Regex = token(r"[{][{]" );
|
||||||
static ref LINE: Regex = re(r"^(?m)[ \t]+[^ \t\n\r].*$");
|
static ref NAME: Regex = token(r"([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 INDENT: Regex = re(r"^([ \t]*)[^ \t\n\r]" );
|
||||||
static ref INTERPOLATION_START: Regex = re(r"^[{][{]" );
|
static ref INTERPOLATION_START: Regex = re(r"^[{][{]" );
|
||||||
static ref LEADING_TEXT: Regex = re(r"^(?m)(.+?)[{][{]" );
|
static ref LEADING_TEXT: Regex = re(r"^(?m)(.+?)[{][{]" );
|
||||||
|
static ref LINE: Regex = re(r"^(?m)[ \t]+[^ \t\n\r].*$");
|
||||||
static ref TEXT: Regex = re(r"^(?m)(.+)" );
|
static ref TEXT: Regex = re(r"^(?m)(.+)" );
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1063,6 +1071,8 @@ fn tokenize(text: &str) -> Result<Vec<Token>, Error> {
|
|||||||
panic!("interpolation must be closed at end of line");
|
panic!("interpolation must be closed at end of line");
|
||||||
}
|
}
|
||||||
(captures.at(1).unwrap(), captures.at(2).unwrap(), Eol)
|
(captures.at(1).unwrap(), captures.at(2).unwrap(), Eol)
|
||||||
|
} else if let Some(captures) = BACKTICK.captures(rest) {
|
||||||
|
(captures.at(1).unwrap(), captures.at(2).unwrap(), Backtick)
|
||||||
} else if let Some(captures) = COLON.captures(rest) {
|
} else if let Some(captures) = COLON.captures(rest) {
|
||||||
(captures.at(1).unwrap(), captures.at(2).unwrap(), Colon)
|
(captures.at(1).unwrap(), captures.at(2).unwrap(), Colon)
|
||||||
} else if let Some(captures) = PLUS.captures(rest) {
|
} else if let Some(captures) = PLUS.captures(rest) {
|
||||||
@ -1312,6 +1322,7 @@ impl<'a> Parser<'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},
|
||||||
|
Backtick => Expression::Backtick{raw: &first.lexeme[1..first.lexeme.len() - 1]},
|
||||||
StringToken => {
|
StringToken => {
|
||||||
let raw = &first.lexeme[1..first.lexeme.len() - 1];
|
let raw = &first.lexeme[1..first.lexeme.len() - 1];
|
||||||
let mut cooked = String::new();
|
let mut cooked = String::new();
|
||||||
|
21
src/unit.rs
21
src/unit.rs
@ -35,20 +35,21 @@ fn tokenize_error(text: &str, expected: Error) {
|
|||||||
fn token_summary(tokens: &[Token]) -> String {
|
fn token_summary(tokens: &[Token]) -> String {
|
||||||
tokens.iter().map(|t| {
|
tokens.iter().map(|t| {
|
||||||
match t.kind {
|
match t.kind {
|
||||||
|
super::TokenKind::Backtick => "`",
|
||||||
|
super::TokenKind::Colon => ":",
|
||||||
|
super::TokenKind::Comment{..} => "#",
|
||||||
|
super::TokenKind::Dedent => "<",
|
||||||
|
super::TokenKind::Eof => ".",
|
||||||
|
super::TokenKind::Eol => "$",
|
||||||
|
super::TokenKind::Equals => "=",
|
||||||
|
super::TokenKind::Indent{..} => ">",
|
||||||
|
super::TokenKind::InterpolationEnd => "}",
|
||||||
|
super::TokenKind::InterpolationStart => "{",
|
||||||
super::TokenKind::Line{..} => "^",
|
super::TokenKind::Line{..} => "^",
|
||||||
super::TokenKind::Name => "N",
|
super::TokenKind::Name => "N",
|
||||||
super::TokenKind::Colon => ":",
|
|
||||||
super::TokenKind::StringToken => "'",
|
|
||||||
super::TokenKind::Plus => "+",
|
super::TokenKind::Plus => "+",
|
||||||
super::TokenKind::Equals => "=",
|
super::TokenKind::StringToken => "'",
|
||||||
super::TokenKind::Comment{..} => "#",
|
|
||||||
super::TokenKind::Indent{..} => ">",
|
|
||||||
super::TokenKind::Text => "_",
|
super::TokenKind::Text => "_",
|
||||||
super::TokenKind::InterpolationStart => "{",
|
|
||||||
super::TokenKind::InterpolationEnd => "}",
|
|
||||||
super::TokenKind::Dedent => "<",
|
|
||||||
super::TokenKind::Eol => "$",
|
|
||||||
super::TokenKind::Eof => ".",
|
|
||||||
}
|
}
|
||||||
}).collect::<Vec<_>>().join("")
|
}).collect::<Vec<_>>().join("")
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user