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 ( JSONReaderStateValue JSONReaderState = iota JSONReaderStateValueEnd ) func NewJSONReader(reader *bufio.Reader) *JSONReader { return &JSONReader { path: nil, structure: nil, state: JSONReaderStateValue, reader: reader, } } type JSONReader struct { path []walk.Value structure []JSONReaderStructure state JSONReaderState reader *bufio.Reader } func (reader *JSONReader) Read() (walk.WalkItem, error) { switch reader.state { case JSONReaderStateValue: if len(reader.structure) == 0 { path := reader.clonePath() value, err := reader.readValue() if err != nil { panic("Missing JSON input") } return walk.WalkItem { Path: path, Value: []walk.Value{value}, }, nil } switch reader.structure[len(reader.structure) - 1] { case JSONReaderStructureArray: r, err := reader.nextNonWsRune() if err != nil { panic("Missing rest of array") } if r == ']' { reader.structure = reader.structure[:len(reader.structure) - 1] reader.path = reader.path[:len(reader.path) - 1] reader.state = JSONReaderStateValueEnd return reader.Read() } reader.reader.UnreadRune() prevIndex := reader.path[len(reader.path) - 1].(walk.NumberScalar) reader.path[len(reader.path) - 1] = prevIndex + 1 path := reader.clonePath() value, err := reader.readValue() if err != nil { panic("Missing value in array") } return walk.WalkItem { Path: path, Value: []walk.Value{value}, }, nil case JSONReaderStructureMap: r, err := reader.nextNonWsRune() if err != nil { panic("Reached EOF inside JSON map") } if r == '}' { reader.structure = reader.structure[:len(reader.structure) - 1] reader.path = reader.path[:len(reader.path) - 1] reader.state = JSONReaderStateValueEnd return reader.Read() } if r != '"' { panic("Expected key in map, found something else") } key := reader.readString() reader.path[len(reader.path) - 1] = walk.StringStructure(key) r, err = reader.nextNonWsRune() if err != nil { panic("Reached EOF after map key") } if r != ':' { panic("Expected : after map key, found something else") } path := reader.clonePath() value, err := reader.readValue() if err != nil { panic("Missing value in map") } return walk.WalkItem { Path: path, Value: []walk.Value{value}, }, nil 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 return reader.Read() } 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 return reader.Read() } 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.Value, error) { r, err := reader.nextNonWsRune() if err != nil { panic("Missing value in JSON") } switch r { case 'n': reader.requireString("ull") reader.state = JSONReaderStateValueEnd return walk.NullScalar{}, nil case 'f': reader.requireString("alse") reader.state = JSONReaderStateValueEnd return walk.BoolScalar(false), nil case 't': reader.requireString("rue") reader.state = JSONReaderStateValueEnd return walk.BoolScalar(true), nil case '"': v := reader.readString() reader.state = JSONReaderStateValueEnd return walk.StringStructure(v), nil case '{': reader.state = JSONReaderStateValue reader.structure = append(reader.structure, JSONReaderStructureMap) reader.path = append(reader.path, walk.StringStructure("")) return walk.MapStructure(make(map[string]walk.Value)), nil case '[': reader.state = JSONReaderStateValue reader.structure = append(reader.structure, JSONReaderStructureArray) reader.path = append(reader.path, walk.NumberScalar(-1)) return walk.ArrayStructure{}, 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 return walk.NumberScalar(number), nil } panic("Invalid JSON value starting with: " + string(r)) } 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) 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) clonePath() []walk.Value { return append([]walk.Value{}, reader.path...) } func (reader *JSONReader) AssertDone() { // TODO }