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

     1  package expressions
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/lmorg/murex/lang/expressions/symbols"
     7  	"github.com/lmorg/murex/utils/ansi"
     8  )
     9  
    10  func (tree *ParserT) createStringAst(qStart, qEnd rune, exec bool) error {
    11  	// create JSON dict
    12  	value, err := tree.parseString(qStart, qEnd, exec)
    13  	if err != nil {
    14  		return err
    15  	}
    16  	tree.appendAst(symbols.QuoteParenthesis, value...)
    17  	tree.charPos++
    18  	return nil
    19  }
    20  
    21  func (tree *ParserT) parseParenthesis(exec bool) ([]rune, error) {
    22  	value, err := tree.parseString('(', ')', exec)
    23  	if err != nil {
    24  		return nil, err
    25  	}
    26  
    27  	tree.charPos++
    28  
    29  	if exec {
    30  		value = []rune(ansi.ExpandConsts(string(value)))
    31  	}
    32  
    33  	return value, nil
    34  }
    35  
    36  func (tree *ParserT) parseString(qStart, qEnd rune, exec bool) ([]rune, error) {
    37  	if exec && qStart != '\'' {
    38  		return tree.parseStringInfix(qEnd, exec)
    39  	}
    40  
    41  	var value []rune
    42  
    43  	if !exec {
    44  		value = []rune{qStart}
    45  	}
    46  
    47  	for tree.charPos++; tree.charPos < len(tree.expression); tree.charPos++ {
    48  		r := tree.expression[tree.charPos]
    49  
    50  		switch {
    51  		case r == '(' && qEnd == ')':
    52  			v, err := tree.parseParenthesis(exec)
    53  			if err != nil {
    54  				return nil, err
    55  			}
    56  			value = append(value, v...)
    57  
    58  		case r == '\n':
    59  			value = append(value, r)
    60  			tree.crLf()
    61  
    62  		case r == qEnd:
    63  			// end quote
    64  			goto endString
    65  
    66  		default:
    67  			// string
    68  			value = append(value, r)
    69  		}
    70  	}
    71  
    72  	return value, raiseError(
    73  		tree.expression, nil, tree.charPos, fmt.Sprintf(
    74  			"missing closing quote (%s)",
    75  			string([]rune{qEnd})))
    76  
    77  endString:
    78  	tree.charPos--
    79  	if !exec {
    80  		value = append(value, qEnd)
    81  	}
    82  
    83  	return value, nil
    84  }
    85  
    86  func (tree *ParserT) parseStringInfix(qEnd rune, exec bool) ([]rune, error) {
    87  	var (
    88  		value   []rune
    89  		escaped bool
    90  	)
    91  
    92  	for tree.charPos++; tree.charPos < len(tree.expression); tree.charPos++ {
    93  		r := tree.expression[tree.charPos]
    94  
    95  		switch {
    96  		case escaped:
    97  			switch r {
    98  			case 's':
    99  				value = append(value, ' ')
   100  			case 't':
   101  				value = append(value, '\t')
   102  			case 'r':
   103  				value = append(value, '\r')
   104  			case 'n':
   105  				value = append(value, '\n')
   106  			default:
   107  				value = append(value, r)
   108  			}
   109  			// end escape
   110  			escaped = false
   111  
   112  		case r == '\\' && qEnd != ')':
   113  			// start escape
   114  			escaped = true
   115  
   116  		case r == '\n':
   117  			value = append(value, r)
   118  			tree.crLf()
   119  
   120  		case r == '$':
   121  			switch {
   122  			case tree.nextChar() == '{':
   123  				// subshell
   124  				subshell, fn, err := tree.parseSubShell(exec, r, varAsString)
   125  				if err != nil {
   126  					return nil, err
   127  				}
   128  				if exec {
   129  					val, err := fn()
   130  					if err != nil {
   131  						return nil, err
   132  					}
   133  					value = append(value, []rune(val.Value.(string))...)
   134  				} else {
   135  					value = append(value, subshell...)
   136  				}
   137  			default:
   138  				// inline scalar
   139  				scalar, v, _, err := tree.parseVarScalar(exec, exec, varAsString)
   140  				if err != nil {
   141  					return nil, err
   142  				}
   143  				if exec {
   144  					value = append(value, []rune(v.(string))...)
   145  				} else {
   146  					value = append(value, scalar...)
   147  				}
   148  			}
   149  
   150  		case r == '~':
   151  			// tilde
   152  			tilde := tree.parseVarTilde(exec)
   153  			value = append(value, []rune(tilde)...)
   154  
   155  		case r == '(' && qEnd == ')':
   156  			v, err := tree.parseParenthesis(exec)
   157  			if err != nil {
   158  				return nil, err
   159  			}
   160  			value = append(value, '(')
   161  			value = append(value, v...)
   162  			value = append(value, ')')
   163  
   164  		case r == qEnd:
   165  			// end quote
   166  			goto endString
   167  
   168  		default:
   169  			// string
   170  			value = append(value, r)
   171  		}
   172  	}
   173  
   174  	return value, raiseError(
   175  		tree.expression, nil, tree.charPos, fmt.Sprintf(
   176  			"missing closing quote '%s'",
   177  			string([]rune{qEnd})))
   178  
   179  endString:
   180  	tree.charPos--
   181  	return value, nil
   182  }
   183  
   184  func (tree *ParserT) parseBackTick(quote rune, exec bool) ([]rune, error) {
   185  	if exec {
   186  		quote = '\''
   187  	}
   188  
   189  	var value []rune
   190  
   191  	value = []rune{quote}
   192  
   193  	for tree.charPos++; tree.charPos < len(tree.expression); tree.charPos++ {
   194  		r := tree.expression[tree.charPos]
   195  
   196  		switch r {
   197  		case '`':
   198  			// end quote
   199  			goto endBackTick
   200  
   201  		default:
   202  			// string
   203  			value = append(value, r)
   204  		}
   205  	}
   206  
   207  	return value, raiseError(
   208  		tree.expression, nil, tree.charPos, "missing closing backtick, '`'")
   209  
   210  endBackTick:
   211  	tree.charPos--
   212  	value = append(value, quote)
   213  
   214  	return value, nil
   215  }
   216  
   217  func (tree *ParserT) parseBlockQuote() ([]rune, error) {
   218  	start := tree.charPos
   219  
   220  	for tree.charPos++; tree.charPos < len(tree.expression); tree.charPos++ {
   221  		r := tree.expression[tree.charPos]
   222  
   223  		switch r {
   224  		case '#':
   225  			tree.parseComment()
   226  
   227  		case '\n':
   228  			tree.crLf()
   229  
   230  		case '%':
   231  			switch tree.nextChar() {
   232  			case '[':
   233  				tree.charPos++
   234  				_, _, err := tree.parseArray(false)
   235  				if err != nil {
   236  					return nil, err
   237  				}
   238  			case '{':
   239  				tree.charPos++
   240  				_, _, err := tree.parseObject(false)
   241  				if err != nil {
   242  					return nil, err
   243  				}
   244  				tree.charPos++
   245  			}
   246  
   247  		case '\'':
   248  			_, err := tree.parseString('\'', '\'', false)
   249  			if err != nil {
   250  				return nil, err
   251  			}
   252  			tree.charPos++
   253  
   254  		case '"':
   255  			_, err := tree.parseStringInfix('"', false)
   256  			if err != nil {
   257  				return nil, err
   258  			}
   259  			tree.charPos++
   260  
   261  		case '(':
   262  			_, err := tree.parseString('(', ')', false)
   263  			if err != nil {
   264  				return nil, err
   265  			}
   266  			tree.charPos++
   267  
   268  		case '{':
   269  			_, err := tree.parseBlockQuote()
   270  			if err != nil {
   271  				return nil, err
   272  			}
   273  
   274  		case '}':
   275  			// end quote
   276  			return tree.expression[start : tree.charPos+1], nil
   277  
   278  		default:
   279  			// nothing to do
   280  		}
   281  	}
   282  
   283  	return nil, raiseError(tree.expression, nil, tree.charPos, "missing closing brace '}'")
   284  }
   285  
   286  func (tree *ParserT) parseNamedPipe() []rune {
   287  	start := tree.charPos
   288  
   289  	for tree.charPos++; tree.charPos < len(tree.expression); tree.charPos++ {
   290  		r := tree.expression[tree.charPos]
   291  
   292  		if isBareChar(r) || r == '!' || r == ':' || r == '=' || r == '.' {
   293  			continue
   294  		}
   295  
   296  		if r == '>' {
   297  			return tree.expression[start+1 : tree.charPos]
   298  		}
   299  
   300  		break
   301  	}
   302  
   303  	tree.charPos = start
   304  	return nil
   305  }