jsonrepair.go

   1// Package jsonrepair provides utilities to repair malformed JSON.
   2package jsonrepair
   3
   4import (
   5	"bytes"
   6	"encoding/json"
   7	"errors"
   8	"reflect"
   9	"slices"
  10	"strconv"
  11	"strings"
  12	"unicode"
  13	"unicode/utf16"
  14)
  15
  16// Option is a function that configures the JSON repairer.
  17type Option func(*options)
  18
  19type options struct {
  20	ensureASCII   *bool
  21	skipJSONLoads bool
  22	streamStable  bool
  23	strict        bool
  24}
  25
  26// LogEntry represents a log entry with context and text.
  27type LogEntry struct {
  28	Context string `json:"context"`
  29	Text    string `json:"text"`
  30}
  31
  32type numberValue struct {
  33	raw string
  34}
  35
  36type objectEntry struct {
  37	key   string
  38	value any
  39}
  40
  41type orderedObject struct {
  42	entries []objectEntry
  43	index   map[string]int
  44}
  45
  46func newOrderedObject() *orderedObject {
  47	return &orderedObject{index: map[string]int{}}
  48}
  49
  50func (o *orderedObject) set(key string, value any) {
  51	if idx, ok := o.index[key]; ok {
  52		o.entries[idx].value = value
  53		return
  54	}
  55	o.index[key] = len(o.entries)
  56	o.entries = append(o.entries, objectEntry{key: key, value: value})
  57}
  58
  59func (o *orderedObject) get(key string) (any, bool) {
  60	idx, ok := o.index[key]
  61	if !ok {
  62		return nil, false
  63	}
  64	return o.entries[idx].value, true
  65}
  66
  67func (o *orderedObject) lastKey() (string, bool) {
  68	if len(o.entries) == 0 {
  69		return "", false
  70	}
  71	return o.entries[len(o.entries)-1].key, true
  72}
  73
  74func (o *orderedObject) hasKey(key string) bool {
  75	_, ok := o.index[key]
  76	return ok
  77}
  78
  79func (o *orderedObject) merge(other *orderedObject) {
  80	for _, entry := range other.entries {
  81		o.set(entry.key, entry.value)
  82	}
  83}
  84
  85type contextValue int
  86
  87const (
  88	contextObjectKey contextValue = iota
  89	contextObjectValue
  90	contextArray
  91)
  92
  93type jsonContext struct {
  94	context []contextValue
  95	current *contextValue
  96	empty   bool
  97}
  98
  99func newJSONContext() *jsonContext {
 100	return &jsonContext{empty: true}
 101}
 102
 103func (c *jsonContext) set(value contextValue) {
 104	c.context = append(c.context, value)
 105	c.current = &c.context[len(c.context)-1]
 106	c.empty = false
 107}
 108
 109func (c *jsonContext) reset() {
 110	if len(c.context) > 0 {
 111		c.context = c.context[:len(c.context)-1]
 112	}
 113	if len(c.context) == 0 {
 114		c.current = nil
 115		c.empty = true
 116		return
 117	}
 118	c.current = &c.context[len(c.context)-1]
 119}
 120
 121func (c *jsonContext) contains(value contextValue) bool {
 122	return slices.Contains(c.context, value)
 123}
 124
 125type parser struct {
 126	jsonStr      []rune
 127	index        int
 128	context      *jsonContext
 129	logging      bool
 130	logger       []LogEntry
 131	streamStable bool
 132	strict       bool
 133	log          func(string)
 134}
 135
 136func newParser(input string, logging bool, streamStable bool, strict bool) *parser {
 137	p := &parser{
 138		jsonStr:      []rune(input),
 139		context:      newJSONContext(),
 140		logging:      logging,
 141		streamStable: streamStable,
 142		strict:       strict,
 143	}
 144	if logging {
 145		p.log = p.addLog
 146	} else {
 147		p.log = func(string) {}
 148	}
 149	return p
 150}
 151
 152func (p *parser) addLog(text string) {
 153	window := 10
 154	start := max(p.index-window, 0)
 155	end := min(p.index+window, len(p.jsonStr))
 156	context := string(p.jsonStr[start:end])
 157	p.logger = append(p.logger, LogEntry{Text: text, Context: context})
 158}
 159
 160func (p *parser) parse() (any, []LogEntry, error) {
 161	jsonValue, err := p.parseJSON()
 162	if err != nil {
 163		return nil, nil, err
 164	}
 165	if p.index < len(p.jsonStr) {
 166		p.log("The parser returned early, checking if there's more json elements")
 167		values := []any{jsonValue}
 168		for p.index < len(p.jsonStr) {
 169			p.context.reset()
 170			j, parseErr := p.parseJSON()
 171			if parseErr != nil {
 172				return nil, nil, parseErr
 173			}
 174			if isTruthy(j) {
 175				if len(values) > 0 && isSameObject(values[len(values)-1], j) {
 176					values = values[:len(values)-1]
 177				} else if len(values) > 0 && !isTruthy(values[len(values)-1]) {
 178					values = values[:len(values)-1]
 179				}
 180				values = append(values, j)
 181			} else {
 182				if len(values) > 1 {
 183					_, ok := p.getCharAt(0)
 184					if !ok {
 185						break
 186					}
 187					if len(values) > 1 {
 188						values = values[:len(values)-1]
 189					}
 190					p.index = len(p.jsonStr)
 191					break
 192				}
 193				p.index++
 194			}
 195		}
 196		if len(values) == 1 {
 197			p.log("There were no more elements, returning the element without the array")
 198			jsonValue = values[0]
 199		} else if p.strict {
 200			p.log("Multiple top-level JSON elements found in strict mode, raising an error")
 201			return nil, nil, errors.New("multiple top-level JSON elements found in strict mode")
 202		} else {
 203			jsonValue = values
 204		}
 205	}
 206	return jsonValue, p.logger, nil
 207}
 208
 209func (p *parser) parseJSON() (any, error) {
 210	for {
 211		char, ok := p.getCharAt(0)
 212		if !ok {
 213			return "", nil
 214		}
 215		if char == '{' {
 216			p.index++
 217			return p.parseObject()
 218		}
 219		if char == '[' {
 220			p.index++
 221			return p.parseArray()
 222		}
 223		if !p.context.empty && (isStringDelimiter(char) || unicode.IsLetter(char)) {
 224			return p.parseString()
 225		}
 226		if !p.context.empty && (unicode.IsDigit(char) || char == '-' || char == '.') {
 227			return p.parseNumber()
 228		}
 229		if p.context.empty && (unicode.IsDigit(char) || char == '-' || char == '.') {
 230			if onlyWhitespaceBefore(p) {
 231				return p.parseNumber()
 232			}
 233		}
 234		if char == '#' || char == '/' {
 235			return p.parseComment()
 236		}
 237		if !p.context.empty && (char == 't' || char == 'f' || char == 'n') {
 238			value := p.parseBooleanOrNull()
 239			if value != "" {
 240				return value, nil
 241			}
 242			return p.parseString()
 243		}
 244		if p.context.empty && (char == 't' || char == 'f' || char == 'n') {
 245			if onlyWhitespaceBefore(p) {
 246				value := p.parseBooleanOrNull()
 247				if value != "" {
 248					return value, nil
 249				}
 250			}
 251		}
 252		if p.context.empty && char == ':' {
 253			return "", nil
 254		}
 255		p.index++
 256	}
 257}
 258
 259func (p *parser) getCharAt(offset int) (rune, bool) {
 260	idx := p.index + offset
 261	if idx < 0 || idx >= len(p.jsonStr) {
 262		return 0, false
 263	}
 264	return p.jsonStr[idx], true
 265}
 266
 267func (p *parser) skipWhitespaces() {
 268	for {
 269		char, ok := p.getCharAt(0)
 270		if !ok || !unicode.IsSpace(char) {
 271			return
 272		}
 273		p.index++
 274	}
 275}
 276
 277func (p *parser) scrollWhitespaces(idx int) int {
 278	for {
 279		char, ok := p.getCharAt(idx)
 280		if !ok || !unicode.IsSpace(char) {
 281			return idx
 282		}
 283		idx++
 284	}
 285}
 286
 287func (p *parser) skipToCharacter(character rune, idx int) int {
 288	targets := map[rune]struct{}{character: {}}
 289	return p.skipToCharacters(targets, idx)
 290}
 291
 292func (p *parser) skipToCharacters(targets map[rune]struct{}, idx int) int {
 293	i := p.index + idx
 294	backslashes := 0
 295	for i < len(p.jsonStr) {
 296		ch := p.jsonStr[i]
 297		if ch == '\\' {
 298			backslashes++
 299			i++
 300			continue
 301		}
 302		if _, ok := targets[ch]; ok && backslashes%2 == 0 {
 303			return i - p.index
 304		}
 305		backslashes = 0
 306		i++
 307	}
 308	return len(p.jsonStr) - p.index
 309}
 310
 311func (p *parser) parseArray() (any, error) {
 312	arr := []any{}
 313	p.context.set(contextArray)
 314	char, ok := p.getCharAt(0)
 315	for ok && char != ']' && char != '}' {
 316		p.skipWhitespaces()
 317		var value any
 318		if isStringDelimiter(char) {
 319			i := 1
 320			i = p.skipToCharacter(char, i)
 321			i = p.scrollWhitespaces(i + 1)
 322			if nextChar, ok := p.getCharAt(i); ok && nextChar == ':' {
 323				value, _ = p.parseObject()
 324			} else {
 325				value, _ = p.parseString()
 326			}
 327		} else {
 328			var err error
 329			value, err = p.parseJSON()
 330			if err != nil {
 331				return nil, err
 332			}
 333		}
 334
 335		if isStrictlyEmpty(value) {
 336			if nextChar, ok := p.getCharAt(0); !ok || (nextChar != ']' && nextChar != ',') {
 337				p.index++
 338			} else {
 339				arr = append(arr, value)
 340			}
 341		} else if strVal, ok := value.(string); ok && strVal == "..." {
 342			if prev, ok := p.getCharAt(-1); ok && prev == '.' {
 343				p.log("While parsing an array, found a stray '...'; ignoring it")
 344			} else {
 345				arr = append(arr, value)
 346			}
 347		} else {
 348			arr = append(arr, value)
 349		}
 350
 351		char, ok = p.getCharAt(0)
 352		for ok && char != ']' && (unicode.IsSpace(char) || char == ',') {
 353			p.index++
 354			char, ok = p.getCharAt(0)
 355		}
 356	}
 357
 358	if char != ']' {
 359		p.log("While parsing an array we missed the closing ], ignoring it")
 360	}
 361
 362	p.index++
 363	p.context.reset()
 364	return arr, nil
 365}
 366
 367func (p *parser) parseComment() (any, error) {
 368	char, ok := p.getCharAt(0)
 369	if !ok {
 370		return "", nil
 371	}
 372	termination := map[rune]struct{}{'\n': {}, '\r': {}}
 373	if p.context.contains(contextArray) {
 374		termination[']'] = struct{}{}
 375	}
 376	if p.context.contains(contextObjectValue) {
 377		termination['}'] = struct{}{}
 378	}
 379	if p.context.contains(contextObjectKey) {
 380		termination[':'] = struct{}{}
 381	}
 382	if char == '#' {
 383		comment := []rune{}
 384		for ok {
 385			if _, hit := termination[char]; hit {
 386				break
 387			}
 388			comment = append(comment, char)
 389			p.index++
 390			char, ok = p.getCharAt(0)
 391		}
 392		p.log("Found line comment: " + string(comment) + ", ignoring")
 393	} else if char == '/' {
 394		nextChar, ok := p.getCharAt(1)
 395		if ok && nextChar == '/' {
 396			comment := []rune{'/', '/'}
 397			p.index += 2
 398			char, ok = p.getCharAt(0)
 399			for ok {
 400				if _, hit := termination[char]; hit {
 401					break
 402				}
 403				comment = append(comment, char)
 404				p.index++
 405				char, ok = p.getCharAt(0)
 406			}
 407			p.log("Found line comment: " + string(comment) + ", ignoring")
 408		} else if ok && nextChar == '*' {
 409			comment := []rune{'/', '*'}
 410			p.index += 2
 411			for {
 412				char, ok = p.getCharAt(0)
 413				if !ok {
 414					p.log("Reached end-of-string while parsing block comment; unclosed block comment.")
 415					break
 416				}
 417				comment = append(comment, char)
 418				p.index++
 419				if len(comment) >= 2 && comment[len(comment)-2] == '*' && comment[len(comment)-1] == '/' {
 420					break
 421				}
 422			}
 423			p.log("Found block comment: " + string(comment) + ", ignoring")
 424		} else {
 425			p.index++
 426		}
 427	}
 428	if p.context.empty {
 429		return p.parseJSON()
 430	}
 431	return "", nil
 432}
 433
 434func (p *parser) parseNumber() (any, error) {
 435	numberChars := "0123456789-.eE/,_"
 436	numberStr := ""
 437	char, ok := p.getCharAt(0)
 438	isArray := p.context.current != nil && *p.context.current == contextArray
 439	for ok && strings.ContainsRune(numberChars, char) && (!isArray || char != ',' || strings.Contains(numberStr, "/")) {
 440		if char != '_' {
 441			numberStr += string(char)
 442		}
 443		p.index++
 444		char, ok = p.getCharAt(0)
 445	}
 446	if nextChar, ok := p.getCharAt(0); ok && unicode.IsLetter(nextChar) {
 447		p.index -= len([]rune(numberStr))
 448		return p.parseString()
 449	}
 450	if len(numberStr) > 0 {
 451		last := numberStr[len(numberStr)-1]
 452		if last == '-' || last == 'e' || last == 'E' || last == '/' || last == ',' {
 453			numberStr = numberStr[:len(numberStr)-1]
 454			p.index--
 455		}
 456	}
 457	if strings.Contains(numberStr, "/") || strings.Contains(numberStr, "-") || strings.Contains(numberStr, ",") {
 458		if numberStr == "-" {
 459			return "", nil
 460		}
 461		if strings.ContainsAny(numberStr, "eE") {
 462			floatVal, err := strconv.ParseFloat(numberStr, 64)
 463			if err == nil {
 464				formatted := formatFloat(floatVal)
 465				return numberValue{raw: formatted}, nil
 466			}
 467			return numberStr, nil
 468		}
 469		return numberStr, nil
 470	}
 471	if strings.ContainsAny(numberStr, ".eE") {
 472		floatVal, err := strconv.ParseFloat(numberStr, 64)
 473		if err == nil {
 474			formatted := formatFloat(floatVal)
 475			return numberValue{raw: formatted}, nil
 476		}
 477		return numberStr, nil
 478	}
 479	if numberStr == "" {
 480		return "", nil
 481	}
 482	return numberValue{raw: numberStr}, nil
 483}
 484
 485func (p *parser) parseObject() (any, error) {
 486	obj := newOrderedObject()
 487	startIndex := p.index
 488	for {
 489		p.skipWhitespaces()
 490		char, ok := p.getCharAt(0)
 491		if !ok || char == '}' {
 492			break
 493		}
 494		if current, ok := p.getCharAt(0); ok && current == ':' {
 495			p.log("While parsing an object we found a : before a key, ignoring")
 496			p.index++
 497		}
 498		p.context.set(contextObjectKey)
 499		rollbackIndex := p.index
 500		key := ""
 501		for {
 502			current, ok := p.getCharAt(0)
 503			if !ok {
 504				break
 505			}
 506			rollbackIndex = p.index
 507			if current == '[' && key == "" {
 508				prevKey, ok := obj.lastKey()
 509				if ok {
 510					prevValue, _ := obj.get(prevKey)
 511					if prevArray, ok := prevValue.([]any); ok && !p.strict {
 512						p.index++
 513						newArrayValue, err := p.parseArray()
 514						if err != nil {
 515							return nil, err
 516						}
 517						if newArray, ok := newArrayValue.([]any); ok {
 518							listLengths := []int{}
 519							for _, item := range prevArray {
 520								if nested, ok := item.([]any); ok {
 521									listLengths = append(listLengths, len(nested))
 522								}
 523							}
 524							expectedLen := 0
 525							if len(listLengths) > 0 {
 526								same := true
 527								for _, length := range listLengths {
 528									if length != listLengths[0] {
 529										same = false
 530										break
 531									}
 532								}
 533								if same {
 534									expectedLen = listLengths[0]
 535								}
 536							}
 537							if expectedLen > 0 {
 538								tail := []any{}
 539								for len(prevArray) > 0 {
 540									if _, ok := prevArray[len(prevArray)-1].([]any); ok {
 541										break
 542									}
 543									tail = append(tail, prevArray[len(prevArray)-1])
 544									prevArray = prevArray[:len(prevArray)-1]
 545								}
 546								if len(tail) > 0 {
 547									reverseAny(tail)
 548									if len(tail)%expectedLen == 0 {
 549										p.log("While parsing an object we found row values without an inner array, grouping them into rows")
 550										for i := 0; i < len(tail); i += expectedLen {
 551											prevArray = append(prevArray, tail[i:i+expectedLen])
 552										}
 553									} else {
 554										prevArray = append(prevArray, tail...)
 555									}
 556								}
 557								if len(newArray) > 0 {
 558									allLists := true
 559									for _, item := range newArray {
 560										if _, ok := item.([]any); !ok {
 561											allLists = false
 562											break
 563										}
 564									}
 565									if allLists {
 566										p.log("While parsing an object we found additional rows, appending them without flattening")
 567										prevArray = append(prevArray, newArray...)
 568									} else {
 569										prevArray = append(prevArray, newArray)
 570									}
 571								}
 572							} else {
 573								if len(newArray) == 1 {
 574									if nested, ok := newArray[0].([]any); ok {
 575										prevArray = append(prevArray, nested...)
 576									} else {
 577										prevArray = append(prevArray, newArray...)
 578									}
 579								} else {
 580									prevArray = append(prevArray, newArray...)
 581								}
 582							}
 583							obj.set(prevKey, prevArray)
 584						}
 585						p.skipWhitespaces()
 586						if nextChar, ok := p.getCharAt(0); ok && nextChar == ',' {
 587							p.index++
 588						}
 589						p.skipWhitespaces()
 590						continue
 591					}
 592				}
 593			}
 594			rawKeyValue, err := p.parseString()
 595			if err != nil {
 596				return nil, err
 597			}
 598			rawKey, _ := rawKeyValue.(string)
 599			key = rawKey
 600			if key == "" {
 601				p.skipWhitespaces()
 602			}
 603			if key != "" || (key == "" && func() bool { ch, ok := p.getCharAt(0); return ok && (ch == ':' || ch == '}') }()) {
 604				if key == "" && p.strict {
 605					p.log("Empty key found in strict mode while parsing object, raising an error")
 606					return nil, errors.New("empty key found in strict mode while parsing object")
 607				}
 608				break
 609			}
 610		}
 611		if p.context.contains(contextArray) && obj.hasKey(key) {
 612			if p.strict {
 613				p.log("Duplicate key found in strict mode while parsing object, raising an error")
 614				return nil, errors.New("duplicate key found in strict mode while parsing object")
 615			}
 616			p.log("While parsing an object we found a duplicate key, closing the object here and rolling back the index")
 617			p.index = rollbackIndex - 1
 618			p.insertRune(p.index+1, '{')
 619			break
 620		}
 621		p.skipWhitespaces()
 622		if current, ok := p.getCharAt(0); !ok || current == '}' {
 623			continue
 624		}
 625		p.skipWhitespaces()
 626		if current, ok := p.getCharAt(0); ok && current != ':' {
 627			if p.strict {
 628				p.log("Missing ':' after key in strict mode while parsing object, raising an error")
 629				return nil, errors.New("missing ':' after key in strict mode while parsing object")
 630			}
 631			p.log("While parsing an object we missed a : after a key")
 632		}
 633		p.index++
 634		p.context.reset()
 635		p.context.set(contextObjectValue)
 636		p.skipWhitespaces()
 637		value := any("")
 638		if current, ok := p.getCharAt(0); ok && (current == ',' || current == '}') {
 639			p.log("While parsing an object value we found a stray " + string(current) + ", ignoring it")
 640		} else {
 641			var err error
 642			value, err = p.parseJSON()
 643			if err != nil {
 644				return nil, err
 645			}
 646		}
 647		if value == "" && p.strict {
 648			if prev, ok := p.getCharAt(-1); !ok || !isStringDelimiter(prev) {
 649				p.log("Parsed value is empty in strict mode while parsing object, raising an error")
 650				return nil, errors.New("parsed value is empty in strict mode while parsing object")
 651			}
 652		}
 653		p.context.reset()
 654		obj.set(key, value)
 655		if current, ok := p.getCharAt(0); ok && (current == ',' || current == '\'' || current == '"') {
 656			p.index++
 657		}
 658		if current, ok := p.getCharAt(0); ok && current == ']' && p.context.contains(contextArray) {
 659			p.log("While parsing an object we found a closing array bracket, closing the object here and rolling back the index")
 660			p.index--
 661			break
 662		}
 663		p.skipWhitespaces()
 664	}
 665	p.index++
 666	if len(obj.entries) == 0 && p.index-startIndex > 2 {
 667		if p.strict {
 668			p.log("Parsed object is empty but contains extra characters in strict mode, raising an error")
 669			return nil, errors.New("parsed object is empty but contains extra characters in strict mode")
 670		}
 671		if p.context.empty && p.index-startIndex <= 3 {
 672			return obj, nil
 673		}
 674		if p.context.empty {
 675			prefix := string(p.jsonStr[:startIndex-1])
 676			if strings.TrimSpace(prefix) == "" {
 677				return obj, nil
 678			}
 679		}
 680		p.log("Parsed object is empty, we will try to parse this as an array instead")
 681		p.index = startIndex
 682		return p.parseArray()
 683	}
 684	if len(obj.entries) == 0 && p.index-startIndex <= 2 {
 685		return obj, nil
 686	}
 687	if !p.context.empty {
 688		if current, ok := p.getCharAt(0); ok && current == '}' {
 689			if p.context.current == nil || (*p.context.current != contextObjectKey && *p.context.current != contextObjectValue) {
 690				p.log("Found an extra closing brace that shouldn't be there, skipping it")
 691				p.index++
 692			}
 693		}
 694		return obj, nil
 695	}
 696	p.skipWhitespaces()
 697	if current, ok := p.getCharAt(0); !ok || current != ',' {
 698		return obj, nil
 699	}
 700	p.index++
 701	p.skipWhitespaces()
 702	if current, ok := p.getCharAt(0); !ok || !isStringDelimiter(current) {
 703		return obj, nil
 704	}
 705	if !p.strict {
 706		p.log("Found a comma and string delimiter after object closing brace, checking for additional key-value pairs")
 707		additionalValue, err := p.parseObject()
 708		if err != nil {
 709			return nil, err
 710		}
 711		if additionalObj, ok := additionalValue.(*orderedObject); ok {
 712			obj.merge(additionalObj)
 713		}
 714	}
 715	return obj, nil
 716}
 717
 718func (p *parser) parseString() (any, error) {
 719	missingQuotes := false
 720	doubledQuotes := false
 721	ldelim := '"'
 722	rdelim := '"'
 723
 724	char, ok := p.getCharAt(0)
 725	if ok && (char == '#' || char == '/') {
 726		return p.parseComment()
 727	}
 728	for ok && !isStringDelimiter(char) && !isAlphaNum(char) {
 729		p.index++
 730		char, ok = p.getCharAt(0)
 731	}
 732	if !ok {
 733		return "", nil
 734	}
 735	if char == '\'' {
 736		ldelim = '\''
 737		rdelim = '\''
 738	} else if char == '“' {
 739		ldelim = '“'
 740		rdelim = '”'
 741	} else if isAlphaNum(char) {
 742		if (char == 't' || char == 'f' || char == 'n') && (p.context.current == nil || *p.context.current != contextObjectKey) {
 743			value := p.parseBooleanOrNull()
 744			if value != "" {
 745				return value, nil
 746			}
 747		}
 748		if (char == 'T' || char == 'F' || char == 'N') && (p.context.current == nil || *p.context.current != contextObjectKey) {
 749			value := p.parseBooleanOrNull()
 750			if value != "" {
 751				return value, nil
 752			}
 753		}
 754		p.log("While parsing a string, we found a literal instead of a quote")
 755		missingQuotes = true
 756	}
 757
 758	if !missingQuotes {
 759		p.index++
 760	}
 761	if next, ok := p.getCharAt(0); ok && next == '`' {
 762		if value, ok := p.parseJSONLLMBlock(); ok {
 763			return value, nil
 764		}
 765		if p.context.empty {
 766			return "", nil
 767		}
 768		p.log("While parsing a string, we found code fences but they did not enclose valid JSON, continuing parsing the string")
 769	}
 770
 771	if next, ok := p.getCharAt(0); ok && next == ldelim {
 772		if (p.context.current != nil && *p.context.current == contextObjectKey && func() bool { ch, ok := p.getCharAt(1); return ok && ch == ':' }()) ||
 773			(p.context.current != nil && *p.context.current == contextObjectValue && func() bool { ch, ok := p.getCharAt(1); return ok && (ch == ',' || ch == '}') }()) ||
 774			(p.context.current != nil && *p.context.current == contextArray && func() bool { ch, ok := p.getCharAt(1); return ok && (ch == ',' || ch == ']') }()) {
 775			p.index++
 776			return "", nil
 777		}
 778		if p.context.current != nil && *p.context.current == contextObjectKey {
 779			i := p.scrollWhitespaces(1)
 780			if ch, ok := p.getCharAt(i); ok && ch == ':' {
 781				p.index++
 782				return "", nil
 783			}
 784		}
 785		if next2, ok := p.getCharAt(1); ok && next2 == ldelim {
 786			p.log("While parsing a string, we found a doubled quote and then a quote again, ignoring it")
 787			if p.strict {
 788				return nil, errors.New("found doubled quotes followed by another quote")
 789			}
 790			return "", nil
 791		}
 792		i := p.skipToCharacter(rdelim, 1)
 793		if nextChar, ok := p.getCharAt(i + 1); ok && nextChar == rdelim {
 794			p.log("While parsing a string, we found a valid starting doubled quote")
 795			doubledQuotes = true
 796			p.index++
 797		} else {
 798			i = p.scrollWhitespaces(1)
 799			nextChar, ok := p.getCharAt(i)
 800			if ok && (isStringDelimiter(nextChar) || nextChar == '{' || nextChar == '[') {
 801				p.log("While parsing a string, we found a doubled quote but also another quote afterwards, ignoring it")
 802				if p.strict {
 803					return nil, errors.New("found doubled quotes followed by another quote while parsing a string")
 804				}
 805				p.index++
 806				return "", nil
 807			}
 808			if !ok || (nextChar != ',' && nextChar != ']' && nextChar != '}') {
 809				p.log("While parsing a string, we found a doubled quote but it was a mistake, removing one quote")
 810				p.index++
 811			}
 812		}
 813	}
 814
 815	stringAcc := []rune{}
 816	char, ok = p.getCharAt(0)
 817	unmatchedDelimiter := false
 818	for ok && char != rdelim {
 819		if missingQuotes {
 820			if p.context.current != nil && *p.context.current == contextObjectKey {
 821				if char == ':' || unicode.IsSpace(char) {
 822					p.log("While parsing a string missing the left delimiter in object key context, we found a :, stopping here")
 823					break
 824				}
 825			}
 826			if p.context.current != nil && *p.context.current == contextArray {
 827				if char == ']' || char == ',' {
 828					p.log("While parsing a string missing the left delimiter in array context, we found a ] or ,, stopping here")
 829					break
 830				}
 831			}
 832		}
 833		if !p.streamStable && p.context.current != nil && *p.context.current == contextObjectValue {
 834			if (char == ',' || char == '}') && (len(stringAcc) == 0 || stringAcc[len(stringAcc)-1] != rdelim) {
 835				rstringDelimiterMissing := true
 836				next := rune(0)
 837				p.skipWhitespaces()
 838				if next, ok := p.getCharAt(1); ok && next == '\\' {
 839					rstringDelimiterMissing = false
 840				}
 841				i := p.skipToCharacter(rdelim, 1)
 842				if _, ok := p.getCharAt(i); ok {
 843					i++
 844					i = p.scrollWhitespaces(i)
 845					next, _ = p.getCharAt(i)
 846					if next == ',' || next == '}' {
 847						rstringDelimiterMissing = false
 848					} else {
 849						i = p.skipToCharacter(ldelim, i)
 850						if _, ok := p.getCharAt(i); !ok {
 851							rstringDelimiterMissing = false
 852						} else {
 853							i = p.scrollWhitespaces(i + 1)
 854							next, _ = p.getCharAt(i)
 855							if next != ':' {
 856								rstringDelimiterMissing = false
 857							}
 858						}
 859					}
 860				} else {
 861					i = p.skipToCharacter(':', 1)
 862					if _, ok := p.getCharAt(i); ok {
 863						break
 864					}
 865					i = p.scrollWhitespaces(1)
 866					j := p.skipToCharacter('}', i)
 867					if j-i > 1 {
 868						rstringDelimiterMissing = false
 869					} else if _, ok := p.getCharAt(j); ok {
 870						for k := len(stringAcc) - 1; k >= 0; k-- {
 871							if stringAcc[k] == '{' {
 872								rstringDelimiterMissing = false
 873								break
 874							}
 875						}
 876					}
 877				}
 878				if rstringDelimiterMissing {
 879					p.log("While parsing a string missing the left delimiter in object value context, we found a , or } and we couldn't determine that a right delimiter was present. Stopping here")
 880					break
 881				}
 882			}
 883		}
 884		if !p.streamStable && p.context.contains(contextArray) && char == ']' {
 885			i := p.skipToCharacter(rdelim, 0)
 886			if _, ok := p.getCharAt(i); !ok {
 887				break
 888			}
 889		}
 890		if p.context.current != nil && *p.context.current == contextObjectValue && char == '}' {
 891			i := p.scrollWhitespaces(1)
 892			nextChar, ok := p.getCharAt(i)
 893			if ok && nextChar == '`' {
 894				if c1, ok := p.getCharAt(i + 1); ok && c1 == '`' {
 895					if c2, ok := p.getCharAt(i + 2); ok && c2 == '`' {
 896						p.log("While parsing a string in object value context, we found a } that closes the object before code fences, stopping here")
 897						break
 898					}
 899				}
 900			}
 901			if !ok {
 902				p.log("While parsing a string in object value context, we found a } that closes the object, stopping here")
 903				break
 904			}
 905		}
 906		stringAcc = append(stringAcc, char)
 907		p.index++
 908		char, ok = p.getCharAt(0)
 909		if !ok {
 910			if p.streamStable && len(stringAcc) > 0 && stringAcc[len(stringAcc)-1] == '\\' {
 911				stringAcc = stringAcc[:len(stringAcc)-1]
 912			}
 913			break
 914		}
 915		if len(stringAcc) > 0 && stringAcc[len(stringAcc)-1] == '\\' {
 916			p.log("Found a stray escape sequence, normalizing it")
 917			if char == rdelim || char == 't' || char == 'n' || char == 'r' || char == 'b' || char == '\\' {
 918				stringAcc = stringAcc[:len(stringAcc)-1]
 919				escapeSeqs := map[rune]rune{'t': '\t', 'n': '\n', 'r': '\r', 'b': '\b'}
 920				if replacement, ok := escapeSeqs[char]; ok {
 921					stringAcc = append(stringAcc, replacement)
 922				} else {
 923					stringAcc = append(stringAcc, char)
 924				}
 925				p.index++
 926				char, ok = p.getCharAt(0)
 927				for ok && len(stringAcc) > 0 && stringAcc[len(stringAcc)-1] == '\\' && (char == rdelim || char == '\\') {
 928					stringAcc = append(stringAcc[:len(stringAcc)-1], char)
 929					p.index++
 930					char, ok = p.getCharAt(0)
 931				}
 932				continue
 933			}
 934			if char == 'u' || char == 'x' {
 935				numChars := 4
 936				if char == 'x' {
 937					numChars = 2
 938				}
 939				nextChars := p.sliceRunes(p.index+1, p.index+1+numChars)
 940				if len(nextChars) == numChars && isHexString(string(nextChars)) {
 941					p.log("Found a unicode escape sequence, normalizing it")
 942					parsed, _ := strconv.ParseInt(string(nextChars), 16, 32)
 943					stringAcc = append(stringAcc[:len(stringAcc)-1], rune(parsed))
 944					p.index += 1 + numChars
 945					char, ok = p.getCharAt(0)
 946					continue
 947				}
 948			} else if isStringDelimiter(char) && char != rdelim {
 949				p.log("Found a delimiter that was escaped but shouldn't be escaped, removing the escape")
 950				stringAcc = append(stringAcc[:len(stringAcc)-1], char)
 951				p.index++
 952				char, ok = p.getCharAt(0)
 953				continue
 954			}
 955		}
 956		if char == ':' && !missingQuotes && p.context.current != nil && *p.context.current == contextObjectKey {
 957			i := p.skipToCharacter(ldelim, 1)
 958			if _, ok := p.getCharAt(i); ok {
 959				i++
 960				i = p.skipToCharacter(rdelim, i)
 961				if _, ok := p.getCharAt(i); ok {
 962					i++
 963					i = p.scrollWhitespaces(i)
 964					ch, ok := p.getCharAt(i)
 965					if ok && (ch == ',' || ch == '}') {
 966						p.log("While parsing a string missing the right delimiter in object key context, we found a " + string(ch) + " stopping here")
 967						break
 968					}
 969				}
 970			} else {
 971				p.log("While parsing a string missing the right delimiter in object key context, we found a :, stopping here")
 972				break
 973			}
 974		}
 975		if char == rdelim && (len(stringAcc) == 0 || stringAcc[len(stringAcc)-1] != '\\') {
 976			if doubledQuotes {
 977				if next, ok := p.getCharAt(1); ok && next == rdelim {
 978					p.log("While parsing a string, we found a doubled quote, ignoring it")
 979					p.index++
 980				}
 981			} else if missingQuotes && p.context.current != nil && *p.context.current == contextObjectValue {
 982				i := 1
 983				nextChar, ok := p.getCharAt(i)
 984				for ok && nextChar != rdelim && nextChar != ldelim {
 985					i++
 986					nextChar, ok = p.getCharAt(i)
 987				}
 988				if ok {
 989					i++
 990					i = p.scrollWhitespaces(i)
 991					if ch, ok := p.getCharAt(i); ok && ch == ':' {
 992						p.index--
 993						char, _ = p.getCharAt(0)
 994						p.log("In a string with missing quotes and object value context, I found a delimeter but it turns out it was the beginning on the next key. Stopping here.")
 995						break
 996					}
 997				}
 998			} else if unmatchedDelimiter {
 999				unmatchedDelimiter = false
1000				stringAcc = append(stringAcc, char)
1001				p.index++
1002				char, ok = p.getCharAt(0)
1003			} else {
1004				i := 1
1005				nextChar, ok := p.getCharAt(i)
1006				checkCommaInObjectValue := true
1007				for ok && nextChar != rdelim && nextChar != ldelim {
1008					if checkCommaInObjectValue && unicode.IsLetter(nextChar) {
1009						checkCommaInObjectValue = false
1010					}
1011					if (p.context.contains(contextObjectKey) && (nextChar == ':' || nextChar == '}')) ||
1012						(p.context.contains(contextObjectValue) && nextChar == '}') ||
1013						(p.context.contains(contextArray) && (nextChar == ']' || nextChar == ',')) ||
1014						(checkCommaInObjectValue && p.context.current != nil && *p.context.current == contextObjectValue && nextChar == ',') {
1015						break
1016					}
1017					i++
1018					nextChar, ok = p.getCharAt(i)
1019				}
1020				if nextChar == ',' && p.context.current != nil && *p.context.current == contextObjectValue {
1021					i++
1022					i = p.skipToCharacter(rdelim, i)
1023					i++
1024					i = p.scrollWhitespaces(i)
1025					nextChar, _ = p.getCharAt(i)
1026					if nextChar == '}' || nextChar == ',' {
1027						p.log("While parsing a string, we found a misplaced quote that would have closed the string but has a different meaning here, ignoring it")
1028						stringAcc = append(stringAcc, char)
1029						p.index++
1030						char, ok = p.getCharAt(0)
1031						if !ok {
1032							break
1033						}
1034						continue
1035					}
1036				} else if nextChar == rdelim && func() bool { prev, ok := p.getCharAt(i - 1); return ok && prev != '\\' }() {
1037					if onlyWhitespaceUntil(p, i) {
1038						break
1039					}
1040					if p.context.current != nil && *p.context.current == contextObjectValue {
1041						i = p.scrollWhitespaces(i + 1)
1042						if ch, ok := p.getCharAt(i); ok && ch == ',' {
1043							i = p.skipToCharacter(ldelim, i+1)
1044							i++
1045							i = p.skipToCharacter(rdelim, i+1)
1046							i++
1047							i = p.scrollWhitespaces(i)
1048							if ch, ok := p.getCharAt(i); ok && ch == ':' {
1049								p.log("While parsing a string, we found a misplaced quote that would have closed the string but has a different meaning here, ignoring it")
1050								stringAcc = append(stringAcc, char)
1051								p.index++
1052								char, ok = p.getCharAt(0)
1053								if !ok {
1054									break
1055								}
1056								continue
1057							}
1058						}
1059						i = p.skipToCharacter(rdelim, i+1)
1060						i++
1061						nextChar, ok = p.getCharAt(i)
1062						for ok && nextChar != ':' {
1063							if nextChar == ',' || nextChar == ']' || nextChar == '}' || (nextChar == rdelim && func() bool { prev, ok := p.getCharAt(i - 1); return ok && prev != '\\' }()) {
1064								break
1065							}
1066							i++
1067							nextChar, ok = p.getCharAt(i)
1068						}
1069						if nextChar != ':' {
1070							p.log("While parsing a string, we found a misplaced quote that would have closed the string but has a different meaning here, ignoring it")
1071							unmatchedDelimiter = !unmatchedDelimiter
1072							stringAcc = append(stringAcc, char)
1073							p.index++
1074							char, ok = p.getCharAt(0)
1075							if !ok {
1076								break
1077							}
1078						}
1079					} else if p.context.current != nil && *p.context.current == contextArray {
1080						evenDelimiters := nextChar == rdelim
1081						for nextChar == rdelim {
1082							i = p.skipToCharacters(map[rune]struct{}{rdelim: {}, ']': {}}, i+1)
1083							nextChar, ok = p.getCharAt(i)
1084							if !ok || nextChar != rdelim {
1085								evenDelimiters = false
1086								break
1087							}
1088							i = p.skipToCharacters(map[rune]struct{}{rdelim: {}, ']': {}}, i+1)
1089							nextChar, _ = p.getCharAt(i)
1090						}
1091						if evenDelimiters {
1092							p.log("While parsing a string in Array context, we detected a quoted section that would have closed the string but has a different meaning here, ignoring it")
1093							unmatchedDelimiter = !unmatchedDelimiter
1094							stringAcc = append(stringAcc, char)
1095							p.index++
1096							char, ok = p.getCharAt(0)
1097							if !ok {
1098								break
1099							}
1100						} else {
1101							break
1102						}
1103					} else if p.context.current != nil && *p.context.current == contextObjectKey {
1104						p.log("While parsing a string in Object Key context, we detected a quoted section that would have closed the string but has a different meaning here, ignoring it")
1105						stringAcc = append(stringAcc, char)
1106						p.index++
1107						char, ok = p.getCharAt(0)
1108						if !ok {
1109							break
1110						}
1111					}
1112				}
1113			}
1114		}
1115	}
1116	if ok && missingQuotes && p.context.current != nil && *p.context.current == contextObjectKey && unicode.IsSpace(char) {
1117		p.log("While parsing a string, handling an extreme corner case in which the LLM added a comment instead of valid string, invalidate the string and return an empty value")
1118		p.skipWhitespaces()
1119		if ch, ok := p.getCharAt(0); ok {
1120			if ch != ':' && ch != ',' {
1121				p.index--
1122				return "", nil
1123			}
1124			if ch == ',' {
1125				p.index--
1126				return "", nil
1127			}
1128		}
1129	}
1130	if missingQuotes && p.context.current != nil && *p.context.current == contextObjectKey {
1131		if !onlyWhitespaceUntil(p, p.scrollWhitespaces(0)) {
1132			stringAcc = trimRightWhitespace(stringAcc)
1133			if len(stringAcc) == 0 {
1134				return "", nil
1135			}
1136		}
1137	}
1138	if !ok || char != rdelim {
1139		if !p.streamStable {
1140			p.log("While parsing a string, we missed the closing quote, ignoring")
1141			stringAcc = trimRightWhitespace(stringAcc)
1142		}
1143	} else {
1144		p.index++
1145	}
1146	if !p.streamStable && (missingQuotes || (len(stringAcc) > 0 && stringAcc[len(stringAcc)-1] == '\n')) {
1147		stringAcc = trimRightWhitespace(stringAcc)
1148	}
1149	if missingQuotes && p.context.empty {
1150		next := p.scrollWhitespaces(0)
1151		if ch, ok := p.getCharAt(next); ok && (ch == '{' || ch == '[' || ch == '`') {
1152			return "", nil
1153		}
1154		if !p.streamStable {
1155			stringAcc = trimRightWhitespace(stringAcc)
1156		}
1157		if len(stringAcc) == 0 {
1158			return "", nil
1159		}
1160	}
1161	if p.context.empty {
1162		next := p.scrollWhitespaces(0)
1163		if ch, ok := p.getCharAt(next); ok && (ch == '{' || ch == '[' || ch == '`') {
1164			return "", nil
1165		}
1166	}
1167	if len(stringAcc) == 1 && stringAcc[0] == rdelim {
1168		return "", nil
1169	}
1170	if p.context.empty && missingQuotes {
1171		if len(stringAcc) == 1 && stringAcc[0] == '"' {
1172			return "", nil
1173		}
1174	}
1175	return string(stringAcc), nil
1176}
1177
1178func (p *parser) parseBooleanOrNull() any {
1179	char, ok := p.getCharAt(0)
1180	if !ok {
1181		return ""
1182	}
1183	valueMap := map[rune]struct {
1184		token string
1185		value any
1186	}{
1187		't': {"true", true},
1188		'f': {"false", false},
1189		'n': {"null", nil},
1190	}
1191	lower := unicode.ToLower(char)
1192	value, ok := valueMap[lower]
1193	if !ok {
1194		return ""
1195	}
1196	matchUpper := unicode.IsUpper(char)
1197	i := 0
1198	startingIndex := p.index
1199	current := lower
1200	for ok && i < len(value.token) && current == rune(value.token[i]) {
1201		i++
1202		p.index++
1203		char, ok = p.getCharAt(0)
1204		if ok {
1205			if unicode.IsUpper(char) {
1206				matchUpper = true
1207			}
1208			current = unicode.ToLower(char)
1209		}
1210	}
1211	if i == len(value.token) {
1212		if matchUpper && p.context.empty {
1213			p.index = startingIndex
1214			return ""
1215		}
1216		return value.value
1217	}
1218	p.index = startingIndex
1219	return ""
1220}
1221
1222func (p *parser) parseJSONLLMBlock() (any, bool) {
1223	if p.sliceString(p.index, p.index+7) == "```json" {
1224		i := p.skipToCharacter('`', 7)
1225		if p.sliceString(p.index+i, p.index+i+3) == "```" {
1226			p.index += 7
1227			value, err := p.parseJSON()
1228			if err != nil {
1229				return nil, false
1230			}
1231			return value, true
1232		}
1233	}
1234	return nil, false
1235}
1236
1237func (p *parser) sliceRunes(start int, end int) []rune {
1238	if start < 0 {
1239		start = 0
1240	}
1241	if end > len(p.jsonStr) {
1242		end = len(p.jsonStr)
1243	}
1244	if start > end {
1245		return []rune{}
1246	}
1247	return p.jsonStr[start:end]
1248}
1249
1250func (p *parser) sliceString(start int, end int) string {
1251	return string(p.sliceRunes(start, end))
1252}
1253
1254func (p *parser) insertRune(pos int, r rune) {
1255	if pos < 0 {
1256		pos = 0
1257	}
1258	if pos > len(p.jsonStr) {
1259		pos = len(p.jsonStr)
1260	}
1261	p.jsonStr = append(p.jsonStr[:pos], append([]rune{r}, p.jsonStr[pos:]...)...)
1262}
1263
1264func onlyWhitespaceUntil(p *parser, end int) bool {
1265	for j := 1; j < end; j++ {
1266		c, ok := p.getCharAt(j)
1267		if ok && !unicode.IsSpace(c) {
1268			return false
1269		}
1270	}
1271	return true
1272}
1273
1274func onlyWhitespaceBefore(p *parser) bool {
1275	for i := p.index - 1; i >= 0; i-- {
1276		c := p.jsonStr[i]
1277		if !unicode.IsSpace(c) {
1278			return false
1279		}
1280	}
1281	return true
1282}
1283
1284func reverseAny(values []any) {
1285	for i, j := 0, len(values)-1; i < j; i, j = i+1, j-1 {
1286		values[i], values[j] = values[j], values[i]
1287	}
1288}
1289
1290func isStrictlyEmpty(value any) bool {
1291	switch v := value.(type) {
1292	case string:
1293		return len(v) == 0
1294	case []any:
1295		return len(v) == 0
1296	case *orderedObject:
1297		return len(v.entries) == 0
1298	default:
1299		return false
1300	}
1301}
1302
1303func isSameObject(obj1 any, obj2 any) bool {
1304	switch v1 := obj1.(type) {
1305	case *orderedObject:
1306		v2, ok := obj2.(*orderedObject)
1307		if !ok {
1308			return false
1309		}
1310		if len(v1.entries) != len(v2.entries) {
1311			return false
1312		}
1313		for _, entry := range v1.entries {
1314			val2, ok := v2.get(entry.key)
1315			if !ok {
1316				return false
1317			}
1318			if !isSameObject(entry.value, val2) {
1319				return false
1320			}
1321		}
1322		return true
1323	case []any:
1324		v2, ok := obj2.([]any)
1325		if !ok {
1326			return false
1327		}
1328		if len(v1) != len(v2) {
1329			return false
1330		}
1331		for i := range v1 {
1332			if !isSameObject(v1[i], v2[i]) {
1333				return false
1334			}
1335		}
1336		return true
1337	default:
1338		if obj1 == nil || obj2 == nil {
1339			return obj1 == obj2
1340		}
1341		return reflect.TypeOf(obj1) == reflect.TypeOf(obj2)
1342	}
1343}
1344
1345func isTruthy(value any) bool {
1346	switch v := value.(type) {
1347	case string:
1348		return v != ""
1349	case []any:
1350		return len(v) > 0
1351	case *orderedObject:
1352		return len(v.entries) > 0
1353	case bool:
1354		return v
1355	case numberValue:
1356		return v.raw != ""
1357	case nil:
1358		return false
1359	default:
1360		return true
1361	}
1362}
1363
1364func isStringDelimiter(char rune) bool {
1365	switch char {
1366	case '"', '\'', '“', '”':
1367		return true
1368	default:
1369		return false
1370	}
1371}
1372
1373func isAlphaNum(char rune) bool {
1374	return unicode.IsLetter(char) || unicode.IsDigit(char)
1375}
1376
1377func trimRightWhitespace(values []rune) []rune {
1378	for len(values) > 0 {
1379		if !unicode.IsSpace(values[len(values)-1]) {
1380			break
1381		}
1382		values = values[:len(values)-1]
1383	}
1384	return values
1385}
1386
1387func isHexString(value string) bool {
1388	if value == "" {
1389		return false
1390	}
1391	for _, c := range value {
1392		if (c < '0' || c > '9') && (c < 'a' || c > 'f') && (c < 'A' || c > 'F') {
1393			return false
1394		}
1395	}
1396	return true
1397}
1398
1399func formatFloat(value float64) string {
1400	formatted := strconv.FormatFloat(value, 'f', -1, 64)
1401	if !strings.Contains(formatted, ".") {
1402		formatted += ".0"
1403	}
1404	return formatted
1405}
1406
1407func applyOptions(opts []Option) options {
1408	cfg := options{}
1409	for _, opt := range opts {
1410		if opt != nil {
1411			opt(&cfg)
1412		}
1413	}
1414	return cfg
1415}
1416
1417func ensureASCIIValue(cfg options) bool {
1418	if cfg.ensureASCII == nil {
1419		return true
1420	}
1421	return *cfg.ensureASCII
1422}
1423
1424// WithEnsureASCII sets whether to escape non-ASCII characters.
1425func WithEnsureASCII(value bool) Option {
1426	return func(o *options) {
1427		o.ensureASCII = &value
1428	}
1429}
1430
1431// WithSkipJSONLoads skips JSON parsing during load.
1432func WithSkipJSONLoads() Option {
1433	return func(o *options) {
1434		o.skipJSONLoads = true
1435	}
1436}
1437
1438// WithStreamStable enables streaming-stable parsing.
1439func WithStreamStable() Option {
1440	return func(o *options) {
1441		o.streamStable = true
1442	}
1443}
1444
1445// WithStrict enables strict parsing mode.
1446func WithStrict() Option {
1447	return func(o *options) {
1448		o.strict = true
1449	}
1450}
1451
1452// RepairJSON takes a potentially malformed JSON string output from LLMs and
1453// attempts to repair it into a valid JSON string. It returns the repaired JSON
1454// string or an error if the input cannot be repaired.
1455func RepairJSON(input string, opts ...Option) (string, error) {
1456	cfg := applyOptions(opts)
1457	p := newParser(input, false, cfg.streamStable, cfg.strict)
1458	value, _, err := p.parse()
1459	if err != nil {
1460		return "", err
1461	}
1462	if str, ok := value.(string); ok {
1463		trimmed := strings.TrimSpace(str)
1464		if str == "" || trimmed == "" {
1465			return "", nil
1466		}
1467		return "", nil
1468	}
1469	if value == "" {
1470		return "", nil
1471	}
1472	return serialize(value, ensureASCIIValue(cfg)), nil
1473}
1474
1475// Loads takes a potentially malformed JSON string output from LLMs and attempts
1476// to repair it and parse it into a Go value.
1477func Loads(input string, opts ...Option) (any, error) {
1478	cfg := applyOptions(opts)
1479	p := newParser(input, false, cfg.streamStable, cfg.strict)
1480	value, _, err := p.parse()
1481	if err != nil {
1482		return nil, err
1483	}
1484	if value == "" {
1485		return "", nil
1486	}
1487	return normalizeValue(value), nil
1488}
1489
1490// RepairJSONWithLog takes a potentially malformed JSON string output from LLMs
1491// and attempts to repair it into a valid JSON string, while also returning logs
1492// of the repair process.
1493func RepairJSONWithLog(input string, opts ...Option) (any, []LogEntry, error) {
1494	cfg := applyOptions(opts)
1495	p := newParser(input, true, cfg.streamStable, cfg.strict)
1496	value, logs, err := p.parse()
1497	if err != nil {
1498		return nil, nil, err
1499	}
1500	if logs == nil {
1501		logs = []LogEntry{}
1502	}
1503	if value == "" {
1504		return "", logs, nil
1505	}
1506	return normalizeValue(value), logs, nil
1507}
1508
1509func normalizeValue(value any) any {
1510	switch v := value.(type) {
1511	case *orderedObject:
1512		result := map[string]any{}
1513		for _, entry := range v.entries {
1514			result[entry.key] = normalizeValue(entry.value)
1515		}
1516		return result
1517	case []any:
1518		items := make([]any, 0, len(v))
1519		for _, item := range v {
1520			items = append(items, normalizeValue(item))
1521		}
1522		return items
1523	case numberValue:
1524		return json.Number(v.raw)
1525	default:
1526		return v
1527	}
1528}
1529
1530func serialize(value any, ensureASCII bool) string {
1531	var buf bytes.Buffer
1532	writeValue(&buf, value, ensureASCII)
1533	return buf.String()
1534}
1535
1536func writeValue(buf *bytes.Buffer, value any, ensureASCII bool) {
1537	switch v := value.(type) {
1538	case string:
1539		buf.WriteByte('"')
1540		writeEscapedString(buf, v, ensureASCII)
1541		buf.WriteByte('"')
1542	case numberValue:
1543		buf.WriteString(v.raw)
1544	case json.Number:
1545		buf.WriteString(v.String())
1546	case bool:
1547		if v {
1548			buf.WriteString("true")
1549		} else {
1550			buf.WriteString("false")
1551		}
1552	case nil:
1553		buf.WriteString("null")
1554	case []any:
1555		buf.WriteByte('[')
1556		for i, item := range v {
1557			if i > 0 {
1558				buf.WriteString(", ")
1559			}
1560			writeValue(buf, item, ensureASCII)
1561		}
1562		buf.WriteByte(']')
1563	case *orderedObject:
1564		buf.WriteByte('{')
1565		for i, entry := range v.entries {
1566			if i > 0 {
1567				buf.WriteString(", ")
1568			}
1569			buf.WriteByte('"')
1570			writeEscapedString(buf, entry.key, ensureASCII)
1571			buf.WriteByte('"')
1572			buf.WriteString(": ")
1573			writeValue(buf, entry.value, ensureASCII)
1574		}
1575		buf.WriteByte('}')
1576	case float64:
1577		buf.WriteString(formatFloat(v))
1578	case int:
1579		buf.WriteString(strconv.Itoa(v))
1580	case int64:
1581		buf.WriteString(strconv.FormatInt(v, 10))
1582	case uint64:
1583		buf.WriteString(strconv.FormatUint(v, 10))
1584	case map[string]any:
1585		buf.WriteByte('{')
1586		idx := 0
1587		for key, item := range v {
1588			if idx > 0 {
1589				buf.WriteString(", ")
1590			}
1591			buf.WriteByte('"')
1592			writeEscapedString(buf, key, ensureASCII)
1593			buf.WriteByte('"')
1594			buf.WriteString(": ")
1595			writeValue(buf, item, ensureASCII)
1596			idx++
1597		}
1598		buf.WriteByte('}')
1599	default:
1600		buf.WriteString("null")
1601	}
1602}
1603
1604func writeEscapedString(buf *bytes.Buffer, value string, ensureASCII bool) {
1605	for _, r := range value {
1606		switch r {
1607		case '\\':
1608			buf.WriteString("\\\\")
1609		case '"':
1610			buf.WriteString("\\\"")
1611		case '\b':
1612			buf.WriteString("\\b")
1613		case '\f':
1614			buf.WriteString("\\f")
1615		case '\n':
1616			buf.WriteString("\\n")
1617		case '\r':
1618			buf.WriteString("\\r")
1619		case '\t':
1620			buf.WriteString("\\t")
1621		default:
1622			if r < 0x20 {
1623				buf.WriteString("\\u")
1624				buf.WriteString(hex4(r))
1625				continue
1626			}
1627			if ensureASCII && r > 0x7f {
1628				if r > 0xFFFF {
1629					for _, rr := range utf16.Encode([]rune{r}) {
1630						buf.WriteString("\\u")
1631						buf.WriteString(hex4(rune(rr)))
1632					}
1633					continue
1634				}
1635				buf.WriteString("\\u")
1636				buf.WriteString(hex4(r))
1637				continue
1638			}
1639			buf.WriteRune(r)
1640		}
1641	}
1642}
1643
1644func hex4(r rune) string {
1645	value := int(r)
1646	result := strconv.FormatInt(int64(value), 16)
1647	return strings.Repeat("0", 4-len(result)) + strings.ToLower(result)
1648}