github.com/lmorg/murex@v0.0.0-20240217211045-e081c89cd4ef/lang/expressions/parse_object.go (about)

     1  package expressions
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/lmorg/murex/lang/expressions/primitives"
     7  	"github.com/lmorg/murex/lang/expressions/symbols"
     8  	"github.com/lmorg/murex/lang/types"
     9  )
    10  
    11  func (tree *ParserT) createObjectAst(exec bool) error {
    12  	// create JSON dict
    13  	_, dt, err := tree.parseObject(exec)
    14  	if err != nil {
    15  		return err
    16  	}
    17  	tree.appendAstWithPrimitive(symbols.ObjectBegin, dt)
    18  	tree.charPos++
    19  
    20  	return nil
    21  }
    22  
    23  func (tree *ParserT) parseObject(exec bool) ([]rune, *primitives.DataType, error) {
    24  	start := tree.charPos
    25  	o := newParseObjectT(tree)
    26  
    27  	for tree.charPos++; tree.charPos < len(tree.expression); tree.charPos++ {
    28  		r := tree.expression[tree.charPos]
    29  
    30  		switch r {
    31  		case '#':
    32  			tree.parseComment()
    33  
    34  		case '/':
    35  			if tree.nextChar() == '#' {
    36  				if err := tree.parseCommentMultiLine(); err != nil {
    37  					return nil, nil, err
    38  				}
    39  			} else {
    40  				err := o.AppendRune(r)
    41  				if err != nil {
    42  					return nil, nil, err
    43  				}
    44  			}
    45  
    46  		case '\'', '"':
    47  			// quoted string
    48  			value, err := tree.parseString(r, r, exec)
    49  			if err != nil {
    50  				return nil, nil, err
    51  			}
    52  			err = o.UpdateInterface(string(value))
    53  			if err != nil {
    54  				return nil, nil, err
    55  			}
    56  			tree.charPos++
    57  
    58  		case '%':
    59  			switch tree.nextChar() {
    60  			case '[', '{':
    61  				// do nothing because action covered in the next iteration
    62  			case '(':
    63  				// start nested string
    64  				tree.charPos++
    65  				value, err := tree.parseParenthesis(exec)
    66  				if err != nil {
    67  					return nil, nil, err
    68  				}
    69  				err = o.UpdateInterface(string(value))
    70  				if err != nil {
    71  					return nil, nil, err
    72  				}
    73  			default:
    74  				// string
    75  				err := o.AppendRune(r)
    76  				if err != nil {
    77  					return nil, nil, err
    78  				}
    79  			}
    80  
    81  		case '[':
    82  			// start nested array
    83  			if o.stage == 0 {
    84  				return nil, nil, fmt.Errorf("object keys cannot be an array")
    85  			}
    86  			_, dt, err := tree.parseArray(exec)
    87  			if err != nil {
    88  				return nil, nil, err
    89  			}
    90  			v, err := dt.GetValue()
    91  			if err != nil {
    92  				return nil, nil, err
    93  			}
    94  			err = o.UpdateInterface(v.Value)
    95  			if err != nil {
    96  				return nil, nil, err
    97  			}
    98  			tree.charPos++
    99  
   100  		case '{':
   101  			// start nested object
   102  			if o.stage == 0 {
   103  				return nil, nil, raiseError(
   104  					tree.expression, nil, tree.charPos, "object keys cannot be another object")
   105  			}
   106  			_, dt, err := tree.parseObject(exec)
   107  			if err != nil {
   108  				return nil, nil, err
   109  			}
   110  			v, err := dt.GetValue()
   111  			if err != nil {
   112  				return nil, nil, err
   113  			}
   114  			err = o.UpdateInterface(v.Value)
   115  			if err != nil {
   116  				return nil, nil, err
   117  			}
   118  			tree.charPos++
   119  
   120  		case '$':
   121  			switch {
   122  			case tree.nextChar() == '{':
   123  				// inline subshell
   124  				strOrVal := varFormatting(o.stage)
   125  				subshell, fn, err := tree.parseSubShell(exec, r, strOrVal)
   126  				if err != nil {
   127  					return nil, nil, err
   128  				}
   129  				if exec {
   130  					val, err := fn()
   131  					if err != nil {
   132  						return nil, nil, err
   133  					}
   134  					err = o.UpdateInterface(val.Value)
   135  					if err != nil {
   136  						return nil, nil, err
   137  					}
   138  				} else {
   139  					err = o.UpdateInterface(string(subshell))
   140  					if err != nil {
   141  						return nil, nil, err
   142  					}
   143  				}
   144  			default:
   145  				// inline scalar
   146  				strOrVal := varFormatting(o.stage)
   147  				scalar, v, _, err := tree.parseVarScalar(exec, exec, strOrVal)
   148  				if err != nil {
   149  					return nil, nil, err
   150  				}
   151  				if exec {
   152  					err = o.UpdateInterface(v)
   153  					if err != nil {
   154  						return nil, nil, err
   155  					}
   156  				} else {
   157  					err = o.UpdateInterface(string(scalar))
   158  					if err != nil {
   159  						return nil, nil, err
   160  					}
   161  				}
   162  			}
   163  
   164  		case '~':
   165  			// tilde
   166  			err := o.AppendRune([]rune(tree.parseVarTilde(exec))...)
   167  			if err != nil {
   168  				return nil, nil, err
   169  			}
   170  
   171  		case '@':
   172  			switch tree.nextChar() {
   173  			case '{':
   174  				subshell, fn, err := tree.parseSubShell(exec, r, varAsValue)
   175  				if err != nil {
   176  					return nil, nil, err
   177  				}
   178  				if exec {
   179  					val, err := fn()
   180  					if err != nil {
   181  						return nil, nil, err
   182  					}
   183  					err = o.UpdateInterface(val.Value)
   184  					if err != nil {
   185  						return nil, nil, err
   186  					}
   187  				} else {
   188  					err = o.UpdateInterface(string(subshell))
   189  					if err != nil {
   190  						return nil, nil, err
   191  					}
   192  				}
   193  			default:
   194  				_, v, err := tree.parseVarArray(exec)
   195  				if err != nil {
   196  					return nil, nil, err
   197  				}
   198  				err = o.UpdateInterface(v)
   199  				if err != nil {
   200  					return nil, nil, err
   201  				}
   202  			}
   203  
   204  		case ':':
   205  			if o.stage != OBJ_STAGE_KEY {
   206  				return nil, nil, raiseError(
   207  					tree.expression, nil, tree.charPos, "invalid symbol ':' expecting ',' or '}' instead")
   208  			}
   209  			o.stage++
   210  
   211  		case '\n':
   212  			err := o.WriteKeyValuePair()
   213  			if err != nil {
   214  				return nil, nil, err
   215  			}
   216  			tree.crLf()
   217  
   218  		case ',':
   219  			err := o.WriteKeyValuePair()
   220  			if err != nil {
   221  				return nil, nil, err
   222  			}
   223  
   224  		case '}':
   225  			err := o.WriteKeyValuePair()
   226  			if err != nil {
   227  				return nil, nil, err
   228  			}
   229  			goto endObject
   230  
   231  		case '\r':
   232  			continue
   233  
   234  		case ' ', '\t':
   235  			if !o.IsValueUndefined() {
   236  				o.keyValue[o.stage].ValueSet = true
   237  			}
   238  			continue
   239  
   240  		default:
   241  			value := tree.parseArrayBareword()
   242  			v, err := types.ConvertGoType(value, types.Number)
   243  			if err == nil {
   244  				// is a number
   245  				err = o.UpdateInterface(v)
   246  			} else {
   247  				// is a string
   248  				s := string(value)
   249  				switch s {
   250  				case "true":
   251  					err = o.UpdateInterface(true)
   252  				case "false":
   253  					err = o.UpdateInterface(false)
   254  				case "null":
   255  					err = o.UpdateInterface(nil)
   256  				default:
   257  					err = o.UpdateInterface(s)
   258  				}
   259  			}
   260  			if err != nil {
   261  				return nil, nil, err
   262  			}
   263  		}
   264  	}
   265  
   266  	return nil, nil, raiseError(
   267  		tree.expression, nil, tree.charPos, "missing closing bracket '}'")
   268  
   269  endObject:
   270  	value := tree.expression[start:tree.charPos]
   271  	tree.charPos--
   272  	dt := primitives.NewPrimitive(primitives.Object, o.obj)
   273  	return value, dt, nil
   274  }