github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/pkg/kconfig/expr.go (about) 1 // Copyright 2020 syzkaller project authors. All rights reserved. 2 // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 4 package kconfig 5 6 import ( 7 "fmt" 8 ) 9 10 // expr represents an arbitrary kconfig expression used in "depends on", "visible if", "if", etc. 11 // Currently we can only extract dependent symbols from expressions. 12 type expr interface { 13 String() string 14 collectDeps(map[string]bool) 15 } 16 17 type exprShell struct { 18 cmd string 19 } 20 21 func (ex *exprShell) String() string { 22 return "$" + ex.cmd 23 } 24 25 func (ex *exprShell) collectDeps(deps map[string]bool) { 26 } 27 28 type exprNot struct { 29 ex expr 30 } 31 32 func (ex *exprNot) String() string { 33 return fmt.Sprintf("!(%v)", ex.ex) 34 } 35 36 func (ex *exprNot) collectDeps(deps map[string]bool) { 37 } 38 39 type exprIdent struct { 40 name string 41 } 42 43 func (ex *exprIdent) String() string { 44 return ex.name 45 } 46 47 func (ex *exprIdent) collectDeps(deps map[string]bool) { 48 deps[ex.name] = true 49 } 50 51 type exprString struct { 52 val string 53 } 54 55 func (ex *exprString) String() string { 56 return fmt.Sprintf("%q", ex.val) 57 } 58 59 func (ex *exprString) collectDeps(deps map[string]bool) { 60 } 61 62 type exprBin struct { 63 op binOp 64 lex expr 65 rex expr 66 } 67 68 type binOp int 69 70 const ( 71 opNop binOp = iota 72 opAnd 73 opOr 74 opEq 75 opNe 76 opLt 77 opLe 78 opGt 79 opGe 80 ) 81 82 func (op binOp) String() string { 83 switch op { 84 case opAnd: 85 return "&&" 86 case opOr: 87 return "||" 88 case opEq: 89 return "=" 90 case opNe: 91 return "!=" 92 case opLt: 93 return "<" 94 case opLe: 95 return "<=" 96 case opGt: 97 return ">" 98 case opGe: 99 return ">=" 100 default: 101 return fmt.Sprintf("???(%v)", int(op)) 102 } 103 } 104 105 func (ex *exprBin) String() string { 106 return fmt.Sprintf("(%v %v %v)", ex.lex, ex.op, ex.rex) 107 } 108 109 func (ex *exprBin) collectDeps(deps map[string]bool) { 110 ex.lex.collectDeps(deps) 111 ex.rex.collectDeps(deps) 112 } 113 114 func exprAnd(lex, rex expr) expr { 115 if lex == nil { 116 return rex 117 } 118 if rex == nil { 119 return lex 120 } 121 return &exprBin{ 122 op: opAnd, 123 lex: lex, 124 rex: rex, 125 } 126 } 127 128 // Recursive-descent parsing with strict precedence levels. 129 // See kconfig docs for reference: 130 // https://www.kernel.org/doc/html/latest/kbuild/kconfig-language.html#menu-dependencies 131 // The doc claims that all operators have different precedence levels, 132 // e.g. '<' has higher precedence than '>' rather than being left-associative with the same precedence. 133 // This is somewhat strange semantics and here it is implemented as simply being left-associative. 134 // For now it does not matter since we do not evaluate expressions. 135 func (p *parser) parseExpr() expr { 136 ex := p.parseExprAnd() 137 for p.TryConsume("||") { 138 ex = &exprBin{ 139 op: opOr, 140 lex: ex, 141 rex: p.parseExprAnd(), 142 } 143 } 144 return ex 145 } 146 147 func (p *parser) parseExprAnd() expr { 148 ex := p.parseExprCmp() 149 for p.TryConsume("&&") { 150 ex = &exprBin{ 151 op: opAnd, 152 lex: ex, 153 rex: p.parseExprCmp(), 154 } 155 } 156 return ex 157 } 158 159 func (p *parser) parseExprCmp() expr { 160 ex := p.parseExprTerm() 161 for { 162 op := opNop 163 switch { 164 case p.TryConsume("="): 165 op = opEq 166 case p.TryConsume("!="): 167 op = opNe 168 case p.TryConsume("<="): 169 op = opLe 170 case p.TryConsume(">="): 171 op = opGe 172 case p.TryConsume("<"): 173 op = opLt 174 case p.TryConsume(">"): 175 op = opGt 176 } 177 if op == opNop { 178 break 179 } 180 ex = &exprBin{ 181 op: op, 182 lex: ex, 183 rex: p.parseExprTerm(), 184 } 185 } 186 return ex 187 } 188 189 func (p *parser) parseExprTerm() expr { 190 if p.TryConsume("$") { 191 return &exprShell{ 192 cmd: p.Shell(), 193 } 194 } 195 if str, ok := p.TryQuotedString(); ok { 196 return &exprString{ 197 val: str, 198 } 199 } 200 if p.TryConsume("!") { 201 return &exprNot{ 202 ex: p.parseExprTerm(), 203 } 204 } 205 if p.TryConsume("(") { 206 ex := p.parseExpr() 207 p.MustConsume(")") 208 return ex 209 } 210 return &exprIdent{ 211 name: p.Ident(), 212 } 213 }