Skip to content

Commit ad92d3e

Browse files
authored
Fix the jsonish bug with no newline before object field (#1985)
Fix the jsonish bug with no newline before object field <!-- ELLIPSIS_HIDDEN --> ---- > [!IMPORTANT] > Fixes JSON parser bug for object fields without newline and adds test for enum parsing without leading newline. > > - **Behavior**: > - Fixes bug in `should_close_unescaped_string()` in `json_parse_state.rs` to handle object fields without a newline. > - Adds handling for identifiers as possible values in `should_close_unescaped_string()`. > - **Enums**: > - Introduces `Pos` enum in `json_parse_state.rs` to manage parsing states: `InNothing`, `Unknown`, `InObjectKey`, `InObjectValue`, `InArray`. > - **Tests**: > - Adds `test_enum_without_leading_newline` in `test_class.rs` to verify parsing of enums without leading newline. > > <sup>This description was created by </sup>[<img alt="Ellipsis" src="https://img.shields.io/badge/Ellipsis-blue?color=175173">](https://www.ellipsis.dev?ref=BoundaryML%2Fbaml&utm_source=github&utm_medium=referral)<sup> for 4e47377. You can [customize](https://app.ellipsis.dev/BoundaryML/settings/summaries) this summary. It will automatically update as commits are pushed.</sup> <!-- ELLIPSIS_HIDDEN -->
1 parent 18b57dd commit ad92d3e

2 files changed

Lines changed: 47 additions & 15 deletions

File tree

engine/baml-lib/jsonish/src/jsonish/parser/fixing_parser/json_parse_state.rs

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,15 @@ pub struct JsonParseState {
1919
pub completed_values: Vec<(&'static str, Value, Vec<Fixes>)>,
2020
}
2121

22+
#[derive(Clone, Debug)]
23+
enum Pos {
24+
InNothing, // 0
25+
Unknown, // 1
26+
InObjectKey, // 2
27+
InObjectValue, // 3
28+
InArray, // 4
29+
}
30+
2231
impl JsonParseState {
2332
pub fn new() -> Self {
2433
JsonParseState {
@@ -128,26 +137,26 @@ impl JsonParseState {
128137
&mut self,
129138
mut next: Peekable<impl Iterator<Item = (usize, char)>>,
130139
) -> CloseStringResult {
131-
let pos = if self.collection_stack.len() >= 2 {
140+
let pos: Pos = if self.collection_stack.len() >= 2 {
132141
self.collection_stack
133142
.get(self.collection_stack.len() - 2)
134143
.map(|(c, _)| match c {
135144
JsonCollection::Object(keys, values, _) => {
136145
if keys.len() == values.len() {
137-
2
146+
Pos::InObjectKey
138147
} else {
139-
3
148+
Pos::InObjectValue
140149
}
141150
}
142-
JsonCollection::Array(_, _) => 4,
143-
_ => 1,
151+
JsonCollection::Array(_, _) => Pos::InArray,
152+
_ => Pos::Unknown,
144153
})
145154
.unwrap()
146155
} else {
147-
0
156+
Pos::InNothing
148157
};
149158
match pos {
150-
0 => {
159+
Pos::InNothing => {
151160
// in nothing, so perhaps the first '{' or '[' is the start of a new object or array
152161
let mut counter = 0;
153162
for (idx, c) in next.by_ref() {
@@ -164,8 +173,8 @@ impl JsonParseState {
164173
}
165174
CloseStringResult::Close(counter, CompletionState::Incomplete)
166175
}
167-
1 => CloseStringResult::Continue,
168-
2 => {
176+
Pos::Unknown => CloseStringResult::Continue,
177+
Pos::InObjectKey => {
169178
// in object key
170179
let mut counter = 0;
171180
for (idx, c) in next.by_ref() {
@@ -179,7 +188,7 @@ impl JsonParseState {
179188
}
180189
CloseStringResult::Close(counter, CompletionState::Incomplete)
181190
}
182-
3 => {
191+
Pos::InObjectValue => {
183192
// in object value
184193
let mut counter = 0;
185194
while let Some((idx, c)) = next.next() {
@@ -198,7 +207,10 @@ impl JsonParseState {
198207
let is_bool = current_value.trim().eq_ignore_ascii_case("true")
199208
|| current_value.trim().eq_ignore_ascii_case("false");
200209
let is_null = current_value.trim().eq_ignore_ascii_case("null");
201-
let is_possible_value = is_numeric || is_bool || is_null;
210+
let is_identifier =
211+
!(current_value.contains(" ") || current_value.contains("("));
212+
let is_possible_value =
213+
is_numeric || is_bool || is_null || is_identifier;
202214

203215
if let Some((_, next_c)) = next.peek() {
204216
match next_c {
@@ -291,7 +303,7 @@ impl JsonParseState {
291303
}
292304
CloseStringResult::Close(counter, CompletionState::Incomplete)
293305
}
294-
4 => {
306+
Pos::InArray => {
295307
// in array
296308
let mut counter = 0;
297309
for (idx, c) in next {
@@ -307,7 +319,6 @@ impl JsonParseState {
307319
counter += 1; // Indicate that we called next() one time after the final `Some`.
308320
CloseStringResult::Close(counter, CompletionState::Incomplete)
309321
}
310-
_ => unreachable!("Invalid position"),
311322
}
312323
}
313324

engine/baml-lib/jsonish/src/tests/test_class.rs

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1490,7 +1490,6 @@ test_partial_deserializer_streaming!(
14901490
}
14911491
);
14921492

1493-
14941493
test_deserializer!(
14951494
test_string_in_object_with_unescaped_quotes,
14961495
r#"class Foo {
@@ -1601,4 +1600,26 @@ test_partial_deserializer_streaming!(
16011600
"rec_one": vec!["and then i said \"hi\", \"and also \"bye"],
16021601
"rec_two": []
16031602
}
1604-
);
1603+
);
1604+
1605+
test_deserializer!(
1606+
test_enum_without_leading_newline,
1607+
r#"enum Foo {
1608+
FOO
1609+
BAR
1610+
}
1611+
1612+
class WithFoo {
1613+
foo Foo
1614+
name string
1615+
}
1616+
"#,
1617+
r#"
1618+
{foo:FOO, name: "Greg"}
1619+
"#,
1620+
FieldType::Class("WithFoo".to_string()),
1621+
{
1622+
"foo": "FOO",
1623+
"name": "Greg"
1624+
}
1625+
);

0 commit comments

Comments
 (0)