mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-01 07:41:12 +00:00
First (failed!) attempt at string interpolation
This commit is contained in:
parent
6bdff2b069
commit
7ae610ad18
3 changed files with 70 additions and 35 deletions
|
@ -31,7 +31,7 @@ fn problem(prob: Problem) -> Evaluated {
|
|||
pub fn scoped_eval(expr: Expr, vars: &Scope) -> Evaluated {
|
||||
match expr {
|
||||
// Primitives need no further evaluation
|
||||
Error(_) | Int(_) | Str(_) | Frac(_, _) | Char(_) | Bool(_) | Closure(_, _) | Expr::EmptyRecord => Evaluated(expr),
|
||||
Error(_) | Int(_) | EmptyStr | Str(_) | InterpolatedStr(_, _) | Frac(_, _) | Char(_) | Bool(_) | Closure(_, _) | Expr::EmptyRecord => Evaluated(expr),
|
||||
|
||||
// Resolve variable names
|
||||
Var(name) => match vars.get(&name) {
|
||||
|
|
|
@ -7,7 +7,9 @@ pub enum Expr {
|
|||
// Literals
|
||||
Int(i64),
|
||||
Frac(i64, u64),
|
||||
EmptyStr,
|
||||
Str(String),
|
||||
InterpolatedStr(Vec<(String, Ident)>, String),
|
||||
Char(char),
|
||||
Bool(bool),
|
||||
|
||||
|
@ -34,7 +36,7 @@ pub enum Expr {
|
|||
Error(Problem),
|
||||
}
|
||||
|
||||
type Ident = String;
|
||||
pub type Ident = String;
|
||||
|
||||
impl fmt::Display for Expr {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
|
|
99
src/parse.rs
99
src/parse.rs
|
@ -441,8 +441,12 @@ pub fn string_literal<I>() -> impl Parser<Input = I, Output = Expr>
|
|||
where I: Stream<Item = char, Position = IndentablePosition>,
|
||||
I::Error: ParseError<I::Item, I::Range, I::Position>
|
||||
{
|
||||
between(char('"'), char('"'), many(string_body()))
|
||||
.map(|str| Expr::Str(str))
|
||||
char('"').with(
|
||||
choice((
|
||||
char('"').with(value(Expr::EmptyStr)),
|
||||
string_body()
|
||||
))
|
||||
)
|
||||
}
|
||||
|
||||
pub fn char_literal<I>() -> impl Parser<Input = I, Output = Expr>
|
||||
|
@ -487,43 +491,72 @@ where
|
|||
char('u').with(between(char('{'), char('}'), hex_code_pt))
|
||||
}
|
||||
|
||||
fn string_body<I>() -> impl Parser<Input = I, Output = char>
|
||||
fn string_body<I>() -> impl Parser<Input = I, Output = Expr>
|
||||
where
|
||||
I: Stream<Item = char, Position = IndentablePosition>,
|
||||
I::Error: ParseError<I::Item, I::Range, I::Position>,
|
||||
{
|
||||
parser(|input: &mut I| {
|
||||
let (parsed_char, consumed) = try!(any().parse_lazy(input).into());
|
||||
let mut escaped = satisfy_map(|escaped_char| {
|
||||
// NOTE! When modifying this, revisit char_body too!
|
||||
// Their implementations are similar but not the same.
|
||||
match escaped_char {
|
||||
'"' => Some('"'),
|
||||
'\\' => Some('\\'),
|
||||
't' => Some('\t'),
|
||||
'n' => Some('\n'),
|
||||
'r' => Some('\r'),
|
||||
_ => None,
|
||||
}
|
||||
});
|
||||
let mut interpolated_pairs_so_far:Option<Vec<(Ident, String)>> = None;
|
||||
let mut current_string:String = String::new();
|
||||
|
||||
match parsed_char {
|
||||
'\\' => {
|
||||
consumed.combine(|_| {
|
||||
// Try to parse basic backslash-escaped literals
|
||||
// e.g. \t, \n, \r
|
||||
escaped.parse_stream(input).or_else(|_|
|
||||
// If we didn't find any of those, try \u{...}
|
||||
unicode_code_pt().parse_stream(input)
|
||||
)
|
||||
})
|
||||
},
|
||||
'"' => {
|
||||
// We should never consume a double quote unless
|
||||
// it's preceded by a backslash
|
||||
Err(Consumed::Empty(I::Error::empty(input.position()).into()))
|
||||
},
|
||||
_ => Ok((parsed_char, consumed)),
|
||||
loop {
|
||||
let (parsed_char, consumed) = try!(any().parse_lazy(input).into());
|
||||
|
||||
match parsed_char {
|
||||
'"' => {
|
||||
// An unescaped " means we've reached the end of the string!
|
||||
match interpolated_pairs_so_far {
|
||||
Some(pairs) => {
|
||||
return Ok((Expr::InterpolatedStr(pairs, current_string), consumed));
|
||||
},
|
||||
None => {
|
||||
return Ok((Expr::Str(current_string), consumed));
|
||||
}
|
||||
};
|
||||
},
|
||||
'\\' => {
|
||||
choice((
|
||||
unicode_code_pt(),
|
||||
satisfy_map(|escaped_char| {
|
||||
// Try to parse basic backslash-escaped literals
|
||||
// e.g. \t, \n, \r
|
||||
//
|
||||
// NOTE! When modifying this, revisit char_body too!
|
||||
// Their implementations are similar but not the same.
|
||||
match escaped_char {
|
||||
'"' => Some('"'),
|
||||
'\\' => Some('\\'),
|
||||
't' => Some('\t'),
|
||||
'n' => Some('\n'),
|
||||
'r' => Some('\r'),
|
||||
_ => None,
|
||||
}
|
||||
}),
|
||||
)).parse_stream(input).map(|(escaped_char, _)| {
|
||||
current_string.push(escaped_char);
|
||||
}).or_else(|_|
|
||||
// If we didn't find any of those, try \(...)
|
||||
between(char('('), char(')'), ident())
|
||||
.parse_stream(input)
|
||||
.map(|(variable, _)| {
|
||||
let pair = (variable, current_string.clone());
|
||||
|
||||
current_string = String::new();
|
||||
|
||||
match interpolated_pairs_so_far {
|
||||
None => {
|
||||
interpolated_pairs_so_far = Some(vec![pair]);
|
||||
},
|
||||
Some(mut pairs) => {
|
||||
pairs.push(pair);
|
||||
}
|
||||
};
|
||||
})
|
||||
)?;
|
||||
},
|
||||
_ => ()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue