xref: /openbmc/u-boot/tools/buildman/kconfiglib.py (revision 83d290c56fab2d38cd1ab4c4cc7099559c1d5046)
19170818aSMasahiro Yamada# SPDX-License-Identifier: ISC
2f219e013SMasahiro Yamada#
3f219e013SMasahiro Yamada# Author: Ulf Magnusson
4f219e013SMasahiro Yamada#   https://github.com/ulfalizer/Kconfiglib
5f219e013SMasahiro Yamada
6f219e013SMasahiro Yamada# This is Kconfiglib, a Python library for scripting, debugging, and extracting
7f219e013SMasahiro Yamada# information from Kconfig-based configuration systems. To view the
8f219e013SMasahiro Yamada# documentation, run
9f219e013SMasahiro Yamada#
10f219e013SMasahiro Yamada#  $ pydoc kconfiglib
11f219e013SMasahiro Yamada#
12f219e013SMasahiro Yamada# or, if you prefer HTML,
13f219e013SMasahiro Yamada#
14f219e013SMasahiro Yamada#  $ pydoc -w kconfiglib
15f219e013SMasahiro Yamada#
16f219e013SMasahiro Yamada# The examples/ subdirectory contains examples, to be run with e.g.
17f219e013SMasahiro Yamada#
18f219e013SMasahiro Yamada#  $ make scriptconfig SCRIPT=Kconfiglib/examples/print_tree.py
19f219e013SMasahiro Yamada#
20f219e013SMasahiro Yamada# Look in testsuite.py for the test suite.
21f219e013SMasahiro Yamada
22f219e013SMasahiro Yamada"""
23f219e013SMasahiro YamadaKconfiglib is a Python library for scripting and extracting information from
24f219e013SMasahiro YamadaKconfig-based configuration systems. Features include the following:
25f219e013SMasahiro Yamada
26f219e013SMasahiro Yamada - Symbol values and properties can be looked up and values assigned
27f219e013SMasahiro Yamada   programmatically.
28f219e013SMasahiro Yamada - .config files can be read and written.
29f219e013SMasahiro Yamada - Expressions can be evaluated in the context of a Kconfig configuration.
30f219e013SMasahiro Yamada - Relations between symbols can be quickly determined, such as finding all
31f219e013SMasahiro Yamada   symbols that reference a particular symbol.
32f219e013SMasahiro Yamada - Highly compatible with the scripts/kconfig/*conf utilities. The test suite
33f219e013SMasahiro Yamada   automatically compares outputs between Kconfiglib and the C implementation
34f219e013SMasahiro Yamada   for a large number of cases.
35f219e013SMasahiro Yamada
36f219e013SMasahiro YamadaFor the Linux kernel, scripts are run using
37f219e013SMasahiro Yamada
3890c36d8aSUlf Magnusson $ make scriptconfig [ARCH=<arch>] SCRIPT=<path to script> [SCRIPT_ARG=<arg>]
39f219e013SMasahiro Yamada
4090c36d8aSUlf MagnussonUsing the 'scriptconfig' target ensures that required environment variables
4190c36d8aSUlf Magnusson(SRCARCH, ARCH, srctree, KERNELVERSION, etc.) are set up correctly.
42f219e013SMasahiro Yamada
4390c36d8aSUlf MagnussonScripts receive the name of the Kconfig file to load in sys.argv[1]. As of
4490c36d8aSUlf MagnussonLinux 4.1.0-rc5, this is always "Kconfig" from the kernel top-level directory.
4590c36d8aSUlf MagnussonIf an argument is provided with SCRIPT_ARG, it appears as sys.argv[2].
46f219e013SMasahiro Yamada
47f219e013SMasahiro YamadaTo get an interactive Python prompt with Kconfiglib preloaded and a Config
4890c36d8aSUlf Magnussonobject 'c' created, run
49f219e013SMasahiro Yamada
5090c36d8aSUlf Magnusson $ make iscriptconfig [ARCH=<arch>]
51f219e013SMasahiro Yamada
5290c36d8aSUlf MagnussonKconfiglib supports both Python 2 and Python 3. For (i)scriptconfig, the Python
5390c36d8aSUlf Magnussoninterpreter to use can be passed in PYTHONCMD, which defaults to 'python'. PyPy
5490c36d8aSUlf Magnussonworks well too, and might give a nice speedup for long-running jobs.
55f219e013SMasahiro Yamada
5690c36d8aSUlf MagnussonThe examples/ directory contains short example scripts, which can be run with
5790c36d8aSUlf Magnussone.g.
58f219e013SMasahiro Yamada
59f219e013SMasahiro Yamada $ make scriptconfig SCRIPT=Kconfiglib/examples/print_tree.py
60f219e013SMasahiro Yamada
61f219e013SMasahiro Yamadaor
62f219e013SMasahiro Yamada
6390c36d8aSUlf Magnusson $ make scriptconfig SCRIPT=Kconfiglib/examples/help_grep.py SCRIPT_ARG=kernel
64f219e013SMasahiro Yamada
6590c36d8aSUlf Magnussontestsuite.py contains the test suite. See the top of the script for how to run
6690c36d8aSUlf Magnussonit.
67f219e013SMasahiro Yamada
68f219e013SMasahiro YamadaCredits: Written by Ulf "Ulfalizer" Magnusson
69f219e013SMasahiro Yamada
7090c36d8aSUlf MagnussonSend bug reports, suggestions and other feedback to ulfalizer a.t Google's
7190c36d8aSUlf Magnussonemail service. Don't wrestle with internal APIs. Tell me what you need and I
7290c36d8aSUlf Magnussonmight add it in a safe way as a client API instead."""
73f219e013SMasahiro Yamada
74f219e013SMasahiro Yamadaimport os
75*4e1102f6SUlf Magnussonimport platform
76f219e013SMasahiro Yamadaimport re
77f219e013SMasahiro Yamadaimport sys
78f219e013SMasahiro Yamada
7990c36d8aSUlf Magnusson# File layout:
8090c36d8aSUlf Magnusson#
8190c36d8aSUlf Magnusson# Public classes
8290c36d8aSUlf Magnusson# Public functions
8390c36d8aSUlf Magnusson# Internal classes
8490c36d8aSUlf Magnusson# Internal functions
8590c36d8aSUlf Magnusson# Internal global constants
8690c36d8aSUlf Magnusson
8790c36d8aSUlf Magnusson# Line length: 79 columns
8890c36d8aSUlf Magnusson
8990c36d8aSUlf Magnusson#
9090c36d8aSUlf Magnusson# Public classes
9190c36d8aSUlf Magnusson#
9290c36d8aSUlf Magnusson
9390c36d8aSUlf Magnussonclass Config(object):
94f219e013SMasahiro Yamada
95f219e013SMasahiro Yamada    """Represents a Kconfig configuration, e.g. for i386 or ARM. This is the
96f219e013SMasahiro Yamada    set of symbols and other items appearing in the configuration together with
97f219e013SMasahiro Yamada    their values. Creating any number of Config objects -- including for
98f219e013SMasahiro Yamada    different architectures -- is safe; Kconfiglib has no global state."""
99f219e013SMasahiro Yamada
100f219e013SMasahiro Yamada    #
101f219e013SMasahiro Yamada    # Public interface
102f219e013SMasahiro Yamada    #
103f219e013SMasahiro Yamada
10490c36d8aSUlf Magnusson    def __init__(self, filename="Kconfig", base_dir=None, print_warnings=True,
105f219e013SMasahiro Yamada                 print_undef_assign=False):
106f219e013SMasahiro Yamada        """Creates a new Config object, representing a Kconfig configuration.
107f219e013SMasahiro Yamada        Raises Kconfig_Syntax_Error on syntax errors.
108f219e013SMasahiro Yamada
10990c36d8aSUlf Magnusson        filename (default: "Kconfig"): The base Kconfig file of the
11090c36d8aSUlf Magnusson           configuration. For the Linux kernel, you'll probably want "Kconfig"
11190c36d8aSUlf Magnusson           from the top-level directory, as environment variables will make
11290c36d8aSUlf Magnusson           sure the right Kconfig is included from there
11390c36d8aSUlf Magnusson           (arch/<architecture>/Kconfig). If you are using Kconfiglib via 'make
11490c36d8aSUlf Magnusson           scriptconfig', the filename of the base base Kconfig file will be in
11590c36d8aSUlf Magnusson           sys.argv[1].
116f219e013SMasahiro Yamada
11790c36d8aSUlf Magnusson        base_dir (default: None): The base directory relative to which 'source'
11890c36d8aSUlf Magnusson           statements within Kconfig files will work. For the Linux kernel this
11990c36d8aSUlf Magnusson           should be the top-level directory of the kernel tree. $-references
12090c36d8aSUlf Magnusson           to existing environment variables will be expanded.
121f219e013SMasahiro Yamada
12290c36d8aSUlf Magnusson           If None (the default), the environment variable 'srctree' will be
12390c36d8aSUlf Magnusson           used if set, and the current directory otherwise. 'srctree' is set
12490c36d8aSUlf Magnusson           by the Linux makefiles to the top-level kernel directory. A default
12590c36d8aSUlf Magnusson           of "." would not work with an alternative build directory.
126f219e013SMasahiro Yamada
12790c36d8aSUlf Magnusson        print_warnings (default: True): Set to True if warnings related to this
12890c36d8aSUlf Magnusson           configuration should be printed to stderr. This can be changed later
12990c36d8aSUlf Magnusson           with Config.set_print_warnings(). It is provided as a constructor
13090c36d8aSUlf Magnusson           argument since warnings might be generated during parsing.
131f219e013SMasahiro Yamada
13290c36d8aSUlf Magnusson        print_undef_assign (default: False): Set to True if informational
13390c36d8aSUlf Magnusson           messages related to assignments to undefined symbols should be
13490c36d8aSUlf Magnusson           printed to stderr for this configuration. Can be changed later with
135f219e013SMasahiro Yamada           Config.set_print_undef_assign()."""
136f219e013SMasahiro Yamada
137f219e013SMasahiro Yamada        # The set of all symbols, indexed by name (a string)
138f219e013SMasahiro Yamada        self.syms = {}
13990c36d8aSUlf Magnusson        # Python 2/3 compatibility hack. This is the only one needed.
140*4e1102f6SUlf Magnusson        self.syms_iter = self.syms.values if sys.version_info[0] >= 3 else \
141*4e1102f6SUlf Magnusson                         self.syms.itervalues
142f219e013SMasahiro Yamada
143f219e013SMasahiro Yamada        # The set of all defined symbols in the configuration in the order they
144f219e013SMasahiro Yamada        # appear in the Kconfig files. This excludes the special symbols n, m,
145f219e013SMasahiro Yamada        # and y as well as symbols that are referenced but never defined.
146f219e013SMasahiro Yamada        self.kconfig_syms = []
147f219e013SMasahiro Yamada
148f219e013SMasahiro Yamada        # The set of all named choices (yes, choices can have names), indexed
149f219e013SMasahiro Yamada        # by name (a string)
150f219e013SMasahiro Yamada        self.named_choices = {}
151f219e013SMasahiro Yamada
15290c36d8aSUlf Magnusson        # Lists containing all choices, menus and comments in the configuration
15390c36d8aSUlf Magnusson        self.choices = []
15490c36d8aSUlf Magnusson        self.menus = []
15590c36d8aSUlf Magnusson        self.comments = []
15690c36d8aSUlf Magnusson
15790c36d8aSUlf Magnusson        def register_special_symbol(type_, name, val):
158f219e013SMasahiro Yamada            sym = Symbol()
159f219e013SMasahiro Yamada            sym.is_special_ = True
160f219e013SMasahiro Yamada            sym.is_defined_ = True
161f219e013SMasahiro Yamada            sym.config = self
162f219e013SMasahiro Yamada            sym.name = name
16390c36d8aSUlf Magnusson            sym.type = type_
16490c36d8aSUlf Magnusson            sym.cached_val = val
165f219e013SMasahiro Yamada            self.syms[name] = sym
166f219e013SMasahiro Yamada            return sym
167f219e013SMasahiro Yamada
168f219e013SMasahiro Yamada        # The special symbols n, m and y, used as shorthand for "n", "m" and
169f219e013SMasahiro Yamada        # "y"
170f219e013SMasahiro Yamada        self.n = register_special_symbol(TRISTATE, "n", "n")
171f219e013SMasahiro Yamada        self.m = register_special_symbol(TRISTATE, "m", "m")
172f219e013SMasahiro Yamada        self.y = register_special_symbol(TRISTATE, "y", "y")
173f219e013SMasahiro Yamada        # DEFCONFIG_LIST uses this
174*4e1102f6SUlf Magnusson        register_special_symbol(STRING, "UNAME_RELEASE", platform.uname()[2])
175f219e013SMasahiro Yamada
176f219e013SMasahiro Yamada        # The symbol with "option defconfig_list" set, containing a list of
177f219e013SMasahiro Yamada        # default .config files
178f219e013SMasahiro Yamada        self.defconfig_sym = None
179f219e013SMasahiro Yamada
180f219e013SMasahiro Yamada        # See Symbol.get_(src)arch()
181f219e013SMasahiro Yamada        self.arch = os.environ.get("ARCH")
182f219e013SMasahiro Yamada        self.srcarch = os.environ.get("SRCARCH")
183f219e013SMasahiro Yamada
184*4e1102f6SUlf Magnusson        # If you set CONFIG_ in the environment, Kconfig will prefix all symbols
185*4e1102f6SUlf Magnusson        # with its value when saving the configuration, instead of using the default, "CONFIG_".
186*4e1102f6SUlf Magnusson        self.config_prefix = os.environ.get("CONFIG_")
187*4e1102f6SUlf Magnusson        if self.config_prefix is None:
188*4e1102f6SUlf Magnusson            self.config_prefix = "CONFIG_"
189*4e1102f6SUlf Magnusson
190f219e013SMasahiro Yamada        # See Config.__init__(). We need this for get_defconfig_filename().
191f219e013SMasahiro Yamada        self.srctree = os.environ.get("srctree")
192f219e013SMasahiro Yamada        if self.srctree is None:
193f219e013SMasahiro Yamada            self.srctree = "."
194f219e013SMasahiro Yamada
195f219e013SMasahiro Yamada        self.filename = filename
196*4e1102f6SUlf Magnusson        self.base_dir = self.srctree if base_dir is None else \
197*4e1102f6SUlf Magnusson                        os.path.expandvars(base_dir)
198f219e013SMasahiro Yamada
199f219e013SMasahiro Yamada        # The 'mainmenu' text
200f219e013SMasahiro Yamada        self.mainmenu_text = None
201f219e013SMasahiro Yamada
202f219e013SMasahiro Yamada        # The filename of the most recently loaded .config file
203f219e013SMasahiro Yamada        self.config_filename = None
204f219e013SMasahiro Yamada        # The textual header of the most recently loaded .config, uncommented
205f219e013SMasahiro Yamada        self.config_header = None
206f219e013SMasahiro Yamada
207f219e013SMasahiro Yamada        self.print_warnings = print_warnings
208f219e013SMasahiro Yamada        self.print_undef_assign = print_undef_assign
2098639f69aSSimon Glass        self._warnings = []
210f219e013SMasahiro Yamada
211f219e013SMasahiro Yamada        # For parsing routines that stop when finding a line belonging to a
212f219e013SMasahiro Yamada        # different construct, these holds that line and the tokenized version
213f219e013SMasahiro Yamada        # of that line. The purpose is to avoid having to re-tokenize the line,
214f219e013SMasahiro Yamada        # which is inefficient and causes problems when recording references to
215f219e013SMasahiro Yamada        # symbols.
216f219e013SMasahiro Yamada        self.end_line = None
217f219e013SMasahiro Yamada        self.end_line_tokens = None
218f219e013SMasahiro Yamada
219f219e013SMasahiro Yamada        # See the comment in _parse_expr().
22090c36d8aSUlf Magnusson        self._cur_item = None
22190c36d8aSUlf Magnusson        self._line = None
22290c36d8aSUlf Magnusson        self._filename = None
22390c36d8aSUlf Magnusson        self._linenr = None
22490c36d8aSUlf Magnusson        self._transform_m = None
225f219e013SMasahiro Yamada
226f219e013SMasahiro Yamada        # Parse the Kconfig files
227*4e1102f6SUlf Magnusson        self.top_block = []
228*4e1102f6SUlf Magnusson        self._parse_file(filename, None, None, None, self.top_block)
229f219e013SMasahiro Yamada
230f219e013SMasahiro Yamada        # Build Symbol.dep for all symbols
231f219e013SMasahiro Yamada        self._build_dep()
232f219e013SMasahiro Yamada
233f219e013SMasahiro Yamada    def get_arch(self):
234f219e013SMasahiro Yamada        """Returns the value the environment variable ARCH had at the time the
235f219e013SMasahiro Yamada        Config instance was created, or None if ARCH was not set. For the
236f219e013SMasahiro Yamada        kernel, this corresponds to the architecture being built for, with
237f219e013SMasahiro Yamada        values such as "i386" or "mips"."""
238f219e013SMasahiro Yamada        return self.arch
239f219e013SMasahiro Yamada
240f219e013SMasahiro Yamada    def get_srcarch(self):
241f219e013SMasahiro Yamada        """Returns the value the environment variable SRCARCH had at the time
242f219e013SMasahiro Yamada        the Config instance was created, or None if SRCARCH was not set. For
24390c36d8aSUlf Magnusson        the kernel, this corresponds to the particular arch/ subdirectory
24490c36d8aSUlf Magnusson        containing architecture-specific code."""
245f219e013SMasahiro Yamada        return self.srcarch
246f219e013SMasahiro Yamada
247f219e013SMasahiro Yamada    def get_srctree(self):
248f219e013SMasahiro Yamada        """Returns the value the environment variable srctree had at the time
249f219e013SMasahiro Yamada        the Config instance was created, or None if srctree was not defined.
250f219e013SMasahiro Yamada        This variable points to the source directory and is used when building
251f219e013SMasahiro Yamada        in a separate directory."""
252f219e013SMasahiro Yamada        return self.srctree
253f219e013SMasahiro Yamada
25490c36d8aSUlf Magnusson    def get_base_dir(self):
25590c36d8aSUlf Magnusson        """Returns the base directory relative to which 'source' statements
25690c36d8aSUlf Magnusson        will work, passed as an argument to Config.__init__()."""
25790c36d8aSUlf Magnusson        return self.base_dir
25890c36d8aSUlf Magnusson
25990c36d8aSUlf Magnusson    def get_kconfig_filename(self):
26090c36d8aSUlf Magnusson        """Returns the name of the (base) kconfig file this configuration was
26190c36d8aSUlf Magnusson        loaded from."""
26290c36d8aSUlf Magnusson        return self.filename
26390c36d8aSUlf Magnusson
264f219e013SMasahiro Yamada    def get_config_filename(self):
26590c36d8aSUlf Magnusson        """Returns the filename of the most recently loaded configuration file,
26690c36d8aSUlf Magnusson        or None if no configuration has been loaded."""
267f219e013SMasahiro Yamada        return self.config_filename
268f219e013SMasahiro Yamada
26990c36d8aSUlf Magnusson    def get_config_header(self):
27090c36d8aSUlf Magnusson        """Returns the (uncommented) textual header of the .config file most
27190c36d8aSUlf Magnusson        recently loaded with load_config(). Returns None if no .config file has
27290c36d8aSUlf Magnusson        been loaded or if the most recently loaded .config file has no header.
27390c36d8aSUlf Magnusson        The header consists of all lines up to but not including the first line
27490c36d8aSUlf Magnusson        that either
27590c36d8aSUlf Magnusson
27690c36d8aSUlf Magnusson        1. Does not start with "#"
27790c36d8aSUlf Magnusson        2. Has the form "# CONFIG_FOO is not set."
27890c36d8aSUlf Magnusson        """
27990c36d8aSUlf Magnusson        return self.config_header
28090c36d8aSUlf Magnusson
281f219e013SMasahiro Yamada    def get_mainmenu_text(self):
282f219e013SMasahiro Yamada        """Returns the text of the 'mainmenu' statement (with $-references to
283f219e013SMasahiro Yamada        symbols replaced by symbol values), or None if the configuration has no
284f219e013SMasahiro Yamada        'mainmenu' statement."""
285f219e013SMasahiro Yamada        return None if self.mainmenu_text is None else \
286f219e013SMasahiro Yamada          self._expand_sym_refs(self.mainmenu_text)
287f219e013SMasahiro Yamada
288f219e013SMasahiro Yamada    def get_defconfig_filename(self):
289f219e013SMasahiro Yamada        """Returns the name of the defconfig file, which is the first existing
290f219e013SMasahiro Yamada        file in the list given in a symbol having 'option defconfig_list' set.
291f219e013SMasahiro Yamada        $-references to symbols will be expanded ("$FOO bar" -> "foo bar" if
292f219e013SMasahiro Yamada        FOO has the value "foo"). Returns None in case of no defconfig file.
293f219e013SMasahiro Yamada        Setting 'option defconfig_list' on multiple symbols currently results
294f219e013SMasahiro Yamada        in undefined behavior.
295f219e013SMasahiro Yamada
296f219e013SMasahiro Yamada        If the environment variable 'srctree' was set when the Config was
297f219e013SMasahiro Yamada        created, get_defconfig_filename() will first look relative to that
298f219e013SMasahiro Yamada        directory before looking in the current directory; see
2999d01b787SMasahiro Yamada        Config.__init__().
3009d01b787SMasahiro Yamada
30190c36d8aSUlf Magnusson        WARNING: A wart here is that scripts/kconfig/Makefile sometimes uses
30290c36d8aSUlf Magnusson        the --defconfig=<defconfig> option when calling the C implementation of
30390c36d8aSUlf Magnusson        e.g. 'make defconfig'. This option overrides the 'option
30490c36d8aSUlf Magnusson        defconfig_list' symbol, meaning the result from
30590c36d8aSUlf Magnusson        get_defconfig_filename() might not match what 'make defconfig' would
30690c36d8aSUlf Magnusson        use. That probably ought to be worked around somehow, so that this
30790c36d8aSUlf Magnusson        function always gives the "expected" result."""
308f219e013SMasahiro Yamada        if self.defconfig_sym is None:
309f219e013SMasahiro Yamada            return None
31090c36d8aSUlf Magnusson        for filename, cond_expr in self.defconfig_sym.def_exprs:
311f219e013SMasahiro Yamada            if self._eval_expr(cond_expr) == "y":
312f219e013SMasahiro Yamada                filename = self._expand_sym_refs(filename)
313f219e013SMasahiro Yamada                # We first look in $srctree. os.path.join() won't work here as
314f219e013SMasahiro Yamada                # an absolute path in filename would override $srctree.
31590c36d8aSUlf Magnusson                srctree_filename = os.path.normpath(self.srctree + "/" +
31690c36d8aSUlf Magnusson                                                    filename)
317f219e013SMasahiro Yamada                if os.path.exists(srctree_filename):
318f219e013SMasahiro Yamada                    return srctree_filename
319f219e013SMasahiro Yamada                if os.path.exists(filename):
320f219e013SMasahiro Yamada                    return filename
321f219e013SMasahiro Yamada        return None
322f219e013SMasahiro Yamada
323f219e013SMasahiro Yamada    def get_symbol(self, name):
324f219e013SMasahiro Yamada        """Returns the symbol with name 'name', or None if no such symbol
325f219e013SMasahiro Yamada        appears in the configuration. An alternative shorthand is conf[name],
326f219e013SMasahiro Yamada        where conf is a Config instance, though that will instead raise
327f219e013SMasahiro Yamada        KeyError if the symbol does not exist."""
328f219e013SMasahiro Yamada        return self.syms.get(name)
329f219e013SMasahiro Yamada
33090c36d8aSUlf Magnusson    def __getitem__(self, name):
33190c36d8aSUlf Magnusson        """Returns the symbol with name 'name'. Raises KeyError if the symbol
33290c36d8aSUlf Magnusson        does not appear in the configuration."""
33390c36d8aSUlf Magnusson        return self.syms[name]
334f219e013SMasahiro Yamada
335f219e013SMasahiro Yamada    def get_symbols(self, all_symbols=True):
336f219e013SMasahiro Yamada        """Returns a list of symbols from the configuration. An alternative for
337f219e013SMasahiro Yamada        iterating over all defined symbols (in the order of definition) is
338f219e013SMasahiro Yamada
339f219e013SMasahiro Yamada        for sym in config:
340f219e013SMasahiro Yamada            ...
341f219e013SMasahiro Yamada
342f219e013SMasahiro Yamada        which relies on Config implementing __iter__() and is equivalent to
343f219e013SMasahiro Yamada
344f219e013SMasahiro Yamada        for sym in config.get_symbols(False):
345f219e013SMasahiro Yamada            ...
346f219e013SMasahiro Yamada
34790c36d8aSUlf Magnusson        all_symbols (default: True): If True, all symbols -- including special
34890c36d8aSUlf Magnusson           and undefined symbols -- will be included in the result, in an
34990c36d8aSUlf Magnusson           undefined order. If False, only symbols actually defined and not
35090c36d8aSUlf Magnusson           merely referred to in the configuration will be included in the
35190c36d8aSUlf Magnusson           result, and will appear in the order that they are defined within
35290c36d8aSUlf Magnusson           the Kconfig configuration files."""
35390c36d8aSUlf Magnusson        return list(self.syms.values()) if all_symbols else self.kconfig_syms
35490c36d8aSUlf Magnusson
35590c36d8aSUlf Magnusson    def __iter__(self):
35690c36d8aSUlf Magnusson        """Convenience function for iterating over the set of all defined
35790c36d8aSUlf Magnusson        symbols in the configuration, used like
35890c36d8aSUlf Magnusson
35990c36d8aSUlf Magnusson        for sym in conf:
36090c36d8aSUlf Magnusson            ...
36190c36d8aSUlf Magnusson
36290c36d8aSUlf Magnusson        The iteration happens in the order of definition within the Kconfig
36390c36d8aSUlf Magnusson        configuration files. Symbols only referred to but not defined will not
36490c36d8aSUlf Magnusson        be included, nor will the special symbols n, m, and y. If you want to
36590c36d8aSUlf Magnusson        include such symbols as well, see config.get_symbols()."""
36690c36d8aSUlf Magnusson        return iter(self.kconfig_syms)
367f219e013SMasahiro Yamada
368f219e013SMasahiro Yamada    def get_choices(self):
369f219e013SMasahiro Yamada        """Returns a list containing all choice statements in the
370f219e013SMasahiro Yamada        configuration, in the order they appear in the Kconfig files."""
371f219e013SMasahiro Yamada        return self.choices
372f219e013SMasahiro Yamada
373f219e013SMasahiro Yamada    def get_menus(self):
374f219e013SMasahiro Yamada        """Returns a list containing all menus in the configuration, in the
375f219e013SMasahiro Yamada        order they appear in the Kconfig files."""
376f219e013SMasahiro Yamada        return self.menus
377f219e013SMasahiro Yamada
378f219e013SMasahiro Yamada    def get_comments(self):
379f219e013SMasahiro Yamada        """Returns a list containing all comments in the configuration, in the
380f219e013SMasahiro Yamada        order they appear in the Kconfig files."""
381f219e013SMasahiro Yamada        return self.comments
382f219e013SMasahiro Yamada
38390c36d8aSUlf Magnusson    def get_top_level_items(self):
38490c36d8aSUlf Magnusson        """Returns a list containing the items (symbols, menus, choices, and
38590c36d8aSUlf Magnusson        comments) at the top level of the configuration -- that is, all items
38690c36d8aSUlf Magnusson        that do not appear within a menu or choice. The items appear in the
38790c36d8aSUlf Magnusson        same order as within the configuration."""
38890c36d8aSUlf Magnusson        return self.top_block
38990c36d8aSUlf Magnusson
39090c36d8aSUlf Magnusson    def load_config(self, filename, replace=True):
39190c36d8aSUlf Magnusson        """Loads symbol values from a file in the familiar .config format.
39290c36d8aSUlf Magnusson        Equivalent to calling Symbol.set_user_value() to set each of the
39390c36d8aSUlf Magnusson        values.
39490c36d8aSUlf Magnusson
39590c36d8aSUlf Magnusson        "# CONFIG_FOO is not set" within a .config file is treated specially
39690c36d8aSUlf Magnusson        and sets the user value of FOO to 'n'. The C implementation works the
39790c36d8aSUlf Magnusson        same way.
39890c36d8aSUlf Magnusson
39990c36d8aSUlf Magnusson        filename: The .config file to load. $-references to existing
40090c36d8aSUlf Magnusson          environment variables will be expanded. For scripts to work even when
40190c36d8aSUlf Magnusson          an alternative build directory is used with the Linux kernel, you
40290c36d8aSUlf Magnusson          need to refer to the top-level kernel directory with "$srctree".
40390c36d8aSUlf Magnusson
40490c36d8aSUlf Magnusson        replace (default: True): True if the configuration should replace the
4058639f69aSSimon Glass           old configuration; False if it should add to it.
40690c36d8aSUlf Magnusson
4078639f69aSSimon Glass        Returns a list or warnings (hopefully empty)
4088639f69aSSimon Glass        """
4098639f69aSSimon Glass
4108639f69aSSimon Glass        self._warnings = []
411*4e1102f6SUlf Magnusson        # Regular expressions for parsing .config files
412*4e1102f6SUlf Magnusson        _set_re_match = re.compile(r"{}(\w+)=(.*)".format(self.config_prefix)).match
413*4e1102f6SUlf Magnusson        _unset_re_match = re.compile(r"# {}(\w+) is not set".format(self.config_prefix)).match
414*4e1102f6SUlf Magnusson
41590c36d8aSUlf Magnusson        # Put this first so that a missing file doesn't screw up our state
41690c36d8aSUlf Magnusson        filename = os.path.expandvars(filename)
41790c36d8aSUlf Magnusson        line_feeder = _FileFeed(filename)
41890c36d8aSUlf Magnusson
41990c36d8aSUlf Magnusson        self.config_filename = filename
42090c36d8aSUlf Magnusson
42190c36d8aSUlf Magnusson        #
42290c36d8aSUlf Magnusson        # Read header
42390c36d8aSUlf Magnusson        #
42490c36d8aSUlf Magnusson
42590c36d8aSUlf Magnusson        def is_header_line(line):
42690c36d8aSUlf Magnusson            return line is not None and line.startswith("#") and \
42790c36d8aSUlf Magnusson                   not _unset_re_match(line)
42890c36d8aSUlf Magnusson
42990c36d8aSUlf Magnusson        self.config_header = None
43090c36d8aSUlf Magnusson
43190c36d8aSUlf Magnusson        line = line_feeder.peek_next()
43290c36d8aSUlf Magnusson        if is_header_line(line):
43390c36d8aSUlf Magnusson            self.config_header = ""
43490c36d8aSUlf Magnusson            while is_header_line(line_feeder.peek_next()):
43590c36d8aSUlf Magnusson                self.config_header += line_feeder.get_next()[1:]
43690c36d8aSUlf Magnusson            # Remove trailing newline
43790c36d8aSUlf Magnusson            if self.config_header.endswith("\n"):
43890c36d8aSUlf Magnusson                self.config_header = self.config_header[:-1]
43990c36d8aSUlf Magnusson
44090c36d8aSUlf Magnusson        #
44190c36d8aSUlf Magnusson        # Read assignments. Hotspot for some workloads.
44290c36d8aSUlf Magnusson        #
44390c36d8aSUlf Magnusson
44490c36d8aSUlf Magnusson        def warn_override(filename, linenr, name, old_user_val, new_user_val):
44590c36d8aSUlf Magnusson            self._warn('overriding the value of {0}. '
44690c36d8aSUlf Magnusson                       'Old value: "{1}", new value: "{2}".'
44790c36d8aSUlf Magnusson                       .format(name, old_user_val, new_user_val),
44890c36d8aSUlf Magnusson                       filename, linenr)
44990c36d8aSUlf Magnusson
45090c36d8aSUlf Magnusson        # Invalidate everything to keep things simple. It might be possible to
45190c36d8aSUlf Magnusson        # improve performance for the case where multiple configurations are
45290c36d8aSUlf Magnusson        # loaded by only invalidating a symbol (and its dependent symbols) if
45390c36d8aSUlf Magnusson        # the new user value differs from the old. One complication would be
45490c36d8aSUlf Magnusson        # that symbols not mentioned in the .config must lose their user value
45590c36d8aSUlf Magnusson        # when replace = True, which is the usual case.
45690c36d8aSUlf Magnusson        if replace:
45790c36d8aSUlf Magnusson            self.unset_user_values()
45890c36d8aSUlf Magnusson        else:
45990c36d8aSUlf Magnusson            self._invalidate_all()
46090c36d8aSUlf Magnusson
46190c36d8aSUlf Magnusson        while 1:
46290c36d8aSUlf Magnusson            line = line_feeder.get_next()
46390c36d8aSUlf Magnusson            if line is None:
4648639f69aSSimon Glass                return self._warnings
46590c36d8aSUlf Magnusson
46690c36d8aSUlf Magnusson            line = line.rstrip()
46790c36d8aSUlf Magnusson
46890c36d8aSUlf Magnusson            set_match = _set_re_match(line)
46990c36d8aSUlf Magnusson            if set_match:
47090c36d8aSUlf Magnusson                name, val = set_match.groups()
47190c36d8aSUlf Magnusson
47290c36d8aSUlf Magnusson                if val.startswith('"'):
47390c36d8aSUlf Magnusson                    if len(val) < 2 or val[-1] != '"':
47490c36d8aSUlf Magnusson                        _parse_error(line, "malformed string literal",
47590c36d8aSUlf Magnusson                                     line_feeder.filename, line_feeder.linenr)
47690c36d8aSUlf Magnusson                    # Strip quotes and remove escapings. The unescaping
47790c36d8aSUlf Magnusson                    # procedure should be safe since " can only appear as \"
47890c36d8aSUlf Magnusson                    # inside the string.
47990c36d8aSUlf Magnusson                    val = val[1:-1].replace('\\"', '"').replace("\\\\", "\\")
48090c36d8aSUlf Magnusson
48190c36d8aSUlf Magnusson                if name in self.syms:
48290c36d8aSUlf Magnusson                    sym = self.syms[name]
48390c36d8aSUlf Magnusson                    if sym.user_val is not None:
48490c36d8aSUlf Magnusson                        warn_override(line_feeder.filename, line_feeder.linenr,
48590c36d8aSUlf Magnusson                                      name, sym.user_val, val)
48690c36d8aSUlf Magnusson
48790c36d8aSUlf Magnusson                    if sym.is_choice_sym:
48890c36d8aSUlf Magnusson                        user_mode = sym.parent.user_mode
48990c36d8aSUlf Magnusson                        if user_mode is not None and user_mode != val:
49090c36d8aSUlf Magnusson                            self._warn("assignment to {0} changes mode of "
49190c36d8aSUlf Magnusson                                       'containing choice from "{1}" to "{2}".'
49290c36d8aSUlf Magnusson                                       .format(name, val, user_mode),
49390c36d8aSUlf Magnusson                                       line_feeder.filename,
49490c36d8aSUlf Magnusson                                       line_feeder.linenr)
49590c36d8aSUlf Magnusson
49690c36d8aSUlf Magnusson                    sym._set_user_value_no_invalidate(val, True)
49790c36d8aSUlf Magnusson                else:
49890c36d8aSUlf Magnusson                    if self.print_undef_assign:
49990c36d8aSUlf Magnusson                        _stderr_msg('note: attempt to assign the value "{0}" '
50090c36d8aSUlf Magnusson                                    "to the undefined symbol {1}."
50190c36d8aSUlf Magnusson                                    .format(val, name),
50290c36d8aSUlf Magnusson                                    line_feeder.filename, line_feeder.linenr)
50390c36d8aSUlf Magnusson            else:
50490c36d8aSUlf Magnusson                unset_match = _unset_re_match(line)
50590c36d8aSUlf Magnusson                if unset_match:
50690c36d8aSUlf Magnusson                    name = unset_match.group(1)
50790c36d8aSUlf Magnusson                    if name in self.syms:
50890c36d8aSUlf Magnusson                        sym = self.syms[name]
50990c36d8aSUlf Magnusson                        if sym.user_val is not None:
51090c36d8aSUlf Magnusson                            warn_override(line_feeder.filename,
51190c36d8aSUlf Magnusson                                          line_feeder.linenr,
51290c36d8aSUlf Magnusson                                          name, sym.user_val, "n")
51390c36d8aSUlf Magnusson
51490c36d8aSUlf Magnusson                        sym._set_user_value_no_invalidate("n", True)
51590c36d8aSUlf Magnusson
51690c36d8aSUlf Magnusson    def write_config(self, filename, header=None):
51790c36d8aSUlf Magnusson        """Writes out symbol values in the familiar .config format.
51890c36d8aSUlf Magnusson
51990c36d8aSUlf Magnusson        Kconfiglib makes sure the format matches what the C implementation
52090c36d8aSUlf Magnusson        would generate, down to whitespace. This eases testing.
52190c36d8aSUlf Magnusson
52290c36d8aSUlf Magnusson        filename: The filename under which to save the configuration.
52390c36d8aSUlf Magnusson
52490c36d8aSUlf Magnusson        header (default: None): A textual header that will appear at the
52590c36d8aSUlf Magnusson           beginning of the file, with each line commented out automatically.
52690c36d8aSUlf Magnusson           None means no header."""
52790c36d8aSUlf Magnusson
52890c36d8aSUlf Magnusson        for sym in self.syms_iter():
52990c36d8aSUlf Magnusson            sym.already_written = False
53090c36d8aSUlf Magnusson
53190c36d8aSUlf Magnusson        with open(filename, "w") as f:
53290c36d8aSUlf Magnusson            # Write header
53390c36d8aSUlf Magnusson            if header is not None:
534*4e1102f6SUlf Magnusson                f.write(_comment(header) + "\n")
53590c36d8aSUlf Magnusson
53690c36d8aSUlf Magnusson            # Build and write configuration
53790c36d8aSUlf Magnusson            conf_strings = []
53890c36d8aSUlf Magnusson            _make_block_conf(self.top_block, conf_strings.append)
539*4e1102f6SUlf Magnusson            f.write("\n".join(conf_strings) + "\n")
54090c36d8aSUlf Magnusson
541f219e013SMasahiro Yamada    def eval(self, s):
542f219e013SMasahiro Yamada        """Returns the value of the expression 's' -- where 's' is represented
543f219e013SMasahiro Yamada        as a string -- in the context of the configuration. Raises
544f219e013SMasahiro Yamada        Kconfig_Syntax_Error if syntax errors are detected in 's'.
545f219e013SMasahiro Yamada
546f219e013SMasahiro Yamada        For example, if FOO and BAR are tristate symbols at least one of which
547f219e013SMasahiro Yamada        has the value "y", then config.eval("y && (FOO || BAR)") => "y"
548f219e013SMasahiro Yamada
5499d01b787SMasahiro Yamada        This function always yields a tristate value. To get the value of
550f219e013SMasahiro Yamada        non-bool, non-tristate symbols, use Symbol.get_value().
551f219e013SMasahiro Yamada
552f219e013SMasahiro Yamada        The result of this function is consistent with how evaluation works for
553f219e013SMasahiro Yamada        conditional expressions in the configuration as well as in the C
554f219e013SMasahiro Yamada        implementation. "m" and m are rewritten as '"m" && MODULES' and 'm &&
555f219e013SMasahiro Yamada        MODULES', respectively, and a result of "m" will get promoted to "y" if
55690c36d8aSUlf Magnusson        we're running without modules.
55790c36d8aSUlf Magnusson
55890c36d8aSUlf Magnusson        Syntax checking is somewhat lax, partly to be compatible with lax
55990c36d8aSUlf Magnusson        parsing in the C implementation."""
560f219e013SMasahiro Yamada        return self._eval_expr(self._parse_expr(self._tokenize(s, True), # Feed
56190c36d8aSUlf Magnusson                                                None, # Current symbol/choice
562f219e013SMasahiro Yamada                                                s))   # line
563f219e013SMasahiro Yamada
56490c36d8aSUlf Magnusson    def unset_user_values(self):
56590c36d8aSUlf Magnusson        """Resets the values of all symbols, as if Config.load_config() or
56690c36d8aSUlf Magnusson        Symbol.set_user_value() had never been called."""
56790c36d8aSUlf Magnusson        for sym in self.syms_iter():
56890c36d8aSUlf Magnusson            sym._unset_user_value_no_recursive_invalidate()
569f219e013SMasahiro Yamada
570f219e013SMasahiro Yamada    def set_print_warnings(self, print_warnings):
571f219e013SMasahiro Yamada        """Determines whether warnings related to this configuration (for
572f219e013SMasahiro Yamada        things like attempting to assign illegal values to symbols with
573f219e013SMasahiro Yamada        Symbol.set_user_value()) should be printed to stderr.
574f219e013SMasahiro Yamada
57590c36d8aSUlf Magnusson        print_warnings: True if warnings should be printed."""
576f219e013SMasahiro Yamada        self.print_warnings = print_warnings
577f219e013SMasahiro Yamada
578f219e013SMasahiro Yamada    def set_print_undef_assign(self, print_undef_assign):
579f219e013SMasahiro Yamada        """Determines whether informational messages related to assignments to
580f219e013SMasahiro Yamada        undefined symbols should be printed to stderr for this configuration.
581f219e013SMasahiro Yamada
58290c36d8aSUlf Magnusson        print_undef_assign: If True, such messages will be printed."""
583f219e013SMasahiro Yamada        self.print_undef_assign = print_undef_assign
584f219e013SMasahiro Yamada
585f219e013SMasahiro Yamada    def __str__(self):
586f219e013SMasahiro Yamada        """Returns a string containing various information about the Config."""
58790c36d8aSUlf Magnusson        return _lines("Configuration",
58890c36d8aSUlf Magnusson                      "File                                   : " +
58990c36d8aSUlf Magnusson                        self.filename,
59090c36d8aSUlf Magnusson                      "Base directory                         : " +
59190c36d8aSUlf Magnusson                        self.base_dir,
592f219e013SMasahiro Yamada                      "Value of $ARCH at creation time        : " +
593f219e013SMasahiro Yamada                        ("(not set)" if self.arch is None else self.arch),
594f219e013SMasahiro Yamada                      "Value of $SRCARCH at creation time     : " +
59590c36d8aSUlf Magnusson                        ("(not set)" if self.srcarch is None else
59690c36d8aSUlf Magnusson                                        self.srcarch),
597f219e013SMasahiro Yamada                      "Source tree (derived from $srctree;",
59890c36d8aSUlf Magnusson                      "defaults to '.' if $srctree isn't set) : " +
59990c36d8aSUlf Magnusson                        self.srctree,
600f219e013SMasahiro Yamada                      "Most recently loaded .config           : " +
60190c36d8aSUlf Magnusson                        ("(no .config loaded)"
60290c36d8aSUlf Magnusson                          if self.config_filename is None else
603f219e013SMasahiro Yamada                             self.config_filename),
604f219e013SMasahiro Yamada                      "Print warnings                         : " +
60590c36d8aSUlf Magnusson                        BOOL_STR[self.print_warnings],
606f219e013SMasahiro Yamada                      "Print assignments to undefined symbols : " +
60790c36d8aSUlf Magnusson                        BOOL_STR[self.print_undef_assign])
608f219e013SMasahiro Yamada
609f219e013SMasahiro Yamada    #
610f219e013SMasahiro Yamada    # Private methods
611f219e013SMasahiro Yamada    #
612f219e013SMasahiro Yamada
613f219e013SMasahiro Yamada    #
61490c36d8aSUlf Magnusson    # Kconfig parsing
615f219e013SMasahiro Yamada    #
616f219e013SMasahiro Yamada
617*4e1102f6SUlf Magnusson    def _parse_file(self, filename, parent, deps, visible_if_deps, block):
618*4e1102f6SUlf Magnusson        """Parses the Kconfig file 'filename'. Appends the Items in the file
619*4e1102f6SUlf Magnusson        (and any file it sources) to the list passed in the 'block' parameter.
620*4e1102f6SUlf Magnusson        See _parse_block() for the meaning of the parameters."""
621*4e1102f6SUlf Magnusson        self._parse_block(_FileFeed(filename), None, parent, deps,
622*4e1102f6SUlf Magnusson                          visible_if_deps, block)
623f219e013SMasahiro Yamada
624f219e013SMasahiro Yamada    def _parse_block(self, line_feeder, end_marker, parent, deps,
625*4e1102f6SUlf Magnusson                     visible_if_deps, block):
626f219e013SMasahiro Yamada        """Parses a block, which is the contents of either a file or an if,
627*4e1102f6SUlf Magnusson        menu, or choice statement. Appends the Items to the list passed in the
628*4e1102f6SUlf Magnusson        'block' parameter.
629f219e013SMasahiro Yamada
63090c36d8aSUlf Magnusson        line_feeder: A _FileFeed instance feeding lines from a file. The
63190c36d8aSUlf Magnusson          Kconfig language is line-based in practice.
632f219e013SMasahiro Yamada
63390c36d8aSUlf Magnusson        end_marker: The token that ends the block, e.g. T_ENDIF ("endif") for
63490c36d8aSUlf Magnusson           ifs. None for files.
63590c36d8aSUlf Magnusson
63690c36d8aSUlf Magnusson        parent: The enclosing menu or choice, or None if we're at the top
637f219e013SMasahiro Yamada           level.
638f219e013SMasahiro Yamada
63990c36d8aSUlf Magnusson        deps: Dependencies from enclosing menus, choices and ifs.
640f219e013SMasahiro Yamada
64190c36d8aSUlf Magnusson        visible_if_deps (default: None): 'visible if' dependencies from
642f219e013SMasahiro Yamada           enclosing menus.
643f219e013SMasahiro Yamada
644*4e1102f6SUlf Magnusson        block: The list to add items to."""
645f219e013SMasahiro Yamada
646f219e013SMasahiro Yamada        while 1:
647f219e013SMasahiro Yamada            # Do we already have a tokenized line that we determined wasn't
648f219e013SMasahiro Yamada            # part of whatever we were parsing earlier? See comment in
649f219e013SMasahiro Yamada            # Config.__init__().
650f219e013SMasahiro Yamada            if self.end_line is not None:
651f219e013SMasahiro Yamada                line = self.end_line
65290c36d8aSUlf Magnusson                tokens = self.end_line_tokens
65390c36d8aSUlf Magnusson                tokens.unget_all()
654f219e013SMasahiro Yamada
655f219e013SMasahiro Yamada                self.end_line = None
656f219e013SMasahiro Yamada                self.end_line_tokens = None
657f219e013SMasahiro Yamada            else:
658f219e013SMasahiro Yamada                line = line_feeder.get_next()
659f219e013SMasahiro Yamada                if line is None:
660f219e013SMasahiro Yamada                    if end_marker is not None:
66190c36d8aSUlf Magnusson                        raise Kconfig_Syntax_Error("Unexpected end of file {0}"
66290c36d8aSUlf Magnusson                                                 .format(line_feeder.filename))
663*4e1102f6SUlf Magnusson                    return
664f219e013SMasahiro Yamada
66590c36d8aSUlf Magnusson                tokens = self._tokenize(line, False, line_feeder.filename,
66690c36d8aSUlf Magnusson                                        line_feeder.linenr)
667f219e013SMasahiro Yamada
668f219e013SMasahiro Yamada            t0 = tokens.get_next()
66990c36d8aSUlf Magnusson            if t0 is None:
67090c36d8aSUlf Magnusson                continue
671f219e013SMasahiro Yamada
67290c36d8aSUlf Magnusson            # Cases are ordered roughly by frequency, which speeds things up a
67390c36d8aSUlf Magnusson            # bit
674f219e013SMasahiro Yamada
675f219e013SMasahiro Yamada            if t0 == T_CONFIG or t0 == T_MENUCONFIG:
676f219e013SMasahiro Yamada                # The tokenizer will automatically allocate a new Symbol object
677f219e013SMasahiro Yamada                # for any new names it encounters, so we don't need to worry
678f219e013SMasahiro Yamada                # about that here.
679f219e013SMasahiro Yamada                sym = tokens.get_next()
680f219e013SMasahiro Yamada
681f219e013SMasahiro Yamada                # Symbols defined in multiple places get the parent of their
68290c36d8aSUlf Magnusson                # first definition. However, for symbols whose parents are
68390c36d8aSUlf Magnusson                # choice statements, the choice statement takes precedence.
684f219e013SMasahiro Yamada                if not sym.is_defined_ or isinstance(parent, Choice):
685f219e013SMasahiro Yamada                    sym.parent = parent
686f219e013SMasahiro Yamada                sym.is_defined_ = True
687f219e013SMasahiro Yamada
688*4e1102f6SUlf Magnusson                self._parse_properties(line_feeder, sym, deps, visible_if_deps)
689*4e1102f6SUlf Magnusson
690f219e013SMasahiro Yamada                self.kconfig_syms.append(sym)
69190c36d8aSUlf Magnusson                block.append(sym)
692f219e013SMasahiro Yamada
69390c36d8aSUlf Magnusson            elif t0 == T_SOURCE:
69490c36d8aSUlf Magnusson                kconfig_file = tokens.get_next()
69590c36d8aSUlf Magnusson                exp_kconfig_file = self._expand_sym_refs(kconfig_file)
69690c36d8aSUlf Magnusson                f = os.path.join(self.base_dir, exp_kconfig_file)
69790c36d8aSUlf Magnusson                if not os.path.exists(f):
69890c36d8aSUlf Magnusson                    raise IOError('{0}:{1}: sourced file "{2}" (expands to '
69990c36d8aSUlf Magnusson                                  '"{3}") not found. Perhaps base_dir '
70090c36d8aSUlf Magnusson                                  '(argument to Config.__init__(), currently '
70190c36d8aSUlf Magnusson                                  '"{4}") is set to the wrong value.'
70290c36d8aSUlf Magnusson                                  .format(line_feeder.filename,
70390c36d8aSUlf Magnusson                                          line_feeder.linenr,
70490c36d8aSUlf Magnusson                                          kconfig_file, exp_kconfig_file,
70590c36d8aSUlf Magnusson                                          self.base_dir))
70690c36d8aSUlf Magnusson                # Add items to the same block
70790c36d8aSUlf Magnusson                self._parse_file(f, parent, deps, visible_if_deps, block)
708f219e013SMasahiro Yamada
70990c36d8aSUlf Magnusson            elif t0 == end_marker:
71090c36d8aSUlf Magnusson                # We have reached the end of the block
711*4e1102f6SUlf Magnusson                return
712f219e013SMasahiro Yamada
713f219e013SMasahiro Yamada            elif t0 == T_IF:
714f219e013SMasahiro Yamada                # If statements are treated as syntactic sugar for adding
715f219e013SMasahiro Yamada                # dependencies to enclosed items and do not have an explicit
716f219e013SMasahiro Yamada                # object representation.
717f219e013SMasahiro Yamada
71890c36d8aSUlf Magnusson                dep_expr = self._parse_expr(tokens, None, line,
71990c36d8aSUlf Magnusson                                            line_feeder.filename,
72090c36d8aSUlf Magnusson                                            line_feeder.linenr)
72190c36d8aSUlf Magnusson                # Add items to the same block
72290c36d8aSUlf Magnusson                self._parse_block(line_feeder, T_ENDIF, parent,
723f219e013SMasahiro Yamada                                  _make_and(dep_expr, deps),
72490c36d8aSUlf Magnusson                                  visible_if_deps, block)
72590c36d8aSUlf Magnusson
72690c36d8aSUlf Magnusson            elif t0 == T_COMMENT:
72790c36d8aSUlf Magnusson                comment = Comment()
72890c36d8aSUlf Magnusson                comment.config = self
72990c36d8aSUlf Magnusson                comment.parent = parent
73090c36d8aSUlf Magnusson                comment.filename = line_feeder.filename
73190c36d8aSUlf Magnusson                comment.linenr = line_feeder.linenr
73290c36d8aSUlf Magnusson                comment.text = tokens.get_next()
73390c36d8aSUlf Magnusson
73490c36d8aSUlf Magnusson                self._parse_properties(line_feeder, comment, deps,
73590c36d8aSUlf Magnusson                                       visible_if_deps)
73690c36d8aSUlf Magnusson
737*4e1102f6SUlf Magnusson                self.comments.append(comment)
738*4e1102f6SUlf Magnusson                block.append(comment)
739*4e1102f6SUlf Magnusson
74090c36d8aSUlf Magnusson            elif t0 == T_MENU:
74190c36d8aSUlf Magnusson                menu = Menu()
74290c36d8aSUlf Magnusson                menu.config = self
74390c36d8aSUlf Magnusson                menu.parent = parent
74490c36d8aSUlf Magnusson                menu.filename = line_feeder.filename
74590c36d8aSUlf Magnusson                menu.linenr = line_feeder.linenr
74690c36d8aSUlf Magnusson                menu.title = tokens.get_next()
74790c36d8aSUlf Magnusson
74890c36d8aSUlf Magnusson                self._parse_properties(line_feeder, menu, deps,
74990c36d8aSUlf Magnusson                                       visible_if_deps)
750*4e1102f6SUlf Magnusson
751*4e1102f6SUlf Magnusson                # This needs to go before _parse_block() so that we get the
752*4e1102f6SUlf Magnusson                # proper menu ordering in the case of nested functions
753*4e1102f6SUlf Magnusson                self.menus.append(menu)
754*4e1102f6SUlf Magnusson                # Parse contents and put Items in menu.block
755*4e1102f6SUlf Magnusson                self._parse_block(line_feeder, T_ENDMENU, menu, menu.dep_expr,
75690c36d8aSUlf Magnusson                                  _make_and(visible_if_deps,
757*4e1102f6SUlf Magnusson                                            menu.visible_if_expr),
758*4e1102f6SUlf Magnusson                                  menu.block)
759*4e1102f6SUlf Magnusson
760*4e1102f6SUlf Magnusson                block.append(menu)
761f219e013SMasahiro Yamada
762f219e013SMasahiro Yamada            elif t0 == T_CHOICE:
76390c36d8aSUlf Magnusson                name = tokens.get_next()
76490c36d8aSUlf Magnusson                if name is None:
765f219e013SMasahiro Yamada                    choice = Choice()
766f219e013SMasahiro Yamada                    self.choices.append(choice)
76790c36d8aSUlf Magnusson                else:
76890c36d8aSUlf Magnusson                    # Named choice
76990c36d8aSUlf Magnusson                    choice = self.named_choices.get(name)
77090c36d8aSUlf Magnusson                    if choice is None:
77190c36d8aSUlf Magnusson                        choice = Choice()
772f219e013SMasahiro Yamada                        choice.name = name
773f219e013SMasahiro Yamada                        self.named_choices[name] = choice
77490c36d8aSUlf Magnusson                        self.choices.append(choice)
775f219e013SMasahiro Yamada
776f219e013SMasahiro Yamada                choice.config = self
777f219e013SMasahiro Yamada                choice.parent = parent
778f219e013SMasahiro Yamada
77990c36d8aSUlf Magnusson                choice.def_locations.append((line_feeder.filename,
78090c36d8aSUlf Magnusson                                             line_feeder.linenr))
781f219e013SMasahiro Yamada
78290c36d8aSUlf Magnusson                self._parse_properties(line_feeder, choice, deps,
783f219e013SMasahiro Yamada                                       visible_if_deps)
784*4e1102f6SUlf Magnusson
785*4e1102f6SUlf Magnusson                # Parse contents and put Items in choice.block
786*4e1102f6SUlf Magnusson                self._parse_block(line_feeder, T_ENDCHOICE, choice, deps,
787*4e1102f6SUlf Magnusson                                  visible_if_deps, choice.block)
788f219e013SMasahiro Yamada
789f219e013SMasahiro Yamada                choice._determine_actual_symbols()
790f219e013SMasahiro Yamada
79190c36d8aSUlf Magnusson                # If no type is specified for the choice, its type is that of
79290c36d8aSUlf Magnusson                # the first choice item with a specified type
793f219e013SMasahiro Yamada                if choice.type == UNKNOWN:
79490c36d8aSUlf Magnusson                    for item in choice.actual_symbols:
795f219e013SMasahiro Yamada                        if item.type != UNKNOWN:
796f219e013SMasahiro Yamada                            choice.type = item.type
797f219e013SMasahiro Yamada                            break
798f219e013SMasahiro Yamada
799f219e013SMasahiro Yamada                # Each choice item of UNKNOWN type gets the type of the choice
80090c36d8aSUlf Magnusson                for item in choice.actual_symbols:
801f219e013SMasahiro Yamada                    if item.type == UNKNOWN:
802f219e013SMasahiro Yamada                        item.type = choice.type
803f219e013SMasahiro Yamada
80490c36d8aSUlf Magnusson                block.append(choice)
805f219e013SMasahiro Yamada
806f219e013SMasahiro Yamada            elif t0 == T_MAINMENU:
807f219e013SMasahiro Yamada                text = tokens.get_next()
808f219e013SMasahiro Yamada                if self.mainmenu_text is not None:
809f219e013SMasahiro Yamada                    self._warn("overriding 'mainmenu' text. "
810f219e013SMasahiro Yamada                               'Old value: "{0}", new value: "{1}".'
811f219e013SMasahiro Yamada                               .format(self.mainmenu_text, text),
81290c36d8aSUlf Magnusson                               line_feeder.filename, line_feeder.linenr)
813f219e013SMasahiro Yamada                self.mainmenu_text = text
814f219e013SMasahiro Yamada
815f219e013SMasahiro Yamada            else:
81690c36d8aSUlf Magnusson                _parse_error(line, "unrecognized construct",
81790c36d8aSUlf Magnusson                             line_feeder.filename, line_feeder.linenr)
818f219e013SMasahiro Yamada
819f219e013SMasahiro Yamada    def _parse_properties(self, line_feeder, stmt, deps, visible_if_deps):
82090c36d8aSUlf Magnusson        """Parsing of properties for symbols, menus, choices, and comments.
82190c36d8aSUlf Magnusson        Takes care of propagating dependencies from enclosing menus and ifs."""
822f219e013SMasahiro Yamada
823f219e013SMasahiro Yamada        def parse_val_and_cond(tokens, line, filename, linenr):
824f219e013SMasahiro Yamada            """Parses '<expr1> if <expr2>' constructs, where the 'if' part is
825f219e013SMasahiro Yamada            optional. Returns a tuple containing the parsed expressions, with
826f219e013SMasahiro Yamada            None as the second element if the 'if' part is missing."""
827*4e1102f6SUlf Magnusson            return (self._parse_expr(tokens, stmt, line, filename, linenr,
828*4e1102f6SUlf Magnusson                                     False),
829*4e1102f6SUlf Magnusson                    self._parse_expr(tokens, stmt, line, filename, linenr)
830*4e1102f6SUlf Magnusson                    if tokens.check(T_IF) else None)
831f219e013SMasahiro Yamada
832f219e013SMasahiro Yamada        # In case the symbol is defined in multiple locations, we need to
833*4e1102f6SUlf Magnusson        # remember what prompts, defaults, selects, and implies are new for
834*4e1102f6SUlf Magnusson        # this definition, as "depends on" should only apply to the local
835f219e013SMasahiro Yamada        # definition.
836f219e013SMasahiro Yamada        new_prompt = None
837f219e013SMasahiro Yamada        new_def_exprs = []
838f219e013SMasahiro Yamada        new_selects = []
839*4e1102f6SUlf Magnusson        new_implies = []
840f219e013SMasahiro Yamada
841f219e013SMasahiro Yamada        # Dependencies from 'depends on' statements
842f219e013SMasahiro Yamada        depends_on_expr = None
843f219e013SMasahiro Yamada
844f219e013SMasahiro Yamada        while 1:
845f219e013SMasahiro Yamada            line = line_feeder.get_next()
846f219e013SMasahiro Yamada            if line is None:
847f219e013SMasahiro Yamada                break
848f219e013SMasahiro Yamada
84990c36d8aSUlf Magnusson            filename = line_feeder.filename
85090c36d8aSUlf Magnusson            linenr = line_feeder.linenr
851f219e013SMasahiro Yamada
852f219e013SMasahiro Yamada            tokens = self._tokenize(line, False, filename, linenr)
853f219e013SMasahiro Yamada
85490c36d8aSUlf Magnusson            t0 = tokens.get_next()
85590c36d8aSUlf Magnusson            if t0 is None:
856f219e013SMasahiro Yamada                continue
857f219e013SMasahiro Yamada
85890c36d8aSUlf Magnusson            # Cases are ordered roughly by frequency, which speeds things up a
85990c36d8aSUlf Magnusson            # bit
860f219e013SMasahiro Yamada
86190c36d8aSUlf Magnusson            if t0 == T_DEPENDS:
86290c36d8aSUlf Magnusson                if not tokens.check(T_ON):
86390c36d8aSUlf Magnusson                    _parse_error(line, 'expected "on" after "depends"',
86490c36d8aSUlf Magnusson                                 filename, linenr)
865f219e013SMasahiro Yamada
86690c36d8aSUlf Magnusson                parsed_deps = self._parse_expr(tokens, stmt, line, filename,
86790c36d8aSUlf Magnusson                                               linenr)
868f219e013SMasahiro Yamada
86990c36d8aSUlf Magnusson                if isinstance(stmt, (Menu, Comment)):
87090c36d8aSUlf Magnusson                    stmt.orig_deps = _make_and(stmt.orig_deps, parsed_deps)
87190c36d8aSUlf Magnusson                else:
87290c36d8aSUlf Magnusson                    depends_on_expr = _make_and(depends_on_expr, parsed_deps)
87390c36d8aSUlf Magnusson
87490c36d8aSUlf Magnusson            elif t0 == T_HELP:
87590c36d8aSUlf Magnusson                # Find first non-blank (not all-space) line and get its
87690c36d8aSUlf Magnusson                # indentation
87790c36d8aSUlf Magnusson                line = line_feeder.next_nonblank()
878f219e013SMasahiro Yamada                if line is None:
879f219e013SMasahiro Yamada                    stmt.help = ""
880f219e013SMasahiro Yamada                    break
881f219e013SMasahiro Yamada                indent = _indentation(line)
88290c36d8aSUlf Magnusson                if indent == 0:
883f219e013SMasahiro Yamada                    # If the first non-empty lines has zero indent, there is no
884f219e013SMasahiro Yamada                    # help text
885f219e013SMasahiro Yamada                    stmt.help = ""
88690c36d8aSUlf Magnusson                    line_feeder.unget()
887f219e013SMasahiro Yamada                    break
888f219e013SMasahiro Yamada
889f219e013SMasahiro Yamada                # The help text goes on till the first non-empty line with less
890f219e013SMasahiro Yamada                # indent
89190c36d8aSUlf Magnusson                help_lines = [_deindent(line, indent)]
892f219e013SMasahiro Yamada                while 1:
893f219e013SMasahiro Yamada                    line = line_feeder.get_next()
89490c36d8aSUlf Magnusson                    if line is None or \
895f219e013SMasahiro Yamada                       (not line.isspace() and _indentation(line) < indent):
896f219e013SMasahiro Yamada                        stmt.help = "".join(help_lines)
897f219e013SMasahiro Yamada                        break
898f219e013SMasahiro Yamada                    help_lines.append(_deindent(line, indent))
899f219e013SMasahiro Yamada
900f219e013SMasahiro Yamada                if line is None:
901f219e013SMasahiro Yamada                    break
902f219e013SMasahiro Yamada
90390c36d8aSUlf Magnusson                line_feeder.unget()
904f219e013SMasahiro Yamada
905*4e1102f6SUlf Magnusson            elif t0 == T_SELECT:
906f219e013SMasahiro Yamada                target = tokens.get_next()
907f219e013SMasahiro Yamada
908f219e013SMasahiro Yamada                stmt.referenced_syms.add(target)
909f219e013SMasahiro Yamada                stmt.selected_syms.add(target)
910f219e013SMasahiro Yamada
911*4e1102f6SUlf Magnusson                new_selects.append(
912*4e1102f6SUlf Magnusson                    (target,
913*4e1102f6SUlf Magnusson                     self._parse_expr(tokens, stmt, line, filename, linenr)
914*4e1102f6SUlf Magnusson                     if tokens.check(T_IF) else None))
915*4e1102f6SUlf Magnusson
916*4e1102f6SUlf Magnusson            elif t0 == T_IMPLY:
917*4e1102f6SUlf Magnusson                target = tokens.get_next()
918*4e1102f6SUlf Magnusson
919*4e1102f6SUlf Magnusson                stmt.referenced_syms.add(target)
920*4e1102f6SUlf Magnusson                stmt.implied_syms.add(target)
921*4e1102f6SUlf Magnusson
922*4e1102f6SUlf Magnusson                new_implies.append(
923*4e1102f6SUlf Magnusson                    (target,
924*4e1102f6SUlf Magnusson                     self._parse_expr(tokens, stmt, line, filename, linenr)
925*4e1102f6SUlf Magnusson                     if tokens.check(T_IF) else None))
926f219e013SMasahiro Yamada
927f219e013SMasahiro Yamada            elif t0 in (T_BOOL, T_TRISTATE, T_INT, T_HEX, T_STRING):
92890c36d8aSUlf Magnusson                stmt.type = TOKEN_TO_TYPE[t0]
92990c36d8aSUlf Magnusson                if tokens.peek_next() is not None:
93090c36d8aSUlf Magnusson                    new_prompt = parse_val_and_cond(tokens, line, filename,
93190c36d8aSUlf Magnusson                                                    linenr)
932f219e013SMasahiro Yamada
93390c36d8aSUlf Magnusson            elif t0 == T_DEFAULT:
93490c36d8aSUlf Magnusson                new_def_exprs.append(parse_val_and_cond(tokens, line, filename,
93590c36d8aSUlf Magnusson                                                        linenr))
936f219e013SMasahiro Yamada
937f219e013SMasahiro Yamada            elif t0 == T_DEF_BOOL:
938f219e013SMasahiro Yamada                stmt.type = BOOL
93990c36d8aSUlf Magnusson                if tokens.peek_next() is not None:
94090c36d8aSUlf Magnusson                    new_def_exprs.append(parse_val_and_cond(tokens, line,
94190c36d8aSUlf Magnusson                                                            filename, linenr))
942f219e013SMasahiro Yamada
94390c36d8aSUlf Magnusson            elif t0 == T_PROMPT:
94490c36d8aSUlf Magnusson                # 'prompt' properties override each other within a single
94590c36d8aSUlf Magnusson                # definition of a symbol, but additional prompts can be added
94690c36d8aSUlf Magnusson                # by defining the symbol multiple times; hence 'new_prompt'
94790c36d8aSUlf Magnusson                # instead of 'prompt'.
94890c36d8aSUlf Magnusson                new_prompt = parse_val_and_cond(tokens, line, filename, linenr)
94990c36d8aSUlf Magnusson
95090c36d8aSUlf Magnusson            elif t0 == T_RANGE:
95190c36d8aSUlf Magnusson                low = tokens.get_next()
95290c36d8aSUlf Magnusson                high = tokens.get_next()
95390c36d8aSUlf Magnusson                stmt.referenced_syms.add(low)
95490c36d8aSUlf Magnusson                stmt.referenced_syms.add(high)
95590c36d8aSUlf Magnusson
956*4e1102f6SUlf Magnusson                stmt.ranges.append(
957*4e1102f6SUlf Magnusson                    (low, high,
958*4e1102f6SUlf Magnusson                     self._parse_expr(tokens, stmt, line, filename, linenr)
959*4e1102f6SUlf Magnusson                     if tokens.check(T_IF) else None))
960f219e013SMasahiro Yamada
961f219e013SMasahiro Yamada            elif t0 == T_DEF_TRISTATE:
962f219e013SMasahiro Yamada                stmt.type = TRISTATE
96390c36d8aSUlf Magnusson                if tokens.peek_next() is not None:
96490c36d8aSUlf Magnusson                    new_def_exprs.append(parse_val_and_cond(tokens, line,
96590c36d8aSUlf Magnusson                                                            filename, linenr))
966f219e013SMasahiro Yamada
967f219e013SMasahiro Yamada            elif t0 == T_OPTION:
968f219e013SMasahiro Yamada                if tokens.check(T_ENV) and tokens.check(T_EQUAL):
969f219e013SMasahiro Yamada                    env_var = tokens.get_next()
970f219e013SMasahiro Yamada
971f219e013SMasahiro Yamada                    stmt.is_special_ = True
972f219e013SMasahiro Yamada                    stmt.is_from_env = True
973f219e013SMasahiro Yamada
974f219e013SMasahiro Yamada                    if env_var not in os.environ:
97590c36d8aSUlf Magnusson                        self._warn("The symbol {0} references the "
97690c36d8aSUlf Magnusson                                   "non-existent environment variable {1} and "
97790c36d8aSUlf Magnusson                                   "will get the empty string as its value. "
97890c36d8aSUlf Magnusson                                   "If you're using Kconfiglib via "
97990c36d8aSUlf Magnusson                                   "'make (i)scriptconfig', it should have "
98090c36d8aSUlf Magnusson                                   "set up the environment correctly for you. "
98190c36d8aSUlf Magnusson                                   "If you still got this message, that "
98290c36d8aSUlf Magnusson                                   "might be an error, and you should email "
98390c36d8aSUlf Magnusson                                   "ulfalizer a.t Google's email service."""
98490c36d8aSUlf Magnusson                                   .format(stmt.name, env_var),
98590c36d8aSUlf Magnusson                                   filename, linenr)
986f219e013SMasahiro Yamada
98790c36d8aSUlf Magnusson                        stmt.cached_val = ""
988f219e013SMasahiro Yamada                    else:
98990c36d8aSUlf Magnusson                        stmt.cached_val = os.environ[env_var]
990f219e013SMasahiro Yamada
991f219e013SMasahiro Yamada                elif tokens.check(T_DEFCONFIG_LIST):
992f219e013SMasahiro Yamada                    self.defconfig_sym = stmt
993f219e013SMasahiro Yamada
994f219e013SMasahiro Yamada                elif tokens.check(T_MODULES):
99590c36d8aSUlf Magnusson                    # To reduce warning spam, only warn if 'option modules' is
99690c36d8aSUlf Magnusson                    # set on some symbol that isn't MODULES, which should be
99790c36d8aSUlf Magnusson                    # safe. I haven't run into any projects that make use
99890c36d8aSUlf Magnusson                    # modules besides the kernel yet, and there it's likely to
99990c36d8aSUlf Magnusson                    # keep being called "MODULES".
100090c36d8aSUlf Magnusson                    if stmt.name != "MODULES":
1001f219e013SMasahiro Yamada                        self._warn("the 'modules' option is not supported. "
1002f219e013SMasahiro Yamada                                   "Let me know if this is a problem for you; "
10039d01b787SMasahiro Yamada                                   "it shouldn't be that hard to implement. "
10049d01b787SMasahiro Yamada                                   "(Note that modules are still supported -- "
10059d01b787SMasahiro Yamada                                   "Kconfiglib just assumes the symbol name "
100690c36d8aSUlf Magnusson                                   "MODULES, like older versions of the C "
100790c36d8aSUlf Magnusson                                   "implementation did when 'option modules' "
100890c36d8aSUlf Magnusson                                   "wasn't used.)",
100990c36d8aSUlf Magnusson                                   filename, linenr)
1010f219e013SMasahiro Yamada
10119d01b787SMasahiro Yamada                elif tokens.check(T_ALLNOCONFIG_Y):
10129d01b787SMasahiro Yamada                    if not isinstance(stmt, Symbol):
10139d01b787SMasahiro Yamada                        _parse_error(line,
101490c36d8aSUlf Magnusson                                     "the 'allnoconfig_y' option is only "
101590c36d8aSUlf Magnusson                                     "valid for symbols",
101690c36d8aSUlf Magnusson                                     filename, linenr)
10179d01b787SMasahiro Yamada                    stmt.allnoconfig_y = True
10189d01b787SMasahiro Yamada
1019f219e013SMasahiro Yamada                else:
102090c36d8aSUlf Magnusson                    _parse_error(line, "unrecognized option", filename, linenr)
102190c36d8aSUlf Magnusson
102290c36d8aSUlf Magnusson            elif t0 == T_VISIBLE:
102390c36d8aSUlf Magnusson                if not tokens.check(T_IF):
102490c36d8aSUlf Magnusson                    _parse_error(line, 'expected "if" after "visible"',
102590c36d8aSUlf Magnusson                                 filename, linenr)
102690c36d8aSUlf Magnusson                if not isinstance(stmt, Menu):
102790c36d8aSUlf Magnusson                    _parse_error(line,
102890c36d8aSUlf Magnusson                                 "'visible if' is only valid for menus",
102990c36d8aSUlf Magnusson                                 filename, linenr)
103090c36d8aSUlf Magnusson
103190c36d8aSUlf Magnusson                parsed_deps = self._parse_expr(tokens, stmt, line, filename,
103290c36d8aSUlf Magnusson                                               linenr)
103390c36d8aSUlf Magnusson                stmt.visible_if_expr = _make_and(stmt.visible_if_expr,
103490c36d8aSUlf Magnusson                                                 parsed_deps)
103590c36d8aSUlf Magnusson
103690c36d8aSUlf Magnusson            elif t0 == T_OPTIONAL:
103790c36d8aSUlf Magnusson                if not isinstance(stmt, Choice):
103890c36d8aSUlf Magnusson                    _parse_error(line,
103990c36d8aSUlf Magnusson                                 '"optional" is only valid for choices',
104090c36d8aSUlf Magnusson                                 filename,
104190c36d8aSUlf Magnusson                                 linenr)
104290c36d8aSUlf Magnusson                stmt.optional = True
1043f219e013SMasahiro Yamada
1044f219e013SMasahiro Yamada            else:
1045f219e013SMasahiro Yamada                # See comment in Config.__init__()
1046f219e013SMasahiro Yamada                self.end_line = line
1047f219e013SMasahiro Yamada                self.end_line_tokens = tokens
1048f219e013SMasahiro Yamada                break
1049f219e013SMasahiro Yamada
105090c36d8aSUlf Magnusson        # Done parsing properties. Now propagate 'depends on' and enclosing
105190c36d8aSUlf Magnusson        # menu/if dependencies to expressions.
1052f219e013SMasahiro Yamada
105390c36d8aSUlf Magnusson        # The set of symbols referenced directly by the statement plus all
105490c36d8aSUlf Magnusson        # symbols referenced by enclosing menus and ifs
105590c36d8aSUlf Magnusson        stmt.all_referenced_syms = stmt.referenced_syms | _get_expr_syms(deps)
105690c36d8aSUlf Magnusson
105790c36d8aSUlf Magnusson        # Save original dependencies from enclosing menus and ifs
1058f219e013SMasahiro Yamada        stmt.deps_from_containing = deps
1059f219e013SMasahiro Yamada
106090c36d8aSUlf Magnusson        if isinstance(stmt, (Menu, Comment)):
106190c36d8aSUlf Magnusson            stmt.dep_expr = _make_and(stmt.orig_deps, deps)
1062f219e013SMasahiro Yamada        else:
106390c36d8aSUlf Magnusson            # Symbol or Choice
1064f219e013SMasahiro Yamada
1065f219e013SMasahiro Yamada            # See comment for 'menu_dep'
1066*4e1102f6SUlf Magnusson            stmt.menu_dep = _make_and(deps, depends_on_expr)
1067f219e013SMasahiro Yamada
106890c36d8aSUlf Magnusson            # Propagate dependencies to prompts
1069f219e013SMasahiro Yamada
1070f219e013SMasahiro Yamada            if new_prompt is not None:
1071f219e013SMasahiro Yamada                prompt, cond_expr = new_prompt
1072*4e1102f6SUlf Magnusson                # Propagate 'visible if' dependencies from menus and local
1073*4e1102f6SUlf Magnusson                # 'depends on' dependencies
1074*4e1102f6SUlf Magnusson                cond_expr = _make_and(_make_and(cond_expr, visible_if_deps),
1075*4e1102f6SUlf Magnusson                                      depends_on_expr)
107690c36d8aSUlf Magnusson                # Save original
1077*4e1102f6SUlf Magnusson                stmt.orig_prompts.append((prompt, cond_expr))
107890c36d8aSUlf Magnusson                # Finalize with dependencies from enclosing menus and ifs
1079*4e1102f6SUlf Magnusson                stmt.prompts.append((prompt, _make_and(cond_expr, deps)))
108090c36d8aSUlf Magnusson
108190c36d8aSUlf Magnusson            # Propagate dependencies to defaults
108290c36d8aSUlf Magnusson
108390c36d8aSUlf Magnusson            # Propagate 'depends on' dependencies
108490c36d8aSUlf Magnusson            new_def_exprs = [(val_expr, _make_and(cond_expr, depends_on_expr))
108590c36d8aSUlf Magnusson                             for val_expr, cond_expr in new_def_exprs]
108690c36d8aSUlf Magnusson            # Save original
108790c36d8aSUlf Magnusson            stmt.orig_def_exprs.extend(new_def_exprs)
108890c36d8aSUlf Magnusson            # Finalize with dependencies from enclosing menus and ifs
108990c36d8aSUlf Magnusson            stmt.def_exprs.extend([(val_expr, _make_and(cond_expr, deps))
109090c36d8aSUlf Magnusson                                   for val_expr, cond_expr in new_def_exprs])
109190c36d8aSUlf Magnusson
1092*4e1102f6SUlf Magnusson            # Propagate dependencies to selects and implies
1093f219e013SMasahiro Yamada
1094*4e1102f6SUlf Magnusson            # Only symbols can select and imply
1095f219e013SMasahiro Yamada            if isinstance(stmt, Symbol):
109690c36d8aSUlf Magnusson                # Propagate 'depends on' dependencies
109790c36d8aSUlf Magnusson                new_selects = [(target, _make_and(cond_expr, depends_on_expr))
109890c36d8aSUlf Magnusson                               for target, cond_expr in new_selects]
1099*4e1102f6SUlf Magnusson                new_implies = [(target, _make_and(cond_expr, depends_on_expr))
1100*4e1102f6SUlf Magnusson                               for target, cond_expr in new_implies]
110190c36d8aSUlf Magnusson                # Save original
1102f219e013SMasahiro Yamada                stmt.orig_selects.extend(new_selects)
1103*4e1102f6SUlf Magnusson                stmt.orig_implies.extend(new_implies)
110490c36d8aSUlf Magnusson                # Finalize with dependencies from enclosing menus and ifs
110590c36d8aSUlf Magnusson                for target, cond in new_selects:
1106*4e1102f6SUlf Magnusson                    target.rev_dep = \
1107*4e1102f6SUlf Magnusson                        _make_or(target.rev_dep,
1108*4e1102f6SUlf Magnusson                                 _make_and(stmt, _make_and(cond, deps)))
1109*4e1102f6SUlf Magnusson                for target, cond in new_implies:
1110*4e1102f6SUlf Magnusson                    target.weak_rev_dep = \
1111*4e1102f6SUlf Magnusson                        _make_or(target.weak_rev_dep,
1112*4e1102f6SUlf Magnusson                                 _make_and(stmt, _make_and(cond, deps)))
1113f219e013SMasahiro Yamada
111490c36d8aSUlf Magnusson    def _parse_expr(self, feed, cur_item, line, filename=None, linenr=None,
111590c36d8aSUlf Magnusson                    transform_m=True):
111690c36d8aSUlf Magnusson        """Parses an expression from the tokens in 'feed' using a simple
111790c36d8aSUlf Magnusson        top-down approach. The result has the form
111890c36d8aSUlf Magnusson        '(<operator>, [<parsed operands>])', where <operator> is e.g.
111990c36d8aSUlf Magnusson        kconfiglib.AND. If there is only one operand (i.e., no && or ||), then
112090c36d8aSUlf Magnusson        the operand is returned directly. This also goes for subexpressions.
1121f219e013SMasahiro Yamada
112290c36d8aSUlf Magnusson        feed: _Feed instance containing the tokens for the expression.
1123f219e013SMasahiro Yamada
112490c36d8aSUlf Magnusson        cur_item: The item (Symbol, Choice, Menu, or Comment) currently being
112590c36d8aSUlf Magnusson           parsed, or None if we're not parsing an item. Used for recording
112690c36d8aSUlf Magnusson           references to symbols.
112790c36d8aSUlf Magnusson
112890c36d8aSUlf Magnusson        line: The line containing the expression being parsed.
112990c36d8aSUlf Magnusson
113090c36d8aSUlf Magnusson        filename (default: None): The file containing the expression.
113190c36d8aSUlf Magnusson
113290c36d8aSUlf Magnusson        linenr (default: None): The line number containing the expression.
113390c36d8aSUlf Magnusson
113490c36d8aSUlf Magnusson        transform_m (default: False): Determines if 'm' should be rewritten to
113590c36d8aSUlf Magnusson           'm && MODULES' -- see parse_val_and_cond().
113690c36d8aSUlf Magnusson
113790c36d8aSUlf Magnusson        Expression grammar, in decreasing order of precedence:
113890c36d8aSUlf Magnusson
113990c36d8aSUlf Magnusson        <expr> -> <symbol>
114090c36d8aSUlf Magnusson                  <symbol> '=' <symbol>
114190c36d8aSUlf Magnusson                  <symbol> '!=' <symbol>
114290c36d8aSUlf Magnusson                  '(' <expr> ')'
114390c36d8aSUlf Magnusson                  '!' <expr>
114490c36d8aSUlf Magnusson                  <expr> '&&' <expr>
114590c36d8aSUlf Magnusson                  <expr> '||' <expr>"""
114690c36d8aSUlf Magnusson
114790c36d8aSUlf Magnusson        # Use instance variables to avoid having to pass these as arguments
114890c36d8aSUlf Magnusson        # through the top-down parser in _parse_expr_rec(), which is tedious
114990c36d8aSUlf Magnusson        # and obfuscates the code. A profiler run shows no noticeable
115090c36d8aSUlf Magnusson        # performance difference.
115190c36d8aSUlf Magnusson        self._cur_item = cur_item
115290c36d8aSUlf Magnusson        self._transform_m = transform_m
115390c36d8aSUlf Magnusson        self._line = line
115490c36d8aSUlf Magnusson        self._filename = filename
115590c36d8aSUlf Magnusson        self._linenr = linenr
115690c36d8aSUlf Magnusson
115790c36d8aSUlf Magnusson        return self._parse_expr_rec(feed)
115890c36d8aSUlf Magnusson
115990c36d8aSUlf Magnusson    def _parse_expr_rec(self, feed):
116090c36d8aSUlf Magnusson        or_term = self._parse_or_term(feed)
116190c36d8aSUlf Magnusson        if not feed.check(T_OR):
116290c36d8aSUlf Magnusson            # Common case -- no need for an OR node since it's just a single
116390c36d8aSUlf Magnusson            # operand
116490c36d8aSUlf Magnusson            return or_term
116590c36d8aSUlf Magnusson        or_terms = [or_term, self._parse_or_term(feed)]
116690c36d8aSUlf Magnusson        while feed.check(T_OR):
116790c36d8aSUlf Magnusson            or_terms.append(self._parse_or_term(feed))
116890c36d8aSUlf Magnusson        return (OR, or_terms)
116990c36d8aSUlf Magnusson
117090c36d8aSUlf Magnusson    def _parse_or_term(self, feed):
117190c36d8aSUlf Magnusson        and_term = self._parse_factor(feed)
117290c36d8aSUlf Magnusson        if not feed.check(T_AND):
117390c36d8aSUlf Magnusson            # Common case -- no need for an AND node since it's just a single
117490c36d8aSUlf Magnusson            # operand
117590c36d8aSUlf Magnusson            return and_term
117690c36d8aSUlf Magnusson        and_terms = [and_term, self._parse_factor(feed)]
117790c36d8aSUlf Magnusson        while feed.check(T_AND):
117890c36d8aSUlf Magnusson            and_terms.append(self._parse_factor(feed))
117990c36d8aSUlf Magnusson        return (AND, and_terms)
118090c36d8aSUlf Magnusson
118190c36d8aSUlf Magnusson    def _parse_factor(self, feed):
118290c36d8aSUlf Magnusson        token = feed.get_next()
118390c36d8aSUlf Magnusson
118490c36d8aSUlf Magnusson        if isinstance(token, (Symbol, str)):
118590c36d8aSUlf Magnusson            if self._cur_item is not None and isinstance(token, Symbol):
118690c36d8aSUlf Magnusson                self._cur_item.referenced_syms.add(token)
118790c36d8aSUlf Magnusson
118890c36d8aSUlf Magnusson            next_token = feed.peek_next()
118990c36d8aSUlf Magnusson            # For conditional expressions ('depends on <expr>',
119090c36d8aSUlf Magnusson            # '... if <expr>', # etc.), "m" and m are rewritten to
119190c36d8aSUlf Magnusson            # "m" && MODULES.
119290c36d8aSUlf Magnusson            if next_token != T_EQUAL and next_token != T_UNEQUAL:
119390c36d8aSUlf Magnusson                if self._transform_m and (token is self.m or token == "m"):
119490c36d8aSUlf Magnusson                    return (AND, ["m", self._sym_lookup("MODULES")])
119590c36d8aSUlf Magnusson                return token
119690c36d8aSUlf Magnusson
119790c36d8aSUlf Magnusson            relation = EQUAL if (feed.get_next() == T_EQUAL) else UNEQUAL
119890c36d8aSUlf Magnusson            token_2 = feed.get_next()
119990c36d8aSUlf Magnusson            if self._cur_item is not None and isinstance(token_2, Symbol):
120090c36d8aSUlf Magnusson                self._cur_item.referenced_syms.add(token_2)
120190c36d8aSUlf Magnusson            return (relation, token, token_2)
120290c36d8aSUlf Magnusson
120390c36d8aSUlf Magnusson        if token == T_NOT:
120490c36d8aSUlf Magnusson            return (NOT, self._parse_factor(feed))
120590c36d8aSUlf Magnusson
120690c36d8aSUlf Magnusson        if token == T_OPEN_PAREN:
120790c36d8aSUlf Magnusson            expr_parse = self._parse_expr_rec(feed)
120890c36d8aSUlf Magnusson            if not feed.check(T_CLOSE_PAREN):
120990c36d8aSUlf Magnusson                _parse_error(self._line, "missing end parenthesis",
121090c36d8aSUlf Magnusson                             self._filename, self._linenr)
121190c36d8aSUlf Magnusson            return expr_parse
121290c36d8aSUlf Magnusson
121390c36d8aSUlf Magnusson        _parse_error(self._line, "malformed expression", self._filename,
121490c36d8aSUlf Magnusson                     self._linenr)
121590c36d8aSUlf Magnusson
121690c36d8aSUlf Magnusson    def _tokenize(self, s, for_eval, filename=None, linenr=None):
121790c36d8aSUlf Magnusson        """Returns a _Feed instance containing tokens derived from the string
121890c36d8aSUlf Magnusson        's'. Registers any new symbols encountered (via _sym_lookup()).
121990c36d8aSUlf Magnusson
122090c36d8aSUlf Magnusson        (I experimented with a pure regular expression implementation, but it
122190c36d8aSUlf Magnusson        came out slower, less readable, and wouldn't have been as flexible.)
122290c36d8aSUlf Magnusson
122390c36d8aSUlf Magnusson        for_eval: True when parsing an expression for a call to Config.eval(),
122490c36d8aSUlf Magnusson           in which case we should not treat the first token specially nor
122590c36d8aSUlf Magnusson           register new symbols."""
122690c36d8aSUlf Magnusson
122790c36d8aSUlf Magnusson        s = s.strip()
122890c36d8aSUlf Magnusson        if s == "" or s[0] == "#":
122990c36d8aSUlf Magnusson            return _Feed([])
123090c36d8aSUlf Magnusson
123190c36d8aSUlf Magnusson        if for_eval:
123290c36d8aSUlf Magnusson            previous = None # The previous token seen
123390c36d8aSUlf Magnusson            tokens = []
123490c36d8aSUlf Magnusson            i = 0 # The current index in the string being tokenized
123590c36d8aSUlf Magnusson
123690c36d8aSUlf Magnusson        else:
123790c36d8aSUlf Magnusson            # The initial word on a line is parsed specially. Let
123890c36d8aSUlf Magnusson            # command_chars = [A-Za-z0-9_]. Then
123990c36d8aSUlf Magnusson            #  - leading non-command_chars characters are ignored, and
124090c36d8aSUlf Magnusson            #  - the first token consists the following one or more
124190c36d8aSUlf Magnusson            #    command_chars characters.
124290c36d8aSUlf Magnusson            # This is why things like "----help--" are accepted.
124390c36d8aSUlf Magnusson            initial_token_match = _initial_token_re_match(s)
124490c36d8aSUlf Magnusson            if initial_token_match is None:
124590c36d8aSUlf Magnusson                return _Feed([])
124690c36d8aSUlf Magnusson            keyword = _get_keyword(initial_token_match.group(1))
124790c36d8aSUlf Magnusson            if keyword == T_HELP:
124890c36d8aSUlf Magnusson                # Avoid junk after "help", e.g. "---", being registered as a
124990c36d8aSUlf Magnusson                # symbol
125090c36d8aSUlf Magnusson                return _Feed([T_HELP])
125190c36d8aSUlf Magnusson            if keyword is None:
125290c36d8aSUlf Magnusson                # We expect a keyword as the first token
125390c36d8aSUlf Magnusson                _tokenization_error(s, filename, linenr)
125490c36d8aSUlf Magnusson
125590c36d8aSUlf Magnusson            previous = keyword
125690c36d8aSUlf Magnusson            tokens = [keyword]
125790c36d8aSUlf Magnusson            # The current index in the string being tokenized
125890c36d8aSUlf Magnusson            i = initial_token_match.end()
125990c36d8aSUlf Magnusson
126090c36d8aSUlf Magnusson        # _tokenize() is a hotspot during parsing, and this speeds things up a
126190c36d8aSUlf Magnusson        # bit
126290c36d8aSUlf Magnusson        strlen = len(s)
126390c36d8aSUlf Magnusson        append = tokens.append
126490c36d8aSUlf Magnusson
126590c36d8aSUlf Magnusson        # Main tokenization loop. (Handles tokens past the first one.)
126690c36d8aSUlf Magnusson        while i < strlen:
126790c36d8aSUlf Magnusson            # Test for an identifier/keyword preceded by whitespace first; this
126890c36d8aSUlf Magnusson            # is the most common case.
126990c36d8aSUlf Magnusson            id_keyword_match = _id_keyword_re_match(s, i)
127090c36d8aSUlf Magnusson            if id_keyword_match:
127190c36d8aSUlf Magnusson                # We have an identifier or keyword. The above also stripped any
127290c36d8aSUlf Magnusson                # whitespace for us.
127390c36d8aSUlf Magnusson                name = id_keyword_match.group(1)
127490c36d8aSUlf Magnusson                # Jump past it
127590c36d8aSUlf Magnusson                i = id_keyword_match.end()
127690c36d8aSUlf Magnusson
127790c36d8aSUlf Magnusson                keyword = _get_keyword(name)
127890c36d8aSUlf Magnusson                if keyword is not None:
127990c36d8aSUlf Magnusson                    # It's a keyword
128090c36d8aSUlf Magnusson                    append(keyword)
128190c36d8aSUlf Magnusson                elif previous in STRING_LEX:
128290c36d8aSUlf Magnusson                    # What would ordinarily be considered an identifier is
128390c36d8aSUlf Magnusson                    # treated as a string after certain tokens
128490c36d8aSUlf Magnusson                    append(name)
128590c36d8aSUlf Magnusson                else:
128690c36d8aSUlf Magnusson                    # It's a symbol name. _sym_lookup() will take care of
128790c36d8aSUlf Magnusson                    # allocating a new Symbol instance if it's the first time
128890c36d8aSUlf Magnusson                    # we see it.
128990c36d8aSUlf Magnusson                    sym = self._sym_lookup(name, for_eval)
129090c36d8aSUlf Magnusson
129190c36d8aSUlf Magnusson                    if previous == T_CONFIG or previous == T_MENUCONFIG:
129290c36d8aSUlf Magnusson                        # If the previous token is T_(MENU)CONFIG
129390c36d8aSUlf Magnusson                        # ("(menu)config"), we're tokenizing the first line of
129490c36d8aSUlf Magnusson                        # a symbol definition, and should remember this as a
129590c36d8aSUlf Magnusson                        # location where the symbol is defined
129690c36d8aSUlf Magnusson                        sym.def_locations.append((filename, linenr))
129790c36d8aSUlf Magnusson                    else:
129890c36d8aSUlf Magnusson                        # Otherwise, it's a reference to the symbol
129990c36d8aSUlf Magnusson                        sym.ref_locations.append((filename, linenr))
130090c36d8aSUlf Magnusson
130190c36d8aSUlf Magnusson                    append(sym)
130290c36d8aSUlf Magnusson
130390c36d8aSUlf Magnusson            else:
130490c36d8aSUlf Magnusson                # Not an identifier/keyword
130590c36d8aSUlf Magnusson
130690c36d8aSUlf Magnusson                while i < strlen and s[i].isspace():
130790c36d8aSUlf Magnusson                    i += 1
130890c36d8aSUlf Magnusson                if i == strlen:
130990c36d8aSUlf Magnusson                    break
131090c36d8aSUlf Magnusson                c = s[i]
131190c36d8aSUlf Magnusson                i += 1
131290c36d8aSUlf Magnusson
131390c36d8aSUlf Magnusson                # String literal (constant symbol)
131490c36d8aSUlf Magnusson                if c == '"' or c == "'":
131590c36d8aSUlf Magnusson                    if "\\" in s:
131690c36d8aSUlf Magnusson                        # Slow path: This could probably be sped up, but it's a
131790c36d8aSUlf Magnusson                        # very unusual case anyway.
131890c36d8aSUlf Magnusson                        quote = c
131990c36d8aSUlf Magnusson                        val = ""
132090c36d8aSUlf Magnusson                        while 1:
132190c36d8aSUlf Magnusson                            if i >= len(s):
132290c36d8aSUlf Magnusson                                _tokenization_error(s, filename, linenr)
132390c36d8aSUlf Magnusson                            c = s[i]
132490c36d8aSUlf Magnusson                            if c == quote:
132590c36d8aSUlf Magnusson                                break
132690c36d8aSUlf Magnusson                            if c == "\\":
132790c36d8aSUlf Magnusson                                if i + 1 >= len(s):
132890c36d8aSUlf Magnusson                                    _tokenization_error(s, filename, linenr)
132990c36d8aSUlf Magnusson                                val += s[i + 1]
133090c36d8aSUlf Magnusson                                i += 2
133190c36d8aSUlf Magnusson                            else:
133290c36d8aSUlf Magnusson                                val += c
133390c36d8aSUlf Magnusson                                i += 1
133490c36d8aSUlf Magnusson                        i += 1
133590c36d8aSUlf Magnusson                        append(val)
133690c36d8aSUlf Magnusson                    else:
133790c36d8aSUlf Magnusson                        # Fast path: If the string contains no backslashes
133890c36d8aSUlf Magnusson                        # (almost always) we can simply look for the matching
133990c36d8aSUlf Magnusson                        # quote.
134090c36d8aSUlf Magnusson                        end = s.find(c, i)
134190c36d8aSUlf Magnusson                        if end == -1:
134290c36d8aSUlf Magnusson                            _tokenization_error(s, filename, linenr)
134390c36d8aSUlf Magnusson                        append(s[i:end])
134490c36d8aSUlf Magnusson                        i = end + 1
134590c36d8aSUlf Magnusson
134690c36d8aSUlf Magnusson                elif c == "&":
134790c36d8aSUlf Magnusson                    # Invalid characters are ignored
134890c36d8aSUlf Magnusson                    if i >= len(s) or s[i] != "&": continue
134990c36d8aSUlf Magnusson                    append(T_AND)
135090c36d8aSUlf Magnusson                    i += 1
135190c36d8aSUlf Magnusson
135290c36d8aSUlf Magnusson                elif c == "|":
135390c36d8aSUlf Magnusson                    # Invalid characters are ignored
135490c36d8aSUlf Magnusson                    if i >= len(s) or s[i] != "|": continue
135590c36d8aSUlf Magnusson                    append(T_OR)
135690c36d8aSUlf Magnusson                    i += 1
135790c36d8aSUlf Magnusson
135890c36d8aSUlf Magnusson                elif c == "!":
135990c36d8aSUlf Magnusson                    if i < len(s) and s[i] == "=":
136090c36d8aSUlf Magnusson                        append(T_UNEQUAL)
136190c36d8aSUlf Magnusson                        i += 1
136290c36d8aSUlf Magnusson                    else:
136390c36d8aSUlf Magnusson                        append(T_NOT)
136490c36d8aSUlf Magnusson
136590c36d8aSUlf Magnusson                elif c == "=": append(T_EQUAL)
136690c36d8aSUlf Magnusson                elif c == "(": append(T_OPEN_PAREN)
136790c36d8aSUlf Magnusson                elif c == ")": append(T_CLOSE_PAREN)
136890c36d8aSUlf Magnusson                elif c == "#": break # Comment
136990c36d8aSUlf Magnusson
137090c36d8aSUlf Magnusson                else: continue # Invalid characters are ignored
137190c36d8aSUlf Magnusson
137290c36d8aSUlf Magnusson            previous = tokens[-1]
137390c36d8aSUlf Magnusson
137490c36d8aSUlf Magnusson        return _Feed(tokens)
137590c36d8aSUlf Magnusson
137690c36d8aSUlf Magnusson    def _sym_lookup(self, name, for_eval=False):
137790c36d8aSUlf Magnusson        """Fetches the symbol 'name' from the symbol table, creating and
137890c36d8aSUlf Magnusson        registering it if it does not exist. If 'for_eval' is True, the symbol
137990c36d8aSUlf Magnusson        won't be added to the symbol table if it does not exist -- this is for
138090c36d8aSUlf Magnusson        Config.eval()."""
1381f219e013SMasahiro Yamada        if name in self.syms:
1382f219e013SMasahiro Yamada            return self.syms[name]
1383f219e013SMasahiro Yamada
1384f219e013SMasahiro Yamada        new_sym = Symbol()
1385f219e013SMasahiro Yamada        new_sym.config = self
1386f219e013SMasahiro Yamada        new_sym.name = name
138790c36d8aSUlf Magnusson        if for_eval:
1388f219e013SMasahiro Yamada            self._warn("no symbol {0} in configuration".format(name))
138990c36d8aSUlf Magnusson        else:
139090c36d8aSUlf Magnusson            self.syms[name] = new_sym
1391f219e013SMasahiro Yamada        return new_sym
1392f219e013SMasahiro Yamada
1393f219e013SMasahiro Yamada    #
139490c36d8aSUlf Magnusson    # Expression evaluation
1395f219e013SMasahiro Yamada    #
1396f219e013SMasahiro Yamada
1397f219e013SMasahiro Yamada    def _eval_expr(self, expr):
139890c36d8aSUlf Magnusson        """Evaluates an expression to "n", "m", or "y"."""
1399f219e013SMasahiro Yamada
140090c36d8aSUlf Magnusson        # Handles e.g. an "x if y" condition where the "if y" part is missing.
1401f219e013SMasahiro Yamada        if expr is None:
1402f219e013SMasahiro Yamada            return "y"
1403f219e013SMasahiro Yamada
140490c36d8aSUlf Magnusson        res = self._eval_expr_rec(expr)
140590c36d8aSUlf Magnusson        if res == "m":
140690c36d8aSUlf Magnusson            # Promote "m" to "y" if we're running without modules.
140790c36d8aSUlf Magnusson            #
140890c36d8aSUlf Magnusson            # Internally, "m" is often rewritten to "m" && MODULES by both the
140990c36d8aSUlf Magnusson            # C implementation and Kconfiglib, which takes care of cases where
141090c36d8aSUlf Magnusson            # "m" should be demoted to "n" instead.
141190c36d8aSUlf Magnusson            modules_sym = self.syms.get("MODULES")
141290c36d8aSUlf Magnusson            if modules_sym is None or modules_sym.get_value() != "y":
141390c36d8aSUlf Magnusson                return "y"
141490c36d8aSUlf Magnusson        return res
141590c36d8aSUlf Magnusson
141690c36d8aSUlf Magnusson    def _eval_expr_rec(self, expr):
1417f219e013SMasahiro Yamada        if isinstance(expr, Symbol):
1418f219e013SMasahiro Yamada            # Non-bool/tristate symbols are always "n" in a tristate sense,
1419f219e013SMasahiro Yamada            # regardless of their value
1420f219e013SMasahiro Yamada            if expr.type != BOOL and expr.type != TRISTATE:
1421f219e013SMasahiro Yamada                return "n"
1422f219e013SMasahiro Yamada            return expr.get_value()
1423f219e013SMasahiro Yamada
1424f219e013SMasahiro Yamada        if isinstance(expr, str):
1425f219e013SMasahiro Yamada            return expr if (expr == "y" or expr == "m") else "n"
1426f219e013SMasahiro Yamada
142790c36d8aSUlf Magnusson        # Ordered by frequency
1428f219e013SMasahiro Yamada
142990c36d8aSUlf Magnusson        if expr[0] == AND:
1430f219e013SMasahiro Yamada            res = "y"
1431f219e013SMasahiro Yamada            for subexpr in expr[1]:
143290c36d8aSUlf Magnusson                ev = self._eval_expr_rec(subexpr)
1433f219e013SMasahiro Yamada                # Return immediately upon discovering an "n" term
1434f219e013SMasahiro Yamada                if ev == "n":
1435f219e013SMasahiro Yamada                    return "n"
1436f219e013SMasahiro Yamada                if ev == "m":
1437f219e013SMasahiro Yamada                    res = "m"
1438f219e013SMasahiro Yamada            # 'res' is either "m" or "y" here; we already handled the
1439f219e013SMasahiro Yamada            # short-circuiting "n" case in the loop.
1440f219e013SMasahiro Yamada            return res
1441f219e013SMasahiro Yamada
144290c36d8aSUlf Magnusson        if expr[0] == NOT:
144390c36d8aSUlf Magnusson            ev = self._eval_expr_rec(expr[1])
1444f219e013SMasahiro Yamada            if ev == "y":
1445f219e013SMasahiro Yamada                return "n"
1446f219e013SMasahiro Yamada            return "y" if (ev == "n") else "m"
1447f219e013SMasahiro Yamada
144890c36d8aSUlf Magnusson        if expr[0] == OR:
144990c36d8aSUlf Magnusson            res = "n"
145090c36d8aSUlf Magnusson            for subexpr in expr[1]:
145190c36d8aSUlf Magnusson                ev = self._eval_expr_rec(subexpr)
145290c36d8aSUlf Magnusson                # Return immediately upon discovering a "y" term
145390c36d8aSUlf Magnusson                if ev == "y":
145490c36d8aSUlf Magnusson                    return "y"
145590c36d8aSUlf Magnusson                if ev == "m":
145690c36d8aSUlf Magnusson                    res = "m"
145790c36d8aSUlf Magnusson            # 'res' is either "n" or "m" here; we already handled the
145890c36d8aSUlf Magnusson            # short-circuiting "y" case in the loop.
145990c36d8aSUlf Magnusson            return res
1460f219e013SMasahiro Yamada
146190c36d8aSUlf Magnusson        if expr[0] == EQUAL:
146290c36d8aSUlf Magnusson            return "y" if (_str_val(expr[1]) == _str_val(expr[2])) else "n"
146390c36d8aSUlf Magnusson
146490c36d8aSUlf Magnusson        if expr[0] == UNEQUAL:
146590c36d8aSUlf Magnusson            return "y" if (_str_val(expr[1]) != _str_val(expr[2])) else "n"
1466f219e013SMasahiro Yamada
1467f219e013SMasahiro Yamada        _internal_error("Internal error while evaluating expression: "
146890c36d8aSUlf Magnusson                        "unknown operation {0}.".format(expr[0]))
1469f219e013SMasahiro Yamada
1470f219e013SMasahiro Yamada    def _eval_min(self, e1, e2):
147190c36d8aSUlf Magnusson        """Returns the minimum value of the two expressions. Equates None with
147290c36d8aSUlf Magnusson        'y'."""
1473f219e013SMasahiro Yamada        e1_eval = self._eval_expr(e1)
1474f219e013SMasahiro Yamada        e2_eval = self._eval_expr(e2)
1475f219e013SMasahiro Yamada        return e1_eval if tri_less(e1_eval, e2_eval) else e2_eval
1476f219e013SMasahiro Yamada
1477f219e013SMasahiro Yamada    def _eval_max(self, e1, e2):
147890c36d8aSUlf Magnusson        """Returns the maximum value of the two expressions. Equates None with
147990c36d8aSUlf Magnusson        'y'."""
1480f219e013SMasahiro Yamada        e1_eval = self._eval_expr(e1)
1481f219e013SMasahiro Yamada        e2_eval = self._eval_expr(e2)
1482f219e013SMasahiro Yamada        return e1_eval if tri_greater(e1_eval, e2_eval) else e2_eval
1483f219e013SMasahiro Yamada
1484f219e013SMasahiro Yamada    #
148590c36d8aSUlf Magnusson    # Dependency tracking (for caching and invalidation)
1486f219e013SMasahiro Yamada    #
1487f219e013SMasahiro Yamada
1488f219e013SMasahiro Yamada    def _build_dep(self):
1489f219e013SMasahiro Yamada        """Populates the Symbol.dep sets, linking the symbol to the symbols
1490f219e013SMasahiro Yamada        that immediately depend on it in the sense that changing the value of
1491f219e013SMasahiro Yamada        the symbol might affect the values of those other symbols. This is used
1492f219e013SMasahiro Yamada        for caching/invalidation purposes. The calculated sets might be larger
1493f219e013SMasahiro Yamada        than necessary as we don't do any complicated analysis of the
1494f219e013SMasahiro Yamada        expressions."""
1495f219e013SMasahiro Yamada
1496f219e013SMasahiro Yamada        # Adds 'sym' as a directly dependent symbol to all symbols that appear
1497f219e013SMasahiro Yamada        # in the expression 'e'
1498f219e013SMasahiro Yamada        def add_expr_deps(e, sym):
1499f219e013SMasahiro Yamada            for s in _get_expr_syms(e):
1500f219e013SMasahiro Yamada                s.dep.add(sym)
1501f219e013SMasahiro Yamada
1502f219e013SMasahiro Yamada        # The directly dependent symbols of a symbol are:
1503f219e013SMasahiro Yamada        #  - Any symbols whose prompts, default values, rev_dep (select
1504*4e1102f6SUlf Magnusson        #    condition), weak_rev_dep (imply condition) or ranges depend on the
1505*4e1102f6SUlf Magnusson        #    symbol
1506f219e013SMasahiro Yamada        #  - Any symbols that belong to the same choice statement as the symbol
1507f219e013SMasahiro Yamada        #    (these won't be included in 'dep' as that makes the dependency
1508f219e013SMasahiro Yamada        #    graph unwieldy, but Symbol._get_dependent() will include them)
1509f219e013SMasahiro Yamada        #  - Any symbols in a choice statement that depends on the symbol
151090c36d8aSUlf Magnusson        for sym in self.syms_iter():
151190c36d8aSUlf Magnusson            for _, e in sym.prompts:
1512f219e013SMasahiro Yamada                add_expr_deps(e, sym)
1513f219e013SMasahiro Yamada
151490c36d8aSUlf Magnusson            for v, e in sym.def_exprs:
1515f219e013SMasahiro Yamada                add_expr_deps(v, sym)
1516f219e013SMasahiro Yamada                add_expr_deps(e, sym)
1517f219e013SMasahiro Yamada
1518f219e013SMasahiro Yamada            add_expr_deps(sym.rev_dep, sym)
1519*4e1102f6SUlf Magnusson            add_expr_deps(sym.weak_rev_dep, sym)
1520f219e013SMasahiro Yamada
152190c36d8aSUlf Magnusson            for l, u, e in sym.ranges:
1522f219e013SMasahiro Yamada                add_expr_deps(l, sym)
1523f219e013SMasahiro Yamada                add_expr_deps(u, sym)
1524f219e013SMasahiro Yamada                add_expr_deps(e, sym)
1525f219e013SMasahiro Yamada
152690c36d8aSUlf Magnusson            if sym.is_choice_sym:
1527f219e013SMasahiro Yamada                choice = sym.parent
152890c36d8aSUlf Magnusson                for _, e in choice.prompts:
152990c36d8aSUlf Magnusson                    add_expr_deps(e, sym)
153090c36d8aSUlf Magnusson                for _, e in choice.def_exprs:
1531f219e013SMasahiro Yamada                    add_expr_deps(e, sym)
1532f219e013SMasahiro Yamada
153390c36d8aSUlf Magnusson    def _eq_to_sym(self, eq):
153490c36d8aSUlf Magnusson        """_expr_depends_on() helper. For (in)equalities of the form sym = y/m
153590c36d8aSUlf Magnusson        or sym != n, returns sym. For other (in)equalities, returns None."""
153690c36d8aSUlf Magnusson        relation, left, right = eq
1537f219e013SMasahiro Yamada
153890c36d8aSUlf Magnusson        def transform_y_m_n(item):
153990c36d8aSUlf Magnusson            if item is self.y: return "y"
154090c36d8aSUlf Magnusson            if item is self.m: return "m"
154190c36d8aSUlf Magnusson            if item is self.n: return "n"
154290c36d8aSUlf Magnusson            return item
154390c36d8aSUlf Magnusson
154490c36d8aSUlf Magnusson        left = transform_y_m_n(left)
154590c36d8aSUlf Magnusson        right = transform_y_m_n(right)
154690c36d8aSUlf Magnusson
154790c36d8aSUlf Magnusson        # Make sure the symbol (if any) appears to the left
154890c36d8aSUlf Magnusson        if not isinstance(left, Symbol):
154990c36d8aSUlf Magnusson            left, right = right, left
155090c36d8aSUlf Magnusson        if not isinstance(left, Symbol):
155190c36d8aSUlf Magnusson            return None
155290c36d8aSUlf Magnusson        if (relation == EQUAL and (right == "y" or right == "m")) or \
155390c36d8aSUlf Magnusson           (relation == UNEQUAL and right == "n"):
155490c36d8aSUlf Magnusson            return left
155590c36d8aSUlf Magnusson        return None
155690c36d8aSUlf Magnusson
155790c36d8aSUlf Magnusson    def _expr_depends_on(self, expr, sym):
155890c36d8aSUlf Magnusson        """Reimplementation of expr_depends_symbol() from mconf.c. Used to
155990c36d8aSUlf Magnusson        determine if a submenu should be implicitly created, which influences
156090c36d8aSUlf Magnusson        what items inside choice statements are considered choice items."""
156190c36d8aSUlf Magnusson        if expr is None:
156290c36d8aSUlf Magnusson            return False
156390c36d8aSUlf Magnusson
156490c36d8aSUlf Magnusson        def rec(expr):
156590c36d8aSUlf Magnusson            if isinstance(expr, str):
156690c36d8aSUlf Magnusson                return False
156790c36d8aSUlf Magnusson            if isinstance(expr, Symbol):
156890c36d8aSUlf Magnusson                return expr is sym
156990c36d8aSUlf Magnusson
157090c36d8aSUlf Magnusson            if expr[0] in (EQUAL, UNEQUAL):
157190c36d8aSUlf Magnusson                return self._eq_to_sym(expr) is sym
157290c36d8aSUlf Magnusson            if expr[0] == AND:
157390c36d8aSUlf Magnusson                for and_expr in expr[1]:
157490c36d8aSUlf Magnusson                    if rec(and_expr):
157590c36d8aSUlf Magnusson                        return True
157690c36d8aSUlf Magnusson            return False
157790c36d8aSUlf Magnusson
157890c36d8aSUlf Magnusson        return rec(expr)
157990c36d8aSUlf Magnusson
158090c36d8aSUlf Magnusson    def _invalidate_all(self):
158190c36d8aSUlf Magnusson        for sym in self.syms_iter():
158290c36d8aSUlf Magnusson            sym._invalidate()
158390c36d8aSUlf Magnusson
158490c36d8aSUlf Magnusson    #
158590c36d8aSUlf Magnusson    # Printing and misc.
158690c36d8aSUlf Magnusson    #
158790c36d8aSUlf Magnusson
158890c36d8aSUlf Magnusson    def _expand_sym_refs(self, s):
158990c36d8aSUlf Magnusson        """Expands $-references to symbols in 's' to symbol values, or to the
159090c36d8aSUlf Magnusson        empty string for undefined symbols."""
159190c36d8aSUlf Magnusson
159290c36d8aSUlf Magnusson        while 1:
159390c36d8aSUlf Magnusson            sym_ref_match = _sym_ref_re_search(s)
159490c36d8aSUlf Magnusson            if sym_ref_match is None:
159590c36d8aSUlf Magnusson                return s
159690c36d8aSUlf Magnusson
159790c36d8aSUlf Magnusson            sym_name = sym_ref_match.group(0)[1:]
159890c36d8aSUlf Magnusson            sym = self.syms.get(sym_name)
159990c36d8aSUlf Magnusson            expansion = "" if sym is None else sym.get_value()
160090c36d8aSUlf Magnusson
160190c36d8aSUlf Magnusson            s = s[:sym_ref_match.start()] + \
160290c36d8aSUlf Magnusson                expansion + \
160390c36d8aSUlf Magnusson                s[sym_ref_match.end():]
160490c36d8aSUlf Magnusson
160590c36d8aSUlf Magnusson    def _expr_val_str(self, expr, no_value_str="(none)",
160690c36d8aSUlf Magnusson                      get_val_instead_of_eval=False):
160790c36d8aSUlf Magnusson        """Printing helper. Returns a string with 'expr' and its value.
160890c36d8aSUlf Magnusson
160990c36d8aSUlf Magnusson        no_value_str: String to return when 'expr' is missing (None).
161090c36d8aSUlf Magnusson
161190c36d8aSUlf Magnusson        get_val_instead_of_eval: Assume 'expr' is a symbol or string (constant
161290c36d8aSUlf Magnusson          symbol) and get its value directly instead of evaluating it to a
161390c36d8aSUlf Magnusson          tristate value."""
1614f219e013SMasahiro Yamada
1615f219e013SMasahiro Yamada        if expr is None:
1616f219e013SMasahiro Yamada            return no_value_str
1617f219e013SMasahiro Yamada
1618f219e013SMasahiro Yamada        if get_val_instead_of_eval:
1619f219e013SMasahiro Yamada            if isinstance(expr, str):
1620f219e013SMasahiro Yamada                return _expr_to_str(expr)
1621f219e013SMasahiro Yamada            val = expr.get_value()
1622f219e013SMasahiro Yamada        else:
1623f219e013SMasahiro Yamada            val = self._eval_expr(expr)
1624f219e013SMasahiro Yamada
1625f219e013SMasahiro Yamada        return "{0} (value: {1})".format(_expr_to_str(expr), _expr_to_str(val))
1626f219e013SMasahiro Yamada
1627f219e013SMasahiro Yamada    def _get_sym_or_choice_str(self, sc):
1628f219e013SMasahiro Yamada        """Symbols and choices have many properties in common, so we factor out
1629f219e013SMasahiro Yamada        common __str__() stuff here. "sc" is short for "symbol or choice"."""
1630f219e013SMasahiro Yamada
1631f219e013SMasahiro Yamada        # As we deal a lot with string representations here, use some
1632f219e013SMasahiro Yamada        # convenient shorthand:
1633f219e013SMasahiro Yamada        s = _expr_to_str
1634f219e013SMasahiro Yamada
1635f219e013SMasahiro Yamada        #
1636f219e013SMasahiro Yamada        # Common symbol/choice properties
1637f219e013SMasahiro Yamada        #
1638f219e013SMasahiro Yamada
163990c36d8aSUlf Magnusson        user_val_str = "(no user value)" if sc.user_val is None else \
164090c36d8aSUlf Magnusson                       s(sc.user_val)
1641f219e013SMasahiro Yamada
1642f219e013SMasahiro Yamada        # Build prompts string
164390c36d8aSUlf Magnusson        if not sc.prompts:
1644f219e013SMasahiro Yamada            prompts_str = " (no prompts)"
1645f219e013SMasahiro Yamada        else:
1646f219e013SMasahiro Yamada            prompts_str_rows = []
164790c36d8aSUlf Magnusson            for prompt, cond_expr in sc.orig_prompts:
164890c36d8aSUlf Magnusson                prompts_str_rows.append(
1649*4e1102f6SUlf Magnusson                    ' "{0}"'.format(prompt) if cond_expr is None else
165090c36d8aSUlf Magnusson                    ' "{0}" if {1}'.format(prompt,
165190c36d8aSUlf Magnusson                                           self._expr_val_str(cond_expr)))
1652f219e013SMasahiro Yamada            prompts_str = "\n".join(prompts_str_rows)
1653f219e013SMasahiro Yamada
1654f219e013SMasahiro Yamada        # Build locations string
1655*4e1102f6SUlf Magnusson        locations_str = "(no locations)" if not sc.def_locations else \
1656*4e1102f6SUlf Magnusson                        " ".join(["{0}:{1}".format(filename, linenr) for
1657*4e1102f6SUlf Magnusson                                  filename, linenr in sc.def_locations])
1658f219e013SMasahiro Yamada
165990c36d8aSUlf Magnusson        # Build additional-dependencies-from-menus-and-ifs string
166090c36d8aSUlf Magnusson        additional_deps_str = " " + \
166190c36d8aSUlf Magnusson          self._expr_val_str(sc.deps_from_containing,
1662f219e013SMasahiro Yamada                             "(no additional dependencies)")
1663f219e013SMasahiro Yamada
1664f219e013SMasahiro Yamada        #
1665f219e013SMasahiro Yamada        # Symbol-specific stuff
1666f219e013SMasahiro Yamada        #
1667f219e013SMasahiro Yamada
1668f219e013SMasahiro Yamada        if isinstance(sc, Symbol):
1669f219e013SMasahiro Yamada            # Build ranges string
1670f219e013SMasahiro Yamada            if isinstance(sc, Symbol):
167190c36d8aSUlf Magnusson                if not sc.ranges:
1672f219e013SMasahiro Yamada                    ranges_str = " (no ranges)"
1673f219e013SMasahiro Yamada                else:
1674f219e013SMasahiro Yamada                    ranges_str_rows = []
167590c36d8aSUlf Magnusson                    for l, u, cond_expr in sc.ranges:
1676*4e1102f6SUlf Magnusson                        ranges_str_rows.append(
1677*4e1102f6SUlf Magnusson                            " [{0}, {1}]".format(s(l), s(u))
1678*4e1102f6SUlf Magnusson                            if cond_expr is None else
1679*4e1102f6SUlf Magnusson                            " [{0}, {1}] if {2}"
1680*4e1102f6SUlf Magnusson                            .format(s(l), s(u), self._expr_val_str(cond_expr)))
1681f219e013SMasahiro Yamada                    ranges_str = "\n".join(ranges_str_rows)
1682f219e013SMasahiro Yamada
1683f219e013SMasahiro Yamada            # Build default values string
168490c36d8aSUlf Magnusson            if not sc.def_exprs:
1685f219e013SMasahiro Yamada                defaults_str = " (no default values)"
1686f219e013SMasahiro Yamada            else:
1687f219e013SMasahiro Yamada                defaults_str_rows = []
168890c36d8aSUlf Magnusson                for val_expr, cond_expr in sc.orig_def_exprs:
168990c36d8aSUlf Magnusson                    row_str = " " + self._expr_val_str(val_expr, "(none)",
169090c36d8aSUlf Magnusson                                                       sc.type == STRING)
1691f219e013SMasahiro Yamada                    defaults_str_rows.append(row_str)
169290c36d8aSUlf Magnusson                    defaults_str_rows.append("  Condition: " +
169390c36d8aSUlf Magnusson                                               self._expr_val_str(cond_expr))
1694f219e013SMasahiro Yamada                defaults_str = "\n".join(defaults_str_rows)
1695f219e013SMasahiro Yamada
1696f219e013SMasahiro Yamada            # Build selects string
169790c36d8aSUlf Magnusson            if not sc.orig_selects:
1698f219e013SMasahiro Yamada                selects_str = " (no selects)"
1699f219e013SMasahiro Yamada            else:
1700f219e013SMasahiro Yamada                selects_str_rows = []
170190c36d8aSUlf Magnusson                for target, cond_expr in sc.orig_selects:
170290c36d8aSUlf Magnusson                    selects_str_rows.append(
1703*4e1102f6SUlf Magnusson                        " {0}".format(target.name) if cond_expr is None else
170490c36d8aSUlf Magnusson                        " {0} if {1}".format(target.name,
170590c36d8aSUlf Magnusson                                             self._expr_val_str(cond_expr)))
1706f219e013SMasahiro Yamada                selects_str = "\n".join(selects_str_rows)
1707f219e013SMasahiro Yamada
1708*4e1102f6SUlf Magnusson            # Build implies string
1709*4e1102f6SUlf Magnusson            if not sc.orig_implies:
1710*4e1102f6SUlf Magnusson                implies_str = " (no implies)"
1711*4e1102f6SUlf Magnusson            else:
1712*4e1102f6SUlf Magnusson                implies_str_rows = []
1713*4e1102f6SUlf Magnusson                for target, cond_expr in sc.orig_implies:
1714*4e1102f6SUlf Magnusson                    implies_str_rows.append(
1715*4e1102f6SUlf Magnusson                        " {0}".format(target.name) if cond_expr is None else
1716*4e1102f6SUlf Magnusson                        " {0} if {1}".format(target.name,
1717*4e1102f6SUlf Magnusson                                             self._expr_val_str(cond_expr)))
1718*4e1102f6SUlf Magnusson                implies_str = "\n".join(implies_str_rows)
1719*4e1102f6SUlf Magnusson
172090c36d8aSUlf Magnusson            res = _lines("Symbol " +
172190c36d8aSUlf Magnusson                           ("(no name)" if sc.name is None else sc.name),
172290c36d8aSUlf Magnusson                         "Type           : " + TYPENAME[sc.type],
172390c36d8aSUlf Magnusson                         "Value          : " + s(sc.get_value()),
172490c36d8aSUlf Magnusson                         "User value     : " + user_val_str,
172590c36d8aSUlf Magnusson                         "Visibility     : " + s(_get_visibility(sc)),
172690c36d8aSUlf Magnusson                         "Is choice item : " + BOOL_STR[sc.is_choice_sym],
172790c36d8aSUlf Magnusson                         "Is defined     : " + BOOL_STR[sc.is_defined_],
172890c36d8aSUlf Magnusson                         "Is from env.   : " + BOOL_STR[sc.is_from_env],
172990c36d8aSUlf Magnusson                         "Is special     : " + BOOL_STR[sc.is_special_] + "\n")
173090c36d8aSUlf Magnusson            if sc.ranges:
173190c36d8aSUlf Magnusson                res += _lines("Ranges:", ranges_str + "\n")
173290c36d8aSUlf Magnusson            res += _lines("Prompts:",
1733f219e013SMasahiro Yamada                          prompts_str,
1734f219e013SMasahiro Yamada                          "Default values:",
1735f219e013SMasahiro Yamada                          defaults_str,
1736f219e013SMasahiro Yamada                          "Selects:",
1737f219e013SMasahiro Yamada                          selects_str,
1738*4e1102f6SUlf Magnusson                          "Implies:",
1739*4e1102f6SUlf Magnusson                          implies_str,
174090c36d8aSUlf Magnusson                          "Reverse (select-related) dependencies:",
1741*4e1102f6SUlf Magnusson                          " (no reverse dependencies)"
1742*4e1102f6SUlf Magnusson                          if sc.rev_dep == "n"
174390c36d8aSUlf Magnusson                          else " " + self._expr_val_str(sc.rev_dep),
1744*4e1102f6SUlf Magnusson                          "Weak reverse (imply-related) dependencies:",
1745*4e1102f6SUlf Magnusson                          " (no weak reverse dependencies)"
1746*4e1102f6SUlf Magnusson                          if sc.weak_rev_dep == "n"
1747*4e1102f6SUlf Magnusson                          else " " + self._expr_val_str(sc.weak_rev_dep),
174890c36d8aSUlf Magnusson                          "Additional dependencies from enclosing menus "
174990c36d8aSUlf Magnusson                            "and ifs:",
1750f219e013SMasahiro Yamada                          additional_deps_str,
1751f219e013SMasahiro Yamada                          "Locations: " + locations_str)
1752f219e013SMasahiro Yamada
1753f219e013SMasahiro Yamada            return res
1754f219e013SMasahiro Yamada
1755f219e013SMasahiro Yamada        #
1756f219e013SMasahiro Yamada        # Choice-specific stuff
1757f219e013SMasahiro Yamada        #
1758f219e013SMasahiro Yamada
1759f219e013SMasahiro Yamada        # Build selected symbol string
1760f219e013SMasahiro Yamada        sel = sc.get_selection()
176190c36d8aSUlf Magnusson        sel_str = "(no selection)" if sel is None else sel.name
1762f219e013SMasahiro Yamada
1763f219e013SMasahiro Yamada        # Build default values string
176490c36d8aSUlf Magnusson        if not sc.def_exprs:
1765f219e013SMasahiro Yamada            defaults_str = " (no default values)"
1766f219e013SMasahiro Yamada        else:
1767f219e013SMasahiro Yamada            defaults_str_rows = []
176890c36d8aSUlf Magnusson            for sym, cond_expr in sc.orig_def_exprs:
1769*4e1102f6SUlf Magnusson                defaults_str_rows.append(
1770*4e1102f6SUlf Magnusson                    " {0}".format(sym.name) if cond_expr is None else
1771*4e1102f6SUlf Magnusson                    " {0} if {1}".format(sym.name,
177290c36d8aSUlf Magnusson                                         self._expr_val_str(cond_expr)))
1773f219e013SMasahiro Yamada            defaults_str = "\n".join(defaults_str_rows)
1774f219e013SMasahiro Yamada
1775f219e013SMasahiro Yamada        # Build contained symbols string
177690c36d8aSUlf Magnusson        names = [sym.name for sym in sc.actual_symbols]
177790c36d8aSUlf Magnusson        syms_string = " ".join(names) if names else "(empty)"
1778f219e013SMasahiro Yamada
177990c36d8aSUlf Magnusson        return _lines("Choice",
178090c36d8aSUlf Magnusson                      "Name (for named choices): " +
178190c36d8aSUlf Magnusson                        ("(no name)" if sc.name is None else sc.name),
178290c36d8aSUlf Magnusson                      "Type            : " + TYPENAME[sc.type],
1783f219e013SMasahiro Yamada                      "Selected symbol : " + sel_str,
178490c36d8aSUlf Magnusson                      "User value      : " + user_val_str,
178590c36d8aSUlf Magnusson                      "Mode            : " + s(sc.get_mode()),
178690c36d8aSUlf Magnusson                      "Visibility      : " + s(_get_visibility(sc)),
178790c36d8aSUlf Magnusson                      "Optional        : " + BOOL_STR[sc.optional],
1788f219e013SMasahiro Yamada                      "Prompts:",
1789f219e013SMasahiro Yamada                      prompts_str,
1790f219e013SMasahiro Yamada                      "Defaults:",
1791f219e013SMasahiro Yamada                      defaults_str,
1792f219e013SMasahiro Yamada                      "Choice symbols:",
1793f219e013SMasahiro Yamada                      " " + syms_string,
179490c36d8aSUlf Magnusson                      "Additional dependencies from enclosing menus and "
179590c36d8aSUlf Magnusson                        "ifs:",
1796f219e013SMasahiro Yamada                      additional_deps_str,
1797f219e013SMasahiro Yamada                      "Locations: " + locations_str)
1798f219e013SMasahiro Yamada
1799f219e013SMasahiro Yamada    def _warn(self, msg, filename=None, linenr=None):
1800f219e013SMasahiro Yamada        """For printing warnings to stderr."""
18018639f69aSSimon Glass        msg = _build_msg("warning: " + msg, filename, linenr)
1802f219e013SMasahiro Yamada        if self.print_warnings:
18038639f69aSSimon Glass            sys.stderr.write(msg + "\n")
18048639f69aSSimon Glass        self._warnings.append(msg)
1805f219e013SMasahiro Yamada
180690c36d8aSUlf Magnussonclass Item(object):
1807f219e013SMasahiro Yamada
1808f219e013SMasahiro Yamada    """Base class for symbols and other Kconfig constructs. Subclasses are
1809f219e013SMasahiro Yamada    Symbol, Choice, Menu, and Comment."""
1810f219e013SMasahiro Yamada
1811f219e013SMasahiro Yamada    def is_symbol(self):
181290c36d8aSUlf Magnusson        """Returns True if the item is a symbol. Short for
1813f219e013SMasahiro Yamada        isinstance(item, kconfiglib.Symbol)."""
1814f219e013SMasahiro Yamada        return isinstance(self, Symbol)
1815f219e013SMasahiro Yamada
1816f219e013SMasahiro Yamada    def is_choice(self):
181790c36d8aSUlf Magnusson        """Returns True if the item is a choice. Short for
1818f219e013SMasahiro Yamada        isinstance(item, kconfiglib.Choice)."""
1819f219e013SMasahiro Yamada        return isinstance(self, Choice)
1820f219e013SMasahiro Yamada
1821f219e013SMasahiro Yamada    def is_menu(self):
182290c36d8aSUlf Magnusson        """Returns True if the item is a menu. Short for
1823f219e013SMasahiro Yamada        isinstance(item, kconfiglib.Menu)."""
1824f219e013SMasahiro Yamada        return isinstance(self, Menu)
1825f219e013SMasahiro Yamada
1826f219e013SMasahiro Yamada    def is_comment(self):
182790c36d8aSUlf Magnusson        """Returns True if the item is a comment. Short for
1828f219e013SMasahiro Yamada        isinstance(item, kconfiglib.Comment)."""
1829f219e013SMasahiro Yamada        return isinstance(self, Comment)
1830f219e013SMasahiro Yamada
183190c36d8aSUlf Magnussonclass Symbol(Item):
1832f219e013SMasahiro Yamada
1833f219e013SMasahiro Yamada    """Represents a configuration symbol - e.g. FOO for
1834f219e013SMasahiro Yamada
1835f219e013SMasahiro Yamada    config FOO
1836f219e013SMasahiro Yamada        ..."""
1837f219e013SMasahiro Yamada
1838f219e013SMasahiro Yamada    #
1839f219e013SMasahiro Yamada    # Public interface
1840f219e013SMasahiro Yamada    #
1841f219e013SMasahiro Yamada
184290c36d8aSUlf Magnusson    def get_config(self):
184390c36d8aSUlf Magnusson        """Returns the Config instance this symbol is from."""
184490c36d8aSUlf Magnusson        return self.config
184590c36d8aSUlf Magnusson
184690c36d8aSUlf Magnusson    def get_name(self):
184790c36d8aSUlf Magnusson        """Returns the name of the symbol."""
184890c36d8aSUlf Magnusson        return self.name
184990c36d8aSUlf Magnusson
185090c36d8aSUlf Magnusson    def get_type(self):
185190c36d8aSUlf Magnusson        """Returns the type of the symbol: one of UNKNOWN, BOOL, TRISTATE,
185290c36d8aSUlf Magnusson        STRING, HEX, or INT. These are defined at the top level of the module,
185390c36d8aSUlf Magnusson        so you'd do something like
185490c36d8aSUlf Magnusson
185590c36d8aSUlf Magnusson        if sym.get_type() == kconfiglib.STRING:
185690c36d8aSUlf Magnusson            ..."""
185790c36d8aSUlf Magnusson        return self.type
185890c36d8aSUlf Magnusson
185990c36d8aSUlf Magnusson    def get_prompts(self):
186090c36d8aSUlf Magnusson        """Returns a list of prompts defined for the symbol, in the order they
186190c36d8aSUlf Magnusson        appear in the configuration files. Returns the empty list for symbols
186290c36d8aSUlf Magnusson        with no prompt.
186390c36d8aSUlf Magnusson
186490c36d8aSUlf Magnusson        This list will have a single entry for the vast majority of symbols
186590c36d8aSUlf Magnusson        having prompts, but having multiple prompts for a single symbol is
186690c36d8aSUlf Magnusson        possible through having multiple 'config' entries for it."""
186790c36d8aSUlf Magnusson        return [prompt for prompt, _ in self.orig_prompts]
186890c36d8aSUlf Magnusson
186990c36d8aSUlf Magnusson    def get_help(self):
187090c36d8aSUlf Magnusson        """Returns the help text of the symbol, or None if the symbol has no
187190c36d8aSUlf Magnusson        help text."""
187290c36d8aSUlf Magnusson        return self.help
187390c36d8aSUlf Magnusson
187490c36d8aSUlf Magnusson    def get_parent(self):
187590c36d8aSUlf Magnusson        """Returns the menu or choice statement that contains the symbol, or
187690c36d8aSUlf Magnusson        None if the symbol is at the top level. Note that if statements are
187790c36d8aSUlf Magnusson        treated as syntactic and do not have an explicit class
187890c36d8aSUlf Magnusson        representation."""
187990c36d8aSUlf Magnusson        return self.parent
188090c36d8aSUlf Magnusson
188190c36d8aSUlf Magnusson    def get_def_locations(self):
188290c36d8aSUlf Magnusson        """Returns a list of (filename, linenr) tuples, where filename (string)
188390c36d8aSUlf Magnusson        and linenr (int) represent a location where the symbol is defined. For
188490c36d8aSUlf Magnusson        the vast majority of symbols this list will only contain one element.
188590c36d8aSUlf Magnusson        For the following Kconfig, FOO would get two entries: the lines marked
188690c36d8aSUlf Magnusson        with *.
188790c36d8aSUlf Magnusson
188890c36d8aSUlf Magnusson        config FOO *
188990c36d8aSUlf Magnusson            bool "foo prompt 1"
189090c36d8aSUlf Magnusson
189190c36d8aSUlf Magnusson        config FOO *
189290c36d8aSUlf Magnusson            bool "foo prompt 2"
189390c36d8aSUlf Magnusson        """
189490c36d8aSUlf Magnusson        return self.def_locations
189590c36d8aSUlf Magnusson
189690c36d8aSUlf Magnusson    def get_ref_locations(self):
189790c36d8aSUlf Magnusson        """Returns a list of (filename, linenr) tuples, where filename (string)
189890c36d8aSUlf Magnusson        and linenr (int) represent a location where the symbol is referenced in
189990c36d8aSUlf Magnusson        the configuration. For example, the lines marked by * would be included
190090c36d8aSUlf Magnusson        for FOO below:
190190c36d8aSUlf Magnusson
190290c36d8aSUlf Magnusson        config A
190390c36d8aSUlf Magnusson            bool
190490c36d8aSUlf Magnusson            default BAR || FOO *
190590c36d8aSUlf Magnusson
190690c36d8aSUlf Magnusson        config B
190790c36d8aSUlf Magnusson            tristate
190890c36d8aSUlf Magnusson            depends on FOO *
190990c36d8aSUlf Magnusson            default m if FOO *
191090c36d8aSUlf Magnusson
191190c36d8aSUlf Magnusson        if FOO *
191290c36d8aSUlf Magnusson            config A
191390c36d8aSUlf Magnusson                bool "A"
191490c36d8aSUlf Magnusson        endif
191590c36d8aSUlf Magnusson
191690c36d8aSUlf Magnusson        config FOO (definition not included)
191790c36d8aSUlf Magnusson            bool
191890c36d8aSUlf Magnusson        """
191990c36d8aSUlf Magnusson        return self.ref_locations
192090c36d8aSUlf Magnusson
1921f219e013SMasahiro Yamada    def get_value(self):
1922f219e013SMasahiro Yamada        """Calculate and return the value of the symbol. See also
1923f219e013SMasahiro Yamada        Symbol.set_user_value()."""
1924f219e013SMasahiro Yamada
192590c36d8aSUlf Magnusson        if self.cached_val is not None:
192690c36d8aSUlf Magnusson            return self.cached_val
1927f219e013SMasahiro Yamada
1928f219e013SMasahiro Yamada        # As a quirk of Kconfig, undefined symbols get their name as their
1929f219e013SMasahiro Yamada        # value. This is why things like "FOO = bar" work for seeing if FOO has
1930f219e013SMasahiro Yamada        # the value "bar".
1931f219e013SMasahiro Yamada        if self.type == UNKNOWN:
193290c36d8aSUlf Magnusson            self.cached_val = self.name
1933f219e013SMasahiro Yamada            return self.name
1934f219e013SMasahiro Yamada
193590c36d8aSUlf Magnusson        new_val = DEFAULT_VALUE[self.type]
193690c36d8aSUlf Magnusson        vis = _get_visibility(self)
1937f219e013SMasahiro Yamada
193890c36d8aSUlf Magnusson        # This is easiest to calculate together with the value
193990c36d8aSUlf Magnusson        self.write_to_conf = False
1940f219e013SMasahiro Yamada
1941f219e013SMasahiro Yamada        if self.type == BOOL or self.type == TRISTATE:
1942f219e013SMasahiro Yamada            # The visibility and mode (modules-only or single-selection) of
194390c36d8aSUlf Magnusson            # choice items will be taken into account in _get_visibility()
194490c36d8aSUlf Magnusson            if self.is_choice_sym:
1945f219e013SMasahiro Yamada                if vis != "n":
1946f219e013SMasahiro Yamada                    choice = self.parent
1947f219e013SMasahiro Yamada                    mode = choice.get_mode()
1948f219e013SMasahiro Yamada
1949f219e013SMasahiro Yamada                    self.write_to_conf = (mode != "n")
1950f219e013SMasahiro Yamada
1951f219e013SMasahiro Yamada                    if mode == "y":
1952*4e1102f6SUlf Magnusson                        new_val = "y" if choice.get_selection() is self \
1953*4e1102f6SUlf Magnusson                                  else "n"
1954f219e013SMasahiro Yamada                    elif mode == "m":
1955f219e013SMasahiro Yamada                        if self.user_val == "m" or self.user_val == "y":
1956f219e013SMasahiro Yamada                            new_val = "m"
1957f219e013SMasahiro Yamada
1958f219e013SMasahiro Yamada            else:
195990c36d8aSUlf Magnusson                # If the symbol is visible and has a user value, use that.
1960*4e1102f6SUlf Magnusson                # Otherwise, look at defaults and weak reverse dependencies
1961*4e1102f6SUlf Magnusson                # (implies).
1962*4e1102f6SUlf Magnusson                use_defaults_and_weak_rev_deps = True
1963f219e013SMasahiro Yamada
1964f219e013SMasahiro Yamada                if vis != "n":
1965f219e013SMasahiro Yamada                    self.write_to_conf = True
1966f219e013SMasahiro Yamada                    if self.user_val is not None:
1967f219e013SMasahiro Yamada                        new_val = self.config._eval_min(self.user_val, vis)
1968*4e1102f6SUlf Magnusson                        use_defaults_and_weak_rev_deps = False
1969f219e013SMasahiro Yamada
1970*4e1102f6SUlf Magnusson                if use_defaults_and_weak_rev_deps:
197190c36d8aSUlf Magnusson                    for val_expr, cond_expr in self.def_exprs:
1972f219e013SMasahiro Yamada                        cond_eval = self.config._eval_expr(cond_expr)
1973f219e013SMasahiro Yamada                        if cond_eval != "n":
1974f219e013SMasahiro Yamada                            self.write_to_conf = True
197590c36d8aSUlf Magnusson                            new_val = self.config._eval_min(val_expr,
197690c36d8aSUlf Magnusson                                                            cond_eval)
1977f219e013SMasahiro Yamada                            break
1978f219e013SMasahiro Yamada
1979*4e1102f6SUlf Magnusson                    weak_rev_dep_val = \
1980*4e1102f6SUlf Magnusson                        self.config._eval_expr(self.weak_rev_dep)
1981*4e1102f6SUlf Magnusson                    if weak_rev_dep_val != "n":
1982*4e1102f6SUlf Magnusson                        self.write_to_conf = True
1983*4e1102f6SUlf Magnusson                        new_val = self.config._eval_max(new_val,
1984*4e1102f6SUlf Magnusson                                                        weak_rev_dep_val)
1985*4e1102f6SUlf Magnusson
198690c36d8aSUlf Magnusson                # Reverse (select-related) dependencies take precedence
1987f219e013SMasahiro Yamada                rev_dep_val = self.config._eval_expr(self.rev_dep)
1988f219e013SMasahiro Yamada                if rev_dep_val != "n":
1989f219e013SMasahiro Yamada                    self.write_to_conf = True
1990f219e013SMasahiro Yamada                    new_val = self.config._eval_max(new_val, rev_dep_val)
1991f219e013SMasahiro Yamada
1992*4e1102f6SUlf Magnusson            # We need to promote "m" to "y" in two circumstances:
1993*4e1102f6SUlf Magnusson            #  1) If our type is boolean
1994*4e1102f6SUlf Magnusson            #  2) If our weak_rev_dep (from IMPLY) is "y"
1995*4e1102f6SUlf Magnusson            if new_val == "m" and \
1996*4e1102f6SUlf Magnusson               (self.type == BOOL or
1997*4e1102f6SUlf Magnusson                self.config._eval_expr(self.weak_rev_dep) == "y"):
1998f219e013SMasahiro Yamada                new_val = "y"
1999f219e013SMasahiro Yamada
200090c36d8aSUlf Magnusson        elif self.type == INT or self.type == HEX:
2001f219e013SMasahiro Yamada            has_active_range = False
2002f219e013SMasahiro Yamada            low = None
2003f219e013SMasahiro Yamada            high = None
2004f219e013SMasahiro Yamada            use_defaults = True
2005f219e013SMasahiro Yamada
2006f219e013SMasahiro Yamada            base = 16 if self.type == HEX else 10
2007f219e013SMasahiro Yamada
200890c36d8aSUlf Magnusson            for l, h, cond_expr in self.ranges:
2009f219e013SMasahiro Yamada                if self.config._eval_expr(cond_expr) != "n":
2010f219e013SMasahiro Yamada                    has_active_range = True
2011f219e013SMasahiro Yamada
201290c36d8aSUlf Magnusson                    low_str = _str_val(l)
201390c36d8aSUlf Magnusson                    high_str = _str_val(h)
2014f219e013SMasahiro Yamada                    low = int(low_str, base) if \
2015f219e013SMasahiro Yamada                      _is_base_n(low_str, base) else 0
2016f219e013SMasahiro Yamada                    high = int(high_str, base) if \
2017f219e013SMasahiro Yamada                      _is_base_n(high_str, base) else 0
2018f219e013SMasahiro Yamada
2019f219e013SMasahiro Yamada                    break
2020f219e013SMasahiro Yamada
2021f219e013SMasahiro Yamada            if vis != "n":
2022f219e013SMasahiro Yamada                self.write_to_conf = True
2023f219e013SMasahiro Yamada
2024f219e013SMasahiro Yamada                if self.user_val is not None and \
2025f219e013SMasahiro Yamada                   _is_base_n(self.user_val, base) and \
2026f219e013SMasahiro Yamada                   (not has_active_range or
2027f219e013SMasahiro Yamada                    low <= int(self.user_val, base) <= high):
2028f219e013SMasahiro Yamada
2029f219e013SMasahiro Yamada                    # If the user value is OK, it is stored in exactly the same
2030f219e013SMasahiro Yamada                    # form as specified in the assignment (with or without
2031f219e013SMasahiro Yamada                    # "0x", etc).
2032f219e013SMasahiro Yamada
2033f219e013SMasahiro Yamada                    use_defaults = False
2034f219e013SMasahiro Yamada                    new_val = self.user_val
2035f219e013SMasahiro Yamada
2036f219e013SMasahiro Yamada            if use_defaults:
203790c36d8aSUlf Magnusson                for val_expr, cond_expr in self.def_exprs:
2038f219e013SMasahiro Yamada                    if self.config._eval_expr(cond_expr) != "n":
2039f219e013SMasahiro Yamada                        self.write_to_conf = True
2040f219e013SMasahiro Yamada
2041f219e013SMasahiro Yamada                        # If the default value is OK, it is stored in exactly
2042f219e013SMasahiro Yamada                        # the same form as specified. Otherwise, it is clamped
2043f219e013SMasahiro Yamada                        # to the range, and the output has "0x" as appropriate
2044f219e013SMasahiro Yamada                        # for the type.
2045f219e013SMasahiro Yamada
204690c36d8aSUlf Magnusson                        new_val = _str_val(val_expr)
2047f219e013SMasahiro Yamada
2048f219e013SMasahiro Yamada                        if _is_base_n(new_val, base):
2049f219e013SMasahiro Yamada                            new_val_num = int(new_val, base)
2050f219e013SMasahiro Yamada                            if has_active_range:
2051f219e013SMasahiro Yamada                                clamped_val = None
2052f219e013SMasahiro Yamada
2053f219e013SMasahiro Yamada                                if new_val_num < low:
2054f219e013SMasahiro Yamada                                    clamped_val = low
2055f219e013SMasahiro Yamada                                elif new_val_num > high:
2056f219e013SMasahiro Yamada                                    clamped_val = high
2057f219e013SMasahiro Yamada
2058f219e013SMasahiro Yamada                                if clamped_val is not None:
2059f219e013SMasahiro Yamada                                    new_val = (hex(clamped_val) if \
2060f219e013SMasahiro Yamada                                      self.type == HEX else str(clamped_val))
2061f219e013SMasahiro Yamada
2062f219e013SMasahiro Yamada                            break
2063f219e013SMasahiro Yamada                else: # For the for loop
2064f219e013SMasahiro Yamada                    # If no user value or default kicks in but the hex/int has
2065f219e013SMasahiro Yamada                    # an active range, then the low end of the range is used,
2066f219e013SMasahiro Yamada                    # provided it's > 0, with "0x" prepended as appropriate.
2067f219e013SMasahiro Yamada                    if has_active_range and low > 0:
2068f219e013SMasahiro Yamada                        new_val = (hex(low) if self.type == HEX else str(low))
2069f219e013SMasahiro Yamada
207090c36d8aSUlf Magnusson        elif self.type == STRING:
207190c36d8aSUlf Magnusson            use_defaults = True
207290c36d8aSUlf Magnusson
207390c36d8aSUlf Magnusson            if vis != "n":
207490c36d8aSUlf Magnusson                self.write_to_conf = True
207590c36d8aSUlf Magnusson                if self.user_val is not None:
207690c36d8aSUlf Magnusson                    new_val = self.user_val
207790c36d8aSUlf Magnusson                    use_defaults = False
207890c36d8aSUlf Magnusson
207990c36d8aSUlf Magnusson            if use_defaults:
208090c36d8aSUlf Magnusson                for val_expr, cond_expr in self.def_exprs:
208190c36d8aSUlf Magnusson                    if self.config._eval_expr(cond_expr) != "n":
208290c36d8aSUlf Magnusson                        self.write_to_conf = True
208390c36d8aSUlf Magnusson                        new_val = _str_val(val_expr)
208490c36d8aSUlf Magnusson                        break
208590c36d8aSUlf Magnusson
208690c36d8aSUlf Magnusson        self.cached_val = new_val
2087f219e013SMasahiro Yamada        return new_val
2088f219e013SMasahiro Yamada
2089f219e013SMasahiro Yamada    def get_user_value(self):
2090f219e013SMasahiro Yamada        """Returns the value assigned to the symbol in a .config or via
209190c36d8aSUlf Magnusson        Symbol.set_user_value() (provided the value was valid for the type of
209290c36d8aSUlf Magnusson        the symbol). Returns None in case of no user value."""
2093f219e013SMasahiro Yamada        return self.user_val
2094f219e013SMasahiro Yamada
2095f219e013SMasahiro Yamada    def get_upper_bound(self):
2096f219e013SMasahiro Yamada        """For string/hex/int symbols and for bool and tristate symbols that
2097f219e013SMasahiro Yamada        cannot be modified (see is_modifiable()), returns None.
2098f219e013SMasahiro Yamada
2099f219e013SMasahiro Yamada        Otherwise, returns the highest value the symbol can be set to with
210090c36d8aSUlf Magnusson        Symbol.set_user_value() (that will not be truncated): one of "m" or
210190c36d8aSUlf Magnusson        "y", arranged from lowest to highest. This corresponds to the highest
210290c36d8aSUlf Magnusson        value the symbol could be given in e.g. the 'make menuconfig'
210390c36d8aSUlf Magnusson        interface.
2104f219e013SMasahiro Yamada
2105f219e013SMasahiro Yamada        See also the tri_less*() and tri_greater*() functions, which could come
2106f219e013SMasahiro Yamada        in handy."""
2107f219e013SMasahiro Yamada        if self.type != BOOL and self.type != TRISTATE:
2108f219e013SMasahiro Yamada            return None
2109f219e013SMasahiro Yamada        rev_dep = self.config._eval_expr(self.rev_dep)
211090c36d8aSUlf Magnusson        # A bool selected to "m" gets promoted to "y", pinning it
211190c36d8aSUlf Magnusson        if rev_dep == "m" and self.type == BOOL:
211290c36d8aSUlf Magnusson            return None
211390c36d8aSUlf Magnusson        vis = _get_visibility(self)
211490c36d8aSUlf Magnusson        if TRI_TO_INT[vis] > TRI_TO_INT[rev_dep]:
2115f219e013SMasahiro Yamada            return vis
2116f219e013SMasahiro Yamada        return None
2117f219e013SMasahiro Yamada
2118f219e013SMasahiro Yamada    def get_lower_bound(self):
2119f219e013SMasahiro Yamada        """For string/hex/int symbols and for bool and tristate symbols that
2120f219e013SMasahiro Yamada        cannot be modified (see is_modifiable()), returns None.
2121f219e013SMasahiro Yamada
2122f219e013SMasahiro Yamada        Otherwise, returns the lowest value the symbol can be set to with
212390c36d8aSUlf Magnusson        Symbol.set_user_value() (that will not be truncated): one of "n" or
212490c36d8aSUlf Magnusson        "m", arranged from lowest to highest. This corresponds to the lowest
212590c36d8aSUlf Magnusson        value the symbol could be given in e.g. the 'make menuconfig'
212690c36d8aSUlf Magnusson        interface.
2127f219e013SMasahiro Yamada
2128f219e013SMasahiro Yamada        See also the tri_less*() and tri_greater*() functions, which could come
2129f219e013SMasahiro Yamada        in handy."""
2130f219e013SMasahiro Yamada        if self.type != BOOL and self.type != TRISTATE:
2131f219e013SMasahiro Yamada            return None
2132f219e013SMasahiro Yamada        rev_dep = self.config._eval_expr(self.rev_dep)
213390c36d8aSUlf Magnusson        # A bool selected to "m" gets promoted to "y", pinning it
213490c36d8aSUlf Magnusson        if rev_dep == "m" and self.type == BOOL:
213590c36d8aSUlf Magnusson            return None
213690c36d8aSUlf Magnusson        if TRI_TO_INT[_get_visibility(self)] > TRI_TO_INT[rev_dep]:
2137f219e013SMasahiro Yamada            return rev_dep
2138f219e013SMasahiro Yamada        return None
2139f219e013SMasahiro Yamada
2140f219e013SMasahiro Yamada    def get_assignable_values(self):
2141f219e013SMasahiro Yamada        """For string/hex/int symbols and for bool and tristate symbols that
2142f219e013SMasahiro Yamada        cannot be modified (see is_modifiable()), returns the empty list.
2143f219e013SMasahiro Yamada
2144f219e013SMasahiro Yamada        Otherwise, returns a list containing the user values that can be
2145f219e013SMasahiro Yamada        assigned to the symbol (that won't be truncated). Usage example:
2146f219e013SMasahiro Yamada
2147f219e013SMasahiro Yamada        if "m" in sym.get_assignable_values():
2148f219e013SMasahiro Yamada            sym.set_user_value("m")
2149f219e013SMasahiro Yamada
2150f219e013SMasahiro Yamada        This is basically a more convenient interface to
2151f219e013SMasahiro Yamada        get_lower/upper_bound() when wanting to test if a particular tristate
2152f219e013SMasahiro Yamada        value can be assigned."""
2153f219e013SMasahiro Yamada        if self.type != BOOL and self.type != TRISTATE:
2154f219e013SMasahiro Yamada            return []
2155f219e013SMasahiro Yamada        rev_dep = self.config._eval_expr(self.rev_dep)
215690c36d8aSUlf Magnusson        # A bool selected to "m" gets promoted to "y", pinning it
215790c36d8aSUlf Magnusson        if rev_dep == "m" and self.type == BOOL:
215890c36d8aSUlf Magnusson            return []
215990c36d8aSUlf Magnusson        res = ["n", "m", "y"][TRI_TO_INT[rev_dep] :
216090c36d8aSUlf Magnusson                              TRI_TO_INT[_get_visibility(self)] + 1]
2161f219e013SMasahiro Yamada        return res if len(res) > 1 else []
2162f219e013SMasahiro Yamada
2163f219e013SMasahiro Yamada    def get_visibility(self):
2164f219e013SMasahiro Yamada        """Returns the visibility of the symbol: one of "n", "m" or "y". For
2165f219e013SMasahiro Yamada        bool and tristate symbols, this is an upper bound on the value users
2166f219e013SMasahiro Yamada        can set for the symbol. For other types of symbols, a visibility of "n"
2167f219e013SMasahiro Yamada        means the user value will be ignored. A visibility of "n" corresponds
2168f219e013SMasahiro Yamada        to not being visible in the 'make *config' interfaces.
2169f219e013SMasahiro Yamada
2170f219e013SMasahiro Yamada        Example (assuming we're running with modules enabled -- i.e., MODULES
2171f219e013SMasahiro Yamada        set to 'y'):
2172f219e013SMasahiro Yamada
2173f219e013SMasahiro Yamada        # Assume this has been assigned 'n'
2174f219e013SMasahiro Yamada        config N_SYM
2175f219e013SMasahiro Yamada            tristate "N_SYM"
2176f219e013SMasahiro Yamada
2177f219e013SMasahiro Yamada        # Assume this has been assigned 'm'
2178f219e013SMasahiro Yamada        config M_SYM
2179f219e013SMasahiro Yamada            tristate "M_SYM"
2180f219e013SMasahiro Yamada
2181f219e013SMasahiro Yamada        # Has visibility 'n'
2182f219e013SMasahiro Yamada        config A
2183f219e013SMasahiro Yamada            tristate "A"
2184f219e013SMasahiro Yamada            depends on N_SYM
2185f219e013SMasahiro Yamada
2186f219e013SMasahiro Yamada        # Has visibility 'm'
2187f219e013SMasahiro Yamada        config B
2188f219e013SMasahiro Yamada            tristate "B"
2189f219e013SMasahiro Yamada            depends on M_SYM
2190f219e013SMasahiro Yamada
2191f219e013SMasahiro Yamada        # Has visibility 'y'
2192f219e013SMasahiro Yamada        config C
2193f219e013SMasahiro Yamada            tristate "C"
2194f219e013SMasahiro Yamada
2195f219e013SMasahiro Yamada        # Has no prompt, and hence visibility 'n'
2196f219e013SMasahiro Yamada        config D
2197f219e013SMasahiro Yamada            tristate
2198f219e013SMasahiro Yamada
2199f219e013SMasahiro Yamada        Having visibility be tri-valued ensures that e.g. a symbol cannot be
2200f219e013SMasahiro Yamada        set to "y" by the user if it depends on a symbol with value "m", which
2201f219e013SMasahiro Yamada        wouldn't be safe.
2202f219e013SMasahiro Yamada
2203f219e013SMasahiro Yamada        You should probably look at get_lower/upper_bound(),
2204f219e013SMasahiro Yamada        get_assignable_values() and is_modifiable() before using this."""
220590c36d8aSUlf Magnusson        return _get_visibility(self)
2206f219e013SMasahiro Yamada
2207f219e013SMasahiro Yamada    def get_referenced_symbols(self, refs_from_enclosing=False):
2208f219e013SMasahiro Yamada        """Returns the set() of all symbols referenced by this symbol. For
2209f219e013SMasahiro Yamada        example, the symbol defined by
2210f219e013SMasahiro Yamada
2211f219e013SMasahiro Yamada        config FOO
2212f219e013SMasahiro Yamada            bool
2213f219e013SMasahiro Yamada            prompt "foo" if A && B
2214f219e013SMasahiro Yamada            default C if D
2215f219e013SMasahiro Yamada            depends on E
2216f219e013SMasahiro Yamada            select F if G
2217f219e013SMasahiro Yamada
2218f219e013SMasahiro Yamada        references the symbols A through G.
2219f219e013SMasahiro Yamada
222090c36d8aSUlf Magnusson        refs_from_enclosing (default: False): If True, the symbols referenced
222190c36d8aSUlf Magnusson           by enclosing menus and ifs will be included in the result."""
222290c36d8aSUlf Magnusson        return self.all_referenced_syms if refs_from_enclosing else \
222390c36d8aSUlf Magnusson               self.referenced_syms
2224f219e013SMasahiro Yamada
2225f219e013SMasahiro Yamada    def get_selected_symbols(self):
2226f219e013SMasahiro Yamada        """Returns the set() of all symbols X for which this symbol has a
2227f219e013SMasahiro Yamada        'select X' or 'select X if Y' (regardless of whether Y is satisfied or
2228f219e013SMasahiro Yamada        not). This is a subset of the symbols returned by
2229f219e013SMasahiro Yamada        get_referenced_symbols()."""
2230f219e013SMasahiro Yamada        return self.selected_syms
2231f219e013SMasahiro Yamada
2232*4e1102f6SUlf Magnusson    def get_implied_symbols(self):
2233*4e1102f6SUlf Magnusson        """Returns the set() of all symbols X for which this symbol has an
2234*4e1102f6SUlf Magnusson        'imply X' or 'imply X if Y' (regardless of whether Y is satisfied or
2235*4e1102f6SUlf Magnusson        not). This is a subset of the symbols returned by
2236*4e1102f6SUlf Magnusson        get_referenced_symbols()."""
2237*4e1102f6SUlf Magnusson        return self.implied_syms
2238*4e1102f6SUlf Magnusson
223990c36d8aSUlf Magnusson    def set_user_value(self, v):
224090c36d8aSUlf Magnusson        """Sets the user value of the symbol.
2241f219e013SMasahiro Yamada
224290c36d8aSUlf Magnusson        Equal in effect to assigning the value to the symbol within a .config
224390c36d8aSUlf Magnusson        file. Use get_lower/upper_bound() or get_assignable_values() to find
224490c36d8aSUlf Magnusson        the range of currently assignable values for bool and tristate symbols;
224590c36d8aSUlf Magnusson        setting values outside this range will cause the user value to differ
224690c36d8aSUlf Magnusson        from the result of Symbol.get_value() (be truncated). Values that are
224790c36d8aSUlf Magnusson        invalid for the type (such as a_bool.set_user_value("foo")) are
224890c36d8aSUlf Magnusson        ignored, and a warning is emitted if an attempt is made to assign such
224990c36d8aSUlf Magnusson        a value.
2250f219e013SMasahiro Yamada
225190c36d8aSUlf Magnusson        For any type of symbol, is_modifiable() can be used to check if a user
225290c36d8aSUlf Magnusson        value will currently have any effect on the symbol, as determined by
225390c36d8aSUlf Magnusson        its visibility and range of assignable values. Any value that is valid
225490c36d8aSUlf Magnusson        for the type (bool, tristate, etc.) will end up being reflected in
225590c36d8aSUlf Magnusson        get_user_value() though, and might have an effect later if conditions
225690c36d8aSUlf Magnusson        change. To get rid of the user value, use unset_user_value().
2257f219e013SMasahiro Yamada
225890c36d8aSUlf Magnusson        Any symbols dependent on the symbol are (recursively) invalidated, so
225990c36d8aSUlf Magnusson        things will just work with regards to dependencies.
2260f219e013SMasahiro Yamada
226190c36d8aSUlf Magnusson        v: The user value to give to the symbol."""
226290c36d8aSUlf Magnusson        self._set_user_value_no_invalidate(v, False)
2263f219e013SMasahiro Yamada
226490c36d8aSUlf Magnusson        # There might be something more efficient you could do here, but play
226590c36d8aSUlf Magnusson        # it safe.
226690c36d8aSUlf Magnusson        if self.name == "MODULES":
226790c36d8aSUlf Magnusson            self.config._invalidate_all()
226890c36d8aSUlf Magnusson            return
2269f219e013SMasahiro Yamada
227090c36d8aSUlf Magnusson        self._invalidate()
227190c36d8aSUlf Magnusson        self._invalidate_dependent()
2272f219e013SMasahiro Yamada
227390c36d8aSUlf Magnusson    def unset_user_value(self):
227490c36d8aSUlf Magnusson        """Resets the user value of the symbol, as if the symbol had never
227590c36d8aSUlf Magnusson        gotten a user value via Config.load_config() or
227690c36d8aSUlf Magnusson        Symbol.set_user_value()."""
227790c36d8aSUlf Magnusson        self._unset_user_value_no_recursive_invalidate()
227890c36d8aSUlf Magnusson        self._invalidate_dependent()
2279f219e013SMasahiro Yamada
2280f219e013SMasahiro Yamada    def is_modifiable(self):
2281f219e013SMasahiro Yamada        """Returns True if the value of the symbol could be modified by calling
228290c36d8aSUlf Magnusson        Symbol.set_user_value().
2283f219e013SMasahiro Yamada
2284f219e013SMasahiro Yamada        For bools and tristates, this corresponds to the symbol being visible
2285f219e013SMasahiro Yamada        in the 'make menuconfig' interface and not already being pinned to a
2286f219e013SMasahiro Yamada        specific value (e.g. because it is selected by another symbol).
2287f219e013SMasahiro Yamada
2288f219e013SMasahiro Yamada        For strings and numbers, this corresponds to just being visible. (See
2289f219e013SMasahiro Yamada        Symbol.get_visibility().)"""
2290f219e013SMasahiro Yamada        if self.is_special_:
2291f219e013SMasahiro Yamada            return False
2292f219e013SMasahiro Yamada        if self.type == BOOL or self.type == TRISTATE:
2293f219e013SMasahiro Yamada            rev_dep = self.config._eval_expr(self.rev_dep)
229490c36d8aSUlf Magnusson            # A bool selected to "m" gets promoted to "y", pinning it
229590c36d8aSUlf Magnusson            if rev_dep == "m" and self.type == BOOL:
229690c36d8aSUlf Magnusson                return False
229790c36d8aSUlf Magnusson            return TRI_TO_INT[_get_visibility(self)] > TRI_TO_INT[rev_dep]
229890c36d8aSUlf Magnusson        return _get_visibility(self) != "n"
2299f219e013SMasahiro Yamada
2300f219e013SMasahiro Yamada    def is_defined(self):
2301f219e013SMasahiro Yamada        """Returns False if the symbol is referred to in the Kconfig but never
230290c36d8aSUlf Magnusson        actually defined."""
2303f219e013SMasahiro Yamada        return self.is_defined_
2304f219e013SMasahiro Yamada
2305f219e013SMasahiro Yamada    def is_special(self):
2306f219e013SMasahiro Yamada        """Returns True if the symbol is one of the special symbols n, m, y, or
230790c36d8aSUlf Magnusson        UNAME_RELEASE, or gets its value from the environment."""
2308f219e013SMasahiro Yamada        return self.is_special_
2309f219e013SMasahiro Yamada
2310f219e013SMasahiro Yamada    def is_from_environment(self):
231190c36d8aSUlf Magnusson        """Returns True if the symbol gets its value from the environment."""
2312f219e013SMasahiro Yamada        return self.is_from_env
2313f219e013SMasahiro Yamada
2314f219e013SMasahiro Yamada    def has_ranges(self):
2315f219e013SMasahiro Yamada        """Returns True if the symbol is of type INT or HEX and has ranges that
231690c36d8aSUlf Magnusson        limit what values it can take on."""
231790c36d8aSUlf Magnusson        return bool(self.ranges)
2318f219e013SMasahiro Yamada
2319f219e013SMasahiro Yamada    def is_choice_symbol(self):
2320f219e013SMasahiro Yamada        """Returns True if the symbol is in a choice statement and is an actual
232190c36d8aSUlf Magnusson        choice symbol (see Choice.get_symbols())."""
232290c36d8aSUlf Magnusson        return self.is_choice_sym
2323f219e013SMasahiro Yamada
2324f219e013SMasahiro Yamada    def is_choice_selection(self):
2325f219e013SMasahiro Yamada        """Returns True if the symbol is contained in a choice statement and is
232690c36d8aSUlf Magnusson        the selected item. Equivalent to
232790c36d8aSUlf Magnusson
232890c36d8aSUlf Magnusson        sym.is_choice_symbol() and sym.get_parent().get_selection() is sym"""
232990c36d8aSUlf Magnusson        return self.is_choice_sym and self.parent.get_selection() is self
2330f219e013SMasahiro Yamada
23319d01b787SMasahiro Yamada    def is_allnoconfig_y(self):
233290c36d8aSUlf Magnusson        """Returns True if the symbol has the 'allnoconfig_y' option set."""
23339d01b787SMasahiro Yamada        return self.allnoconfig_y
23349d01b787SMasahiro Yamada
2335f219e013SMasahiro Yamada    def __str__(self):
2336f219e013SMasahiro Yamada        """Returns a string containing various information about the symbol."""
2337f219e013SMasahiro Yamada        return self.config._get_sym_or_choice_str(self)
2338f219e013SMasahiro Yamada
2339f219e013SMasahiro Yamada    #
2340f219e013SMasahiro Yamada    # Private methods
2341f219e013SMasahiro Yamada    #
2342f219e013SMasahiro Yamada
2343f219e013SMasahiro Yamada    def __init__(self):
2344f219e013SMasahiro Yamada        """Symbol constructor -- not intended to be called directly by
234590c36d8aSUlf Magnusson        Kconfiglib clients."""
2346f219e013SMasahiro Yamada
2347f219e013SMasahiro Yamada        self.name = None
2348f219e013SMasahiro Yamada        self.type = UNKNOWN
234990c36d8aSUlf Magnusson        self.prompts = []
235090c36d8aSUlf Magnusson        self.def_exprs = [] # 'default' properties
235190c36d8aSUlf Magnusson        self.ranges = [] # 'range' properties (for int and hex)
235290c36d8aSUlf Magnusson        self.help = None # Help text
235390c36d8aSUlf Magnusson        self.rev_dep = "n" # Reverse (select-related) dependencies
2354*4e1102f6SUlf Magnusson        self.weak_rev_dep = "n" # Weak reverse (imply-related) dependencies
235590c36d8aSUlf Magnusson        self.config = None
235690c36d8aSUlf Magnusson        self.parent = None
2357f219e013SMasahiro Yamada
235890c36d8aSUlf Magnusson        self.user_val = None # Value set by user
2359f219e013SMasahiro Yamada
2360*4e1102f6SUlf Magnusson        # The prompt, default value, select, and imply conditions without any
236190c36d8aSUlf Magnusson        # dependencies from menus and ifs propagated to them
2362f219e013SMasahiro Yamada        self.orig_prompts = []
2363f219e013SMasahiro Yamada        self.orig_def_exprs = []
2364f219e013SMasahiro Yamada        self.orig_selects = []
2365*4e1102f6SUlf Magnusson        self.orig_implies = []
2366f219e013SMasahiro Yamada
236790c36d8aSUlf Magnusson        # Dependencies inherited from containing menus and ifs
2368f219e013SMasahiro Yamada        self.deps_from_containing = None
2369f219e013SMasahiro Yamada        # The set of symbols referenced by this symbol (see
2370f219e013SMasahiro Yamada        # get_referenced_symbols())
2371f219e013SMasahiro Yamada        self.referenced_syms = set()
2372f219e013SMasahiro Yamada        # The set of symbols selected by this symbol (see
2373f219e013SMasahiro Yamada        # get_selected_symbols())
2374f219e013SMasahiro Yamada        self.selected_syms = set()
2375*4e1102f6SUlf Magnusson        # The set of symbols implied by this symbol (see get_implied_symbols())
2376*4e1102f6SUlf Magnusson        self.implied_syms = set()
2377f219e013SMasahiro Yamada        # Like 'referenced_syms', but includes symbols from
237890c36d8aSUlf Magnusson        # dependencies inherited from enclosing menus and ifs
2379f219e013SMasahiro Yamada        self.all_referenced_syms = set()
2380f219e013SMasahiro Yamada
2381*4e1102f6SUlf Magnusson        # This records only dependencies from enclosing ifs and menus together
2382*4e1102f6SUlf Magnusson        # with local 'depends on' dependencies. Needed when determining actual
2383*4e1102f6SUlf Magnusson        # choice items (hrrrr...). See Choice._determine_actual_symbols().
2384f219e013SMasahiro Yamada        self.menu_dep = None
2385f219e013SMasahiro Yamada
2386f219e013SMasahiro Yamada        # See Symbol.get_ref/def_locations().
2387f219e013SMasahiro Yamada        self.def_locations = []
2388f219e013SMasahiro Yamada        self.ref_locations = []
2389f219e013SMasahiro Yamada
239090c36d8aSUlf Magnusson        # Populated in Config._build_dep() after parsing. Links the symbol to
239190c36d8aSUlf Magnusson        # the symbols that immediately depend on it (in a caching/invalidation
239290c36d8aSUlf Magnusson        # sense). The total set of dependent symbols for the symbol (the
239390c36d8aSUlf Magnusson        # transitive closure) is calculated on an as-needed basis in
239490c36d8aSUlf Magnusson        # _get_dependent().
239590c36d8aSUlf Magnusson        self.dep = set()
2396f219e013SMasahiro Yamada
239790c36d8aSUlf Magnusson        # Cached values
2398f219e013SMasahiro Yamada
2399f219e013SMasahiro Yamada        # Caches the calculated value
240090c36d8aSUlf Magnusson        self.cached_val = None
240190c36d8aSUlf Magnusson        # Caches the visibility, which acts as an upper bound on the value
240290c36d8aSUlf Magnusson        self.cached_visibility = None
2403f219e013SMasahiro Yamada        # Caches the total list of dependent symbols. Calculated in
2404f219e013SMasahiro Yamada        # _get_dependent().
2405f219e013SMasahiro Yamada        self.cached_deps = None
2406f219e013SMasahiro Yamada
240790c36d8aSUlf Magnusson        # Flags
240890c36d8aSUlf Magnusson
2409f219e013SMasahiro Yamada        # Does the symbol have an entry in the Kconfig file? The trailing
2410f219e013SMasahiro Yamada        # underscore avoids a collision with is_defined().
2411f219e013SMasahiro Yamada        self.is_defined_ = False
241290c36d8aSUlf Magnusson        # Should the symbol get an entry in .config?
241390c36d8aSUlf Magnusson        self.write_to_conf = False
241490c36d8aSUlf Magnusson        # Set to true when _make_conf() is called on a symbol, so that symbols
241590c36d8aSUlf Magnusson        # defined in multiple locations only get one .config entry. We need to
241690c36d8aSUlf Magnusson        # reset it prior to writing out a new .config.
241790c36d8aSUlf Magnusson        self.already_written = False
241890c36d8aSUlf Magnusson        # This is set to True for "actual" choice symbols; see
241990c36d8aSUlf Magnusson        # Choice._determine_actual_symbols().
242090c36d8aSUlf Magnusson        self.is_choice_sym = False
2421f219e013SMasahiro Yamada        # Does the symbol get its value in some special way, e.g. from the
2422f219e013SMasahiro Yamada        # environment or by being one of the special symbols n, m, and y? If
242390c36d8aSUlf Magnusson        # so, the value is stored in self.cached_val, which is never
2424f219e013SMasahiro Yamada        # invalidated. The trailing underscore avoids a collision with
2425f219e013SMasahiro Yamada        # is_special().
2426f219e013SMasahiro Yamada        self.is_special_ = False
2427f219e013SMasahiro Yamada        # Does the symbol get its value from the environment?
2428f219e013SMasahiro Yamada        self.is_from_env = False
24299d01b787SMasahiro Yamada        # Does the symbol have the 'allnoconfig_y' option set?
24309d01b787SMasahiro Yamada        self.allnoconfig_y = False
24319d01b787SMasahiro Yamada
2432f219e013SMasahiro Yamada    def _invalidate(self):
2433f219e013SMasahiro Yamada        if self.is_special_:
2434f219e013SMasahiro Yamada            return
2435f219e013SMasahiro Yamada
243690c36d8aSUlf Magnusson        if self.is_choice_sym:
2437f219e013SMasahiro Yamada            self.parent._invalidate()
2438f219e013SMasahiro Yamada
243990c36d8aSUlf Magnusson        self.cached_val = None
244090c36d8aSUlf Magnusson        self.cached_visibility = None
2441f219e013SMasahiro Yamada
2442f219e013SMasahiro Yamada    def _invalidate_dependent(self):
2443f219e013SMasahiro Yamada        for sym in self._get_dependent():
2444f219e013SMasahiro Yamada            sym._invalidate()
2445f219e013SMasahiro Yamada
2446f219e013SMasahiro Yamada    def _set_user_value_no_invalidate(self, v, suppress_load_warnings):
2447f219e013SMasahiro Yamada        """Like set_user_value(), but does not invalidate any symbols.
2448f219e013SMasahiro Yamada
244990c36d8aSUlf Magnusson        suppress_load_warnings: some warnings are annoying when loading a
245090c36d8aSUlf Magnusson           .config that can be helpful when manually invoking set_user_value().
245190c36d8aSUlf Magnusson           This flag is set to True to suppress such warnings.
2452f219e013SMasahiro Yamada
2453f219e013SMasahiro Yamada           Perhaps this could be made optional for load_config() instead."""
2454f219e013SMasahiro Yamada
2455f219e013SMasahiro Yamada        if self.is_special_:
2456f219e013SMasahiro Yamada            if self.is_from_env:
2457f219e013SMasahiro Yamada                self.config._warn('attempt to assign the value "{0}" to the '
2458f219e013SMasahiro Yamada                                  'symbol {1}, which gets its value from the '
2459f219e013SMasahiro Yamada                                  'environment. Assignment ignored.'
2460f219e013SMasahiro Yamada                                  .format(v, self.name))
2461f219e013SMasahiro Yamada            else:
2462f219e013SMasahiro Yamada                self.config._warn('attempt to assign the value "{0}" to the '
2463f219e013SMasahiro Yamada                                  'special symbol {1}. Assignment ignored.'
2464f219e013SMasahiro Yamada                                  .format(v, self.name))
2465f219e013SMasahiro Yamada            return
2466f219e013SMasahiro Yamada
2467f219e013SMasahiro Yamada        if not self.is_defined_:
2468f219e013SMasahiro Yamada            filename, linenr = self.ref_locations[0]
246990c36d8aSUlf Magnusson            if self.config.print_undef_assign:
247090c36d8aSUlf Magnusson                _stderr_msg('note: attempt to assign the value "{0}" to {1}, '
2471f219e013SMasahiro Yamada                            "which is referenced at {2}:{3} but never "
2472f219e013SMasahiro Yamada                            "defined. Assignment ignored."
2473f219e013SMasahiro Yamada                            .format(v, self.name, filename, linenr))
2474f219e013SMasahiro Yamada            return
2475f219e013SMasahiro Yamada
2476f219e013SMasahiro Yamada        # Check if the value is valid for our type
247790c36d8aSUlf Magnusson        if not ((self.type == BOOL     and (v == "y" or v == "n")   ) or
247890c36d8aSUlf Magnusson                (self.type == TRISTATE and (v == "y" or v == "m" or
247990c36d8aSUlf Magnusson                                            v == "n")               ) or
2480f219e013SMasahiro Yamada                (self.type == STRING                                ) or
2481f219e013SMasahiro Yamada                (self.type == INT      and _is_base_n(v, 10)        ) or
2482f219e013SMasahiro Yamada                (self.type == HEX      and _is_base_n(v, 16)        )):
248390c36d8aSUlf Magnusson            self.config._warn('the value "{0}" is invalid for {1}, which has '
248490c36d8aSUlf Magnusson                              "type {2}. Assignment ignored."
248590c36d8aSUlf Magnusson                              .format(v, self.name, TYPENAME[self.type]))
2486f219e013SMasahiro Yamada            return
2487f219e013SMasahiro Yamada
248890c36d8aSUlf Magnusson        if not self.prompts and not suppress_load_warnings:
2489f219e013SMasahiro Yamada            self.config._warn('assigning "{0}" to the symbol {1} which '
2490f219e013SMasahiro Yamada                              'lacks prompts and thus has visibility "n". '
2491f219e013SMasahiro Yamada                              'The assignment will have no effect.'
2492f219e013SMasahiro Yamada                              .format(v, self.name))
2493f219e013SMasahiro Yamada
2494f219e013SMasahiro Yamada        self.user_val = v
2495f219e013SMasahiro Yamada
249690c36d8aSUlf Magnusson        if self.is_choice_sym and (self.type == BOOL or self.type == TRISTATE):
2497f219e013SMasahiro Yamada            choice = self.parent
2498f219e013SMasahiro Yamada            if v == "y":
2499f219e013SMasahiro Yamada                choice.user_val = self
2500f219e013SMasahiro Yamada                choice.user_mode = "y"
2501f219e013SMasahiro Yamada            elif v == "m":
2502f219e013SMasahiro Yamada                choice.user_val = None
2503f219e013SMasahiro Yamada                choice.user_mode = "m"
2504f219e013SMasahiro Yamada
2505f219e013SMasahiro Yamada    def _unset_user_value_no_recursive_invalidate(self):
2506f219e013SMasahiro Yamada        self._invalidate()
2507f219e013SMasahiro Yamada        self.user_val = None
2508f219e013SMasahiro Yamada
250990c36d8aSUlf Magnusson        if self.is_choice_sym:
2510f219e013SMasahiro Yamada            self.parent._unset_user_value()
2511f219e013SMasahiro Yamada
251290c36d8aSUlf Magnusson    def _make_conf(self, append_fn):
2513f219e013SMasahiro Yamada        if self.already_written:
251490c36d8aSUlf Magnusson            return
2515f219e013SMasahiro Yamada
2516f219e013SMasahiro Yamada        self.already_written = True
2517f219e013SMasahiro Yamada
2518f219e013SMasahiro Yamada        # Note: write_to_conf is determined in get_value()
2519f219e013SMasahiro Yamada        val = self.get_value()
2520f219e013SMasahiro Yamada        if not self.write_to_conf:
252190c36d8aSUlf Magnusson            return
2522f219e013SMasahiro Yamada
2523f219e013SMasahiro Yamada        if self.type == BOOL or self.type == TRISTATE:
2524*4e1102f6SUlf Magnusson            append_fn("{0}{1}={2}".format(self.config.config_prefix, self.name, val)
2525*4e1102f6SUlf Magnusson                      if val == "y" or val == "m" else
2526*4e1102f6SUlf Magnusson                      "# {0}{1} is not set".format(self.config.config_prefix, self.name))
252790c36d8aSUlf Magnusson
252890c36d8aSUlf Magnusson        elif self.type == INT or self.type == HEX:
2529*4e1102f6SUlf Magnusson            append_fn("{0}{1}={2}".format(self.config.config_prefix, self.name, val))
2530f219e013SMasahiro Yamada
2531f219e013SMasahiro Yamada        elif self.type == STRING:
2532f219e013SMasahiro Yamada            # Escape \ and "
2533*4e1102f6SUlf Magnusson            append_fn('{0}{1}="{2}"'
2534*4e1102f6SUlf Magnusson                      .format(self.config.config_prefix, self.name,
253590c36d8aSUlf Magnusson                              val.replace("\\", "\\\\").replace('"', '\\"')))
2536f219e013SMasahiro Yamada
2537f219e013SMasahiro Yamada        else:
253890c36d8aSUlf Magnusson            _internal_error("Internal error while creating .config: unknown "
253990c36d8aSUlf Magnusson                            'type "{0}".'.format(self.type))
2540f219e013SMasahiro Yamada
2541f219e013SMasahiro Yamada    def _get_dependent(self):
2542f219e013SMasahiro Yamada        """Returns the set of symbols that should be invalidated if the value
2543f219e013SMasahiro Yamada        of the symbol changes, because they might be affected by the change.
2544f219e013SMasahiro Yamada        Note that this is an internal API -- it's probably of limited
2545f219e013SMasahiro Yamada        usefulness to clients."""
2546f219e013SMasahiro Yamada        if self.cached_deps is not None:
2547f219e013SMasahiro Yamada            return self.cached_deps
2548f219e013SMasahiro Yamada
254990c36d8aSUlf Magnusson        res = set(self.dep)
255090c36d8aSUlf Magnusson        for s in self.dep:
255190c36d8aSUlf Magnusson            res |= s._get_dependent()
2552f219e013SMasahiro Yamada
255390c36d8aSUlf Magnusson        if self.is_choice_sym:
255490c36d8aSUlf Magnusson            # Choice symbols also depend (recursively) on their siblings. The
255590c36d8aSUlf Magnusson            # siblings are not included in 'dep' to avoid dependency loops.
255690c36d8aSUlf Magnusson            for sibling in self.parent.actual_symbols:
255790c36d8aSUlf Magnusson                if sibling is not self:
255890c36d8aSUlf Magnusson                    res.add(sibling)
255990c36d8aSUlf Magnusson                    res |= sibling.dep
256090c36d8aSUlf Magnusson                    for s in sibling.dep:
256190c36d8aSUlf Magnusson                        res |= s._get_dependent()
2562f219e013SMasahiro Yamada
2563f219e013SMasahiro Yamada        self.cached_deps = res
2564f219e013SMasahiro Yamada        return res
2565f219e013SMasahiro Yamada
2566f219e013SMasahiro Yamada    def _has_auto_menu_dep_on(self, on):
2567f219e013SMasahiro Yamada        """See Choice._determine_actual_symbols()."""
2568f219e013SMasahiro Yamada        if not isinstance(self.parent, Choice):
256990c36d8aSUlf Magnusson            _internal_error("Attempt to determine auto menu dependency for "
257090c36d8aSUlf Magnusson                            "symbol ouside of choice.")
2571f219e013SMasahiro Yamada
257290c36d8aSUlf Magnusson        if not self.prompts:
2573f219e013SMasahiro Yamada            # If we have no prompt, use the menu dependencies instead (what was
2574f219e013SMasahiro Yamada            # specified with 'depends on')
2575f219e013SMasahiro Yamada            return self.menu_dep is not None and \
2576f219e013SMasahiro Yamada                   self.config._expr_depends_on(self.menu_dep, on)
2577f219e013SMasahiro Yamada
257890c36d8aSUlf Magnusson        for _, cond_expr in self.prompts:
2579f219e013SMasahiro Yamada            if self.config._expr_depends_on(cond_expr, on):
2580f219e013SMasahiro Yamada                return True
2581f219e013SMasahiro Yamada
2582f219e013SMasahiro Yamada        return False
2583f219e013SMasahiro Yamada
2584f219e013SMasahiro Yamadaclass Menu(Item):
2585f219e013SMasahiro Yamada
2586f219e013SMasahiro Yamada    """Represents a menu statement."""
2587f219e013SMasahiro Yamada
2588f219e013SMasahiro Yamada    #
2589f219e013SMasahiro Yamada    # Public interface
2590f219e013SMasahiro Yamada    #
2591f219e013SMasahiro Yamada
2592f219e013SMasahiro Yamada    def get_config(self):
2593f219e013SMasahiro Yamada        """Return the Config instance this menu is from."""
2594f219e013SMasahiro Yamada        return self.config
2595f219e013SMasahiro Yamada
2596f219e013SMasahiro Yamada    def get_title(self):
2597f219e013SMasahiro Yamada        """Returns the title text of the menu."""
2598f219e013SMasahiro Yamada        return self.title
2599f219e013SMasahiro Yamada
2600f219e013SMasahiro Yamada    def get_parent(self):
2601f219e013SMasahiro Yamada        """Returns the menu or choice statement that contains the menu, or
2602f219e013SMasahiro Yamada        None if the menu is at the top level. Note that if statements are
2603f219e013SMasahiro Yamada        treated as syntactic sugar and do not have an explicit class
2604f219e013SMasahiro Yamada        representation."""
2605f219e013SMasahiro Yamada        return self.parent
2606f219e013SMasahiro Yamada
2607f219e013SMasahiro Yamada    def get_location(self):
2608f219e013SMasahiro Yamada        """Returns the location of the menu as a (filename, linenr) tuple,
2609f219e013SMasahiro Yamada        where filename is a string and linenr an int."""
2610f219e013SMasahiro Yamada        return (self.filename, self.linenr)
2611f219e013SMasahiro Yamada
261290c36d8aSUlf Magnusson    def get_items(self, recursive=False):
261390c36d8aSUlf Magnusson        """Returns a list containing the items (symbols, menus, choice
261490c36d8aSUlf Magnusson        statements and comments) in in the menu, in the same order that the
261590c36d8aSUlf Magnusson        items appear within the menu.
261690c36d8aSUlf Magnusson
261790c36d8aSUlf Magnusson        recursive (default: False): True if items contained in items within the
261890c36d8aSUlf Magnusson           menu should be included recursively (preorder)."""
261990c36d8aSUlf Magnusson
262090c36d8aSUlf Magnusson        if not recursive:
262190c36d8aSUlf Magnusson            return self.block
262290c36d8aSUlf Magnusson
262390c36d8aSUlf Magnusson        res = []
262490c36d8aSUlf Magnusson        for item in self.block:
262590c36d8aSUlf Magnusson            res.append(item)
262690c36d8aSUlf Magnusson            if isinstance(item, Menu):
262790c36d8aSUlf Magnusson                res.extend(item.get_items(True))
262890c36d8aSUlf Magnusson            elif isinstance(item, Choice):
262990c36d8aSUlf Magnusson                res.extend(item.get_items())
263090c36d8aSUlf Magnusson        return res
263190c36d8aSUlf Magnusson
263290c36d8aSUlf Magnusson    def get_symbols(self, recursive=False):
263390c36d8aSUlf Magnusson        """Returns a list containing the symbols in the menu, in the same order
263490c36d8aSUlf Magnusson        that they appear within the menu.
263590c36d8aSUlf Magnusson
263690c36d8aSUlf Magnusson        recursive (default: False): True if symbols contained in items within
263790c36d8aSUlf Magnusson           the menu should be included recursively."""
263890c36d8aSUlf Magnusson
263990c36d8aSUlf Magnusson        return [item for item in self.get_items(recursive) if
264090c36d8aSUlf Magnusson                isinstance(item, Symbol)]
264190c36d8aSUlf Magnusson
264290c36d8aSUlf Magnusson    def get_visibility(self):
264390c36d8aSUlf Magnusson        """Returns the visibility of the menu. This also affects the visibility
264490c36d8aSUlf Magnusson        of subitems. See also Symbol.get_visibility()."""
264590c36d8aSUlf Magnusson        return self.config._eval_expr(self.dep_expr)
264690c36d8aSUlf Magnusson
264790c36d8aSUlf Magnusson    def get_visible_if_visibility(self):
264890c36d8aSUlf Magnusson        """Returns the visibility the menu gets from its 'visible if'
264990c36d8aSUlf Magnusson        condition. "y" if the menu has no 'visible if' condition."""
265090c36d8aSUlf Magnusson        return self.config._eval_expr(self.visible_if_expr)
265190c36d8aSUlf Magnusson
265290c36d8aSUlf Magnusson    def get_referenced_symbols(self, refs_from_enclosing=False):
265390c36d8aSUlf Magnusson        """See Symbol.get_referenced_symbols()."""
265490c36d8aSUlf Magnusson        return self.all_referenced_syms if refs_from_enclosing else \
265590c36d8aSUlf Magnusson               self.referenced_syms
265690c36d8aSUlf Magnusson
2657f219e013SMasahiro Yamada    def __str__(self):
2658f219e013SMasahiro Yamada        """Returns a string containing various information about the menu."""
2659f219e013SMasahiro Yamada        depends_on_str = self.config._expr_val_str(self.orig_deps,
2660f219e013SMasahiro Yamada                                                   "(no dependencies)")
2661f219e013SMasahiro Yamada        visible_if_str = self.config._expr_val_str(self.visible_if_expr,
2662f219e013SMasahiro Yamada                                                   "(no dependencies)")
2663f219e013SMasahiro Yamada
266490c36d8aSUlf Magnusson        additional_deps_str = " " + \
266590c36d8aSUlf Magnusson          self.config._expr_val_str(self.deps_from_containing,
2666f219e013SMasahiro Yamada                                    "(no additional dependencies)")
2667f219e013SMasahiro Yamada
266890c36d8aSUlf Magnusson        return _lines("Menu",
2669f219e013SMasahiro Yamada                      "Title                     : " + self.title,
2670f219e013SMasahiro Yamada                      "'depends on' dependencies : " + depends_on_str,
2671f219e013SMasahiro Yamada                      "'visible if' dependencies : " + visible_if_str,
267290c36d8aSUlf Magnusson                      "Additional dependencies from enclosing menus and "
267390c36d8aSUlf Magnusson                        "ifs:",
2674f219e013SMasahiro Yamada                      additional_deps_str,
2675f219e013SMasahiro Yamada                      "Location: {0}:{1}".format(self.filename, self.linenr))
2676f219e013SMasahiro Yamada
2677f219e013SMasahiro Yamada    #
2678f219e013SMasahiro Yamada    # Private methods
2679f219e013SMasahiro Yamada    #
2680f219e013SMasahiro Yamada
2681f219e013SMasahiro Yamada    def __init__(self):
2682f219e013SMasahiro Yamada        """Menu constructor -- not intended to be called directly by
268390c36d8aSUlf Magnusson        Kconfiglib clients."""
2684f219e013SMasahiro Yamada
2685f219e013SMasahiro Yamada        self.title = None
2686f219e013SMasahiro Yamada        self.dep_expr = None
268790c36d8aSUlf Magnusson        self.visible_if_expr = None
2688*4e1102f6SUlf Magnusson        self.block = [] # List of contained items
268990c36d8aSUlf Magnusson        self.config = None
269090c36d8aSUlf Magnusson        self.parent = None
2691f219e013SMasahiro Yamada
2692f219e013SMasahiro Yamada        # Dependency expression without dependencies from enclosing menus and
269390c36d8aSUlf Magnusson        # ifs propagated
2694f219e013SMasahiro Yamada        self.orig_deps = None
2695f219e013SMasahiro Yamada
269690c36d8aSUlf Magnusson        # Dependencies inherited from containing menus and ifs
2697f219e013SMasahiro Yamada        self.deps_from_containing = None
2698f219e013SMasahiro Yamada        # The set of symbols referenced by this menu (see
2699f219e013SMasahiro Yamada        # get_referenced_symbols())
2700f219e013SMasahiro Yamada        self.referenced_syms = set()
2701f219e013SMasahiro Yamada        # Like 'referenced_syms', but includes symbols from
270290c36d8aSUlf Magnusson        # dependencies inherited from enclosing menus and ifs
2703f219e013SMasahiro Yamada        self.all_referenced_syms = None
2704f219e013SMasahiro Yamada
2705f219e013SMasahiro Yamada        self.filename = None
2706f219e013SMasahiro Yamada        self.linenr = None
2707f219e013SMasahiro Yamada
270890c36d8aSUlf Magnusson    def _make_conf(self, append_fn):
2709f219e013SMasahiro Yamada        if self.config._eval_expr(self.dep_expr) != "n" and \
2710f219e013SMasahiro Yamada           self.config._eval_expr(self.visible_if_expr) != "n":
271190c36d8aSUlf Magnusson            append_fn("\n#\n# {0}\n#".format(self.title))
271290c36d8aSUlf Magnusson        _make_block_conf(self.block, append_fn)
2713f219e013SMasahiro Yamada
271490c36d8aSUlf Magnussonclass Choice(Item):
2715f219e013SMasahiro Yamada
2716f219e013SMasahiro Yamada    """Represents a choice statement. A choice can be in one of three modes:
2717f219e013SMasahiro Yamada
2718f219e013SMasahiro Yamada    "n" - The choice is not visible and no symbols can be selected.
2719f219e013SMasahiro Yamada
2720f219e013SMasahiro Yamada    "m" - Any number of symbols can be set to "m". The rest will be "n". This
2721f219e013SMasahiro Yamada          is safe since potentially conflicting options don't actually get
2722f219e013SMasahiro Yamada          compiled into the kernel simultaneously with "m".
2723f219e013SMasahiro Yamada
2724f219e013SMasahiro Yamada    "y" - One symbol will be "y" while the rest are "n".
2725f219e013SMasahiro Yamada
2726f219e013SMasahiro Yamada    Only tristate choices can be in "m" mode, and the visibility of the choice
2727f219e013SMasahiro Yamada    is an upper bound on the mode, so that e.g. a choice that depends on a
2728f219e013SMasahiro Yamada    symbol with value "m" will be in "m" mode.
2729f219e013SMasahiro Yamada
2730f219e013SMasahiro Yamada    The mode changes automatically when a value is assigned to a symbol within
2731f219e013SMasahiro Yamada    the choice.
2732f219e013SMasahiro Yamada
2733f219e013SMasahiro Yamada    See Symbol.get_visibility() too."""
2734f219e013SMasahiro Yamada
2735f219e013SMasahiro Yamada    #
2736f219e013SMasahiro Yamada    # Public interface
2737f219e013SMasahiro Yamada    #
2738f219e013SMasahiro Yamada
2739f219e013SMasahiro Yamada    def get_config(self):
2740f219e013SMasahiro Yamada        """Returns the Config instance this choice is from."""
2741f219e013SMasahiro Yamada        return self.config
2742f219e013SMasahiro Yamada
2743f219e013SMasahiro Yamada    def get_name(self):
2744f219e013SMasahiro Yamada        """For named choices, returns the name. Returns None for unnamed
2745f219e013SMasahiro Yamada        choices. No named choices appear anywhere in the kernel Kconfig files
2746f219e013SMasahiro Yamada        as of Linux 3.7.0-rc8."""
2747f219e013SMasahiro Yamada        return self.name
2748f219e013SMasahiro Yamada
274990c36d8aSUlf Magnusson    def get_type(self):
275090c36d8aSUlf Magnusson        """Returns the type of the choice. See Symbol.get_type()."""
275190c36d8aSUlf Magnusson        return self.type
275290c36d8aSUlf Magnusson
2753f219e013SMasahiro Yamada    def get_prompts(self):
2754f219e013SMasahiro Yamada        """Returns a list of prompts defined for the choice, in the order they
2755f219e013SMasahiro Yamada        appear in the configuration files. Returns the empty list for choices
2756f219e013SMasahiro Yamada        with no prompt.
2757f219e013SMasahiro Yamada
2758f219e013SMasahiro Yamada        This list will have a single entry for the vast majority of choices
2759f219e013SMasahiro Yamada        having prompts, but having multiple prompts for a single choice is
2760f219e013SMasahiro Yamada        possible through having multiple 'choice' entries for it (though I'm
2761f219e013SMasahiro Yamada        not sure if that ever happens in practice)."""
2762f219e013SMasahiro Yamada        return [prompt for prompt, _ in self.orig_prompts]
2763f219e013SMasahiro Yamada
2764f219e013SMasahiro Yamada    def get_help(self):
2765f219e013SMasahiro Yamada        """Returns the help text of the choice, or None if the choice has no
2766f219e013SMasahiro Yamada        help text."""
2767f219e013SMasahiro Yamada        return self.help
2768f219e013SMasahiro Yamada
276990c36d8aSUlf Magnusson    def get_parent(self):
277090c36d8aSUlf Magnusson        """Returns the menu or choice statement that contains the choice, or
277190c36d8aSUlf Magnusson        None if the choice is at the top level. Note that if statements are
277290c36d8aSUlf Magnusson        treated as syntactic sugar and do not have an explicit class
277390c36d8aSUlf Magnusson        representation."""
277490c36d8aSUlf Magnusson        return self.parent
277590c36d8aSUlf Magnusson
277690c36d8aSUlf Magnusson    def get_def_locations(self):
277790c36d8aSUlf Magnusson        """Returns a list of (filename, linenr) tuples, where filename (string)
277890c36d8aSUlf Magnusson        and linenr (int) represent a location where the choice is defined. For
277990c36d8aSUlf Magnusson        the vast majority of choices (all of them as of Linux 3.7.0-rc8) this
278090c36d8aSUlf Magnusson        list will only contain one element, but its possible for named choices
278190c36d8aSUlf Magnusson        to be defined in multiple locations."""
278290c36d8aSUlf Magnusson        return self.def_locations
278390c36d8aSUlf Magnusson
278490c36d8aSUlf Magnusson    def get_selection(self):
278590c36d8aSUlf Magnusson        """Returns the symbol selected (either by the user or through
278690c36d8aSUlf Magnusson        defaults), or None if either no symbol is selected or the mode is not
278790c36d8aSUlf Magnusson        "y"."""
278890c36d8aSUlf Magnusson        if self.cached_selection is not None:
278990c36d8aSUlf Magnusson            if self.cached_selection == NO_SELECTION:
279090c36d8aSUlf Magnusson                return None
279190c36d8aSUlf Magnusson            return self.cached_selection
279290c36d8aSUlf Magnusson
279390c36d8aSUlf Magnusson        if self.get_mode() != "y":
279490c36d8aSUlf Magnusson            return self._cache_ret(None)
279590c36d8aSUlf Magnusson
279690c36d8aSUlf Magnusson        # User choice available?
279790c36d8aSUlf Magnusson        if self.user_val is not None and _get_visibility(self.user_val) == "y":
279890c36d8aSUlf Magnusson            return self._cache_ret(self.user_val)
279990c36d8aSUlf Magnusson
280090c36d8aSUlf Magnusson        if self.optional:
280190c36d8aSUlf Magnusson            return self._cache_ret(None)
280290c36d8aSUlf Magnusson
280390c36d8aSUlf Magnusson        return self._cache_ret(self.get_selection_from_defaults())
280490c36d8aSUlf Magnusson
280590c36d8aSUlf Magnusson    def get_selection_from_defaults(self):
280690c36d8aSUlf Magnusson        """Like Choice.get_selection(), but acts as if no symbol has been
280790c36d8aSUlf Magnusson        selected by the user and no 'optional' flag is in effect."""
280890c36d8aSUlf Magnusson
280990c36d8aSUlf Magnusson        if not self.actual_symbols:
281090c36d8aSUlf Magnusson            return None
281190c36d8aSUlf Magnusson
281290c36d8aSUlf Magnusson        for symbol, cond_expr in self.def_exprs:
281390c36d8aSUlf Magnusson            if self.config._eval_expr(cond_expr) != "n":
281490c36d8aSUlf Magnusson                chosen_symbol = symbol
281590c36d8aSUlf Magnusson                break
281690c36d8aSUlf Magnusson        else:
281790c36d8aSUlf Magnusson            chosen_symbol = self.actual_symbols[0]
281890c36d8aSUlf Magnusson
281990c36d8aSUlf Magnusson        # Is the chosen symbol visible?
282090c36d8aSUlf Magnusson        if _get_visibility(chosen_symbol) != "n":
282190c36d8aSUlf Magnusson            return chosen_symbol
282290c36d8aSUlf Magnusson        # Otherwise, pick the first visible symbol
282390c36d8aSUlf Magnusson        for sym in self.actual_symbols:
282490c36d8aSUlf Magnusson            if _get_visibility(sym) != "n":
282590c36d8aSUlf Magnusson                return sym
282690c36d8aSUlf Magnusson        return None
282790c36d8aSUlf Magnusson
282890c36d8aSUlf Magnusson    def get_user_selection(self):
282990c36d8aSUlf Magnusson        """If the choice is in "y" mode and has a user-selected symbol, returns
283090c36d8aSUlf Magnusson        that symbol. Otherwise, returns None."""
283190c36d8aSUlf Magnusson        return self.user_val
2832f219e013SMasahiro Yamada
2833f219e013SMasahiro Yamada    def get_items(self):
2834f219e013SMasahiro Yamada        """Gets all items contained in the choice in the same order as within
2835f219e013SMasahiro Yamada        the configuration ("items" instead of "symbols" since choices and
2836f219e013SMasahiro Yamada        comments might appear within choices. This only happens in one place as
2837f219e013SMasahiro Yamada        of Linux 3.7.0-rc8, in drivers/usb/gadget/Kconfig)."""
283890c36d8aSUlf Magnusson        return self.block
2839f219e013SMasahiro Yamada
2840f219e013SMasahiro Yamada    def get_symbols(self):
2841f219e013SMasahiro Yamada        """Returns a list containing the choice's symbols.
2842f219e013SMasahiro Yamada
2843f219e013SMasahiro Yamada        A quirk (perhaps a bug) of Kconfig is that you can put items within a
2844f219e013SMasahiro Yamada        choice that will not be considered members of the choice insofar as
2845f219e013SMasahiro Yamada        selection is concerned. This happens for example if one symbol within a
2846f219e013SMasahiro Yamada        choice 'depends on' the symbol preceding it, or if you put non-symbol
2847f219e013SMasahiro Yamada        items within choices.
2848f219e013SMasahiro Yamada
2849f219e013SMasahiro Yamada        As of Linux 3.7.0-rc8, this seems to be used intentionally in one
2850f219e013SMasahiro Yamada        place: drivers/usb/gadget/Kconfig.
2851f219e013SMasahiro Yamada
2852f219e013SMasahiro Yamada        This function returns the "proper" symbols of the choice in the order
2853f219e013SMasahiro Yamada        they appear in the choice, excluding such items. If you want all items
2854f219e013SMasahiro Yamada        in the choice, use get_items()."""
2855f219e013SMasahiro Yamada        return self.actual_symbols
2856f219e013SMasahiro Yamada
2857f219e013SMasahiro Yamada    def get_referenced_symbols(self, refs_from_enclosing=False):
2858f219e013SMasahiro Yamada        """See Symbol.get_referenced_symbols()."""
285990c36d8aSUlf Magnusson        return self.all_referenced_syms if refs_from_enclosing else \
286090c36d8aSUlf Magnusson               self.referenced_syms
2861f219e013SMasahiro Yamada
2862f219e013SMasahiro Yamada    def get_visibility(self):
2863f219e013SMasahiro Yamada        """Returns the visibility of the choice statement: one of "n", "m" or
2864f219e013SMasahiro Yamada        "y". This acts as an upper limit on the mode of the choice (though bool
2865f219e013SMasahiro Yamada        choices can only have the mode "y"). See the class documentation for an
2866f219e013SMasahiro Yamada        explanation of modes."""
286790c36d8aSUlf Magnusson        return _get_visibility(self)
2868f219e013SMasahiro Yamada
2869f219e013SMasahiro Yamada    def get_mode(self):
2870f219e013SMasahiro Yamada        """Returns the mode of the choice. See the class documentation for
2871f219e013SMasahiro Yamada        an explanation of modes."""
2872f219e013SMasahiro Yamada        minimum_mode = "n" if self.optional else "m"
2873f219e013SMasahiro Yamada        mode = self.user_mode if self.user_mode is not None else minimum_mode
287490c36d8aSUlf Magnusson        mode = self.config._eval_min(mode, _get_visibility(self))
2875f219e013SMasahiro Yamada
2876f219e013SMasahiro Yamada        # Promote "m" to "y" for boolean choices
2877f219e013SMasahiro Yamada        if mode == "m" and self.type == BOOL:
2878f219e013SMasahiro Yamada            return "y"
2879f219e013SMasahiro Yamada
2880f219e013SMasahiro Yamada        return mode
2881f219e013SMasahiro Yamada
2882f219e013SMasahiro Yamada    def is_optional(self):
288390c36d8aSUlf Magnusson        """Returns True if the choice has the 'optional' flag set (and so will
288490c36d8aSUlf Magnusson        default to "n" mode)."""
2885f219e013SMasahiro Yamada        return self.optional
2886f219e013SMasahiro Yamada
2887f219e013SMasahiro Yamada    def __str__(self):
2888f219e013SMasahiro Yamada        """Returns a string containing various information about the choice
2889f219e013SMasahiro Yamada        statement."""
2890f219e013SMasahiro Yamada        return self.config._get_sym_or_choice_str(self)
2891f219e013SMasahiro Yamada
2892f219e013SMasahiro Yamada    #
2893f219e013SMasahiro Yamada    # Private methods
2894f219e013SMasahiro Yamada    #
2895f219e013SMasahiro Yamada
2896f219e013SMasahiro Yamada    def __init__(self):
2897f219e013SMasahiro Yamada        """Choice constructor -- not intended to be called directly by
289890c36d8aSUlf Magnusson        Kconfiglib clients."""
2899f219e013SMasahiro Yamada
2900f219e013SMasahiro Yamada        self.name = None # Yes, choices can be named
2901f219e013SMasahiro Yamada        self.type = UNKNOWN
290290c36d8aSUlf Magnusson        self.prompts = []
290390c36d8aSUlf Magnusson        self.def_exprs = [] # 'default' properties
290490c36d8aSUlf Magnusson        self.help = None # Help text
2905*4e1102f6SUlf Magnusson        self.block = [] # List of contained items
290690c36d8aSUlf Magnusson        self.config = None
290790c36d8aSUlf Magnusson        self.parent = None
2908f219e013SMasahiro Yamada
290990c36d8aSUlf Magnusson        self.user_val = None
291090c36d8aSUlf Magnusson        self.user_mode = None
2911f219e013SMasahiro Yamada
2912f219e013SMasahiro Yamada        # We need to filter out symbols that appear within the choice block but
2913f219e013SMasahiro Yamada        # are not considered choice items (see
291490c36d8aSUlf Magnusson        # Choice._determine_actual_symbols()) This list holds the "actual"
291590c36d8aSUlf Magnusson        # choice items.
2916f219e013SMasahiro Yamada        self.actual_symbols = []
2917f219e013SMasahiro Yamada
291890c36d8aSUlf Magnusson        # The prompts and default values without any dependencies from
291990c36d8aSUlf Magnusson        # enclosing menus and ifs propagated
292090c36d8aSUlf Magnusson        self.orig_prompts = []
292190c36d8aSUlf Magnusson        self.orig_def_exprs = []
292290c36d8aSUlf Magnusson
292390c36d8aSUlf Magnusson        # Dependencies inherited from containing menus and ifs
292490c36d8aSUlf Magnusson        self.deps_from_containing = None
2925f219e013SMasahiro Yamada        # The set of symbols referenced by this choice (see
2926f219e013SMasahiro Yamada        # get_referenced_symbols())
2927f219e013SMasahiro Yamada        self.referenced_syms = set()
2928f219e013SMasahiro Yamada        # Like 'referenced_syms', but includes symbols from
292990c36d8aSUlf Magnusson        # dependencies inherited from enclosing menus and ifs
2930f219e013SMasahiro Yamada        self.all_referenced_syms = set()
2931f219e013SMasahiro Yamada
2932f219e013SMasahiro Yamada        # See Choice.get_def_locations()
2933f219e013SMasahiro Yamada        self.def_locations = []
2934f219e013SMasahiro Yamada
293590c36d8aSUlf Magnusson        # Cached values
2936f219e013SMasahiro Yamada        self.cached_selection = None
293790c36d8aSUlf Magnusson        self.cached_visibility = None
293890c36d8aSUlf Magnusson
293990c36d8aSUlf Magnusson        self.optional = False
2940f219e013SMasahiro Yamada
2941f219e013SMasahiro Yamada    def _determine_actual_symbols(self):
2942f219e013SMasahiro Yamada        """If a symbol's visibility depends on the preceding symbol within a
294390c36d8aSUlf Magnusson        choice, it is no longer viewed as a choice item. (This is quite
294490c36d8aSUlf Magnusson        possibly a bug, but some things consciously use it... ugh. It stems
294590c36d8aSUlf Magnusson        from automatic submenu creation.) In addition, it's possible to have
294690c36d8aSUlf Magnusson        choices and comments within choices, and those shouldn't be considered
294790c36d8aSUlf Magnusson        choice items either. Only drivers/usb/gadget/Kconfig seems to depend on
294890c36d8aSUlf Magnusson        any of this. This method computes the "actual" items in the choice and
294990c36d8aSUlf Magnusson        sets the is_choice_sym flag on them (retrieved via is_choice_symbol()).
2950f219e013SMasahiro Yamada
2951f219e013SMasahiro Yamada        Don't let this scare you: an earlier version simply checked for a
2952f219e013SMasahiro Yamada        sequence of symbols where all symbols after the first appeared in the
2953f219e013SMasahiro Yamada        'depends on' expression of the first, and that worked fine.  The added
2954f219e013SMasahiro Yamada        complexity is to be future-proof in the event that
2955f219e013SMasahiro Yamada        drivers/usb/gadget/Kconfig turns even more sinister. It might very well
2956f219e013SMasahiro Yamada        be overkilling things (especially if that file is refactored ;)."""
2957f219e013SMasahiro Yamada
2958f219e013SMasahiro Yamada        # Items might depend on each other in a tree structure, so we need a
2959f219e013SMasahiro Yamada        # stack to keep track of the current tentative parent
2960f219e013SMasahiro Yamada        stack = []
2961f219e013SMasahiro Yamada
296290c36d8aSUlf Magnusson        for item in self.block:
2963f219e013SMasahiro Yamada            if not isinstance(item, Symbol):
2964f219e013SMasahiro Yamada                stack = []
2965f219e013SMasahiro Yamada                continue
2966f219e013SMasahiro Yamada
296790c36d8aSUlf Magnusson            while stack:
2968f219e013SMasahiro Yamada                if item._has_auto_menu_dep_on(stack[-1]):
2969f219e013SMasahiro Yamada                    # The item should not be viewed as a choice item, so don't
297090c36d8aSUlf Magnusson                    # set item.is_choice_sym
2971f219e013SMasahiro Yamada                    stack.append(item)
2972f219e013SMasahiro Yamada                    break
2973f219e013SMasahiro Yamada                else:
2974f219e013SMasahiro Yamada                    stack.pop()
2975f219e013SMasahiro Yamada            else:
297690c36d8aSUlf Magnusson                item.is_choice_sym = True
2977f219e013SMasahiro Yamada                self.actual_symbols.append(item)
2978f219e013SMasahiro Yamada                stack.append(item)
2979f219e013SMasahiro Yamada
2980f219e013SMasahiro Yamada    def _cache_ret(self, selection):
2981f219e013SMasahiro Yamada        # As None is used to indicate the lack of a cached value we can't use
2982f219e013SMasahiro Yamada        # that to cache the fact that the choice has no selection. Instead, we
2983f219e013SMasahiro Yamada        # use the symbolic constant NO_SELECTION.
2984f219e013SMasahiro Yamada        if selection is None:
2985f219e013SMasahiro Yamada            self.cached_selection = NO_SELECTION
2986f219e013SMasahiro Yamada        else:
2987f219e013SMasahiro Yamada            self.cached_selection = selection
2988f219e013SMasahiro Yamada
2989f219e013SMasahiro Yamada        return selection
2990f219e013SMasahiro Yamada
2991f219e013SMasahiro Yamada    def _invalidate(self):
2992f219e013SMasahiro Yamada        self.cached_selection = None
299390c36d8aSUlf Magnusson        self.cached_visibility = None
2994f219e013SMasahiro Yamada
2995f219e013SMasahiro Yamada    def _unset_user_value(self):
2996f219e013SMasahiro Yamada        self._invalidate()
2997f219e013SMasahiro Yamada        self.user_val = None
2998f219e013SMasahiro Yamada        self.user_mode = None
2999f219e013SMasahiro Yamada
300090c36d8aSUlf Magnusson    def _make_conf(self, append_fn):
300190c36d8aSUlf Magnusson        _make_block_conf(self.block, append_fn)
3002f219e013SMasahiro Yamada
3003f219e013SMasahiro Yamadaclass Comment(Item):
3004f219e013SMasahiro Yamada
3005f219e013SMasahiro Yamada    """Represents a comment statement."""
3006f219e013SMasahiro Yamada
3007f219e013SMasahiro Yamada    #
3008f219e013SMasahiro Yamada    # Public interface
3009f219e013SMasahiro Yamada    #
3010f219e013SMasahiro Yamada
3011f219e013SMasahiro Yamada    def get_config(self):
3012f219e013SMasahiro Yamada        """Returns the Config instance this comment is from."""
3013f219e013SMasahiro Yamada        return self.config
3014f219e013SMasahiro Yamada
3015f219e013SMasahiro Yamada    def get_text(self):
3016f219e013SMasahiro Yamada        """Returns the text of the comment."""
3017f219e013SMasahiro Yamada        return self.text
3018f219e013SMasahiro Yamada
3019f219e013SMasahiro Yamada    def get_parent(self):
3020f219e013SMasahiro Yamada        """Returns the menu or choice statement that contains the comment, or
3021f219e013SMasahiro Yamada        None if the comment is at the top level. Note that if statements are
3022f219e013SMasahiro Yamada        treated as syntactic sugar and do not have an explicit class
3023f219e013SMasahiro Yamada        representation."""
3024f219e013SMasahiro Yamada        return self.parent
3025f219e013SMasahiro Yamada
3026f219e013SMasahiro Yamada    def get_location(self):
3027f219e013SMasahiro Yamada        """Returns the location of the comment as a (filename, linenr) tuple,
3028f219e013SMasahiro Yamada        where filename is a string and linenr an int."""
3029f219e013SMasahiro Yamada        return (self.filename, self.linenr)
3030f219e013SMasahiro Yamada
303190c36d8aSUlf Magnusson    def get_visibility(self):
303290c36d8aSUlf Magnusson        """Returns the visibility of the comment. See also
303390c36d8aSUlf Magnusson        Symbol.get_visibility()."""
303490c36d8aSUlf Magnusson        return self.config._eval_expr(self.dep_expr)
3035f219e013SMasahiro Yamada
303690c36d8aSUlf Magnusson    def get_referenced_symbols(self, refs_from_enclosing=False):
303790c36d8aSUlf Magnusson        """See Symbol.get_referenced_symbols()."""
303890c36d8aSUlf Magnusson        return self.all_referenced_syms if refs_from_enclosing else \
303990c36d8aSUlf Magnusson               self.referenced_syms
304090c36d8aSUlf Magnusson
304190c36d8aSUlf Magnusson    def __str__(self):
304290c36d8aSUlf Magnusson        """Returns a string containing various information about the
304390c36d8aSUlf Magnusson        comment."""
304490c36d8aSUlf Magnusson        dep_str = self.config._expr_val_str(self.orig_deps,
304590c36d8aSUlf Magnusson                                            "(no dependencies)")
304690c36d8aSUlf Magnusson
304790c36d8aSUlf Magnusson        additional_deps_str = " " + \
304890c36d8aSUlf Magnusson          self.config._expr_val_str(self.deps_from_containing,
3049f219e013SMasahiro Yamada                                    "(no additional dependencies)")
3050f219e013SMasahiro Yamada
305190c36d8aSUlf Magnusson        return _lines("Comment",
3052f219e013SMasahiro Yamada                      "Text: "         + str(self.text),
3053f219e013SMasahiro Yamada                      "Dependencies: " + dep_str,
305490c36d8aSUlf Magnusson                      "Additional dependencies from enclosing menus and "
305590c36d8aSUlf Magnusson                        "ifs:",
3056f219e013SMasahiro Yamada                      additional_deps_str,
3057f219e013SMasahiro Yamada                      "Location: {0}:{1}".format(self.filename, self.linenr))
3058f219e013SMasahiro Yamada
3059f219e013SMasahiro Yamada    #
3060f219e013SMasahiro Yamada    # Private methods
3061f219e013SMasahiro Yamada    #
3062f219e013SMasahiro Yamada
3063f219e013SMasahiro Yamada    def __init__(self):
3064f219e013SMasahiro Yamada        """Comment constructor -- not intended to be called directly by
306590c36d8aSUlf Magnusson        Kconfiglib clients."""
3066f219e013SMasahiro Yamada
3067f219e013SMasahiro Yamada        self.text = None
3068f219e013SMasahiro Yamada        self.dep_expr = None
306990c36d8aSUlf Magnusson        self.config = None
307090c36d8aSUlf Magnusson        self.parent = None
3071f219e013SMasahiro Yamada
3072f219e013SMasahiro Yamada        # Dependency expression without dependencies from enclosing menus and
307390c36d8aSUlf Magnusson        # ifs propagated
3074f219e013SMasahiro Yamada        self.orig_deps = None
3075f219e013SMasahiro Yamada
307690c36d8aSUlf Magnusson        # Dependencies inherited from containing menus and ifs
3077f219e013SMasahiro Yamada        self.deps_from_containing = None
3078f219e013SMasahiro Yamada        # The set of symbols referenced by this comment (see
3079f219e013SMasahiro Yamada        # get_referenced_symbols())
3080f219e013SMasahiro Yamada        self.referenced_syms = set()
3081f219e013SMasahiro Yamada        # Like 'referenced_syms', but includes symbols from
308290c36d8aSUlf Magnusson        # dependencies inherited from enclosing menus and ifs
3083f219e013SMasahiro Yamada        self.all_referenced_syms = None
3084f219e013SMasahiro Yamada
3085f219e013SMasahiro Yamada        self.filename = None
3086f219e013SMasahiro Yamada        self.linenr = None
3087f219e013SMasahiro Yamada
308890c36d8aSUlf Magnusson    def _make_conf(self, append_fn):
3089f219e013SMasahiro Yamada        if self.config._eval_expr(self.dep_expr) != "n":
309090c36d8aSUlf Magnusson            append_fn("\n#\n# {0}\n#".format(self.text))
3091f219e013SMasahiro Yamada
309290c36d8aSUlf Magnussonclass Kconfig_Syntax_Error(Exception):
309390c36d8aSUlf Magnusson    """Exception raised for syntax errors."""
309490c36d8aSUlf Magnusson    pass
3095f219e013SMasahiro Yamada
309690c36d8aSUlf Magnussonclass Internal_Error(Exception):
309790c36d8aSUlf Magnusson    """Exception raised for internal errors."""
309890c36d8aSUlf Magnusson    pass
309990c36d8aSUlf Magnusson
310090c36d8aSUlf Magnusson#
310190c36d8aSUlf Magnusson# Public functions
310290c36d8aSUlf Magnusson#
310390c36d8aSUlf Magnusson
310490c36d8aSUlf Magnussondef tri_less(v1, v2):
310590c36d8aSUlf Magnusson    """Returns True if the tristate v1 is less than the tristate v2, where "n",
310690c36d8aSUlf Magnusson    "m" and "y" are ordered from lowest to highest."""
310790c36d8aSUlf Magnusson    return TRI_TO_INT[v1] < TRI_TO_INT[v2]
310890c36d8aSUlf Magnusson
310990c36d8aSUlf Magnussondef tri_less_eq(v1, v2):
311090c36d8aSUlf Magnusson    """Returns True if the tristate v1 is less than or equal to the tristate
311190c36d8aSUlf Magnusson    v2, where "n", "m" and "y" are ordered from lowest to highest."""
311290c36d8aSUlf Magnusson    return TRI_TO_INT[v1] <= TRI_TO_INT[v2]
311390c36d8aSUlf Magnusson
311490c36d8aSUlf Magnussondef tri_greater(v1, v2):
311590c36d8aSUlf Magnusson    """Returns True if the tristate v1 is greater than the tristate v2, where
311690c36d8aSUlf Magnusson    "n", "m" and "y" are ordered from lowest to highest."""
311790c36d8aSUlf Magnusson    return TRI_TO_INT[v1] > TRI_TO_INT[v2]
311890c36d8aSUlf Magnusson
311990c36d8aSUlf Magnussondef tri_greater_eq(v1, v2):
312090c36d8aSUlf Magnusson    """Returns True if the tristate v1 is greater than or equal to the tristate
312190c36d8aSUlf Magnusson    v2, where "n", "m" and "y" are ordered from lowest to highest."""
312290c36d8aSUlf Magnusson    return TRI_TO_INT[v1] >= TRI_TO_INT[v2]
312390c36d8aSUlf Magnusson
312490c36d8aSUlf Magnusson#
312590c36d8aSUlf Magnusson# Internal classes
312690c36d8aSUlf Magnusson#
312790c36d8aSUlf Magnusson
312890c36d8aSUlf Magnussonclass _Feed(object):
312990c36d8aSUlf Magnusson
313090c36d8aSUlf Magnusson    """Class for working with sequences in a stream-like fashion; handy for
313190c36d8aSUlf Magnusson    tokens."""
313290c36d8aSUlf Magnusson
313390c36d8aSUlf Magnusson    # This would be more helpful on the item classes, but would remove some
313490c36d8aSUlf Magnusson    # flexibility
313590c36d8aSUlf Magnusson    __slots__ = ['items', 'length', 'i']
3136f219e013SMasahiro Yamada
3137f219e013SMasahiro Yamada    def __init__(self, items):
3138f219e013SMasahiro Yamada        self.items = items
3139f219e013SMasahiro Yamada        self.length = len(self.items)
3140f219e013SMasahiro Yamada        self.i = 0
3141f219e013SMasahiro Yamada
3142f219e013SMasahiro Yamada    def get_next(self):
3143f219e013SMasahiro Yamada        if self.i >= self.length:
3144f219e013SMasahiro Yamada            return None
3145f219e013SMasahiro Yamada        item = self.items[self.i]
3146f219e013SMasahiro Yamada        self.i += 1
3147f219e013SMasahiro Yamada        return item
3148f219e013SMasahiro Yamada
3149f219e013SMasahiro Yamada    def peek_next(self):
3150f219e013SMasahiro Yamada        return None if self.i >= self.length else self.items[self.i]
3151f219e013SMasahiro Yamada
3152f219e013SMasahiro Yamada    def check(self, token):
3153f219e013SMasahiro Yamada        """Check if the next token is 'token'. If so, remove it from the token
3154f219e013SMasahiro Yamada        feed and return True. Otherwise, leave it in and return False."""
315590c36d8aSUlf Magnusson        if self.i < self.length and self.items[self.i] == token:
3156f219e013SMasahiro Yamada            self.i += 1
3157f219e013SMasahiro Yamada            return True
3158f219e013SMasahiro Yamada        return False
3159f219e013SMasahiro Yamada
316090c36d8aSUlf Magnusson    def unget_all(self):
316190c36d8aSUlf Magnusson        self.i = 0
3162f219e013SMasahiro Yamada
316390c36d8aSUlf Magnussonclass _FileFeed(object):
3164f219e013SMasahiro Yamada
316590c36d8aSUlf Magnusson    """Feeds lines from a file. Keeps track of the filename and current line
316690c36d8aSUlf Magnusson    number. Joins any line ending in \\ with the following line. We need to be
316790c36d8aSUlf Magnusson    careful to get the line number right in the presence of continuation
316890c36d8aSUlf Magnusson    lines."""
3169f219e013SMasahiro Yamada
317090c36d8aSUlf Magnusson    __slots__ = ['filename', 'lines', 'length', 'linenr']
3171f219e013SMasahiro Yamada
317290c36d8aSUlf Magnusson    def __init__(self, filename):
3173f219e013SMasahiro Yamada        self.filename = _clean_up_path(filename)
317490c36d8aSUlf Magnusson        with open(filename, "r") as f:
317590c36d8aSUlf Magnusson            # No interleaving of I/O and processing yet. Don't know if it would
317690c36d8aSUlf Magnusson            # help.
317790c36d8aSUlf Magnusson            self.lines = f.readlines()
317890c36d8aSUlf Magnusson        self.length = len(self.lines)
317990c36d8aSUlf Magnusson        self.linenr = 0
3180f219e013SMasahiro Yamada
318190c36d8aSUlf Magnusson    def get_next(self):
318290c36d8aSUlf Magnusson        if self.linenr >= self.length:
318390c36d8aSUlf Magnusson            return None
318490c36d8aSUlf Magnusson        line = self.lines[self.linenr]
318590c36d8aSUlf Magnusson        self.linenr += 1
318690c36d8aSUlf Magnusson        while line.endswith("\\\n"):
318790c36d8aSUlf Magnusson            line = line[:-2] + self.lines[self.linenr]
318890c36d8aSUlf Magnusson            self.linenr += 1
318990c36d8aSUlf Magnusson        return line
3190f219e013SMasahiro Yamada
319190c36d8aSUlf Magnusson    def peek_next(self):
319290c36d8aSUlf Magnusson        linenr = self.linenr
319390c36d8aSUlf Magnusson        if linenr >= self.length:
319490c36d8aSUlf Magnusson            return None
319590c36d8aSUlf Magnusson        line = self.lines[linenr]
319690c36d8aSUlf Magnusson        while line.endswith("\\\n"):
319790c36d8aSUlf Magnusson            linenr += 1
319890c36d8aSUlf Magnusson            line = line[:-2] + self.lines[linenr]
319990c36d8aSUlf Magnusson        return line
320090c36d8aSUlf Magnusson
320190c36d8aSUlf Magnusson    def unget(self):
320290c36d8aSUlf Magnusson        self.linenr -= 1
320390c36d8aSUlf Magnusson        while self.lines[self.linenr].endswith("\\\n"):
320490c36d8aSUlf Magnusson            self.linenr -= 1
320590c36d8aSUlf Magnusson
320690c36d8aSUlf Magnusson    def next_nonblank(self):
320790c36d8aSUlf Magnusson        """Removes lines up to and including the next non-blank (not all-space)
320890c36d8aSUlf Magnusson        line and returns it. Returns None if there are no more non-blank
320990c36d8aSUlf Magnusson        lines."""
321090c36d8aSUlf Magnusson        while 1:
321190c36d8aSUlf Magnusson            line = self.get_next()
321290c36d8aSUlf Magnusson            if line is None or not line.isspace():
321390c36d8aSUlf Magnusson                return line
3214f219e013SMasahiro Yamada
3215f219e013SMasahiro Yamada#
321690c36d8aSUlf Magnusson# Internal functions
3217f219e013SMasahiro Yamada#
3218f219e013SMasahiro Yamada
321990c36d8aSUlf Magnussondef _get_visibility(sc):
322090c36d8aSUlf Magnusson    """Symbols and Choices have a "visibility" that acts as an upper bound on
322190c36d8aSUlf Magnusson    the values a user can set for them, corresponding to the visibility in e.g.
322290c36d8aSUlf Magnusson    'make menuconfig'. This function calculates the visibility for the Symbol
322390c36d8aSUlf Magnusson    or Choice 'sc' -- the logic is nearly identical."""
322490c36d8aSUlf Magnusson    if sc.cached_visibility is None:
322590c36d8aSUlf Magnusson        vis = "n"
322690c36d8aSUlf Magnusson        for _, cond_expr in sc.prompts:
322790c36d8aSUlf Magnusson            vis = sc.config._eval_max(vis, cond_expr)
3228f219e013SMasahiro Yamada
322990c36d8aSUlf Magnusson        if isinstance(sc, Symbol) and sc.is_choice_sym:
3230*4e1102f6SUlf Magnusson            if sc.type == TRISTATE and vis == "m" and \
3231*4e1102f6SUlf Magnusson               sc.parent.get_mode() == "y":
3232*4e1102f6SUlf Magnusson                # Choice symbols with visibility "m" are not visible if the
3233*4e1102f6SUlf Magnusson                # choice has mode "y"
3234*4e1102f6SUlf Magnusson                vis = "n"
3235*4e1102f6SUlf Magnusson            else:
323690c36d8aSUlf Magnusson                vis = sc.config._eval_min(vis, _get_visibility(sc.parent))
3237f219e013SMasahiro Yamada
323890c36d8aSUlf Magnusson        # Promote "m" to "y" if we're dealing with a non-tristate
323990c36d8aSUlf Magnusson        if vis == "m" and sc.type != TRISTATE:
324090c36d8aSUlf Magnusson            vis = "y"
3241f219e013SMasahiro Yamada
324290c36d8aSUlf Magnusson        sc.cached_visibility = vis
3243f219e013SMasahiro Yamada
324490c36d8aSUlf Magnusson    return sc.cached_visibility
3245f219e013SMasahiro Yamada
324690c36d8aSUlf Magnussondef _make_and(e1, e2):
324790c36d8aSUlf Magnusson    """Constructs an AND (&&) expression. Performs trivial simplification.
324890c36d8aSUlf Magnusson    Nones equate to 'y'.
324990c36d8aSUlf Magnusson
325090c36d8aSUlf Magnusson    Note: returns None if e1 == e2 == None."""
325190c36d8aSUlf Magnusson    if e1 is None or e1 == "y":
325290c36d8aSUlf Magnusson        return e2
325390c36d8aSUlf Magnusson    if e2 is None or e2 == "y":
325490c36d8aSUlf Magnusson        return e1
325590c36d8aSUlf Magnusson
325690c36d8aSUlf Magnusson    # Prefer to merge argument lists if possible to reduce the number of nodes
325790c36d8aSUlf Magnusson
325890c36d8aSUlf Magnusson    if isinstance(e1, tuple) and e1[0] == AND:
325990c36d8aSUlf Magnusson        if isinstance(e2, tuple) and e2[0] == AND:
326090c36d8aSUlf Magnusson            return (AND, e1[1] + e2[1])
326190c36d8aSUlf Magnusson        return (AND, e1[1] + [e2])
326290c36d8aSUlf Magnusson
326390c36d8aSUlf Magnusson    if isinstance(e2, tuple) and e2[0] == AND:
326490c36d8aSUlf Magnusson        return (AND, e2[1] + [e1])
326590c36d8aSUlf Magnusson
326690c36d8aSUlf Magnusson    return (AND, [e1, e2])
326790c36d8aSUlf Magnusson
326890c36d8aSUlf Magnussondef _make_or(e1, e2):
326990c36d8aSUlf Magnusson    """Constructs an OR (||) expression. Performs trivial simplification and
327090c36d8aSUlf Magnusson    avoids Nones. Nones equate to 'y', which is usually what we want, but needs
327190c36d8aSUlf Magnusson    to be kept in mind."""
327290c36d8aSUlf Magnusson
327390c36d8aSUlf Magnusson    # Perform trivial simplification and avoid None's (which
327490c36d8aSUlf Magnusson    # correspond to y's)
327590c36d8aSUlf Magnusson    if e1 is None or e2 is None or e1 == "y" or e2 == "y":
327690c36d8aSUlf Magnusson        return "y"
327790c36d8aSUlf Magnusson    if e1 == "n":
327890c36d8aSUlf Magnusson        return e2
327990c36d8aSUlf Magnusson
328090c36d8aSUlf Magnusson    # Prefer to merge argument lists if possible to reduce the number of nodes
328190c36d8aSUlf Magnusson
328290c36d8aSUlf Magnusson    if isinstance(e1, tuple) and e1[0] == OR:
328390c36d8aSUlf Magnusson        if isinstance(e2, tuple) and e2[0] == OR:
328490c36d8aSUlf Magnusson            return (OR, e1[1] + e2[1])
328590c36d8aSUlf Magnusson        return (OR, e1[1] + [e2])
328690c36d8aSUlf Magnusson
328790c36d8aSUlf Magnusson    if isinstance(e2, tuple) and e2[0] == OR:
328890c36d8aSUlf Magnusson        return (OR, e2[1] + [e1])
328990c36d8aSUlf Magnusson
329090c36d8aSUlf Magnusson    return (OR, [e1, e2])
329190c36d8aSUlf Magnusson
329290c36d8aSUlf Magnussondef _get_expr_syms_rec(expr, res):
329390c36d8aSUlf Magnusson    """_get_expr_syms() helper. Recurses through expressions."""
329490c36d8aSUlf Magnusson    if isinstance(expr, Symbol):
329590c36d8aSUlf Magnusson        res.add(expr)
329690c36d8aSUlf Magnusson    elif isinstance(expr, str):
329790c36d8aSUlf Magnusson        return
329890c36d8aSUlf Magnusson    elif expr[0] == AND or expr[0] == OR:
329990c36d8aSUlf Magnusson        for term in expr[1]:
330090c36d8aSUlf Magnusson            _get_expr_syms_rec(term, res)
330190c36d8aSUlf Magnusson    elif expr[0] == NOT:
330290c36d8aSUlf Magnusson        _get_expr_syms_rec(expr[1], res)
330390c36d8aSUlf Magnusson    elif expr[0] == EQUAL or expr[0] == UNEQUAL:
330490c36d8aSUlf Magnusson        if isinstance(expr[1], Symbol):
330590c36d8aSUlf Magnusson            res.add(expr[1])
330690c36d8aSUlf Magnusson        if isinstance(expr[2], Symbol):
330790c36d8aSUlf Magnusson            res.add(expr[2])
330890c36d8aSUlf Magnusson    else:
330990c36d8aSUlf Magnusson        _internal_error("Internal error while fetching symbols from an "
331090c36d8aSUlf Magnusson                        "expression with token stream {0}.".format(expr))
331190c36d8aSUlf Magnusson
331290c36d8aSUlf Magnussondef _get_expr_syms(expr):
331390c36d8aSUlf Magnusson    """Returns the set() of symbols appearing in expr."""
331490c36d8aSUlf Magnusson    res = set()
331590c36d8aSUlf Magnusson    if expr is not None:
331690c36d8aSUlf Magnusson        _get_expr_syms_rec(expr, res)
331790c36d8aSUlf Magnusson    return res
331890c36d8aSUlf Magnusson
331990c36d8aSUlf Magnussondef _str_val(obj):
332090c36d8aSUlf Magnusson    """Returns the value of obj as a string. If obj is not a string (constant
332190c36d8aSUlf Magnusson    symbol), it must be a Symbol."""
332290c36d8aSUlf Magnusson    return obj if isinstance(obj, str) else obj.get_value()
332390c36d8aSUlf Magnusson
332490c36d8aSUlf Magnussondef _make_block_conf(block, append_fn):
332590c36d8aSUlf Magnusson    """Returns a list of .config strings for a block (list) of items."""
332690c36d8aSUlf Magnusson
332790c36d8aSUlf Magnusson    # Collect the substrings in a list and later use join() instead of += to
332890c36d8aSUlf Magnusson    # build the final .config contents. With older Python versions, this yields
332990c36d8aSUlf Magnusson    # linear instead of quadratic complexity.
333090c36d8aSUlf Magnusson    for item in block:
333190c36d8aSUlf Magnusson        item._make_conf(append_fn)
333290c36d8aSUlf Magnusson
333390c36d8aSUlf Magnussondef _sym_str_string(sym_or_str):
333490c36d8aSUlf Magnusson    if isinstance(sym_or_str, str):
333590c36d8aSUlf Magnusson        return '"' + sym_or_str + '"'
333690c36d8aSUlf Magnusson    return sym_or_str.name
333790c36d8aSUlf Magnusson
333890c36d8aSUlf Magnussondef _intersperse(lst, op):
333990c36d8aSUlf Magnusson    """_expr_to_str() helper. Gets the string representation of each expression
334090c36d8aSUlf Magnusson    in lst and produces a list where op has been inserted between the
334190c36d8aSUlf Magnusson    elements."""
334290c36d8aSUlf Magnusson    if not lst:
3343f219e013SMasahiro Yamada        return ""
334490c36d8aSUlf Magnusson
334590c36d8aSUlf Magnusson    res = []
334690c36d8aSUlf Magnusson
334790c36d8aSUlf Magnusson    def handle_sub_expr(expr):
334890c36d8aSUlf Magnusson        no_parens = isinstance(expr, (str, Symbol)) or \
334990c36d8aSUlf Magnusson                    expr[0] in (EQUAL, UNEQUAL) or \
335090c36d8aSUlf Magnusson                    PRECEDENCE[op] <= PRECEDENCE[expr[0]]
335190c36d8aSUlf Magnusson        if not no_parens:
335290c36d8aSUlf Magnusson            res.append("(")
335390c36d8aSUlf Magnusson        res.extend(_expr_to_str_rec(expr))
335490c36d8aSUlf Magnusson        if not no_parens:
335590c36d8aSUlf Magnusson            res.append(")")
335690c36d8aSUlf Magnusson
335790c36d8aSUlf Magnusson    op_str = OP_TO_STR[op]
335890c36d8aSUlf Magnusson
335990c36d8aSUlf Magnusson    handle_sub_expr(lst[0])
336090c36d8aSUlf Magnusson    for expr in lst[1:]:
336190c36d8aSUlf Magnusson        res.append(op_str)
336290c36d8aSUlf Magnusson        handle_sub_expr(expr)
336390c36d8aSUlf Magnusson
336490c36d8aSUlf Magnusson    return res
336590c36d8aSUlf Magnusson
336690c36d8aSUlf Magnussondef _expr_to_str_rec(expr):
336790c36d8aSUlf Magnusson    if expr is None:
336890c36d8aSUlf Magnusson        return [""]
336990c36d8aSUlf Magnusson
337090c36d8aSUlf Magnusson    if isinstance(expr, (Symbol, str)):
337190c36d8aSUlf Magnusson        return [_sym_str_string(expr)]
337290c36d8aSUlf Magnusson
337390c36d8aSUlf Magnusson    if expr[0] in (AND, OR):
337490c36d8aSUlf Magnusson        return _intersperse(expr[1], expr[0])
337590c36d8aSUlf Magnusson
337690c36d8aSUlf Magnusson    if expr[0] == NOT:
337790c36d8aSUlf Magnusson        need_parens = not isinstance(expr[1], (str, Symbol))
337890c36d8aSUlf Magnusson
337990c36d8aSUlf Magnusson        res = ["!"]
338090c36d8aSUlf Magnusson        if need_parens:
338190c36d8aSUlf Magnusson            res.append("(")
338290c36d8aSUlf Magnusson        res.extend(_expr_to_str_rec(expr[1]))
338390c36d8aSUlf Magnusson        if need_parens:
338490c36d8aSUlf Magnusson            res.append(")")
338590c36d8aSUlf Magnusson        return res
338690c36d8aSUlf Magnusson
338790c36d8aSUlf Magnusson    if expr[0] in (EQUAL, UNEQUAL):
338890c36d8aSUlf Magnusson        return [_sym_str_string(expr[1]),
338990c36d8aSUlf Magnusson                OP_TO_STR[expr[0]],
339090c36d8aSUlf Magnusson                _sym_str_string(expr[2])]
339190c36d8aSUlf Magnusson
339290c36d8aSUlf Magnussondef _expr_to_str(expr):
339390c36d8aSUlf Magnusson    return "".join(_expr_to_str_rec(expr))
3394f219e013SMasahiro Yamada
3395f219e013SMasahiro Yamadadef _indentation(line):
339690c36d8aSUlf Magnusson    """Returns the length of the line's leading whitespace, treating tab stops
339790c36d8aSUlf Magnusson    as being spaced 8 characters apart."""
339890c36d8aSUlf Magnusson    line = line.expandtabs()
339990c36d8aSUlf Magnusson    return len(line) - len(line.lstrip())
3400f219e013SMasahiro Yamada
3401f219e013SMasahiro Yamadadef _deindent(line, indent):
3402f219e013SMasahiro Yamada    """Deindent 'line' by 'indent' spaces."""
3403f219e013SMasahiro Yamada    line = line.expandtabs()
3404f219e013SMasahiro Yamada    if len(line) <= indent:
3405f219e013SMasahiro Yamada        return line
3406f219e013SMasahiro Yamada    return line[indent:]
3407f219e013SMasahiro Yamada
3408f219e013SMasahiro Yamadadef _is_base_n(s, n):
3409f219e013SMasahiro Yamada    try:
3410f219e013SMasahiro Yamada        int(s, n)
3411f219e013SMasahiro Yamada        return True
3412f219e013SMasahiro Yamada    except ValueError:
3413f219e013SMasahiro Yamada        return False
3414f219e013SMasahiro Yamada
341590c36d8aSUlf Magnussondef _lines(*args):
341690c36d8aSUlf Magnusson    """Returns a string consisting of all arguments, with newlines inserted
3417f219e013SMasahiro Yamada    between them."""
3418f219e013SMasahiro Yamada    return "\n".join(args)
3419f219e013SMasahiro Yamada
3420f219e013SMasahiro Yamadadef _comment(s):
3421f219e013SMasahiro Yamada    """Returns a new string with "#" inserted before each line in 's'."""
3422f219e013SMasahiro Yamada    if not s:
3423f219e013SMasahiro Yamada        return "#"
3424f219e013SMasahiro Yamada    res = "".join(["#" + line for line in s.splitlines(True)])
3425f219e013SMasahiro Yamada    if s.endswith("\n"):
3426f219e013SMasahiro Yamada        return res + "#"
3427f219e013SMasahiro Yamada    return res
3428f219e013SMasahiro Yamada
3429f219e013SMasahiro Yamadadef _clean_up_path(path):
343090c36d8aSUlf Magnusson    """Strips an initial "./" and any trailing slashes from 'path'."""
3431f219e013SMasahiro Yamada    if path.startswith("./"):
3432f219e013SMasahiro Yamada        path = path[2:]
343390c36d8aSUlf Magnusson    return path.rstrip("/")
3434f219e013SMasahiro Yamada
34358639f69aSSimon Glassdef _build_msg(msg, filename, linenr):
3436f219e013SMasahiro Yamada    if filename is not None:
34378639f69aSSimon Glass        msg = "{0}:{1}: ".format(_clean_up_path(filename), linenr) + msg
34388639f69aSSimon Glass    return msg
34398639f69aSSimon Glass
34408639f69aSSimon Glassdef _stderr_msg(msg, filename, linenr):
34418639f69aSSimon Glass    sys.stderr.write(_build_msg(msg, filename, linenr) + "\n")
3442f219e013SMasahiro Yamada
344390c36d8aSUlf Magnussondef _tokenization_error(s, filename, linenr):
344490c36d8aSUlf Magnusson    loc = "" if filename is None else "{0}:{1}: ".format(filename, linenr)
344590c36d8aSUlf Magnusson    raise Kconfig_Syntax_Error("{0}Couldn't tokenize '{1}'"
344690c36d8aSUlf Magnusson                               .format(loc, s.strip()))
3447f219e013SMasahiro Yamada
3448f219e013SMasahiro Yamadadef _parse_error(s, msg, filename, linenr):
344990c36d8aSUlf Magnusson    loc = "" if filename is None else "{0}:{1}: ".format(filename, linenr)
345090c36d8aSUlf Magnusson    raise Kconfig_Syntax_Error("{0}Couldn't parse '{1}'{2}"
345190c36d8aSUlf Magnusson                               .format(loc, s.strip(),
345290c36d8aSUlf Magnusson                                       "." if msg is None else ": " + msg))
3453f219e013SMasahiro Yamada
3454f219e013SMasahiro Yamadadef _internal_error(msg):
345590c36d8aSUlf Magnusson    raise Internal_Error(msg +
345690c36d8aSUlf Magnusson      "\nSorry! You may want to send an email to ulfalizer a.t Google's "
345790c36d8aSUlf Magnusson      "email service to tell me about this. Include the message above and the "
345890c36d8aSUlf Magnusson      "stack trace and describe what you were doing.")
3459f219e013SMasahiro Yamada
346090c36d8aSUlf Magnusson#
346190c36d8aSUlf Magnusson# Internal global constants
346290c36d8aSUlf Magnusson#
3463f219e013SMasahiro Yamada
346490c36d8aSUlf Magnusson# Tokens
346590c36d8aSUlf Magnusson(T_AND, T_OR, T_NOT,
346690c36d8aSUlf Magnusson T_OPEN_PAREN, T_CLOSE_PAREN,
346790c36d8aSUlf Magnusson T_EQUAL, T_UNEQUAL,
346890c36d8aSUlf Magnusson T_MAINMENU, T_MENU, T_ENDMENU,
346990c36d8aSUlf Magnusson T_SOURCE, T_CHOICE, T_ENDCHOICE,
347090c36d8aSUlf Magnusson T_COMMENT, T_CONFIG, T_MENUCONFIG,
347190c36d8aSUlf Magnusson T_HELP, T_IF, T_ENDIF, T_DEPENDS, T_ON,
347290c36d8aSUlf Magnusson T_OPTIONAL, T_PROMPT, T_DEFAULT,
347390c36d8aSUlf Magnusson T_BOOL, T_TRISTATE, T_HEX, T_INT, T_STRING,
347490c36d8aSUlf Magnusson T_DEF_BOOL, T_DEF_TRISTATE,
3475d036107aSTom Rini T_SELECT, T_IMPLY, T_RANGE, T_OPTION, T_ALLNOCONFIG_Y, T_ENV,
3476d036107aSTom Rini T_DEFCONFIG_LIST, T_MODULES, T_VISIBLE) = range(40)
3477f219e013SMasahiro Yamada
347890c36d8aSUlf Magnusson# The leading underscore before the function assignments below prevent pydoc
347990c36d8aSUlf Magnusson# from listing them. The constants could be hidden too, but they're fairly
348090c36d8aSUlf Magnusson# obviously internal anyway, so don't bother spamming the code.
3481f219e013SMasahiro Yamada
348290c36d8aSUlf Magnusson# Keyword to token map. Note that the get() method is assigned directly as a
348390c36d8aSUlf Magnusson# small optimization.
348490c36d8aSUlf Magnusson_get_keyword = \
348590c36d8aSUlf Magnusson  {"mainmenu": T_MAINMENU, "menu": T_MENU, "endmenu": T_ENDMENU,
348690c36d8aSUlf Magnusson   "endif": T_ENDIF, "endchoice": T_ENDCHOICE, "source": T_SOURCE,
348790c36d8aSUlf Magnusson   "choice": T_CHOICE, "config": T_CONFIG, "comment": T_COMMENT,
348890c36d8aSUlf Magnusson   "menuconfig": T_MENUCONFIG, "help": T_HELP, "if": T_IF,
348990c36d8aSUlf Magnusson   "depends": T_DEPENDS, "on": T_ON, "optional": T_OPTIONAL,
349090c36d8aSUlf Magnusson   "prompt": T_PROMPT, "default": T_DEFAULT, "bool": T_BOOL, "boolean": T_BOOL,
349190c36d8aSUlf Magnusson   "tristate": T_TRISTATE, "int": T_INT, "hex": T_HEX, "def_bool": T_DEF_BOOL,
349290c36d8aSUlf Magnusson   "def_tristate": T_DEF_TRISTATE, "string": T_STRING, "select": T_SELECT,
3493d036107aSTom Rini   "imply" : T_IMPLY, "range": T_RANGE, "option": T_OPTION,
3494d036107aSTom Rini   "allnoconfig_y": T_ALLNOCONFIG_Y, "env": T_ENV,
3495d036107aSTom Rini   "defconfig_list": T_DEFCONFIG_LIST, "modules": T_MODULES,
349690c36d8aSUlf Magnusson   "visible": T_VISIBLE}.get
349790c36d8aSUlf Magnusson
349890c36d8aSUlf Magnusson# Strings to use for True and False
349990c36d8aSUlf MagnussonBOOL_STR = {False: "false", True: "true"}
350090c36d8aSUlf Magnusson
350190c36d8aSUlf Magnusson# Tokens after which identifier-like lexemes are treated as strings. T_CHOICE
350290c36d8aSUlf Magnusson# is included to avoid symbols being registered for named choices.
350390c36d8aSUlf MagnussonSTRING_LEX = frozenset((T_BOOL, T_TRISTATE, T_INT, T_HEX, T_STRING, T_CHOICE,
350490c36d8aSUlf Magnusson                        T_PROMPT, T_MENU, T_COMMENT, T_SOURCE, T_MAINMENU))
350590c36d8aSUlf Magnusson
350690c36d8aSUlf Magnusson# Matches the initial token on a line; see _tokenize(). Also eats trailing
350790c36d8aSUlf Magnusson# whitespace as an optimization.
350890c36d8aSUlf Magnusson_initial_token_re_match = re.compile(r"[^\w]*(\w+)\s*").match
350990c36d8aSUlf Magnusson
351090c36d8aSUlf Magnusson# Matches an identifier/keyword optionally preceded by whitespace. Also eats
351190c36d8aSUlf Magnusson# trailing whitespace as an optimization.
351290c36d8aSUlf Magnusson_id_keyword_re_match = re.compile(r"\s*([\w./-]+)\s*").match
351390c36d8aSUlf Magnusson
351490c36d8aSUlf Magnusson# Regular expression for finding $-references to symbols in strings
351590c36d8aSUlf Magnusson_sym_ref_re_search = re.compile(r"\$[A-Za-z0-9_]+").search
351690c36d8aSUlf Magnusson
351790c36d8aSUlf Magnusson# Integers representing symbol types
351890c36d8aSUlf MagnussonUNKNOWN, BOOL, TRISTATE, STRING, HEX, INT = range(6)
351990c36d8aSUlf Magnusson
352090c36d8aSUlf Magnusson# Strings to use for types
352190c36d8aSUlf MagnussonTYPENAME = {UNKNOWN: "unknown", BOOL: "bool", TRISTATE: "tristate",
352290c36d8aSUlf Magnusson            STRING: "string", HEX: "hex", INT: "int"}
352390c36d8aSUlf Magnusson
352490c36d8aSUlf Magnusson# Token to type mapping
352590c36d8aSUlf MagnussonTOKEN_TO_TYPE = {T_BOOL: BOOL, T_TRISTATE: TRISTATE, T_STRING: STRING,
352690c36d8aSUlf Magnusson                 T_INT: INT, T_HEX: HEX}
352790c36d8aSUlf Magnusson
352890c36d8aSUlf Magnusson# Default values for symbols of different types (the value the symbol gets if
352990c36d8aSUlf Magnusson# it is not assigned a user value and none of its 'default' clauses kick in)
353090c36d8aSUlf MagnussonDEFAULT_VALUE = {BOOL: "n", TRISTATE: "n", STRING: "", INT: "", HEX: ""}
353190c36d8aSUlf Magnusson
353290c36d8aSUlf Magnusson# Indicates that no item is selected in a choice statement
353390c36d8aSUlf MagnussonNO_SELECTION = 0
353490c36d8aSUlf Magnusson
353590c36d8aSUlf Magnusson# Integers representing expression types
353690c36d8aSUlf MagnussonAND, OR, NOT, EQUAL, UNEQUAL = range(5)
353790c36d8aSUlf Magnusson
353890c36d8aSUlf Magnusson# Map from tristate values to integers
353990c36d8aSUlf MagnussonTRI_TO_INT = {"n": 0, "m": 1, "y": 2}
354090c36d8aSUlf Magnusson
354190c36d8aSUlf Magnusson# Printing-related stuff
354290c36d8aSUlf Magnusson
354390c36d8aSUlf MagnussonOP_TO_STR = {AND: " && ", OR: " || ", EQUAL: " = ", UNEQUAL: " != "}
354490c36d8aSUlf MagnussonPRECEDENCE = {OR: 0, AND: 1, NOT: 2}
3545