xref: /aosp_15_r20/external/starlark-go/starlarkjson/json.go (revision 4947cdc739c985f6d86941e22894f5cefe7c9e9a)
1*4947cdc7SCole Faust// Copyright 2020 The Bazel Authors. All rights reserved.
2*4947cdc7SCole Faust// Use of this source code is governed by a BSD-style
3*4947cdc7SCole Faust// license that can be found in the LICENSE file.
4*4947cdc7SCole Faust
5*4947cdc7SCole Faust// Package starlarkjson defines utilities for converting Starlark values
6*4947cdc7SCole Faust// to/from JSON strings. The most recent IETF standard for JSON is
7*4947cdc7SCole Faust// https://www.ietf.org/rfc/rfc7159.txt.
8*4947cdc7SCole Faustpackage starlarkjson // import "go.starlark.net/starlarkjson"
9*4947cdc7SCole Faust
10*4947cdc7SCole Faustimport (
11*4947cdc7SCole Faust	"bytes"
12*4947cdc7SCole Faust	"encoding/json"
13*4947cdc7SCole Faust	"fmt"
14*4947cdc7SCole Faust	"log"
15*4947cdc7SCole Faust	"math"
16*4947cdc7SCole Faust	"math/big"
17*4947cdc7SCole Faust	"sort"
18*4947cdc7SCole Faust	"strconv"
19*4947cdc7SCole Faust	"strings"
20*4947cdc7SCole Faust	"unicode/utf8"
21*4947cdc7SCole Faust
22*4947cdc7SCole Faust	"go.starlark.net/starlark"
23*4947cdc7SCole Faust	"go.starlark.net/starlarkstruct"
24*4947cdc7SCole Faust)
25*4947cdc7SCole Faust
26*4947cdc7SCole Faust// Module json is a Starlark module of JSON-related functions.
27*4947cdc7SCole Faust//
28*4947cdc7SCole Faust//   json = module(
29*4947cdc7SCole Faust//      encode,
30*4947cdc7SCole Faust//      decode,
31*4947cdc7SCole Faust//      indent,
32*4947cdc7SCole Faust//   )
33*4947cdc7SCole Faust//
34*4947cdc7SCole Faust// def encode(x):
35*4947cdc7SCole Faust//
36*4947cdc7SCole Faust// The encode function accepts one required positional argument,
37*4947cdc7SCole Faust// which it converts to JSON by cases:
38*4947cdc7SCole Faust// - A Starlark value that implements Go's standard json.Marshal
39*4947cdc7SCole Faust//   interface defines its own JSON encoding.
40*4947cdc7SCole Faust// - None, True, and False are converted to null, true, and false, respectively.
41*4947cdc7SCole Faust// - Starlark int values, no matter how large, are encoded as decimal integers.
42*4947cdc7SCole Faust//   Some decoders may not be able to decode very large integers.
43*4947cdc7SCole Faust// - Starlark float values are encoded using decimal point notation,
44*4947cdc7SCole Faust//   even if the value is an integer.
45*4947cdc7SCole Faust//   It is an error to encode a non-finite floating-point value.
46*4947cdc7SCole Faust// - Starlark strings are encoded as JSON strings, using UTF-16 escapes.
47*4947cdc7SCole Faust// - a Starlark IterableMapping (e.g. dict) is encoded as a JSON object.
48*4947cdc7SCole Faust//   It is an error if any key is not a string.
49*4947cdc7SCole Faust// - any other Starlark Iterable (e.g. list, tuple) is encoded as a JSON array.
50*4947cdc7SCole Faust// - a Starlark HasAttrs (e.g. struct) is encoded as a JSON object.
51*4947cdc7SCole Faust// It an application-defined type matches more than one the cases describe above,
52*4947cdc7SCole Faust// (e.g. it implements both Iterable and HasFields), the first case takes precedence.
53*4947cdc7SCole Faust// Encoding any other value yields an error.
54*4947cdc7SCole Faust//
55*4947cdc7SCole Faust// def decode(x):
56*4947cdc7SCole Faust//
57*4947cdc7SCole Faust// The decode function accepts one positional parameter, a JSON string.
58*4947cdc7SCole Faust// It returns the Starlark value that the string denotes.
59*4947cdc7SCole Faust// - Numbers are parsed as int or float, depending on whether they
60*4947cdc7SCole Faust//   contain a decimal point.
61*4947cdc7SCole Faust// - JSON objects are parsed as new unfrozen Starlark dicts.
62*4947cdc7SCole Faust// - JSON arrays are parsed as new unfrozen Starlark lists.
63*4947cdc7SCole Faust// Decoding fails if x is not a valid JSON string.
64*4947cdc7SCole Faust//
65*4947cdc7SCole Faust// def indent(str, *, prefix="", indent="\t"):
66*4947cdc7SCole Faust//
67*4947cdc7SCole Faust// The indent function pretty-prints a valid JSON encoding,
68*4947cdc7SCole Faust// and returns a string containing the indented form.
69*4947cdc7SCole Faust// It accepts one required positional parameter, the JSON string,
70*4947cdc7SCole Faust// and two optional keyword-only string parameters, prefix and indent,
71*4947cdc7SCole Faust// that specify a prefix of each new line, and the unit of indentation.
72*4947cdc7SCole Faust//
73*4947cdc7SCole Faustvar Module = &starlarkstruct.Module{
74*4947cdc7SCole Faust	Name: "json",
75*4947cdc7SCole Faust	Members: starlark.StringDict{
76*4947cdc7SCole Faust		"encode": starlark.NewBuiltin("json.encode", encode),
77*4947cdc7SCole Faust		"decode": starlark.NewBuiltin("json.decode", decode),
78*4947cdc7SCole Faust		"indent": starlark.NewBuiltin("json.indent", indent),
79*4947cdc7SCole Faust	},
80*4947cdc7SCole Faust}
81*4947cdc7SCole Faust
82*4947cdc7SCole Faustfunc encode(thread *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
83*4947cdc7SCole Faust	var x starlark.Value
84*4947cdc7SCole Faust	if err := starlark.UnpackPositionalArgs(b.Name(), args, kwargs, 1, &x); err != nil {
85*4947cdc7SCole Faust		return nil, err
86*4947cdc7SCole Faust	}
87*4947cdc7SCole Faust
88*4947cdc7SCole Faust	buf := new(bytes.Buffer)
89*4947cdc7SCole Faust
90*4947cdc7SCole Faust	var quoteSpace [128]byte
91*4947cdc7SCole Faust	quote := func(s string) {
92*4947cdc7SCole Faust		// Non-trivial escaping is handled by Go's encoding/json.
93*4947cdc7SCole Faust		if isPrintableASCII(s) {
94*4947cdc7SCole Faust			buf.Write(strconv.AppendQuote(quoteSpace[:0], s))
95*4947cdc7SCole Faust		} else {
96*4947cdc7SCole Faust			// TODO(adonovan): opt: RFC 8259 mandates UTF-8 for JSON.
97*4947cdc7SCole Faust			// Can we avoid this call?
98*4947cdc7SCole Faust			data, _ := json.Marshal(s)
99*4947cdc7SCole Faust			buf.Write(data)
100*4947cdc7SCole Faust		}
101*4947cdc7SCole Faust	}
102*4947cdc7SCole Faust
103*4947cdc7SCole Faust	var emit func(x starlark.Value) error
104*4947cdc7SCole Faust	emit = func(x starlark.Value) error {
105*4947cdc7SCole Faust		switch x := x.(type) {
106*4947cdc7SCole Faust		case json.Marshaler:
107*4947cdc7SCole Faust			// Application-defined starlark.Value types
108*4947cdc7SCole Faust			// may define their own JSON encoding.
109*4947cdc7SCole Faust			data, err := x.MarshalJSON()
110*4947cdc7SCole Faust			if err != nil {
111*4947cdc7SCole Faust				return err
112*4947cdc7SCole Faust			}
113*4947cdc7SCole Faust			buf.Write(data)
114*4947cdc7SCole Faust
115*4947cdc7SCole Faust		case starlark.NoneType:
116*4947cdc7SCole Faust			buf.WriteString("null")
117*4947cdc7SCole Faust
118*4947cdc7SCole Faust		case starlark.Bool:
119*4947cdc7SCole Faust			if x {
120*4947cdc7SCole Faust				buf.WriteString("true")
121*4947cdc7SCole Faust			} else {
122*4947cdc7SCole Faust				buf.WriteString("false")
123*4947cdc7SCole Faust			}
124*4947cdc7SCole Faust
125*4947cdc7SCole Faust		case starlark.Int:
126*4947cdc7SCole Faust			fmt.Fprint(buf, x)
127*4947cdc7SCole Faust
128*4947cdc7SCole Faust		case starlark.Float:
129*4947cdc7SCole Faust			if !isFinite(float64(x)) {
130*4947cdc7SCole Faust				return fmt.Errorf("cannot encode non-finite float %v", x)
131*4947cdc7SCole Faust			}
132*4947cdc7SCole Faust			fmt.Fprintf(buf, "%g", x) // always contains a decimal point
133*4947cdc7SCole Faust
134*4947cdc7SCole Faust		case starlark.String:
135*4947cdc7SCole Faust			quote(string(x))
136*4947cdc7SCole Faust
137*4947cdc7SCole Faust		case starlark.IterableMapping:
138*4947cdc7SCole Faust			// e.g. dict (must have string keys)
139*4947cdc7SCole Faust			buf.WriteByte('{')
140*4947cdc7SCole Faust			items := x.Items()
141*4947cdc7SCole Faust			for _, item := range items {
142*4947cdc7SCole Faust				if _, ok := item[0].(starlark.String); !ok {
143*4947cdc7SCole Faust					return fmt.Errorf("%s has %s key, want string", x.Type(), item[0].Type())
144*4947cdc7SCole Faust				}
145*4947cdc7SCole Faust			}
146*4947cdc7SCole Faust			sort.Slice(items, func(i, j int) bool {
147*4947cdc7SCole Faust				return items[i][0].(starlark.String) < items[j][0].(starlark.String)
148*4947cdc7SCole Faust			})
149*4947cdc7SCole Faust			for i, item := range items {
150*4947cdc7SCole Faust				if i > 0 {
151*4947cdc7SCole Faust					buf.WriteByte(',')
152*4947cdc7SCole Faust				}
153*4947cdc7SCole Faust				k, _ := starlark.AsString(item[0])
154*4947cdc7SCole Faust				quote(k)
155*4947cdc7SCole Faust				buf.WriteByte(':')
156*4947cdc7SCole Faust				if err := emit(item[1]); err != nil {
157*4947cdc7SCole Faust					return fmt.Errorf("in %s key %s: %v", x.Type(), item[0], err)
158*4947cdc7SCole Faust				}
159*4947cdc7SCole Faust			}
160*4947cdc7SCole Faust			buf.WriteByte('}')
161*4947cdc7SCole Faust
162*4947cdc7SCole Faust		case starlark.Iterable:
163*4947cdc7SCole Faust			// e.g. tuple, list
164*4947cdc7SCole Faust			buf.WriteByte('[')
165*4947cdc7SCole Faust			iter := x.Iterate()
166*4947cdc7SCole Faust			defer iter.Done()
167*4947cdc7SCole Faust			var elem starlark.Value
168*4947cdc7SCole Faust			for i := 0; iter.Next(&elem); i++ {
169*4947cdc7SCole Faust				if i > 0 {
170*4947cdc7SCole Faust					buf.WriteByte(',')
171*4947cdc7SCole Faust				}
172*4947cdc7SCole Faust				if err := emit(elem); err != nil {
173*4947cdc7SCole Faust					return fmt.Errorf("at %s index %d: %v", x.Type(), i, err)
174*4947cdc7SCole Faust				}
175*4947cdc7SCole Faust			}
176*4947cdc7SCole Faust			buf.WriteByte(']')
177*4947cdc7SCole Faust
178*4947cdc7SCole Faust		case starlark.HasAttrs:
179*4947cdc7SCole Faust			// e.g. struct
180*4947cdc7SCole Faust			buf.WriteByte('{')
181*4947cdc7SCole Faust			var names []string
182*4947cdc7SCole Faust			names = append(names, x.AttrNames()...)
183*4947cdc7SCole Faust			sort.Strings(names)
184*4947cdc7SCole Faust			for i, name := range names {
185*4947cdc7SCole Faust				v, err := x.Attr(name)
186*4947cdc7SCole Faust				if err != nil || v == nil {
187*4947cdc7SCole Faust					log.Fatalf("internal error: dir(%s) includes %q but value has no .%s field", x.Type(), name, name)
188*4947cdc7SCole Faust				}
189*4947cdc7SCole Faust				if i > 0 {
190*4947cdc7SCole Faust					buf.WriteByte(',')
191*4947cdc7SCole Faust				}
192*4947cdc7SCole Faust				quote(name)
193*4947cdc7SCole Faust				buf.WriteByte(':')
194*4947cdc7SCole Faust				if err := emit(v); err != nil {
195*4947cdc7SCole Faust					return fmt.Errorf("in field .%s: %v", name, err)
196*4947cdc7SCole Faust				}
197*4947cdc7SCole Faust			}
198*4947cdc7SCole Faust			buf.WriteByte('}')
199*4947cdc7SCole Faust
200*4947cdc7SCole Faust		default:
201*4947cdc7SCole Faust			return fmt.Errorf("cannot encode %s as JSON", x.Type())
202*4947cdc7SCole Faust		}
203*4947cdc7SCole Faust		return nil
204*4947cdc7SCole Faust	}
205*4947cdc7SCole Faust
206*4947cdc7SCole Faust	if err := emit(x); err != nil {
207*4947cdc7SCole Faust		return nil, fmt.Errorf("%s: %v", b.Name(), err)
208*4947cdc7SCole Faust	}
209*4947cdc7SCole Faust	return starlark.String(buf.String()), nil
210*4947cdc7SCole Faust}
211*4947cdc7SCole Faust
212*4947cdc7SCole Faust// isPrintableASCII reports whether s contains only printable ASCII.
213*4947cdc7SCole Faustfunc isPrintableASCII(s string) bool {
214*4947cdc7SCole Faust	for i := 0; i < len(s); i++ {
215*4947cdc7SCole Faust		b := s[i]
216*4947cdc7SCole Faust		if b < 0x20 || b >= 0x80 {
217*4947cdc7SCole Faust			return false
218*4947cdc7SCole Faust		}
219*4947cdc7SCole Faust	}
220*4947cdc7SCole Faust	return true
221*4947cdc7SCole Faust}
222*4947cdc7SCole Faust
223*4947cdc7SCole Faust// isFinite reports whether f represents a finite rational value.
224*4947cdc7SCole Faust// It is equivalent to !math.IsNan(f) && !math.IsInf(f, 0).
225*4947cdc7SCole Faustfunc isFinite(f float64) bool {
226*4947cdc7SCole Faust	return math.Abs(f) <= math.MaxFloat64
227*4947cdc7SCole Faust}
228*4947cdc7SCole Faust
229*4947cdc7SCole Faustfunc indent(thread *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
230*4947cdc7SCole Faust	prefix, indent := "", "\t" // keyword-only
231*4947cdc7SCole Faust	if err := starlark.UnpackArgs(b.Name(), nil, kwargs,
232*4947cdc7SCole Faust		"prefix?", &prefix,
233*4947cdc7SCole Faust		"indent?", &indent,
234*4947cdc7SCole Faust	); err != nil {
235*4947cdc7SCole Faust		return nil, err
236*4947cdc7SCole Faust	}
237*4947cdc7SCole Faust	var str string // positional-only
238*4947cdc7SCole Faust	if err := starlark.UnpackPositionalArgs(b.Name(), args, nil, 1, &str); err != nil {
239*4947cdc7SCole Faust		return nil, err
240*4947cdc7SCole Faust	}
241*4947cdc7SCole Faust
242*4947cdc7SCole Faust	buf := new(bytes.Buffer)
243*4947cdc7SCole Faust	if err := json.Indent(buf, []byte(str), prefix, indent); err != nil {
244*4947cdc7SCole Faust		return nil, fmt.Errorf("%s: %v", b.Name(), err)
245*4947cdc7SCole Faust	}
246*4947cdc7SCole Faust	return starlark.String(buf.String()), nil
247*4947cdc7SCole Faust}
248*4947cdc7SCole Faust
249*4947cdc7SCole Faustfunc decode(thread *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (_ starlark.Value, err error) {
250*4947cdc7SCole Faust	var s string
251*4947cdc7SCole Faust	if err := starlark.UnpackPositionalArgs(b.Name(), args, kwargs, 1, &s); err != nil {
252*4947cdc7SCole Faust		return nil, err
253*4947cdc7SCole Faust	}
254*4947cdc7SCole Faust
255*4947cdc7SCole Faust	// The decoder necessarily makes certain representation choices
256*4947cdc7SCole Faust	// such as list vs tuple, struct vs dict, int vs float.
257*4947cdc7SCole Faust	// In principle, we could parameterize it to allow the caller to
258*4947cdc7SCole Faust	// control the returned types, but there's no compelling need yet.
259*4947cdc7SCole Faust
260*4947cdc7SCole Faust	// Use panic/recover with a distinguished type (failure) for error handling.
261*4947cdc7SCole Faust	type failure string
262*4947cdc7SCole Faust	fail := func(format string, args ...interface{}) {
263*4947cdc7SCole Faust		panic(failure(fmt.Sprintf(format, args...)))
264*4947cdc7SCole Faust	}
265*4947cdc7SCole Faust
266*4947cdc7SCole Faust	i := 0
267*4947cdc7SCole Faust
268*4947cdc7SCole Faust	// skipSpace consumes leading spaces, and reports whether there is more input.
269*4947cdc7SCole Faust	skipSpace := func() bool {
270*4947cdc7SCole Faust		for ; i < len(s); i++ {
271*4947cdc7SCole Faust			b := s[i]
272*4947cdc7SCole Faust			if b != ' ' && b != '\t' && b != '\n' && b != '\r' {
273*4947cdc7SCole Faust				return true
274*4947cdc7SCole Faust			}
275*4947cdc7SCole Faust		}
276*4947cdc7SCole Faust		return false
277*4947cdc7SCole Faust	}
278*4947cdc7SCole Faust
279*4947cdc7SCole Faust	// next consumes leading spaces and returns the first non-space.
280*4947cdc7SCole Faust	// It panics if at EOF.
281*4947cdc7SCole Faust	next := func() byte {
282*4947cdc7SCole Faust		if skipSpace() {
283*4947cdc7SCole Faust			return s[i]
284*4947cdc7SCole Faust		}
285*4947cdc7SCole Faust		fail("unexpected end of file")
286*4947cdc7SCole Faust		panic("unreachable")
287*4947cdc7SCole Faust	}
288*4947cdc7SCole Faust
289*4947cdc7SCole Faust	// parse returns the next JSON value from the input.
290*4947cdc7SCole Faust	// It consumes leading but not trailing whitespace.
291*4947cdc7SCole Faust	// It panics on error.
292*4947cdc7SCole Faust	var parse func() starlark.Value
293*4947cdc7SCole Faust	parse = func() starlark.Value {
294*4947cdc7SCole Faust		b := next()
295*4947cdc7SCole Faust		switch b {
296*4947cdc7SCole Faust		case '"':
297*4947cdc7SCole Faust			// string
298*4947cdc7SCole Faust
299*4947cdc7SCole Faust			// Find end of quotation.
300*4947cdc7SCole Faust			// Also, record whether trivial unquoting is safe.
301*4947cdc7SCole Faust			// Non-trivial unquoting is handled by Go's encoding/json.
302*4947cdc7SCole Faust			safe := true
303*4947cdc7SCole Faust			closed := false
304*4947cdc7SCole Faust			j := i + 1
305*4947cdc7SCole Faust			for ; j < len(s); j++ {
306*4947cdc7SCole Faust				b := s[j]
307*4947cdc7SCole Faust				if b == '\\' {
308*4947cdc7SCole Faust					safe = false
309*4947cdc7SCole Faust					j++ // skip x in \x
310*4947cdc7SCole Faust				} else if b == '"' {
311*4947cdc7SCole Faust					closed = true
312*4947cdc7SCole Faust					j++ // skip '"'
313*4947cdc7SCole Faust					break
314*4947cdc7SCole Faust				} else if b >= utf8.RuneSelf {
315*4947cdc7SCole Faust					safe = false
316*4947cdc7SCole Faust				}
317*4947cdc7SCole Faust			}
318*4947cdc7SCole Faust			if !closed {
319*4947cdc7SCole Faust				fail("unclosed string literal")
320*4947cdc7SCole Faust			}
321*4947cdc7SCole Faust
322*4947cdc7SCole Faust			r := s[i:j]
323*4947cdc7SCole Faust			i = j
324*4947cdc7SCole Faust
325*4947cdc7SCole Faust			// unquote
326*4947cdc7SCole Faust			if safe {
327*4947cdc7SCole Faust				r = r[1 : len(r)-1]
328*4947cdc7SCole Faust			} else if err := json.Unmarshal([]byte(r), &r); err != nil {
329*4947cdc7SCole Faust				fail("%s", err)
330*4947cdc7SCole Faust			}
331*4947cdc7SCole Faust			return starlark.String(r)
332*4947cdc7SCole Faust
333*4947cdc7SCole Faust		case 'n':
334*4947cdc7SCole Faust			if strings.HasPrefix(s[i:], "null") {
335*4947cdc7SCole Faust				i += len("null")
336*4947cdc7SCole Faust				return starlark.None
337*4947cdc7SCole Faust			}
338*4947cdc7SCole Faust
339*4947cdc7SCole Faust		case 't':
340*4947cdc7SCole Faust			if strings.HasPrefix(s[i:], "true") {
341*4947cdc7SCole Faust				i += len("true")
342*4947cdc7SCole Faust				return starlark.True
343*4947cdc7SCole Faust			}
344*4947cdc7SCole Faust
345*4947cdc7SCole Faust		case 'f':
346*4947cdc7SCole Faust			if strings.HasPrefix(s[i:], "false") {
347*4947cdc7SCole Faust				i += len("false")
348*4947cdc7SCole Faust				return starlark.False
349*4947cdc7SCole Faust			}
350*4947cdc7SCole Faust
351*4947cdc7SCole Faust		case '[':
352*4947cdc7SCole Faust			// array
353*4947cdc7SCole Faust			var elems []starlark.Value
354*4947cdc7SCole Faust
355*4947cdc7SCole Faust			i++ // '['
356*4947cdc7SCole Faust			b = next()
357*4947cdc7SCole Faust			if b != ']' {
358*4947cdc7SCole Faust				for {
359*4947cdc7SCole Faust					elem := parse()
360*4947cdc7SCole Faust					elems = append(elems, elem)
361*4947cdc7SCole Faust					b = next()
362*4947cdc7SCole Faust					if b != ',' {
363*4947cdc7SCole Faust						if b != ']' {
364*4947cdc7SCole Faust							fail("got %q, want ',' or ']'", b)
365*4947cdc7SCole Faust						}
366*4947cdc7SCole Faust						break
367*4947cdc7SCole Faust					}
368*4947cdc7SCole Faust					i++ // ','
369*4947cdc7SCole Faust				}
370*4947cdc7SCole Faust			}
371*4947cdc7SCole Faust			i++ // ']'
372*4947cdc7SCole Faust			return starlark.NewList(elems)
373*4947cdc7SCole Faust
374*4947cdc7SCole Faust		case '{':
375*4947cdc7SCole Faust			// object
376*4947cdc7SCole Faust			dict := new(starlark.Dict)
377*4947cdc7SCole Faust
378*4947cdc7SCole Faust			i++ // '{'
379*4947cdc7SCole Faust			b = next()
380*4947cdc7SCole Faust			if b != '}' {
381*4947cdc7SCole Faust				for {
382*4947cdc7SCole Faust					key := parse()
383*4947cdc7SCole Faust					if _, ok := key.(starlark.String); !ok {
384*4947cdc7SCole Faust						fail("got %s for object key, want string", key.Type())
385*4947cdc7SCole Faust					}
386*4947cdc7SCole Faust					b = next()
387*4947cdc7SCole Faust					if b != ':' {
388*4947cdc7SCole Faust						fail("after object key, got %q, want ':' ", b)
389*4947cdc7SCole Faust					}
390*4947cdc7SCole Faust					i++ // ':'
391*4947cdc7SCole Faust					value := parse()
392*4947cdc7SCole Faust					dict.SetKey(key, value) // can't fail
393*4947cdc7SCole Faust					b = next()
394*4947cdc7SCole Faust					if b != ',' {
395*4947cdc7SCole Faust						if b != '}' {
396*4947cdc7SCole Faust							fail("in object, got %q, want ',' or '}'", b)
397*4947cdc7SCole Faust						}
398*4947cdc7SCole Faust						break
399*4947cdc7SCole Faust					}
400*4947cdc7SCole Faust					i++ // ','
401*4947cdc7SCole Faust				}
402*4947cdc7SCole Faust			}
403*4947cdc7SCole Faust			i++ // '}'
404*4947cdc7SCole Faust			return dict
405*4947cdc7SCole Faust
406*4947cdc7SCole Faust		default:
407*4947cdc7SCole Faust			// number?
408*4947cdc7SCole Faust			if isdigit(b) || b == '-' {
409*4947cdc7SCole Faust				// scan literal. Allow [0-9+-eE.] for now.
410*4947cdc7SCole Faust				float := false
411*4947cdc7SCole Faust				var j int
412*4947cdc7SCole Faust				for j = i + 1; j < len(s); j++ {
413*4947cdc7SCole Faust					b = s[j]
414*4947cdc7SCole Faust					if isdigit(b) {
415*4947cdc7SCole Faust						// ok
416*4947cdc7SCole Faust					} else if b == '.' ||
417*4947cdc7SCole Faust						b == 'e' ||
418*4947cdc7SCole Faust						b == 'E' ||
419*4947cdc7SCole Faust						b == '+' ||
420*4947cdc7SCole Faust						b == '-' {
421*4947cdc7SCole Faust						float = true
422*4947cdc7SCole Faust					} else {
423*4947cdc7SCole Faust						break
424*4947cdc7SCole Faust					}
425*4947cdc7SCole Faust				}
426*4947cdc7SCole Faust				num := s[i:j]
427*4947cdc7SCole Faust				i = j
428*4947cdc7SCole Faust
429*4947cdc7SCole Faust				// Unlike most C-like languages,
430*4947cdc7SCole Faust				// JSON disallows a leading zero before a digit.
431*4947cdc7SCole Faust				digits := num
432*4947cdc7SCole Faust				if num[0] == '-' {
433*4947cdc7SCole Faust					digits = num[1:]
434*4947cdc7SCole Faust				}
435*4947cdc7SCole Faust				if digits == "" || digits[0] == '0' && len(digits) > 1 && isdigit(digits[1]) {
436*4947cdc7SCole Faust					fail("invalid number: %s", num)
437*4947cdc7SCole Faust				}
438*4947cdc7SCole Faust
439*4947cdc7SCole Faust				// parse literal
440*4947cdc7SCole Faust				if float {
441*4947cdc7SCole Faust					x, err := strconv.ParseFloat(num, 64)
442*4947cdc7SCole Faust					if err != nil {
443*4947cdc7SCole Faust						fail("invalid number: %s", num)
444*4947cdc7SCole Faust					}
445*4947cdc7SCole Faust					return starlark.Float(x)
446*4947cdc7SCole Faust				} else {
447*4947cdc7SCole Faust					x, ok := new(big.Int).SetString(num, 10)
448*4947cdc7SCole Faust					if !ok {
449*4947cdc7SCole Faust						fail("invalid number: %s", num)
450*4947cdc7SCole Faust					}
451*4947cdc7SCole Faust					return starlark.MakeBigInt(x)
452*4947cdc7SCole Faust				}
453*4947cdc7SCole Faust			}
454*4947cdc7SCole Faust		}
455*4947cdc7SCole Faust		fail("unexpected character %q", b)
456*4947cdc7SCole Faust		panic("unreachable")
457*4947cdc7SCole Faust	}
458*4947cdc7SCole Faust	defer func() {
459*4947cdc7SCole Faust		x := recover()
460*4947cdc7SCole Faust		switch x := x.(type) {
461*4947cdc7SCole Faust		case failure:
462*4947cdc7SCole Faust			err = fmt.Errorf("json.decode: at offset %d, %s", i, x)
463*4947cdc7SCole Faust		case nil:
464*4947cdc7SCole Faust			// nop
465*4947cdc7SCole Faust		default:
466*4947cdc7SCole Faust			panic(x) // unexpected panic
467*4947cdc7SCole Faust		}
468*4947cdc7SCole Faust	}()
469*4947cdc7SCole Faust	x := parse()
470*4947cdc7SCole Faust	if skipSpace() {
471*4947cdc7SCole Faust		fail("unexpected character %q after value", s[i])
472*4947cdc7SCole Faust	}
473*4947cdc7SCole Faust	return x, nil
474*4947cdc7SCole Faust}
475*4947cdc7SCole Faust
476*4947cdc7SCole Faustfunc isdigit(b byte) bool {
477*4947cdc7SCole Faust	return b >= '0' && b <= '9'
478*4947cdc7SCole Faust}
479