Refactoring. Checkpoint commit.
This commit is contained in:
parent
810365f22b
commit
70e9d4e653
3
justfile
3
justfile
@ -10,6 +10,9 @@ backtrace:
|
|||||||
build:
|
build:
|
||||||
cargo build
|
cargo build
|
||||||
|
|
||||||
|
check:
|
||||||
|
cargo check
|
||||||
|
|
||||||
publish: clippy build
|
publish: clippy build
|
||||||
# make sure version is up to date
|
# make sure version is up to date
|
||||||
git diff --no-ext-diff --quiet --exit-code
|
git diff --no-ext-diff --quiet --exit-code
|
||||||
|
10
notes
10
notes
@ -1,6 +1,14 @@
|
|||||||
notes
|
notes
|
||||||
-----
|
-----
|
||||||
|
|
||||||
|
|
||||||
|
- all assignments are evaluated before running anything
|
||||||
|
- for shebang recipes, all expressions are evaluated first
|
||||||
|
- for non-shebang recipes, lines are evaluated one by one and executed
|
||||||
|
|
||||||
|
- later we can decide to not evaluate assignments that aren't referenced
|
||||||
|
by any recipe
|
||||||
|
|
||||||
- deferred evaluation, even though it's good for testing, is a bad idea
|
- deferred evaluation, even though it's good for testing, is a bad idea
|
||||||
i should have a resolve assignments phase which just checks,
|
i should have a resolve assignments phase which just checks,
|
||||||
an evaluate assignments phase which runs before any recipe
|
an evaluate assignments phase which runs before any recipe
|
||||||
@ -11,6 +19,8 @@ notes
|
|||||||
- should consider renaming it to --evaluate if it actually evaluates stuff
|
- should consider renaming it to --evaluate if it actually evaluates stuff
|
||||||
- test values of assignments
|
- test values of assignments
|
||||||
- test values of interpolations
|
- test values of interpolations
|
||||||
|
- test results of concatination
|
||||||
|
- test string escape parsing
|
||||||
|
|
||||||
- remove evaluated_lines field from recipe
|
- remove evaluated_lines field from recipe
|
||||||
|
|
||||||
|
@ -159,7 +159,7 @@ recipe:
|
|||||||
"",
|
"",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
#[test]
|
#[test]
|
||||||
fn debug() {
|
fn debug() {
|
||||||
let text =
|
let text =
|
||||||
@ -182,6 +182,7 @@ recipe:
|
|||||||
"",
|
"",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn status() {
|
fn status() {
|
||||||
|
181
src/lib.rs
181
src/lib.rs
@ -136,10 +136,14 @@ 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<&str, String>
|
||||||
|
) -> Result<(), RunError<'a>> {
|
||||||
let mut arg_map = BTreeMap::new();
|
let mut arg_map = BTreeMap::new();
|
||||||
for (i, argument) in arguments.iter().enumerate() {
|
for (i, argument) in arguments.iter().enumerate() {
|
||||||
arg_map.insert(*self.arguments.get(i).unwrap(), Some(*argument));
|
arg_map.insert(*self.arguments.get(i).unwrap(), *argument);
|
||||||
}
|
}
|
||||||
|
|
||||||
let evaluated_lines;
|
let evaluated_lines;
|
||||||
@ -263,19 +267,11 @@ impl<'a> Display for Recipe<'a> {
|
|||||||
if j == 0 {
|
if j == 0 {
|
||||||
try!(write!(f, " "));
|
try!(write!(f, " "));
|
||||||
}
|
}
|
||||||
if f.alternate() {
|
|
||||||
match *piece {
|
|
||||||
Fragment::Text{ref text} => try!(write!(f, "{}", text.lexeme)),
|
|
||||||
Fragment::Expression{ref expression, value: None} => try!(write!(f, "{}{} # ? {}", "{{", expression, "}}")),
|
|
||||||
Fragment::Expression{ref expression, value: Some(ref string)} => try!(write!(f, "{}{} # \"{}\"{}", "{{", expression, string, "}}")),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
match *piece {
|
match *piece {
|
||||||
Fragment::Text{ref text} => try!(write!(f, "{}", text.lexeme)),
|
Fragment::Text{ref text} => try!(write!(f, "{}", text.lexeme)),
|
||||||
Fragment::Expression{ref expression, ..} => try!(write!(f, "{}{}{}", "{{", expression, "}}")),
|
Fragment::Expression{ref expression, ..} => try!(write!(f, "{}{}{}", "{{", expression, "}}")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if i + 1 < self.lines.len() {
|
if i + 1 < self.lines.len() {
|
||||||
try!(write!(f, "\n"));
|
try!(write!(f, "\n"));
|
||||||
}
|
}
|
||||||
@ -453,50 +449,31 @@ impl<'a: 'b, 'b> AssignmentResolver<'a, 'b> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn evaluate<'a>(
|
fn evaluate_assignments<'a>(
|
||||||
assignments: &BTreeMap<&'a str, Expression<'a>>,
|
assignments: &BTreeMap<&'a str, Expression<'a>>,
|
||||||
assignment_tokens: &BTreeMap<&'a str, Token<'a>>,
|
) -> Result<BTreeMap<&'a str, String>, RunError<'a>> {
|
||||||
recipes: &mut BTreeMap<&'a str, Recipe<'a>>,
|
|
||||||
) -> Result<BTreeMap<&'a str, String>, Error<'a>> {
|
|
||||||
let mut evaluated = BTreeMap::new();
|
|
||||||
|
|
||||||
{
|
|
||||||
let mut evaluator = Evaluator {
|
let mut evaluator = Evaluator {
|
||||||
seen: HashSet::new(),
|
evaluated: BTreeMap::new(),
|
||||||
stack: vec![],
|
|
||||||
evaluated: &mut evaluated,
|
|
||||||
scope: &BTreeMap::new(),
|
scope: &BTreeMap::new(),
|
||||||
assignments: assignments,
|
assignments: assignments,
|
||||||
assignment_tokens: assignment_tokens,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
for name in assignments.keys() {
|
for name in assignments.keys() {
|
||||||
try!(evaluator.evaluate_assignment(name));
|
try!(evaluator.evaluate_assignment(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
for recipe in recipes.values_mut() {
|
Ok(evaluator.evaluated)
|
||||||
let mut arguments = BTreeMap::new();
|
|
||||||
for argument in &recipe.arguments {
|
|
||||||
arguments.insert(*argument, None);
|
|
||||||
}
|
|
||||||
try!(evaluator.evaluate_recipe(recipe, &arguments));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(evaluated)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn evaluate_lines<'a>(
|
fn evaluate_lines<'a>(
|
||||||
lines: &Vec<Vec<Fragment<'a>>>,
|
lines: &Vec<Vec<Fragment<'a>>>,
|
||||||
scope: &BTreeMap<&'a str, String>,
|
scope: &BTreeMap<&'a str, String>,
|
||||||
arguments: &BTreeMap<&str, Option<&str>>
|
arguments: &BTreeMap<&str, &str>
|
||||||
) -> Result<Option<Vec<String>>, Error<'a>> {
|
) -> Result<Option<Vec<String>>, RunError<'a>> {
|
||||||
let mut evaluator = Evaluator {
|
let mut evaluator = Evaluator {
|
||||||
seen: HashSet::new(),
|
evaluated: BTreeMap::new(),
|
||||||
stack: vec![],
|
|
||||||
evaluated: &mut BTreeMap::new(),
|
|
||||||
scope: scope,
|
scope: scope,
|
||||||
assignments: &BTreeMap::new(),
|
assignments: &BTreeMap::new(),
|
||||||
assignment_tokens: &BTreeMap::new(),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut evaluated_lines = vec![];
|
let mut evaluated_lines = vec![];
|
||||||
@ -509,11 +486,7 @@ fn evaluate_lines<'a>(
|
|||||||
line += &value;
|
line += &value;
|
||||||
}
|
}
|
||||||
Fragment::Expression{ref expression, value: None} => {
|
Fragment::Expression{ref expression, value: None} => {
|
||||||
if let Some(value) = try!(evaluator.evaluate_expression(expression, &arguments)) {
|
line += &try!(evaluator.evaluate_expression(expression, &arguments));
|
||||||
line += &value;
|
|
||||||
} else {
|
|
||||||
return Ok(None);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -524,94 +497,57 @@ fn evaluate_lines<'a>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct Evaluator<'a: 'b, 'b> {
|
struct Evaluator<'a: 'b, 'b> {
|
||||||
stack: Vec<&'a str>,
|
evaluated: BTreeMap<&'a str, String>,
|
||||||
seen: HashSet<&'a str>,
|
|
||||||
evaluated: &'b mut BTreeMap<&'a str, String>,
|
|
||||||
scope: &'b BTreeMap<&'a str, String>,
|
scope: &'b BTreeMap<&'a str, String>,
|
||||||
assignments: &'b BTreeMap<&'a str, Expression<'a>>,
|
assignments: &'b BTreeMap<&'a str, Expression<'a>>,
|
||||||
assignment_tokens: &'b BTreeMap<&'a str, Token<'a>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'b> Evaluator<'a, 'b> {
|
impl<'a, 'b> Evaluator<'a, 'b> {
|
||||||
fn evaluate_assignment(&mut self, name: &'a str) -> Result<(), Error<'a>> {
|
fn evaluate_assignment(&mut self, name: &'a str) -> Result<(), RunError<'a>> {
|
||||||
if self.evaluated.contains_key(name) {
|
if self.evaluated.contains_key(name) {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
self.stack.push(name);
|
|
||||||
self.seen.insert(name);
|
|
||||||
|
|
||||||
if let Some(expression) = self.assignments.get(name) {
|
if let Some(expression) = self.assignments.get(name) {
|
||||||
let value = try!(self.evaluate_expression(expression, &BTreeMap::new())).unwrap();
|
let value = try!(self.evaluate_expression(expression, &BTreeMap::new()));
|
||||||
self.evaluated.insert(name, value);
|
self.evaluated.insert(name, value);
|
||||||
} else {
|
} else {
|
||||||
let token = self.assignment_tokens.get(name).unwrap();
|
panic!("internal error: unknown assigment");
|
||||||
return Err(token.error(ErrorKind::UnknownVariable {variable: name}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.stack.pop();
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn evaluate_recipe(
|
|
||||||
&mut self,
|
|
||||||
recipe: &mut Recipe<'a>,
|
|
||||||
arguments: &BTreeMap<&str, Option<&str>>,
|
|
||||||
) -> Result<(), Error<'a>> {
|
|
||||||
for fragments in &mut recipe.lines {
|
|
||||||
for mut fragment in fragments.iter_mut() {
|
|
||||||
match *fragment {
|
|
||||||
Fragment::Text{..} => {},
|
|
||||||
Fragment::Expression{ref expression, ref mut value} => {
|
|
||||||
*value = try!(self.evaluate_expression(&expression, arguments));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn evaluate_expression(
|
fn evaluate_expression(
|
||||||
&mut self,
|
&mut self,
|
||||||
expression: &Expression<'a>,
|
expression: &Expression<'a>,
|
||||||
arguments: &BTreeMap<&str, Option<&str>>
|
arguments: &BTreeMap<&str, &str>
|
||||||
) -> Result<Option<String>, Error<'a>> {
|
) -> Result<String, RunError<'a>> {
|
||||||
Ok(match *expression {
|
Ok(match *expression {
|
||||||
Expression::Variable{name, ref token} => {
|
Expression::Variable{name, ..} => {
|
||||||
if self.evaluated.contains_key(name) {
|
if self.evaluated.contains_key(name) {
|
||||||
Some(self.evaluated.get(name).unwrap().clone())
|
self.evaluated.get(name).unwrap().clone()
|
||||||
} else if self.scope.contains_key(name) {
|
} else if self.scope.contains_key(name) {
|
||||||
Some(self.scope.get(name).unwrap().clone())
|
self.scope.get(name).unwrap().clone()
|
||||||
} else if self.seen.contains(name) {
|
|
||||||
let token = self.assignment_tokens.get(name).unwrap();
|
|
||||||
self.stack.push(name);
|
|
||||||
return Err(token.error(ErrorKind::CircularVariableDependency {
|
|
||||||
variable: name,
|
|
||||||
circle: self.stack.clone(),
|
|
||||||
}));
|
|
||||||
} else if self.assignments.contains_key(name) {
|
} else if self.assignments.contains_key(name) {
|
||||||
try!(self.evaluate_assignment(name));
|
try!(self.evaluate_assignment(name));
|
||||||
Some(self.evaluated.get(name).unwrap().clone())
|
self.evaluated.get(name).unwrap().clone()
|
||||||
} else if arguments.contains_key(name) {
|
} else if arguments.contains_key(name) {
|
||||||
arguments.get(name).unwrap().map(|s| s.to_string())
|
arguments.get(name).unwrap().to_string()
|
||||||
} else {
|
} else {
|
||||||
return Err(token.error(ErrorKind::UnknownVariable{variable: name}));
|
panic!("internal error: unknown error");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expression::String{ref cooked, ..} => {
|
Expression::String{ref cooked, ..} => {
|
||||||
Some(cooked.clone())
|
cooked.clone()
|
||||||
}
|
}
|
||||||
Expression::Backtick{raw, ..} => {
|
Expression::Backtick{raw, ..} => {
|
||||||
Some(raw.to_string())
|
raw.to_string()
|
||||||
}
|
}
|
||||||
Expression::Concatination{ref lhs, ref rhs} => {
|
Expression::Concatination{ref lhs, ref rhs} => {
|
||||||
let lhs = try!(self.evaluate_expression(lhs, arguments));
|
try!(self.evaluate_expression(lhs, arguments))
|
||||||
let rhs = try!(self.evaluate_expression(rhs, arguments));
|
+
|
||||||
if let (Some(lhs), Some(rhs)) = (lhs, rhs) {
|
&try!(self.evaluate_expression(rhs, arguments))
|
||||||
Some(lhs + &rhs)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -782,7 +718,6 @@ impl<'a> Display for Error<'a> {
|
|||||||
struct Justfile<'a> {
|
struct Justfile<'a> {
|
||||||
recipes: BTreeMap<&'a str, Recipe<'a>>,
|
recipes: BTreeMap<&'a str, Recipe<'a>>,
|
||||||
assignments: BTreeMap<&'a str, Expression<'a>>,
|
assignments: BTreeMap<&'a str, Expression<'a>>,
|
||||||
values: BTreeMap<&'a str, String>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'b> Justfile<'a> where 'a: 'b {
|
impl<'a, 'b> Justfile<'a> where 'a: 'b {
|
||||||
@ -808,18 +743,10 @@ impl<'a, 'b> Justfile<'a> where 'a: 'b {
|
|||||||
self.recipes.keys().cloned().collect()
|
self.recipes.keys().cloned().collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_recipe(&self, recipe: &Recipe<'a>, arguments: &[&'a str], ran: &mut HashSet<&'a str>) -> Result<(), RunError> {
|
|
||||||
for dependency_name in &recipe.dependencies {
|
|
||||||
if !ran.contains(dependency_name) {
|
|
||||||
try!(self.run_recipe(&self.recipes[dependency_name], &[], ran));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try!(recipe.run(arguments, &self.values));
|
|
||||||
ran.insert(recipe.name);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run(&'a self, arguments: &[&'b str]) -> Result<(), RunError<'b>> {
|
fn run(&'a self, arguments: &[&'b str]) -> Result<(), RunError<'b>> {
|
||||||
|
let scope = try!(evaluate_assignments(&self.assignments));
|
||||||
|
let mut ran = HashSet::new();
|
||||||
|
|
||||||
for (i, argument) in arguments.iter().enumerate() {
|
for (i, argument) in arguments.iter().enumerate() {
|
||||||
if let Some(recipe) = self.recipes.get(argument) {
|
if let Some(recipe) = self.recipes.get(argument) {
|
||||||
if !recipe.arguments.is_empty() {
|
if !recipe.arguments.is_empty() {
|
||||||
@ -834,8 +761,7 @@ impl<'a, 'b> Justfile<'a> where 'a: 'b {
|
|||||||
expected: recipe.arguments.len(),
|
expected: recipe.arguments.len(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
let mut ran = HashSet::new();
|
try!(self.run_recipe(recipe, rest, &scope, &mut ran));
|
||||||
try!(self.run_recipe(recipe, rest, &mut ran));
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -852,14 +778,29 @@ impl<'a, 'b> Justfile<'a> where 'a: 'b {
|
|||||||
if !missing.is_empty() {
|
if !missing.is_empty() {
|
||||||
return Err(RunError::UnknownRecipes{recipes: missing});
|
return Err(RunError::UnknownRecipes{recipes: missing});
|
||||||
}
|
}
|
||||||
let recipes: Vec<_> = arguments.iter().map(|name| self.recipes.get(name).unwrap()).collect();
|
for recipe in arguments.iter().map(|name| self.recipes.get(name).unwrap()) {
|
||||||
let mut ran = HashSet::new();
|
try!(self.run_recipe(recipe, &[], &scope, &mut ran));
|
||||||
for recipe in recipes {
|
|
||||||
try!(self.run_recipe(recipe, &[], &mut ran));
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn run_recipe(
|
||||||
|
&self,
|
||||||
|
recipe: &Recipe<'a>,
|
||||||
|
arguments: &[&'a str],
|
||||||
|
scope: &BTreeMap<&str, String>,
|
||||||
|
ran: &mut HashSet<&'a str>
|
||||||
|
) -> Result<(), RunError> {
|
||||||
|
for dependency_name in &recipe.dependencies {
|
||||||
|
if !ran.contains(dependency_name) {
|
||||||
|
try!(self.run_recipe(&self.recipes[dependency_name], &[], scope, ran));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try!(recipe.run(arguments, &scope));
|
||||||
|
ran.insert(recipe.name);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn get(&self, name: &str) -> Option<&Recipe<'a>> {
|
fn get(&self, name: &str) -> Option<&Recipe<'a>> {
|
||||||
self.recipes.get(name)
|
self.recipes.get(name)
|
||||||
}
|
}
|
||||||
@ -869,22 +810,14 @@ impl<'a> Display for Justfile<'a> {
|
|||||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||||
let mut items = self.recipes.len() + self.assignments.len();
|
let mut items = self.recipes.len() + self.assignments.len();
|
||||||
for (name, expression) in &self.assignments {
|
for (name, expression) in &self.assignments {
|
||||||
if f.alternate() {
|
|
||||||
try!(write!(f, "{} = {} # \"{}\"", name, expression, self.values.get(name).unwrap()));
|
|
||||||
} else {
|
|
||||||
try!(write!(f, "{} = {}", name, expression));
|
try!(write!(f, "{} = {}", name, expression));
|
||||||
}
|
|
||||||
items -= 1;
|
items -= 1;
|
||||||
if items != 0 {
|
if items != 0 {
|
||||||
try!(write!(f, "\n\n"));
|
try!(write!(f, "\n\n"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for recipe in self.recipes.values() {
|
for recipe in self.recipes.values() {
|
||||||
if f.alternate() {
|
|
||||||
try!(write!(f, "{:#}", recipe));
|
|
||||||
} else {
|
|
||||||
try!(write!(f, "{}", recipe));
|
try!(write!(f, "{}", recipe));
|
||||||
}
|
|
||||||
items -= 1;
|
items -= 1;
|
||||||
if items != 0 {
|
if items != 0 {
|
||||||
try!(write!(f, "\n\n"));
|
try!(write!(f, "\n\n"));
|
||||||
@ -1556,13 +1489,9 @@ impl<'a> Parser<'a> {
|
|||||||
|
|
||||||
try!(resolve_assignments(&assignments, &assignment_tokens));
|
try!(resolve_assignments(&assignments, &assignment_tokens));
|
||||||
|
|
||||||
let values =
|
|
||||||
try!(evaluate(&assignments, &assignment_tokens, &mut recipes));
|
|
||||||
|
|
||||||
Ok(Justfile {
|
Ok(Justfile {
|
||||||
recipes: recipes,
|
recipes: recipes,
|
||||||
assignments: assignments,
|
assignments: assignments,
|
||||||
values: values,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
20
src/unit.rs
20
src/unit.rs
@ -246,16 +246,16 @@ hello a b c : x y z #hello
|
|||||||
1
|
1
|
||||||
2
|
2
|
||||||
3
|
3
|
||||||
", "bar = foo # \"xx\"
|
", "bar = foo
|
||||||
|
|
||||||
foo = \"xx\" # \"xx\"
|
foo = \"xx\"
|
||||||
|
|
||||||
goodbye = \"y\" # \"y\"
|
goodbye = \"y\"
|
||||||
|
|
||||||
hello a b c: x y z
|
hello a b c: x y z
|
||||||
#! blah
|
#! blah
|
||||||
#blarg
|
#blarg
|
||||||
{{foo + bar # \"xxxx\"}}abc{{goodbye + \"x\" # \"yx\"}}xyz
|
{{foo + bar}}abc{{goodbye + \"x\"}}xyz
|
||||||
1
|
1
|
||||||
2
|
2
|
||||||
3
|
3
|
||||||
@ -275,11 +275,11 @@ c = a + b + a + b
|
|||||||
b = "1"
|
b = "1"
|
||||||
"#,
|
"#,
|
||||||
|
|
||||||
r#"a = "0" # "0"
|
r#"a = "0"
|
||||||
|
|
||||||
b = "1" # "1"
|
b = "1"
|
||||||
|
|
||||||
c = a + b + a + b # "0101""#);
|
c = a + b + a + b"#);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -447,7 +447,7 @@ fn unterminated_string_with_escapes() {
|
|||||||
fn string_quote_escape() {
|
fn string_quote_escape() {
|
||||||
parse_summary(
|
parse_summary(
|
||||||
r#"a = "hello\"""#,
|
r#"a = "hello\"""#,
|
||||||
r#"a = "hello\"" # "hello"""#
|
r#"a = "hello\"""#
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -455,7 +455,7 @@ fn string_quote_escape() {
|
|||||||
fn string_escapes() {
|
fn string_escapes() {
|
||||||
parse_summary(
|
parse_summary(
|
||||||
r#"a = "\n\t\r\"\\""#,
|
r#"a = "\n\t\r\"\\""#,
|
||||||
concat!(r#"a = "\n\t\r\"\\" "#, "# \"\n\t\r\"\\\"")
|
r#"a = "\n\t\r\"\\""#
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -465,7 +465,7 @@ fn arguments() {
|
|||||||
"a b c:
|
"a b c:
|
||||||
{{b}} {{c}}",
|
{{b}} {{c}}",
|
||||||
"a b c:
|
"a b c:
|
||||||
{{b # ? }} {{c # ? }}",
|
{{b}} {{c}}",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user