summaryrefslogtreecommitdiff
path: root/src/lib/json/generic.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/json/generic.go')
-rw-r--r--src/lib/json/generic.go303
1 files changed, 303 insertions, 0 deletions
diff --git a/src/lib/json/generic.go b/src/lib/json/generic.go
new file mode 100644
index 000000000..e5e76bf59
--- /dev/null
+++ b/src/lib/json/generic.go
@@ -0,0 +1,303 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Generic JSON representation.
+
+package json
+
+import (
+ "array";
+ "fmt";
+ "math";
+ "json";
+ "strconv";
+ "strings";
+)
+
+export const (
+ StringKind = iota;
+ NumberKind;
+ MapKind; // JSON term is "Object", but in Go, it's a map
+ ArrayKind;
+ BoolKind;
+ NullKind;
+)
+
+export type Json interface {
+ Kind() int;
+ String() string;
+ Number() float64;
+ Bool() bool;
+ Get(s string) Json;
+ Elem(i int) Json;
+ Len() int;
+}
+
+export func JsonToString(j Json) string {
+ if j == nil {
+ return "null"
+ }
+ if j.Kind() == StringKind {
+ return Quote(j.String())
+ }
+ return j.String()
+}
+
+type Null struct { }
+export var null Json = &Null{}
+func (*Null) Kind() int { return NullKind }
+func (*Null) String() string { return "null" }
+func (*Null) Number() float64 { return 0 }
+func (*Null) Bool() bool { return false }
+func (*Null) Get(s string) Json { return null }
+func (*Null) Elem(int) Json { return null }
+func (*Null) Len() int { return 0 }
+
+type String struct { s string; Null }
+func (j *String) Kind() int { return StringKind }
+func (j *String) String() string { return j.s }
+
+type Number struct { f float64; Null }
+func (j *Number) Kind() int { return NumberKind }
+func (j *Number) Number() float64 { return j.f }
+func (j *Number) String() string {
+ if math.Floor(j.f) == j.f {
+ return fmt.sprintf("%.0f", j.f);
+ }
+ return fmt.sprintf("%g", j.f);
+}
+
+type Array struct { a *array.Array; Null }
+func (j *Array) Kind() int { return ArrayKind }
+func (j *Array) Len() int { return j.a.Len() }
+func (j *Array) Elem(i int) Json {
+ if i < 0 || i >= j.a.Len() {
+ return null
+ }
+ return j.a.At(i)
+}
+func (j *Array) String() string {
+ s := "[";
+ for i := 0; i < j.a.Len(); i++ {
+ if i > 0 {
+ s += ",";
+ }
+ s += JsonToString(j.a.At(i).(Json));
+ }
+ s += "]";
+ return s;
+}
+
+type Bool struct { b bool; Null }
+func (j *Bool) Kind() int { return BoolKind }
+func (j *Bool) Bool() bool { return j.b }
+func (j *Bool) String() string {
+ if j.b {
+ return "true"
+ }
+ return "false"
+}
+
+type Map struct { m *map[string]Json; Null }
+func (j *Map) Kind() int { return MapKind }
+func (j *Map) Get(s string) Json {
+ if j.m == nil {
+ return null
+ }
+ v, ok := j.m[s];
+ if !ok {
+ return null
+ }
+ return v;
+}
+func (j *Map) String() string {
+ s := "{";
+ first := true;
+ for k,v range j.m {
+ if first {
+ first = false;
+ } else {
+ s += ",";
+ }
+ s += Quote(k);
+ s += ":";
+ s += JsonToString(v);
+ }
+ s += "}";
+ return s;
+}
+
+export func Walk(j Json, path string) Json {
+ for len(path) > 0 {
+ var elem string;
+ if i := strings.index(path, '/'); i >= 0 {
+ elem = path[0:i];
+ path = path[i+1:len(path)];
+ } else {
+ elem = path;
+ path = "";
+ }
+ switch j.Kind() {
+ case ArrayKind:
+ indx, err := strconv.atoi(elem);
+ if err != nil {
+ return null
+ }
+ j = j.Elem(indx);
+ case MapKind:
+ j = j.Get(elem);
+ default:
+ return null
+ }
+ }
+ return j
+}
+
+export func Equal(a, b Json) bool {
+ switch {
+ case a == nil && b == nil:
+ return true;
+ case a == nil || b == nil:
+ return false;
+ case a.Kind() != b.Kind():
+ return false;
+ }
+
+ switch a.Kind() {
+ case NullKind:
+ return true;
+ case StringKind:
+ return a.String() == b.String();
+ case NumberKind:
+ return a.Number() == b.Number();
+ case BoolKind:
+ return a.Bool() == b.Bool();
+ case ArrayKind:
+ if a.Len() != b.Len() {
+ return false;
+ }
+ for i := 0; i < a.Len(); i++ {
+ if !Equal(a.Elem(i), b.Elem(i)) {
+ return false;
+ }
+ }
+ return true;
+ case MapKind:
+ m := a.(*Map).m;
+ if len(m) != len(b.(*Map).m) {
+ return false;
+ }
+ for k,v range m {
+ if !Equal(v, b.Get(k)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // invalid kind
+ return false;
+}
+
+
+// Parse builder for Json objects.
+
+type JsonBuilder struct {
+ // either writing to *ptr
+ ptr *Json;
+
+ // or to a[i] (can't set ptr = &a[i])
+ a *array.Array;
+ i int;
+
+ // or to m[k] (can't set ptr = &m[k])
+ m *map[string] Json;
+ k string;
+}
+
+func (b *JsonBuilder) Put(j Json) {
+ switch {
+ case b.ptr != nil:
+ *b.ptr = j;
+ case b.a != nil:
+ b.a.Set(b.i, j);
+ case b.m != nil:
+ b.m[b.k] = j;
+ }
+}
+
+func (b *JsonBuilder) Get() Json {
+ switch {
+ case b.ptr != nil:
+ return *b.ptr;
+ case b.a != nil:
+ return b.a.At(b.i);
+ case b.m != nil:
+ return b.m[b.k];
+ }
+ return nil
+}
+
+func (b *JsonBuilder) Float64(f float64) {
+ b.Put(&Number{f, Null{}})
+}
+
+func (b *JsonBuilder) Int64(i int64) {
+ b.Float64(float64(i))
+}
+
+func (b *JsonBuilder) Uint64(i uint64) {
+ b.Float64(float64(i))
+}
+
+func (b *JsonBuilder) Bool(tf bool) {
+ b.Put(&Bool{tf, Null{}})
+}
+
+func (b *JsonBuilder) Null() {
+ b.Put(null)
+}
+
+func (b *JsonBuilder) String(s string) {
+ b.Put(&String{s, Null{}})
+}
+
+
+func (b *JsonBuilder) Array() {
+ b.Put(&Array{array.New(0), Null{}})
+}
+
+func (b *JsonBuilder) Map() {
+ b.Put(&Map{new(map[string]Json), Null{}})
+}
+
+func (b *JsonBuilder) Elem(i int) Builder {
+ bb := new(JsonBuilder);
+ bb.a = b.Get().(*Array).a;
+ bb.i = i;
+ for i >= bb.a.Len() {
+ bb.a.Push(null)
+ }
+ return bb
+}
+
+func (b *JsonBuilder) Key(k string) Builder {
+ bb := new(JsonBuilder);
+ bb.m = b.Get().(*Map).m;
+ bb.k = k;
+ bb.m[k] = null;
+ return bb
+}
+
+export func StringToJson(s string) (json Json, ok bool, errtok string) {
+ var errindx int;
+ var j Json;
+ b := new(JsonBuilder);
+ b.ptr = &j;
+ ok, errindx, errtok = Parse(s, b);
+ if !ok {
+ return nil, false, errtok
+ }
+ return j, true, ""
+}