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


Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • tools/config.py

    raca97582 ra35b458  
    4242import random
    4343
    44 ARGPOS_RULES = 1
    45 ARGPOS_CHOICE = 2
    46 ARGPOS_PRESET = 3
    47 
    48 RULES_FILE = sys.argv[ARGPOS_RULES]
     44RULES_FILE = sys.argv[1]
    4945MAKEFILE = 'Makefile.config'
    5046MACROS = 'config.h'
    5147PRESETS_DIR = 'defaults'
    52 
    53 class 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
    82 class 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)
    19548
    19649def read_config(fname, config):
     
    20659        inf.close()
    20760
     61def 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
     91def 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
    208133def parse_rules(fname, rules):
    209134        "Parse rules file"
     
    224149
    225150                        cond = res.group(1)
    226                         if cond:
    227                                 cond = CondParser(cond).parse()
    228151                        varname = res.group(2)
    229152                        vartype = res.group(3)
     
    309232                varname, vartype, name, choices, cond = rule
    310233
    311                 if cond and not cond.evaluate(config):
     234                if cond and (not check_condition(cond, config, rules)):
    312235                        continue
    313236
     
    356279
    357280        # First check that this rule would make sense
    358         if cond and not cond.evaluate(config):
    359                 return random_choices(config, rules, start_index + 1)
     281        if cond:
     282                if not check_condition(cond, config, rules):
     283                        return random_choices(config, rules, start_index + 1)
    360284
    361285        # Remember previous choices for backtracking
     
    563487
    564488        for varname, vartype, name, choices, cond in rules:
    565                 if cond and not cond.evaluate(config):
     489                if cond and (not check_condition(cond, config, rules)):
    566490                        continue
    567491
     
    590514        outmk.write('TIMESTAMP_UNIX = %d\n' % timestamp_unix)
    591515        outmc.write('#define TIMESTAMP_UNIX %d\n' % timestamp_unix)
    592         defs += ' "-DTIMESTAMP_UNIX=%d"' % timestamp_unix
     516        defs += ' "-DTIMESTAMP_UNIX=%d"\n' % timestamp_unix
    593517
    594518        outmk.write('TIMESTAMP = %s\n' % timestamp)
    595519        outmc.write('#define TIMESTAMP %s\n' % timestamp)
    596         defs += ' "-DTIMESTAMP=%s"' % timestamp
    597 
    598         outmk.write('%s\n' % defs)
     520        defs += ' "-DTIMESTAMP=%s"\n' % timestamp
     521
     522        outmk.write(defs)
    599523
    600524        outmk.close()
     
    680604        parse_rules(RULES_FILE, rules)
    681605
    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 
    692606        # Input configuration file can be specified on command line
    693607        # otherwise configuration from previous run is used.
    694         if preset is not None:
    695                 profile = parse_profile_name(preset)
     608        if len(sys.argv) >= 4:
     609                profile = parse_profile_name(sys.argv[3])
    696610                read_presets(profile, config)
    697611        elif os.path.exists(MAKEFILE):
     
    699613
    700614        # Default mode: check values and regenerate configuration files
    701         if choice == 'default':
     615        if (len(sys.argv) >= 3) and (sys.argv[2] == 'default'):
    702616                if (infer_verify_choices(config, rules)):
    703617                        preprocess_config(config, rules)
     
    707621        # Hands-off mode: check values and regenerate configuration files,
    708622        # but no interactive fallback
    709         if choice == 'hands-off':
    710                 # We deliberately test this because we do not want
     623        if (len(sys.argv) >= 3) and (sys.argv[2] == 'hands-off'):
     624                # We deliberately test sys.argv >= 4 because we do not want
    711625                # to read implicitly any possible previous run configuration
    712                 if preset is None:
     626                if len(sys.argv) < 4:
    713627                        sys.stderr.write("Configuration error: No presets specified\n")
    714628                        return 2
     
    723637
    724638        # Check mode: only check configuration
    725         if choice == 'check':
     639        if (len(sys.argv) >= 3) and (sys.argv[2] == 'check'):
    726640                if infer_verify_choices(config, rules):
    727641                        return 0
     
    729643
    730644        # Random mode
    731         if choice == 'random':
     645        if (len(sys.argv) == 3) and (sys.argv[2] == 'random'):
    732646                ok = random_choices(config, rules, 0)
    733647                if not ok:
     
    762676                                varname, vartype, name, choices, cond = rule
    763677
    764                                 if cond and not cond.evaluate(config):
     678                                if cond and (not check_condition(cond, config, rules)):
    765679                                        continue
    766680
Note: See TracChangeset for help on using the changeset viewer.