github.com/coveo/gotemplate@v2.7.7+incompatible/template/razor_repl_expr.go (about)

     1  package template
     2  
     3  import (
     4  	"fmt"
     5  	"go/parser"
     6  	"regexp"
     7  	"strings"
     8  
     9  	"github.com/coveo/gotemplate/utils"
    10  	"github.com/fatih/color"
    11  	"github.com/op/go-logging"
    12  )
    13  
    14  const (
    15  	protectString = "_=LONG_STRING="
    16  	literalAt     = "_=!AT!=_"
    17  	literalStart  = `{{ "{{" }}`
    18  	stringRep     = "_STRING_"
    19  	rangeExpr     = "_range_"
    20  	defaultExpr   = "_default_"
    21  	funcExpr      = "_func_"
    22  	funcCall      = "__FUNCCALL__"
    23  	dotRep        = "_DOT_PREFIX_"
    24  	ellipsisRep   = "_ELLIPSIS_"
    25  	globalRep     = "_GLOBAL_"
    26  )
    27  
    28  var dotPrefix = regexp.MustCompile(`(?P<prefix>^|[^\w\)\]])\.(?P<value>\w[\w\.]*)?`)
    29  var idRegex = regexp.MustCompile(`^[\p{L}\d_]+$`)
    30  
    31  func expressionParser(repl replacement, match string) string {
    32  	expr, _ := expressionParserInternal(repl, match, false, false)
    33  	return expr
    34  }
    35  
    36  func expressionParserSkipError(repl replacement, match string) string {
    37  	expr, _ := expressionParserInternal(repl, match, true, false)
    38  	return expr
    39  }
    40  
    41  func expressionParserInternal(repl replacement, match string, skipError, internal bool) (result string, err error) {
    42  	matches, _ := utils.MultiMatch(match, repl.re)
    43  	var expr, expression string
    44  	if expression = matches["expr"]; expression != "" {
    45  		if getLogLevelInternal() >= logging.DEBUG {
    46  			defer func() {
    47  				if !debugMode && result != match {
    48  					log.Debug("Resulting expression =", result)
    49  				}
    50  			}()
    51  		}
    52  
    53  		// We first protect strings declared in the expression
    54  		protected, includedStrings := String(expression).Protect()
    55  
    56  		// We transform the expression into a valid go statement
    57  		for k, v := range map[string]string{"$": stringRep, "range": rangeExpr, "default": defaultExpr, "func": funcExpr, "...": ellipsisRep} {
    58  			protected = protected.Replace(k, v)
    59  		}
    60  		protected = String(dotPrefix.ReplaceAllString(protected.Str(), fmt.Sprintf("${prefix}%s${value}", dotRep)))
    61  		for k, v := range map[string]string{ellipsisRep: "...", "<>": "!=", "÷": "/", "≠": "!=", "≦": "<=", "≧": ">=", "«": "<<", "»": ">>"} {
    62  			protected = protected.Replace(k, v)
    63  		}
    64  
    65  		for key, val := range ops {
    66  			protected = protected.Replace(" "+val+" ", key)
    67  		}
    68  		// We add support to partial slice
    69  		protected = String(indexExpression(protected.Str()))
    70  
    71  		// We restore the strings into the expression
    72  		expr = protected.RestoreProtected(includedStrings).Str()
    73  	}
    74  
    75  	if indexExpr := matches["index"]; indexExpr != "" {
    76  		indexExpr = indexExpression(indexExpr)
    77  		indexExpr = indexExpr[1 : len(indexExpr)-1]
    78  
    79  		sep, slicer, limit2 := ",", "extract", false
    80  		if strings.Contains(indexExpr, ":") {
    81  			sep, slicer, limit2 = ":", "slice", true
    82  		}
    83  		values := strings.Split(indexExpr, sep)
    84  		if !debugMode && limit2 && len(values) > 2 {
    85  			log.Errorf("Only one : character is allowed in slice expression: %s", match)
    86  		}
    87  		for i := range values {
    88  			if values[i], err = expressionParserInternal(exprRepl, values[i], true, true); err != nil {
    89  				return match, err
    90  			}
    91  		}
    92  		indexExpr = strings.Replace(strings.Join(values, " "), `$`, `$$`, -1)
    93  		repl.replace = strings.Replace(repl.replace, "${index}", indexExpr, -1)
    94  		repl.replace = strings.Replace(repl.replace, "${slicer}", slicer, -1)
    95  	}
    96  
    97  	if selectExpr := matches["selection"]; selectExpr != "" {
    98  		if selectExpr, err = expressionParserInternal(exprRepl, selectExpr, true, true); err != nil {
    99  			return match, err
   100  		}
   101  		repl.replace = strings.Replace(repl.replace, "${selection}", selectExpr, -1)
   102  	}
   103  
   104  	if expr != "" {
   105  		node := nodeValue
   106  		if internal {
   107  			node = nodeValueInternal
   108  		}
   109  		tr, err := parser.ParseExpr(expr)
   110  		if err == nil {
   111  			result, err := node(tr)
   112  			if err == nil {
   113  				result = strings.Replace(result, stringRep, "$$", -1)
   114  				result = strings.Replace(result, rangeExpr, "range", -1)
   115  				result = strings.Replace(result, defaultExpr, "default", -1)
   116  				result = strings.Replace(result, funcExpr, "func", -1)
   117  				result = strings.Replace(result, dotRep, ".", -1)
   118  				result = strings.Replace(result, globalRep, "$$.", -1)
   119  				repl.replace = strings.Replace(repl.replace, "${expr}", result, -1)
   120  				result = repl.re.ReplaceAllString(match, repl.replace)
   121  				result = strings.Replace(result, "$.slice ", "slice $.", -1)
   122  				return result, nil
   123  			}
   124  		}
   125  		if !debugMode && err != nil && getLogLevelInternal() >= 6 {
   126  			log.Debug(color.CyanString(fmt.Sprintf("Invalid expression '%s' : %v", expression, err)))
   127  		}
   128  		if skipError {
   129  			return match, err
   130  		}
   131  		repl.replace = strings.Replace(repl.replace, "${expr}", strings.Replace(expression, "$", "$$", -1), -1)
   132  	}
   133  
   134  	return repl.re.ReplaceAllString(match, repl.replace), nil
   135  }
   136  
   137  var exprRepl = replacement{
   138  	name:    "Expression",
   139  	re:      regexp.MustCompile(`(?P<expr>.*)`),
   140  	replace: `${expr}`,
   141  }
   142  
   143  func indexExpression(expr string) string {
   144  	expr = negativeSlice.ReplaceAllString(expr, "[${index}:0]")
   145  	expr = strings.Replace(expr, "[]", "[0:-1]", -1)
   146  	expr = strings.Replace(expr, "[:", "[0:", -1)
   147  	expr = strings.Replace(expr, ":]", ":-1]", -1)
   148  	return expr
   149  }
   150  
   151  var negativeSlice = regexp.MustCompile(`\[(?P<index>-\d+):]`)