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_span = p.span();
|
||||||
let name = p.quoted()?;
|
let name = p.quoted()?;
|
||||||
let identity = p.identity()?;
|
let identity = p.identity()?;
|
||||||
|
p.open()?;
|
||||||
let opening = p.take("{")?;
|
let opening = p.take("{")?;
|
||||||
let header = Header {
|
let header = Header {
|
||||||
kind,
|
kind,
|
||||||
@@ -363,6 +364,7 @@ pub(super) fn parse(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
let closing = p.take("}")?;
|
let closing = p.take("}")?;
|
||||||
|
p.close();
|
||||||
let body = fields.finish(kind, parameters, closing)?;
|
let body = fields.finish(kind, parameters, closing)?;
|
||||||
Ok(Declaration {
|
Ok(Declaration {
|
||||||
header,
|
header,
|
||||||
@@ -527,6 +529,7 @@ impl Cursor<'_, '_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn dimension(&mut self) -> Result<DimensionSyntax, Error> {
|
fn dimension(&mut self) -> Result<DimensionSyntax, Error> {
|
||||||
|
self.open()?;
|
||||||
self.take("{")?;
|
self.take("{")?;
|
||||||
let mut exponents = [0; 7];
|
let mut exponents = [0; 7];
|
||||||
let mut seen = [false; 7];
|
let mut seen = [false; 7];
|
||||||
@@ -577,6 +580,7 @@ impl Cursor<'_, '_> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
self.take("}")?;
|
self.take("}")?;
|
||||||
|
self.close();
|
||||||
Ok(DimensionSyntax { exponents, entries })
|
Ok(DimensionSyntax { exponents, entries })
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -595,6 +599,7 @@ impl Cursor<'_, '_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn overrides(&mut self) -> Result<Vec<OverrideSyntax>, Error> {
|
fn overrides(&mut self) -> Result<Vec<OverrideSyntax>, Error> {
|
||||||
|
self.open()?;
|
||||||
self.take("{")?;
|
self.take("{")?;
|
||||||
let mut entries = Vec::new();
|
let mut entries = Vec::new();
|
||||||
let mut names = BTreeSet::new();
|
let mut names = BTreeSet::new();
|
||||||
@@ -621,6 +626,7 @@ impl Cursor<'_, '_> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
self.take("}")?;
|
self.take("}")?;
|
||||||
|
self.close();
|
||||||
Ok(entries)
|
Ok(entries)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -506,7 +506,7 @@ fn declaration_budgets_share_literals_types_references_and_failure_state() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn typed_frame_budget_and_truncated_declaration_cleanup_have_nearby_successes() {
|
fn typed_frame_budget_and_truncated_declaration_cleanup_have_nearby_successes() {
|
||||||
let source = "calculation c \"C\" { result List(Quantity(@q)); body unbound; }";
|
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();
|
let mut profile = limits();
|
||||||
profile.frames = maximum;
|
profile.frames = maximum;
|
||||||
let mut shared = Budget::new(profile).unwrap();
|
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; }");
|
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, "\"雪\""),
|
(String, "\"雪\""),
|
||||||
(Whitespace, " "),
|
(Whitespace, " "),
|
||||||
(Punctuation, "@"),
|
(Punctuation, "@"),
|
||||||
(Identifier, "alias"),
|
(Keyword, "alias"),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
("\"incomplete 雪", &[(Error, "\"incomplete 雪")]),
|
("\"incomplete 雪", &[(Error, "\"incomplete 雪")]),
|
||||||
|
|||||||
Reference in New Issue
Block a user