package fin import ( "fmt" "io" "maps" "slices" "strconv" ) type Data struct { data map[string]any dataRef *map[string]any scanner *Scanner currentObject map[string]any stack []map[string]any currentToken Token } func NewData() *Data { dataRef := GetMap() data := *dataRef d := &Data{ data: data, dataRef: dataRef, stack: make([]map[string]any, 0, 8), } d.currentObject = d.data return d } func (d *Data) Release() { if d.scanner != nil { ReleaseScanner(d.scanner) d.scanner = nil } if d.dataRef != nil { PutMap(d.dataRef) d.data = nil d.dataRef = nil } d.currentObject = nil d.stack = nil } func (d *Data) GetData() map[string]any { return d.data } func (d *Data) Get(key string) (any, error) { if key == "" { return d.data, nil } var current any = d.data start := 0 keyLen := len(key) for i := range keyLen { if key[i] == '.' || i == keyLen-1 { end := i if i == keyLen-1 && key[i] != '.' { end = i + 1 } part := key[start:end] switch node := current.(type) { case map[string]any: val, ok := node[part] if !ok { return nil, fmt.Errorf("key %s not found", part) } current = val case []any: index, err := strconv.Atoi(part) if err != nil { return nil, fmt.Errorf("invalid array index: %s", part) } if index < 0 || index >= len(node) { return nil, fmt.Errorf("array index out of bounds: %d", index) } current = node[index] case []string: index, err := strconv.Atoi(part) if err != nil { return nil, fmt.Errorf("invalid array index: %s", part) } if index < 0 || index >= len(node) { return nil, fmt.Errorf("array index out of bounds: %d", index) } current = node[index] case []int: index, err := strconv.Atoi(part) if err != nil { return nil, fmt.Errorf("invalid array index: %s", part) } if index < 0 || index >= len(node) { return nil, fmt.Errorf("array index out of bounds: %d", index) } current = node[index] default: return nil, fmt.Errorf("cannot access %s in non-container value", part) } if i == keyLen-1 { return current, nil } start = i + 1 } } return current, nil } func (d *Data) GetOr(key string, defaultValue any) any { val, err := d.Get(key) if err != nil { return defaultValue } return val } func convertTo[T any](val any, key string, converter func(any) (T, bool)) (T, error) { var zero T result, ok := converter(val) if ok { return result, nil } return zero, fmt.Errorf("value for key %s cannot be converted to %T", key, zero) } func toString(val any) (string, bool) { switch v := val.(type) { case string: return v, true case bool: return strconv.FormatBool(v), true case int: return strconv.Itoa(v), true case float64: return strconv.FormatFloat(v, 'f', -1, 64), true } return "", false } func toBool(val any) (bool, bool) { switch v := val.(type) { case bool: return v, true case string: if result, err := strconv.ParseBool(v); err == nil { return result, true } } return false, false } func toInt(val any) (int, bool) { switch v := val.(type) { case int: return v, true case float64: return int(v), true case string: if result, err := strconv.Atoi(v); err == nil { return result, true } } return 0, false } func toFloat(val any) (float64, bool) { switch v := val.(type) { case float64: return v, true case int: return float64(v), true case string: if result, err := strconv.ParseFloat(v, 64); err == nil { return result, true } } return 0, false } func (d *Data) GetString(key string) (string, error) { val, err := d.Get(key) if err != nil { return "", err } return convertTo(val, key, toString) } func (d *Data) GetBool(key string) (bool, error) { val, err := d.Get(key) if err != nil { return false, err } return convertTo(val, key, toBool) } func (d *Data) GetInt(key string) (int, error) { val, err := d.Get(key) if err != nil { return 0, err } return convertTo(val, key, toInt) } func (d *Data) GetFloat(key string) (float64, error) { val, err := d.Get(key) if err != nil { return 0, err } return convertTo(val, key, toFloat) } func (d *Data) GetArray(key string) ([]any, error) { val, err := d.Get(key) if err != nil { return nil, err } switch v := val.(type) { case []any: return v, nil case []string: result := make([]any, len(v)) for i, s := range v { result[i] = s } return result, nil case []int: result := make([]any, len(v)) for i, n := range v { result[i] = n } return result, nil default: return nil, fmt.Errorf("value for key %s is not an array", key) } } func (d *Data) GetMap(key string) (map[string]any, error) { val, err := d.Get(key) if err != nil { return nil, err } if m, ok := val.(map[string]any); ok { return m, nil } return nil, fmt.Errorf("value for key %s is not a map", key) } func getTypedArray[T any](d *Data, key string, converter func(any) (T, bool)) ([]T, error) { arr, err := d.GetArray(key) if err != nil { return nil, err } result := make([]T, len(arr)) for i, v := range arr { converted, ok := converter(v) if !ok { return nil, fmt.Errorf("array element at index %d cannot be converted", i) } result[i] = converted } return result, nil } func (d *Data) GetStringArray(key string) ([]string, error) { return getTypedArray(d, key, func(v any) (string, bool) { s, ok := v.(string) return s, ok }) } func (d *Data) GetIntArray(key string) ([]int, error) { return getTypedArray(d, key, func(v any) (int, bool) { switch val := v.(type) { case int: return val, true case float64: return int(val), true } return 0, false }) } func (d *Data) GetFloatArray(key string) ([]float64, error) { return getTypedArray(d, key, func(v any) (float64, bool) { switch val := v.(type) { case float64: return val, true case int: return float64(val), true } return 0, false }) } func (d *Data) Error(msg string) error { return fmt.Errorf("line %d, column %d: %s", d.currentToken.Line, d.currentToken.Column, msg) } func (d *Data) makeStringKey(tokenValue []byte) string { keyBytes := GetByteSlice() *keyBytes = append((*keyBytes)[:0], tokenValue...) result := string(*keyBytes) PutByteSlice(keyBytes) return result } func (d *Data) Parse(r io.Reader) error { d.scanner = NewScanner(r) d.currentObject = d.data err := d.parseContent() if d.scanner != nil { ReleaseScanner(d.scanner) d.scanner = nil } return err } func (d *Data) nextToken() (Token, error) { for { token, err := d.scanner.NextToken() if err != nil { return token, err } if token.Type != TokenComment { d.currentToken = token return token, nil } } } func (d *Data) parseContent() error { for { token, err := d.nextToken() if err != nil { return err } if token.Type == TokenEOF { break } if token.Type != TokenName { return d.Error(fmt.Sprintf("expected name at top level, got token type %v", token.Type)) } name := d.makeStringKey(token.Value) nextToken, err := d.nextToken() if err != nil { if err == io.EOF { d.currentObject[name] = "" break } return err } var value any if nextToken.Type == TokenOpenBrace { value, err = d.parseObject() if err != nil { return err } } else { value = d.tokenToValue(nextToken) lookAhead, nextErr := d.nextToken() if nextErr == nil && lookAhead.Type == TokenOpenBrace { nestedValue, err := d.parseObject() if err != nil { return err } if mapValue, ok := nestedValue.(map[string]any); ok { mapRef := GetMap() newMap := *mapRef maps.Copy(newMap, mapValue) newMap["value"] = value value = newMap } } else if nextErr == nil && lookAhead.Type != TokenEOF { d.scanner.UnreadToken(lookAhead) } } d.currentObject[name] = value } return nil } func (d *Data) parseObject() (any, error) { isArray := true arrayRef := GetArray() arrayElements := *arrayRef mapRef := GetMap() objectElements := *mapRef defer func() { if !isArray { PutArray(arrayRef) } else { PutMap(mapRef) } }() for { token, err := d.nextToken() if err != nil { if err == io.EOF { return nil, fmt.Errorf("unexpected EOF in object/array") } return nil, err } if token.Type == TokenCloseBrace { if isArray && len(arrayElements) > 0 { result := optimizeArray(arrayElements) *arrayRef = nil return result, nil } result := objectElements *mapRef = nil return result, nil } switch token.Type { case TokenName: key := d.makeStringKey(token.Value) nextToken, err := d.nextToken() if err != nil { if err == io.EOF { objectElements[key] = "" isArray = false return objectElements, nil } return nil, err } if nextToken.Type == TokenOpenBrace { isArray = false nestedValue, err := d.parseObject() if err != nil { return nil, err } objectElements[key] = nestedValue } else { isArray = false value := d.tokenToValue(nextToken) objectElements[key] = value lookAhead, nextErr := d.nextToken() if nextErr == nil && lookAhead.Type == TokenOpenBrace { nestedValue, err := d.parseObject() if err != nil { return nil, err } if mapValue, ok := nestedValue.(map[string]any); ok { combinedMapRef := GetMap() combinedMap := *combinedMapRef maps.Copy(combinedMap, mapValue) combinedMap["value"] = value objectElements[key] = combinedMap } } else if nextErr == nil && lookAhead.Type != TokenEOF && lookAhead.Type != TokenCloseBrace { d.scanner.UnreadToken(lookAhead) } else if nextErr == nil && lookAhead.Type == TokenCloseBrace { d.scanner.UnreadToken(lookAhead) } } case TokenString, TokenNumber, TokenBoolean: value := d.tokenToValue(token) arrayElements = append(arrayElements, value) case TokenOpenBrace: nestedValue, err := d.parseObject() if err != nil { return nil, err } if isArray { arrayElements = append(arrayElements, nestedValue) } else { return nil, d.Error("unexpected nested object without a key") } default: return nil, d.Error(fmt.Sprintf("unexpected token type: %v", token.Type)) } } } func Load(r io.Reader) (*Data, error) { data := NewData() err := data.Parse(r) if err != nil { data.Release() return nil, err } return data, nil } func (d *Data) tokenToValue(token Token) any { switch token.Type { case TokenString: return d.makeStringKey(token.Value) case TokenNumber: valueStr := string(token.Value) if slices.Contains(token.Value, '.') { val, _ := strconv.ParseFloat(valueStr, 64) return val } val, _ := strconv.Atoi(valueStr) return val case TokenBoolean: return bytesEqual(token.Value, []byte("true")) case TokenName: if bytesEqual(token.Value, []byte("true")) || bytesEqual(token.Value, []byte("false")) { return bytesEqual(token.Value, []byte("true")) } if len(token.Value) > 0 && isDigitOrMinus(token.Value[0]) { valueStr := string(token.Value) if slices.Contains(token.Value, '.') { if val, err := strconv.ParseFloat(valueStr, 64); err == nil { return val } } else { if val, err := strconv.Atoi(valueStr); err == nil { return val } } return valueStr } return string(token.Value) } return nil } func isLetter(b byte) bool { return (b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') } func isDigit(b byte) bool { return b >= '0' && b <= '9' } func isDigitOrMinus(b byte) bool { return isDigit(b) || b == '-' } func bytesEqual(b1, b2 []byte) bool { if len(b1) != len(b2) { return false } for i := range b1 { if b1[i] != b2[i] { return false } } return true } func optimizeArray(arr []any) any { if len(arr) == 0 { return arr } // Check all strings if allType[string](arr) { result := make([]string, len(arr)) for i, v := range arr { result[i] = v.(string) } return result } // Check all ints if allType[int](arr) { result := make([]int, len(arr)) for i, v := range arr { result[i] = v.(int) } return result } return arr } func allType[T any](arr []any) bool { for _, v := range arr { if _, ok := v.(T); !ok { return false } } return true }