parse interpolated strings
This commit is contained in:
parent
c5c1717e57
commit
62671094a0
15
src/main.rs
15
src/main.rs
|
@ -58,12 +58,7 @@ use crate::base::*;
|
|||
|
||||
pub fn main() {
|
||||
let src = "
|
||||
fn foo {
|
||||
\"this is a docstring\"
|
||||
() -> :foo
|
||||
(_) -> :bar
|
||||
}
|
||||
doc (foo)
|
||||
\"{} {foobar}\"
|
||||
";
|
||||
let (tokens, lex_errs) = lexer().parse(src).into_output_errors();
|
||||
if !lex_errs.is_empty() {
|
||||
|
@ -78,13 +73,13 @@ doc (foo)
|
|||
let (ast, _) = parser()
|
||||
.parse(Stream::from_iter(to_parse).map((0..src.len()).into(), |(t, s)| (t, s)))
|
||||
.unwrap();
|
||||
// println!("{}", ast);
|
||||
println!("{}", ast);
|
||||
|
||||
let mut ctx = base();
|
||||
// let mut ctx = base();
|
||||
|
||||
let result = eval(&ast, &mut ctx).unwrap();
|
||||
// let result = eval(&ast, &mut ctx).unwrap();
|
||||
|
||||
println!("{}", result);
|
||||
// println!("{}", result);
|
||||
|
||||
// struct_scalpel::print_dissection_info::<value::Value>()
|
||||
// struct_scalpel::print_dissection_info::<parser::Ast>();
|
||||
|
|
125
src/parser.rs
125
src/parser.rs
|
@ -34,16 +34,18 @@ impl<'src> fmt::Display for MatchClause<'src> {
|
|||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum StringPart<'src> {
|
||||
Data(&'src str),
|
||||
Word(&'src str),
|
||||
pub enum StringPart {
|
||||
Data(String),
|
||||
Word(String),
|
||||
Inline(String),
|
||||
}
|
||||
|
||||
impl<'src> fmt::Display for StringPart<'src> {
|
||||
fn fmt(self: &StringPart<'src>, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
impl fmt::Display for StringPart {
|
||||
fn fmt(self: &StringPart, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let rep = match self {
|
||||
StringPart::Data(s) => format!("{{{s}}}"),
|
||||
StringPart::Word(s) => s.to_string(),
|
||||
StringPart::Word(s) => format!("{{{s}}}"),
|
||||
StringPart::Data(s) => s.to_string(),
|
||||
StringPart::Inline(s) => s.to_string(),
|
||||
};
|
||||
write!(f, "{}", rep)
|
||||
}
|
||||
|
@ -59,7 +61,7 @@ pub enum Ast<'src> {
|
|||
Keyword(&'src str),
|
||||
Word(&'src str),
|
||||
String(&'src str),
|
||||
Interpolated(Vec<Spanned<StringPart<'src>>>),
|
||||
Interpolated(Vec<Spanned<StringPart>>),
|
||||
Block(Vec<Spanned<Self>>),
|
||||
If(Box<Spanned<Self>>, Box<Spanned<Self>>, Box<Spanned<Self>>),
|
||||
Tuple(Vec<Spanned<Self>>),
|
||||
|
@ -91,7 +93,7 @@ impl fmt::Display for Ast<'_> {
|
|||
Ast::Interpolated(strs) => {
|
||||
write!(
|
||||
f,
|
||||
"String: \"{}\"",
|
||||
"Interpolated: \"{}\"",
|
||||
strs.iter()
|
||||
.map(|(s, _)| s.to_string())
|
||||
.collect::<Vec<_>>()
|
||||
|
@ -251,6 +253,7 @@ pub enum Pattern<'src> {
|
|||
Boolean(bool),
|
||||
Number(f64),
|
||||
String(&'src str),
|
||||
Interpolated(Vec<StringPart>),
|
||||
Keyword(&'src str),
|
||||
Word(&'src str),
|
||||
As(&'src str, &'src str),
|
||||
|
@ -300,10 +303,26 @@ impl fmt::Display for Pattern<'_> {
|
|||
.join(", ")
|
||||
),
|
||||
Pattern::Pair(key, value) => write!(f, ":{} {}", key, value.0),
|
||||
Pattern::Interpolated(strprts) => write!(
|
||||
f,
|
||||
"\"{}\"",
|
||||
strprts
|
||||
.iter()
|
||||
.map(|part| part.to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join("")
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_word_char(c: char) -> bool {
|
||||
if c.is_ascii_alphanumeric() {
|
||||
return true;
|
||||
};
|
||||
matches!(c, '_' | '/' | '?' | '!')
|
||||
}
|
||||
|
||||
// TODO: write this
|
||||
// 1. we need an enum for a return type
|
||||
// either a string part or a word part
|
||||
|
@ -311,8 +330,81 @@ impl fmt::Display for Pattern<'_> {
|
|||
// 3. this should loop through the string and allow for escaping braces
|
||||
// consider using Rust-style escapes: {{}}, rather than \{\}
|
||||
// {{{foo}}}
|
||||
pub fn parse_string<'src>(s: &'src str) -> Vec<StringPart<'src>> {
|
||||
vec![]
|
||||
fn parse_string(s: &str, span: SimpleSpan) -> Result<Vec<Spanned<StringPart>>, String> {
|
||||
let mut parts = vec![];
|
||||
let mut current_part = String::new();
|
||||
let mut start = span.start;
|
||||
let mut is_word = false;
|
||||
|
||||
let mut chars = s.char_indices();
|
||||
while let Some((i, char)) = chars.next() {
|
||||
match char {
|
||||
'{' => {
|
||||
if is_word {
|
||||
return Err("interpolations must only contain words".to_string());
|
||||
};
|
||||
match chars.next() {
|
||||
None => return Err("unclosed brace".to_string()),
|
||||
Some((_, '{')) => current_part.push('{'),
|
||||
Some((i, c)) => {
|
||||
if !current_part.is_empty() {
|
||||
parts.push((
|
||||
StringPart::Data(current_part),
|
||||
SimpleSpan::new(start, start + i),
|
||||
));
|
||||
};
|
||||
current_part = String::new();
|
||||
start = i;
|
||||
is_word = true;
|
||||
if c.is_ascii_lowercase() {
|
||||
current_part.push(c);
|
||||
} else {
|
||||
return Err("interpolations must only contain words".to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
'}' => {
|
||||
if is_word {
|
||||
parts.push((
|
||||
StringPart::Word(current_part),
|
||||
SimpleSpan::new(start, start + i),
|
||||
));
|
||||
current_part = String::new();
|
||||
start = i;
|
||||
is_word = false;
|
||||
} else {
|
||||
match chars.next() {
|
||||
None => return Err("unclosed brace".to_string()),
|
||||
Some((_, c)) => current_part.push(c),
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
if is_word {
|
||||
if is_word_char(char) {
|
||||
current_part.push(char)
|
||||
} else {
|
||||
return Err("interpolations must only contain words".to_string());
|
||||
}
|
||||
} else {
|
||||
current_part.push(char)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parts.push((
|
||||
if is_word {
|
||||
StringPart::Word(current_part)
|
||||
} else if current_part == s.to_string() {
|
||||
StringPart::Inline(current_part)
|
||||
} else {
|
||||
StringPart::Data(current_part)
|
||||
},
|
||||
SimpleSpan::new(start, span.end),
|
||||
));
|
||||
Ok(parts)
|
||||
}
|
||||
|
||||
pub fn parser<'src, I>(
|
||||
|
@ -448,7 +540,16 @@ where
|
|||
}
|
||||
.map_with(|v, e| (v, e.span()));
|
||||
|
||||
let string = select! {Token::String(s) => Ast::String(s)}.map_with(|s, e| (s, e.span()));
|
||||
let string = select! {Token::String(s) => s}.try_map_with(|s, e| {
|
||||
let parsed = parse_string(s, e.span());
|
||||
match parsed {
|
||||
Ok(parts) => match parts[0] {
|
||||
(StringPart::Inline(_), _) => Ok((Ast::String(s), e.span())),
|
||||
_ => Ok((Ast::Interpolated(parts), e.span())),
|
||||
},
|
||||
Err(msg) => Err(Rich::custom(e.span(), msg)),
|
||||
}
|
||||
});
|
||||
|
||||
let tuple = simple
|
||||
.clone()
|
||||
|
|
Loading…
Reference in New Issue
Block a user