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  }