|
| 1 | +/** |
| 2 | + * Licensed to the Apache Software Foundation (ASF) under one |
| 3 | + * or more contributor license agreements. See the NOTICE file |
| 4 | + * distributed with this work for additional information |
| 5 | + * regarding copyright ownership. The ASF licenses this file |
| 6 | + * to you under the Apache License, Version 2.0 (the |
| 7 | + * "License"); you may not use this file except in compliance |
| 8 | + * with the License. You may obtain a copy of the License at |
| 9 | + * |
| 10 | + * http://www.apache.org/licenses/LICENSE-2.0 |
| 11 | + * |
| 12 | + * Unless required by applicable law or agreed to in writing, |
| 13 | + * software distributed under the License is distributed on an |
| 14 | + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| 15 | + * KIND, either express or implied. See the License for the |
| 16 | + * specific language governing permissions and limitations |
| 17 | + * under the License. |
| 18 | + */ |
| 19 | + |
| 20 | +package syscfg |
| 21 | + |
| 22 | +import ( |
| 23 | + "fmt" |
| 24 | + "go/ast" |
| 25 | + "go/parser" |
| 26 | + "go/token" |
| 27 | + "mynewt.apache.org/newt/util" |
| 28 | + "strconv" |
| 29 | +) |
| 30 | + |
| 31 | +type Eval struct { |
| 32 | + cfg *Cfg |
| 33 | +} |
| 34 | + |
| 35 | +func int2bool(x int) bool { |
| 36 | + return x != 0 |
| 37 | +} |
| 38 | + |
| 39 | +func bool2int(b bool) int { |
| 40 | + if b { |
| 41 | + return 1 |
| 42 | + } |
| 43 | + |
| 44 | + return 0 |
| 45 | +} |
| 46 | + |
| 47 | +func (cfg *Cfg) exists(name string) bool { |
| 48 | + _, ok := cfg.Settings[name] |
| 49 | + |
| 50 | + return ok |
| 51 | +} |
| 52 | + |
| 53 | +func (cfg *Cfg) exprEvalLiteral(e *ast.BasicLit) (interface{}, error) { |
| 54 | + kind := e.Kind |
| 55 | + val := e.Value |
| 56 | + |
| 57 | + switch kind { |
| 58 | + case token.INT: |
| 59 | + return strconv.Atoi(val) |
| 60 | + case token.STRING: |
| 61 | + return val, nil |
| 62 | + } |
| 63 | + |
| 64 | + return 0, util.FmtNewtError("Invalid exprEvalLiteral used in expression") |
| 65 | +} |
| 66 | + |
| 67 | +func (cfg *Cfg) exprEvalBinaryExpr(e *ast.BinaryExpr) (int, error) { |
| 68 | + switch e.Op { |
| 69 | + case token.ADD: |
| 70 | + case token.SUB: |
| 71 | + case token.MUL: |
| 72 | + case token.QUO: |
| 73 | + case token.REM: |
| 74 | + case token.LAND: |
| 75 | + case token.LOR: |
| 76 | + case token.EQL: |
| 77 | + case token.LSS: |
| 78 | + case token.GTR: |
| 79 | + case token.NEQ: |
| 80 | + case token.LEQ: |
| 81 | + case token.GEQ: |
| 82 | + default: |
| 83 | + return 0, util.FmtNewtError("Invalid \"%s\" operator in expression", e.Op.String()) |
| 84 | + } |
| 85 | + |
| 86 | + var x interface{} |
| 87 | + var y interface{} |
| 88 | + var err error |
| 89 | + |
| 90 | + x, err = cfg.exprEvalNode(e.X) |
| 91 | + if err != nil { |
| 92 | + return 0, err |
| 93 | + } |
| 94 | + y, err = cfg.exprEvalNode(e.Y) |
| 95 | + if err != nil { |
| 96 | + return 0, err |
| 97 | + } |
| 98 | + |
| 99 | + xv, xok := x.(int) |
| 100 | + yv, yok := y.(int) |
| 101 | + |
| 102 | + if xok != yok { |
| 103 | + return 0, util.FmtNewtError("Mismatched types for \"%s\" operator in expression", e.Op.String()) |
| 104 | + } |
| 105 | + |
| 106 | + ret := 0 |
| 107 | + |
| 108 | + if xok { |
| 109 | + switch e.Op { |
| 110 | + case token.ADD: |
| 111 | + ret = xv + yv |
| 112 | + case token.SUB: |
| 113 | + ret = xv - yv |
| 114 | + case token.MUL: |
| 115 | + ret = xv * yv |
| 116 | + case token.QUO: |
| 117 | + ret = xv / yv |
| 118 | + case token.REM: |
| 119 | + ret = xv % yv |
| 120 | + case token.LAND: |
| 121 | + ret = bool2int(int2bool(xv) && int2bool(yv)) |
| 122 | + case token.LOR: |
| 123 | + ret = bool2int(int2bool(xv) || int2bool(yv)) |
| 124 | + case token.EQL: |
| 125 | + ret = bool2int(xv == yv) |
| 126 | + case token.LSS: |
| 127 | + ret = bool2int(xv < yv) |
| 128 | + case token.GTR: |
| 129 | + ret = bool2int(xv > yv) |
| 130 | + case token.NEQ: |
| 131 | + ret = bool2int(xv != yv) |
| 132 | + case token.LEQ: |
| 133 | + ret = bool2int(xv <= yv) |
| 134 | + case token.GEQ: |
| 135 | + ret = bool2int(xv >= yv) |
| 136 | + } |
| 137 | + } else { |
| 138 | + // Each node is evaluated to int/string only so below assertions |
| 139 | + // should never fail |
| 140 | + switch e.Op { |
| 141 | + case token.EQL: |
| 142 | + ret = bool2int(x.(string) == y.(string)) |
| 143 | + case token.NEQ: |
| 144 | + ret = bool2int(x.(string) != y.(string)) |
| 145 | + default: |
| 146 | + return 0, util.FmtNewtError("Operator \"%s\" not supported for string literals", |
| 147 | + e.Op.String()) |
| 148 | + } |
| 149 | + } |
| 150 | + |
| 151 | + return ret, nil |
| 152 | +} |
| 153 | + |
| 154 | +func (cfg *Cfg) exprEvalUnaryExpr(e *ast.UnaryExpr) (int, error) { |
| 155 | + if e.Op != token.NOT { |
| 156 | + return 0, util.FmtNewtError("Invalid \"%s\" operator in expression", e.Op.String()) |
| 157 | + } |
| 158 | + |
| 159 | + x, err := cfg.exprEvalNode(e.X) |
| 160 | + if err != nil { |
| 161 | + return 0, err |
| 162 | + } |
| 163 | + |
| 164 | + xv, ok := x.(int) |
| 165 | + if !ok { |
| 166 | + return 0, util.FmtNewtError("String literals not applicable for \"%s\" operator", e.Op.String()) |
| 167 | + } |
| 168 | + |
| 169 | + ret := bool2int(!int2bool(xv)) |
| 170 | + |
| 171 | + return ret, nil |
| 172 | +} |
| 173 | + |
| 174 | +func (cfg *Cfg) exprEvalCallExpr(e *ast.CallExpr) (interface{}, error) { |
| 175 | + f := e.Fun.(*ast.Ident) |
| 176 | + expectedArgc := -1 |
| 177 | + minArgc := -1 |
| 178 | + |
| 179 | + switch f.Name { |
| 180 | + case "min", "max": |
| 181 | + expectedArgc = 2 |
| 182 | + case "in_range", "clamp", "ite": |
| 183 | + expectedArgc = 3 |
| 184 | + case "in_set": |
| 185 | + minArgc = 2 |
| 186 | + default: |
| 187 | + return 0, util.FmtNewtError("Invalid function in expression: \"%s\"", f.Name) |
| 188 | + } |
| 189 | + |
| 190 | + argc := len(e.Args) |
| 191 | + |
| 192 | + if expectedArgc > 0 && argc != expectedArgc { |
| 193 | + return 0, util.FmtNewtError("Invalid number of arguments for \"%s\": expected %d, got %d", |
| 194 | + f.Name, expectedArgc, argc) |
| 195 | + } |
| 196 | + |
| 197 | + if minArgc > 0 && argc < minArgc { |
| 198 | + return 0, util.FmtNewtError("Invalid number of arguments for \"%s\": expected at least %d, got %d", |
| 199 | + f.Name, minArgc, argc) |
| 200 | + } |
| 201 | + |
| 202 | + argv := []interface{}{} |
| 203 | + argvs := []string{} |
| 204 | + for _, node := range e.Args { |
| 205 | + arg, err := cfg.exprEvalNode(node) |
| 206 | + if err != nil { |
| 207 | + return 0, err |
| 208 | + } |
| 209 | + |
| 210 | + argv = append(argv, arg) |
| 211 | + argvs = append(argvs, fmt.Sprintf("%v", arg)) |
| 212 | + } |
| 213 | + |
| 214 | + var ret interface{} |
| 215 | + |
| 216 | + switch f.Name { |
| 217 | + case "min": |
| 218 | + a, ok1 := argv[0].(int) |
| 219 | + b, ok2 := argv[1].(int) |
| 220 | + if !ok1 || !ok2 { |
| 221 | + return 0, util.FmtNewtError("Invalid argument type for \"%s\"", f.Name) |
| 222 | + } |
| 223 | + ret = util.Min(a, b) |
| 224 | + case "max": |
| 225 | + a, ok1 := argv[0].(int) |
| 226 | + b, ok2 := argv[1].(int) |
| 227 | + if !ok1 || !ok2 { |
| 228 | + return 0, util.FmtNewtError("Invalid argument type for \"%s\"", f.Name) |
| 229 | + } |
| 230 | + ret = util.Max(a, b) |
| 231 | + case "clamp": |
| 232 | + v, ok1 := argv[0].(int) |
| 233 | + a, ok2 := argv[1].(int) |
| 234 | + b, ok3 := argv[2].(int) |
| 235 | + if !ok1 || !ok2 || !ok3 { |
| 236 | + return 0, util.FmtNewtError("Invalid argument type for \"%s\"", f.Name) |
| 237 | + } |
| 238 | + if v < a { |
| 239 | + ret = a |
| 240 | + } else if v > b { |
| 241 | + ret = b |
| 242 | + } else { |
| 243 | + ret = v |
| 244 | + } |
| 245 | + case "ite": |
| 246 | + v, ok1 := argv[0].(int) |
| 247 | + if !ok1 { |
| 248 | + return 0, util.FmtNewtError("Invalid argument type for \"%s\"", f.Name) |
| 249 | + } |
| 250 | + if v != 0 { |
| 251 | + ret = argv[1] |
| 252 | + } else { |
| 253 | + ret = argv[2] |
| 254 | + } |
| 255 | + case "in_range": |
| 256 | + v, ok1 := argv[0].(int) |
| 257 | + a, ok2 := argv[1].(int) |
| 258 | + b, ok3 := argv[2].(int) |
| 259 | + if !ok1 || !ok2 || !ok3 { |
| 260 | + return 0, util.FmtNewtError("Invalid argument type for \"%s\"", f.Name) |
| 261 | + } |
| 262 | + ret = bool2int(v >= a && v <= b) |
| 263 | + case "in_set": |
| 264 | + m := make(map[interface{}]struct{}) |
| 265 | + for _, arg := range argv[1:] { |
| 266 | + m[arg] = struct{}{} |
| 267 | + } |
| 268 | + _, ok := m[argv[0]] |
| 269 | + ret = bool2int(ok) |
| 270 | + } |
| 271 | + |
| 272 | + return ret, nil |
| 273 | +} |
| 274 | + |
| 275 | +func (cfg *Cfg) exprEvalIdentifier(e *ast.Ident) (interface{}, error) { |
| 276 | + name := e.Name |
| 277 | + |
| 278 | + entry, ok := cfg.Settings[name] |
| 279 | + if !ok { |
| 280 | + return 0, util.FmtNewtError("Undefined identifier referenced: %s", name) |
| 281 | + } |
| 282 | + |
| 283 | + var val interface{} |
| 284 | + var err error |
| 285 | + |
| 286 | + switch entry.EvalState { |
| 287 | + case CFG_EVAL_STATE_NONE: |
| 288 | + entry, err = cfg.evalEntry(entry) |
| 289 | + val = entry.EvalValue |
| 290 | + case CFG_EVAL_STATE_RUNNING: |
| 291 | + err = util.FmtNewtError("Circular identifier dependency in expression") |
| 292 | + case CFG_EVAL_STATE_SUCCESS: |
| 293 | + val = entry.EvalValue |
| 294 | + case CFG_EVAL_STATE_FAILED: |
| 295 | + err = util.FmtNewtError("") |
| 296 | + } |
| 297 | + |
| 298 | + return val, err |
| 299 | +} |
| 300 | + |
| 301 | +func (cfg *Cfg) exprEvalNode(node ast.Node) (interface{}, error) { |
| 302 | + switch e := node.(type) { |
| 303 | + case *ast.BasicLit: |
| 304 | + return cfg.exprEvalLiteral(e) |
| 305 | + case *ast.BinaryExpr: |
| 306 | + return cfg.exprEvalBinaryExpr(e) |
| 307 | + case *ast.UnaryExpr: |
| 308 | + return cfg.exprEvalUnaryExpr(e) |
| 309 | + case *ast.CallExpr: |
| 310 | + return cfg.exprEvalCallExpr(e) |
| 311 | + case *ast.Ident: |
| 312 | + return cfg.exprEvalIdentifier(e) |
| 313 | + case *ast.ParenExpr: |
| 314 | + return cfg.exprEvalNode(e.X) |
| 315 | + } |
| 316 | + |
| 317 | + return 0, util.FmtNewtError("Invalid token in expression") |
| 318 | +} |
| 319 | + |
| 320 | +func (cfg *Cfg) evalEntry(entry CfgEntry) (CfgEntry, error) { |
| 321 | + name := entry.Name |
| 322 | + |
| 323 | + if entry.EvalState != CFG_EVAL_STATE_NONE { |
| 324 | + panic("This should never happen :>") |
| 325 | + } |
| 326 | + |
| 327 | + entry.EvalState = CFG_EVAL_STATE_RUNNING |
| 328 | + cfg.Settings[name] = entry |
| 329 | + |
| 330 | + entry.EvalOrigValue = entry.Value |
| 331 | + |
| 332 | + node, _ := parser.ParseExpr(entry.Value) |
| 333 | + newVal, err := cfg.exprEvalNode(node) |
| 334 | + if err != nil { |
| 335 | + entry.EvalState = CFG_EVAL_STATE_FAILED |
| 336 | + entry.EvalError = err |
| 337 | + cfg.Settings[entry.Name] = entry |
| 338 | + cfg.InvalidExpressions[entry.Name] = struct{}{} |
| 339 | + err = util.FmtNewtError("") |
| 340 | + return entry, err |
| 341 | + } |
| 342 | + |
| 343 | + switch val := newVal.(type) { |
| 344 | + case int: |
| 345 | + entry.EvalValue = val |
| 346 | + entry.Value = strconv.Itoa(val) |
| 347 | + case string: |
| 348 | + entry.EvalValue = val |
| 349 | + entry.Value = val |
| 350 | + default: |
| 351 | + panic("This should never happen :>") |
| 352 | + } |
| 353 | + |
| 354 | + entry.EvalState = CFG_EVAL_STATE_SUCCESS |
| 355 | + cfg.Settings[entry.Name] = entry |
| 356 | + |
| 357 | + return entry, nil |
| 358 | +} |
| 359 | + |
| 360 | +func (cfg *Cfg) Evaluate(name string) { |
| 361 | + entry := cfg.Settings[name] |
| 362 | + |
| 363 | + switch entry.EvalState { |
| 364 | + case CFG_EVAL_STATE_NONE: |
| 365 | + cfg.evalEntry(entry) |
| 366 | + case CFG_EVAL_STATE_RUNNING: |
| 367 | + panic("This should never happen :>") |
| 368 | + case CFG_EVAL_STATE_SUCCESS: |
| 369 | + // Already evaluated |
| 370 | + case CFG_EVAL_STATE_FAILED: |
| 371 | + // Already evaluated |
| 372 | + } |
| 373 | +} |
0 commit comments