fix(source): count typed braces in shared syntax frames
This commit is contained in:
@@ -264,6 +264,7 @@ pub(super) fn parse(
|
||||
let name_span = p.span();
|
||||
let name = p.quoted()?;
|
||||
let identity = p.identity()?;
|
||||
p.open()?;
|
||||
let opening = p.take("{")?;
|
||||
let header = Header {
|
||||
kind,
|
||||
@@ -363,6 +364,7 @@ pub(super) fn parse(
|
||||
}
|
||||
}
|
||||
let closing = p.take("}")?;
|
||||
p.close();
|
||||
let body = fields.finish(kind, parameters, closing)?;
|
||||
Ok(Declaration {
|
||||
header,
|
||||
@@ -527,6 +529,7 @@ impl Cursor<'_, '_> {
|
||||
}
|
||||
|
||||
fn dimension(&mut self) -> Result<DimensionSyntax, Error> {
|
||||
self.open()?;
|
||||
self.take("{")?;
|
||||
let mut exponents = [0; 7];
|
||||
let mut seen = [false; 7];
|
||||
@@ -577,6 +580,7 @@ impl Cursor<'_, '_> {
|
||||
});
|
||||
}
|
||||
self.take("}")?;
|
||||
self.close();
|
||||
Ok(DimensionSyntax { exponents, entries })
|
||||
}
|
||||
|
||||
@@ -595,6 +599,7 @@ impl Cursor<'_, '_> {
|
||||
}
|
||||
|
||||
fn overrides(&mut self) -> Result<Vec<OverrideSyntax>, Error> {
|
||||
self.open()?;
|
||||
self.take("{")?;
|
||||
let mut entries = Vec::new();
|
||||
let mut names = BTreeSet::new();
|
||||
@@ -621,6 +626,7 @@ impl Cursor<'_, '_> {
|
||||
});
|
||||
}
|
||||
self.take("}")?;
|
||||
self.close();
|
||||
Ok(entries)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -506,7 +506,7 @@ fn declaration_budgets_share_literals_types_references_and_failure_state() {
|
||||
#[test]
|
||||
fn typed_frame_budget_and_truncated_declaration_cleanup_have_nearby_successes() {
|
||||
let source = "calculation c \"C\" { result List(Quantity(@q)); body unbound; }";
|
||||
for (maximum, succeeds) in [(2, true), (1, false)] {
|
||||
for (maximum, succeeds) in [(3, true), (2, false)] {
|
||||
let mut profile = limits();
|
||||
profile.frames = maximum;
|
||||
let mut shared = Budget::new(profile).unwrap();
|
||||
@@ -529,3 +529,85 @@ fn typed_frame_budget_and_truncated_declaration_cleanup_have_nearby_successes()
|
||||
declaration("oa_check fresh \"Fresh\" { predicate true; }");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn typed_brace_frames_have_exact_zero_one_two_boundaries() {
|
||||
let mut profile = limits();
|
||||
profile.frames = 0;
|
||||
let mut shared = Budget::new(profile).unwrap();
|
||||
let tokens = lex::tokens("unbound", &mut shared).unwrap();
|
||||
let mut cursor = Cursor::new("unbound", &tokens, &mut shared);
|
||||
assert!(matches!(
|
||||
syntax::binding(&mut cursor).unwrap(),
|
||||
Binding::Unbound(_)
|
||||
));
|
||||
assert_eq!(cursor.frames, 0);
|
||||
assert_eq!(shared.usage.deepest_frame, 0);
|
||||
|
||||
for (source, needed) in [
|
||||
("calculation c \"C\" { result Number; body unbound; }", 1),
|
||||
("quantity_kind q \"Q\" { dimension {}; algebra Linear; }", 2),
|
||||
(
|
||||
"oa_analysis a \"A\" { base ModelBaseline; overrides {}; }",
|
||||
2,
|
||||
),
|
||||
] {
|
||||
for maximum in 0..=2 {
|
||||
let mut profile = limits();
|
||||
profile.frames = maximum;
|
||||
let mut shared = Budget::new(profile).unwrap();
|
||||
let tokens = lex::tokens(source, &mut shared).unwrap();
|
||||
let mut cursor = Cursor::new(source, &tokens, &mut shared);
|
||||
let result = parse(&mut cursor, 8, None);
|
||||
assert_eq!(
|
||||
result.is_ok(),
|
||||
maximum >= needed,
|
||||
"{source}, limit {maximum}"
|
||||
);
|
||||
if let Err(error) = result {
|
||||
assert!(error.message.contains("frames"));
|
||||
assert_eq!(text(source, error.span), "{");
|
||||
assert_eq!(
|
||||
error.span.start,
|
||||
if maximum == 0 {
|
||||
source.find('{').unwrap()
|
||||
} else {
|
||||
source.rfind('{').unwrap()
|
||||
}
|
||||
);
|
||||
} else {
|
||||
assert_eq!(cursor.frames, 0);
|
||||
cursor.end().unwrap();
|
||||
assert_eq!(shared.usage.deepest_frame, needed);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn declaration_frames_restore_the_callers_inherited_context() {
|
||||
let source = "calculation a \"A\" { result Number; body unbound; } calculation b \"B\" { result Number; body unbound; }";
|
||||
for maximum in [1, 2] {
|
||||
let mut profile = limits();
|
||||
profile.frames = maximum;
|
||||
let mut shared = Budget::new(profile).unwrap();
|
||||
let tokens = lex::tokens(source, &mut shared).unwrap();
|
||||
let mut cursor = Cursor::new(source, &tokens, &mut shared);
|
||||
// Future project/include integration owns this outer context. The
|
||||
// declaration consumer must add to it, then restore it after closing.
|
||||
cursor.frames = 1;
|
||||
let first = parse(&mut cursor, 8, None);
|
||||
if maximum == 1 {
|
||||
let error = first.unwrap_err();
|
||||
assert!(error.message.contains("frames"));
|
||||
assert_eq!(text(source, error.span), "{");
|
||||
} else {
|
||||
assert!(first.is_ok());
|
||||
assert_eq!(cursor.frames, 1);
|
||||
parse(&mut cursor, 8, None).unwrap();
|
||||
assert_eq!(cursor.frames, 1);
|
||||
cursor.end().unwrap();
|
||||
assert_eq!(shared.usage.deepest_frame, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -302,7 +302,7 @@ mod tests {
|
||||
(String, "\"雪\""),
|
||||
(Whitespace, " "),
|
||||
(Punctuation, "@"),
|
||||
(Identifier, "alias"),
|
||||
(Keyword, "alias"),
|
||||
],
|
||||
),
|
||||
("\"incomplete 雪", &[(Error, "\"incomplete 雪")]),
|
||||
|
||||
Reference in New Issue
Block a user