tamer: nir::desugar::interp: Valid parses
This completes the valid parses, though some more refactoring will be done. Next up is error handling and recovery. DEV-13156main
parent
4a7fe887d5
commit
6b9979da9a
|
@ -306,25 +306,49 @@ impl<const TY: NirSymbolTy> ParseState for InterpState<TY> {
|
|||
// Note that this is the position _relative to the offset_,
|
||||
// not the beginning of the string.
|
||||
match s[offset..].chars().position(|ch| ch == '{') {
|
||||
Some(0) => todo!("no literal prefix"),
|
||||
// The literal is the empty string,
|
||||
// which is useless to output,
|
||||
// so ignore it and proceed with parsing.
|
||||
Some(0) => {
|
||||
Transition(ParseInterpAt(s, gen_param, offset + 1))
|
||||
.incomplete()
|
||||
.with_lookahead(SugaredNirSymbol(sym, span))
|
||||
}
|
||||
|
||||
// Everything from the offset until the curly brace is a
|
||||
// literal.
|
||||
Some(pos) => {
|
||||
let literal = s[offset..pos].intern();
|
||||
Some(rel_pos) => {
|
||||
let end = offset + rel_pos;
|
||||
|
||||
let literal = s[offset..end].intern();
|
||||
let span_text =
|
||||
span.context().span_or_zz(offset, pos - offset);
|
||||
span.context().span_or_zz(offset, rel_pos);
|
||||
|
||||
let text = PlainNir::TplParamText(
|
||||
PlainNirSymbol::Todo(literal, span_text),
|
||||
);
|
||||
|
||||
Transition(ParseInterpAt(s, gen_param, pos + 1))
|
||||
Transition(ParseInterpAt(s, gen_param, end + 1))
|
||||
.ok(Expanded(text))
|
||||
.with_lookahead(SugaredNirSymbol(sym, span))
|
||||
}
|
||||
|
||||
None => todo!("remaining literal"),
|
||||
// The remainder of the specification is a literal.
|
||||
None => {
|
||||
let literal = s[offset..].intern();
|
||||
let span_text =
|
||||
span.context().span_or_zz(offset, s.len() - offset);
|
||||
|
||||
let text = PlainNir::TplParamText(
|
||||
PlainNirSymbol::Todo(literal, span_text),
|
||||
);
|
||||
|
||||
// Keep in the current state but update the offset;
|
||||
// we'll complete parsing next pass.
|
||||
Transition(ParseLiteralAt(s, gen_param, s.len()))
|
||||
.ok(Expanded(text))
|
||||
.with_lookahead(SugaredNirSymbol(sym, span))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -431,6 +455,9 @@ impl<const TY: NirSymbolTy> InterpState<TY> {
|
|||
PlainNirSymbol::Todo(gen_desc, span),
|
||||
);
|
||||
|
||||
// Begin parsing in a _literal_ context,
|
||||
// since interpolation is most commonly utilized with literal
|
||||
// prefixes.
|
||||
Transition(ParseLiteralAt(sym.lookup_str(), gen_param, 0))
|
||||
.ok(Expanded(open))
|
||||
.with_lookahead(SugaredNirSymbol(sym, span))
|
||||
|
|
|
@ -37,13 +37,18 @@ type Sut<const TY: NirSymbolTy> = InterpState<TY>;
|
|||
// Best to just leave it be.
|
||||
#[test]
|
||||
fn does_not_desugar_literal_only() {
|
||||
let sym = "foo".into();
|
||||
let toks = vec![SugaredNirSymbol::<{ StringLiteral }>(sym, S1)];
|
||||
// `@bar@` is a metavariable,
|
||||
// but it's also a literal because it's not enclosed in braces.
|
||||
for literal in ["foo", "@bar@"] {
|
||||
let sym = literal.into();
|
||||
let toks = vec![SugaredNirSymbol::<{ StringLiteral }>(sym, S1)];
|
||||
|
||||
assert_eq!(
|
||||
Ok(vec![Object(ReplaceSym(PlainNirSymbol::Todo(sym, S1)))]),
|
||||
Sut::parse(toks.into_iter()).collect(),
|
||||
);
|
||||
assert_eq!(
|
||||
Ok(vec![Object(ReplaceSym(PlainNirSymbol::Todo(sym, S1)))]),
|
||||
Sut::parse(toks.into_iter()).collect(),
|
||||
"literal `{literal}` must not desugar",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// When ending with an interpolated variable,
|
||||
|
@ -123,3 +128,208 @@ fn desugars_literal_with_ending_var() {
|
|||
|
||||
assert_eq!(sut.next(), None);
|
||||
}
|
||||
|
||||
// This is largely the same as the above test,
|
||||
// with the literal and interpolation var reversed.
|
||||
//
|
||||
// Explanations above are omitted here.
|
||||
#[test]
|
||||
fn desugars_var_with_ending_literal() {
|
||||
let given_val = "{@foo@}bar";
|
||||
// |[---] [-]
|
||||
// |1 5 7 9
|
||||
// | B C|
|
||||
// [--------]
|
||||
// 0 9
|
||||
// A
|
||||
|
||||
let a = DC.span(0, 10);
|
||||
let b = DC.span(1, 5);
|
||||
let c = DC.span(7, 3);
|
||||
|
||||
let given_sym = SugaredNirSymbol::<{ StringLiteral }>(given_val.into(), a);
|
||||
let toks = vec![given_sym];
|
||||
|
||||
let GenIdentSymbolId(expect_name) = gen_tpl_param_ident_at_offset(a);
|
||||
let expect_dfn = PlainNirSymbol::Todo(expect_name.into(), a);
|
||||
let expect_param = PlainNirSymbol::Todo("@foo@".into(), b);
|
||||
let expect_text = PlainNirSymbol::Todo("bar".into(), c);
|
||||
|
||||
let mut sut = Sut::parse(toks.into_iter());
|
||||
|
||||
//
|
||||
// See above test for explanations that are not repeated here.
|
||||
//
|
||||
|
||||
assert_matches!(
|
||||
sut.next(),
|
||||
Some(Ok(Object(Expanded(PlainNir::TplParamOpen(
|
||||
dfn,
|
||||
PlainNirSymbol::Todo(desc_str, desc_span)
|
||||
))))) if dfn == expect_dfn
|
||||
&& desc_str.lookup_str().contains(given_val)
|
||||
&& desc_span == a
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
sut.next(),
|
||||
Some(Ok(Object(Expanded(PlainNir::TplParamValue(expect_param))))),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
sut.next(),
|
||||
Some(Ok(Object(Expanded(PlainNir::TplParamText(expect_text)))))
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
sut.next(),
|
||||
Some(Ok(Object(Expanded(PlainNir::TplParamClose(a)))))
|
||||
);
|
||||
|
||||
assert_matches!(
|
||||
sut.next(),
|
||||
Some(Ok(Object(ReplaceSym(PlainNirSymbol::Todo(given_replace, given_span)))))
|
||||
if given_replace == expect_name && given_span == a
|
||||
);
|
||||
|
||||
assert_eq!(sut.next(), None);
|
||||
}
|
||||
|
||||
// Combination of the above two tests.
|
||||
//
|
||||
// Explanations above are omitted here.
|
||||
#[test]
|
||||
fn desugars_many_vars_and_literals() {
|
||||
let given_val = "foo{@bar@}baz{@quux@}";
|
||||
// [-] [---] [-] [----]|
|
||||
// 0 2 4 8 10 14 19|
|
||||
// |B C D E |
|
||||
// [-------------------]
|
||||
// 0 20
|
||||
// A
|
||||
|
||||
let a = DC.span(0, 21);
|
||||
let b = DC.span(0, 3);
|
||||
let c = DC.span(4, 5);
|
||||
let d = DC.span(10, 3);
|
||||
let e = DC.span(14, 6);
|
||||
|
||||
let given_sym = SugaredNirSymbol::<{ StringLiteral }>(given_val.into(), a);
|
||||
let toks = vec![given_sym];
|
||||
|
||||
let GenIdentSymbolId(expect_name) = gen_tpl_param_ident_at_offset(a);
|
||||
let expect_dfn = PlainNirSymbol::Todo(expect_name.into(), a);
|
||||
let expect_text1 = PlainNirSymbol::Todo("foo".into(), b);
|
||||
let expect_param1 = PlainNirSymbol::Todo("@bar@".into(), c);
|
||||
let expect_text2 = PlainNirSymbol::Todo("baz".into(), d);
|
||||
let expect_param2 = PlainNirSymbol::Todo("@quux@".into(), e);
|
||||
|
||||
let mut sut = Sut::parse(toks.into_iter());
|
||||
|
||||
//
|
||||
// See above tests for explanations that are not repeated here.
|
||||
//
|
||||
|
||||
assert_matches!(
|
||||
sut.next(),
|
||||
Some(Ok(Object(Expanded(PlainNir::TplParamOpen(
|
||||
dfn,
|
||||
PlainNirSymbol::Todo(desc_str, desc_span)
|
||||
))))) if dfn == expect_dfn
|
||||
&& desc_str.lookup_str().contains(given_val)
|
||||
&& desc_span == a
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Ok(vec![
|
||||
// These two are the as previous tests.
|
||||
Object(Expanded(PlainNir::TplParamText(expect_text1))),
|
||||
Object(Expanded(PlainNir::TplParamValue(expect_param1))),
|
||||
// This pair repeats literals and vars further into the pattern
|
||||
// to ensure that the parser is able to handle returning to
|
||||
// previous states and is able to handle inputs at different
|
||||
// offsets.
|
||||
Object(Expanded(PlainNir::TplParamText(expect_text2))),
|
||||
Object(Expanded(PlainNir::TplParamValue(expect_param2))),
|
||||
]),
|
||||
sut.by_ref().take(4).collect(),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
sut.next(),
|
||||
Some(Ok(Object(Expanded(PlainNir::TplParamClose(a)))))
|
||||
);
|
||||
|
||||
assert_matches!(
|
||||
sut.next(),
|
||||
Some(Ok(Object(ReplaceSym(PlainNirSymbol::Todo(given_replace, given_span)))))
|
||||
if given_replace == expect_name && given_span == a
|
||||
);
|
||||
|
||||
assert_eq!(sut.next(), None);
|
||||
}
|
||||
|
||||
// Adjacent vars with empty literal between them.
|
||||
#[test]
|
||||
fn desugars_adjacent_interpolated_vars() {
|
||||
let given_val = "{@foo@}{@bar@}{@baz@}";
|
||||
// |[---] [---] [---]|
|
||||
// |1 5 8 12 15 19|
|
||||
// | B C D |
|
||||
// [-------------------]
|
||||
// 0 20
|
||||
// A
|
||||
|
||||
let a = DC.span(0, 21);
|
||||
let b = DC.span(1, 5);
|
||||
let c = DC.span(8, 5);
|
||||
let d = DC.span(15, 5);
|
||||
|
||||
let given_sym = SugaredNirSymbol::<{ StringLiteral }>(given_val.into(), a);
|
||||
let toks = vec![given_sym];
|
||||
|
||||
let GenIdentSymbolId(expect_name) = gen_tpl_param_ident_at_offset(a);
|
||||
let expect_dfn = PlainNirSymbol::Todo(expect_name.into(), a);
|
||||
let expect_param1 = PlainNirSymbol::Todo("@foo@".into(), b);
|
||||
let expect_param2 = PlainNirSymbol::Todo("@bar@".into(), c);
|
||||
let expect_param3 = PlainNirSymbol::Todo("@baz@".into(), d);
|
||||
|
||||
let mut sut = Sut::parse(toks.into_iter());
|
||||
|
||||
//
|
||||
// See above tests for explanations that are not repeated here.
|
||||
//
|
||||
|
||||
assert_matches!(
|
||||
sut.next(),
|
||||
Some(Ok(Object(Expanded(PlainNir::TplParamOpen(
|
||||
dfn,
|
||||
PlainNirSymbol::Todo(desc_str, desc_span)
|
||||
))))) if dfn == expect_dfn
|
||||
&& desc_str.lookup_str().contains(given_val)
|
||||
&& desc_span == a
|
||||
);
|
||||
|
||||
// These are the three adjacent vars.
|
||||
assert_eq!(
|
||||
Ok(vec![
|
||||
Object(Expanded(PlainNir::TplParamValue(expect_param1))),
|
||||
Object(Expanded(PlainNir::TplParamValue(expect_param2))),
|
||||
Object(Expanded(PlainNir::TplParamValue(expect_param3))),
|
||||
]),
|
||||
sut.by_ref().take(3).collect(),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
sut.next(),
|
||||
Some(Ok(Object(Expanded(PlainNir::TplParamClose(a)))))
|
||||
);
|
||||
|
||||
assert_matches!(
|
||||
sut.next(),
|
||||
Some(Ok(Object(ReplaceSym(PlainNirSymbol::Todo(given_replace, given_span)))))
|
||||
if given_replace == expect_name && given_span == a
|
||||
);
|
||||
|
||||
assert_eq!(sut.next(), None);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue