Implement string interpolation

This commit is contained in:
Shunsuke Shibayama 2022-12-05 21:24:10 +09:00
parent c569df390c
commit 47132cfab1
4 changed files with 345 additions and 81 deletions

View file

@ -548,6 +548,7 @@ impl Parser {
match self.peek() {
Some(t)
if t.category_is(TC::Literal)
|| t.is(StrInterpLeft)
|| t.is(Symbol)
|| t.category_is(TC::UnaryOp)
|| t.is(LParen)
@ -1442,6 +1443,13 @@ impl Parser {
self.level -= 1;
Ok(Expr::Lit(lit))
}
Some(t) if t.is(StrInterpLeft) => {
let str_interp = self
.try_reduce_string_interpolation()
.map_err(|_| self.stack_dec())?;
self.level -= 1;
Ok(str_interp)
}
Some(t) if t.is(AtSign) => {
let decos = self.opt_reduce_decorators()?;
let expr = self.try_reduce_chunk(false, in_brace)?;
@ -2161,6 +2169,87 @@ impl Parser {
}
}
/// "...\{, expr, }..." ==> "..." + str(expr) + "..."
/// "...\{, expr, }..." ==> "..." + str(expr) + "..."
fn try_reduce_string_interpolation(&mut self) -> ParseResult<Expr> {
debug_call_info!(self);
let mut left = self.lpop();
left.content = Str::from(left.content.trim_end_matches("\\{").to_string() + "\"");
left.kind = StrLit;
let mut expr = Expr::Lit(Literal::from(left));
loop {
match self.peek() {
Some(l) if l.is(StrInterpRight) => {
let mut right = self.lpop();
right.content =
Str::from(format!("\"{}", right.content.trim_start_matches('}')));
right.kind = StrLit;
let right = Expr::Lit(Literal::from(right));
let op = Token::new(
Plus,
"+",
right.ln_begin().unwrap(),
right.col_begin().unwrap(),
);
expr = Expr::BinOp(BinOp::new(op, expr, right));
self.level -= 1;
return Ok(expr);
}
Some(_) => {
let mid_expr = self.try_reduce_expr(true, false, false, false)?;
let str_func = Expr::local(
"str",
mid_expr.ln_begin().unwrap(),
mid_expr.col_begin().unwrap(),
);
let call = Call::new(
str_func,
None,
Args::new(vec![PosArg::new(mid_expr)], vec![], None),
);
let op = Token::new(
Plus,
"+",
call.ln_begin().unwrap(),
call.col_begin().unwrap(),
);
let bin = BinOp::new(op, expr, Expr::Call(call));
expr = Expr::BinOp(bin);
if self.cur_is(StrInterpMid) {
let mut mid = self.lpop();
mid.content = Str::from(format!(
"\"{}\"",
mid.content.trim_start_matches('}').trim_end_matches("\\{")
));
mid.kind = StrLit;
let mid = Expr::Lit(Literal::from(mid));
let op = Token::new(
Plus,
"+",
mid.ln_begin().unwrap(),
mid.col_begin().unwrap(),
);
expr = Expr::BinOp(BinOp::new(op, expr, mid));
}
}
None => {
self.level -= 1;
let err = ParseError::syntax_error(
line!() as usize,
expr.loc(),
switch_lang!(
"japanese" => "文字列補間の終わりが見つかりませんでした",
"english" => "end of string interpolation not found",
),
None,
);
self.errs.push(err);
return Err(());
}
}
}
}
/// x |> f() => f(x)
fn try_reduce_stream_operator(&mut self, stack: &mut Vec<ExprOrOp>) -> ParseResult<()> {
debug_call_info!(self);