]> Cypherpunks repositories - gostls13.git/commitdiff
add JSON library
authorRuss Cox <rsc@golang.org>
Thu, 11 Dec 2008 20:25:58 +0000 (12:25 -0800)
committerRuss Cox <rsc@golang.org>
Thu, 11 Dec 2008 20:25:58 +0000 (12:25 -0800)
R=r
DELTA=1127  (1127 added, 0 deleted, 0 changed)
OCL=20975
CL=20983

src/lib/Makefile
src/lib/json/Makefile [new file with mode: 0644]
src/lib/json/generic.go [new file with mode: 0644]
src/lib/json/generic_test.go [new file with mode: 0644]
src/lib/json/parse.go [new file with mode: 0644]
src/lib/json/struct.go [new file with mode: 0644]
src/lib/json/struct_test.go [new file with mode: 0644]
src/run.bash

index 7d855e4346b03485449e93800fdb0fe940cf3801..32e2918307d53ef44b84b9e6e49508812553ec9d 100644 (file)
@@ -12,6 +12,7 @@ DIRS=\
        hash\
        http\
        io\
+       json\
        math\
        net\
        os\
@@ -94,6 +95,8 @@ fmt.dirinstall: io.dirinstall reflect.dirinstall strconv.dirinstall
 hash.dirinstall: os.dirinstall
 http.dirinstall: bufio.install io.dirinstall net.dirinstall os.dirinstall strings.install
 io.dirinstall: os.dirinstall syscall.dirinstall
+json.dirinstall: container/array.dirinstall fmt.dirinstall io.dirinstall math.dirinstall \
+       strconv.dirinstall strings.install utf8.install
 net.dirinstall: fmt.dirinstall once.install os.dirinstall strconv.dirinstall
 os.dirinstall: syscall.dirinstall
 regexp.dirinstall: os.dirinstall
diff --git a/src/lib/json/Makefile b/src/lib/json/Makefile
new file mode 100644 (file)
index 0000000..581f9d4
--- /dev/null
@@ -0,0 +1,64 @@
+# 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.
+
+# DO NOT EDIT.  Automatically generated by gobuild.
+# gobuild -m >Makefile
+O=6
+GC=$(O)g
+CC=$(O)c -w
+AS=$(O)a
+AR=$(O)ar
+
+default: packages
+
+clean:
+       rm -f *.$O *.a $O.out
+
+test: packages
+       gotest
+
+coverage: packages
+       gotest
+       6cov -g `pwd` | grep -v '_test\.go:'
+
+%.$O: %.go
+       $(GC) $*.go
+
+%.$O: %.c
+       $(CC) $*.c
+
+%.$O: %.s
+       $(AS) $*.s
+
+O1=\
+       parse.$O\
+
+O2=\
+       generic.$O\
+       struct.$O\
+
+json.a: a1 a2
+
+a1:    $(O1)
+       $(AR) grc json.a parse.$O
+       rm -f $(O1)
+
+a2:    $(O2)
+       $(AR) grc json.a generic.$O struct.$O
+       rm -f $(O2)
+
+newpkg: clean
+       $(AR) grc json.a
+
+$(O1): newpkg
+$(O2): a1
+
+nuke: clean
+       rm -f $(GOROOT)/pkg/json.a
+
+packages: json.a
+
+install: packages
+       cp json.a $(GOROOT)/pkg/json.a
+
diff --git a/src/lib/json/generic.go b/src/lib/json/generic.go
new file mode 100644 (file)
index 0000000..e5e76bf
--- /dev/null
@@ -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, ""
+}
diff --git a/src/lib/json/generic_test.go b/src/lib/json/generic_test.go
new file mode 100644 (file)
index 0000000..a061af0
--- /dev/null
@@ -0,0 +1,72 @@
+// 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.
+
+package json
+
+import (
+       "json";
+       "testing";
+)
+
+var jsontests = []string {
+       `null`,
+       `true`,
+       `false`,
+       `"abc"`,
+       `123`,
+       `0.1`,
+       `1e-10`,
+       `[]`,
+       `[1,2,3,4]`,
+       `[1,2,"abc",null,true,false]`,
+       `{}`,
+       `{"a":1}`,
+}
+
+export func TestJson(t *testing.T) {
+       for i := 0; i < len(jsontests); i++ {
+               val, ok, errtok := StringToJson(jsontests[i]);
+               if !ok {
+                       t.Errorf("StringToJson(%#q) => error near %v", jsontests[i], errtok);
+                       continue;
+               }
+               str := JsonToString(val);
+               if str != jsontests[i] {
+                       t.Errorf("JsonToString(StringToJson(%#q)) = %#q", jsontests[i], str);
+                       continue;
+               }
+       }
+}
+
+export func TestJsonMap(t *testing.T) {
+       values := new(map[string]Json);
+       mapstr := "{";
+       for i := 0; i < len(jsontests); i++ {
+               val, ok, errtok := StringToJson(jsontests[i]);
+               if !ok {
+                       t.Errorf("StringToJson(%#q) => error near %v", jsontests[i], errtok);
+               }
+               if i > 0 {
+                       mapstr += ",";
+               }
+               values[jsontests[i]] = val;
+               mapstr += Quote(jsontests[i]);
+               mapstr += ":";
+               mapstr += JsonToString(val);
+       }
+       mapstr += "}";
+
+       mapv, ok, errtok := StringToJson(mapstr);
+       if !ok {
+               t.Fatalf("StringToJson(%#q) => error near %v", mapstr, errtok);
+       }
+       if mapv == nil {
+               t.Fatalf("StringToJson(%#q) => nil, %v, %v", mapstr, ok, errtok);
+       }
+       for k,v range values {
+               if v1 := mapv.Get(k); !Equal(v1, v) {
+                       t.Errorf("MapTest: Walk(%#q) => %v, want %v", k, v1, v);
+               }
+       }
+}
diff --git a/src/lib/json/parse.go b/src/lib/json/parse.go
new file mode 100644 (file)
index 0000000..1373a12
--- /dev/null
@@ -0,0 +1,400 @@
+// 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.
+
+// JSON (JavaScript Object Notation) parser.
+// See http://www.json.org/
+
+package json
+
+import (
+       "array";
+       "fmt";
+       "io";
+       "math";
+       "strconv";
+       "strings";
+       "utf8";
+)
+
+// Strings
+//
+//   Double quoted with escapes: \" \\ \/ \b \f \n \r \t \uXXXX.
+//   No literal control characters, supposedly.
+//   Have also seen \' and embedded newlines.
+
+func UnHex(p string, r, l int) (v int, ok bool) {
+       v = 0;
+       for i := r; i < l; i++ {
+               if i >= len(p) {
+                       return 0, false
+               }
+               v *= 16;
+               switch {
+               case '0' <= p[i] && p[i] <= '9':
+                       v += int(p[i] - '0');
+               case 'a' <= p[i] && p[i] <= 'f':
+                       v += int(p[i] - 'a' + 10);
+               case 'A' <= p[i] && p[i] <= 'F':
+                       v += int(p[i] - 'A' + 10);
+               default:
+                       return 0, false;
+               }
+       }
+       return v, true;
+}
+
+export func Unquote(s string) (t string, ok bool) {
+       if len(s) < 2 || s[0] != '"' || s[len(s)-1] != '"' {
+               return
+       }
+       b := new([]byte, len(s));
+       w := 0;
+       for r := 1; r < len(s)-1; {
+               switch {
+               case s[r] == '\\':
+                       r++;
+                       if r >= len(s)-1 {
+                               return
+                       }
+                       switch s[r] {
+                       default:
+                               return;
+                       case '"', '\\', '/', '\'':
+                               b[w] = s[r];
+                               r++;
+                               w++;
+                       case 'b':
+                               b[w] = '\b';
+                               r++;
+                               w++;
+                       case 'f':
+                               b[w] = '\f';
+                               r++;
+                               w++;
+                       case 'n':
+                               b[w] = '\n';
+                               r++;
+                               w++;
+                       case 'r':
+                               b[w] = '\r';
+                               r++;
+                               w++;
+                       case 't':
+                               b[w] = '\t';
+                               r++;
+                               w++;
+                       case 'u':
+                               r++;
+                               rune, ok := UnHex(s, r, 4);
+                               if !ok {
+                                       return
+                               }
+                               r += 4;
+                               w += utf8.EncodeRune(rune, b[w:len(b)]);
+                       }
+               // Control characters are invalid, but we've seen raw \n.
+               case s[r] < ' ' && s[r] != '\n':
+                       if s[r] == '\n' {
+                               b[w] = '\n';
+                               r++;
+                               w++;
+                               break;
+                       }
+                       return;
+               // ASCII
+               case s[r] < utf8.RuneSelf:
+                       b[w] = s[r];
+                       r++;
+                       w++;
+               // Coerce to well-formed UTF-8.
+               default:
+                       rune, size := utf8.DecodeRuneInString(s, r);
+                       r += size;
+                       w += utf8.EncodeRune(rune, b[w:len(b)]);
+               }
+       }
+       return string(b[0:w]), true
+}
+
+export func Quote(s string) string {
+       chr := new([]byte, utf8.UTFMax);
+       chr0 := chr[0:1];
+       b := new(io.ByteBuffer);
+       chr[0] = '"';
+       b.Write(chr0);
+       for i := 0; i < len(s); i++ {
+               switch {
+               case s[i]=='"' || s[i]=='\\':
+                       chr[0] = '\\';
+                       chr[1] = s[i];
+                       b.Write(chr[0:2]);
+
+               case s[i] == '\b':
+                       chr[0] = '\\';
+                       chr[1] = 'b';
+                       b.Write(chr[0:2]);
+
+               case s[i] == '\f':
+                       chr[0] = '\\';
+                       chr[1] = 'f';
+                       b.Write(chr[0:2]);
+
+               case s[i] == '\n':
+                       chr[0] = '\\';
+                       chr[1] = 'n';
+                       b.Write(chr[0:2]);
+
+               case s[i] == '\r':
+                       chr[0] = '\\';
+                       chr[1] = 'r';
+                       b.Write(chr[0:2]);
+
+               case s[i] == '\t':
+                       chr[0] = '\\';
+                       chr[1] = 't';
+                       b.Write(chr[0:2]);
+
+               case 0x20 <= s[i] && s[i] < utf8.RuneSelf:
+                       chr[0] = s[i];
+                       b.Write(chr0);
+               }
+       }
+       chr[0] = '"';
+       b.Write(chr0);
+       return string(b.Data());
+}
+
+
+// Lexer
+
+type Lexer struct {
+       s string;
+       i int;
+       kind int;
+       token string;
+}
+
+func Punct(c byte) bool {
+       return c=='"' || c=='[' || c==']' || c==':' || c=='{' || c=='}' || c==','
+}
+
+func White(c byte) bool {
+       return c==' ' || c=='\t' || c=='\n' || c=='\v'
+}
+
+func SkipWhite(p string, i int) int {
+       for i < len(p) && White(p[i]) {
+               i++
+       }
+       return i
+}
+
+func SkipToken(p string, i int) int {
+       for i < len(p) && !Punct(p[i]) && !White(p[i]) {
+               i++
+       }
+       return i
+}
+
+func SkipString(p string, i int) int {
+       for i++; i < len(p) && p[i] != '"'; i++ {
+               if p[i] == '\\' {
+                       i++
+               }
+       }
+       if i >= len(p) {
+               return i
+       }
+       return i+1
+}
+
+func (t *Lexer) Next() {
+       i, s := t.i, t.s;
+       i = SkipWhite(s, i);
+       if i >= len(s) {
+               t.kind = 0;
+               t.token = "";
+               t.i = len(s);
+               return;
+       }
+
+       c := s[i];
+       switch {
+       case c == '-' || '0' <= c && c <= '9':
+               j := SkipToken(s, i);
+               t.kind = '1';
+               t.token = s[i:j];
+               i = j;
+
+       case 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z':
+               j := SkipToken(s, i);
+               t.kind = 'a';
+               t.token = s[i:j];
+               i = j;
+
+       case c == '"':
+               j := SkipString(s, i);
+               t.kind = '"';
+               t.token = s[i:j];
+               i = j;
+
+       case c == '[', c == ']', c == ':', c == '{', c == '}', c == ',':
+               t.kind = int(c);
+               t.token = s[i:i+1];
+               i++;
+
+       default:
+               t.kind = '?';
+               t.token = s[i:i+1];
+       }
+
+       t.i = i;
+}
+
+
+// Parser
+//
+// Implements parsing but not the actions.  Those are
+// carried out by the implementation of the Builder interface.
+// A Builder represents the object being created.
+// Calling a method like Int64(i) sets that object to i.
+// Calling a method like Elem(i) or Key(s) creates a
+// new builder for a subpiece of the object (logically,
+// an array element or a map key).
+//
+// There are two Builders, in other files.
+// The JsonBuilder builds a generic Json structure
+// in which maps are maps.
+// The StructBuilder copies data into a possibly
+// nested data structure, using the "map keys"
+// as struct field names.
+
+type Value interface {}
+
+export type Builder interface {
+       // Set value
+       Int64(i int64);
+       Uint64(i uint64);
+       Float64(f float64);
+       String(s string);
+       Bool(b bool);
+       Null();
+       Array();
+       Map();
+
+       // Create sub-Builders
+       Elem(i int) Builder;
+       Key(s string) Builder;
+}
+
+func ParseValue(lex *Lexer, build Builder) bool {
+       ok := false;
+Switch:
+       switch lex.kind {
+       case 0:
+               break;
+       case '1':
+               // If the number is exactly an integer, use that.
+               if i, err := strconv.atoi64(lex.token); err == nil {
+                       build.Int64(i);
+                       ok = true;
+               }
+               else if i, err := strconv.atoui64(lex.token); err == nil {
+                       build.Uint64(i);
+                       ok = true;
+               }
+               // Fall back to floating point.
+               else if f, err := strconv.atof64(lex.token); err == nil {
+                       build.Float64(f);
+                       ok = true;
+               }
+
+       case 'a':
+               switch lex.token {
+               case "true":
+                       build.Bool(true);
+                       ok = true;
+               case "false":
+                       build.Bool(false);
+                       ok = true;
+               case "null":
+                       build.Null();
+                       ok = true;
+               }
+
+       case '"':
+               if str, ok1 := Unquote(lex.token); ok1 {
+                       build.String(str);
+                       ok = true;
+               }
+
+       case '[':
+               // array
+               build.Array();
+               lex.Next();
+               n := 0;
+               for lex.kind != ']' {
+                       if n > 0 {
+                               if lex.kind != ',' {
+                                       break Switch;
+                               }
+                               lex.Next();
+                       }
+                       if !ParseValue(lex, build.Elem(n)) {
+                               break Switch;
+                       }
+                       n++;
+               }
+               ok = true;
+
+       case '{':
+               // map
+               lex.Next();
+               build.Map();
+               n := 0;
+               for lex.kind != '}' {
+                       if n > 0 {
+                               if lex.kind != ',' {
+                                       break Switch;
+                               }
+                               lex.Next();
+                       }
+                       if lex.kind != '"' {
+                               break Switch;
+                       }
+                       key, ok := Unquote(lex.token);
+                       if !ok {
+                               break Switch;
+                       }
+                       lex.Next();
+                       if lex.kind != ':' {
+                               break Switch;
+                       }
+                       lex.Next();
+                       if !ParseValue(lex, build.Key(key)) {
+                               break Switch;
+                       }
+                       n++;
+               }
+               ok = true;
+       }
+
+       if ok {
+               lex.Next();
+       }
+       return ok;
+}
+
+export func Parse(s string, build Builder) (ok bool, errindx int, errtok string) {
+       lex := new(Lexer);
+       lex.s = s;
+       lex.Next();
+       if ParseValue(lex, build) {
+               if lex.kind == 0 {      // EOF
+                       return true, 0, ""
+               }
+       }
+       return false, lex.i, lex.token
+}
+
diff --git a/src/lib/json/struct.go b/src/lib/json/struct.go
new file mode 100644 (file)
index 0000000..091dd7b
--- /dev/null
@@ -0,0 +1,214 @@
+// 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.
+
+// Marshalling and unmarshalling of
+// JSON data into Go structs using reflection.
+
+package json
+
+import (
+       "json";
+       "reflect";
+)
+
+type StructBuilder struct {
+       val reflect.Value
+}
+
+var nobuilder *StructBuilder
+
+func SetFloat(v reflect.Value, f float64) {
+       switch v.Kind() {
+       case reflect.FloatKind:
+               v.(reflect.FloatValue).Set(float(f));
+       case reflect.Float32Kind:
+               v.(reflect.Float32Value).Set(float32(f));
+       case reflect.Float64Kind:
+               v.(reflect.Float64Value).Set(float64(f));
+       }
+}
+
+func SetInt(v reflect.Value, i int64) {
+       switch v.Kind() {
+       case reflect.IntKind:
+               v.(reflect.IntValue).Set(int(i));
+       case reflect.Int8Kind:
+               v.(reflect.Int8Value).Set(int8(i));
+       case reflect.Int16Kind:
+               v.(reflect.Int16Value).Set(int16(i));
+       case reflect.Int32Kind:
+               v.(reflect.Int32Value).Set(int32(i));
+       case reflect.Int64Kind:
+               v.(reflect.Int64Value).Set(int64(i));
+       case reflect.UintKind:
+               v.(reflect.UintValue).Set(uint(i));
+       case reflect.Uint8Kind:
+               v.(reflect.Uint8Value).Set(uint8(i));
+       case reflect.Uint16Kind:
+               v.(reflect.Uint16Value).Set(uint16(i));
+       case reflect.Uint32Kind:
+               v.(reflect.Uint32Value).Set(uint32(i));
+       case reflect.Uint64Kind:
+               v.(reflect.Uint64Value).Set(uint64(i));
+       }
+}
+
+func (b *StructBuilder) Int64(i int64) {
+       if b == nil {
+               return
+       }
+       v := b.val;
+       switch v.Kind() {
+       case reflect.FloatKind, reflect.Float32Kind, reflect.Float64Kind:
+               SetFloat(v, float64(i));
+       default:
+               SetInt(v, i);
+       }
+}
+
+func (b *StructBuilder) Uint64(i uint64) {
+       if b == nil {
+               return
+       }
+       v := b.val;
+       switch v.Kind() {
+       case reflect.FloatKind, reflect.Float32Kind, reflect.Float64Kind:
+               SetFloat(v, float64(i));
+       default:
+               SetInt(v, int64(i));
+       }
+}
+
+func (b *StructBuilder) Float64(f float64) {
+       if b == nil {
+               return
+       }
+       v := b.val;
+       switch v.Kind() {
+       case reflect.FloatKind, reflect.Float32Kind, reflect.Float64Kind:
+               SetFloat(v, f);
+       default:
+               SetInt(v, int64(f));
+       }
+}
+
+func (b *StructBuilder) Null() {
+}
+
+func (b *StructBuilder) String(s string) {
+       if b == nil {
+               return
+       }
+       if v := b.val; v.Kind() == reflect.StringKind {
+               v.(reflect.StringValue).Set(s);
+       }
+}
+
+func (b *StructBuilder) Bool(tf bool) {
+       if b == nil {
+               return
+       }
+       if v := b.val; v.Kind() == reflect.BoolKind {
+               v.(reflect.BoolValue).Set(tf);
+       }
+}
+
+func (b *StructBuilder) Array() {
+       if b == nil {
+               return
+       }
+       if v := b.val; v.Kind() == reflect.PtrKind {
+               pv := v.(reflect.PtrValue);
+               psubtype := pv.Type().(reflect.PtrType).Sub();
+               if pv.Get() == nil && psubtype.Kind() == reflect.ArrayKind {
+                       av := reflect.NewOpenArrayValue(psubtype, 0, 8);
+                       pv.SetSub(av);
+               }
+       }
+}
+
+func (b *StructBuilder) Elem(i int) Builder {
+       if b == nil || i < 0 {
+               return nobuilder
+       }
+       v := b.val;
+       if v.Kind() == reflect.PtrKind {
+               // If we have a pointer to an array, allocate or grow
+               // the array as necessary.  Then set v to the array itself.
+               pv := v.(reflect.PtrValue);
+               psub := pv.Sub();
+               if psub.Kind() == reflect.ArrayKind {
+                       av := psub.(reflect.ArrayValue);
+                       if i > av.Cap() {
+                               n := av.Cap();
+                               if n < 8 {
+                                       n = 8
+                               }
+                               for n <= i {
+                                       n *= 2
+                               }
+                               av1 := reflect.NewOpenArrayValue(av.Type(), av.Len(), n);
+                               reflect.CopyArray(av1, av, av.Len());
+                               pv.SetSub(av1);
+                               av = av1;
+                       }
+               }
+               v = psub;
+       }
+       if v.Kind() == reflect.ArrayKind {
+               // Array was grown above, or is fixed size.
+               av := v.(reflect.ArrayValue);
+               if av.Len() <= i && i < av.Cap() {
+                       av.SetLen(i+1);
+               }
+               if i < av.Len() {
+                       return &StructBuilder{ av.Elem(i) }
+               }
+       }
+       return nobuilder
+}
+
+func (b *StructBuilder) Map() {
+       if b == nil {
+               return
+       }
+       if v := b.val; v.Kind() == reflect.PtrKind {
+               pv := v.(reflect.PtrValue);
+               if pv.Get() == nil {
+                       pv.SetSub(reflect.NewInitValue(pv.Type().(reflect.PtrType).Sub()))
+               }
+       }
+}
+
+func (b *StructBuilder) Key(k string) Builder {
+       if b == nil {
+               return nobuilder
+       }
+       v := b.val;
+       if v.Kind() == reflect.PtrKind {
+               v = v.(reflect.PtrValue).Sub();
+       }
+       if v.Kind() == reflect.StructKind {
+               sv := v.(reflect.StructValue);
+               t := v.Type().(reflect.StructType);
+               for i := 0; i < t.Len(); i++ {
+                       name, typ, tag, off := t.Field(i);
+                       if k == name {
+                               return &StructBuilder{ sv.Field(i) }
+                       }
+               }
+       }
+       return nobuilder
+}
+
+export func Unmarshal(s string, val interface{}) (ok bool, errtok string) {
+       var errindx int;
+       var val1 interface{};
+       b := &StructBuilder{ reflect.NewValue(val) };
+       ok, errindx, errtok = Parse(s, b);
+       if !ok {
+               return false, errtok
+       }
+       return true, ""
+}
diff --git a/src/lib/json/struct_test.go b/src/lib/json/struct_test.go
new file mode 100644 (file)
index 0000000..c5dc903
--- /dev/null
@@ -0,0 +1,82 @@
+// 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.
+
+package json
+
+import (
+       "json";
+       "testing";
+)
+
+type MyStruct struct {
+       t bool;
+       f bool;
+       s string;
+       i8 int8;
+       i16 int16;
+       i32 int32;
+       i64 int64;
+       u8 uint8;
+       u16 uint16;
+       u32 uint32;
+       u64 uint64;
+       i int;
+       u uint;
+       fl float;
+       fl32 float32;
+       fl64 float64;
+       a *[]string;
+       my *MyStruct;
+};
+
+const Encoded =
+       `{"t":true,"f":false,"s":"abc","i8":1,"i16":2,"i32":3,"i64":4,`
+       ` "u8":5,"u16":6,"u32":7,"u64":8,`
+       ` "i":-9,"u":10,"bogusfield":"should be ignored",`
+       ` "fl":11.5,"fl32":12.25,"fl64":13.75,`
+       ` "a":["x","y","z"],"my":{"s":"subguy"}}`;
+
+
+func Check(t *testing.T, ok bool, name string, v interface{}) {
+       if !ok {
+               t.Errorf("%s = %v (BAD)", name, v);
+       } else {
+               t.Logf("%s = %v (good)", name, v);
+       }
+}
+
+export func TestUnmarshal(t *testing.T) {
+       var m MyStruct;
+       m.f = true;
+       ok, errtok := Unmarshal(Encoded, &m);
+       if !ok {
+               t.Fatalf("Unmarshal failed near %s", errtok);
+       }
+       Check(t, m.t==true, "t", m.t);
+       Check(t, m.f==false, "f", m.f);
+       Check(t, m.s=="abc", "s", m.s);
+       Check(t, m.i8==1, "i8", m.i8);
+       Check(t, m.i16==2, "i16", m.i16);
+       Check(t, m.i32==3, "i32", m.i32);
+       Check(t, m.i64==4, "i64", m.i64);
+       Check(t, m.u8==5, "u8", m.u8);
+       Check(t, m.u16==6, "u16", m.u16);
+       Check(t, m.u32==7, "u32", m.u32);
+       Check(t, m.u64==8, "u64", m.u64);
+       Check(t, m.i==-9, "i", m.i);
+       Check(t, m.u==10, "u", m.u);
+       Check(t, m.fl==11.5, "fl", m.fl);
+       Check(t, m.fl32==12.25, "fl32", m.fl32);
+       Check(t, m.fl64==13.75, "fl64", m.fl64);
+       Check(t, m.a!=nil, "a", m.a);
+       if m.a != nil {
+               Check(t, m.a[0]=="x", "a[0]", m.a[0]);
+               Check(t, m.a[1]=="y", "a[1]", m.a[1]);
+               Check(t, m.a[2]=="z", "a[2]", m.a[2]);
+       }
+       Check(t, m.my!=nil, "my", m.my);
+       if m.my != nil {
+               Check(t, m.my.s=="subguy", "my.s", m.my.s);
+       }
+}
index dccc924eb212fabb6559aac698724472d42e7905..979a5ac0201145f4bff3096512ae8b853cf3db0a 100755 (executable)
@@ -26,6 +26,7 @@ maketest() {
 maketest \
        lib/fmt\
        lib/hash\
+       lib/json\
        lib/math\
        lib/reflect\
        lib/regexp\