1// ================================================================
2// Constructors
3// ================================================================
4
5package types
6
7import (
8	"errors"
9
10	"miller/src/lib"
11)
12
13// ----------------------------------------------------------------
14// TODO: comment how these are part of the copy-reduction project.
15
16var value_MLRVAL_ERROR = mlrvalFromError()
17var value_MLRVAL_ABSENT = mlrvalFromAbsent()
18var value_MLRVAL_VOID = mlrvalFromVoid()
19var value_MLRVAL_TRUE = mlrvalFromTrue()
20var value_MLRVAL_FALSE = mlrvalFromFalse()
21
22var MLRVAL_ERROR = &value_MLRVAL_ERROR
23var MLRVAL_ABSENT = &value_MLRVAL_ABSENT
24var MLRVAL_VOID = &value_MLRVAL_VOID
25var MLRVAL_TRUE = &value_MLRVAL_TRUE
26var MLRVAL_FALSE = &value_MLRVAL_FALSE
27
28// ----------------------------------------------------------------
29func MlrvalPointerFromString(input string) *Mlrval {
30	if input == "" {
31		return MLRVAL_VOID
32	}
33	var this Mlrval
34	this.mvtype = MT_STRING
35	this.printrep = input
36	this.printrepValid = true
37	return &this
38}
39
40// xxx comment why two -- one for from parsed user data; other for from math ops
41func MlrvalPointerFromIntString(input string) *Mlrval {
42	ival, ok := lib.TryIntFromString(input)
43	// xxx comment assummption is input-string already deemed parseable so no error return
44	lib.InternalCodingErrorIf(!ok)
45	var this Mlrval
46	this.mvtype = MT_INT
47	this.printrep = input
48	this.printrepValid = true
49	this.intval = ival
50	return &this
51}
52
53func MlrvalPointerFromInt(input int) *Mlrval {
54	var this Mlrval
55	this.mvtype = MT_INT
56	this.printrepValid = false
57	this.intval = input
58	return &this
59}
60
61// xxx comment why two -- one for from parsed user data; other for from math ops
62// xxx comment assummption is input-string already deemed parseable so no error return
63func MlrvalPointerFromFloat64String(input string) *Mlrval {
64	fval, ok := lib.TryFloat64FromString(input)
65	// xxx comment assummption is input-string already deemed parseable so no error return
66	lib.InternalCodingErrorIf(!ok)
67	var this Mlrval
68	this.mvtype = MT_FLOAT
69	this.printrep = input
70	this.printrepValid = true
71	this.floatval = fval
72	return &this
73}
74
75func MlrvalPointerFromFloat64(input float64) *Mlrval {
76	var this Mlrval
77	this.mvtype = MT_FLOAT
78	this.printrepValid = false
79	this.floatval = input
80	return &this
81}
82
83func MlrvalPointerFromBool(input bool) *Mlrval {
84	if input == true {
85		return MLRVAL_TRUE
86	} else {
87		return MLRVAL_FALSE
88	}
89}
90
91func MlrvalPointerFromBoolString(input string) *Mlrval {
92	if input == "true" {
93		return MLRVAL_TRUE
94	} else if input == "false" {
95		return MLRVAL_FALSE
96	} else {
97		lib.InternalCodingErrorIf(true)
98		return MLRVAL_ERROR // not reached
99	}
100}
101
102func MlrvalPointerFromInferredType(input string) *Mlrval {
103	// xxx the parsing has happened so stash it ...
104	// xxx emphasize the invariant that a non-invalid printrep always
105	// matches the nval ...
106	if input == "" {
107		return MLRVAL_VOID
108	}
109
110	_, iok := lib.TryIntFromString(input)
111	if iok {
112		return MlrvalPointerFromIntString(input)
113	}
114
115	_, fok := lib.TryFloat64FromString(input)
116	if fok {
117		return MlrvalPointerFromFloat64String(input)
118	}
119
120	_, bok := lib.TryBoolFromBoolString(input)
121	if bok {
122		return MlrvalPointerFromBoolString(input)
123	}
124
125	return MlrvalPointerFromString(input)
126}
127
128func MlrvalPointerFromEmptyMap() *Mlrval {
129	var this Mlrval
130	this.mvtype = MT_MAP
131	this.printrepValid = false
132	this.mapval = NewMlrmap()
133	return &this
134}
135
136// ----------------------------------------------------------------
137// Does not copy the data. We can make a SetFromArrayLiteralCopy if needed
138// using values.CopyMlrvalArray().
139func MlrvalPointerFromArrayLiteralReference(input []Mlrval) *Mlrval {
140	var this Mlrval
141	this.mvtype = MT_ARRAY
142	this.printrepValid = false
143	this.arrayval = input
144	return &this
145}
146
147func MlrvalPointerFromMap(that *Mlrmap) *Mlrval {
148	this := MlrvalPointerFromEmptyMap()
149	if that == nil {
150		// TODO maybe return 2nd-arg error in the API
151		return MLRVAL_ERROR
152	}
153
154	for pe := that.Head; pe != nil; pe = pe.Next {
155		this.mapval.PutCopy(pe.Key, pe.Value)
156	}
157	return this
158}
159
160// Like previous but doesn't copy. Only safe when the argument's sole purpose
161// is to be passed into here.
162func MlrvalPointerFromMapReferenced(that *Mlrmap) *Mlrval {
163	this := MlrvalPointerFromEmptyMap()
164	if that == nil {
165		// xxx maybe return 2nd-arg error in the API
166		return MLRVAL_ERROR
167	}
168
169	for pe := that.Head; pe != nil; pe = pe.Next {
170		this.mapval.PutReference(pe.Key, pe.Value)
171	}
172	return this
173}
174
175// TODO: comment not MLRVAL_PENDING constants since this intended to be mutated
176// by the JSON parser.
177func MlrvalPointerFromPending() *Mlrval {
178	var this Mlrval
179	this.mvtype = MT_PENDING
180	this.printrepValid = false
181	return &this
182}
183
184// ----------------------------------------------------------------
185// TODO: comment about being designed to be mutated for JSON API.
186func MlrvalFromPending() Mlrval {
187	return Mlrval{
188		mvtype:        MT_PENDING,
189		printrep:      "(bug-if-you-see-this-pending-type)",
190		printrepValid: true,
191		intval:        0,
192		floatval:      0.0,
193		boolval:       false,
194		arrayval:      nil,
195		mapval:        nil,
196	}
197}
198
199func mlrvalFromError() Mlrval {
200	return Mlrval{
201		mvtype:        MT_ERROR,
202		printrep:      "(error)", // xxx const somewhere
203		printrepValid: true,
204		intval:        0,
205		floatval:      0.0,
206		boolval:       false,
207		arrayval:      nil,
208		mapval:        nil,
209	}
210}
211
212func mlrvalFromAbsent() Mlrval {
213	return Mlrval{
214		mvtype:        MT_ABSENT,
215		printrep:      "(absent)",
216		printrepValid: true,
217		intval:        0,
218		floatval:      0.0,
219		boolval:       false,
220		arrayval:      nil,
221		mapval:        nil,
222	}
223}
224
225func mlrvalFromVoid() Mlrval {
226	return Mlrval{
227		mvtype:        MT_VOID,
228		printrep:      "",
229		printrepValid: true,
230		intval:        0,
231		floatval:      0.0,
232		boolval:       false,
233		arrayval:      nil,
234		mapval:        nil,
235	}
236}
237
238func MlrvalFromString(input string) Mlrval {
239	if input == "" {
240		return mlrvalFromVoid()
241	} else {
242		return Mlrval{
243			mvtype:        MT_STRING,
244			printrep:      input,
245			printrepValid: true,
246			intval:        0,
247			floatval:      0.0,
248			boolval:       false,
249			arrayval:      nil,
250			mapval:        nil,
251		}
252	}
253}
254
255func MlrvalFromInt(input int) Mlrval {
256	return Mlrval{
257		mvtype:        MT_INT,
258		printrep:      "(bug-if-you-see-this-int-type)",
259		printrepValid: false,
260		intval:        input,
261		floatval:      0.0,
262		boolval:       false,
263		arrayval:      nil,
264		mapval:        nil,
265	}
266}
267
268func MlrvalFromFloat64(input float64) Mlrval {
269	return Mlrval{
270		mvtype:        MT_FLOAT,
271		printrep:      "(bug-if-you-see-this-float-type)",
272		printrepValid: false,
273		intval:        0,
274		floatval:      input,
275		boolval:       false,
276		arrayval:      nil,
277		mapval:        nil,
278	}
279}
280
281func mlrvalFromTrue() Mlrval {
282	return Mlrval{
283		mvtype:        MT_BOOL,
284		printrep:      "true",
285		printrepValid: true,
286		intval:        0,
287		floatval:      0.0,
288		boolval:       true,
289		arrayval:      nil,
290		mapval:        nil,
291	}
292}
293
294func mlrvalFromFalse() Mlrval {
295	return Mlrval{
296		mvtype:        MT_BOOL,
297		printrep:      "false",
298		printrepValid: true,
299		intval:        0,
300		floatval:      0.0,
301		boolval:       false,
302		arrayval:      nil,
303		mapval:        nil,
304	}
305}
306
307func MlrvalFromBool(input bool) Mlrval {
308	if input == true {
309		return mlrvalFromTrue()
310	} else {
311		return mlrvalFromFalse()
312	}
313}
314
315func MlrvalFromBoolString(input string) Mlrval {
316	if input == "true" {
317		return mlrvalFromTrue()
318	} else {
319		return mlrvalFromFalse()
320	}
321	// else panic
322}
323
324// ----------------------------------------------------------------
325// Does not copy the data. We can make a MlrvalFromArrayLiteralCopy if needed,
326// using values.CopyMlrvalArray().
327
328func MlrvalEmptyArray() Mlrval {
329	return Mlrval{
330		mvtype:        MT_ARRAY,
331		printrep:      "(bug-if-you-see-this-array-type)",
332		printrepValid: false,
333		intval:        0,
334		floatval:      0.0,
335		boolval:       false,
336		arrayval:      make([]Mlrval, 0, 10),
337		mapval:        nil,
338	}
339}
340
341// Users can do things like '$new[1][2][3] = 4' even if '$new' isn't already
342// allocated. This function supports that.
343func NewSizedMlrvalArray(length int) *Mlrval {
344	arrayval := make([]Mlrval, length, 2*length)
345
346	for i := 0; i < int(length); i++ {
347		arrayval[i] = MlrvalFromString("")
348	}
349
350	return &Mlrval{
351		mvtype:        MT_ARRAY,
352		printrep:      "(bug-if-you-see-this-array-type)",
353		printrepValid: false,
354		intval:        0,
355		floatval:      0.0,
356		boolval:       false,
357		arrayval:      arrayval,
358		mapval:        nil,
359	}
360}
361
362func LengthenMlrvalArray(array *[]Mlrval, newLength64 int) {
363	newLength := int(newLength64)
364	lib.InternalCodingErrorIf(newLength <= len(*array))
365
366	if newLength <= cap(*array) {
367		newArray := (*array)[:newLength]
368		for zindex := len(*array); zindex < newLength; zindex++ {
369			newArray[zindex] = MlrvalFromString("")
370		}
371		*array = newArray
372	} else {
373		newArray := make([]Mlrval, newLength, 2*newLength)
374		zindex := 0
375		for zindex = 0; zindex < len(*array); zindex++ {
376			newArray[zindex] = (*array)[zindex]
377		}
378		for zindex = len(*array); zindex < newLength; zindex++ {
379			newArray[zindex] = MlrvalFromString("")
380		}
381		*array = newArray
382	}
383}
384
385// ----------------------------------------------------------------
386func MlrvalEmptyMap() Mlrval {
387	return Mlrval{
388		mvtype:        MT_MAP,
389		printrep:      "(bug-if-you-see-this-map-type)",
390		printrepValid: false,
391		intval:        0,
392		floatval:      0.0,
393		boolval:       false,
394		arrayval:      nil,
395		mapval:        NewMlrmap(),
396	}
397}
398
399// Like previous but doesn't copy. Only safe when the argument's sole purpose
400// is to be passed into here.
401func MlrvalFromMapReferenced(that *Mlrmap) Mlrval {
402	this := MlrvalEmptyMap()
403	if that == nil {
404		// xxx maybe return 2nd-arg error in the API
405		return *MLRVAL_ERROR
406	}
407
408	for pe := that.Head; pe != nil; pe = pe.Next {
409		this.mapval.PutReference(pe.Key, pe.Value)
410	}
411
412	return this
413}
414
415// ----------------------------------------------------------------
416// This is for auto-deepen of nested maps in things like
417//
418//   $foo[1]["a"][2]["b"] = 3
419//
420// Autocreated levels are maps.  Array levels can be explicitly created e.g.
421//
422//   $foo[1]["a"] ??= []
423//   $foo[1]["a"][2]["b"] = 3
424
425func NewMlrvalForAutoDeepen(mvtype MVType) (*Mlrval, error) {
426	if mvtype == MT_STRING || mvtype == MT_INT {
427		empty := MlrvalEmptyMap()
428		return &empty, nil
429	} else {
430		return nil, errors.New(
431			"Miller: indices must be string or int; got " + GetTypeName(mvtype),
432		)
433	}
434}
435