package json import ( "bufio" "main/walk" "strings" "strconv" "errors" ) func isWhitespace(r rune) bool { for _, ws := range " \t\r\n" { if r == ws { return true } } return false } func isNumberRune(r rune) bool { return '0' <= r && r <= '9' || r == '.' } type JSONReaderStructure int const ( JSONReaderStructureArray JSONReaderStructure = iota JSONReaderStructureMap ) type JSONReaderState int const ( // Immediately before a value JSONReaderStateValue JSONReaderState = iota // Immediate after a value JSONReaderStateValueEnd ) func NewJSONReader(reader *bufio.Reader) *JSONReader { return &JSONReader { path: nil, structure: nil, state: JSONReaderStateValue, reader: reader, } } type JSONReader struct { path []walk.PathSegment structure []JSONReaderStructure state JSONReaderState reader *bufio.Reader prevStart bool } func (reader *JSONReader) Read() (walk.WalkItem, error) { switch reader.state { case JSONReaderStateValue: if len(reader.structure) == 0 { // Before the start of a root JSON value return reader.readValue() } switch reader.structure[len(reader.structure) - 1] { case JSONReaderStructureArray: // Before a value inside an array r, err := reader.nextNonWsRune() if err != nil { panic("Missing rest of array") } if r == ']' { // End of an array reader.structure = reader.structure[:len(reader.structure) - 1] reader.path = reader.path[:len(reader.path) - 1] reader.state = JSONReaderStateValueEnd item := walk.WalkItem { Value: reader.buildWalkItemValue(walk.ArrayValue([]walk.ArrayElement{})), Start: false, PrevStart: reader.prevStart, End: true, NextEnd: reader.isNextEnd(), } reader.prevStart = false return item, nil } // Element in array reader.reader.UnreadRune() prevIndex := reader.path[len(reader.path) - 1].(int) reader.path[len(reader.path) - 1] = prevIndex + 1 return reader.readValue() case JSONReaderStructureMap: // Before a value inside a map r, err := reader.nextNonWsRune() if err != nil { panic("Reached EOF inside JSON map") } if r == '}' { // End of a map reader.structure = reader.structure[:len(reader.structure) - 1] reader.path = reader.path[:len(reader.path) - 1] reader.state = JSONReaderStateValueEnd item := walk.WalkItem { Value: reader.buildWalkItemValue(walk.MapValue([]walk.MapElement{})), Start: false, PrevStart: reader.prevStart, End: true, NextEnd: reader.isNextEnd(), } reader.prevStart = false return item, nil } // Element in map if r != '"' { panic("Expected key in map, found something else") } key := reader.readString() reader.path[len(reader.path) - 1] = key r, err = reader.nextNonWsRune() if err != nil { panic("Reached EOF after map key") } if r != ':' { panic("Expected : after map key, found something else") } return reader.readValue() default: panic("Invalid JSONReaderStructure") } case JSONReaderStateValueEnd: if len(reader.structure) == 0 { _, err := reader.nextNonWsRune() if err == nil { panic("input continues after JSON value") } return walk.WalkItem{}, errors.New("eof") } switch reader.structure[len(reader.structure) - 1] { case JSONReaderStructureArray: r, err := reader.nextNonWsRune() if err != nil { panic("Missing end of array") } if r == ']' { reader.path = reader.path[:len(reader.path) - 1] reader.structure = reader.structure[:len(reader.structure) - 1] reader.state = JSONReaderStateValueEnd item := walk.WalkItem { Value: reader.buildWalkItemValue(walk.ArrayValue([]walk.ArrayElement{})), Start: false, PrevStart: reader.prevStart, End: true, NextEnd: reader.isNextEnd(), } reader.prevStart = false return item, nil } if r != ',' { panic("Missing , after array value") } reader.state = JSONReaderStateValue return reader.Read() case JSONReaderStructureMap: r, err := reader.nextNonWsRune() if err != nil { panic("Missing end of map") } if r == '}' { reader.path = reader.path[:len(reader.path) - 1] reader.structure = reader.structure[:len(reader.structure) - 1] reader.state = JSONReaderStateValueEnd item := walk.WalkItem { Value: reader.buildWalkItemValue(walk.MapValue([]walk.MapElement{})), Start: false, PrevStart: reader.prevStart, End: true, NextEnd: reader.isNextEnd(), } reader.prevStart = false return item, nil } if r != ',' { panic("Missing , after map value") } reader.state = JSONReaderStateValue return reader.Read() default: panic("Invalid JSONReaderStructure") } default: panic("Invalid JSONReaderState") } } func (reader *JSONReader) readValue() (walk.WalkItem, error) { r, err := reader.nextNonWsRune() if err != nil { panic("Missing value in JSON") } switch r { case 'n': reader.requireString("ull") reader.state = JSONReaderStateValueEnd value := walk.WalkItem { Value: reader.buildWalkItemValue(walk.NullValue{}), Start: false, PrevStart: reader.prevStart, End: false, NextEnd: reader.isNextEnd(), } reader.prevStart = false return value, nil case 'f': reader.requireString("alse") reader.state = JSONReaderStateValueEnd value := walk.WalkItem { Value: reader.buildWalkItemValue(walk.BoolValue(false)), Start: false, PrevStart: reader.prevStart, End: false, NextEnd: reader.isNextEnd(), } reader.prevStart = false return value, nil case 't': reader.requireString("rue") reader.state = JSONReaderStateValueEnd value := walk.WalkItem { Value: reader.buildWalkItemValue(walk.BoolValue(true)), Start: false, PrevStart: reader.prevStart, End: false, NextEnd: reader.isNextEnd(), } reader.prevStart = false return value, nil case '"': v := reader.readString() reader.state = JSONReaderStateValueEnd value := walk.WalkItem { Value: reader.buildWalkItemValue(walk.StringValue(v)), Start: false, PrevStart: reader.prevStart, End: false, NextEnd: reader.isNextEnd(), } reader.prevStart = false return value, nil case '{': reader.state = JSONReaderStateValue reader.structure = append(reader.structure, JSONReaderStructureMap) value := walk.WalkItem { Value: reader.buildWalkItemValue(walk.MapValue([]walk.MapElement{})), Start: true, PrevStart: reader.prevStart, End: false, NextEnd: reader.isNextEnd(), } reader.prevStart = true reader.path = append(reader.path, walk.StringValue("")) return value, nil case '[': reader.state = JSONReaderStateValue reader.structure = append(reader.structure, JSONReaderStructureArray) value := walk.WalkItem { Value: reader.buildWalkItemValue(walk.ArrayValue([]walk.ArrayElement{})), Start: true, PrevStart: reader.prevStart, End: false, NextEnd: reader.isNextEnd(), } reader.prevStart = true reader.path = append(reader.path, -1) return value, nil } if isNumberRune(r) { var builder strings.Builder builder.WriteRune(r) for { r, _, err = reader.reader.ReadRune() if err != nil { break } if !isNumberRune(r) { reader.reader.UnreadRune() break } builder.WriteRune(r) } number, parseError := strconv.ParseFloat(builder.String(), 64) if parseError != nil { panic("Invalid number") } reader.state = JSONReaderStateValueEnd value := walk.WalkItem { Value: reader.buildWalkItemValue(walk.NumberValue(number)), Start: false, PrevStart: reader.prevStart, End: false, NextEnd: reader.isNextEnd(), } reader.prevStart = false return value, nil } panic("Invalid JSON value starting with: " + string(r)) } func (reader *JSONReader) buildWalkItemValue(value walk.Value) walk.Value { for i := len(reader.path) - 1; i >= 0; i -= 1 { switch segment := reader.path[i].(type) { case int: value = walk.ArrayValue { walk.ArrayElement { Index: segment, Value: value, }, } case string: value = walk.MapValue { walk.MapElement { Key: segment, Value: value, }, } default: panic("Invalid segment type") } } return value } func (reader *JSONReader) readString() string { var builder strings.Builder for { r, _, err := reader.reader.ReadRune() if err != nil { panic("Missing rest of string") } if r == '"' { break } if r == '\\' { r, _, err = reader.reader.ReadRune() if err != nil { panic("Missing rune after \\") } builder.WriteRune(r) continue } builder.WriteRune(r) } return builder.String() } func (reader *JSONReader) isNextEnd() bool { r, err := reader.peekNonWsRune() if err != nil { return false } return r == ']' || r == '}' } func (reader *JSONReader) peekNonWsRune() (rune, error) { r, err := reader.nextNonWsRune() if err != nil { return 0, err } reader.reader.UnreadRune() return r, nil } func (reader *JSONReader) nextNonWsRune() (rune, error) { for { r, _, err := reader.reader.ReadRune() if err != nil { return 0, err } if !isWhitespace(r) { return r, nil } } } func (reader *JSONReader) requireString(criteria string) { for _, r := range criteria { reader.require(r) } } func (reader *JSONReader) require(criterion rune) { r, _, err := reader.reader.ReadRune() if err != nil { panic("Error while reading required rune: " + err.Error()) } if r != criterion { panic("Required rune not read") } } func (reader *JSONReader) AssertDone() { // TODO }