github.com/bingoohuang/gg@v0.0.0-20240325092523-45da7dee9335/pkg/vars/parse.go (about) 1 package vars 2 3 import ( 4 "fmt" 5 "strings" 6 "unicode" 7 "unicode/utf8" 8 ) 9 10 type Valuer interface { 11 Value(name, params, expr string) (any, error) 12 } 13 14 type ValuerHandler func(name, params string) (any, error) 15 16 func (f ValuerHandler) Value(name, params string) (any, error) { return f(name, params) } 17 18 func (s Subs) Eval(valuer Valuer) (any, error) { 19 if len(s) == 1 && s.CountVars() == len(s) { 20 v := s[0].(*SubVar) 21 return valuer.Value(v.Name, v.Params, v.Expr) 22 } 23 24 value := "" 25 for _, sub := range s { 26 switch v := sub.(type) { 27 case *SubTxt: 28 value += v.Val 29 case *SubVar: 30 vv, err := valuer.Value(v.Name, v.Params, v.Expr) 31 if err != nil { 32 return nil, err 33 } 34 value += ToString(vv) 35 } 36 } 37 38 return value, nil 39 } 40 41 type SubTxt struct { 42 Val string 43 } 44 45 func (s SubTxt) IsVar() bool { return false } 46 47 type SubVar struct { 48 Name string 49 Params string 50 Expr string 51 } 52 53 func (s SubVar) IsVar() bool { return true } 54 55 type Sub interface { 56 IsVar() bool 57 } 58 59 type Subs []Sub 60 61 func (s Subs) CountVars() (count int) { 62 for _, sub := range s { 63 if sub.IsVar() { 64 count++ 65 } 66 } 67 68 return 69 } 70 71 func ParseExpr(src string) Subs { 72 s := src 73 var subs []Sub 74 left := "" 75 for { 76 a := strings.Index(s, "@") 77 if a < 0 || a == len(s)-1 { 78 left += s 79 break 80 } 81 82 left += s[:a] 83 84 a++ 85 s = s[a:] 86 if s[0] == '@' { 87 s = s[1:] 88 left += "@" 89 } else if bracket := PairBracket(s[0]); bracket != nil { 90 if rb := strings.IndexByte(s[1:], bracket.Right); rb > 0 { 91 fn := s[1 : rb+1] 92 s = s[rb+2:] 93 94 subLiteral, subVar := parseName(&fn, &left, bracket) 95 if subLiteral != nil { 96 subs = append(subs, subLiteral) 97 } 98 if subVar != nil { 99 subs = append(subs, subVar) 100 } 101 } 102 } else { 103 subLiteral, subVar := parseName(&s, &left, bracket) 104 if subLiteral != nil { 105 subs = append(subs, subLiteral) 106 } 107 if subVar != nil { 108 subs = append(subs, subVar) 109 } 110 } 111 } 112 113 if left != "" { 114 subs = append(subs, &SubTxt{Val: left}) 115 } 116 117 if Subs(subs).CountVars() == 0 { 118 return []Sub{&SubTxt{Val: src}} 119 } 120 121 return subs 122 } 123 124 type Bracket struct { 125 Left byte 126 Right byte 127 } 128 129 func PairBracket(left byte) *Bracket { 130 switch left { 131 case '{': 132 return &Bracket{Left: '{', Right: '}'} 133 case '[': 134 return &Bracket{Left: '[', Right: ']'} 135 case '#', '%', '`': 136 return &Bracket{Left: left, Right: left} 137 case '<': 138 return &Bracket{Left: '<', Right: '>'} 139 } 140 return nil 141 } 142 143 func parseName(s, left *string, bracket *Bracket) (subLiteral, subVar Sub) { 144 original := *s 145 name := "" 146 offset := 0 147 for i, r := range *s { 148 if !validNameRune(r) { 149 name = (*s)[:i] 150 break 151 } 152 offset += utf8.RuneLen(r) 153 } 154 155 nonParam := name == "" && offset == len(*s) 156 if nonParam { 157 name = *s 158 } 159 160 if *left != "" { 161 subLiteral = &SubTxt{Val: *left} 162 *left = "" 163 } 164 165 sv := &SubVar{ 166 Name: name, 167 } 168 subVar = sv 169 170 if !nonParam && offset > 0 && offset < len(*s) { 171 if (*s)[offset] == '(' { 172 if rb := strings.LastIndexByte(*s, ')'); rb > 0 { 173 sv.Params = (*s)[offset+1 : rb] 174 *s = (*s)[rb+1:] 175 sv.Expr = wrap(original[:rb+1], bracket) 176 return 177 } 178 } 179 } 180 181 *s = (*s)[offset:] 182 sv.Expr = wrap(original[:offset], bracket) 183 184 return 185 } 186 187 func wrap(s string, bracket *Bracket) string { 188 if bracket != nil { 189 return "@" + string(bracket.Left) + s + string(bracket.Right) 190 } 191 192 return "@" + s 193 } 194 195 func validNameRune(r int32) bool { 196 return unicode.IsLetter(r) || unicode.IsDigit(r) || unicode.Is(unicode.Han, r) || 197 r == '_' || r == '-' || r == '.' 198 } 199 200 func ToString(value any) string { 201 switch vv := value.(type) { 202 case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64: 203 return fmt.Sprintf("%d", vv) 204 case float32, float64: 205 return fmt.Sprintf("%f", vv) 206 case bool: 207 return fmt.Sprintf("%t", vv) 208 case string: 209 return vv 210 default: 211 vvv := fmt.Sprintf("%v", value) 212 return vvv 213 } 214 }