summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRuss Cox <rsc@golang.org>2008-12-11 12:25:58 -0800
committerRuss Cox <rsc@golang.org>2008-12-11 12:25:58 -0800
commit8708b6b3e44bfd0da24095db8c4c49b425405158 (patch)
treee1ced2b2e9d92ddbcd1e7b6c9f53b57c529d0efc
parentcfa051116c97dc97e02fcdde9887c2e455dead81 (diff)
downloadgolang-8708b6b3e44bfd0da24095db8c4c49b425405158.tar.gz
add JSON library
R=r DELTA=1127 (1127 added, 0 deleted, 0 changed) OCL=20975 CL=20983
-rw-r--r--src/lib/Makefile3
-rw-r--r--src/lib/json/Makefile64
-rw-r--r--src/lib/json/generic.go303
-rw-r--r--src/lib/json/generic_test.go72
-rw-r--r--src/lib/json/parse.go400
-rw-r--r--src/lib/json/struct.go214
-rw-r--r--src/lib/json/struct_test.go82
-rwxr-xr-xsrc/run.bash1
8 files changed, 1139 insertions, 0 deletions
diff --git a/src/lib/Makefile b/src/lib/Makefile
index 7d855e434..32e291830 100644
--- a/src/lib/Makefile
+++ b/src/lib/Makefile
@@ -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
index 000000000..581f9d4b6
--- /dev/null
+++ b/src/lib/json/Makefile
@@ -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
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, ""
+}
diff --git a/src/lib/json/generic_test.go b/src/lib/json/generic_test.go
new file mode 100644
index 000000000..a061af032
--- /dev/null
+++ b/src/lib/json/generic_test.go
@@ -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
index 000000000..1373a124a
--- /dev/null
+++ b/src/lib/json/parse.go
@@ -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
index 000000000..091dd7b22
--- /dev/null
+++ b/src/lib/json/struct.go
@@ -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
index 000000000..c5dc903da
--- /dev/null
+++ b/src/lib/json/struct_test.go
@@ -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);
+ }
+}
diff --git a/src/run.bash b/src/run.bash
index dccc924eb..979a5ac02 100755
--- a/src/run.bash
+++ b/src/run.bash
@@ -26,6 +26,7 @@ maketest() {
maketest \
lib/fmt\
lib/hash\
+ lib/json\
lib/math\
lib/reflect\
lib/regexp\