parseJSONWithStackTracking parses JSON while tracking the stack structure Returns the error position and any error encountered This implements stack tracking similar to llama.cpp's json_error_locator
(input string, errLoc *JSONErrorLocator)
| 244 | // Returns the error position and any error encountered |
| 245 | // This implements stack tracking similar to llama.cpp's json_error_locator |
| 246 | func parseJSONWithStackTracking(input string, errLoc *JSONErrorLocator) (int, error) { |
| 247 | // First, try to parse to get exact error position |
| 248 | decoder := json.NewDecoder(strings.NewReader(input)) |
| 249 | var test any |
| 250 | err := decoder.Decode(&test) |
| 251 | if err != nil { |
| 252 | errLoc.foundError = true |
| 253 | errLoc.exceptionMessage = err.Error() |
| 254 | |
| 255 | var errorPos int |
| 256 | if syntaxErr, ok := err.(*json.SyntaxError); ok { |
| 257 | errorPos = int(syntaxErr.Offset) |
| 258 | errLoc.position = errorPos |
| 259 | } else { |
| 260 | // Fallback: use end of input |
| 261 | errorPos = len(input) |
| 262 | errLoc.position = errorPos |
| 263 | } |
| 264 | |
| 265 | // Now build the stack by parsing up to the error position |
| 266 | // This matches llama.cpp's approach of tracking stack during SAX parsing |
| 267 | partialInput := input |
| 268 | if errorPos > 0 && errorPos < len(input) { |
| 269 | partialInput = input[:errorPos] |
| 270 | } |
| 271 | |
| 272 | // Track stack by parsing character by character up to error |
| 273 | pos := 0 |
| 274 | inString := false |
| 275 | escape := false |
| 276 | keyStart := -1 |
| 277 | keyEnd := -1 |
| 278 | |
| 279 | for pos < len(partialInput) { |
| 280 | ch := partialInput[pos] |
| 281 | |
| 282 | if escape { |
| 283 | escape = false |
| 284 | pos++ |
| 285 | continue |
| 286 | } |
| 287 | |
| 288 | if ch == '\\' { |
| 289 | escape = true |
| 290 | pos++ |
| 291 | continue |
| 292 | } |
| 293 | |
| 294 | if ch == '"' { |
| 295 | if !inString { |
| 296 | // Starting a string |
| 297 | inString = true |
| 298 | // Check if we're in an object context (expecting a key) |
| 299 | if len(errLoc.stack) > 0 { |
| 300 | top := errLoc.stack[len(errLoc.stack)-1] |
| 301 | if top.Type == JSONStackElementObject { |
| 302 | // This could be a key |
| 303 | keyStart = pos + 1 // Start after quote |
no test coverage detected