Lines
70.83 %
Functions
60 %
Branches
100 %
//! Double-quoted and triple-quoted string literals.
use winnow::combinator::{alt, cut_err};
use winnow::error::{AddContext, ContextError, ErrMode, ModalResult, StrContext, StrContextValue};
use winnow::prelude::*;
use winnow::token::{any, take_till};
use crate::ast::Expr;
pub(super) fn parse_string(input: &mut &str) -> ModalResult<Expr> {
alt((parse_triple_quoted_string, parse_double_quoted_string)).parse_next(input)
}
fn parse_double_quoted_string(input: &mut &str) -> ModalResult<Expr> {
let _ = '"'.parse_next(input)?;
let mut result = String::new();
loop {
let chunk: &str = take_till(0.., |c| c == '"' || c == '\\').parse_next(input)?;
result.push_str(chunk);
match cut_err(any)
.context(StrContext::Label("string"))
.context(StrContext::Expected(StrContextValue::Description(
"closing quote",
)))
.parse_next(input)?
{
'"' => return Ok(Expr::String(result)),
'\\' => {
let escaped = any.parse_next(input)?;
match escaped {
'n' => result.push('\n'),
't' => result.push('\t'),
'r' => result.push('\r'),
'\\' => result.push('\\'),
'"' => result.push('"'),
c => {
result.push('\\');
result.push(c);
_ => unreachable!(),
fn parse_triple_quoted_string(input: &mut &str) -> ModalResult<Expr> {
let _ = "\"\"\"".parse_next(input)?;
let mut content = String::new();
if input.starts_with("\"\"\"") {
return Ok(Expr::String(content));
if input.is_empty() {
return Err(ErrMode::Cut(
ContextError::new()
.add_context(input, &input.checkpoint(), StrContext::Label("string"))
.add_context(
input,
&input.checkpoint(),
StrContext::Expected(StrContextValue::Description("closing \"\"\"")),
),
));
content.push(any.parse_next(input)?);