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