add JSON library

R=r
DELTA=1127  (1127 added, 0 deleted, 0 changed)
OCL=20975
CL=20983
This commit is contained in:
Russ Cox 2008-12-11 12:25:58 -08:00
parent fa945d5bf8
commit 793a6effcf
8 changed files with 1139 additions and 0 deletions

View 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

64
src/lib/json/Makefile Normal file
View File

@ -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

303
src/lib/json/generic.go Normal file
View File

@ -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, ""
}

View File

@ -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);
}
}
}

400
src/lib/json/parse.go Normal file
View File

@ -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
}

214
src/lib/json/struct.go Normal file
View File

@ -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, ""
}

View File

@ -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);
}
}

View File

@ -26,6 +26,7 @@ maketest() {
maketest \
lib/fmt\
lib/hash\
lib/json\
lib/math\
lib/reflect\
lib/regexp\