github.com/vektra/tachyon@v0.0.0-20150921164542-0da4f3861aef/expand.go (about) 1 package tachyon 2 3 import ( 4 "bytes" 5 "errors" 6 "fmt" 7 "github.com/vektra/tachyon/lisp" 8 "strings" 9 "unicode" 10 ) 11 12 var cTemplateStart = []byte(`{{`) 13 var cTemplateEnd = []byte(`}}`) 14 var cExprStart = []byte(`$(`) 15 var cExprEnd = []byte(`)`) 16 17 var eUnclosedTemplate = errors.New("Unclosed template") 18 var eUnclosedExpr = errors.New("Unclosed lisp expression") 19 20 func expandTemplates(s Scope, args string) (string, error) { 21 a := []byte(args) 22 23 var buf bytes.Buffer 24 25 for { 26 idx := bytes.Index(a, cTemplateStart) 27 28 if idx == -1 { 29 buf.Write(a) 30 break 31 } 32 33 buf.Write(a[:idx]) 34 35 in := a[idx+2:] 36 37 fin := bytes.Index(in, cTemplateEnd) 38 39 if fin == -1 { 40 return "", eUnclosedTemplate 41 } 42 43 name := bytes.TrimSpace(in[:fin]) 44 45 parts := strings.Split(string(name), ".") 46 47 var ( 48 val Value 49 ok bool 50 ) 51 52 if len(parts) == 1 { 53 val, ok = s.Get(string(name)) 54 } else { 55 cur := parts[0] 56 57 val, ok = s.Get(cur) 58 59 for _, sub := range parts[1:] { 60 m, ok := val.(Map) 61 if !ok { 62 m, ok = val.Read().(Map) 63 if !ok { 64 return "", fmt.Errorf("Variable '%s' is not a Map (%T)", cur, val.Read()) 65 } 66 } 67 68 val, ok = m.Get(sub) 69 if !ok { 70 return "", fmt.Errorf("Variable '%s' has no key '%s'", cur, sub) 71 } 72 cur = sub 73 } 74 } 75 76 if ok { 77 switch val := val.Read().(type) { 78 case int64, int: 79 buf.WriteString(fmt.Sprintf("%d", val)) 80 default: 81 buf.WriteString(fmt.Sprintf("%s", val)) 82 } 83 84 a = in[fin+2:] 85 } else { 86 return "", fmt.Errorf("Undefined variable: %s", string(name)) 87 } 88 } 89 90 return buf.String(), nil 91 } 92 93 func findExprClose(buf []byte) int { 94 opens := 0 95 96 for idx, r := range buf { 97 switch r { 98 case ')': 99 opens-- 100 101 if opens == 0 { 102 return idx 103 } 104 105 case '(': 106 opens++ 107 } 108 } 109 110 return -1 111 } 112 113 func varChar(r rune) bool { 114 if unicode.IsLetter(r) { 115 return true 116 } 117 if unicode.IsDigit(r) { 118 return true 119 } 120 if r == '_' { 121 return true 122 } 123 return false 124 } 125 126 func inferValue(val Value) lisp.Value { 127 switch lv := val.Read().(type) { 128 case int: 129 return lisp.NumberValue(int64(lv)) 130 case int32: 131 return lisp.NumberValue(int64(lv)) 132 case int64: 133 return lisp.NumberValue(lv) 134 case string: 135 return lisp.StringValue(lv) 136 case *Result: 137 return lisp.MapValue(&lispResult{lv}) 138 default: 139 } 140 141 return lisp.StringValue(fmt.Sprintf("%s", val.Read())) 142 } 143 144 type lispResult struct { 145 res *Result 146 } 147 148 func (lr *lispResult) Get(key string) (lisp.Value, bool) { 149 v, ok := lr.res.Get(key) 150 151 if !ok { 152 return lisp.Nil, false 153 } 154 155 return inferValue(v), true 156 } 157 158 type lispInferredScope struct { 159 Scope Scope 160 } 161 162 func (s lispInferredScope) Get(key string) (lisp.Value, bool) { 163 val, ok := s.Scope.Get(key) 164 165 if !ok { 166 return lisp.Nil, false 167 } 168 169 return inferValue(val), true 170 } 171 172 func (s lispInferredScope) Set(key string, v lisp.Value) lisp.Value { 173 s.Scope.Set(key, v.Interface()) 174 return v 175 } 176 177 func (s lispInferredScope) Create(key string, v lisp.Value) lisp.Value { 178 s.Scope.Set(key, v.Interface()) 179 return v 180 } 181 182 var cDollar = []byte(`$`) 183 184 func ExpandVars(s Scope, args string) (string, error) { 185 args, err := expandTemplates(s, args) 186 187 if err != nil { 188 return "", err 189 } 190 191 a := []byte(args) 192 193 var buf bytes.Buffer 194 195 for { 196 idx := bytes.Index(a, cDollar) 197 198 if idx == -1 { 199 buf.Write(a) 200 break 201 } else if a[idx+1] == '(' { 202 buf.Write(a[:idx]) 203 204 in := a[idx+1:] 205 206 fin := findExprClose(in) 207 208 if fin == -1 { 209 return "", eUnclosedExpr 210 } 211 212 sexp := in[:fin+1] 213 214 ls := lispInferredScope{s} 215 216 val, err := lisp.EvalString(string(sexp), ls) 217 218 if err != nil { 219 return "", err 220 } 221 222 buf.WriteString(val.String()) 223 a = in[fin+1:] 224 } else { 225 buf.Write(a[:idx]) 226 227 in := a[idx+1:] 228 229 fin := 0 230 231 for fin < len(in) { 232 if !varChar(rune(in[fin])) { 233 break 234 } 235 fin++ 236 } 237 238 if val, ok := s.Get(string(in[:fin])); ok { 239 switch val := val.Read().(type) { 240 case int64, int: 241 buf.WriteString(fmt.Sprintf("%d", val)) 242 default: 243 buf.WriteString(fmt.Sprintf("%s", val)) 244 } 245 246 a = in[fin:] 247 } else { 248 return "", fmt.Errorf("Undefined variable: %s", string(in[:fin])) 249 } 250 } 251 } 252 253 return buf.String(), nil 254 }