Changes in tools/config.py [a35b458:aca97582] in mainline


Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • tools/config.py

    ra35b458 raca97582  
    4242import random
    4343
    44 RULES_FILE = sys.argv[1]
     44ARGPOS_RULES = 1
     45ARGPOS_CHOICE = 2
     46ARGPOS_PRESET = 3
     47
     48RULES_FILE = sys.argv[ARGPOS_RULES]
    4549MAKEFILE = 'Makefile.config'
    4650MACROS = 'config.h'
    4751PRESETS_DIR = 'defaults'
     52
     53class BinaryOp:
     54        def __init__(self, operator, left, right):
     55                assert operator in ('&', '|', '=', '!=')
     56
     57                self._operator = operator
     58                self._left = left
     59                self._right = right
     60
     61        def evaluate(self, config):
     62                if self._operator == '&':
     63                        return self._left.evaluate(config) and \
     64                            self._right.evaluate(config)
     65                if self._operator == '|':
     66                        return self._left.evaluate(config) or \
     67                            self._right.evaluate(config)
     68
     69                # '=' or '!='
     70                if not self._left in config:
     71                        config_val = ''
     72                else:
     73                        config_val = config[self._left]
     74                        if config_val == '*':
     75                                config_val = 'y'
     76
     77                if self._operator == '=':
     78                        return self._right == config_val
     79                return self._right != config_val
     80
     81# Expression parser
     82class CondParser:
     83        TOKEN_EOE = 0
     84        TOKEN_SPECIAL = 1
     85        TOKEN_STRING = 2
     86
     87        def __init__(self, text):
     88                self._text = text
     89
     90        def parse(self):
     91                self._position = -1
     92                self._next_char()
     93                self._next_token()
     94
     95                res = self._parse_expr()
     96                if self._token_type != self.TOKEN_EOE:
     97                        self._error("Expected end of expression")
     98                return res
     99
     100        def _next_char(self):
     101                self._position += 1
     102                if self._position >= len(self._text):
     103                        self._char = None
     104                else:
     105                        self._char = self._text[self._position]
     106                self._is_special_char = self._char in \
     107                    ('&', '|', '=', '!', '(', ')')
     108
     109        def _error(self, msg):
     110                raise RuntimeError("Error parsing expression: %s:\n%s\n%s^" %
     111                    (msg, self._text, " " * self._token_position))
     112
     113        def _next_token(self):
     114                self._token_position = self._position
     115
     116                # End of expression
     117                if self._char == None:
     118                        self._token = None
     119                        self._token_type = self.TOKEN_EOE
     120                        return
     121
     122                # '&', '|', '=', '!=', '(', ')'
     123                if self._is_special_char:
     124                        self._token = self._char
     125                        self._next_char()
     126                        if self._token == '!':
     127                                if self._char != '=':
     128                                        self._error("Expected '='")
     129                                self._token += self._char
     130                                self._next_char()
     131                        self._token_type = self.TOKEN_SPECIAL
     132                        return
     133
     134                # <var> or <val>
     135                self._token = ''
     136                self._token_type = self.TOKEN_STRING
     137                while True:
     138                        self._token += self._char
     139                        self._next_char()
     140                        if self._is_special_char or self._char == None:
     141                                break
     142
     143        def _parse_expr(self):
     144                """ <expr> ::= <or_expr> ('&' <or_expr>)* """
     145
     146                left = self._parse_or_expr()
     147                while self._token == '&':
     148                        self._next_token()
     149                        left = BinaryOp('&', left, self._parse_or_expr())
     150                return left
     151
     152        def _parse_or_expr(self):
     153                """ <or_expr> ::= <factor> ('|' <factor>)* """
     154
     155                left = self._parse_factor()
     156                while self._token == '|':
     157                        self._next_token()
     158                        left = BinaryOp('|', left, self._parse_factor())
     159                return left
     160
     161        def _parse_factor(self):
     162                """ <factor> ::= <var> <cond> | '(' <expr> ')' """
     163
     164                if self._token == '(':
     165                        self._next_token()
     166                        res = self._parse_expr()
     167                        if self._token != ')':
     168                                self._error("Expected ')'")
     169                        self._next_token()
     170                        return res
     171
     172                if self._token_type == self.TOKEN_STRING:
     173                        var = self._token
     174                        self._next_token()
     175                        return self._parse_cond(var)
     176
     177                self._error("Expected '(' or <var>")
     178
     179        def _parse_cond(self, var):
     180                """ <cond> ::= '=' <val> | '!=' <val> """
     181
     182                if self._token not in ('=', '!='):
     183                        self._error("Expected '=' or '!='")
     184
     185                oper = self._token
     186                self._next_token()
     187
     188                if self._token_type != self.TOKEN_STRING:
     189                        self._error("Expected <val>")
     190
     191                val = self._token
     192                self._next_token()
     193
     194                return BinaryOp(oper, var, val)
    48195
    49196def read_config(fname, config):
     
    59206        inf.close()
    60207
    61 def check_condition(text, config, rules):
    62         "Check that the condition specified on input line is True (only CNF and DNF is supported)"
    63 
    64         ctype = 'cnf'
    65 
    66         if (')|' in text) or ('|(' in text):
    67                 ctype = 'dnf'
    68 
    69         if ctype == 'cnf':
    70                 conds = text.split('&')
    71         else:
    72                 conds = text.split('|')
    73 
    74         for cond in conds:
    75                 if cond.startswith('(') and cond.endswith(')'):
    76                         cond = cond[1:-1]
    77 
    78                 inside = check_inside(cond, config, ctype)
    79 
    80                 if (ctype == 'cnf') and (not inside):
    81                         return False
    82 
    83                 if (ctype == 'dnf') and inside:
    84                         return True
    85 
    86         if ctype == 'cnf':
    87                 return True
    88 
    89         return False
    90 
    91 def check_inside(text, config, ctype):
    92         "Check for condition"
    93 
    94         if ctype == 'cnf':
    95                 conds = text.split('|')
    96         else:
    97                 conds = text.split('&')
    98 
    99         for cond in conds:
    100                 res = re.match(r'^(.*?)(!?=)(.*)$', cond)
    101                 if not res:
    102                         raise RuntimeError("Invalid condition: %s" % cond)
    103 
    104                 condname = res.group(1)
    105                 oper = res.group(2)
    106                 condval = res.group(3)
    107 
    108                 if not condname in config:
    109                         varval = ''
    110                 else:
    111                         varval = config[condname]
    112                         if (varval == '*'):
    113                                 varval = 'y'
    114 
    115                 if ctype == 'cnf':
    116                         if (oper == '=') and (condval == varval):
    117                                 return True
    118 
    119                         if (oper == '!=') and (condval != varval):
    120                                 return True
    121                 else:
    122                         if (oper == '=') and (condval != varval):
    123                                 return False
    124 
    125                         if (oper == '!=') and (condval == varval):
    126                                 return False
    127 
    128         if ctype == 'cnf':
    129                 return False
    130 
    131         return True
    132 
    133208def parse_rules(fname, rules):
    134209        "Parse rules file"
     
    149224
    150225                        cond = res.group(1)
     226                        if cond:
     227                                cond = CondParser(cond).parse()
    151228                        varname = res.group(2)
    152229                        vartype = res.group(3)
     
    232309                varname, vartype, name, choices, cond = rule
    233310
    234                 if cond and (not check_condition(cond, config, rules)):
     311                if cond and not cond.evaluate(config):
    235312                        continue
    236313
     
    279356
    280357        # First check that this rule would make sense
    281         if cond:
    282                 if not check_condition(cond, config, rules):
    283                         return random_choices(config, rules, start_index + 1)
     358        if cond and not cond.evaluate(config):
     359                return random_choices(config, rules, start_index + 1)
    284360
    285361        # Remember previous choices for backtracking
     
    487563
    488564        for varname, vartype, name, choices, cond in rules:
    489                 if cond and (not check_condition(cond, config, rules)):
     565                if cond and not cond.evaluate(config):
    490566                        continue
    491567
     
    514590        outmk.write('TIMESTAMP_UNIX = %d\n' % timestamp_unix)
    515591        outmc.write('#define TIMESTAMP_UNIX %d\n' % timestamp_unix)
    516         defs += ' "-DTIMESTAMP_UNIX=%d"\n' % timestamp_unix
     592        defs += ' "-DTIMESTAMP_UNIX=%d"' % timestamp_unix
    517593
    518594        outmk.write('TIMESTAMP = %s\n' % timestamp)
    519595        outmc.write('#define TIMESTAMP %s\n' % timestamp)
    520         defs += ' "-DTIMESTAMP=%s"\n' % timestamp
    521 
    522         outmk.write(defs)
     596        defs += ' "-DTIMESTAMP=%s"' % timestamp
     597
     598        outmk.write('%s\n' % defs)
    523599
    524600        outmk.close()
     
    604680        parse_rules(RULES_FILE, rules)
    605681
     682        if len(sys.argv) > ARGPOS_CHOICE:
     683                choice = sys.argv[ARGPOS_CHOICE]
     684        else:
     685                choice = None
     686
     687        if len(sys.argv) > ARGPOS_PRESET:
     688                preset = sys.argv[ARGPOS_PRESET]
     689        else:
     690                preset = None
     691
    606692        # Input configuration file can be specified on command line
    607693        # otherwise configuration from previous run is used.
    608         if len(sys.argv) >= 4:
    609                 profile = parse_profile_name(sys.argv[3])
     694        if preset is not None:
     695                profile = parse_profile_name(preset)
    610696                read_presets(profile, config)
    611697        elif os.path.exists(MAKEFILE):
     
    613699
    614700        # Default mode: check values and regenerate configuration files
    615         if (len(sys.argv) >= 3) and (sys.argv[2] == 'default'):
     701        if choice == 'default':
    616702                if (infer_verify_choices(config, rules)):
    617703                        preprocess_config(config, rules)
     
    621707        # Hands-off mode: check values and regenerate configuration files,
    622708        # but no interactive fallback
    623         if (len(sys.argv) >= 3) and (sys.argv[2] == 'hands-off'):
    624                 # We deliberately test sys.argv >= 4 because we do not want
     709        if choice == 'hands-off':
     710                # We deliberately test this because we do not want
    625711                # to read implicitly any possible previous run configuration
    626                 if len(sys.argv) < 4:
     712                if preset is None:
    627713                        sys.stderr.write("Configuration error: No presets specified\n")
    628714                        return 2
     
    637723
    638724        # Check mode: only check configuration
    639         if (len(sys.argv) >= 3) and (sys.argv[2] == 'check'):
     725        if choice == 'check':
    640726                if infer_verify_choices(config, rules):
    641727                        return 0
     
    643729
    644730        # Random mode
    645         if (len(sys.argv) == 3) and (sys.argv[2] == 'random'):
     731        if choice == 'random':
    646732                ok = random_choices(config, rules, 0)
    647733                if not ok:
     
    676762                                varname, vartype, name, choices, cond = rule
    677763
    678                                 if cond and (not check_condition(cond, config, rules)):
     764                                if cond and not cond.evaluate(config):
    679765                                        continue
    680766
Note: See TracChangeset for help on using the changeset viewer.