github.com/viant/toolbox@v0.34.5/data/map.go (about)

     1  package data
     2  
     3  import (
     4  	"bytes"
     5  	"github.com/viant/toolbox"
     6  	"log"
     7  	"strings"
     8  	"time"
     9  )
    10  
    11  //Map types is an alias type to map[string]interface{} with extended behaviours
    12  type Map map[string]interface{}
    13  
    14  //Udf represents a user defined function used to transform data.
    15  type Udf func(interface{}, Map) (interface{}, error)
    16  
    17  //Put puts key value into the map.
    18  func (s *Map) Put(key string, value interface{}) {
    19  	(*s)[key] = value
    20  }
    21  
    22  //Delete removes the supplied keys, it supports key of path expression with dot i.e. request.method
    23  func (s *Map) Delete(keys ...string) {
    24  	for _, key := range keys {
    25  		if !strings.Contains(key, ".") {
    26  			delete(*s, key)
    27  			continue
    28  		}
    29  		keyParts := strings.Split(key, ".")
    30  		var temp = *s
    31  		for i, part := range keyParts {
    32  			if temp == nil {
    33  				break
    34  			}
    35  			isLasPart := i+1 == len(keyParts)
    36  			if isLasPart {
    37  				delete(temp, part)
    38  			} else if temp[part] != nil && toolbox.IsMap(temp[part]) {
    39  				subMap := toolbox.AsMap(temp[part])
    40  				temp = Map(subMap)
    41  			} else {
    42  				break
    43  			}
    44  		}
    45  	}
    46  }
    47  
    48  //Replace replaces supplied key/path with corresponding value
    49  func (s *Map) Replace(key, val string) {
    50  	if !strings.Contains(key, ".") {
    51  		(*s)[key] = val
    52  		return
    53  	}
    54  	keyParts := strings.Split(key, ".")
    55  	var temp = *s
    56  	for i, part := range keyParts {
    57  		if temp == nil {
    58  			break
    59  		}
    60  		isLasPart := i+1 == len(keyParts)
    61  		if isLasPart {
    62  			temp[part] = val
    63  		} else if temp[part] != nil && toolbox.IsMap(temp[part]) {
    64  			subMap := toolbox.AsMap(temp[part])
    65  			temp = Map(subMap)
    66  		} else {
    67  			break
    68  		}
    69  	}
    70  
    71  }
    72  
    73  //Has returns true if the provided key is present
    74  func (s *Map) Has(key string) bool {
    75  	_, found := (*s)[key]
    76  	return found
    77  }
    78  
    79  //Get returns a value for provided key
    80  func (s *Map) Get(key string) interface{} {
    81  	if result, found := (*s)[key]; found {
    82  		return result
    83  	}
    84  	return nil
    85  }
    86  
    87  /*
    88  GetValue returns value for provided expression.
    89  The expression uses dot (.) to denote nested data structure.
    90  The following expression as supported
    91   1) <-key shift
    92   2) ++key pre increment
    93   3) key++ post increment
    94   4) $key reference access
    95  */
    96  func (s *Map) GetValue(expr string) (interface{}, bool) {
    97  	if expr == "" {
    98  		return nil, false
    99  	}
   100  	if strings.HasPrefix(expr, "{") && strings.HasSuffix(expr, "}") {
   101  		expr = expr[1 : len(expr)-1]
   102  	}
   103  
   104  	isShiftOperation := strings.HasPrefix(expr, "<-")
   105  	if isShiftOperation {
   106  		expr = string(expr[2:])
   107  	}
   108  
   109  	isPostIncrementOperation := strings.HasSuffix(expr, "++")
   110  	if isPostIncrementOperation {
   111  		expr = string(expr[:len(expr)-2])
   112  	}
   113  
   114  	isPreIncrementOperation := strings.HasPrefix(expr, "++")
   115  	if isPreIncrementOperation {
   116  		expr = string(expr[2:])
   117  	}
   118  
   119  	isReference := strings.HasPrefix(expr, "$")
   120  	if isReference {
   121  		expr = string(expr[1:])
   122  		expr = s.GetString(expr)
   123  		if expr == "" {
   124  			return nil, false
   125  		}
   126  	}
   127  
   128  	state := *s
   129  
   130  	if strings.Contains(expr, ".") || strings.HasSuffix(expr, "]") {
   131  		fragments := strings.Split(expr, ".")
   132  		for i, fragment := range fragments {
   133  			var index interface{}
   134  			arrayIndexPosition := strings.Index(fragment, "[")
   135  			if arrayIndexPosition != -1 {
   136  				arrayEndPosition := strings.Index(fragment, "]")
   137  				if arrayEndPosition > arrayIndexPosition && arrayEndPosition < len(fragment) {
   138  					arrayIndex := string(fragment[arrayIndexPosition+1 : arrayEndPosition])
   139  					index = arrayIndex
   140  					fragment = string(fragment[:arrayIndexPosition])
   141  				}
   142  			}
   143  			isLast := i+1 == len(fragments)
   144  
   145  			hasKey := state.Has(fragment)
   146  			if !hasKey {
   147  				return nil, false
   148  			}
   149  
   150  			var candidate = state.Get(fragment)
   151  			if !isLast && candidate == nil {
   152  				return nil, false
   153  			}
   154  
   155  			if index != nil {
   156  
   157  				if intIndex, err := toolbox.ToInt(index); err == nil {
   158  					if !toolbox.IsSlice(candidate) {
   159  						return nil, false
   160  					}
   161  					var aSlice = toolbox.AsSlice(candidate)
   162  					if intIndex >= len(aSlice) {
   163  						return nil, false
   164  					}
   165  					if intIndex < len(aSlice) {
   166  						candidate = aSlice[intIndex]
   167  					} else {
   168  						candidate = nil
   169  					}
   170  				} else if textIndex, ok := index.(string); ok {
   171  					if !toolbox.IsMap(candidate) {
   172  						return nil, false
   173  					}
   174  					aMap := toolbox.AsMap(candidate)
   175  					if candidate, ok = aMap[textIndex]; !ok {
   176  						return nil, false
   177  					}
   178  				} else {
   179  					return nil, false
   180  				}
   181  
   182  				if isLast {
   183  					return candidate, true
   184  				}
   185  			}
   186  
   187  			if isLast {
   188  				expr = fragment
   189  				continue
   190  			}
   191  			if toolbox.IsMap(candidate) {
   192  				newState := toolbox.AsMap(candidate)
   193  				if newState != nil {
   194  					state = newState
   195  				}
   196  			} else {
   197  				value, _ := state.GetValue(fragment)
   198  				if f, ok := value.(func(key string) interface{}); ok {
   199  					return f(fragments[i+1]), true
   200  				}
   201  				return nil, false
   202  			}
   203  		}
   204  	}
   205  
   206  	if state.Has(expr) {
   207  		var result = state.Get(expr)
   208  		if isPostIncrementOperation {
   209  			state.Put(expr, toolbox.AsInt(result)+1)
   210  		} else if isPreIncrementOperation {
   211  			result = toolbox.AsInt(result) + 1
   212  			state.Put(expr, result)
   213  		} else if isShiftOperation {
   214  
   215  			aCollection := state.GetCollection(expr)
   216  			if len(*aCollection) == 0 {
   217  				return nil, false
   218  			}
   219  			var result = (*aCollection)[0]
   220  			var newCollection = (*aCollection)[1:]
   221  			state.Put(expr, &newCollection)
   222  			return result, true
   223  		}
   224  		if f, ok := result.(func() interface{}); ok {
   225  			return f(), true
   226  		}
   227  		return result, true
   228  	}
   229  	return nil, false
   230  }
   231  
   232  /*
   233  Set value sets value in the map for the supplied expression.
   234  The expression uses dot (.) to denote nested data structure. For instance the following expression key1.subKey1.attr1
   235  Would take or create map for key1, followied bu takeing or creating antother map for subKey1 to set attr1 key with provided value
   236  The following expression as supported
   237   1) $key reference - the final key is determined from key's content.
   238   2) ->key push expression to append value at the end of the slice
   239  */
   240  func (s *Map) SetValue(expr string, value interface{}) {
   241  	if expr == "" {
   242  		return
   243  	}
   244  	if value == nil {
   245  		return
   246  	}
   247  	if strings.Index(expr, "$") != -1 {
   248  		expr = s.ExpandAsText(expr)
   249  	}
   250  
   251  	state := *s
   252  	isPushOperation := strings.HasPrefix(expr, "->")
   253  	if isPushOperation {
   254  		expr = string(expr[2:])
   255  	}
   256  	if strings.HasPrefix(expr, "{") && strings.HasSuffix(expr, "}") {
   257  		expr = expr[1 : len(expr)-1]
   258  	}
   259  
   260  	if strings.Contains(expr, ".") {
   261  		fragments := strings.Split(expr, ".")
   262  		nodePath := strings.Join(fragments[:len(fragments)-1], ".")
   263  		if node, ok := s.GetValue(nodePath); ok && toolbox.IsMap(node) {
   264  			if _, writable := node.(map[string]interface{}); !writable {
   265  				node = Map(toolbox.AsMap(node))
   266  				s.SetValue(nodePath, node)
   267  			}
   268  			expr = fragments[len(fragments)-1]
   269  			state = Map(toolbox.AsMap(node))
   270  		} else {
   271  			for i, fragment := range fragments {
   272  				isLast := i+1 == len(fragments)
   273  				if isLast {
   274  					expr = fragment
   275  				} else {
   276  					subState := state.GetMap(fragment)
   277  					if subState == nil {
   278  						subState = NewMap()
   279  						state.Put(fragment, subState)
   280  					}
   281  					state = subState
   282  				}
   283  			}
   284  		}
   285  	}
   286  
   287  	if isPushOperation {
   288  		collection := state.GetCollection(expr)
   289  		if collection == nil {
   290  			collection = NewCollection()
   291  			state.Put(expr, collection)
   292  		}
   293  		collection.Push(value)
   294  		state.Put(expr, collection)
   295  		return
   296  	}
   297  	state.Put(expr, value)
   298  }
   299  
   300  //Apply copies all elements of provided map to this map.
   301  func (s *Map) Apply(source map[string]interface{}) {
   302  	for k, v := range source {
   303  		(*s)[k] = v
   304  	}
   305  }
   306  
   307  //GetString returns value for provided key as string.
   308  func (s *Map) GetString(key string) string {
   309  	if result, found := (*s)[key]; found {
   310  		return toolbox.AsString(result)
   311  	}
   312  	return ""
   313  }
   314  
   315  //GetInt returns value for provided key as int.
   316  func (s *Map) GetInt(key string) int {
   317  	if result, found := (*s)[key]; found {
   318  		return toolbox.AsInt(result)
   319  	}
   320  	return 0
   321  }
   322  
   323  //GetFloat returns value for provided key as float64.
   324  func (s *Map) GetFloat(key string) float64 {
   325  	if result, found := (*s)[key]; found {
   326  		return toolbox.AsFloat(result)
   327  	}
   328  	return 0.0
   329  }
   330  
   331  //GetBoolean returns value for provided key as boolean.
   332  func (s *Map) GetBoolean(key string) bool {
   333  	if result, found := (*s)[key]; found {
   334  		return toolbox.AsBoolean(result)
   335  	}
   336  	return false
   337  }
   338  
   339  //GetCollection returns value for provided key as collection pointer.
   340  func (s *Map) GetCollection(key string) *Collection {
   341  	if result, found := (*s)[key]; found {
   342  		collectionPointer, ok := result.(*Collection)
   343  		if ok {
   344  			return collectionPointer
   345  		}
   346  
   347  		aSlice, ok := result.([]interface{})
   348  		collection := Collection(aSlice)
   349  		if ok {
   350  			return &collection
   351  		}
   352  		if !toolbox.IsSlice(result) {
   353  			return nil
   354  		}
   355  		aSlice = toolbox.AsSlice(result)
   356  		collection = Collection(aSlice)
   357  		return &collection
   358  	}
   359  	return nil
   360  }
   361  
   362  //GetMap returns value for provided key as  map.
   363  func (s *Map) GetMap(key string) Map {
   364  	if result, found := (*s)[key]; found {
   365  		aMap, ok := result.(Map)
   366  		if ok {
   367  			return aMap
   368  		}
   369  		aMap, ok = result.(map[string]interface{})
   370  		if ok {
   371  			return aMap
   372  		}
   373  		var result = toolbox.AsMap(result)
   374  		(*s)[key] = result
   375  		return result
   376  	}
   377  	return nil
   378  }
   379  
   380  //Range iterates every key, value pair of this map, calling supplied callback as long it does return true.
   381  func (s *Map) Range(callback func(k string, v interface{}) (bool, error)) error {
   382  	for k, v := range *s {
   383  		next, err := callback(k, v)
   384  		if err != nil {
   385  			return err
   386  		}
   387  		if !next {
   388  			break
   389  		}
   390  	}
   391  	return nil
   392  }
   393  
   394  //Clones create a clone of this map.
   395  func (s *Map) Clone() Map {
   396  	var result = NewMap()
   397  	for key, value := range *s {
   398  		if aMap, casted := value.(Map); casted {
   399  			result[key] = aMap.Clone()
   400  		} else {
   401  			result[key] = value
   402  		}
   403  	}
   404  	return result
   405  }
   406  
   407  //Returns a map that can be encoded by json or other decoders.
   408  //Since a map can store a function, it filters out any non marshalable types.
   409  func (s *Map) AsEncodableMap() map[string]interface{} {
   410  	var result = make(map[string]interface{})
   411  	for k, v := range *s {
   412  		if v == nil {
   413  			continue
   414  		}
   415  		result[k] = asEncodableValue(v)
   416  	}
   417  	return result
   418  }
   419  
   420  //asEncodableValue returns all non func values or func() literal for function.
   421  func asEncodableValue(v interface{}) interface{} {
   422  	if v == nil {
   423  		return nil
   424  	}
   425  	value := v
   426  	if toolbox.IsFunc(v) {
   427  		return "func()"
   428  	}
   429  	if toolbox.IsMap(v) {
   430  		var aMap = Map(toolbox.AsMap(v))
   431  		value = aMap.AsEncodableMap()
   432  	} else if toolbox.IsSlice(v) {
   433  		var targetSlice = make([]interface{}, 0)
   434  		var sourceSlice = toolbox.AsSlice(v)
   435  		for _, item := range sourceSlice {
   436  			targetSlice = append(targetSlice, asEncodableValue(item))
   437  		}
   438  		value = targetSlice
   439  	} else if toolbox.IsString(v) || toolbox.IsInt(v) || toolbox.IsFloat(v) {
   440  		value = v
   441  	} else {
   442  		value = toolbox.AsString(v)
   443  	}
   444  	return value
   445  }
   446  
   447  func hasGenericKeys(aMap map[string]interface{}) bool {
   448  	for k := range aMap {
   449  		if strings.HasPrefix(k, "$AsInt") || strings.HasPrefix(k, "$AsFloat") || strings.HasPrefix(k, "$AsBool") {
   450  			return true
   451  		}
   452  	}
   453  	return false
   454  }
   455  
   456  //Expand expands provided value of any type with dollar sign expression/
   457  func (s *Map) Expand(source interface{}) interface{} {
   458  	switch value := source.(type) {
   459  	case bool, []byte, int, uint, int8, int16, int32, int64, uint8, uint16, uint32, uint64, float32, float64, time.Time:
   460  		return source
   461  	case *[]byte:
   462  		return s.expandExpressions(string(*value))
   463  	case *string:
   464  		if value == nil {
   465  			return ""
   466  		}
   467  		return s.expandExpressions(*value)
   468  	case string:
   469  		return s.expandExpressions(value)
   470  	case map[string]interface{}:
   471  		if hasGenericKeys(value) {
   472  			result := make(map[interface{}]interface{})
   473  			for k, v := range value {
   474  				var key = s.Expand(k)
   475  				result[key] = s.Expand(v)
   476  			}
   477  			return result
   478  		}
   479  		resultMap := make(map[string]interface{})
   480  		for k, v := range value {
   481  			var key = s.ExpandAsText(k)
   482  			var expanded = s.Expand(v)
   483  
   484  			if key == "..." {
   485  				if expanded != nil && toolbox.IsMap(expanded) {
   486  					for key, value := range toolbox.AsMap(expanded) {
   487  						resultMap[key] = value
   488  					}
   489  					continue
   490  				}
   491  			}
   492  			resultMap[key] = expanded
   493  		}
   494  		return resultMap
   495  	case map[interface{}]interface{}:
   496  		var resultMap = make(map[interface{}]interface{})
   497  		for k, v := range value {
   498  			var key = s.Expand(k)
   499  			var expanded = s.Expand(v)
   500  			if key == "..." {
   501  				if expanded != nil && toolbox.IsMap(expanded) {
   502  					for key, value := range toolbox.AsMap(expanded) {
   503  						resultMap[key] = value
   504  					}
   505  					continue
   506  				}
   507  			}
   508  			resultMap[key] = expanded
   509  		}
   510  		return resultMap
   511  	case []interface{}:
   512  		var resultSlice = make([]interface{}, len(value))
   513  		for i, value := range value {
   514  			resultSlice[i] = s.Expand(value)
   515  		}
   516  		return resultSlice
   517  	default:
   518  
   519  		if source == nil {
   520  			return nil
   521  		}
   522  
   523  		if toolbox.IsMap(source) {
   524  			switch aMap := value.(type) {
   525  			case map[string]interface{}:
   526  				return s.Expand(aMap)
   527  			case map[interface{}]interface{}:
   528  				return s.Expand(aMap)
   529  			default:
   530  				return s.Expand(toolbox.AsMap(value))
   531  			}
   532  
   533  		} else if toolbox.IsSlice(source) {
   534  			return s.Expand(toolbox.AsSlice(value))
   535  		} else if toolbox.IsStruct(value) {
   536  			aMap := toolbox.AsMap(value)
   537  			return s.Expand(aMap)
   538  		} else if value != nil {
   539  			return s.Expand(toolbox.AsString(value))
   540  		}
   541  	}
   542  	return source
   543  }
   544  
   545  //ExpandAsText expands all matching expressions starting with dollar sign ($)
   546  func (s *Map) ExpandAsText(text string) string {
   547  	result := s.expandExpressions(text)
   548  	if toolbox.IsSlice(result) || toolbox.IsMap(result) {
   549  		buf := new(bytes.Buffer)
   550  		err := toolbox.NewJSONEncoderFactory().Create(buf).Encode(result)
   551  		if err == nil {
   552  			return buf.String()
   553  		}
   554  	}
   555  	if text, ok := result.(string); ok || result == nil {
   556  		return text
   557  	}
   558  	return toolbox.AsString(result)
   559  }
   560  
   561  func (s *Map) evaluateUDF(candidate interface{}, argument interface{}) (interface{}, bool) {
   562  	var canExpandAll = true
   563  
   564  	if toolbox.IsString(argument) {
   565  		var expandable = strings.TrimSpace(toolbox.AsString(argument))
   566  		Parse(expandable, func(expression string, udf bool, argument interface{}) (interface{}, bool) {
   567  			if _, has := s.GetValue(string(expression[1:])); !has {
   568  				canExpandAll = false
   569  			}
   570  			return nil, false
   571  		})
   572  	}
   573  
   574  	if !canExpandAll {
   575  		return nil, false
   576  	}
   577  	udf, ok := candidate.(func(interface{}, Map) (interface{}, error))
   578  	if !ok {
   579  		return nil, false
   580  	}
   581  
   582  	expandedArgument := s.expandArgumentsExpressions(argument)
   583  	if toolbox.IsString(expandedArgument) {
   584  		expandedText := toolbox.AsString(expandedArgument)
   585  		if toolbox.IsStructuredJSON(expandedText) {
   586  			evaluated, err := toolbox.JSONToInterface(expandedText)
   587  			if err != nil {
   588  				return nil, false
   589  			}
   590  			expandedArgument = evaluated
   591  		}
   592  	}
   593  	evaluated, err := udf(expandedArgument, *s)
   594  	if err == nil {
   595  		return evaluated, true
   596  	}
   597  	log.Printf("failed to evaluate %v, %v", candidate, err)
   598  	return nil, false
   599  }
   600  
   601  func (s *Map) hasCycle(source interface{}, ownerVariable string) bool {
   602  	switch value := source.(type) {
   603  	case string:
   604  		return strings.Contains(value, ownerVariable)
   605  	case Map:
   606  		for k, v := range value {
   607  			if s.hasCycle(k, ownerVariable) || s.hasCycle(v, ownerVariable) {
   608  				return true
   609  			}
   610  		}
   611  
   612  	case map[string]interface{}:
   613  		for k, v := range value {
   614  			if s.hasCycle(k, ownerVariable) || s.hasCycle(v, ownerVariable) {
   615  				return true
   616  			}
   617  		}
   618  
   619  	case []interface{}:
   620  		for _, v := range value {
   621  			if s.hasCycle(v, ownerVariable) {
   622  				return true
   623  			}
   624  		}
   625  	case Collection:
   626  		for _, v := range value {
   627  			if s.hasCycle(v, ownerVariable) {
   628  				return true
   629  			}
   630  		}
   631  	}
   632  	return false
   633  }
   634  
   635  //expandExpressions will check provided text with any expression starting with dollar sign ($) to substitute it with key in the map if it is present.
   636  //The result can be an expanded text or type of key referenced by the expression.
   637  func (s *Map) expandExpressions(text string) interface{} {
   638  	if strings.Index(text, "$") == -1 {
   639  		return text
   640  	}
   641  	var expandVariable = func(expression string, isUDF bool, argument interface{}) (interface{}, bool) {
   642  		value, hasExpValue := s.GetValue(string(expression[1:]))
   643  		if hasExpValue {
   644  			if value != expression && s.hasCycle(value, expression) {
   645  				log.Printf("detected data cycle on %v in value: %v", expression, value)
   646  				return expression, true
   647  			}
   648  			if isUDF {
   649  				if evaluated, ok := s.evaluateUDF(value, argument); ok {
   650  					return evaluated, true
   651  				}
   652  			} else {
   653  				if value != nil && (toolbox.IsMap(value) || toolbox.IsSlice(value)) {
   654  					return s.Expand(value), true
   655  				}
   656  				if text, ok := value.(string); ok {
   657  					return text, true
   658  				}
   659  				if value != nil {
   660  					return toolbox.DereferenceValue(value), true
   661  				}
   662  				return value, true
   663  			}
   664  		}
   665  
   666  		if isUDF {
   667  			expandedArgument := s.expandArgumentsExpressions(argument)
   668  			_, isByteArray := expandedArgument.([]byte)
   669  			if !toolbox.IsMap(expandedArgument) && !toolbox.IsSlice(expandedArgument) || isByteArray {
   670  				argument = toolbox.AsString(expandedArgument)
   671  			}
   672  			return expression + "(" + toolbox.AsString(argument) + ")", true
   673  		}
   674  		return expression, true
   675  	}
   676  
   677  	return Parse(text, expandVariable)
   678  }
   679  
   680  //expandExpressions will check provided text with any expression starting with dollar sign ($) to substitute it with key in the map if it is present.
   681  //The result can be an expanded text or type of key referenced by the expression.
   682  func (s *Map) expandArgumentsExpressions(argument interface{}) interface{} {
   683  	if argument == nil || !toolbox.IsString(argument) {
   684  		return argument
   685  	}
   686  
   687  	argumentLiteral, ok := argument.(string)
   688  
   689  	if ok {
   690  		if toolbox.IsStructuredJSON(argumentLiteral) {
   691  			return s.expandExpressions(argumentLiteral)
   692  		}
   693  	}
   694  
   695  	var expandVariable = func(expression string, isUDF bool, argument interface{}) (interface{}, bool) {
   696  		value, hasExpValue := s.GetValue(string(expression[1:]))
   697  		if hasExpValue {
   698  			if value != expression && s.hasCycle(value, expression) {
   699  				log.Printf("detected data cycle on %v in value: %v", expression, value)
   700  				return expression, true
   701  			}
   702  			if isUDF {
   703  				if evaluated, ok := s.evaluateUDF(value, argument); ok {
   704  					return evaluated, true
   705  				}
   706  			} else {
   707  				if value != nil && (toolbox.IsMap(value) || toolbox.IsSlice(value)) {
   708  					return s.Expand(value), true
   709  				}
   710  				if text, ok := value.(string); ok {
   711  					return text, true
   712  				}
   713  				if value != nil {
   714  					return toolbox.DereferenceValue(value), true
   715  				}
   716  				return value, true
   717  			}
   718  		}
   719  		if isUDF {
   720  			expandedArgument := s.expandArgumentsExpressions(argument)
   721  			_, isByteArray := expandedArgument.([]byte)
   722  			if !toolbox.IsMap(expandedArgument) && !toolbox.IsSlice(expandedArgument) || isByteArray {
   723  				argument = toolbox.AsString(expandedArgument)
   724  			}
   725  			expression = expression + "(" + toolbox.AsString(argument) + ")"
   726  			return expression, true
   727  		}
   728  		return expression, true
   729  	}
   730  
   731  	tokenizer := toolbox.NewTokenizer(argumentLiteral, invalidToken, eofToken, matchers)
   732  	var result = make([]interface{}, 0)
   733  	for tokenizer.Index < len(argumentLiteral) {
   734  		match, err := toolbox.ExpectTokenOptionallyFollowedBy(tokenizer, whitespace, "expected argument", doubleQuoteEnclosedToken, comaToken, unmatchedToken, eofToken)
   735  		if err != nil {
   736  			return Parse(argumentLiteral, expandVariable)
   737  		}
   738  		switch match.Token {
   739  		case doubleQuoteEnclosedToken:
   740  			result = append(result, strings.Trim(match.Matched, `"`))
   741  		case comaToken:
   742  			result = append(result, match.Matched)
   743  			tokenizer.Index++
   744  		case unmatchedToken:
   745  			result = append(result, match.Matched)
   746  		}
   747  	}
   748  
   749  	for i, arg := range result {
   750  		textArg, ok := arg.(string)
   751  		if !ok {
   752  			continue
   753  		}
   754  		textArg = strings.Trim(textArg, "'")
   755  		result[i] = Parse(textArg, expandVariable)
   756  
   757  	}
   758  	if len(result) == 1 {
   759  		return result[0]
   760  	}
   761  	return result
   762  }
   763  
   764  //NewMap creates a new instance of a map.
   765  func NewMap() Map {
   766  	return make(map[string]interface{})
   767  }