diff options
Diffstat (limited to 'parser/parser.go')
-rw-r--r-- | parser/parser.go | 126 |
1 files changed, 126 insertions, 0 deletions
diff --git a/parser/parser.go b/parser/parser.go new file mode 100644 index 0000000..222e4c0 --- /dev/null +++ b/parser/parser.go @@ -0,0 +1,126 @@ +package parser + +import ( + "fmt" + "strconv" + "strings" + + "go.neonxp.dev/json/model" +) + +func Parse(json string) (*model.Node, error) { + l := newLexer(json) + go l.Run(initJson) + n, err := parse(l.Output) + if err != nil { + return nil, err + } + return model.NewNode(n), nil +} + +func parse(ch chan lexem) (any, error) { + prefix := <-ch + switch prefix.Type { + case lObjectStart: + return parseObject(ch) + case lArrayStart: + return parseArray(ch) + case lString: + return strings.Trim(prefix.Value, `"`), nil + case lNumber: + num, err := strconv.ParseFloat(prefix.Value, 64) + if err != nil { + return nil, err + } + return num, nil + case lBoolean: + if strings.ToLower(prefix.Value) == "true" { + return true, nil + } + return false, nil + case lNull: + return nil, nil + } + return nil, fmt.Errorf("ivalid token: '%s' type=%s", prefix.Value, prefix.Type.String()) +} + +func parseObject(ch chan lexem) (model.NodeObjectValue, error) { + m := model.NodeObjectValue{} + nextKey := "" + for l := range ch { + switch l.Type { + case lObjectKey: + nextKey = strings.Trim(l.Value, `"`) + case lString: + m.Set(nextKey, strings.Trim(l.Value, `"`)) + case lNumber: + num, err := strconv.ParseFloat(l.Value, 64) + if err != nil { + return nil, err + } + m.Set(nextKey, num) + case lBoolean: + if strings.ToLower(l.Value) == "true" { + m.Set(nextKey, true) + continue + } + m.Set(nextKey, false) + case lNull: + m.Set(nextKey, nil) + case lObjectStart: + obj, err := parseObject(ch) + if err != nil { + return nil, err + } + m.Set(nextKey, obj) + case lArrayStart: + arr, err := parseArray(ch) + if err != nil { + return nil, err + } + m.Set(nextKey, arr) + case lObjectEnd: + return m, nil + } + } + return nil, fmt.Errorf("unexpected end of object") +} + +func parseArray(ch chan lexem) (model.NodeArrayValue, error) { + m := model.NodeArrayValue{} + for l := range ch { + switch l.Type { + case lString: + m = append(m, model.NewNode(strings.Trim(l.Value, `"`))) + case lNumber: + num, err := strconv.ParseFloat(l.Value, 64) + if err != nil { + return nil, err + } + m = append(m, model.NewNode(num)) + case lBoolean: + if strings.ToLower(l.Value) == "true" { + m = append(m, model.NewNode(true)) + continue + } + m = append(m, model.NewNode(false)) + case lNull: + m = append(m, model.NewNode(nil)) + case lObjectStart: + obj, err := parseObject(ch) + if err != nil { + return nil, err + } + m = append(m, model.NewNode(obj)) + case lArrayStart: + arr, err := parseArray(ch) + if err != nil { + return nil, err + } + m = append(m, model.NewNode(arr)) + case lArrayEnd: + return m, nil + } + } + return nil, fmt.Errorf("unexpected end of object") +} |