1package tengo
2
3import (
4	"errors"
5	"fmt"
6	"strconv"
7	"time"
8)
9
10var (
11	// MaxStringLen is the maximum byte-length for string value. Note this
12	// limit applies to all compiler/VM instances in the process.
13	MaxStringLen = 2147483647
14
15	// MaxBytesLen is the maximum length for bytes value. Note this limit
16	// applies to all compiler/VM instances in the process.
17	MaxBytesLen = 2147483647
18)
19
20const (
21	// GlobalsSize is the maximum number of global variables for a VM.
22	GlobalsSize = 1024
23
24	// StackSize is the maximum stack size for a VM.
25	StackSize = 2048
26
27	// MaxFrames is the maximum number of function frames for a VM.
28	MaxFrames = 1024
29)
30
31// CallableFunc is a function signature for the callable functions.
32type CallableFunc = func(args ...Object) (ret Object, err error)
33
34// CountObjects returns the number of objects that a given object o contains.
35// For scalar value types, it will always be 1. For compound value types,
36// this will include its elements and all of their elements recursively.
37func CountObjects(o Object) (c int) {
38	c = 1
39	switch o := o.(type) {
40	case *Array:
41		for _, v := range o.Value {
42			c += CountObjects(v)
43		}
44	case *ImmutableArray:
45		for _, v := range o.Value {
46			c += CountObjects(v)
47		}
48	case *Map:
49		for _, v := range o.Value {
50			c += CountObjects(v)
51		}
52	case *ImmutableMap:
53		for _, v := range o.Value {
54			c += CountObjects(v)
55		}
56	case *Error:
57		c += CountObjects(o.Value)
58	}
59	return
60}
61
62// ToString will try to convert object o to string value.
63func ToString(o Object) (v string, ok bool) {
64	if o == UndefinedValue {
65		return
66	}
67	ok = true
68	if str, isStr := o.(*String); isStr {
69		v = str.Value
70	} else {
71		v = o.String()
72	}
73	return
74}
75
76// ToInt will try to convert object o to int value.
77func ToInt(o Object) (v int, ok bool) {
78	switch o := o.(type) {
79	case *Int:
80		v = int(o.Value)
81		ok = true
82	case *Float:
83		v = int(o.Value)
84		ok = true
85	case *Char:
86		v = int(o.Value)
87		ok = true
88	case *Bool:
89		if o == TrueValue {
90			v = 1
91		}
92		ok = true
93	case *String:
94		c, err := strconv.ParseInt(o.Value, 10, 64)
95		if err == nil {
96			v = int(c)
97			ok = true
98		}
99	}
100	return
101}
102
103// ToInt64 will try to convert object o to int64 value.
104func ToInt64(o Object) (v int64, ok bool) {
105	switch o := o.(type) {
106	case *Int:
107		v = o.Value
108		ok = true
109	case *Float:
110		v = int64(o.Value)
111		ok = true
112	case *Char:
113		v = int64(o.Value)
114		ok = true
115	case *Bool:
116		if o == TrueValue {
117			v = 1
118		}
119		ok = true
120	case *String:
121		c, err := strconv.ParseInt(o.Value, 10, 64)
122		if err == nil {
123			v = c
124			ok = true
125		}
126	}
127	return
128}
129
130// ToFloat64 will try to convert object o to float64 value.
131func ToFloat64(o Object) (v float64, ok bool) {
132	switch o := o.(type) {
133	case *Int:
134		v = float64(o.Value)
135		ok = true
136	case *Float:
137		v = o.Value
138		ok = true
139	case *String:
140		c, err := strconv.ParseFloat(o.Value, 64)
141		if err == nil {
142			v = c
143			ok = true
144		}
145	}
146	return
147}
148
149// ToBool will try to convert object o to bool value.
150func ToBool(o Object) (v bool, ok bool) {
151	ok = true
152	v = !o.IsFalsy()
153	return
154}
155
156// ToRune will try to convert object o to rune value.
157func ToRune(o Object) (v rune, ok bool) {
158	switch o := o.(type) {
159	case *Int:
160		v = rune(o.Value)
161		ok = true
162	case *Char:
163		v = o.Value
164		ok = true
165	}
166	return
167}
168
169// ToByteSlice will try to convert object o to []byte value.
170func ToByteSlice(o Object) (v []byte, ok bool) {
171	switch o := o.(type) {
172	case *Bytes:
173		v = o.Value
174		ok = true
175	case *String:
176		v = []byte(o.Value)
177		ok = true
178	}
179	return
180}
181
182// ToTime will try to convert object o to time.Time value.
183func ToTime(o Object) (v time.Time, ok bool) {
184	switch o := o.(type) {
185	case *Time:
186		v = o.Value
187		ok = true
188	case *Int:
189		v = time.Unix(o.Value, 0)
190		ok = true
191	}
192	return
193}
194
195// ToInterface attempts to convert an object o to an interface{} value
196func ToInterface(o Object) (res interface{}) {
197	switch o := o.(type) {
198	case *Int:
199		res = o.Value
200	case *String:
201		res = o.Value
202	case *Float:
203		res = o.Value
204	case *Bool:
205		res = o == TrueValue
206	case *Char:
207		res = o.Value
208	case *Bytes:
209		res = o.Value
210	case *Array:
211		res = make([]interface{}, len(o.Value))
212		for i, val := range o.Value {
213			res.([]interface{})[i] = ToInterface(val)
214		}
215	case *ImmutableArray:
216		res = make([]interface{}, len(o.Value))
217		for i, val := range o.Value {
218			res.([]interface{})[i] = ToInterface(val)
219		}
220	case *Map:
221		res = make(map[string]interface{})
222		for key, v := range o.Value {
223			res.(map[string]interface{})[key] = ToInterface(v)
224		}
225	case *ImmutableMap:
226		res = make(map[string]interface{})
227		for key, v := range o.Value {
228			res.(map[string]interface{})[key] = ToInterface(v)
229		}
230	case *Time:
231		res = o.Value
232	case *Error:
233		res = errors.New(o.String())
234	case *Undefined:
235		res = nil
236	case Object:
237		return o
238	}
239	return
240}
241
242// FromInterface will attempt to convert an interface{} v to a Tengo Object
243func FromInterface(v interface{}) (Object, error) {
244	switch v := v.(type) {
245	case nil:
246		return UndefinedValue, nil
247	case string:
248		if len(v) > MaxStringLen {
249			return nil, ErrStringLimit
250		}
251		return &String{Value: v}, nil
252	case int64:
253		return &Int{Value: v}, nil
254	case int:
255		return &Int{Value: int64(v)}, nil
256	case bool:
257		if v {
258			return TrueValue, nil
259		}
260		return FalseValue, nil
261	case rune:
262		return &Char{Value: v}, nil
263	case byte:
264		return &Char{Value: rune(v)}, nil
265	case float64:
266		return &Float{Value: v}, nil
267	case []byte:
268		if len(v) > MaxBytesLen {
269			return nil, ErrBytesLimit
270		}
271		return &Bytes{Value: v}, nil
272	case error:
273		return &Error{Value: &String{Value: v.Error()}}, nil
274	case map[string]Object:
275		return &Map{Value: v}, nil
276	case map[string]interface{}:
277		kv := make(map[string]Object)
278		for vk, vv := range v {
279			vo, err := FromInterface(vv)
280			if err != nil {
281				return nil, err
282			}
283			kv[vk] = vo
284		}
285		return &Map{Value: kv}, nil
286	case []Object:
287		return &Array{Value: v}, nil
288	case []interface{}:
289		arr := make([]Object, len(v))
290		for i, e := range v {
291			vo, err := FromInterface(e)
292			if err != nil {
293				return nil, err
294			}
295			arr[i] = vo
296		}
297		return &Array{Value: arr}, nil
298	case time.Time:
299		return &Time{Value: v}, nil
300	case Object:
301		return v, nil
302	case CallableFunc:
303		return &UserFunction{Value: v}, nil
304	}
305	return nil, fmt.Errorf("cannot convert to object: %T", v)
306}
307