1// ================================================================
2// The `types.Mlrval` structure includes **string, int, float, boolean, void,
3// absent, and error** types (not unlike PHP's `zval`) as well as
4// type-conversion logic for various operators.
5//
6// Whenever I say "int" and "float" with regard to mlrvals I always mean
7// "int" and "float64". If I ever miss a spot and use Go int/float types then
8// that is a bug. It would be great to be able to somehow lint for this.
9// ================================================================
10
11package types
12
13type Mlrval struct {
14
15	// Enumeration for string / int / float / boolean / etc.
16	// I would call this "type" not "mvtype" but "type" is a keyword in Go.
17	mvtype MVType
18
19	// An int/float always starts from a string -- be it from record data from
20	// a file, or a literal within a DSL expression. The printrep is exactly
21	// that string, however the user formatted it, and the intval/floatval is
22	// computed from that -- and in sync with it -- at construction time.
23	//
24	// When a mlrval is computed from one or more others -- e.g. '$z = $x + 4'
25	// -- the printrep is not updated. That would be wasted CPU, since the
26	// string representation is not needed until when/if the value is printed
27	// as output. For computation methods the printrep is neglected and the
28	// printrepValid is set to false.
29	//
30	// In the String() method the printrep is computed from the intval/floatval
31	// and printrepValid is set back to true.
32	//
33	// Note that for MT_STRING, the printrep is always valid since it is the
34	// only payload for the mlrval.
35	//
36	// Thus we (a) keep user-specific input-formatting when possible, for the
37	// principle of least surprise; (b) avoid reformatting strings during
38	// intermediate arithmetic expressions; (c) resync arithmetic results to
39	// string formatting on a just-in-time basis when output is printed.
40	printrep      string
41	printrepValid bool
42	intval        int
43	floatval      float64
44	boolval       bool
45	arrayval      []Mlrval
46	mapval        *Mlrmap
47}
48
49// Enumeration for mlrval types
50//
51// There are two kinds of null: ABSENT (key not present in a record) and VOID
52// (key present with empty value).  Note void is an acceptable string (empty
53// string) but not an acceptable number. (In Javascript, similarly, there are
54// undefined and null, respectively.)
55
56type MVType int
57
58// Important: the values of these enums are used to index into disposition
59// matrices. If they are changed, it will break the disposition matrices, or
60// they will all need manual re-indexing.
61const (
62	// Type not yet determined, during JSON decode.
63	MT_PENDING MVType = -1
64
65	// E.g. error encountered in one eval & it propagates up the AST at
66	// evaluation time.  Various runtime errors, such as file-not-found, result
67	// in a message to stderr and os.Exit(1). But errors in user-provided data
68	// are intended to result in "(error)"-valued output rather than a crash.
69	// This is analogous to the way that IEEE-754 arithmetic carries around
70	// Inf and NaN through computation chains.
71	MT_ERROR = 0
72
73	// Key not present in input record, e.g. 'foo = $nosuchkey'
74	MT_ABSENT = 1
75
76	// Key present in input record with empty value, e.g. input data '$x=,$y=2'
77	MT_VOID = 2
78
79	MT_STRING = 3
80
81	MT_INT = 4
82
83	MT_FLOAT = 5
84
85	MT_BOOL = 6
86
87	MT_ARRAY = 7
88
89	MT_MAP = 8
90
91	// Not a type -- this is a dimension for disposition vectors and
92	// disposition matrices. For example, when we want to add two mlrvals,
93	// instead of if/elsing or switching on the types of both operands, we
94	// instead jump directly to a type-specific function in a matrix of
95	// function pointers which is MT_DIM x MT_DIM.
96	MT_DIM = 9
97)
98
99var TYPE_NAMES = [MT_DIM]string{
100	"error",
101	"absent",
102	"empty", // For backward compatiblity with the C impl: this is user-visible
103	"string",
104	"int",
105	"float",
106	"bool",
107	"array",
108	"map",
109}
110
111// ----------------------------------------------------------------
112// For typed assignments in the DSL
113
114// TODO: comment more re typedecls
115const MT_TYPE_MASK_STRING = (1 << MT_STRING) | (1 << MT_VOID)
116const MT_TYPE_MASK_INT = 1 << MT_INT
117const MT_TYPE_MASK_FLOAT = 1 << MT_FLOAT
118const MT_TYPE_MASK_NUM = (1 << MT_INT) | (1 << MT_FLOAT)
119const MT_TYPE_MASK_BOOL = 1 << MT_BOOL
120const MT_TYPE_MASK_ARRAY = 1 << MT_ARRAY
121const MT_TYPE_MASK_MAP = 1 << MT_MAP
122const MT_TYPE_MASK_VAR = (1 << MT_VOID) | (1 << MT_STRING) | (1 << MT_INT) |
123	(1 << MT_FLOAT) | (1 << MT_BOOL) | (1 << MT_ARRAY) | (1 << MT_MAP)
124
125// Not exposed in userspace
126const MT_TYPE_MASK_ANY = (1 << MT_ERROR) | (1 << MT_ABSENT) | MT_TYPE_MASK_VAR
127
128var typeNameToMaskMap = map[string]int{
129	"var":   MT_TYPE_MASK_VAR,
130	"str":   MT_TYPE_MASK_STRING,
131	"int":   MT_TYPE_MASK_INT,
132	"float": MT_TYPE_MASK_FLOAT,
133	"num":   MT_TYPE_MASK_NUM,
134	"bool":  MT_TYPE_MASK_BOOL,
135	"arr":   MT_TYPE_MASK_ARRAY,
136	"map":   MT_TYPE_MASK_MAP,
137	"any":   MT_TYPE_MASK_ANY,
138}
139
140func TypeNameToMask(typeName string) (mask int, present bool) {
141	retval := typeNameToMaskMap[typeName]
142	if retval != 0 {
143		return retval, true
144	} else {
145		return 0, false
146	}
147}
148
149func (this *Mlrval) GetTypeBit() int {
150	return 1 << this.mvtype
151}
152