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+):]`)