xref: /rk3399_rockchip-uboot/tools/buildman/kconfiglib.py (revision 90c36d8ab91f07679145f7fe79d7a20d4b016b31)
1f219e013SMasahiro Yamada#
29170818aSMasahiro Yamada# SPDX-License-Identifier:	ISC
3f219e013SMasahiro Yamada#
4f219e013SMasahiro Yamada# Author: Ulf Magnusson
5f219e013SMasahiro Yamada#   https://github.com/ulfalizer/Kconfiglib
6f219e013SMasahiro Yamada
7f219e013SMasahiro Yamada# This is Kconfiglib, a Python library for scripting, debugging, and extracting
8f219e013SMasahiro Yamada# information from Kconfig-based configuration systems. To view the
9f219e013SMasahiro Yamada# documentation, run
10f219e013SMasahiro Yamada#
11f219e013SMasahiro Yamada#  $ pydoc kconfiglib
12f219e013SMasahiro Yamada#
13f219e013SMasahiro Yamada# or, if you prefer HTML,
14f219e013SMasahiro Yamada#
15f219e013SMasahiro Yamada#  $ pydoc -w kconfiglib
16f219e013SMasahiro Yamada#
17f219e013SMasahiro Yamada# The examples/ subdirectory contains examples, to be run with e.g.
18f219e013SMasahiro Yamada#
19f219e013SMasahiro Yamada#  $ make scriptconfig SCRIPT=Kconfiglib/examples/print_tree.py
20f219e013SMasahiro Yamada#
21f219e013SMasahiro Yamada# Look in testsuite.py for the test suite.
22f219e013SMasahiro Yamada
23f219e013SMasahiro Yamada"""
24f219e013SMasahiro YamadaKconfiglib is a Python library for scripting and extracting information from
25f219e013SMasahiro YamadaKconfig-based configuration systems. Features include the following:
26f219e013SMasahiro Yamada
27f219e013SMasahiro Yamada - Symbol values and properties can be looked up and values assigned
28f219e013SMasahiro Yamada   programmatically.
29f219e013SMasahiro Yamada - .config files can be read and written.
30f219e013SMasahiro Yamada - Expressions can be evaluated in the context of a Kconfig configuration.
31f219e013SMasahiro Yamada - Relations between symbols can be quickly determined, such as finding all
32f219e013SMasahiro Yamada   symbols that reference a particular symbol.
33f219e013SMasahiro Yamada - Highly compatible with the scripts/kconfig/*conf utilities. The test suite
34f219e013SMasahiro Yamada   automatically compares outputs between Kconfiglib and the C implementation
35f219e013SMasahiro Yamada   for a large number of cases.
36f219e013SMasahiro Yamada
37f219e013SMasahiro YamadaFor the Linux kernel, scripts are run using
38f219e013SMasahiro Yamada
39*90c36d8aSUlf Magnusson $ make scriptconfig [ARCH=<arch>] SCRIPT=<path to script> [SCRIPT_ARG=<arg>]
40f219e013SMasahiro Yamada
41*90c36d8aSUlf MagnussonUsing the 'scriptconfig' target ensures that required environment variables
42*90c36d8aSUlf Magnusson(SRCARCH, ARCH, srctree, KERNELVERSION, etc.) are set up correctly.
43f219e013SMasahiro Yamada
44*90c36d8aSUlf MagnussonScripts receive the name of the Kconfig file to load in sys.argv[1]. As of
45*90c36d8aSUlf MagnussonLinux 4.1.0-rc5, this is always "Kconfig" from the kernel top-level directory.
46*90c36d8aSUlf MagnussonIf an argument is provided with SCRIPT_ARG, it appears as sys.argv[2].
47f219e013SMasahiro Yamada
48f219e013SMasahiro YamadaTo get an interactive Python prompt with Kconfiglib preloaded and a Config
49*90c36d8aSUlf Magnussonobject 'c' created, run
50f219e013SMasahiro Yamada
51*90c36d8aSUlf Magnusson $ make iscriptconfig [ARCH=<arch>]
52f219e013SMasahiro Yamada
53*90c36d8aSUlf MagnussonKconfiglib supports both Python 2 and Python 3. For (i)scriptconfig, the Python
54*90c36d8aSUlf Magnussoninterpreter to use can be passed in PYTHONCMD, which defaults to 'python'. PyPy
55*90c36d8aSUlf Magnussonworks well too, and might give a nice speedup for long-running jobs.
56f219e013SMasahiro Yamada
57*90c36d8aSUlf MagnussonThe examples/ directory contains short example scripts, which can be run with
58*90c36d8aSUlf Magnussone.g.
59f219e013SMasahiro Yamada
60f219e013SMasahiro Yamada $ make scriptconfig SCRIPT=Kconfiglib/examples/print_tree.py
61f219e013SMasahiro Yamada
62f219e013SMasahiro Yamadaor
63f219e013SMasahiro Yamada
64*90c36d8aSUlf Magnusson $ make scriptconfig SCRIPT=Kconfiglib/examples/help_grep.py SCRIPT_ARG=kernel
65f219e013SMasahiro Yamada
66*90c36d8aSUlf Magnussontestsuite.py contains the test suite. See the top of the script for how to run
67*90c36d8aSUlf Magnussonit.
68f219e013SMasahiro Yamada
69f219e013SMasahiro YamadaCredits: Written by Ulf "Ulfalizer" Magnusson
70f219e013SMasahiro Yamada
71*90c36d8aSUlf MagnussonSend bug reports, suggestions and other feedback to ulfalizer a.t Google's
72*90c36d8aSUlf Magnussonemail service. Don't wrestle with internal APIs. Tell me what you need and I
73*90c36d8aSUlf Magnussonmight add it in a safe way as a client API instead."""
74f219e013SMasahiro Yamada
75f219e013SMasahiro Yamadaimport os
76f219e013SMasahiro Yamadaimport re
77f219e013SMasahiro Yamadaimport sys
78f219e013SMasahiro Yamada
79*90c36d8aSUlf Magnusson# File layout:
80*90c36d8aSUlf Magnusson#
81*90c36d8aSUlf Magnusson# Public classes
82*90c36d8aSUlf Magnusson# Public functions
83*90c36d8aSUlf Magnusson# Internal classes
84*90c36d8aSUlf Magnusson# Internal functions
85*90c36d8aSUlf Magnusson# Internal global constants
86*90c36d8aSUlf Magnusson
87*90c36d8aSUlf Magnusson# Line length: 79 columns
88*90c36d8aSUlf Magnusson
89*90c36d8aSUlf Magnusson#
90*90c36d8aSUlf Magnusson# Public classes
91*90c36d8aSUlf Magnusson#
92*90c36d8aSUlf Magnusson
93*90c36d8aSUlf 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
104*90c36d8aSUlf 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
109*90c36d8aSUlf Magnusson        filename (default: "Kconfig"): The base Kconfig file of the
110*90c36d8aSUlf Magnusson           configuration. For the Linux kernel, you'll probably want "Kconfig"
111*90c36d8aSUlf Magnusson           from the top-level directory, as environment variables will make
112*90c36d8aSUlf Magnusson           sure the right Kconfig is included from there
113*90c36d8aSUlf Magnusson           (arch/<architecture>/Kconfig). If you are using Kconfiglib via 'make
114*90c36d8aSUlf Magnusson           scriptconfig', the filename of the base base Kconfig file will be in
115*90c36d8aSUlf Magnusson           sys.argv[1].
116f219e013SMasahiro Yamada
117*90c36d8aSUlf Magnusson        base_dir (default: None): The base directory relative to which 'source'
118*90c36d8aSUlf Magnusson           statements within Kconfig files will work. For the Linux kernel this
119*90c36d8aSUlf Magnusson           should be the top-level directory of the kernel tree. $-references
120*90c36d8aSUlf Magnusson           to existing environment variables will be expanded.
121f219e013SMasahiro Yamada
122*90c36d8aSUlf Magnusson           If None (the default), the environment variable 'srctree' will be
123*90c36d8aSUlf Magnusson           used if set, and the current directory otherwise. 'srctree' is set
124*90c36d8aSUlf Magnusson           by the Linux makefiles to the top-level kernel directory. A default
125*90c36d8aSUlf Magnusson           of "." would not work with an alternative build directory.
126f219e013SMasahiro Yamada
127*90c36d8aSUlf Magnusson        print_warnings (default: True): Set to True if warnings related to this
128*90c36d8aSUlf Magnusson           configuration should be printed to stderr. This can be changed later
129*90c36d8aSUlf Magnusson           with Config.set_print_warnings(). It is provided as a constructor
130*90c36d8aSUlf Magnusson           argument since warnings might be generated during parsing.
131f219e013SMasahiro Yamada
132*90c36d8aSUlf Magnusson        print_undef_assign (default: False): Set to True if informational
133*90c36d8aSUlf Magnusson           messages related to assignments to undefined symbols should be
134*90c36d8aSUlf 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 = {}
139*90c36d8aSUlf Magnusson        # Python 2/3 compatibility hack. This is the only one needed.
140*90c36d8aSUlf Magnusson        if sys.version_info[0] >= 3:
141*90c36d8aSUlf Magnusson            self.syms_iter = self.syms.values
142*90c36d8aSUlf Magnusson        else:
143*90c36d8aSUlf Magnusson            self.syms_iter = self.syms.itervalues
144f219e013SMasahiro Yamada
145f219e013SMasahiro Yamada        # The set of all defined symbols in the configuration in the order they
146f219e013SMasahiro Yamada        # appear in the Kconfig files. This excludes the special symbols n, m,
147f219e013SMasahiro Yamada        # and y as well as symbols that are referenced but never defined.
148f219e013SMasahiro Yamada        self.kconfig_syms = []
149f219e013SMasahiro Yamada
150f219e013SMasahiro Yamada        # The set of all named choices (yes, choices can have names), indexed
151f219e013SMasahiro Yamada        # by name (a string)
152f219e013SMasahiro Yamada        self.named_choices = {}
153f219e013SMasahiro Yamada
154*90c36d8aSUlf Magnusson        # Lists containing all choices, menus and comments in the configuration
155*90c36d8aSUlf Magnusson        self.choices = []
156*90c36d8aSUlf Magnusson        self.menus = []
157*90c36d8aSUlf Magnusson        self.comments = []
158*90c36d8aSUlf Magnusson
159*90c36d8aSUlf Magnusson        def register_special_symbol(type_, name, val):
160f219e013SMasahiro Yamada            sym = Symbol()
161f219e013SMasahiro Yamada            sym.is_special_ = True
162f219e013SMasahiro Yamada            sym.is_defined_ = True
163f219e013SMasahiro Yamada            sym.config = self
164f219e013SMasahiro Yamada            sym.name = name
165*90c36d8aSUlf Magnusson            sym.type = type_
166*90c36d8aSUlf Magnusson            sym.cached_val = val
167f219e013SMasahiro Yamada            self.syms[name] = sym
168f219e013SMasahiro Yamada            return sym
169f219e013SMasahiro Yamada
170f219e013SMasahiro Yamada        # The special symbols n, m and y, used as shorthand for "n", "m" and
171f219e013SMasahiro Yamada        # "y"
172f219e013SMasahiro Yamada        self.n = register_special_symbol(TRISTATE, "n", "n")
173f219e013SMasahiro Yamada        self.m = register_special_symbol(TRISTATE, "m", "m")
174f219e013SMasahiro Yamada        self.y = register_special_symbol(TRISTATE, "y", "y")
175f219e013SMasahiro Yamada        # DEFCONFIG_LIST uses this
176f219e013SMasahiro Yamada        register_special_symbol(STRING, "UNAME_RELEASE", os.uname()[2])
177f219e013SMasahiro Yamada
178f219e013SMasahiro Yamada        # The symbol with "option defconfig_list" set, containing a list of
179f219e013SMasahiro Yamada        # default .config files
180f219e013SMasahiro Yamada        self.defconfig_sym = None
181f219e013SMasahiro Yamada
182f219e013SMasahiro Yamada        # See Symbol.get_(src)arch()
183f219e013SMasahiro Yamada        self.arch = os.environ.get("ARCH")
184f219e013SMasahiro Yamada        self.srcarch = os.environ.get("SRCARCH")
185f219e013SMasahiro Yamada
186f219e013SMasahiro Yamada        # See Config.__init__(). We need this for get_defconfig_filename().
187f219e013SMasahiro Yamada        self.srctree = os.environ.get("srctree")
188f219e013SMasahiro Yamada        if self.srctree is None:
189f219e013SMasahiro Yamada            self.srctree = "."
190f219e013SMasahiro Yamada
191f219e013SMasahiro Yamada        self.filename = filename
192*90c36d8aSUlf Magnusson        if base_dir is None:
193*90c36d8aSUlf Magnusson            self.base_dir = self.srctree
194*90c36d8aSUlf Magnusson        else:
195*90c36d8aSUlf Magnusson            self.base_dir = os.path.expandvars(base_dir)
196f219e013SMasahiro Yamada
197f219e013SMasahiro Yamada        # The 'mainmenu' text
198f219e013SMasahiro Yamada        self.mainmenu_text = None
199f219e013SMasahiro Yamada
200f219e013SMasahiro Yamada        # The filename of the most recently loaded .config file
201f219e013SMasahiro Yamada        self.config_filename = None
202f219e013SMasahiro Yamada        # The textual header of the most recently loaded .config, uncommented
203f219e013SMasahiro Yamada        self.config_header = None
204f219e013SMasahiro Yamada
205f219e013SMasahiro Yamada        self.print_warnings = print_warnings
206f219e013SMasahiro Yamada        self.print_undef_assign = print_undef_assign
207f219e013SMasahiro Yamada
208f219e013SMasahiro Yamada        # For parsing routines that stop when finding a line belonging to a
209f219e013SMasahiro Yamada        # different construct, these holds that line and the tokenized version
210f219e013SMasahiro Yamada        # of that line. The purpose is to avoid having to re-tokenize the line,
211f219e013SMasahiro Yamada        # which is inefficient and causes problems when recording references to
212f219e013SMasahiro Yamada        # symbols.
213f219e013SMasahiro Yamada        self.end_line = None
214f219e013SMasahiro Yamada        self.end_line_tokens = None
215f219e013SMasahiro Yamada
216f219e013SMasahiro Yamada        # See the comment in _parse_expr().
217*90c36d8aSUlf Magnusson        self._cur_item = None
218*90c36d8aSUlf Magnusson        self._line = None
219*90c36d8aSUlf Magnusson        self._filename = None
220*90c36d8aSUlf Magnusson        self._linenr = None
221*90c36d8aSUlf Magnusson        self._transform_m = None
222f219e013SMasahiro Yamada
223f219e013SMasahiro Yamada        # Parse the Kconfig files
224f219e013SMasahiro Yamada        self.top_block = self._parse_file(filename, None, None, None)
225f219e013SMasahiro Yamada
226f219e013SMasahiro Yamada        # Build Symbol.dep for all symbols
227f219e013SMasahiro Yamada        self._build_dep()
228f219e013SMasahiro Yamada
229f219e013SMasahiro Yamada    def get_arch(self):
230f219e013SMasahiro Yamada        """Returns the value the environment variable ARCH had at the time the
231f219e013SMasahiro Yamada        Config instance was created, or None if ARCH was not set. For the
232f219e013SMasahiro Yamada        kernel, this corresponds to the architecture being built for, with
233f219e013SMasahiro Yamada        values such as "i386" or "mips"."""
234f219e013SMasahiro Yamada        return self.arch
235f219e013SMasahiro Yamada
236f219e013SMasahiro Yamada    def get_srcarch(self):
237f219e013SMasahiro Yamada        """Returns the value the environment variable SRCARCH had at the time
238f219e013SMasahiro Yamada        the Config instance was created, or None if SRCARCH was not set. For
239*90c36d8aSUlf Magnusson        the kernel, this corresponds to the particular arch/ subdirectory
240*90c36d8aSUlf Magnusson        containing architecture-specific code."""
241f219e013SMasahiro Yamada        return self.srcarch
242f219e013SMasahiro Yamada
243f219e013SMasahiro Yamada    def get_srctree(self):
244f219e013SMasahiro Yamada        """Returns the value the environment variable srctree had at the time
245f219e013SMasahiro Yamada        the Config instance was created, or None if srctree was not defined.
246f219e013SMasahiro Yamada        This variable points to the source directory and is used when building
247f219e013SMasahiro Yamada        in a separate directory."""
248f219e013SMasahiro Yamada        return self.srctree
249f219e013SMasahiro Yamada
250*90c36d8aSUlf Magnusson    def get_base_dir(self):
251*90c36d8aSUlf Magnusson        """Returns the base directory relative to which 'source' statements
252*90c36d8aSUlf Magnusson        will work, passed as an argument to Config.__init__()."""
253*90c36d8aSUlf Magnusson        return self.base_dir
254*90c36d8aSUlf Magnusson
255*90c36d8aSUlf Magnusson    def get_kconfig_filename(self):
256*90c36d8aSUlf Magnusson        """Returns the name of the (base) kconfig file this configuration was
257*90c36d8aSUlf Magnusson        loaded from."""
258*90c36d8aSUlf Magnusson        return self.filename
259*90c36d8aSUlf Magnusson
260f219e013SMasahiro Yamada    def get_config_filename(self):
261*90c36d8aSUlf Magnusson        """Returns the filename of the most recently loaded configuration file,
262*90c36d8aSUlf Magnusson        or None if no configuration has been loaded."""
263f219e013SMasahiro Yamada        return self.config_filename
264f219e013SMasahiro Yamada
265*90c36d8aSUlf Magnusson    def get_config_header(self):
266*90c36d8aSUlf Magnusson        """Returns the (uncommented) textual header of the .config file most
267*90c36d8aSUlf Magnusson        recently loaded with load_config(). Returns None if no .config file has
268*90c36d8aSUlf Magnusson        been loaded or if the most recently loaded .config file has no header.
269*90c36d8aSUlf Magnusson        The header consists of all lines up to but not including the first line
270*90c36d8aSUlf Magnusson        that either
271*90c36d8aSUlf Magnusson
272*90c36d8aSUlf Magnusson        1. Does not start with "#"
273*90c36d8aSUlf Magnusson        2. Has the form "# CONFIG_FOO is not set."
274*90c36d8aSUlf Magnusson        """
275*90c36d8aSUlf Magnusson        return self.config_header
276*90c36d8aSUlf Magnusson
277f219e013SMasahiro Yamada    def get_mainmenu_text(self):
278f219e013SMasahiro Yamada        """Returns the text of the 'mainmenu' statement (with $-references to
279f219e013SMasahiro Yamada        symbols replaced by symbol values), or None if the configuration has no
280f219e013SMasahiro Yamada        'mainmenu' statement."""
281f219e013SMasahiro Yamada        return None if self.mainmenu_text is None else \
282f219e013SMasahiro Yamada          self._expand_sym_refs(self.mainmenu_text)
283f219e013SMasahiro Yamada
284f219e013SMasahiro Yamada    def get_defconfig_filename(self):
285f219e013SMasahiro Yamada        """Returns the name of the defconfig file, which is the first existing
286f219e013SMasahiro Yamada        file in the list given in a symbol having 'option defconfig_list' set.
287f219e013SMasahiro Yamada        $-references to symbols will be expanded ("$FOO bar" -> "foo bar" if
288f219e013SMasahiro Yamada        FOO has the value "foo"). Returns None in case of no defconfig file.
289f219e013SMasahiro Yamada        Setting 'option defconfig_list' on multiple symbols currently results
290f219e013SMasahiro Yamada        in undefined behavior.
291f219e013SMasahiro Yamada
292f219e013SMasahiro Yamada        If the environment variable 'srctree' was set when the Config was
293f219e013SMasahiro Yamada        created, get_defconfig_filename() will first look relative to that
294f219e013SMasahiro Yamada        directory before looking in the current directory; see
2959d01b787SMasahiro Yamada        Config.__init__().
2969d01b787SMasahiro Yamada
297*90c36d8aSUlf Magnusson        WARNING: A wart here is that scripts/kconfig/Makefile sometimes uses
298*90c36d8aSUlf Magnusson        the --defconfig=<defconfig> option when calling the C implementation of
299*90c36d8aSUlf Magnusson        e.g. 'make defconfig'. This option overrides the 'option
300*90c36d8aSUlf Magnusson        defconfig_list' symbol, meaning the result from
301*90c36d8aSUlf Magnusson        get_defconfig_filename() might not match what 'make defconfig' would
302*90c36d8aSUlf Magnusson        use. That probably ought to be worked around somehow, so that this
303*90c36d8aSUlf Magnusson        function always gives the "expected" result."""
304f219e013SMasahiro Yamada        if self.defconfig_sym is None:
305f219e013SMasahiro Yamada            return None
306*90c36d8aSUlf Magnusson        for filename, cond_expr in self.defconfig_sym.def_exprs:
307f219e013SMasahiro Yamada            if self._eval_expr(cond_expr) == "y":
308f219e013SMasahiro Yamada                filename = self._expand_sym_refs(filename)
309f219e013SMasahiro Yamada                # We first look in $srctree. os.path.join() won't work here as
310f219e013SMasahiro Yamada                # an absolute path in filename would override $srctree.
311*90c36d8aSUlf Magnusson                srctree_filename = os.path.normpath(self.srctree + "/" +
312*90c36d8aSUlf Magnusson                                                    filename)
313f219e013SMasahiro Yamada                if os.path.exists(srctree_filename):
314f219e013SMasahiro Yamada                    return srctree_filename
315f219e013SMasahiro Yamada                if os.path.exists(filename):
316f219e013SMasahiro Yamada                    return filename
317f219e013SMasahiro Yamada        return None
318f219e013SMasahiro Yamada
319f219e013SMasahiro Yamada    def get_symbol(self, name):
320f219e013SMasahiro Yamada        """Returns the symbol with name 'name', or None if no such symbol
321f219e013SMasahiro Yamada        appears in the configuration. An alternative shorthand is conf[name],
322f219e013SMasahiro Yamada        where conf is a Config instance, though that will instead raise
323f219e013SMasahiro Yamada        KeyError if the symbol does not exist."""
324f219e013SMasahiro Yamada        return self.syms.get(name)
325f219e013SMasahiro Yamada
326*90c36d8aSUlf Magnusson    def __getitem__(self, name):
327*90c36d8aSUlf Magnusson        """Returns the symbol with name 'name'. Raises KeyError if the symbol
328*90c36d8aSUlf Magnusson        does not appear in the configuration."""
329*90c36d8aSUlf Magnusson        return self.syms[name]
330f219e013SMasahiro Yamada
331f219e013SMasahiro Yamada    def get_symbols(self, all_symbols=True):
332f219e013SMasahiro Yamada        """Returns a list of symbols from the configuration. An alternative for
333f219e013SMasahiro Yamada        iterating over all defined symbols (in the order of definition) is
334f219e013SMasahiro Yamada
335f219e013SMasahiro Yamada        for sym in config:
336f219e013SMasahiro Yamada            ...
337f219e013SMasahiro Yamada
338f219e013SMasahiro Yamada        which relies on Config implementing __iter__() and is equivalent to
339f219e013SMasahiro Yamada
340f219e013SMasahiro Yamada        for sym in config.get_symbols(False):
341f219e013SMasahiro Yamada            ...
342f219e013SMasahiro Yamada
343*90c36d8aSUlf Magnusson        all_symbols (default: True): If True, all symbols -- including special
344*90c36d8aSUlf Magnusson           and undefined symbols -- will be included in the result, in an
345*90c36d8aSUlf Magnusson           undefined order. If False, only symbols actually defined and not
346*90c36d8aSUlf Magnusson           merely referred to in the configuration will be included in the
347*90c36d8aSUlf Magnusson           result, and will appear in the order that they are defined within
348*90c36d8aSUlf Magnusson           the Kconfig configuration files."""
349*90c36d8aSUlf Magnusson        return list(self.syms.values()) if all_symbols else self.kconfig_syms
350*90c36d8aSUlf Magnusson
351*90c36d8aSUlf Magnusson    def __iter__(self):
352*90c36d8aSUlf Magnusson        """Convenience function for iterating over the set of all defined
353*90c36d8aSUlf Magnusson        symbols in the configuration, used like
354*90c36d8aSUlf Magnusson
355*90c36d8aSUlf Magnusson        for sym in conf:
356*90c36d8aSUlf Magnusson            ...
357*90c36d8aSUlf Magnusson
358*90c36d8aSUlf Magnusson        The iteration happens in the order of definition within the Kconfig
359*90c36d8aSUlf Magnusson        configuration files. Symbols only referred to but not defined will not
360*90c36d8aSUlf Magnusson        be included, nor will the special symbols n, m, and y. If you want to
361*90c36d8aSUlf Magnusson        include such symbols as well, see config.get_symbols()."""
362*90c36d8aSUlf Magnusson        return iter(self.kconfig_syms)
363f219e013SMasahiro Yamada
364f219e013SMasahiro Yamada    def get_choices(self):
365f219e013SMasahiro Yamada        """Returns a list containing all choice statements in the
366f219e013SMasahiro Yamada        configuration, in the order they appear in the Kconfig files."""
367f219e013SMasahiro Yamada        return self.choices
368f219e013SMasahiro Yamada
369f219e013SMasahiro Yamada    def get_menus(self):
370f219e013SMasahiro Yamada        """Returns a list containing all menus in the configuration, in the
371f219e013SMasahiro Yamada        order they appear in the Kconfig files."""
372f219e013SMasahiro Yamada        return self.menus
373f219e013SMasahiro Yamada
374f219e013SMasahiro Yamada    def get_comments(self):
375f219e013SMasahiro Yamada        """Returns a list containing all comments in the configuration, in the
376f219e013SMasahiro Yamada        order they appear in the Kconfig files."""
377f219e013SMasahiro Yamada        return self.comments
378f219e013SMasahiro Yamada
379*90c36d8aSUlf Magnusson    def get_top_level_items(self):
380*90c36d8aSUlf Magnusson        """Returns a list containing the items (symbols, menus, choices, and
381*90c36d8aSUlf Magnusson        comments) at the top level of the configuration -- that is, all items
382*90c36d8aSUlf Magnusson        that do not appear within a menu or choice. The items appear in the
383*90c36d8aSUlf Magnusson        same order as within the configuration."""
384*90c36d8aSUlf Magnusson        return self.top_block
385*90c36d8aSUlf Magnusson
386*90c36d8aSUlf Magnusson    def load_config(self, filename, replace=True):
387*90c36d8aSUlf Magnusson        """Loads symbol values from a file in the familiar .config format.
388*90c36d8aSUlf Magnusson        Equivalent to calling Symbol.set_user_value() to set each of the
389*90c36d8aSUlf Magnusson        values.
390*90c36d8aSUlf Magnusson
391*90c36d8aSUlf Magnusson        "# CONFIG_FOO is not set" within a .config file is treated specially
392*90c36d8aSUlf Magnusson        and sets the user value of FOO to 'n'. The C implementation works the
393*90c36d8aSUlf Magnusson        same way.
394*90c36d8aSUlf Magnusson
395*90c36d8aSUlf Magnusson        filename: The .config file to load. $-references to existing
396*90c36d8aSUlf Magnusson          environment variables will be expanded. For scripts to work even when
397*90c36d8aSUlf Magnusson          an alternative build directory is used with the Linux kernel, you
398*90c36d8aSUlf Magnusson          need to refer to the top-level kernel directory with "$srctree".
399*90c36d8aSUlf Magnusson
400*90c36d8aSUlf Magnusson        replace (default: True): True if the configuration should replace the
401*90c36d8aSUlf Magnusson           old configuration; False if it should add to it."""
402*90c36d8aSUlf Magnusson
403*90c36d8aSUlf Magnusson        # Put this first so that a missing file doesn't screw up our state
404*90c36d8aSUlf Magnusson        filename = os.path.expandvars(filename)
405*90c36d8aSUlf Magnusson        line_feeder = _FileFeed(filename)
406*90c36d8aSUlf Magnusson
407*90c36d8aSUlf Magnusson        self.config_filename = filename
408*90c36d8aSUlf Magnusson
409*90c36d8aSUlf Magnusson        #
410*90c36d8aSUlf Magnusson        # Read header
411*90c36d8aSUlf Magnusson        #
412*90c36d8aSUlf Magnusson
413*90c36d8aSUlf Magnusson        def is_header_line(line):
414*90c36d8aSUlf Magnusson            return line is not None and line.startswith("#") and \
415*90c36d8aSUlf Magnusson                   not _unset_re_match(line)
416*90c36d8aSUlf Magnusson
417*90c36d8aSUlf Magnusson        self.config_header = None
418*90c36d8aSUlf Magnusson
419*90c36d8aSUlf Magnusson        line = line_feeder.peek_next()
420*90c36d8aSUlf Magnusson        if is_header_line(line):
421*90c36d8aSUlf Magnusson            self.config_header = ""
422*90c36d8aSUlf Magnusson            while is_header_line(line_feeder.peek_next()):
423*90c36d8aSUlf Magnusson                self.config_header += line_feeder.get_next()[1:]
424*90c36d8aSUlf Magnusson            # Remove trailing newline
425*90c36d8aSUlf Magnusson            if self.config_header.endswith("\n"):
426*90c36d8aSUlf Magnusson                self.config_header = self.config_header[:-1]
427*90c36d8aSUlf Magnusson
428*90c36d8aSUlf Magnusson        #
429*90c36d8aSUlf Magnusson        # Read assignments. Hotspot for some workloads.
430*90c36d8aSUlf Magnusson        #
431*90c36d8aSUlf Magnusson
432*90c36d8aSUlf Magnusson        def warn_override(filename, linenr, name, old_user_val, new_user_val):
433*90c36d8aSUlf Magnusson            self._warn('overriding the value of {0}. '
434*90c36d8aSUlf Magnusson                       'Old value: "{1}", new value: "{2}".'
435*90c36d8aSUlf Magnusson                       .format(name, old_user_val, new_user_val),
436*90c36d8aSUlf Magnusson                       filename, linenr)
437*90c36d8aSUlf Magnusson
438*90c36d8aSUlf Magnusson        # Invalidate everything to keep things simple. It might be possible to
439*90c36d8aSUlf Magnusson        # improve performance for the case where multiple configurations are
440*90c36d8aSUlf Magnusson        # loaded by only invalidating a symbol (and its dependent symbols) if
441*90c36d8aSUlf Magnusson        # the new user value differs from the old. One complication would be
442*90c36d8aSUlf Magnusson        # that symbols not mentioned in the .config must lose their user value
443*90c36d8aSUlf Magnusson        # when replace = True, which is the usual case.
444*90c36d8aSUlf Magnusson        if replace:
445*90c36d8aSUlf Magnusson            self.unset_user_values()
446*90c36d8aSUlf Magnusson        else:
447*90c36d8aSUlf Magnusson            self._invalidate_all()
448*90c36d8aSUlf Magnusson
449*90c36d8aSUlf Magnusson        while 1:
450*90c36d8aSUlf Magnusson            line = line_feeder.get_next()
451*90c36d8aSUlf Magnusson            if line is None:
452*90c36d8aSUlf Magnusson                return
453*90c36d8aSUlf Magnusson
454*90c36d8aSUlf Magnusson            line = line.rstrip()
455*90c36d8aSUlf Magnusson
456*90c36d8aSUlf Magnusson            set_match = _set_re_match(line)
457*90c36d8aSUlf Magnusson            if set_match:
458*90c36d8aSUlf Magnusson                name, val = set_match.groups()
459*90c36d8aSUlf Magnusson
460*90c36d8aSUlf Magnusson                if val.startswith('"'):
461*90c36d8aSUlf Magnusson                    if len(val) < 2 or val[-1] != '"':
462*90c36d8aSUlf Magnusson                        _parse_error(line, "malformed string literal",
463*90c36d8aSUlf Magnusson                                     line_feeder.filename, line_feeder.linenr)
464*90c36d8aSUlf Magnusson                    # Strip quotes and remove escapings. The unescaping
465*90c36d8aSUlf Magnusson                    # procedure should be safe since " can only appear as \"
466*90c36d8aSUlf Magnusson                    # inside the string.
467*90c36d8aSUlf Magnusson                    val = val[1:-1].replace('\\"', '"').replace("\\\\", "\\")
468*90c36d8aSUlf Magnusson
469*90c36d8aSUlf Magnusson                if name in self.syms:
470*90c36d8aSUlf Magnusson                    sym = self.syms[name]
471*90c36d8aSUlf Magnusson                    if sym.user_val is not None:
472*90c36d8aSUlf Magnusson                        warn_override(line_feeder.filename, line_feeder.linenr,
473*90c36d8aSUlf Magnusson                                      name, sym.user_val, val)
474*90c36d8aSUlf Magnusson
475*90c36d8aSUlf Magnusson                    if sym.is_choice_sym:
476*90c36d8aSUlf Magnusson                        user_mode = sym.parent.user_mode
477*90c36d8aSUlf Magnusson                        if user_mode is not None and user_mode != val:
478*90c36d8aSUlf Magnusson                            self._warn("assignment to {0} changes mode of "
479*90c36d8aSUlf Magnusson                                       'containing choice from "{1}" to "{2}".'
480*90c36d8aSUlf Magnusson                                       .format(name, val, user_mode),
481*90c36d8aSUlf Magnusson                                       line_feeder.filename,
482*90c36d8aSUlf Magnusson                                       line_feeder.linenr)
483*90c36d8aSUlf Magnusson
484*90c36d8aSUlf Magnusson                    sym._set_user_value_no_invalidate(val, True)
485*90c36d8aSUlf Magnusson                else:
486*90c36d8aSUlf Magnusson                    if self.print_undef_assign:
487*90c36d8aSUlf Magnusson                        _stderr_msg('note: attempt to assign the value "{0}" '
488*90c36d8aSUlf Magnusson                                    "to the undefined symbol {1}."
489*90c36d8aSUlf Magnusson                                    .format(val, name),
490*90c36d8aSUlf Magnusson                                    line_feeder.filename, line_feeder.linenr)
491*90c36d8aSUlf Magnusson            else:
492*90c36d8aSUlf Magnusson                unset_match = _unset_re_match(line)
493*90c36d8aSUlf Magnusson                if unset_match:
494*90c36d8aSUlf Magnusson                    name = unset_match.group(1)
495*90c36d8aSUlf Magnusson                    if name in self.syms:
496*90c36d8aSUlf Magnusson                        sym = self.syms[name]
497*90c36d8aSUlf Magnusson                        if sym.user_val is not None:
498*90c36d8aSUlf Magnusson                            warn_override(line_feeder.filename,
499*90c36d8aSUlf Magnusson                                          line_feeder.linenr,
500*90c36d8aSUlf Magnusson                                          name, sym.user_val, "n")
501*90c36d8aSUlf Magnusson
502*90c36d8aSUlf Magnusson                        sym._set_user_value_no_invalidate("n", True)
503*90c36d8aSUlf Magnusson
504*90c36d8aSUlf Magnusson    def write_config(self, filename, header=None):
505*90c36d8aSUlf Magnusson        """Writes out symbol values in the familiar .config format.
506*90c36d8aSUlf Magnusson
507*90c36d8aSUlf Magnusson        Kconfiglib makes sure the format matches what the C implementation
508*90c36d8aSUlf Magnusson        would generate, down to whitespace. This eases testing.
509*90c36d8aSUlf Magnusson
510*90c36d8aSUlf Magnusson        filename: The filename under which to save the configuration.
511*90c36d8aSUlf Magnusson
512*90c36d8aSUlf Magnusson        header (default: None): A textual header that will appear at the
513*90c36d8aSUlf Magnusson           beginning of the file, with each line commented out automatically.
514*90c36d8aSUlf Magnusson           None means no header."""
515*90c36d8aSUlf Magnusson
516*90c36d8aSUlf Magnusson        for sym in self.syms_iter():
517*90c36d8aSUlf Magnusson            sym.already_written = False
518*90c36d8aSUlf Magnusson
519*90c36d8aSUlf Magnusson        with open(filename, "w") as f:
520*90c36d8aSUlf Magnusson            # Write header
521*90c36d8aSUlf Magnusson            if header is not None:
522*90c36d8aSUlf Magnusson                f.write(_comment(header))
523*90c36d8aSUlf Magnusson                f.write("\n")
524*90c36d8aSUlf Magnusson
525*90c36d8aSUlf Magnusson            # Build and write configuration
526*90c36d8aSUlf Magnusson            conf_strings = []
527*90c36d8aSUlf Magnusson            _make_block_conf(self.top_block, conf_strings.append)
528*90c36d8aSUlf Magnusson            f.write("\n".join(conf_strings))
529*90c36d8aSUlf Magnusson            f.write("\n")
530*90c36d8aSUlf Magnusson
531f219e013SMasahiro Yamada    def eval(self, s):
532f219e013SMasahiro Yamada        """Returns the value of the expression 's' -- where 's' is represented
533f219e013SMasahiro Yamada        as a string -- in the context of the configuration. Raises
534f219e013SMasahiro Yamada        Kconfig_Syntax_Error if syntax errors are detected in 's'.
535f219e013SMasahiro Yamada
536f219e013SMasahiro Yamada        For example, if FOO and BAR are tristate symbols at least one of which
537f219e013SMasahiro Yamada        has the value "y", then config.eval("y && (FOO || BAR)") => "y"
538f219e013SMasahiro Yamada
5399d01b787SMasahiro Yamada        This function always yields a tristate value. To get the value of
540f219e013SMasahiro Yamada        non-bool, non-tristate symbols, use Symbol.get_value().
541f219e013SMasahiro Yamada
542f219e013SMasahiro Yamada        The result of this function is consistent with how evaluation works for
543f219e013SMasahiro Yamada        conditional expressions in the configuration as well as in the C
544f219e013SMasahiro Yamada        implementation. "m" and m are rewritten as '"m" && MODULES' and 'm &&
545f219e013SMasahiro Yamada        MODULES', respectively, and a result of "m" will get promoted to "y" if
546*90c36d8aSUlf Magnusson        we're running without modules.
547*90c36d8aSUlf Magnusson
548*90c36d8aSUlf Magnusson        Syntax checking is somewhat lax, partly to be compatible with lax
549*90c36d8aSUlf Magnusson        parsing in the C implementation."""
550f219e013SMasahiro Yamada        return self._eval_expr(self._parse_expr(self._tokenize(s, True), # Feed
551*90c36d8aSUlf Magnusson                                                None, # Current symbol/choice
552f219e013SMasahiro Yamada                                                s))   # line
553f219e013SMasahiro Yamada
554*90c36d8aSUlf Magnusson    def unset_user_values(self):
555*90c36d8aSUlf Magnusson        """Resets the values of all symbols, as if Config.load_config() or
556*90c36d8aSUlf Magnusson        Symbol.set_user_value() had never been called."""
557*90c36d8aSUlf Magnusson        for sym in self.syms_iter():
558*90c36d8aSUlf Magnusson            sym._unset_user_value_no_recursive_invalidate()
559f219e013SMasahiro Yamada
560f219e013SMasahiro Yamada    def set_print_warnings(self, print_warnings):
561f219e013SMasahiro Yamada        """Determines whether warnings related to this configuration (for
562f219e013SMasahiro Yamada        things like attempting to assign illegal values to symbols with
563f219e013SMasahiro Yamada        Symbol.set_user_value()) should be printed to stderr.
564f219e013SMasahiro Yamada
565*90c36d8aSUlf Magnusson        print_warnings: True if warnings should be printed."""
566f219e013SMasahiro Yamada        self.print_warnings = print_warnings
567f219e013SMasahiro Yamada
568f219e013SMasahiro Yamada    def set_print_undef_assign(self, print_undef_assign):
569f219e013SMasahiro Yamada        """Determines whether informational messages related to assignments to
570f219e013SMasahiro Yamada        undefined symbols should be printed to stderr for this configuration.
571f219e013SMasahiro Yamada
572*90c36d8aSUlf Magnusson        print_undef_assign: If True, such messages will be printed."""
573f219e013SMasahiro Yamada        self.print_undef_assign = print_undef_assign
574f219e013SMasahiro Yamada
575f219e013SMasahiro Yamada    def __str__(self):
576f219e013SMasahiro Yamada        """Returns a string containing various information about the Config."""
577*90c36d8aSUlf Magnusson        return _lines("Configuration",
578*90c36d8aSUlf Magnusson                      "File                                   : " +
579*90c36d8aSUlf Magnusson                        self.filename,
580*90c36d8aSUlf Magnusson                      "Base directory                         : " +
581*90c36d8aSUlf Magnusson                        self.base_dir,
582f219e013SMasahiro Yamada                      "Value of $ARCH at creation time        : " +
583f219e013SMasahiro Yamada                        ("(not set)" if self.arch is None else self.arch),
584f219e013SMasahiro Yamada                      "Value of $SRCARCH at creation time     : " +
585*90c36d8aSUlf Magnusson                        ("(not set)" if self.srcarch is None else
586*90c36d8aSUlf Magnusson                                        self.srcarch),
587f219e013SMasahiro Yamada                      "Source tree (derived from $srctree;",
588*90c36d8aSUlf Magnusson                      "defaults to '.' if $srctree isn't set) : " +
589*90c36d8aSUlf Magnusson                        self.srctree,
590f219e013SMasahiro Yamada                      "Most recently loaded .config           : " +
591*90c36d8aSUlf Magnusson                        ("(no .config loaded)"
592*90c36d8aSUlf Magnusson                          if self.config_filename is None else
593f219e013SMasahiro Yamada                             self.config_filename),
594f219e013SMasahiro Yamada                      "Print warnings                         : " +
595*90c36d8aSUlf Magnusson                        BOOL_STR[self.print_warnings],
596f219e013SMasahiro Yamada                      "Print assignments to undefined symbols : " +
597*90c36d8aSUlf Magnusson                        BOOL_STR[self.print_undef_assign])
598f219e013SMasahiro Yamada
599f219e013SMasahiro Yamada    #
600f219e013SMasahiro Yamada    # Private methods
601f219e013SMasahiro Yamada    #
602f219e013SMasahiro Yamada
603f219e013SMasahiro Yamada    #
604*90c36d8aSUlf Magnusson    # Kconfig parsing
605f219e013SMasahiro Yamada    #
606f219e013SMasahiro Yamada
607f219e013SMasahiro Yamada    def _parse_file(self, filename, parent, deps, visible_if_deps, res=None):
608*90c36d8aSUlf Magnusson        """Parses the Kconfig file 'filename'. Returns a list with the Items in
609*90c36d8aSUlf Magnusson        the file. See _parse_block() for the meaning of the parameters."""
610*90c36d8aSUlf Magnusson        return self._parse_block(_FileFeed(filename), None, parent, deps,
611*90c36d8aSUlf Magnusson                                 visible_if_deps, res)
612f219e013SMasahiro Yamada
613f219e013SMasahiro Yamada    def _parse_block(self, line_feeder, end_marker, parent, deps,
614*90c36d8aSUlf Magnusson                     visible_if_deps, res=None):
615f219e013SMasahiro Yamada        """Parses a block, which is the contents of either a file or an if,
616*90c36d8aSUlf Magnusson        menu, or choice statement. Returns a list with the Items in the block.
617f219e013SMasahiro Yamada
618*90c36d8aSUlf Magnusson        line_feeder: A _FileFeed instance feeding lines from a file. The
619*90c36d8aSUlf Magnusson          Kconfig language is line-based in practice.
620f219e013SMasahiro Yamada
621*90c36d8aSUlf Magnusson        end_marker: The token that ends the block, e.g. T_ENDIF ("endif") for
622*90c36d8aSUlf Magnusson           ifs. None for files.
623*90c36d8aSUlf Magnusson
624*90c36d8aSUlf Magnusson        parent: The enclosing menu or choice, or None if we're at the top
625f219e013SMasahiro Yamada           level.
626f219e013SMasahiro Yamada
627*90c36d8aSUlf Magnusson        deps: Dependencies from enclosing menus, choices and ifs.
628f219e013SMasahiro Yamada
629*90c36d8aSUlf Magnusson        visible_if_deps (default: None): 'visible if' dependencies from
630f219e013SMasahiro Yamada           enclosing menus.
631f219e013SMasahiro Yamada
632*90c36d8aSUlf Magnusson        res (default: None): The list to add items to. If None, a new list is
633*90c36d8aSUlf Magnusson           created to hold the items."""
634f219e013SMasahiro Yamada
635*90c36d8aSUlf Magnusson        block = [] if res is None else res
636f219e013SMasahiro Yamada
637f219e013SMasahiro Yamada        while 1:
638f219e013SMasahiro Yamada            # Do we already have a tokenized line that we determined wasn't
639f219e013SMasahiro Yamada            # part of whatever we were parsing earlier? See comment in
640f219e013SMasahiro Yamada            # Config.__init__().
641f219e013SMasahiro Yamada            if self.end_line is not None:
642f219e013SMasahiro Yamada                line = self.end_line
643*90c36d8aSUlf Magnusson                tokens = self.end_line_tokens
644*90c36d8aSUlf Magnusson                tokens.unget_all()
645f219e013SMasahiro Yamada
646f219e013SMasahiro Yamada                self.end_line = None
647f219e013SMasahiro Yamada                self.end_line_tokens = None
648f219e013SMasahiro Yamada            else:
649f219e013SMasahiro Yamada                line = line_feeder.get_next()
650f219e013SMasahiro Yamada                if line is None:
651f219e013SMasahiro Yamada                    if end_marker is not None:
652*90c36d8aSUlf Magnusson                        raise Kconfig_Syntax_Error("Unexpected end of file {0}"
653*90c36d8aSUlf Magnusson                                                 .format(line_feeder.filename))
654f219e013SMasahiro Yamada                    return block
655f219e013SMasahiro Yamada
656*90c36d8aSUlf Magnusson                tokens = self._tokenize(line, False, line_feeder.filename,
657*90c36d8aSUlf Magnusson                                        line_feeder.linenr)
658f219e013SMasahiro Yamada
659f219e013SMasahiro Yamada            t0 = tokens.get_next()
660*90c36d8aSUlf Magnusson            if t0 is None:
661*90c36d8aSUlf Magnusson                continue
662f219e013SMasahiro Yamada
663*90c36d8aSUlf Magnusson            # Cases are ordered roughly by frequency, which speeds things up a
664*90c36d8aSUlf Magnusson            # bit
665f219e013SMasahiro Yamada
666f219e013SMasahiro Yamada            if t0 == T_CONFIG or t0 == T_MENUCONFIG:
667f219e013SMasahiro Yamada                # The tokenizer will automatically allocate a new Symbol object
668f219e013SMasahiro Yamada                # for any new names it encounters, so we don't need to worry
669f219e013SMasahiro Yamada                # about that here.
670f219e013SMasahiro Yamada                sym = tokens.get_next()
671f219e013SMasahiro Yamada
672f219e013SMasahiro Yamada                # Symbols defined in multiple places get the parent of their
673*90c36d8aSUlf Magnusson                # first definition. However, for symbols whose parents are
674*90c36d8aSUlf Magnusson                # choice statements, the choice statement takes precedence.
675f219e013SMasahiro Yamada                if not sym.is_defined_ or isinstance(parent, Choice):
676f219e013SMasahiro Yamada                    sym.parent = parent
677f219e013SMasahiro Yamada
678f219e013SMasahiro Yamada                sym.is_defined_ = True
679f219e013SMasahiro Yamada
680f219e013SMasahiro Yamada                self.kconfig_syms.append(sym)
681*90c36d8aSUlf Magnusson                block.append(sym)
682f219e013SMasahiro Yamada
683f219e013SMasahiro Yamada                self._parse_properties(line_feeder, sym, deps, visible_if_deps)
684f219e013SMasahiro Yamada
685*90c36d8aSUlf Magnusson            elif t0 == T_SOURCE:
686*90c36d8aSUlf Magnusson                kconfig_file = tokens.get_next()
687*90c36d8aSUlf Magnusson                exp_kconfig_file = self._expand_sym_refs(kconfig_file)
688*90c36d8aSUlf Magnusson                f = os.path.join(self.base_dir, exp_kconfig_file)
689*90c36d8aSUlf Magnusson                if not os.path.exists(f):
690*90c36d8aSUlf Magnusson                    raise IOError('{0}:{1}: sourced file "{2}" (expands to '
691*90c36d8aSUlf Magnusson                                  '"{3}") not found. Perhaps base_dir '
692*90c36d8aSUlf Magnusson                                  '(argument to Config.__init__(), currently '
693*90c36d8aSUlf Magnusson                                  '"{4}") is set to the wrong value.'
694*90c36d8aSUlf Magnusson                                  .format(line_feeder.filename,
695*90c36d8aSUlf Magnusson                                          line_feeder.linenr,
696*90c36d8aSUlf Magnusson                                          kconfig_file, exp_kconfig_file,
697*90c36d8aSUlf Magnusson                                          self.base_dir))
698*90c36d8aSUlf Magnusson                # Add items to the same block
699*90c36d8aSUlf Magnusson                self._parse_file(f, parent, deps, visible_if_deps, block)
700f219e013SMasahiro Yamada
701*90c36d8aSUlf Magnusson            elif t0 == end_marker:
702*90c36d8aSUlf Magnusson                # We have reached the end of the block
703*90c36d8aSUlf Magnusson                return block
704f219e013SMasahiro Yamada
705f219e013SMasahiro Yamada            elif t0 == T_IF:
706f219e013SMasahiro Yamada                # If statements are treated as syntactic sugar for adding
707f219e013SMasahiro Yamada                # dependencies to enclosed items and do not have an explicit
708f219e013SMasahiro Yamada                # object representation.
709f219e013SMasahiro Yamada
710*90c36d8aSUlf Magnusson                dep_expr = self._parse_expr(tokens, None, line,
711*90c36d8aSUlf Magnusson                                            line_feeder.filename,
712*90c36d8aSUlf Magnusson                                            line_feeder.linenr)
713*90c36d8aSUlf Magnusson                # Add items to the same block
714*90c36d8aSUlf Magnusson                self._parse_block(line_feeder, T_ENDIF, parent,
715f219e013SMasahiro Yamada                                  _make_and(dep_expr, deps),
716*90c36d8aSUlf Magnusson                                  visible_if_deps, block)
717*90c36d8aSUlf Magnusson
718*90c36d8aSUlf Magnusson            elif t0 == T_COMMENT:
719*90c36d8aSUlf Magnusson                comment = Comment()
720*90c36d8aSUlf Magnusson
721*90c36d8aSUlf Magnusson                comment.config = self
722*90c36d8aSUlf Magnusson                comment.parent = parent
723*90c36d8aSUlf Magnusson                comment.filename = line_feeder.filename
724*90c36d8aSUlf Magnusson                comment.linenr = line_feeder.linenr
725*90c36d8aSUlf Magnusson                comment.text = tokens.get_next()
726*90c36d8aSUlf Magnusson
727*90c36d8aSUlf Magnusson                self.comments.append(comment)
728*90c36d8aSUlf Magnusson                block.append(comment)
729*90c36d8aSUlf Magnusson
730*90c36d8aSUlf Magnusson                self._parse_properties(line_feeder, comment, deps,
731*90c36d8aSUlf Magnusson                                       visible_if_deps)
732*90c36d8aSUlf Magnusson
733*90c36d8aSUlf Magnusson            elif t0 == T_MENU:
734*90c36d8aSUlf Magnusson                menu = Menu()
735*90c36d8aSUlf Magnusson
736*90c36d8aSUlf Magnusson                menu.config = self
737*90c36d8aSUlf Magnusson                menu.parent = parent
738*90c36d8aSUlf Magnusson                menu.filename = line_feeder.filename
739*90c36d8aSUlf Magnusson                menu.linenr = line_feeder.linenr
740*90c36d8aSUlf Magnusson                menu.title = tokens.get_next()
741*90c36d8aSUlf Magnusson
742*90c36d8aSUlf Magnusson                self.menus.append(menu)
743*90c36d8aSUlf Magnusson                block.append(menu)
744*90c36d8aSUlf Magnusson
745*90c36d8aSUlf Magnusson                # Parse properties and contents
746*90c36d8aSUlf Magnusson                self._parse_properties(line_feeder, menu, deps,
747*90c36d8aSUlf Magnusson                                       visible_if_deps)
748*90c36d8aSUlf Magnusson                menu.block = self._parse_block(line_feeder, T_ENDMENU, menu,
749*90c36d8aSUlf Magnusson                                               menu.dep_expr,
750*90c36d8aSUlf Magnusson                                               _make_and(visible_if_deps,
751*90c36d8aSUlf Magnusson                                                         menu.visible_if_expr))
752f219e013SMasahiro Yamada
753f219e013SMasahiro Yamada            elif t0 == T_CHOICE:
754*90c36d8aSUlf Magnusson                name = tokens.get_next()
755*90c36d8aSUlf Magnusson                if name is None:
756f219e013SMasahiro Yamada                    choice = Choice()
757f219e013SMasahiro Yamada                    self.choices.append(choice)
758*90c36d8aSUlf Magnusson                else:
759*90c36d8aSUlf Magnusson                    # Named choice
760*90c36d8aSUlf Magnusson                    choice = self.named_choices.get(name)
761*90c36d8aSUlf Magnusson                    if choice is None:
762*90c36d8aSUlf Magnusson                        choice = Choice()
763f219e013SMasahiro Yamada                        choice.name = name
764f219e013SMasahiro Yamada                        self.named_choices[name] = choice
765*90c36d8aSUlf Magnusson                        self.choices.append(choice)
766f219e013SMasahiro Yamada
767f219e013SMasahiro Yamada                choice.config = self
768f219e013SMasahiro Yamada                choice.parent = parent
769f219e013SMasahiro Yamada
770*90c36d8aSUlf Magnusson                choice.def_locations.append((line_feeder.filename,
771*90c36d8aSUlf Magnusson                                             line_feeder.linenr))
772f219e013SMasahiro Yamada
773f219e013SMasahiro Yamada                # Parse properties and contents
774*90c36d8aSUlf Magnusson                self._parse_properties(line_feeder, choice, deps,
775f219e013SMasahiro Yamada                                       visible_if_deps)
776*90c36d8aSUlf Magnusson                choice.block = self._parse_block(line_feeder, T_ENDCHOICE,
777*90c36d8aSUlf Magnusson                                                 choice, deps, visible_if_deps)
778f219e013SMasahiro Yamada
779f219e013SMasahiro Yamada                choice._determine_actual_symbols()
780f219e013SMasahiro Yamada
781*90c36d8aSUlf Magnusson                # If no type is specified for the choice, its type is that of
782*90c36d8aSUlf Magnusson                # the first choice item with a specified type
783f219e013SMasahiro Yamada                if choice.type == UNKNOWN:
784*90c36d8aSUlf Magnusson                    for item in choice.actual_symbols:
785f219e013SMasahiro Yamada                        if item.type != UNKNOWN:
786f219e013SMasahiro Yamada                            choice.type = item.type
787f219e013SMasahiro Yamada                            break
788f219e013SMasahiro Yamada
789f219e013SMasahiro Yamada                # Each choice item of UNKNOWN type gets the type of the choice
790*90c36d8aSUlf Magnusson                for item in choice.actual_symbols:
791f219e013SMasahiro Yamada                    if item.type == UNKNOWN:
792f219e013SMasahiro Yamada                        item.type = choice.type
793f219e013SMasahiro Yamada
794*90c36d8aSUlf Magnusson                block.append(choice)
795f219e013SMasahiro Yamada
796f219e013SMasahiro Yamada            elif t0 == T_MAINMENU:
797f219e013SMasahiro Yamada                text = tokens.get_next()
798f219e013SMasahiro Yamada                if self.mainmenu_text is not None:
799f219e013SMasahiro Yamada                    self._warn("overriding 'mainmenu' text. "
800f219e013SMasahiro Yamada                               'Old value: "{0}", new value: "{1}".'
801f219e013SMasahiro Yamada                               .format(self.mainmenu_text, text),
802*90c36d8aSUlf Magnusson                               line_feeder.filename, line_feeder.linenr)
803f219e013SMasahiro Yamada                self.mainmenu_text = text
804f219e013SMasahiro Yamada
805f219e013SMasahiro Yamada            else:
806*90c36d8aSUlf Magnusson                _parse_error(line, "unrecognized construct",
807*90c36d8aSUlf Magnusson                             line_feeder.filename, line_feeder.linenr)
808f219e013SMasahiro Yamada
809f219e013SMasahiro Yamada    def _parse_properties(self, line_feeder, stmt, deps, visible_if_deps):
810*90c36d8aSUlf Magnusson        """Parsing of properties for symbols, menus, choices, and comments.
811*90c36d8aSUlf Magnusson        Takes care of propagating dependencies from enclosing menus and ifs."""
812f219e013SMasahiro Yamada
813f219e013SMasahiro Yamada        def parse_val_and_cond(tokens, line, filename, linenr):
814f219e013SMasahiro Yamada            """Parses '<expr1> if <expr2>' constructs, where the 'if' part is
815f219e013SMasahiro Yamada            optional. Returns a tuple containing the parsed expressions, with
816f219e013SMasahiro Yamada            None as the second element if the 'if' part is missing."""
817f219e013SMasahiro Yamada            val = self._parse_expr(tokens, stmt, line, filename, linenr, False)
818f219e013SMasahiro Yamada            if tokens.check(T_IF):
819*90c36d8aSUlf Magnusson                return (val, self._parse_expr(tokens, stmt, line, filename,
820*90c36d8aSUlf Magnusson                                              linenr))
821f219e013SMasahiro Yamada            return (val, None)
822f219e013SMasahiro Yamada
823f219e013SMasahiro Yamada        # In case the symbol is defined in multiple locations, we need to
824f219e013SMasahiro Yamada        # remember what prompts, defaults, and selects are new for this
825f219e013SMasahiro Yamada        # definition, as "depends on" should only apply to the local
826f219e013SMasahiro Yamada        # definition.
827f219e013SMasahiro Yamada        new_prompt = None
828f219e013SMasahiro Yamada        new_def_exprs = []
829f219e013SMasahiro Yamada        new_selects = []
830f219e013SMasahiro Yamada
831f219e013SMasahiro Yamada        # Dependencies from 'depends on' statements
832f219e013SMasahiro Yamada        depends_on_expr = None
833f219e013SMasahiro Yamada
834f219e013SMasahiro Yamada        while 1:
835f219e013SMasahiro Yamada            line = line_feeder.get_next()
836f219e013SMasahiro Yamada            if line is None:
837f219e013SMasahiro Yamada                break
838f219e013SMasahiro Yamada
839*90c36d8aSUlf Magnusson            filename = line_feeder.filename
840*90c36d8aSUlf Magnusson            linenr = line_feeder.linenr
841f219e013SMasahiro Yamada
842f219e013SMasahiro Yamada            tokens = self._tokenize(line, False, filename, linenr)
843f219e013SMasahiro Yamada
844*90c36d8aSUlf Magnusson            t0 = tokens.get_next()
845*90c36d8aSUlf Magnusson            if t0 is None:
846f219e013SMasahiro Yamada                continue
847f219e013SMasahiro Yamada
848*90c36d8aSUlf Magnusson            # Cases are ordered roughly by frequency, which speeds things up a
849*90c36d8aSUlf Magnusson            # bit
850f219e013SMasahiro Yamada
851*90c36d8aSUlf Magnusson            if t0 == T_DEPENDS:
852*90c36d8aSUlf Magnusson                if not tokens.check(T_ON):
853*90c36d8aSUlf Magnusson                    _parse_error(line, 'expected "on" after "depends"',
854*90c36d8aSUlf Magnusson                                 filename, linenr)
855f219e013SMasahiro Yamada
856*90c36d8aSUlf Magnusson                parsed_deps = self._parse_expr(tokens, stmt, line, filename,
857*90c36d8aSUlf Magnusson                                               linenr)
858f219e013SMasahiro Yamada
859*90c36d8aSUlf Magnusson                if isinstance(stmt, (Menu, Comment)):
860*90c36d8aSUlf Magnusson                    stmt.orig_deps = _make_and(stmt.orig_deps, parsed_deps)
861*90c36d8aSUlf Magnusson                else:
862*90c36d8aSUlf Magnusson                    depends_on_expr = _make_and(depends_on_expr, parsed_deps)
863*90c36d8aSUlf Magnusson
864*90c36d8aSUlf Magnusson            elif t0 == T_HELP:
865*90c36d8aSUlf Magnusson                # Find first non-blank (not all-space) line and get its
866*90c36d8aSUlf Magnusson                # indentation
867*90c36d8aSUlf Magnusson                line = line_feeder.next_nonblank()
868f219e013SMasahiro Yamada                if line is None:
869f219e013SMasahiro Yamada                    stmt.help = ""
870f219e013SMasahiro Yamada                    break
871f219e013SMasahiro Yamada                indent = _indentation(line)
872*90c36d8aSUlf Magnusson                if indent == 0:
873f219e013SMasahiro Yamada                    # If the first non-empty lines has zero indent, there is no
874f219e013SMasahiro Yamada                    # help text
875f219e013SMasahiro Yamada                    stmt.help = ""
876*90c36d8aSUlf Magnusson                    line_feeder.unget()
877f219e013SMasahiro Yamada                    break
878f219e013SMasahiro Yamada
879f219e013SMasahiro Yamada                # The help text goes on till the first non-empty line with less
880f219e013SMasahiro Yamada                # indent
881*90c36d8aSUlf Magnusson                help_lines = [_deindent(line, indent)]
882f219e013SMasahiro Yamada                while 1:
883f219e013SMasahiro Yamada                    line = line_feeder.get_next()
884*90c36d8aSUlf Magnusson                    if line is None or \
885f219e013SMasahiro Yamada                       (not line.isspace() and _indentation(line) < indent):
886f219e013SMasahiro Yamada                        stmt.help = "".join(help_lines)
887f219e013SMasahiro Yamada                        break
888f219e013SMasahiro Yamada                    help_lines.append(_deindent(line, indent))
889f219e013SMasahiro Yamada
890f219e013SMasahiro Yamada                if line is None:
891f219e013SMasahiro Yamada                    break
892f219e013SMasahiro Yamada
893*90c36d8aSUlf Magnusson                line_feeder.unget()
894f219e013SMasahiro Yamada
895f219e013SMasahiro Yamada            elif t0 == T_SELECT:
896f219e013SMasahiro Yamada                target = tokens.get_next()
897f219e013SMasahiro Yamada
898f219e013SMasahiro Yamada                stmt.referenced_syms.add(target)
899f219e013SMasahiro Yamada                stmt.selected_syms.add(target)
900f219e013SMasahiro Yamada
901f219e013SMasahiro Yamada                if tokens.check(T_IF):
902f219e013SMasahiro Yamada                    new_selects.append((target,
903*90c36d8aSUlf Magnusson                                        self._parse_expr(tokens, stmt, line,
904*90c36d8aSUlf Magnusson                                                         filename, linenr)))
905f219e013SMasahiro Yamada                else:
906f219e013SMasahiro Yamada                    new_selects.append((target, None))
907f219e013SMasahiro Yamada
908f219e013SMasahiro Yamada            elif t0 in (T_BOOL, T_TRISTATE, T_INT, T_HEX, T_STRING):
909*90c36d8aSUlf Magnusson                stmt.type = TOKEN_TO_TYPE[t0]
910*90c36d8aSUlf Magnusson                if tokens.peek_next() is not None:
911*90c36d8aSUlf Magnusson                    new_prompt = parse_val_and_cond(tokens, line, filename,
912*90c36d8aSUlf Magnusson                                                    linenr)
913f219e013SMasahiro Yamada
914*90c36d8aSUlf Magnusson            elif t0 == T_DEFAULT:
915*90c36d8aSUlf Magnusson                new_def_exprs.append(parse_val_and_cond(tokens, line, filename,
916*90c36d8aSUlf Magnusson                                                        linenr))
917f219e013SMasahiro Yamada
918f219e013SMasahiro Yamada            elif t0 == T_DEF_BOOL:
919f219e013SMasahiro Yamada                stmt.type = BOOL
920*90c36d8aSUlf Magnusson                if tokens.peek_next() is not None:
921*90c36d8aSUlf Magnusson                    new_def_exprs.append(parse_val_and_cond(tokens, line,
922*90c36d8aSUlf Magnusson                                                            filename, linenr))
923f219e013SMasahiro Yamada
924*90c36d8aSUlf Magnusson            elif t0 == T_PROMPT:
925*90c36d8aSUlf Magnusson                # 'prompt' properties override each other within a single
926*90c36d8aSUlf Magnusson                # definition of a symbol, but additional prompts can be added
927*90c36d8aSUlf Magnusson                # by defining the symbol multiple times; hence 'new_prompt'
928*90c36d8aSUlf Magnusson                # instead of 'prompt'.
929*90c36d8aSUlf Magnusson                new_prompt = parse_val_and_cond(tokens, line, filename, linenr)
930*90c36d8aSUlf Magnusson
931*90c36d8aSUlf Magnusson            elif t0 == T_RANGE:
932*90c36d8aSUlf Magnusson                low = tokens.get_next()
933*90c36d8aSUlf Magnusson                high = tokens.get_next()
934*90c36d8aSUlf Magnusson                stmt.referenced_syms.add(low)
935*90c36d8aSUlf Magnusson                stmt.referenced_syms.add(high)
936*90c36d8aSUlf Magnusson
937*90c36d8aSUlf Magnusson                if tokens.check(T_IF):
938*90c36d8aSUlf Magnusson                    stmt.ranges.append((low, high,
939*90c36d8aSUlf Magnusson                                        self._parse_expr(tokens, stmt, line,
940*90c36d8aSUlf Magnusson                                                         filename, linenr)))
941*90c36d8aSUlf Magnusson                else:
942*90c36d8aSUlf Magnusson                    stmt.ranges.append((low, high, None))
943f219e013SMasahiro Yamada
944f219e013SMasahiro Yamada            elif t0 == T_DEF_TRISTATE:
945f219e013SMasahiro Yamada                stmt.type = TRISTATE
946*90c36d8aSUlf Magnusson                if tokens.peek_next() is not None:
947*90c36d8aSUlf Magnusson                    new_def_exprs.append(parse_val_and_cond(tokens, line,
948*90c36d8aSUlf Magnusson                                                            filename, linenr))
949f219e013SMasahiro Yamada
950f219e013SMasahiro Yamada            elif t0 == T_OPTION:
951f219e013SMasahiro Yamada                if tokens.check(T_ENV) and tokens.check(T_EQUAL):
952f219e013SMasahiro Yamada                    env_var = tokens.get_next()
953f219e013SMasahiro Yamada
954f219e013SMasahiro Yamada                    stmt.is_special_ = True
955f219e013SMasahiro Yamada                    stmt.is_from_env = True
956f219e013SMasahiro Yamada
957f219e013SMasahiro Yamada                    if env_var not in os.environ:
958*90c36d8aSUlf Magnusson                        self._warn("The symbol {0} references the "
959*90c36d8aSUlf Magnusson                                   "non-existent environment variable {1} and "
960*90c36d8aSUlf Magnusson                                   "will get the empty string as its value. "
961*90c36d8aSUlf Magnusson                                   "If you're using Kconfiglib via "
962*90c36d8aSUlf Magnusson                                   "'make (i)scriptconfig', it should have "
963*90c36d8aSUlf Magnusson                                   "set up the environment correctly for you. "
964*90c36d8aSUlf Magnusson                                   "If you still got this message, that "
965*90c36d8aSUlf Magnusson                                   "might be an error, and you should email "
966*90c36d8aSUlf Magnusson                                   "ulfalizer a.t Google's email service."""
967*90c36d8aSUlf Magnusson                                   .format(stmt.name, env_var),
968*90c36d8aSUlf Magnusson                                   filename, linenr)
969f219e013SMasahiro Yamada
970*90c36d8aSUlf Magnusson                        stmt.cached_val = ""
971f219e013SMasahiro Yamada                    else:
972*90c36d8aSUlf Magnusson                        stmt.cached_val = os.environ[env_var]
973f219e013SMasahiro Yamada
974f219e013SMasahiro Yamada                elif tokens.check(T_DEFCONFIG_LIST):
975f219e013SMasahiro Yamada                    self.defconfig_sym = stmt
976f219e013SMasahiro Yamada
977f219e013SMasahiro Yamada                elif tokens.check(T_MODULES):
978*90c36d8aSUlf Magnusson                    # To reduce warning spam, only warn if 'option modules' is
979*90c36d8aSUlf Magnusson                    # set on some symbol that isn't MODULES, which should be
980*90c36d8aSUlf Magnusson                    # safe. I haven't run into any projects that make use
981*90c36d8aSUlf Magnusson                    # modules besides the kernel yet, and there it's likely to
982*90c36d8aSUlf Magnusson                    # keep being called "MODULES".
983*90c36d8aSUlf Magnusson                    if stmt.name != "MODULES":
984f219e013SMasahiro Yamada                        self._warn("the 'modules' option is not supported. "
985f219e013SMasahiro Yamada                                   "Let me know if this is a problem for you; "
9869d01b787SMasahiro Yamada                                   "it shouldn't be that hard to implement. "
9879d01b787SMasahiro Yamada                                   "(Note that modules are still supported -- "
9889d01b787SMasahiro Yamada                                   "Kconfiglib just assumes the symbol name "
989*90c36d8aSUlf Magnusson                                   "MODULES, like older versions of the C "
990*90c36d8aSUlf Magnusson                                   "implementation did when 'option modules' "
991*90c36d8aSUlf Magnusson                                   "wasn't used.)",
992*90c36d8aSUlf Magnusson                                   filename, linenr)
993f219e013SMasahiro Yamada
9949d01b787SMasahiro Yamada                elif tokens.check(T_ALLNOCONFIG_Y):
9959d01b787SMasahiro Yamada                    if not isinstance(stmt, Symbol):
9969d01b787SMasahiro Yamada                        _parse_error(line,
997*90c36d8aSUlf Magnusson                                     "the 'allnoconfig_y' option is only "
998*90c36d8aSUlf Magnusson                                     "valid for symbols",
999*90c36d8aSUlf Magnusson                                     filename, linenr)
10009d01b787SMasahiro Yamada                    stmt.allnoconfig_y = True
10019d01b787SMasahiro Yamada
1002f219e013SMasahiro Yamada                else:
1003*90c36d8aSUlf Magnusson                    _parse_error(line, "unrecognized option", filename, linenr)
1004*90c36d8aSUlf Magnusson
1005*90c36d8aSUlf Magnusson            elif t0 == T_VISIBLE:
1006*90c36d8aSUlf Magnusson                if not tokens.check(T_IF):
1007*90c36d8aSUlf Magnusson                    _parse_error(line, 'expected "if" after "visible"',
1008*90c36d8aSUlf Magnusson                                 filename, linenr)
1009*90c36d8aSUlf Magnusson                if not isinstance(stmt, Menu):
1010*90c36d8aSUlf Magnusson                    _parse_error(line,
1011*90c36d8aSUlf Magnusson                                 "'visible if' is only valid for menus",
1012*90c36d8aSUlf Magnusson                                 filename, linenr)
1013*90c36d8aSUlf Magnusson
1014*90c36d8aSUlf Magnusson                parsed_deps = self._parse_expr(tokens, stmt, line, filename,
1015*90c36d8aSUlf Magnusson                                               linenr)
1016*90c36d8aSUlf Magnusson                stmt.visible_if_expr = _make_and(stmt.visible_if_expr,
1017*90c36d8aSUlf Magnusson                                                 parsed_deps)
1018*90c36d8aSUlf Magnusson
1019*90c36d8aSUlf Magnusson            elif t0 == T_OPTIONAL:
1020*90c36d8aSUlf Magnusson                if not isinstance(stmt, Choice):
1021*90c36d8aSUlf Magnusson                    _parse_error(line,
1022*90c36d8aSUlf Magnusson                                 '"optional" is only valid for choices',
1023*90c36d8aSUlf Magnusson                                 filename,
1024*90c36d8aSUlf Magnusson                                 linenr)
1025*90c36d8aSUlf Magnusson                stmt.optional = True
1026f219e013SMasahiro Yamada
1027f219e013SMasahiro Yamada            else:
1028f219e013SMasahiro Yamada                # See comment in Config.__init__()
1029f219e013SMasahiro Yamada                self.end_line = line
1030f219e013SMasahiro Yamada                self.end_line_tokens = tokens
1031f219e013SMasahiro Yamada                break
1032f219e013SMasahiro Yamada
1033*90c36d8aSUlf Magnusson        # Done parsing properties. Now propagate 'depends on' and enclosing
1034*90c36d8aSUlf Magnusson        # menu/if dependencies to expressions.
1035f219e013SMasahiro Yamada
1036*90c36d8aSUlf Magnusson        # The set of symbols referenced directly by the statement plus all
1037*90c36d8aSUlf Magnusson        # symbols referenced by enclosing menus and ifs
1038*90c36d8aSUlf Magnusson        stmt.all_referenced_syms = stmt.referenced_syms | _get_expr_syms(deps)
1039*90c36d8aSUlf Magnusson
1040*90c36d8aSUlf Magnusson        # Save original dependencies from enclosing menus and ifs
1041f219e013SMasahiro Yamada        stmt.deps_from_containing = deps
1042f219e013SMasahiro Yamada
1043*90c36d8aSUlf Magnusson        if isinstance(stmt, (Menu, Comment)):
1044*90c36d8aSUlf Magnusson            stmt.dep_expr = _make_and(stmt.orig_deps, deps)
1045f219e013SMasahiro Yamada        else:
1046*90c36d8aSUlf Magnusson            # Symbol or Choice
1047f219e013SMasahiro Yamada
1048f219e013SMasahiro Yamada            # See comment for 'menu_dep'
1049f219e013SMasahiro Yamada            stmt.menu_dep = depends_on_expr
1050f219e013SMasahiro Yamada
1051*90c36d8aSUlf Magnusson            # Propagate dependencies to prompts
1052f219e013SMasahiro Yamada
1053f219e013SMasahiro Yamada            if new_prompt is not None:
1054*90c36d8aSUlf Magnusson                # Propagate 'visible if' dependencies from enclosing menus
1055f219e013SMasahiro Yamada                prompt, cond_expr = new_prompt
1056f219e013SMasahiro Yamada                cond_expr = _make_and(cond_expr, visible_if_deps)
1057*90c36d8aSUlf Magnusson                # Propagate 'depends on' dependencies
1058f219e013SMasahiro Yamada                new_prompt = (prompt, _make_and(cond_expr, depends_on_expr))
1059*90c36d8aSUlf Magnusson                # Save original
1060f219e013SMasahiro Yamada                stmt.orig_prompts.append(new_prompt)
1061*90c36d8aSUlf Magnusson                # Finalize with dependencies from enclosing menus and ifs
1062*90c36d8aSUlf Magnusson                stmt.prompts.append((new_prompt[0],
1063*90c36d8aSUlf Magnusson                                     _make_and(new_prompt[1], deps)))
1064*90c36d8aSUlf Magnusson
1065*90c36d8aSUlf Magnusson            # Propagate dependencies to defaults
1066*90c36d8aSUlf Magnusson
1067*90c36d8aSUlf Magnusson            # Propagate 'depends on' dependencies
1068*90c36d8aSUlf Magnusson            new_def_exprs = [(val_expr, _make_and(cond_expr, depends_on_expr))
1069*90c36d8aSUlf Magnusson                             for val_expr, cond_expr in new_def_exprs]
1070*90c36d8aSUlf Magnusson            # Save original
1071*90c36d8aSUlf Magnusson            stmt.orig_def_exprs.extend(new_def_exprs)
1072*90c36d8aSUlf Magnusson            # Finalize with dependencies from enclosing menus and ifs
1073*90c36d8aSUlf Magnusson            stmt.def_exprs.extend([(val_expr, _make_and(cond_expr, deps))
1074*90c36d8aSUlf Magnusson                                   for val_expr, cond_expr in new_def_exprs])
1075*90c36d8aSUlf Magnusson
1076*90c36d8aSUlf Magnusson            # Propagate dependencies to selects
1077f219e013SMasahiro Yamada
1078f219e013SMasahiro Yamada            # Only symbols can select
1079f219e013SMasahiro Yamada            if isinstance(stmt, Symbol):
1080*90c36d8aSUlf Magnusson                # Propagate 'depends on' dependencies
1081*90c36d8aSUlf Magnusson                new_selects = [(target, _make_and(cond_expr, depends_on_expr))
1082*90c36d8aSUlf Magnusson                               for target, cond_expr in new_selects]
1083*90c36d8aSUlf Magnusson                # Save original
1084f219e013SMasahiro Yamada                stmt.orig_selects.extend(new_selects)
1085*90c36d8aSUlf Magnusson                # Finalize with dependencies from enclosing menus and ifs
1086*90c36d8aSUlf Magnusson                for target, cond in new_selects:
1087f219e013SMasahiro Yamada                    target.rev_dep = _make_or(target.rev_dep,
1088f219e013SMasahiro Yamada                                              _make_and(stmt,
1089f219e013SMasahiro Yamada                                                        _make_and(cond, deps)))
1090f219e013SMasahiro Yamada
1091*90c36d8aSUlf Magnusson    def _parse_expr(self, feed, cur_item, line, filename=None, linenr=None,
1092*90c36d8aSUlf Magnusson                    transform_m=True):
1093*90c36d8aSUlf Magnusson        """Parses an expression from the tokens in 'feed' using a simple
1094*90c36d8aSUlf Magnusson        top-down approach. The result has the form
1095*90c36d8aSUlf Magnusson        '(<operator>, [<parsed operands>])', where <operator> is e.g.
1096*90c36d8aSUlf Magnusson        kconfiglib.AND. If there is only one operand (i.e., no && or ||), then
1097*90c36d8aSUlf Magnusson        the operand is returned directly. This also goes for subexpressions.
1098f219e013SMasahiro Yamada
1099*90c36d8aSUlf Magnusson        feed: _Feed instance containing the tokens for the expression.
1100f219e013SMasahiro Yamada
1101*90c36d8aSUlf Magnusson        cur_item: The item (Symbol, Choice, Menu, or Comment) currently being
1102*90c36d8aSUlf Magnusson           parsed, or None if we're not parsing an item. Used for recording
1103*90c36d8aSUlf Magnusson           references to symbols.
1104*90c36d8aSUlf Magnusson
1105*90c36d8aSUlf Magnusson        line: The line containing the expression being parsed.
1106*90c36d8aSUlf Magnusson
1107*90c36d8aSUlf Magnusson        filename (default: None): The file containing the expression.
1108*90c36d8aSUlf Magnusson
1109*90c36d8aSUlf Magnusson        linenr (default: None): The line number containing the expression.
1110*90c36d8aSUlf Magnusson
1111*90c36d8aSUlf Magnusson        transform_m (default: False): Determines if 'm' should be rewritten to
1112*90c36d8aSUlf Magnusson           'm && MODULES' -- see parse_val_and_cond().
1113*90c36d8aSUlf Magnusson
1114*90c36d8aSUlf Magnusson        Expression grammar, in decreasing order of precedence:
1115*90c36d8aSUlf Magnusson
1116*90c36d8aSUlf Magnusson        <expr> -> <symbol>
1117*90c36d8aSUlf Magnusson                  <symbol> '=' <symbol>
1118*90c36d8aSUlf Magnusson                  <symbol> '!=' <symbol>
1119*90c36d8aSUlf Magnusson                  '(' <expr> ')'
1120*90c36d8aSUlf Magnusson                  '!' <expr>
1121*90c36d8aSUlf Magnusson                  <expr> '&&' <expr>
1122*90c36d8aSUlf Magnusson                  <expr> '||' <expr>"""
1123*90c36d8aSUlf Magnusson
1124*90c36d8aSUlf Magnusson        # Use instance variables to avoid having to pass these as arguments
1125*90c36d8aSUlf Magnusson        # through the top-down parser in _parse_expr_rec(), which is tedious
1126*90c36d8aSUlf Magnusson        # and obfuscates the code. A profiler run shows no noticeable
1127*90c36d8aSUlf Magnusson        # performance difference.
1128*90c36d8aSUlf Magnusson        self._cur_item = cur_item
1129*90c36d8aSUlf Magnusson        self._transform_m = transform_m
1130*90c36d8aSUlf Magnusson        self._line = line
1131*90c36d8aSUlf Magnusson        self._filename = filename
1132*90c36d8aSUlf Magnusson        self._linenr = linenr
1133*90c36d8aSUlf Magnusson
1134*90c36d8aSUlf Magnusson        return self._parse_expr_rec(feed)
1135*90c36d8aSUlf Magnusson
1136*90c36d8aSUlf Magnusson    def _parse_expr_rec(self, feed):
1137*90c36d8aSUlf Magnusson        or_term = self._parse_or_term(feed)
1138*90c36d8aSUlf Magnusson        if not feed.check(T_OR):
1139*90c36d8aSUlf Magnusson            # Common case -- no need for an OR node since it's just a single
1140*90c36d8aSUlf Magnusson            # operand
1141*90c36d8aSUlf Magnusson            return or_term
1142*90c36d8aSUlf Magnusson        or_terms = [or_term, self._parse_or_term(feed)]
1143*90c36d8aSUlf Magnusson        while feed.check(T_OR):
1144*90c36d8aSUlf Magnusson            or_terms.append(self._parse_or_term(feed))
1145*90c36d8aSUlf Magnusson        return (OR, or_terms)
1146*90c36d8aSUlf Magnusson
1147*90c36d8aSUlf Magnusson    def _parse_or_term(self, feed):
1148*90c36d8aSUlf Magnusson        and_term = self._parse_factor(feed)
1149*90c36d8aSUlf Magnusson        if not feed.check(T_AND):
1150*90c36d8aSUlf Magnusson            # Common case -- no need for an AND node since it's just a single
1151*90c36d8aSUlf Magnusson            # operand
1152*90c36d8aSUlf Magnusson            return and_term
1153*90c36d8aSUlf Magnusson        and_terms = [and_term, self._parse_factor(feed)]
1154*90c36d8aSUlf Magnusson        while feed.check(T_AND):
1155*90c36d8aSUlf Magnusson            and_terms.append(self._parse_factor(feed))
1156*90c36d8aSUlf Magnusson        return (AND, and_terms)
1157*90c36d8aSUlf Magnusson
1158*90c36d8aSUlf Magnusson    def _parse_factor(self, feed):
1159*90c36d8aSUlf Magnusson        token = feed.get_next()
1160*90c36d8aSUlf Magnusson
1161*90c36d8aSUlf Magnusson        if isinstance(token, (Symbol, str)):
1162*90c36d8aSUlf Magnusson            if self._cur_item is not None and isinstance(token, Symbol):
1163*90c36d8aSUlf Magnusson                self._cur_item.referenced_syms.add(token)
1164*90c36d8aSUlf Magnusson
1165*90c36d8aSUlf Magnusson            next_token = feed.peek_next()
1166*90c36d8aSUlf Magnusson            # For conditional expressions ('depends on <expr>',
1167*90c36d8aSUlf Magnusson            # '... if <expr>', # etc.), "m" and m are rewritten to
1168*90c36d8aSUlf Magnusson            # "m" && MODULES.
1169*90c36d8aSUlf Magnusson            if next_token != T_EQUAL and next_token != T_UNEQUAL:
1170*90c36d8aSUlf Magnusson                if self._transform_m and (token is self.m or token == "m"):
1171*90c36d8aSUlf Magnusson                    return (AND, ["m", self._sym_lookup("MODULES")])
1172*90c36d8aSUlf Magnusson                return token
1173*90c36d8aSUlf Magnusson
1174*90c36d8aSUlf Magnusson            relation = EQUAL if (feed.get_next() == T_EQUAL) else UNEQUAL
1175*90c36d8aSUlf Magnusson            token_2 = feed.get_next()
1176*90c36d8aSUlf Magnusson            if self._cur_item is not None and isinstance(token_2, Symbol):
1177*90c36d8aSUlf Magnusson                self._cur_item.referenced_syms.add(token_2)
1178*90c36d8aSUlf Magnusson            return (relation, token, token_2)
1179*90c36d8aSUlf Magnusson
1180*90c36d8aSUlf Magnusson        if token == T_NOT:
1181*90c36d8aSUlf Magnusson            return (NOT, self._parse_factor(feed))
1182*90c36d8aSUlf Magnusson
1183*90c36d8aSUlf Magnusson        if token == T_OPEN_PAREN:
1184*90c36d8aSUlf Magnusson            expr_parse = self._parse_expr_rec(feed)
1185*90c36d8aSUlf Magnusson            if not feed.check(T_CLOSE_PAREN):
1186*90c36d8aSUlf Magnusson                _parse_error(self._line, "missing end parenthesis",
1187*90c36d8aSUlf Magnusson                             self._filename, self._linenr)
1188*90c36d8aSUlf Magnusson            return expr_parse
1189*90c36d8aSUlf Magnusson
1190*90c36d8aSUlf Magnusson        _parse_error(self._line, "malformed expression", self._filename,
1191*90c36d8aSUlf Magnusson                     self._linenr)
1192*90c36d8aSUlf Magnusson
1193*90c36d8aSUlf Magnusson    def _tokenize(self, s, for_eval, filename=None, linenr=None):
1194*90c36d8aSUlf Magnusson        """Returns a _Feed instance containing tokens derived from the string
1195*90c36d8aSUlf Magnusson        's'. Registers any new symbols encountered (via _sym_lookup()).
1196*90c36d8aSUlf Magnusson
1197*90c36d8aSUlf Magnusson        (I experimented with a pure regular expression implementation, but it
1198*90c36d8aSUlf Magnusson        came out slower, less readable, and wouldn't have been as flexible.)
1199*90c36d8aSUlf Magnusson
1200*90c36d8aSUlf Magnusson        for_eval: True when parsing an expression for a call to Config.eval(),
1201*90c36d8aSUlf Magnusson           in which case we should not treat the first token specially nor
1202*90c36d8aSUlf Magnusson           register new symbols."""
1203*90c36d8aSUlf Magnusson
1204*90c36d8aSUlf Magnusson        s = s.strip()
1205*90c36d8aSUlf Magnusson        if s == "" or s[0] == "#":
1206*90c36d8aSUlf Magnusson            return _Feed([])
1207*90c36d8aSUlf Magnusson
1208*90c36d8aSUlf Magnusson        if for_eval:
1209*90c36d8aSUlf Magnusson            previous = None # The previous token seen
1210*90c36d8aSUlf Magnusson            tokens = []
1211*90c36d8aSUlf Magnusson            i = 0 # The current index in the string being tokenized
1212*90c36d8aSUlf Magnusson
1213*90c36d8aSUlf Magnusson        else:
1214*90c36d8aSUlf Magnusson            # The initial word on a line is parsed specially. Let
1215*90c36d8aSUlf Magnusson            # command_chars = [A-Za-z0-9_]. Then
1216*90c36d8aSUlf Magnusson            #  - leading non-command_chars characters are ignored, and
1217*90c36d8aSUlf Magnusson            #  - the first token consists the following one or more
1218*90c36d8aSUlf Magnusson            #    command_chars characters.
1219*90c36d8aSUlf Magnusson            # This is why things like "----help--" are accepted.
1220*90c36d8aSUlf Magnusson            initial_token_match = _initial_token_re_match(s)
1221*90c36d8aSUlf Magnusson            if initial_token_match is None:
1222*90c36d8aSUlf Magnusson                return _Feed([])
1223*90c36d8aSUlf Magnusson            keyword = _get_keyword(initial_token_match.group(1))
1224*90c36d8aSUlf Magnusson            if keyword == T_HELP:
1225*90c36d8aSUlf Magnusson                # Avoid junk after "help", e.g. "---", being registered as a
1226*90c36d8aSUlf Magnusson                # symbol
1227*90c36d8aSUlf Magnusson                return _Feed([T_HELP])
1228*90c36d8aSUlf Magnusson            if keyword is None:
1229*90c36d8aSUlf Magnusson                # We expect a keyword as the first token
1230*90c36d8aSUlf Magnusson                _tokenization_error(s, filename, linenr)
1231*90c36d8aSUlf Magnusson
1232*90c36d8aSUlf Magnusson            previous = keyword
1233*90c36d8aSUlf Magnusson            tokens = [keyword]
1234*90c36d8aSUlf Magnusson            # The current index in the string being tokenized
1235*90c36d8aSUlf Magnusson            i = initial_token_match.end()
1236*90c36d8aSUlf Magnusson
1237*90c36d8aSUlf Magnusson        # _tokenize() is a hotspot during parsing, and this speeds things up a
1238*90c36d8aSUlf Magnusson        # bit
1239*90c36d8aSUlf Magnusson        strlen = len(s)
1240*90c36d8aSUlf Magnusson        append = tokens.append
1241*90c36d8aSUlf Magnusson
1242*90c36d8aSUlf Magnusson        # Main tokenization loop. (Handles tokens past the first one.)
1243*90c36d8aSUlf Magnusson        while i < strlen:
1244*90c36d8aSUlf Magnusson            # Test for an identifier/keyword preceded by whitespace first; this
1245*90c36d8aSUlf Magnusson            # is the most common case.
1246*90c36d8aSUlf Magnusson            id_keyword_match = _id_keyword_re_match(s, i)
1247*90c36d8aSUlf Magnusson            if id_keyword_match:
1248*90c36d8aSUlf Magnusson                # We have an identifier or keyword. The above also stripped any
1249*90c36d8aSUlf Magnusson                # whitespace for us.
1250*90c36d8aSUlf Magnusson                name = id_keyword_match.group(1)
1251*90c36d8aSUlf Magnusson                # Jump past it
1252*90c36d8aSUlf Magnusson                i = id_keyword_match.end()
1253*90c36d8aSUlf Magnusson
1254*90c36d8aSUlf Magnusson                keyword = _get_keyword(name)
1255*90c36d8aSUlf Magnusson                if keyword is not None:
1256*90c36d8aSUlf Magnusson                    # It's a keyword
1257*90c36d8aSUlf Magnusson                    append(keyword)
1258*90c36d8aSUlf Magnusson                elif previous in STRING_LEX:
1259*90c36d8aSUlf Magnusson                    # What would ordinarily be considered an identifier is
1260*90c36d8aSUlf Magnusson                    # treated as a string after certain tokens
1261*90c36d8aSUlf Magnusson                    append(name)
1262*90c36d8aSUlf Magnusson                else:
1263*90c36d8aSUlf Magnusson                    # It's a symbol name. _sym_lookup() will take care of
1264*90c36d8aSUlf Magnusson                    # allocating a new Symbol instance if it's the first time
1265*90c36d8aSUlf Magnusson                    # we see it.
1266*90c36d8aSUlf Magnusson                    sym = self._sym_lookup(name, for_eval)
1267*90c36d8aSUlf Magnusson
1268*90c36d8aSUlf Magnusson                    if previous == T_CONFIG or previous == T_MENUCONFIG:
1269*90c36d8aSUlf Magnusson                        # If the previous token is T_(MENU)CONFIG
1270*90c36d8aSUlf Magnusson                        # ("(menu)config"), we're tokenizing the first line of
1271*90c36d8aSUlf Magnusson                        # a symbol definition, and should remember this as a
1272*90c36d8aSUlf Magnusson                        # location where the symbol is defined
1273*90c36d8aSUlf Magnusson                        sym.def_locations.append((filename, linenr))
1274*90c36d8aSUlf Magnusson                    else:
1275*90c36d8aSUlf Magnusson                        # Otherwise, it's a reference to the symbol
1276*90c36d8aSUlf Magnusson                        sym.ref_locations.append((filename, linenr))
1277*90c36d8aSUlf Magnusson
1278*90c36d8aSUlf Magnusson                    append(sym)
1279*90c36d8aSUlf Magnusson
1280*90c36d8aSUlf Magnusson            else:
1281*90c36d8aSUlf Magnusson                # Not an identifier/keyword
1282*90c36d8aSUlf Magnusson
1283*90c36d8aSUlf Magnusson                while i < strlen and s[i].isspace():
1284*90c36d8aSUlf Magnusson                    i += 1
1285*90c36d8aSUlf Magnusson                if i == strlen:
1286*90c36d8aSUlf Magnusson                    break
1287*90c36d8aSUlf Magnusson                c = s[i]
1288*90c36d8aSUlf Magnusson                i += 1
1289*90c36d8aSUlf Magnusson
1290*90c36d8aSUlf Magnusson                # String literal (constant symbol)
1291*90c36d8aSUlf Magnusson                if c == '"' or c == "'":
1292*90c36d8aSUlf Magnusson                    if "\\" in s:
1293*90c36d8aSUlf Magnusson                        # Slow path: This could probably be sped up, but it's a
1294*90c36d8aSUlf Magnusson                        # very unusual case anyway.
1295*90c36d8aSUlf Magnusson                        quote = c
1296*90c36d8aSUlf Magnusson                        val = ""
1297*90c36d8aSUlf Magnusson                        while 1:
1298*90c36d8aSUlf Magnusson                            if i >= len(s):
1299*90c36d8aSUlf Magnusson                                _tokenization_error(s, filename, linenr)
1300*90c36d8aSUlf Magnusson                            c = s[i]
1301*90c36d8aSUlf Magnusson                            if c == quote:
1302*90c36d8aSUlf Magnusson                                break
1303*90c36d8aSUlf Magnusson                            if c == "\\":
1304*90c36d8aSUlf Magnusson                                if i + 1 >= len(s):
1305*90c36d8aSUlf Magnusson                                    _tokenization_error(s, filename, linenr)
1306*90c36d8aSUlf Magnusson                                val += s[i + 1]
1307*90c36d8aSUlf Magnusson                                i += 2
1308*90c36d8aSUlf Magnusson                            else:
1309*90c36d8aSUlf Magnusson                                val += c
1310*90c36d8aSUlf Magnusson                                i += 1
1311*90c36d8aSUlf Magnusson                        i += 1
1312*90c36d8aSUlf Magnusson                        append(val)
1313*90c36d8aSUlf Magnusson                    else:
1314*90c36d8aSUlf Magnusson                        # Fast path: If the string contains no backslashes
1315*90c36d8aSUlf Magnusson                        # (almost always) we can simply look for the matching
1316*90c36d8aSUlf Magnusson                        # quote.
1317*90c36d8aSUlf Magnusson                        end = s.find(c, i)
1318*90c36d8aSUlf Magnusson                        if end == -1:
1319*90c36d8aSUlf Magnusson                            _tokenization_error(s, filename, linenr)
1320*90c36d8aSUlf Magnusson                        append(s[i:end])
1321*90c36d8aSUlf Magnusson                        i = end + 1
1322*90c36d8aSUlf Magnusson
1323*90c36d8aSUlf Magnusson                elif c == "&":
1324*90c36d8aSUlf Magnusson                    # Invalid characters are ignored
1325*90c36d8aSUlf Magnusson                    if i >= len(s) or s[i] != "&": continue
1326*90c36d8aSUlf Magnusson                    append(T_AND)
1327*90c36d8aSUlf Magnusson                    i += 1
1328*90c36d8aSUlf Magnusson
1329*90c36d8aSUlf Magnusson                elif c == "|":
1330*90c36d8aSUlf Magnusson                    # Invalid characters are ignored
1331*90c36d8aSUlf Magnusson                    if i >= len(s) or s[i] != "|": continue
1332*90c36d8aSUlf Magnusson                    append(T_OR)
1333*90c36d8aSUlf Magnusson                    i += 1
1334*90c36d8aSUlf Magnusson
1335*90c36d8aSUlf Magnusson                elif c == "!":
1336*90c36d8aSUlf Magnusson                    if i < len(s) and s[i] == "=":
1337*90c36d8aSUlf Magnusson                        append(T_UNEQUAL)
1338*90c36d8aSUlf Magnusson                        i += 1
1339*90c36d8aSUlf Magnusson                    else:
1340*90c36d8aSUlf Magnusson                        append(T_NOT)
1341*90c36d8aSUlf Magnusson
1342*90c36d8aSUlf Magnusson                elif c == "=": append(T_EQUAL)
1343*90c36d8aSUlf Magnusson                elif c == "(": append(T_OPEN_PAREN)
1344*90c36d8aSUlf Magnusson                elif c == ")": append(T_CLOSE_PAREN)
1345*90c36d8aSUlf Magnusson                elif c == "#": break # Comment
1346*90c36d8aSUlf Magnusson
1347*90c36d8aSUlf Magnusson                else: continue # Invalid characters are ignored
1348*90c36d8aSUlf Magnusson
1349*90c36d8aSUlf Magnusson            previous = tokens[-1]
1350*90c36d8aSUlf Magnusson
1351*90c36d8aSUlf Magnusson        return _Feed(tokens)
1352*90c36d8aSUlf Magnusson
1353*90c36d8aSUlf Magnusson    def _sym_lookup(self, name, for_eval=False):
1354*90c36d8aSUlf Magnusson        """Fetches the symbol 'name' from the symbol table, creating and
1355*90c36d8aSUlf Magnusson        registering it if it does not exist. If 'for_eval' is True, the symbol
1356*90c36d8aSUlf Magnusson        won't be added to the symbol table if it does not exist -- this is for
1357*90c36d8aSUlf Magnusson        Config.eval()."""
1358f219e013SMasahiro Yamada        if name in self.syms:
1359f219e013SMasahiro Yamada            return self.syms[name]
1360f219e013SMasahiro Yamada
1361f219e013SMasahiro Yamada        new_sym = Symbol()
1362f219e013SMasahiro Yamada        new_sym.config = self
1363f219e013SMasahiro Yamada        new_sym.name = name
1364*90c36d8aSUlf Magnusson        if for_eval:
1365f219e013SMasahiro Yamada            self._warn("no symbol {0} in configuration".format(name))
1366*90c36d8aSUlf Magnusson        else:
1367*90c36d8aSUlf Magnusson            self.syms[name] = new_sym
1368f219e013SMasahiro Yamada        return new_sym
1369f219e013SMasahiro Yamada
1370f219e013SMasahiro Yamada    #
1371*90c36d8aSUlf Magnusson    # Expression evaluation
1372f219e013SMasahiro Yamada    #
1373f219e013SMasahiro Yamada
1374f219e013SMasahiro Yamada    def _eval_expr(self, expr):
1375*90c36d8aSUlf Magnusson        """Evaluates an expression to "n", "m", or "y"."""
1376f219e013SMasahiro Yamada
1377*90c36d8aSUlf Magnusson        # Handles e.g. an "x if y" condition where the "if y" part is missing.
1378f219e013SMasahiro Yamada        if expr is None:
1379f219e013SMasahiro Yamada            return "y"
1380f219e013SMasahiro Yamada
1381*90c36d8aSUlf Magnusson        res = self._eval_expr_rec(expr)
1382*90c36d8aSUlf Magnusson        if res == "m":
1383*90c36d8aSUlf Magnusson            # Promote "m" to "y" if we're running without modules.
1384*90c36d8aSUlf Magnusson            #
1385*90c36d8aSUlf Magnusson            # Internally, "m" is often rewritten to "m" && MODULES by both the
1386*90c36d8aSUlf Magnusson            # C implementation and Kconfiglib, which takes care of cases where
1387*90c36d8aSUlf Magnusson            # "m" should be demoted to "n" instead.
1388*90c36d8aSUlf Magnusson            modules_sym = self.syms.get("MODULES")
1389*90c36d8aSUlf Magnusson            if modules_sym is None or modules_sym.get_value() != "y":
1390*90c36d8aSUlf Magnusson                return "y"
1391*90c36d8aSUlf Magnusson        return res
1392*90c36d8aSUlf Magnusson
1393*90c36d8aSUlf Magnusson    def _eval_expr_rec(self, expr):
1394f219e013SMasahiro Yamada        if isinstance(expr, Symbol):
1395f219e013SMasahiro Yamada            # Non-bool/tristate symbols are always "n" in a tristate sense,
1396f219e013SMasahiro Yamada            # regardless of their value
1397f219e013SMasahiro Yamada            if expr.type != BOOL and expr.type != TRISTATE:
1398f219e013SMasahiro Yamada                return "n"
1399f219e013SMasahiro Yamada            return expr.get_value()
1400f219e013SMasahiro Yamada
1401f219e013SMasahiro Yamada        if isinstance(expr, str):
1402f219e013SMasahiro Yamada            return expr if (expr == "y" or expr == "m") else "n"
1403f219e013SMasahiro Yamada
1404*90c36d8aSUlf Magnusson        # Ordered by frequency
1405f219e013SMasahiro Yamada
1406*90c36d8aSUlf Magnusson        if expr[0] == AND:
1407f219e013SMasahiro Yamada            res = "y"
1408f219e013SMasahiro Yamada            for subexpr in expr[1]:
1409*90c36d8aSUlf Magnusson                ev = self._eval_expr_rec(subexpr)
1410f219e013SMasahiro Yamada                # Return immediately upon discovering an "n" term
1411f219e013SMasahiro Yamada                if ev == "n":
1412f219e013SMasahiro Yamada                    return "n"
1413f219e013SMasahiro Yamada                if ev == "m":
1414f219e013SMasahiro Yamada                    res = "m"
1415f219e013SMasahiro Yamada            # 'res' is either "m" or "y" here; we already handled the
1416f219e013SMasahiro Yamada            # short-circuiting "n" case in the loop.
1417f219e013SMasahiro Yamada            return res
1418f219e013SMasahiro Yamada
1419*90c36d8aSUlf Magnusson        if expr[0] == NOT:
1420*90c36d8aSUlf Magnusson            ev = self._eval_expr_rec(expr[1])
1421f219e013SMasahiro Yamada            if ev == "y":
1422f219e013SMasahiro Yamada                return "n"
1423f219e013SMasahiro Yamada            return "y" if (ev == "n") else "m"
1424f219e013SMasahiro Yamada
1425*90c36d8aSUlf Magnusson        if expr[0] == OR:
1426*90c36d8aSUlf Magnusson            res = "n"
1427*90c36d8aSUlf Magnusson            for subexpr in expr[1]:
1428*90c36d8aSUlf Magnusson                ev = self._eval_expr_rec(subexpr)
1429*90c36d8aSUlf Magnusson                # Return immediately upon discovering a "y" term
1430*90c36d8aSUlf Magnusson                if ev == "y":
1431*90c36d8aSUlf Magnusson                    return "y"
1432*90c36d8aSUlf Magnusson                if ev == "m":
1433*90c36d8aSUlf Magnusson                    res = "m"
1434*90c36d8aSUlf Magnusson            # 'res' is either "n" or "m" here; we already handled the
1435*90c36d8aSUlf Magnusson            # short-circuiting "y" case in the loop.
1436*90c36d8aSUlf Magnusson            return res
1437f219e013SMasahiro Yamada
1438*90c36d8aSUlf Magnusson        if expr[0] == EQUAL:
1439*90c36d8aSUlf Magnusson            return "y" if (_str_val(expr[1]) == _str_val(expr[2])) else "n"
1440*90c36d8aSUlf Magnusson
1441*90c36d8aSUlf Magnusson        if expr[0] == UNEQUAL:
1442*90c36d8aSUlf Magnusson            return "y" if (_str_val(expr[1]) != _str_val(expr[2])) else "n"
1443f219e013SMasahiro Yamada
1444f219e013SMasahiro Yamada        _internal_error("Internal error while evaluating expression: "
1445*90c36d8aSUlf Magnusson                        "unknown operation {0}.".format(expr[0]))
1446f219e013SMasahiro Yamada
1447f219e013SMasahiro Yamada    def _eval_min(self, e1, e2):
1448*90c36d8aSUlf Magnusson        """Returns the minimum value of the two expressions. Equates None with
1449*90c36d8aSUlf Magnusson        'y'."""
1450f219e013SMasahiro Yamada        e1_eval = self._eval_expr(e1)
1451f219e013SMasahiro Yamada        e2_eval = self._eval_expr(e2)
1452f219e013SMasahiro Yamada        return e1_eval if tri_less(e1_eval, e2_eval) else e2_eval
1453f219e013SMasahiro Yamada
1454f219e013SMasahiro Yamada    def _eval_max(self, e1, e2):
1455*90c36d8aSUlf Magnusson        """Returns the maximum value of the two expressions. Equates None with
1456*90c36d8aSUlf Magnusson        'y'."""
1457f219e013SMasahiro Yamada        e1_eval = self._eval_expr(e1)
1458f219e013SMasahiro Yamada        e2_eval = self._eval_expr(e2)
1459f219e013SMasahiro Yamada        return e1_eval if tri_greater(e1_eval, e2_eval) else e2_eval
1460f219e013SMasahiro Yamada
1461f219e013SMasahiro Yamada    #
1462*90c36d8aSUlf Magnusson    # Dependency tracking (for caching and invalidation)
1463f219e013SMasahiro Yamada    #
1464f219e013SMasahiro Yamada
1465f219e013SMasahiro Yamada    def _build_dep(self):
1466f219e013SMasahiro Yamada        """Populates the Symbol.dep sets, linking the symbol to the symbols
1467f219e013SMasahiro Yamada        that immediately depend on it in the sense that changing the value of
1468f219e013SMasahiro Yamada        the symbol might affect the values of those other symbols. This is used
1469f219e013SMasahiro Yamada        for caching/invalidation purposes. The calculated sets might be larger
1470f219e013SMasahiro Yamada        than necessary as we don't do any complicated analysis of the
1471f219e013SMasahiro Yamada        expressions."""
1472f219e013SMasahiro Yamada
1473f219e013SMasahiro Yamada        # Adds 'sym' as a directly dependent symbol to all symbols that appear
1474f219e013SMasahiro Yamada        # in the expression 'e'
1475f219e013SMasahiro Yamada        def add_expr_deps(e, sym):
1476f219e013SMasahiro Yamada            for s in _get_expr_syms(e):
1477f219e013SMasahiro Yamada                s.dep.add(sym)
1478f219e013SMasahiro Yamada
1479f219e013SMasahiro Yamada        # The directly dependent symbols of a symbol are:
1480f219e013SMasahiro Yamada        #  - Any symbols whose prompts, default values, rev_dep (select
1481f219e013SMasahiro Yamada        #    condition), or ranges depend on the symbol
1482f219e013SMasahiro Yamada        #  - Any symbols that belong to the same choice statement as the symbol
1483f219e013SMasahiro Yamada        #    (these won't be included in 'dep' as that makes the dependency
1484f219e013SMasahiro Yamada        #    graph unwieldy, but Symbol._get_dependent() will include them)
1485f219e013SMasahiro Yamada        #  - Any symbols in a choice statement that depends on the symbol
1486*90c36d8aSUlf Magnusson        for sym in self.syms_iter():
1487*90c36d8aSUlf Magnusson            for _, e in sym.prompts:
1488f219e013SMasahiro Yamada                add_expr_deps(e, sym)
1489f219e013SMasahiro Yamada
1490*90c36d8aSUlf Magnusson            for v, e in sym.def_exprs:
1491f219e013SMasahiro Yamada                add_expr_deps(v, sym)
1492f219e013SMasahiro Yamada                add_expr_deps(e, sym)
1493f219e013SMasahiro Yamada
1494f219e013SMasahiro Yamada            add_expr_deps(sym.rev_dep, sym)
1495f219e013SMasahiro Yamada
1496*90c36d8aSUlf Magnusson            for l, u, e in sym.ranges:
1497f219e013SMasahiro Yamada                add_expr_deps(l, sym)
1498f219e013SMasahiro Yamada                add_expr_deps(u, sym)
1499f219e013SMasahiro Yamada                add_expr_deps(e, sym)
1500f219e013SMasahiro Yamada
1501*90c36d8aSUlf Magnusson            if sym.is_choice_sym:
1502f219e013SMasahiro Yamada                choice = sym.parent
1503*90c36d8aSUlf Magnusson                for _, e in choice.prompts:
1504*90c36d8aSUlf Magnusson                    add_expr_deps(e, sym)
1505*90c36d8aSUlf Magnusson                for _, e in choice.def_exprs:
1506f219e013SMasahiro Yamada                    add_expr_deps(e, sym)
1507f219e013SMasahiro Yamada
1508*90c36d8aSUlf Magnusson    def _eq_to_sym(self, eq):
1509*90c36d8aSUlf Magnusson        """_expr_depends_on() helper. For (in)equalities of the form sym = y/m
1510*90c36d8aSUlf Magnusson        or sym != n, returns sym. For other (in)equalities, returns None."""
1511*90c36d8aSUlf Magnusson        relation, left, right = eq
1512f219e013SMasahiro Yamada
1513*90c36d8aSUlf Magnusson        def transform_y_m_n(item):
1514*90c36d8aSUlf Magnusson            if item is self.y: return "y"
1515*90c36d8aSUlf Magnusson            if item is self.m: return "m"
1516*90c36d8aSUlf Magnusson            if item is self.n: return "n"
1517*90c36d8aSUlf Magnusson            return item
1518*90c36d8aSUlf Magnusson
1519*90c36d8aSUlf Magnusson        left = transform_y_m_n(left)
1520*90c36d8aSUlf Magnusson        right = transform_y_m_n(right)
1521*90c36d8aSUlf Magnusson
1522*90c36d8aSUlf Magnusson        # Make sure the symbol (if any) appears to the left
1523*90c36d8aSUlf Magnusson        if not isinstance(left, Symbol):
1524*90c36d8aSUlf Magnusson            left, right = right, left
1525*90c36d8aSUlf Magnusson        if not isinstance(left, Symbol):
1526*90c36d8aSUlf Magnusson            return None
1527*90c36d8aSUlf Magnusson        if (relation == EQUAL and (right == "y" or right == "m")) or \
1528*90c36d8aSUlf Magnusson           (relation == UNEQUAL and right == "n"):
1529*90c36d8aSUlf Magnusson            return left
1530*90c36d8aSUlf Magnusson        return None
1531*90c36d8aSUlf Magnusson
1532*90c36d8aSUlf Magnusson    def _expr_depends_on(self, expr, sym):
1533*90c36d8aSUlf Magnusson        """Reimplementation of expr_depends_symbol() from mconf.c. Used to
1534*90c36d8aSUlf Magnusson        determine if a submenu should be implicitly created, which influences
1535*90c36d8aSUlf Magnusson        what items inside choice statements are considered choice items."""
1536*90c36d8aSUlf Magnusson        if expr is None:
1537*90c36d8aSUlf Magnusson            return False
1538*90c36d8aSUlf Magnusson
1539*90c36d8aSUlf Magnusson        def rec(expr):
1540*90c36d8aSUlf Magnusson            if isinstance(expr, str):
1541*90c36d8aSUlf Magnusson                return False
1542*90c36d8aSUlf Magnusson            if isinstance(expr, Symbol):
1543*90c36d8aSUlf Magnusson                return expr is sym
1544*90c36d8aSUlf Magnusson
1545*90c36d8aSUlf Magnusson            if expr[0] in (EQUAL, UNEQUAL):
1546*90c36d8aSUlf Magnusson                return self._eq_to_sym(expr) is sym
1547*90c36d8aSUlf Magnusson            if expr[0] == AND:
1548*90c36d8aSUlf Magnusson                for and_expr in expr[1]:
1549*90c36d8aSUlf Magnusson                    if rec(and_expr):
1550*90c36d8aSUlf Magnusson                        return True
1551*90c36d8aSUlf Magnusson            return False
1552*90c36d8aSUlf Magnusson
1553*90c36d8aSUlf Magnusson        return rec(expr)
1554*90c36d8aSUlf Magnusson
1555*90c36d8aSUlf Magnusson    def _invalidate_all(self):
1556*90c36d8aSUlf Magnusson        for sym in self.syms_iter():
1557*90c36d8aSUlf Magnusson            sym._invalidate()
1558*90c36d8aSUlf Magnusson
1559*90c36d8aSUlf Magnusson    #
1560*90c36d8aSUlf Magnusson    # Printing and misc.
1561*90c36d8aSUlf Magnusson    #
1562*90c36d8aSUlf Magnusson
1563*90c36d8aSUlf Magnusson    def _expand_sym_refs(self, s):
1564*90c36d8aSUlf Magnusson        """Expands $-references to symbols in 's' to symbol values, or to the
1565*90c36d8aSUlf Magnusson        empty string for undefined symbols."""
1566*90c36d8aSUlf Magnusson
1567*90c36d8aSUlf Magnusson        while 1:
1568*90c36d8aSUlf Magnusson            sym_ref_match = _sym_ref_re_search(s)
1569*90c36d8aSUlf Magnusson            if sym_ref_match is None:
1570*90c36d8aSUlf Magnusson                return s
1571*90c36d8aSUlf Magnusson
1572*90c36d8aSUlf Magnusson            sym_name = sym_ref_match.group(0)[1:]
1573*90c36d8aSUlf Magnusson            sym = self.syms.get(sym_name)
1574*90c36d8aSUlf Magnusson            expansion = "" if sym is None else sym.get_value()
1575*90c36d8aSUlf Magnusson
1576*90c36d8aSUlf Magnusson            s = s[:sym_ref_match.start()] + \
1577*90c36d8aSUlf Magnusson                expansion + \
1578*90c36d8aSUlf Magnusson                s[sym_ref_match.end():]
1579*90c36d8aSUlf Magnusson
1580*90c36d8aSUlf Magnusson    def _expr_val_str(self, expr, no_value_str="(none)",
1581*90c36d8aSUlf Magnusson                      get_val_instead_of_eval=False):
1582*90c36d8aSUlf Magnusson        """Printing helper. Returns a string with 'expr' and its value.
1583*90c36d8aSUlf Magnusson
1584*90c36d8aSUlf Magnusson        no_value_str: String to return when 'expr' is missing (None).
1585*90c36d8aSUlf Magnusson
1586*90c36d8aSUlf Magnusson        get_val_instead_of_eval: Assume 'expr' is a symbol or string (constant
1587*90c36d8aSUlf Magnusson          symbol) and get its value directly instead of evaluating it to a
1588*90c36d8aSUlf Magnusson          tristate value."""
1589f219e013SMasahiro Yamada
1590f219e013SMasahiro Yamada        if expr is None:
1591f219e013SMasahiro Yamada            return no_value_str
1592f219e013SMasahiro Yamada
1593f219e013SMasahiro Yamada        if get_val_instead_of_eval:
1594f219e013SMasahiro Yamada            if isinstance(expr, str):
1595f219e013SMasahiro Yamada                return _expr_to_str(expr)
1596f219e013SMasahiro Yamada            val = expr.get_value()
1597f219e013SMasahiro Yamada        else:
1598f219e013SMasahiro Yamada            val = self._eval_expr(expr)
1599f219e013SMasahiro Yamada
1600f219e013SMasahiro Yamada        return "{0} (value: {1})".format(_expr_to_str(expr), _expr_to_str(val))
1601f219e013SMasahiro Yamada
1602f219e013SMasahiro Yamada    def _get_sym_or_choice_str(self, sc):
1603f219e013SMasahiro Yamada        """Symbols and choices have many properties in common, so we factor out
1604f219e013SMasahiro Yamada        common __str__() stuff here. "sc" is short for "symbol or choice"."""
1605f219e013SMasahiro Yamada
1606f219e013SMasahiro Yamada        # As we deal a lot with string representations here, use some
1607f219e013SMasahiro Yamada        # convenient shorthand:
1608f219e013SMasahiro Yamada        s = _expr_to_str
1609f219e013SMasahiro Yamada
1610f219e013SMasahiro Yamada        #
1611f219e013SMasahiro Yamada        # Common symbol/choice properties
1612f219e013SMasahiro Yamada        #
1613f219e013SMasahiro Yamada
1614*90c36d8aSUlf Magnusson        user_val_str = "(no user value)" if sc.user_val is None else \
1615*90c36d8aSUlf Magnusson                       s(sc.user_val)
1616f219e013SMasahiro Yamada
1617f219e013SMasahiro Yamada        # Build prompts string
1618*90c36d8aSUlf Magnusson        if not sc.prompts:
1619f219e013SMasahiro Yamada            prompts_str = " (no prompts)"
1620f219e013SMasahiro Yamada        else:
1621f219e013SMasahiro Yamada            prompts_str_rows = []
1622*90c36d8aSUlf Magnusson            for prompt, cond_expr in sc.orig_prompts:
1623f219e013SMasahiro Yamada                if cond_expr is None:
1624f219e013SMasahiro Yamada                    prompts_str_rows.append(' "{0}"'.format(prompt))
1625f219e013SMasahiro Yamada                else:
1626*90c36d8aSUlf Magnusson                    prompts_str_rows.append(
1627*90c36d8aSUlf Magnusson                      ' "{0}" if {1}'.format(prompt,
1628*90c36d8aSUlf Magnusson                                             self._expr_val_str(cond_expr)))
1629f219e013SMasahiro Yamada            prompts_str = "\n".join(prompts_str_rows)
1630f219e013SMasahiro Yamada
1631f219e013SMasahiro Yamada        # Build locations string
1632*90c36d8aSUlf Magnusson        if not sc.def_locations:
1633f219e013SMasahiro Yamada            locations_str = "(no locations)"
1634f219e013SMasahiro Yamada        else:
1635f219e013SMasahiro Yamada            locations_str = " ".join(["{0}:{1}".format(filename, linenr) for
1636f219e013SMasahiro Yamada                                      (filename, linenr) in sc.def_locations])
1637f219e013SMasahiro Yamada
1638*90c36d8aSUlf Magnusson        # Build additional-dependencies-from-menus-and-ifs string
1639*90c36d8aSUlf Magnusson        additional_deps_str = " " + \
1640*90c36d8aSUlf Magnusson          self._expr_val_str(sc.deps_from_containing,
1641f219e013SMasahiro Yamada                             "(no additional dependencies)")
1642f219e013SMasahiro Yamada
1643f219e013SMasahiro Yamada        #
1644f219e013SMasahiro Yamada        # Symbol-specific stuff
1645f219e013SMasahiro Yamada        #
1646f219e013SMasahiro Yamada
1647f219e013SMasahiro Yamada        if isinstance(sc, Symbol):
1648f219e013SMasahiro Yamada            # Build ranges string
1649f219e013SMasahiro Yamada            if isinstance(sc, Symbol):
1650*90c36d8aSUlf Magnusson                if not sc.ranges:
1651f219e013SMasahiro Yamada                    ranges_str = " (no ranges)"
1652f219e013SMasahiro Yamada                else:
1653f219e013SMasahiro Yamada                    ranges_str_rows = []
1654*90c36d8aSUlf Magnusson                    for l, u, cond_expr in sc.ranges:
1655f219e013SMasahiro Yamada                        if cond_expr is None:
1656*90c36d8aSUlf Magnusson                            ranges_str_rows.append(" [{0}, {1}]".format(s(l),
1657*90c36d8aSUlf Magnusson                                                                        s(u)))
1658f219e013SMasahiro Yamada                        else:
1659f219e013SMasahiro Yamada                            ranges_str_rows.append(" [{0}, {1}] if {2}"
1660*90c36d8aSUlf Magnusson                              .format(s(l), s(u),
1661*90c36d8aSUlf Magnusson                                      self._expr_val_str(cond_expr)))
1662f219e013SMasahiro Yamada                    ranges_str = "\n".join(ranges_str_rows)
1663f219e013SMasahiro Yamada
1664f219e013SMasahiro Yamada            # Build default values string
1665*90c36d8aSUlf Magnusson            if not sc.def_exprs:
1666f219e013SMasahiro Yamada                defaults_str = " (no default values)"
1667f219e013SMasahiro Yamada            else:
1668f219e013SMasahiro Yamada                defaults_str_rows = []
1669*90c36d8aSUlf Magnusson                for val_expr, cond_expr in sc.orig_def_exprs:
1670*90c36d8aSUlf Magnusson                    row_str = " " + self._expr_val_str(val_expr, "(none)",
1671*90c36d8aSUlf Magnusson                                                       sc.type == STRING)
1672f219e013SMasahiro Yamada                    defaults_str_rows.append(row_str)
1673*90c36d8aSUlf Magnusson                    defaults_str_rows.append("  Condition: " +
1674*90c36d8aSUlf Magnusson                                               self._expr_val_str(cond_expr))
1675f219e013SMasahiro Yamada                defaults_str = "\n".join(defaults_str_rows)
1676f219e013SMasahiro Yamada
1677f219e013SMasahiro Yamada            # Build selects string
1678*90c36d8aSUlf Magnusson            if not sc.orig_selects:
1679f219e013SMasahiro Yamada                selects_str = " (no selects)"
1680f219e013SMasahiro Yamada            else:
1681f219e013SMasahiro Yamada                selects_str_rows = []
1682*90c36d8aSUlf Magnusson                for target, cond_expr in sc.orig_selects:
1683f219e013SMasahiro Yamada                    if cond_expr is None:
1684f219e013SMasahiro Yamada                        selects_str_rows.append(" {0}".format(target.name))
1685f219e013SMasahiro Yamada                    else:
1686*90c36d8aSUlf Magnusson                        selects_str_rows.append(
1687*90c36d8aSUlf Magnusson                          " {0} if {1}".format(target.name,
1688*90c36d8aSUlf Magnusson                                               self._expr_val_str(cond_expr)))
1689f219e013SMasahiro Yamada                selects_str = "\n".join(selects_str_rows)
1690f219e013SMasahiro Yamada
1691*90c36d8aSUlf Magnusson            res = _lines("Symbol " +
1692*90c36d8aSUlf Magnusson                           ("(no name)" if sc.name is None else sc.name),
1693*90c36d8aSUlf Magnusson                         "Type           : " + TYPENAME[sc.type],
1694*90c36d8aSUlf Magnusson                         "Value          : " + s(sc.get_value()),
1695*90c36d8aSUlf Magnusson                         "User value     : " + user_val_str,
1696*90c36d8aSUlf Magnusson                         "Visibility     : " + s(_get_visibility(sc)),
1697*90c36d8aSUlf Magnusson                         "Is choice item : " + BOOL_STR[sc.is_choice_sym],
1698*90c36d8aSUlf Magnusson                         "Is defined     : " + BOOL_STR[sc.is_defined_],
1699*90c36d8aSUlf Magnusson                         "Is from env.   : " + BOOL_STR[sc.is_from_env],
1700*90c36d8aSUlf Magnusson                         "Is special     : " + BOOL_STR[sc.is_special_] + "\n")
1701*90c36d8aSUlf Magnusson            if sc.ranges:
1702*90c36d8aSUlf Magnusson                res += _lines("Ranges:", ranges_str + "\n")
1703*90c36d8aSUlf Magnusson            res += _lines("Prompts:",
1704f219e013SMasahiro Yamada                          prompts_str,
1705f219e013SMasahiro Yamada                          "Default values:",
1706f219e013SMasahiro Yamada                          defaults_str,
1707f219e013SMasahiro Yamada                          "Selects:",
1708f219e013SMasahiro Yamada                          selects_str,
1709*90c36d8aSUlf Magnusson                          "Reverse (select-related) dependencies:",
1710*90c36d8aSUlf Magnusson                          " (no reverse dependencies)" if sc.rev_dep == "n"
1711*90c36d8aSUlf Magnusson                            else " " + self._expr_val_str(sc.rev_dep),
1712*90c36d8aSUlf Magnusson                          "Additional dependencies from enclosing menus "
1713*90c36d8aSUlf Magnusson                            "and ifs:",
1714f219e013SMasahiro Yamada                          additional_deps_str,
1715f219e013SMasahiro Yamada                          "Locations: " + locations_str)
1716f219e013SMasahiro Yamada
1717f219e013SMasahiro Yamada            return res
1718f219e013SMasahiro Yamada
1719f219e013SMasahiro Yamada        #
1720f219e013SMasahiro Yamada        # Choice-specific stuff
1721f219e013SMasahiro Yamada        #
1722f219e013SMasahiro Yamada
1723f219e013SMasahiro Yamada        # Build selected symbol string
1724f219e013SMasahiro Yamada        sel = sc.get_selection()
1725*90c36d8aSUlf Magnusson        sel_str = "(no selection)" if sel is None else sel.name
1726f219e013SMasahiro Yamada
1727f219e013SMasahiro Yamada        # Build default values string
1728*90c36d8aSUlf Magnusson        if not sc.def_exprs:
1729f219e013SMasahiro Yamada            defaults_str = " (no default values)"
1730f219e013SMasahiro Yamada        else:
1731f219e013SMasahiro Yamada            defaults_str_rows = []
1732*90c36d8aSUlf Magnusson            for sym, cond_expr in sc.orig_def_exprs:
1733f219e013SMasahiro Yamada                if cond_expr is None:
1734f219e013SMasahiro Yamada                    defaults_str_rows.append(" {0}".format(sym.name))
1735f219e013SMasahiro Yamada                else:
1736*90c36d8aSUlf Magnusson                    defaults_str_rows.append(" {0} if {1}".format(sym.name,
1737*90c36d8aSUlf Magnusson                                                self._expr_val_str(cond_expr)))
1738f219e013SMasahiro Yamada            defaults_str = "\n".join(defaults_str_rows)
1739f219e013SMasahiro Yamada
1740f219e013SMasahiro Yamada        # Build contained symbols string
1741*90c36d8aSUlf Magnusson        names = [sym.name for sym in sc.actual_symbols]
1742*90c36d8aSUlf Magnusson        syms_string = " ".join(names) if names else "(empty)"
1743f219e013SMasahiro Yamada
1744*90c36d8aSUlf Magnusson        return _lines("Choice",
1745*90c36d8aSUlf Magnusson                      "Name (for named choices): " +
1746*90c36d8aSUlf Magnusson                        ("(no name)" if sc.name is None else sc.name),
1747*90c36d8aSUlf Magnusson                      "Type            : " + TYPENAME[sc.type],
1748f219e013SMasahiro Yamada                      "Selected symbol : " + sel_str,
1749*90c36d8aSUlf Magnusson                      "User value      : " + user_val_str,
1750*90c36d8aSUlf Magnusson                      "Mode            : " + s(sc.get_mode()),
1751*90c36d8aSUlf Magnusson                      "Visibility      : " + s(_get_visibility(sc)),
1752*90c36d8aSUlf Magnusson                      "Optional        : " + BOOL_STR[sc.optional],
1753f219e013SMasahiro Yamada                      "Prompts:",
1754f219e013SMasahiro Yamada                      prompts_str,
1755f219e013SMasahiro Yamada                      "Defaults:",
1756f219e013SMasahiro Yamada                      defaults_str,
1757f219e013SMasahiro Yamada                      "Choice symbols:",
1758f219e013SMasahiro Yamada                      " " + syms_string,
1759*90c36d8aSUlf Magnusson                      "Additional dependencies from enclosing menus and "
1760*90c36d8aSUlf Magnusson                        "ifs:",
1761f219e013SMasahiro Yamada                      additional_deps_str,
1762f219e013SMasahiro Yamada                      "Locations: " + locations_str)
1763f219e013SMasahiro Yamada
1764f219e013SMasahiro Yamada    def _warn(self, msg, filename=None, linenr=None):
1765f219e013SMasahiro Yamada        """For printing warnings to stderr."""
1766f219e013SMasahiro Yamada        if self.print_warnings:
1767*90c36d8aSUlf Magnusson            _stderr_msg("warning: " + msg, filename, linenr)
1768f219e013SMasahiro Yamada
1769*90c36d8aSUlf Magnussonclass Item(object):
1770f219e013SMasahiro Yamada
1771f219e013SMasahiro Yamada    """Base class for symbols and other Kconfig constructs. Subclasses are
1772f219e013SMasahiro Yamada    Symbol, Choice, Menu, and Comment."""
1773f219e013SMasahiro Yamada
1774f219e013SMasahiro Yamada    def is_symbol(self):
1775*90c36d8aSUlf Magnusson        """Returns True if the item is a symbol. Short for
1776f219e013SMasahiro Yamada        isinstance(item, kconfiglib.Symbol)."""
1777f219e013SMasahiro Yamada        return isinstance(self, Symbol)
1778f219e013SMasahiro Yamada
1779f219e013SMasahiro Yamada    def is_choice(self):
1780*90c36d8aSUlf Magnusson        """Returns True if the item is a choice. Short for
1781f219e013SMasahiro Yamada        isinstance(item, kconfiglib.Choice)."""
1782f219e013SMasahiro Yamada        return isinstance(self, Choice)
1783f219e013SMasahiro Yamada
1784f219e013SMasahiro Yamada    def is_menu(self):
1785*90c36d8aSUlf Magnusson        """Returns True if the item is a menu. Short for
1786f219e013SMasahiro Yamada        isinstance(item, kconfiglib.Menu)."""
1787f219e013SMasahiro Yamada        return isinstance(self, Menu)
1788f219e013SMasahiro Yamada
1789f219e013SMasahiro Yamada    def is_comment(self):
1790*90c36d8aSUlf Magnusson        """Returns True if the item is a comment. Short for
1791f219e013SMasahiro Yamada        isinstance(item, kconfiglib.Comment)."""
1792f219e013SMasahiro Yamada        return isinstance(self, Comment)
1793f219e013SMasahiro Yamada
1794*90c36d8aSUlf Magnussonclass Symbol(Item):
1795f219e013SMasahiro Yamada
1796f219e013SMasahiro Yamada    """Represents a configuration symbol - e.g. FOO for
1797f219e013SMasahiro Yamada
1798f219e013SMasahiro Yamada    config FOO
1799f219e013SMasahiro Yamada        ..."""
1800f219e013SMasahiro Yamada
1801f219e013SMasahiro Yamada    #
1802f219e013SMasahiro Yamada    # Public interface
1803f219e013SMasahiro Yamada    #
1804f219e013SMasahiro Yamada
1805*90c36d8aSUlf Magnusson    def get_config(self):
1806*90c36d8aSUlf Magnusson        """Returns the Config instance this symbol is from."""
1807*90c36d8aSUlf Magnusson        return self.config
1808*90c36d8aSUlf Magnusson
1809*90c36d8aSUlf Magnusson    def get_name(self):
1810*90c36d8aSUlf Magnusson        """Returns the name of the symbol."""
1811*90c36d8aSUlf Magnusson        return self.name
1812*90c36d8aSUlf Magnusson
1813*90c36d8aSUlf Magnusson    def get_type(self):
1814*90c36d8aSUlf Magnusson        """Returns the type of the symbol: one of UNKNOWN, BOOL, TRISTATE,
1815*90c36d8aSUlf Magnusson        STRING, HEX, or INT. These are defined at the top level of the module,
1816*90c36d8aSUlf Magnusson        so you'd do something like
1817*90c36d8aSUlf Magnusson
1818*90c36d8aSUlf Magnusson        if sym.get_type() == kconfiglib.STRING:
1819*90c36d8aSUlf Magnusson            ..."""
1820*90c36d8aSUlf Magnusson        return self.type
1821*90c36d8aSUlf Magnusson
1822*90c36d8aSUlf Magnusson    def get_prompts(self):
1823*90c36d8aSUlf Magnusson        """Returns a list of prompts defined for the symbol, in the order they
1824*90c36d8aSUlf Magnusson        appear in the configuration files. Returns the empty list for symbols
1825*90c36d8aSUlf Magnusson        with no prompt.
1826*90c36d8aSUlf Magnusson
1827*90c36d8aSUlf Magnusson        This list will have a single entry for the vast majority of symbols
1828*90c36d8aSUlf Magnusson        having prompts, but having multiple prompts for a single symbol is
1829*90c36d8aSUlf Magnusson        possible through having multiple 'config' entries for it."""
1830*90c36d8aSUlf Magnusson        return [prompt for prompt, _ in self.orig_prompts]
1831*90c36d8aSUlf Magnusson
1832*90c36d8aSUlf Magnusson    def get_help(self):
1833*90c36d8aSUlf Magnusson        """Returns the help text of the symbol, or None if the symbol has no
1834*90c36d8aSUlf Magnusson        help text."""
1835*90c36d8aSUlf Magnusson        return self.help
1836*90c36d8aSUlf Magnusson
1837*90c36d8aSUlf Magnusson    def get_parent(self):
1838*90c36d8aSUlf Magnusson        """Returns the menu or choice statement that contains the symbol, or
1839*90c36d8aSUlf Magnusson        None if the symbol is at the top level. Note that if statements are
1840*90c36d8aSUlf Magnusson        treated as syntactic and do not have an explicit class
1841*90c36d8aSUlf Magnusson        representation."""
1842*90c36d8aSUlf Magnusson        return self.parent
1843*90c36d8aSUlf Magnusson
1844*90c36d8aSUlf Magnusson    def get_def_locations(self):
1845*90c36d8aSUlf Magnusson        """Returns a list of (filename, linenr) tuples, where filename (string)
1846*90c36d8aSUlf Magnusson        and linenr (int) represent a location where the symbol is defined. For
1847*90c36d8aSUlf Magnusson        the vast majority of symbols this list will only contain one element.
1848*90c36d8aSUlf Magnusson        For the following Kconfig, FOO would get two entries: the lines marked
1849*90c36d8aSUlf Magnusson        with *.
1850*90c36d8aSUlf Magnusson
1851*90c36d8aSUlf Magnusson        config FOO *
1852*90c36d8aSUlf Magnusson            bool "foo prompt 1"
1853*90c36d8aSUlf Magnusson
1854*90c36d8aSUlf Magnusson        config FOO *
1855*90c36d8aSUlf Magnusson            bool "foo prompt 2"
1856*90c36d8aSUlf Magnusson        """
1857*90c36d8aSUlf Magnusson        return self.def_locations
1858*90c36d8aSUlf Magnusson
1859*90c36d8aSUlf Magnusson    def get_ref_locations(self):
1860*90c36d8aSUlf Magnusson        """Returns a list of (filename, linenr) tuples, where filename (string)
1861*90c36d8aSUlf Magnusson        and linenr (int) represent a location where the symbol is referenced in
1862*90c36d8aSUlf Magnusson        the configuration. For example, the lines marked by * would be included
1863*90c36d8aSUlf Magnusson        for FOO below:
1864*90c36d8aSUlf Magnusson
1865*90c36d8aSUlf Magnusson        config A
1866*90c36d8aSUlf Magnusson            bool
1867*90c36d8aSUlf Magnusson            default BAR || FOO *
1868*90c36d8aSUlf Magnusson
1869*90c36d8aSUlf Magnusson        config B
1870*90c36d8aSUlf Magnusson            tristate
1871*90c36d8aSUlf Magnusson            depends on FOO *
1872*90c36d8aSUlf Magnusson            default m if FOO *
1873*90c36d8aSUlf Magnusson
1874*90c36d8aSUlf Magnusson        if FOO *
1875*90c36d8aSUlf Magnusson            config A
1876*90c36d8aSUlf Magnusson                bool "A"
1877*90c36d8aSUlf Magnusson        endif
1878*90c36d8aSUlf Magnusson
1879*90c36d8aSUlf Magnusson        config FOO (definition not included)
1880*90c36d8aSUlf Magnusson            bool
1881*90c36d8aSUlf Magnusson        """
1882*90c36d8aSUlf Magnusson        return self.ref_locations
1883*90c36d8aSUlf Magnusson
1884f219e013SMasahiro Yamada    def get_value(self):
1885f219e013SMasahiro Yamada        """Calculate and return the value of the symbol. See also
1886f219e013SMasahiro Yamada        Symbol.set_user_value()."""
1887f219e013SMasahiro Yamada
1888*90c36d8aSUlf Magnusson        if self.cached_val is not None:
1889*90c36d8aSUlf Magnusson            return self.cached_val
1890f219e013SMasahiro Yamada
1891f219e013SMasahiro Yamada        # As a quirk of Kconfig, undefined symbols get their name as their
1892f219e013SMasahiro Yamada        # value. This is why things like "FOO = bar" work for seeing if FOO has
1893f219e013SMasahiro Yamada        # the value "bar".
1894f219e013SMasahiro Yamada        if self.type == UNKNOWN:
1895*90c36d8aSUlf Magnusson            self.cached_val = self.name
1896f219e013SMasahiro Yamada            return self.name
1897f219e013SMasahiro Yamada
1898*90c36d8aSUlf Magnusson        new_val = DEFAULT_VALUE[self.type]
1899*90c36d8aSUlf Magnusson        vis = _get_visibility(self)
1900f219e013SMasahiro Yamada
1901*90c36d8aSUlf Magnusson        # This is easiest to calculate together with the value
1902*90c36d8aSUlf Magnusson        self.write_to_conf = False
1903f219e013SMasahiro Yamada
1904f219e013SMasahiro Yamada        if self.type == BOOL or self.type == TRISTATE:
1905f219e013SMasahiro Yamada            # The visibility and mode (modules-only or single-selection) of
1906*90c36d8aSUlf Magnusson            # choice items will be taken into account in _get_visibility()
1907*90c36d8aSUlf Magnusson            if self.is_choice_sym:
1908f219e013SMasahiro Yamada                if vis != "n":
1909f219e013SMasahiro Yamada                    choice = self.parent
1910f219e013SMasahiro Yamada                    mode = choice.get_mode()
1911f219e013SMasahiro Yamada
1912f219e013SMasahiro Yamada                    self.write_to_conf = (mode != "n")
1913f219e013SMasahiro Yamada
1914f219e013SMasahiro Yamada                    if mode == "y":
1915*90c36d8aSUlf Magnusson                        if choice.get_selection() is self:
1916*90c36d8aSUlf Magnusson                            new_val = "y"
1917*90c36d8aSUlf Magnusson                        else:
1918*90c36d8aSUlf Magnusson                            new_val = "n"
1919f219e013SMasahiro Yamada                    elif mode == "m":
1920f219e013SMasahiro Yamada                        if self.user_val == "m" or self.user_val == "y":
1921f219e013SMasahiro Yamada                            new_val = "m"
1922f219e013SMasahiro Yamada
1923f219e013SMasahiro Yamada            else:
1924*90c36d8aSUlf Magnusson                # If the symbol is visible and has a user value, use that.
1925*90c36d8aSUlf Magnusson                # Otherwise, look at defaults.
1926f219e013SMasahiro Yamada                use_defaults = True
1927f219e013SMasahiro Yamada
1928f219e013SMasahiro Yamada                if vis != "n":
1929f219e013SMasahiro Yamada                    self.write_to_conf = True
1930f219e013SMasahiro Yamada                    if self.user_val is not None:
1931f219e013SMasahiro Yamada                        new_val = self.config._eval_min(self.user_val, vis)
1932f219e013SMasahiro Yamada                        use_defaults = False
1933f219e013SMasahiro Yamada
1934f219e013SMasahiro Yamada                if use_defaults:
1935*90c36d8aSUlf Magnusson                    for val_expr, cond_expr in self.def_exprs:
1936f219e013SMasahiro Yamada                        cond_eval = self.config._eval_expr(cond_expr)
1937f219e013SMasahiro Yamada                        if cond_eval != "n":
1938f219e013SMasahiro Yamada                            self.write_to_conf = True
1939*90c36d8aSUlf Magnusson                            new_val = self.config._eval_min(val_expr,
1940*90c36d8aSUlf Magnusson                                                            cond_eval)
1941f219e013SMasahiro Yamada                            break
1942f219e013SMasahiro Yamada
1943*90c36d8aSUlf Magnusson                # Reverse (select-related) dependencies take precedence
1944f219e013SMasahiro Yamada                rev_dep_val = self.config._eval_expr(self.rev_dep)
1945f219e013SMasahiro Yamada                if rev_dep_val != "n":
1946f219e013SMasahiro Yamada                    self.write_to_conf = True
1947f219e013SMasahiro Yamada                    new_val = self.config._eval_max(new_val, rev_dep_val)
1948f219e013SMasahiro Yamada
1949f219e013SMasahiro Yamada            # Promote "m" to "y" for booleans
1950f219e013SMasahiro Yamada            if new_val == "m" and self.type == BOOL:
1951f219e013SMasahiro Yamada                new_val = "y"
1952f219e013SMasahiro Yamada
1953*90c36d8aSUlf Magnusson        elif self.type == INT or self.type == HEX:
1954f219e013SMasahiro Yamada            has_active_range = False
1955f219e013SMasahiro Yamada            low = None
1956f219e013SMasahiro Yamada            high = None
1957f219e013SMasahiro Yamada            use_defaults = True
1958f219e013SMasahiro Yamada
1959f219e013SMasahiro Yamada            base = 16 if self.type == HEX else 10
1960f219e013SMasahiro Yamada
1961*90c36d8aSUlf Magnusson            for l, h, cond_expr in self.ranges:
1962f219e013SMasahiro Yamada                if self.config._eval_expr(cond_expr) != "n":
1963f219e013SMasahiro Yamada                    has_active_range = True
1964f219e013SMasahiro Yamada
1965*90c36d8aSUlf Magnusson                    low_str = _str_val(l)
1966*90c36d8aSUlf Magnusson                    high_str = _str_val(h)
1967f219e013SMasahiro Yamada                    low = int(low_str, base) if \
1968f219e013SMasahiro Yamada                      _is_base_n(low_str, base) else 0
1969f219e013SMasahiro Yamada                    high = int(high_str, base) if \
1970f219e013SMasahiro Yamada                      _is_base_n(high_str, base) else 0
1971f219e013SMasahiro Yamada
1972f219e013SMasahiro Yamada                    break
1973f219e013SMasahiro Yamada
1974f219e013SMasahiro Yamada            if vis != "n":
1975f219e013SMasahiro Yamada                self.write_to_conf = True
1976f219e013SMasahiro Yamada
1977f219e013SMasahiro Yamada                if self.user_val is not None and \
1978f219e013SMasahiro Yamada                   _is_base_n(self.user_val, base) and \
1979f219e013SMasahiro Yamada                   (not has_active_range or
1980f219e013SMasahiro Yamada                    low <= int(self.user_val, base) <= high):
1981f219e013SMasahiro Yamada
1982f219e013SMasahiro Yamada                    # If the user value is OK, it is stored in exactly the same
1983f219e013SMasahiro Yamada                    # form as specified in the assignment (with or without
1984f219e013SMasahiro Yamada                    # "0x", etc).
1985f219e013SMasahiro Yamada
1986f219e013SMasahiro Yamada                    use_defaults = False
1987f219e013SMasahiro Yamada                    new_val = self.user_val
1988f219e013SMasahiro Yamada
1989f219e013SMasahiro Yamada            if use_defaults:
1990*90c36d8aSUlf Magnusson                for val_expr, cond_expr in self.def_exprs:
1991f219e013SMasahiro Yamada                    if self.config._eval_expr(cond_expr) != "n":
1992f219e013SMasahiro Yamada                        self.write_to_conf = True
1993f219e013SMasahiro Yamada
1994f219e013SMasahiro Yamada                        # If the default value is OK, it is stored in exactly
1995f219e013SMasahiro Yamada                        # the same form as specified. Otherwise, it is clamped
1996f219e013SMasahiro Yamada                        # to the range, and the output has "0x" as appropriate
1997f219e013SMasahiro Yamada                        # for the type.
1998f219e013SMasahiro Yamada
1999*90c36d8aSUlf Magnusson                        new_val = _str_val(val_expr)
2000f219e013SMasahiro Yamada
2001f219e013SMasahiro Yamada                        if _is_base_n(new_val, base):
2002f219e013SMasahiro Yamada                            new_val_num = int(new_val, base)
2003f219e013SMasahiro Yamada                            if has_active_range:
2004f219e013SMasahiro Yamada                                clamped_val = None
2005f219e013SMasahiro Yamada
2006f219e013SMasahiro Yamada                                if new_val_num < low:
2007f219e013SMasahiro Yamada                                    clamped_val = low
2008f219e013SMasahiro Yamada                                elif new_val_num > high:
2009f219e013SMasahiro Yamada                                    clamped_val = high
2010f219e013SMasahiro Yamada
2011f219e013SMasahiro Yamada                                if clamped_val is not None:
2012f219e013SMasahiro Yamada                                    new_val = (hex(clamped_val) if \
2013f219e013SMasahiro Yamada                                      self.type == HEX else str(clamped_val))
2014f219e013SMasahiro Yamada
2015f219e013SMasahiro Yamada                            break
2016f219e013SMasahiro Yamada                else: # For the for loop
2017f219e013SMasahiro Yamada                    # If no user value or default kicks in but the hex/int has
2018f219e013SMasahiro Yamada                    # an active range, then the low end of the range is used,
2019f219e013SMasahiro Yamada                    # provided it's > 0, with "0x" prepended as appropriate.
2020f219e013SMasahiro Yamada                    if has_active_range and low > 0:
2021f219e013SMasahiro Yamada                        new_val = (hex(low) if self.type == HEX else str(low))
2022f219e013SMasahiro Yamada
2023*90c36d8aSUlf Magnusson        elif self.type == STRING:
2024*90c36d8aSUlf Magnusson            use_defaults = True
2025*90c36d8aSUlf Magnusson
2026*90c36d8aSUlf Magnusson            if vis != "n":
2027*90c36d8aSUlf Magnusson                self.write_to_conf = True
2028*90c36d8aSUlf Magnusson                if self.user_val is not None:
2029*90c36d8aSUlf Magnusson                    new_val = self.user_val
2030*90c36d8aSUlf Magnusson                    use_defaults = False
2031*90c36d8aSUlf Magnusson
2032*90c36d8aSUlf Magnusson            if use_defaults:
2033*90c36d8aSUlf Magnusson                for val_expr, cond_expr in self.def_exprs:
2034*90c36d8aSUlf Magnusson                    if self.config._eval_expr(cond_expr) != "n":
2035*90c36d8aSUlf Magnusson                        self.write_to_conf = True
2036*90c36d8aSUlf Magnusson                        new_val = _str_val(val_expr)
2037*90c36d8aSUlf Magnusson                        break
2038*90c36d8aSUlf Magnusson
2039*90c36d8aSUlf Magnusson        self.cached_val = new_val
2040f219e013SMasahiro Yamada        return new_val
2041f219e013SMasahiro Yamada
2042f219e013SMasahiro Yamada    def get_user_value(self):
2043f219e013SMasahiro Yamada        """Returns the value assigned to the symbol in a .config or via
2044*90c36d8aSUlf Magnusson        Symbol.set_user_value() (provided the value was valid for the type of
2045*90c36d8aSUlf Magnusson        the symbol). Returns None in case of no user value."""
2046f219e013SMasahiro Yamada        return self.user_val
2047f219e013SMasahiro Yamada
2048f219e013SMasahiro Yamada    def get_upper_bound(self):
2049f219e013SMasahiro Yamada        """For string/hex/int symbols and for bool and tristate symbols that
2050f219e013SMasahiro Yamada        cannot be modified (see is_modifiable()), returns None.
2051f219e013SMasahiro Yamada
2052f219e013SMasahiro Yamada        Otherwise, returns the highest value the symbol can be set to with
2053*90c36d8aSUlf Magnusson        Symbol.set_user_value() (that will not be truncated): one of "m" or
2054*90c36d8aSUlf Magnusson        "y", arranged from lowest to highest. This corresponds to the highest
2055*90c36d8aSUlf Magnusson        value the symbol could be given in e.g. the 'make menuconfig'
2056*90c36d8aSUlf Magnusson        interface.
2057f219e013SMasahiro Yamada
2058f219e013SMasahiro Yamada        See also the tri_less*() and tri_greater*() functions, which could come
2059f219e013SMasahiro Yamada        in handy."""
2060f219e013SMasahiro Yamada        if self.type != BOOL and self.type != TRISTATE:
2061f219e013SMasahiro Yamada            return None
2062f219e013SMasahiro Yamada        rev_dep = self.config._eval_expr(self.rev_dep)
2063*90c36d8aSUlf Magnusson        # A bool selected to "m" gets promoted to "y", pinning it
2064*90c36d8aSUlf Magnusson        if rev_dep == "m" and self.type == BOOL:
2065*90c36d8aSUlf Magnusson            return None
2066*90c36d8aSUlf Magnusson        vis = _get_visibility(self)
2067*90c36d8aSUlf Magnusson        if TRI_TO_INT[vis] > TRI_TO_INT[rev_dep]:
2068f219e013SMasahiro Yamada            return vis
2069f219e013SMasahiro Yamada        return None
2070f219e013SMasahiro Yamada
2071f219e013SMasahiro Yamada    def get_lower_bound(self):
2072f219e013SMasahiro Yamada        """For string/hex/int symbols and for bool and tristate symbols that
2073f219e013SMasahiro Yamada        cannot be modified (see is_modifiable()), returns None.
2074f219e013SMasahiro Yamada
2075f219e013SMasahiro Yamada        Otherwise, returns the lowest value the symbol can be set to with
2076*90c36d8aSUlf Magnusson        Symbol.set_user_value() (that will not be truncated): one of "n" or
2077*90c36d8aSUlf Magnusson        "m", arranged from lowest to highest. This corresponds to the lowest
2078*90c36d8aSUlf Magnusson        value the symbol could be given in e.g. the 'make menuconfig'
2079*90c36d8aSUlf Magnusson        interface.
2080f219e013SMasahiro Yamada
2081f219e013SMasahiro Yamada        See also the tri_less*() and tri_greater*() functions, which could come
2082f219e013SMasahiro Yamada        in handy."""
2083f219e013SMasahiro Yamada        if self.type != BOOL and self.type != TRISTATE:
2084f219e013SMasahiro Yamada            return None
2085f219e013SMasahiro Yamada        rev_dep = self.config._eval_expr(self.rev_dep)
2086*90c36d8aSUlf Magnusson        # A bool selected to "m" gets promoted to "y", pinning it
2087*90c36d8aSUlf Magnusson        if rev_dep == "m" and self.type == BOOL:
2088*90c36d8aSUlf Magnusson            return None
2089*90c36d8aSUlf Magnusson        if TRI_TO_INT[_get_visibility(self)] > TRI_TO_INT[rev_dep]:
2090f219e013SMasahiro Yamada            return rev_dep
2091f219e013SMasahiro Yamada        return None
2092f219e013SMasahiro Yamada
2093f219e013SMasahiro Yamada    def get_assignable_values(self):
2094f219e013SMasahiro Yamada        """For string/hex/int symbols and for bool and tristate symbols that
2095f219e013SMasahiro Yamada        cannot be modified (see is_modifiable()), returns the empty list.
2096f219e013SMasahiro Yamada
2097f219e013SMasahiro Yamada        Otherwise, returns a list containing the user values that can be
2098f219e013SMasahiro Yamada        assigned to the symbol (that won't be truncated). Usage example:
2099f219e013SMasahiro Yamada
2100f219e013SMasahiro Yamada        if "m" in sym.get_assignable_values():
2101f219e013SMasahiro Yamada            sym.set_user_value("m")
2102f219e013SMasahiro Yamada
2103f219e013SMasahiro Yamada        This is basically a more convenient interface to
2104f219e013SMasahiro Yamada        get_lower/upper_bound() when wanting to test if a particular tristate
2105f219e013SMasahiro Yamada        value can be assigned."""
2106f219e013SMasahiro Yamada        if self.type != BOOL and self.type != TRISTATE:
2107f219e013SMasahiro Yamada            return []
2108f219e013SMasahiro Yamada        rev_dep = self.config._eval_expr(self.rev_dep)
2109*90c36d8aSUlf Magnusson        # A bool selected to "m" gets promoted to "y", pinning it
2110*90c36d8aSUlf Magnusson        if rev_dep == "m" and self.type == BOOL:
2111*90c36d8aSUlf Magnusson            return []
2112*90c36d8aSUlf Magnusson        res = ["n", "m", "y"][TRI_TO_INT[rev_dep] :
2113*90c36d8aSUlf Magnusson                              TRI_TO_INT[_get_visibility(self)] + 1]
2114f219e013SMasahiro Yamada        return res if len(res) > 1 else []
2115f219e013SMasahiro Yamada
2116f219e013SMasahiro Yamada    def get_visibility(self):
2117f219e013SMasahiro Yamada        """Returns the visibility of the symbol: one of "n", "m" or "y". For
2118f219e013SMasahiro Yamada        bool and tristate symbols, this is an upper bound on the value users
2119f219e013SMasahiro Yamada        can set for the symbol. For other types of symbols, a visibility of "n"
2120f219e013SMasahiro Yamada        means the user value will be ignored. A visibility of "n" corresponds
2121f219e013SMasahiro Yamada        to not being visible in the 'make *config' interfaces.
2122f219e013SMasahiro Yamada
2123f219e013SMasahiro Yamada        Example (assuming we're running with modules enabled -- i.e., MODULES
2124f219e013SMasahiro Yamada        set to 'y'):
2125f219e013SMasahiro Yamada
2126f219e013SMasahiro Yamada        # Assume this has been assigned 'n'
2127f219e013SMasahiro Yamada        config N_SYM
2128f219e013SMasahiro Yamada            tristate "N_SYM"
2129f219e013SMasahiro Yamada
2130f219e013SMasahiro Yamada        # Assume this has been assigned 'm'
2131f219e013SMasahiro Yamada        config M_SYM
2132f219e013SMasahiro Yamada            tristate "M_SYM"
2133f219e013SMasahiro Yamada
2134f219e013SMasahiro Yamada        # Has visibility 'n'
2135f219e013SMasahiro Yamada        config A
2136f219e013SMasahiro Yamada            tristate "A"
2137f219e013SMasahiro Yamada            depends on N_SYM
2138f219e013SMasahiro Yamada
2139f219e013SMasahiro Yamada        # Has visibility 'm'
2140f219e013SMasahiro Yamada        config B
2141f219e013SMasahiro Yamada            tristate "B"
2142f219e013SMasahiro Yamada            depends on M_SYM
2143f219e013SMasahiro Yamada
2144f219e013SMasahiro Yamada        # Has visibility 'y'
2145f219e013SMasahiro Yamada        config C
2146f219e013SMasahiro Yamada            tristate "C"
2147f219e013SMasahiro Yamada
2148f219e013SMasahiro Yamada        # Has no prompt, and hence visibility 'n'
2149f219e013SMasahiro Yamada        config D
2150f219e013SMasahiro Yamada            tristate
2151f219e013SMasahiro Yamada
2152f219e013SMasahiro Yamada        Having visibility be tri-valued ensures that e.g. a symbol cannot be
2153f219e013SMasahiro Yamada        set to "y" by the user if it depends on a symbol with value "m", which
2154f219e013SMasahiro Yamada        wouldn't be safe.
2155f219e013SMasahiro Yamada
2156f219e013SMasahiro Yamada        You should probably look at get_lower/upper_bound(),
2157f219e013SMasahiro Yamada        get_assignable_values() and is_modifiable() before using this."""
2158*90c36d8aSUlf Magnusson        return _get_visibility(self)
2159f219e013SMasahiro Yamada
2160f219e013SMasahiro Yamada    def get_referenced_symbols(self, refs_from_enclosing=False):
2161f219e013SMasahiro Yamada        """Returns the set() of all symbols referenced by this symbol. For
2162f219e013SMasahiro Yamada        example, the symbol defined by
2163f219e013SMasahiro Yamada
2164f219e013SMasahiro Yamada        config FOO
2165f219e013SMasahiro Yamada            bool
2166f219e013SMasahiro Yamada            prompt "foo" if A && B
2167f219e013SMasahiro Yamada            default C if D
2168f219e013SMasahiro Yamada            depends on E
2169f219e013SMasahiro Yamada            select F if G
2170f219e013SMasahiro Yamada
2171f219e013SMasahiro Yamada        references the symbols A through G.
2172f219e013SMasahiro Yamada
2173*90c36d8aSUlf Magnusson        refs_from_enclosing (default: False): If True, the symbols referenced
2174*90c36d8aSUlf Magnusson           by enclosing menus and ifs will be included in the result."""
2175*90c36d8aSUlf Magnusson        return self.all_referenced_syms if refs_from_enclosing else \
2176*90c36d8aSUlf Magnusson               self.referenced_syms
2177f219e013SMasahiro Yamada
2178f219e013SMasahiro Yamada    def get_selected_symbols(self):
2179f219e013SMasahiro Yamada        """Returns the set() of all symbols X for which this symbol has a
2180f219e013SMasahiro Yamada        'select X' or 'select X if Y' (regardless of whether Y is satisfied or
2181f219e013SMasahiro Yamada        not). This is a subset of the symbols returned by
2182f219e013SMasahiro Yamada        get_referenced_symbols()."""
2183f219e013SMasahiro Yamada        return self.selected_syms
2184f219e013SMasahiro Yamada
2185*90c36d8aSUlf Magnusson    def set_user_value(self, v):
2186*90c36d8aSUlf Magnusson        """Sets the user value of the symbol.
2187f219e013SMasahiro Yamada
2188*90c36d8aSUlf Magnusson        Equal in effect to assigning the value to the symbol within a .config
2189*90c36d8aSUlf Magnusson        file. Use get_lower/upper_bound() or get_assignable_values() to find
2190*90c36d8aSUlf Magnusson        the range of currently assignable values for bool and tristate symbols;
2191*90c36d8aSUlf Magnusson        setting values outside this range will cause the user value to differ
2192*90c36d8aSUlf Magnusson        from the result of Symbol.get_value() (be truncated). Values that are
2193*90c36d8aSUlf Magnusson        invalid for the type (such as a_bool.set_user_value("foo")) are
2194*90c36d8aSUlf Magnusson        ignored, and a warning is emitted if an attempt is made to assign such
2195*90c36d8aSUlf Magnusson        a value.
2196f219e013SMasahiro Yamada
2197*90c36d8aSUlf Magnusson        For any type of symbol, is_modifiable() can be used to check if a user
2198*90c36d8aSUlf Magnusson        value will currently have any effect on the symbol, as determined by
2199*90c36d8aSUlf Magnusson        its visibility and range of assignable values. Any value that is valid
2200*90c36d8aSUlf Magnusson        for the type (bool, tristate, etc.) will end up being reflected in
2201*90c36d8aSUlf Magnusson        get_user_value() though, and might have an effect later if conditions
2202*90c36d8aSUlf Magnusson        change. To get rid of the user value, use unset_user_value().
2203f219e013SMasahiro Yamada
2204*90c36d8aSUlf Magnusson        Any symbols dependent on the symbol are (recursively) invalidated, so
2205*90c36d8aSUlf Magnusson        things will just work with regards to dependencies.
2206f219e013SMasahiro Yamada
2207*90c36d8aSUlf Magnusson        v: The user value to give to the symbol."""
2208*90c36d8aSUlf Magnusson        self._set_user_value_no_invalidate(v, False)
2209f219e013SMasahiro Yamada
2210*90c36d8aSUlf Magnusson        # There might be something more efficient you could do here, but play
2211*90c36d8aSUlf Magnusson        # it safe.
2212*90c36d8aSUlf Magnusson        if self.name == "MODULES":
2213*90c36d8aSUlf Magnusson            self.config._invalidate_all()
2214*90c36d8aSUlf Magnusson            return
2215f219e013SMasahiro Yamada
2216*90c36d8aSUlf Magnusson        self._invalidate()
2217*90c36d8aSUlf Magnusson        self._invalidate_dependent()
2218f219e013SMasahiro Yamada
2219*90c36d8aSUlf Magnusson    def unset_user_value(self):
2220*90c36d8aSUlf Magnusson        """Resets the user value of the symbol, as if the symbol had never
2221*90c36d8aSUlf Magnusson        gotten a user value via Config.load_config() or
2222*90c36d8aSUlf Magnusson        Symbol.set_user_value()."""
2223*90c36d8aSUlf Magnusson        self._unset_user_value_no_recursive_invalidate()
2224*90c36d8aSUlf Magnusson        self._invalidate_dependent()
2225f219e013SMasahiro Yamada
2226f219e013SMasahiro Yamada    def is_modifiable(self):
2227f219e013SMasahiro Yamada        """Returns True if the value of the symbol could be modified by calling
2228*90c36d8aSUlf Magnusson        Symbol.set_user_value().
2229f219e013SMasahiro Yamada
2230f219e013SMasahiro Yamada        For bools and tristates, this corresponds to the symbol being visible
2231f219e013SMasahiro Yamada        in the 'make menuconfig' interface and not already being pinned to a
2232f219e013SMasahiro Yamada        specific value (e.g. because it is selected by another symbol).
2233f219e013SMasahiro Yamada
2234f219e013SMasahiro Yamada        For strings and numbers, this corresponds to just being visible. (See
2235f219e013SMasahiro Yamada        Symbol.get_visibility().)"""
2236f219e013SMasahiro Yamada        if self.is_special_:
2237f219e013SMasahiro Yamada            return False
2238f219e013SMasahiro Yamada        if self.type == BOOL or self.type == TRISTATE:
2239f219e013SMasahiro Yamada            rev_dep = self.config._eval_expr(self.rev_dep)
2240*90c36d8aSUlf Magnusson            # A bool selected to "m" gets promoted to "y", pinning it
2241*90c36d8aSUlf Magnusson            if rev_dep == "m" and self.type == BOOL:
2242*90c36d8aSUlf Magnusson                return False
2243*90c36d8aSUlf Magnusson            return TRI_TO_INT[_get_visibility(self)] > TRI_TO_INT[rev_dep]
2244*90c36d8aSUlf Magnusson        return _get_visibility(self) != "n"
2245f219e013SMasahiro Yamada
2246f219e013SMasahiro Yamada    def is_defined(self):
2247f219e013SMasahiro Yamada        """Returns False if the symbol is referred to in the Kconfig but never
2248*90c36d8aSUlf Magnusson        actually defined."""
2249f219e013SMasahiro Yamada        return self.is_defined_
2250f219e013SMasahiro Yamada
2251f219e013SMasahiro Yamada    def is_special(self):
2252f219e013SMasahiro Yamada        """Returns True if the symbol is one of the special symbols n, m, y, or
2253*90c36d8aSUlf Magnusson        UNAME_RELEASE, or gets its value from the environment."""
2254f219e013SMasahiro Yamada        return self.is_special_
2255f219e013SMasahiro Yamada
2256f219e013SMasahiro Yamada    def is_from_environment(self):
2257*90c36d8aSUlf Magnusson        """Returns True if the symbol gets its value from the environment."""
2258f219e013SMasahiro Yamada        return self.is_from_env
2259f219e013SMasahiro Yamada
2260f219e013SMasahiro Yamada    def has_ranges(self):
2261f219e013SMasahiro Yamada        """Returns True if the symbol is of type INT or HEX and has ranges that
2262*90c36d8aSUlf Magnusson        limit what values it can take on."""
2263*90c36d8aSUlf Magnusson        return bool(self.ranges)
2264f219e013SMasahiro Yamada
2265f219e013SMasahiro Yamada    def is_choice_symbol(self):
2266f219e013SMasahiro Yamada        """Returns True if the symbol is in a choice statement and is an actual
2267*90c36d8aSUlf Magnusson        choice symbol (see Choice.get_symbols())."""
2268*90c36d8aSUlf Magnusson        return self.is_choice_sym
2269f219e013SMasahiro Yamada
2270f219e013SMasahiro Yamada    def is_choice_selection(self):
2271f219e013SMasahiro Yamada        """Returns True if the symbol is contained in a choice statement and is
2272*90c36d8aSUlf Magnusson        the selected item. Equivalent to
2273*90c36d8aSUlf Magnusson
2274*90c36d8aSUlf Magnusson        sym.is_choice_symbol() and sym.get_parent().get_selection() is sym"""
2275*90c36d8aSUlf Magnusson        return self.is_choice_sym and self.parent.get_selection() is self
2276f219e013SMasahiro Yamada
22779d01b787SMasahiro Yamada    def is_allnoconfig_y(self):
2278*90c36d8aSUlf Magnusson        """Returns True if the symbol has the 'allnoconfig_y' option set."""
22799d01b787SMasahiro Yamada        return self.allnoconfig_y
22809d01b787SMasahiro Yamada
2281f219e013SMasahiro Yamada    def __str__(self):
2282f219e013SMasahiro Yamada        """Returns a string containing various information about the symbol."""
2283f219e013SMasahiro Yamada        return self.config._get_sym_or_choice_str(self)
2284f219e013SMasahiro Yamada
2285f219e013SMasahiro Yamada    #
2286f219e013SMasahiro Yamada    # Private methods
2287f219e013SMasahiro Yamada    #
2288f219e013SMasahiro Yamada
2289f219e013SMasahiro Yamada    def __init__(self):
2290f219e013SMasahiro Yamada        """Symbol constructor -- not intended to be called directly by
2291*90c36d8aSUlf Magnusson        Kconfiglib clients."""
2292f219e013SMasahiro Yamada
2293f219e013SMasahiro Yamada        self.name = None
2294f219e013SMasahiro Yamada        self.type = UNKNOWN
2295*90c36d8aSUlf Magnusson        self.prompts = []
2296*90c36d8aSUlf Magnusson        self.def_exprs = [] # 'default' properties
2297*90c36d8aSUlf Magnusson        self.ranges = [] # 'range' properties (for int and hex)
2298*90c36d8aSUlf Magnusson        self.help = None # Help text
2299*90c36d8aSUlf Magnusson        self.rev_dep = "n" # Reverse (select-related) dependencies
2300*90c36d8aSUlf Magnusson        self.config = None
2301*90c36d8aSUlf Magnusson        self.parent = None
2302f219e013SMasahiro Yamada
2303*90c36d8aSUlf Magnusson        self.user_val = None # Value set by user
2304f219e013SMasahiro Yamada
2305f219e013SMasahiro Yamada        # The prompt, default value and select conditions without any
2306*90c36d8aSUlf Magnusson        # dependencies from menus and ifs propagated to them
2307f219e013SMasahiro Yamada        self.orig_prompts = []
2308f219e013SMasahiro Yamada        self.orig_def_exprs = []
2309f219e013SMasahiro Yamada        self.orig_selects = []
2310f219e013SMasahiro Yamada
2311*90c36d8aSUlf Magnusson        # Dependencies inherited from containing menus and ifs
2312f219e013SMasahiro Yamada        self.deps_from_containing = None
2313f219e013SMasahiro Yamada        # The set of symbols referenced by this symbol (see
2314f219e013SMasahiro Yamada        # get_referenced_symbols())
2315f219e013SMasahiro Yamada        self.referenced_syms = set()
2316f219e013SMasahiro Yamada        # The set of symbols selected by this symbol (see
2317f219e013SMasahiro Yamada        # get_selected_symbols())
2318f219e013SMasahiro Yamada        self.selected_syms = set()
2319f219e013SMasahiro Yamada        # Like 'referenced_syms', but includes symbols from
2320*90c36d8aSUlf Magnusson        # dependencies inherited from enclosing menus and ifs
2321f219e013SMasahiro Yamada        self.all_referenced_syms = set()
2322f219e013SMasahiro Yamada
2323f219e013SMasahiro Yamada        # This records only dependencies specified with 'depends on'. Needed
2324f219e013SMasahiro Yamada        # when determining actual choice items (hrrrr...). See also
2325f219e013SMasahiro Yamada        # Choice._determine_actual_symbols().
2326f219e013SMasahiro Yamada        self.menu_dep = None
2327f219e013SMasahiro Yamada
2328f219e013SMasahiro Yamada        # See Symbol.get_ref/def_locations().
2329f219e013SMasahiro Yamada        self.def_locations = []
2330f219e013SMasahiro Yamada        self.ref_locations = []
2331f219e013SMasahiro Yamada
2332*90c36d8aSUlf Magnusson        # Populated in Config._build_dep() after parsing. Links the symbol to
2333*90c36d8aSUlf Magnusson        # the symbols that immediately depend on it (in a caching/invalidation
2334*90c36d8aSUlf Magnusson        # sense). The total set of dependent symbols for the symbol (the
2335*90c36d8aSUlf Magnusson        # transitive closure) is calculated on an as-needed basis in
2336*90c36d8aSUlf Magnusson        # _get_dependent().
2337*90c36d8aSUlf Magnusson        self.dep = set()
2338f219e013SMasahiro Yamada
2339*90c36d8aSUlf Magnusson        # Cached values
2340f219e013SMasahiro Yamada
2341f219e013SMasahiro Yamada        # Caches the calculated value
2342*90c36d8aSUlf Magnusson        self.cached_val = None
2343*90c36d8aSUlf Magnusson        # Caches the visibility, which acts as an upper bound on the value
2344*90c36d8aSUlf Magnusson        self.cached_visibility = None
2345f219e013SMasahiro Yamada        # Caches the total list of dependent symbols. Calculated in
2346f219e013SMasahiro Yamada        # _get_dependent().
2347f219e013SMasahiro Yamada        self.cached_deps = None
2348f219e013SMasahiro Yamada
2349*90c36d8aSUlf Magnusson        # Flags
2350*90c36d8aSUlf Magnusson
2351f219e013SMasahiro Yamada        # Does the symbol have an entry in the Kconfig file? The trailing
2352f219e013SMasahiro Yamada        # underscore avoids a collision with is_defined().
2353f219e013SMasahiro Yamada        self.is_defined_ = False
2354*90c36d8aSUlf Magnusson        # Should the symbol get an entry in .config?
2355*90c36d8aSUlf Magnusson        self.write_to_conf = False
2356*90c36d8aSUlf Magnusson        # Set to true when _make_conf() is called on a symbol, so that symbols
2357*90c36d8aSUlf Magnusson        # defined in multiple locations only get one .config entry. We need to
2358*90c36d8aSUlf Magnusson        # reset it prior to writing out a new .config.
2359*90c36d8aSUlf Magnusson        self.already_written = False
2360*90c36d8aSUlf Magnusson        # This is set to True for "actual" choice symbols; see
2361*90c36d8aSUlf Magnusson        # Choice._determine_actual_symbols().
2362*90c36d8aSUlf Magnusson        self.is_choice_sym = False
2363f219e013SMasahiro Yamada        # Does the symbol get its value in some special way, e.g. from the
2364f219e013SMasahiro Yamada        # environment or by being one of the special symbols n, m, and y? If
2365*90c36d8aSUlf Magnusson        # so, the value is stored in self.cached_val, which is never
2366f219e013SMasahiro Yamada        # invalidated. The trailing underscore avoids a collision with
2367f219e013SMasahiro Yamada        # is_special().
2368f219e013SMasahiro Yamada        self.is_special_ = False
2369f219e013SMasahiro Yamada        # Does the symbol get its value from the environment?
2370f219e013SMasahiro Yamada        self.is_from_env = False
23719d01b787SMasahiro Yamada        # Does the symbol have the 'allnoconfig_y' option set?
23729d01b787SMasahiro Yamada        self.allnoconfig_y = False
23739d01b787SMasahiro Yamada
2374f219e013SMasahiro Yamada    def _invalidate(self):
2375f219e013SMasahiro Yamada        if self.is_special_:
2376f219e013SMasahiro Yamada            return
2377f219e013SMasahiro Yamada
2378*90c36d8aSUlf Magnusson        if self.is_choice_sym:
2379f219e013SMasahiro Yamada            self.parent._invalidate()
2380f219e013SMasahiro Yamada
2381*90c36d8aSUlf Magnusson        self.cached_val = None
2382*90c36d8aSUlf Magnusson        self.cached_visibility = None
2383f219e013SMasahiro Yamada
2384f219e013SMasahiro Yamada    def _invalidate_dependent(self):
2385f219e013SMasahiro Yamada        for sym in self._get_dependent():
2386f219e013SMasahiro Yamada            sym._invalidate()
2387f219e013SMasahiro Yamada
2388f219e013SMasahiro Yamada    def _set_user_value_no_invalidate(self, v, suppress_load_warnings):
2389f219e013SMasahiro Yamada        """Like set_user_value(), but does not invalidate any symbols.
2390f219e013SMasahiro Yamada
2391*90c36d8aSUlf Magnusson        suppress_load_warnings: some warnings are annoying when loading a
2392*90c36d8aSUlf Magnusson           .config that can be helpful when manually invoking set_user_value().
2393*90c36d8aSUlf Magnusson           This flag is set to True to suppress such warnings.
2394f219e013SMasahiro Yamada
2395f219e013SMasahiro Yamada           Perhaps this could be made optional for load_config() instead."""
2396f219e013SMasahiro Yamada
2397f219e013SMasahiro Yamada        if self.is_special_:
2398f219e013SMasahiro Yamada            if self.is_from_env:
2399f219e013SMasahiro Yamada                self.config._warn('attempt to assign the value "{0}" to the '
2400f219e013SMasahiro Yamada                                  'symbol {1}, which gets its value from the '
2401f219e013SMasahiro Yamada                                  'environment. Assignment ignored.'
2402f219e013SMasahiro Yamada                                  .format(v, self.name))
2403f219e013SMasahiro Yamada            else:
2404f219e013SMasahiro Yamada                self.config._warn('attempt to assign the value "{0}" to the '
2405f219e013SMasahiro Yamada                                  'special symbol {1}. Assignment ignored.'
2406f219e013SMasahiro Yamada                                  .format(v, self.name))
2407f219e013SMasahiro Yamada            return
2408f219e013SMasahiro Yamada
2409f219e013SMasahiro Yamada        if not self.is_defined_:
2410f219e013SMasahiro Yamada            filename, linenr = self.ref_locations[0]
2411*90c36d8aSUlf Magnusson            if self.config.print_undef_assign:
2412*90c36d8aSUlf Magnusson                _stderr_msg('note: attempt to assign the value "{0}" to {1}, '
2413f219e013SMasahiro Yamada                            "which is referenced at {2}:{3} but never "
2414f219e013SMasahiro Yamada                            "defined. Assignment ignored."
2415f219e013SMasahiro Yamada                            .format(v, self.name, filename, linenr))
2416f219e013SMasahiro Yamada            return
2417f219e013SMasahiro Yamada
2418f219e013SMasahiro Yamada        # Check if the value is valid for our type
2419*90c36d8aSUlf Magnusson        if not ((self.type == BOOL     and (v == "y" or v == "n")   ) or
2420*90c36d8aSUlf Magnusson                (self.type == TRISTATE and (v == "y" or v == "m" or
2421*90c36d8aSUlf Magnusson                                            v == "n")               ) or
2422f219e013SMasahiro Yamada                (self.type == STRING                                ) or
2423f219e013SMasahiro Yamada                (self.type == INT      and _is_base_n(v, 10)        ) or
2424f219e013SMasahiro Yamada                (self.type == HEX      and _is_base_n(v, 16)        )):
2425*90c36d8aSUlf Magnusson            self.config._warn('the value "{0}" is invalid for {1}, which has '
2426*90c36d8aSUlf Magnusson                              "type {2}. Assignment ignored."
2427*90c36d8aSUlf Magnusson                              .format(v, self.name, TYPENAME[self.type]))
2428f219e013SMasahiro Yamada            return
2429f219e013SMasahiro Yamada
2430*90c36d8aSUlf Magnusson        if not self.prompts and not suppress_load_warnings:
2431f219e013SMasahiro Yamada            self.config._warn('assigning "{0}" to the symbol {1} which '
2432f219e013SMasahiro Yamada                              'lacks prompts and thus has visibility "n". '
2433f219e013SMasahiro Yamada                              'The assignment will have no effect.'
2434f219e013SMasahiro Yamada                              .format(v, self.name))
2435f219e013SMasahiro Yamada
2436f219e013SMasahiro Yamada        self.user_val = v
2437f219e013SMasahiro Yamada
2438*90c36d8aSUlf Magnusson        if self.is_choice_sym and (self.type == BOOL or self.type == TRISTATE):
2439f219e013SMasahiro Yamada            choice = self.parent
2440f219e013SMasahiro Yamada            if v == "y":
2441f219e013SMasahiro Yamada                choice.user_val = self
2442f219e013SMasahiro Yamada                choice.user_mode = "y"
2443f219e013SMasahiro Yamada            elif v == "m":
2444f219e013SMasahiro Yamada                choice.user_val = None
2445f219e013SMasahiro Yamada                choice.user_mode = "m"
2446f219e013SMasahiro Yamada
2447f219e013SMasahiro Yamada    def _unset_user_value_no_recursive_invalidate(self):
2448f219e013SMasahiro Yamada        self._invalidate()
2449f219e013SMasahiro Yamada        self.user_val = None
2450f219e013SMasahiro Yamada
2451*90c36d8aSUlf Magnusson        if self.is_choice_sym:
2452f219e013SMasahiro Yamada            self.parent._unset_user_value()
2453f219e013SMasahiro Yamada
2454*90c36d8aSUlf Magnusson    def _make_conf(self, append_fn):
2455f219e013SMasahiro Yamada        if self.already_written:
2456*90c36d8aSUlf Magnusson            return
2457f219e013SMasahiro Yamada
2458f219e013SMasahiro Yamada        self.already_written = True
2459f219e013SMasahiro Yamada
2460f219e013SMasahiro Yamada        # Note: write_to_conf is determined in get_value()
2461f219e013SMasahiro Yamada        val = self.get_value()
2462f219e013SMasahiro Yamada        if not self.write_to_conf:
2463*90c36d8aSUlf Magnusson            return
2464f219e013SMasahiro Yamada
2465f219e013SMasahiro Yamada        if self.type == BOOL or self.type == TRISTATE:
2466*90c36d8aSUlf Magnusson            if val == "y" or val == "m":
2467*90c36d8aSUlf Magnusson                append_fn("CONFIG_{0}={1}".format(self.name, val))
2468*90c36d8aSUlf Magnusson            else:
2469*90c36d8aSUlf Magnusson                append_fn("# CONFIG_{0} is not set".format(self.name))
2470*90c36d8aSUlf Magnusson
2471*90c36d8aSUlf Magnusson        elif self.type == INT or self.type == HEX:
2472*90c36d8aSUlf Magnusson            append_fn("CONFIG_{0}={1}".format(self.name, val))
2473f219e013SMasahiro Yamada
2474f219e013SMasahiro Yamada        elif self.type == STRING:
2475f219e013SMasahiro Yamada            # Escape \ and "
2476*90c36d8aSUlf Magnusson            append_fn('CONFIG_{0}="{1}"'
2477f219e013SMasahiro Yamada                      .format(self.name,
2478*90c36d8aSUlf Magnusson                              val.replace("\\", "\\\\").replace('"', '\\"')))
2479f219e013SMasahiro Yamada
2480f219e013SMasahiro Yamada        else:
2481*90c36d8aSUlf Magnusson            _internal_error("Internal error while creating .config: unknown "
2482*90c36d8aSUlf Magnusson                            'type "{0}".'.format(self.type))
2483f219e013SMasahiro Yamada
2484f219e013SMasahiro Yamada    def _get_dependent(self):
2485f219e013SMasahiro Yamada        """Returns the set of symbols that should be invalidated if the value
2486f219e013SMasahiro Yamada        of the symbol changes, because they might be affected by the change.
2487f219e013SMasahiro Yamada        Note that this is an internal API -- it's probably of limited
2488f219e013SMasahiro Yamada        usefulness to clients."""
2489f219e013SMasahiro Yamada        if self.cached_deps is not None:
2490f219e013SMasahiro Yamada            return self.cached_deps
2491f219e013SMasahiro Yamada
2492*90c36d8aSUlf Magnusson        res = set(self.dep)
2493*90c36d8aSUlf Magnusson        for s in self.dep:
2494*90c36d8aSUlf Magnusson            res |= s._get_dependent()
2495f219e013SMasahiro Yamada
2496*90c36d8aSUlf Magnusson        if self.is_choice_sym:
2497*90c36d8aSUlf Magnusson            # Choice symbols also depend (recursively) on their siblings. The
2498*90c36d8aSUlf Magnusson            # siblings are not included in 'dep' to avoid dependency loops.
2499*90c36d8aSUlf Magnusson            for sibling in self.parent.actual_symbols:
2500*90c36d8aSUlf Magnusson                if sibling is not self:
2501*90c36d8aSUlf Magnusson                    res.add(sibling)
2502*90c36d8aSUlf Magnusson                    res |= sibling.dep
2503*90c36d8aSUlf Magnusson                    for s in sibling.dep:
2504*90c36d8aSUlf Magnusson                        res |= s._get_dependent()
2505f219e013SMasahiro Yamada
2506f219e013SMasahiro Yamada        self.cached_deps = res
2507f219e013SMasahiro Yamada        return res
2508f219e013SMasahiro Yamada
2509f219e013SMasahiro Yamada    def _has_auto_menu_dep_on(self, on):
2510f219e013SMasahiro Yamada        """See Choice._determine_actual_symbols()."""
2511f219e013SMasahiro Yamada        if not isinstance(self.parent, Choice):
2512*90c36d8aSUlf Magnusson            _internal_error("Attempt to determine auto menu dependency for "
2513*90c36d8aSUlf Magnusson                            "symbol ouside of choice.")
2514f219e013SMasahiro Yamada
2515*90c36d8aSUlf Magnusson        if not self.prompts:
2516f219e013SMasahiro Yamada            # If we have no prompt, use the menu dependencies instead (what was
2517f219e013SMasahiro Yamada            # specified with 'depends on')
2518f219e013SMasahiro Yamada            return self.menu_dep is not None and \
2519f219e013SMasahiro Yamada                   self.config._expr_depends_on(self.menu_dep, on)
2520f219e013SMasahiro Yamada
2521*90c36d8aSUlf Magnusson        for _, cond_expr in self.prompts:
2522f219e013SMasahiro Yamada            if self.config._expr_depends_on(cond_expr, on):
2523f219e013SMasahiro Yamada                return True
2524f219e013SMasahiro Yamada
2525f219e013SMasahiro Yamada        return False
2526f219e013SMasahiro Yamada
2527f219e013SMasahiro Yamadaclass Menu(Item):
2528f219e013SMasahiro Yamada
2529f219e013SMasahiro Yamada    """Represents a menu statement."""
2530f219e013SMasahiro Yamada
2531f219e013SMasahiro Yamada    #
2532f219e013SMasahiro Yamada    # Public interface
2533f219e013SMasahiro Yamada    #
2534f219e013SMasahiro Yamada
2535f219e013SMasahiro Yamada    def get_config(self):
2536f219e013SMasahiro Yamada        """Return the Config instance this menu is from."""
2537f219e013SMasahiro Yamada        return self.config
2538f219e013SMasahiro Yamada
2539f219e013SMasahiro Yamada    def get_title(self):
2540f219e013SMasahiro Yamada        """Returns the title text of the menu."""
2541f219e013SMasahiro Yamada        return self.title
2542f219e013SMasahiro Yamada
2543f219e013SMasahiro Yamada    def get_parent(self):
2544f219e013SMasahiro Yamada        """Returns the menu or choice statement that contains the menu, or
2545f219e013SMasahiro Yamada        None if the menu is at the top level. Note that if statements are
2546f219e013SMasahiro Yamada        treated as syntactic sugar and do not have an explicit class
2547f219e013SMasahiro Yamada        representation."""
2548f219e013SMasahiro Yamada        return self.parent
2549f219e013SMasahiro Yamada
2550f219e013SMasahiro Yamada    def get_location(self):
2551f219e013SMasahiro Yamada        """Returns the location of the menu as a (filename, linenr) tuple,
2552f219e013SMasahiro Yamada        where filename is a string and linenr an int."""
2553f219e013SMasahiro Yamada        return (self.filename, self.linenr)
2554f219e013SMasahiro Yamada
2555*90c36d8aSUlf Magnusson    def get_items(self, recursive=False):
2556*90c36d8aSUlf Magnusson        """Returns a list containing the items (symbols, menus, choice
2557*90c36d8aSUlf Magnusson        statements and comments) in in the menu, in the same order that the
2558*90c36d8aSUlf Magnusson        items appear within the menu.
2559*90c36d8aSUlf Magnusson
2560*90c36d8aSUlf Magnusson        recursive (default: False): True if items contained in items within the
2561*90c36d8aSUlf Magnusson           menu should be included recursively (preorder)."""
2562*90c36d8aSUlf Magnusson
2563*90c36d8aSUlf Magnusson        if not recursive:
2564*90c36d8aSUlf Magnusson            return self.block
2565*90c36d8aSUlf Magnusson
2566*90c36d8aSUlf Magnusson        res = []
2567*90c36d8aSUlf Magnusson        for item in self.block:
2568*90c36d8aSUlf Magnusson            res.append(item)
2569*90c36d8aSUlf Magnusson            if isinstance(item, Menu):
2570*90c36d8aSUlf Magnusson                res.extend(item.get_items(True))
2571*90c36d8aSUlf Magnusson            elif isinstance(item, Choice):
2572*90c36d8aSUlf Magnusson                res.extend(item.get_items())
2573*90c36d8aSUlf Magnusson        return res
2574*90c36d8aSUlf Magnusson
2575*90c36d8aSUlf Magnusson    def get_symbols(self, recursive=False):
2576*90c36d8aSUlf Magnusson        """Returns a list containing the symbols in the menu, in the same order
2577*90c36d8aSUlf Magnusson        that they appear within the menu.
2578*90c36d8aSUlf Magnusson
2579*90c36d8aSUlf Magnusson        recursive (default: False): True if symbols contained in items within
2580*90c36d8aSUlf Magnusson           the menu should be included recursively."""
2581*90c36d8aSUlf Magnusson
2582*90c36d8aSUlf Magnusson        return [item for item in self.get_items(recursive) if
2583*90c36d8aSUlf Magnusson                isinstance(item, Symbol)]
2584*90c36d8aSUlf Magnusson
2585*90c36d8aSUlf Magnusson    def get_visibility(self):
2586*90c36d8aSUlf Magnusson        """Returns the visibility of the menu. This also affects the visibility
2587*90c36d8aSUlf Magnusson        of subitems. See also Symbol.get_visibility()."""
2588*90c36d8aSUlf Magnusson        return self.config._eval_expr(self.dep_expr)
2589*90c36d8aSUlf Magnusson
2590*90c36d8aSUlf Magnusson    def get_visible_if_visibility(self):
2591*90c36d8aSUlf Magnusson        """Returns the visibility the menu gets from its 'visible if'
2592*90c36d8aSUlf Magnusson        condition. "y" if the menu has no 'visible if' condition."""
2593*90c36d8aSUlf Magnusson        return self.config._eval_expr(self.visible_if_expr)
2594*90c36d8aSUlf Magnusson
2595*90c36d8aSUlf Magnusson    def get_referenced_symbols(self, refs_from_enclosing=False):
2596*90c36d8aSUlf Magnusson        """See Symbol.get_referenced_symbols()."""
2597*90c36d8aSUlf Magnusson        return self.all_referenced_syms if refs_from_enclosing else \
2598*90c36d8aSUlf Magnusson               self.referenced_syms
2599*90c36d8aSUlf Magnusson
2600f219e013SMasahiro Yamada    def __str__(self):
2601f219e013SMasahiro Yamada        """Returns a string containing various information about the menu."""
2602f219e013SMasahiro Yamada        depends_on_str = self.config._expr_val_str(self.orig_deps,
2603f219e013SMasahiro Yamada                                                   "(no dependencies)")
2604f219e013SMasahiro Yamada        visible_if_str = self.config._expr_val_str(self.visible_if_expr,
2605f219e013SMasahiro Yamada                                                   "(no dependencies)")
2606f219e013SMasahiro Yamada
2607*90c36d8aSUlf Magnusson        additional_deps_str = " " + \
2608*90c36d8aSUlf Magnusson          self.config._expr_val_str(self.deps_from_containing,
2609f219e013SMasahiro Yamada                                    "(no additional dependencies)")
2610f219e013SMasahiro Yamada
2611*90c36d8aSUlf Magnusson        return _lines("Menu",
2612f219e013SMasahiro Yamada                      "Title                     : " + self.title,
2613f219e013SMasahiro Yamada                      "'depends on' dependencies : " + depends_on_str,
2614f219e013SMasahiro Yamada                      "'visible if' dependencies : " + visible_if_str,
2615*90c36d8aSUlf Magnusson                      "Additional dependencies from enclosing menus and "
2616*90c36d8aSUlf Magnusson                        "ifs:",
2617f219e013SMasahiro Yamada                      additional_deps_str,
2618f219e013SMasahiro Yamada                      "Location: {0}:{1}".format(self.filename, self.linenr))
2619f219e013SMasahiro Yamada
2620f219e013SMasahiro Yamada    #
2621f219e013SMasahiro Yamada    # Private methods
2622f219e013SMasahiro Yamada    #
2623f219e013SMasahiro Yamada
2624f219e013SMasahiro Yamada    def __init__(self):
2625f219e013SMasahiro Yamada        """Menu constructor -- not intended to be called directly by
2626*90c36d8aSUlf Magnusson        Kconfiglib clients."""
2627f219e013SMasahiro Yamada
2628f219e013SMasahiro Yamada        self.title = None
2629f219e013SMasahiro Yamada        self.dep_expr = None
2630*90c36d8aSUlf Magnusson        self.visible_if_expr = None
2631*90c36d8aSUlf Magnusson        self.block = None
2632*90c36d8aSUlf Magnusson        self.config = None
2633*90c36d8aSUlf Magnusson        self.parent = None
2634f219e013SMasahiro Yamada
2635f219e013SMasahiro Yamada        # Dependency expression without dependencies from enclosing menus and
2636*90c36d8aSUlf Magnusson        # ifs propagated
2637f219e013SMasahiro Yamada        self.orig_deps = None
2638f219e013SMasahiro Yamada
2639*90c36d8aSUlf Magnusson        # Dependencies inherited from containing menus and ifs
2640f219e013SMasahiro Yamada        self.deps_from_containing = None
2641f219e013SMasahiro Yamada        # The set of symbols referenced by this menu (see
2642f219e013SMasahiro Yamada        # get_referenced_symbols())
2643f219e013SMasahiro Yamada        self.referenced_syms = set()
2644f219e013SMasahiro Yamada        # Like 'referenced_syms', but includes symbols from
2645*90c36d8aSUlf Magnusson        # dependencies inherited from enclosing menus and ifs
2646f219e013SMasahiro Yamada        self.all_referenced_syms = None
2647f219e013SMasahiro Yamada
2648f219e013SMasahiro Yamada        self.filename = None
2649f219e013SMasahiro Yamada        self.linenr = None
2650f219e013SMasahiro Yamada
2651*90c36d8aSUlf Magnusson    def _make_conf(self, append_fn):
2652f219e013SMasahiro Yamada        if self.config._eval_expr(self.dep_expr) != "n" and \
2653f219e013SMasahiro Yamada           self.config._eval_expr(self.visible_if_expr) != "n":
2654*90c36d8aSUlf Magnusson            append_fn("\n#\n# {0}\n#".format(self.title))
2655*90c36d8aSUlf Magnusson        _make_block_conf(self.block, append_fn)
2656f219e013SMasahiro Yamada
2657*90c36d8aSUlf Magnussonclass Choice(Item):
2658f219e013SMasahiro Yamada
2659f219e013SMasahiro Yamada    """Represents a choice statement. A choice can be in one of three modes:
2660f219e013SMasahiro Yamada
2661f219e013SMasahiro Yamada    "n" - The choice is not visible and no symbols can be selected.
2662f219e013SMasahiro Yamada
2663f219e013SMasahiro Yamada    "m" - Any number of symbols can be set to "m". The rest will be "n". This
2664f219e013SMasahiro Yamada          is safe since potentially conflicting options don't actually get
2665f219e013SMasahiro Yamada          compiled into the kernel simultaneously with "m".
2666f219e013SMasahiro Yamada
2667f219e013SMasahiro Yamada    "y" - One symbol will be "y" while the rest are "n".
2668f219e013SMasahiro Yamada
2669f219e013SMasahiro Yamada    Only tristate choices can be in "m" mode, and the visibility of the choice
2670f219e013SMasahiro Yamada    is an upper bound on the mode, so that e.g. a choice that depends on a
2671f219e013SMasahiro Yamada    symbol with value "m" will be in "m" mode.
2672f219e013SMasahiro Yamada
2673f219e013SMasahiro Yamada    The mode changes automatically when a value is assigned to a symbol within
2674f219e013SMasahiro Yamada    the choice.
2675f219e013SMasahiro Yamada
2676f219e013SMasahiro Yamada    See Symbol.get_visibility() too."""
2677f219e013SMasahiro Yamada
2678f219e013SMasahiro Yamada    #
2679f219e013SMasahiro Yamada    # Public interface
2680f219e013SMasahiro Yamada    #
2681f219e013SMasahiro Yamada
2682f219e013SMasahiro Yamada    def get_config(self):
2683f219e013SMasahiro Yamada        """Returns the Config instance this choice is from."""
2684f219e013SMasahiro Yamada        return self.config
2685f219e013SMasahiro Yamada
2686f219e013SMasahiro Yamada    def get_name(self):
2687f219e013SMasahiro Yamada        """For named choices, returns the name. Returns None for unnamed
2688f219e013SMasahiro Yamada        choices. No named choices appear anywhere in the kernel Kconfig files
2689f219e013SMasahiro Yamada        as of Linux 3.7.0-rc8."""
2690f219e013SMasahiro Yamada        return self.name
2691f219e013SMasahiro Yamada
2692*90c36d8aSUlf Magnusson    def get_type(self):
2693*90c36d8aSUlf Magnusson        """Returns the type of the choice. See Symbol.get_type()."""
2694*90c36d8aSUlf Magnusson        return self.type
2695*90c36d8aSUlf Magnusson
2696f219e013SMasahiro Yamada    def get_prompts(self):
2697f219e013SMasahiro Yamada        """Returns a list of prompts defined for the choice, in the order they
2698f219e013SMasahiro Yamada        appear in the configuration files. Returns the empty list for choices
2699f219e013SMasahiro Yamada        with no prompt.
2700f219e013SMasahiro Yamada
2701f219e013SMasahiro Yamada        This list will have a single entry for the vast majority of choices
2702f219e013SMasahiro Yamada        having prompts, but having multiple prompts for a single choice is
2703f219e013SMasahiro Yamada        possible through having multiple 'choice' entries for it (though I'm
2704f219e013SMasahiro Yamada        not sure if that ever happens in practice)."""
2705f219e013SMasahiro Yamada        return [prompt for prompt, _ in self.orig_prompts]
2706f219e013SMasahiro Yamada
2707f219e013SMasahiro Yamada    def get_help(self):
2708f219e013SMasahiro Yamada        """Returns the help text of the choice, or None if the choice has no
2709f219e013SMasahiro Yamada        help text."""
2710f219e013SMasahiro Yamada        return self.help
2711f219e013SMasahiro Yamada
2712*90c36d8aSUlf Magnusson    def get_parent(self):
2713*90c36d8aSUlf Magnusson        """Returns the menu or choice statement that contains the choice, or
2714*90c36d8aSUlf Magnusson        None if the choice is at the top level. Note that if statements are
2715*90c36d8aSUlf Magnusson        treated as syntactic sugar and do not have an explicit class
2716*90c36d8aSUlf Magnusson        representation."""
2717*90c36d8aSUlf Magnusson        return self.parent
2718*90c36d8aSUlf Magnusson
2719*90c36d8aSUlf Magnusson    def get_def_locations(self):
2720*90c36d8aSUlf Magnusson        """Returns a list of (filename, linenr) tuples, where filename (string)
2721*90c36d8aSUlf Magnusson        and linenr (int) represent a location where the choice is defined. For
2722*90c36d8aSUlf Magnusson        the vast majority of choices (all of them as of Linux 3.7.0-rc8) this
2723*90c36d8aSUlf Magnusson        list will only contain one element, but its possible for named choices
2724*90c36d8aSUlf Magnusson        to be defined in multiple locations."""
2725*90c36d8aSUlf Magnusson        return self.def_locations
2726*90c36d8aSUlf Magnusson
2727*90c36d8aSUlf Magnusson    def get_selection(self):
2728*90c36d8aSUlf Magnusson        """Returns the symbol selected (either by the user or through
2729*90c36d8aSUlf Magnusson        defaults), or None if either no symbol is selected or the mode is not
2730*90c36d8aSUlf Magnusson        "y"."""
2731*90c36d8aSUlf Magnusson        if self.cached_selection is not None:
2732*90c36d8aSUlf Magnusson            if self.cached_selection == NO_SELECTION:
2733*90c36d8aSUlf Magnusson                return None
2734*90c36d8aSUlf Magnusson            return self.cached_selection
2735*90c36d8aSUlf Magnusson
2736*90c36d8aSUlf Magnusson        if self.get_mode() != "y":
2737*90c36d8aSUlf Magnusson            return self._cache_ret(None)
2738*90c36d8aSUlf Magnusson
2739*90c36d8aSUlf Magnusson        # User choice available?
2740*90c36d8aSUlf Magnusson        if self.user_val is not None and _get_visibility(self.user_val) == "y":
2741*90c36d8aSUlf Magnusson            return self._cache_ret(self.user_val)
2742*90c36d8aSUlf Magnusson
2743*90c36d8aSUlf Magnusson        if self.optional:
2744*90c36d8aSUlf Magnusson            return self._cache_ret(None)
2745*90c36d8aSUlf Magnusson
2746*90c36d8aSUlf Magnusson        return self._cache_ret(self.get_selection_from_defaults())
2747*90c36d8aSUlf Magnusson
2748*90c36d8aSUlf Magnusson    def get_selection_from_defaults(self):
2749*90c36d8aSUlf Magnusson        """Like Choice.get_selection(), but acts as if no symbol has been
2750*90c36d8aSUlf Magnusson        selected by the user and no 'optional' flag is in effect."""
2751*90c36d8aSUlf Magnusson
2752*90c36d8aSUlf Magnusson        if not self.actual_symbols:
2753*90c36d8aSUlf Magnusson            return None
2754*90c36d8aSUlf Magnusson
2755*90c36d8aSUlf Magnusson        for symbol, cond_expr in self.def_exprs:
2756*90c36d8aSUlf Magnusson            if self.config._eval_expr(cond_expr) != "n":
2757*90c36d8aSUlf Magnusson                chosen_symbol = symbol
2758*90c36d8aSUlf Magnusson                break
2759*90c36d8aSUlf Magnusson        else:
2760*90c36d8aSUlf Magnusson            chosen_symbol = self.actual_symbols[0]
2761*90c36d8aSUlf Magnusson
2762*90c36d8aSUlf Magnusson        # Is the chosen symbol visible?
2763*90c36d8aSUlf Magnusson        if _get_visibility(chosen_symbol) != "n":
2764*90c36d8aSUlf Magnusson            return chosen_symbol
2765*90c36d8aSUlf Magnusson        # Otherwise, pick the first visible symbol
2766*90c36d8aSUlf Magnusson        for sym in self.actual_symbols:
2767*90c36d8aSUlf Magnusson            if _get_visibility(sym) != "n":
2768*90c36d8aSUlf Magnusson                return sym
2769*90c36d8aSUlf Magnusson        return None
2770*90c36d8aSUlf Magnusson
2771*90c36d8aSUlf Magnusson    def get_user_selection(self):
2772*90c36d8aSUlf Magnusson        """If the choice is in "y" mode and has a user-selected symbol, returns
2773*90c36d8aSUlf Magnusson        that symbol. Otherwise, returns None."""
2774*90c36d8aSUlf Magnusson        return self.user_val
2775f219e013SMasahiro Yamada
2776f219e013SMasahiro Yamada    def get_items(self):
2777f219e013SMasahiro Yamada        """Gets all items contained in the choice in the same order as within
2778f219e013SMasahiro Yamada        the configuration ("items" instead of "symbols" since choices and
2779f219e013SMasahiro Yamada        comments might appear within choices. This only happens in one place as
2780f219e013SMasahiro Yamada        of Linux 3.7.0-rc8, in drivers/usb/gadget/Kconfig)."""
2781*90c36d8aSUlf Magnusson        return self.block
2782f219e013SMasahiro Yamada
2783f219e013SMasahiro Yamada    def get_symbols(self):
2784f219e013SMasahiro Yamada        """Returns a list containing the choice's symbols.
2785f219e013SMasahiro Yamada
2786f219e013SMasahiro Yamada        A quirk (perhaps a bug) of Kconfig is that you can put items within a
2787f219e013SMasahiro Yamada        choice that will not be considered members of the choice insofar as
2788f219e013SMasahiro Yamada        selection is concerned. This happens for example if one symbol within a
2789f219e013SMasahiro Yamada        choice 'depends on' the symbol preceding it, or if you put non-symbol
2790f219e013SMasahiro Yamada        items within choices.
2791f219e013SMasahiro Yamada
2792f219e013SMasahiro Yamada        As of Linux 3.7.0-rc8, this seems to be used intentionally in one
2793f219e013SMasahiro Yamada        place: drivers/usb/gadget/Kconfig.
2794f219e013SMasahiro Yamada
2795f219e013SMasahiro Yamada        This function returns the "proper" symbols of the choice in the order
2796f219e013SMasahiro Yamada        they appear in the choice, excluding such items. If you want all items
2797f219e013SMasahiro Yamada        in the choice, use get_items()."""
2798f219e013SMasahiro Yamada        return self.actual_symbols
2799f219e013SMasahiro Yamada
2800f219e013SMasahiro Yamada    def get_referenced_symbols(self, refs_from_enclosing=False):
2801f219e013SMasahiro Yamada        """See Symbol.get_referenced_symbols()."""
2802*90c36d8aSUlf Magnusson        return self.all_referenced_syms if refs_from_enclosing else \
2803*90c36d8aSUlf Magnusson               self.referenced_syms
2804f219e013SMasahiro Yamada
2805f219e013SMasahiro Yamada    def get_visibility(self):
2806f219e013SMasahiro Yamada        """Returns the visibility of the choice statement: one of "n", "m" or
2807f219e013SMasahiro Yamada        "y". This acts as an upper limit on the mode of the choice (though bool
2808f219e013SMasahiro Yamada        choices can only have the mode "y"). See the class documentation for an
2809f219e013SMasahiro Yamada        explanation of modes."""
2810*90c36d8aSUlf Magnusson        return _get_visibility(self)
2811f219e013SMasahiro Yamada
2812f219e013SMasahiro Yamada    def get_mode(self):
2813f219e013SMasahiro Yamada        """Returns the mode of the choice. See the class documentation for
2814f219e013SMasahiro Yamada        an explanation of modes."""
2815f219e013SMasahiro Yamada        minimum_mode = "n" if self.optional else "m"
2816f219e013SMasahiro Yamada        mode = self.user_mode if self.user_mode is not None else minimum_mode
2817*90c36d8aSUlf Magnusson        mode = self.config._eval_min(mode, _get_visibility(self))
2818f219e013SMasahiro Yamada
2819f219e013SMasahiro Yamada        # Promote "m" to "y" for boolean choices
2820f219e013SMasahiro Yamada        if mode == "m" and self.type == BOOL:
2821f219e013SMasahiro Yamada            return "y"
2822f219e013SMasahiro Yamada
2823f219e013SMasahiro Yamada        return mode
2824f219e013SMasahiro Yamada
2825f219e013SMasahiro Yamada    def is_optional(self):
2826*90c36d8aSUlf Magnusson        """Returns True if the choice has the 'optional' flag set (and so will
2827*90c36d8aSUlf Magnusson        default to "n" mode)."""
2828f219e013SMasahiro Yamada        return self.optional
2829f219e013SMasahiro Yamada
2830f219e013SMasahiro Yamada    def __str__(self):
2831f219e013SMasahiro Yamada        """Returns a string containing various information about the choice
2832f219e013SMasahiro Yamada        statement."""
2833f219e013SMasahiro Yamada        return self.config._get_sym_or_choice_str(self)
2834f219e013SMasahiro Yamada
2835f219e013SMasahiro Yamada    #
2836f219e013SMasahiro Yamada    # Private methods
2837f219e013SMasahiro Yamada    #
2838f219e013SMasahiro Yamada
2839f219e013SMasahiro Yamada    def __init__(self):
2840f219e013SMasahiro Yamada        """Choice constructor -- not intended to be called directly by
2841*90c36d8aSUlf Magnusson        Kconfiglib clients."""
2842f219e013SMasahiro Yamada
2843f219e013SMasahiro Yamada        self.name = None # Yes, choices can be named
2844f219e013SMasahiro Yamada        self.type = UNKNOWN
2845*90c36d8aSUlf Magnusson        self.prompts = []
2846*90c36d8aSUlf Magnusson        self.def_exprs = [] # 'default' properties
2847*90c36d8aSUlf Magnusson        self.help = None # Help text
2848*90c36d8aSUlf Magnusson        self.block = None # List of contained items
2849*90c36d8aSUlf Magnusson        self.config = None
2850*90c36d8aSUlf Magnusson        self.parent = None
2851f219e013SMasahiro Yamada
2852*90c36d8aSUlf Magnusson        self.user_val = None
2853*90c36d8aSUlf Magnusson        self.user_mode = None
2854f219e013SMasahiro Yamada
2855f219e013SMasahiro Yamada        # We need to filter out symbols that appear within the choice block but
2856f219e013SMasahiro Yamada        # are not considered choice items (see
2857*90c36d8aSUlf Magnusson        # Choice._determine_actual_symbols()) This list holds the "actual"
2858*90c36d8aSUlf Magnusson        # choice items.
2859f219e013SMasahiro Yamada        self.actual_symbols = []
2860f219e013SMasahiro Yamada
2861*90c36d8aSUlf Magnusson        # The prompts and default values without any dependencies from
2862*90c36d8aSUlf Magnusson        # enclosing menus and ifs propagated
2863*90c36d8aSUlf Magnusson        self.orig_prompts = []
2864*90c36d8aSUlf Magnusson        self.orig_def_exprs = []
2865*90c36d8aSUlf Magnusson
2866*90c36d8aSUlf Magnusson        # Dependencies inherited from containing menus and ifs
2867*90c36d8aSUlf Magnusson        self.deps_from_containing = None
2868f219e013SMasahiro Yamada        # The set of symbols referenced by this choice (see
2869f219e013SMasahiro Yamada        # get_referenced_symbols())
2870f219e013SMasahiro Yamada        self.referenced_syms = set()
2871f219e013SMasahiro Yamada        # Like 'referenced_syms', but includes symbols from
2872*90c36d8aSUlf Magnusson        # dependencies inherited from enclosing menus and ifs
2873f219e013SMasahiro Yamada        self.all_referenced_syms = set()
2874f219e013SMasahiro Yamada
2875f219e013SMasahiro Yamada        # See Choice.get_def_locations()
2876f219e013SMasahiro Yamada        self.def_locations = []
2877f219e013SMasahiro Yamada
2878*90c36d8aSUlf Magnusson        # Cached values
2879f219e013SMasahiro Yamada        self.cached_selection = None
2880*90c36d8aSUlf Magnusson        self.cached_visibility = None
2881*90c36d8aSUlf Magnusson
2882*90c36d8aSUlf Magnusson        self.optional = False
2883f219e013SMasahiro Yamada
2884f219e013SMasahiro Yamada    def _determine_actual_symbols(self):
2885f219e013SMasahiro Yamada        """If a symbol's visibility depends on the preceding symbol within a
2886*90c36d8aSUlf Magnusson        choice, it is no longer viewed as a choice item. (This is quite
2887*90c36d8aSUlf Magnusson        possibly a bug, but some things consciously use it... ugh. It stems
2888*90c36d8aSUlf Magnusson        from automatic submenu creation.) In addition, it's possible to have
2889*90c36d8aSUlf Magnusson        choices and comments within choices, and those shouldn't be considered
2890*90c36d8aSUlf Magnusson        choice items either. Only drivers/usb/gadget/Kconfig seems to depend on
2891*90c36d8aSUlf Magnusson        any of this. This method computes the "actual" items in the choice and
2892*90c36d8aSUlf Magnusson        sets the is_choice_sym flag on them (retrieved via is_choice_symbol()).
2893f219e013SMasahiro Yamada
2894f219e013SMasahiro Yamada        Don't let this scare you: an earlier version simply checked for a
2895f219e013SMasahiro Yamada        sequence of symbols where all symbols after the first appeared in the
2896f219e013SMasahiro Yamada        'depends on' expression of the first, and that worked fine.  The added
2897f219e013SMasahiro Yamada        complexity is to be future-proof in the event that
2898f219e013SMasahiro Yamada        drivers/usb/gadget/Kconfig turns even more sinister. It might very well
2899f219e013SMasahiro Yamada        be overkilling things (especially if that file is refactored ;)."""
2900f219e013SMasahiro Yamada
2901f219e013SMasahiro Yamada        # Items might depend on each other in a tree structure, so we need a
2902f219e013SMasahiro Yamada        # stack to keep track of the current tentative parent
2903f219e013SMasahiro Yamada        stack = []
2904f219e013SMasahiro Yamada
2905*90c36d8aSUlf Magnusson        for item in self.block:
2906f219e013SMasahiro Yamada            if not isinstance(item, Symbol):
2907f219e013SMasahiro Yamada                stack = []
2908f219e013SMasahiro Yamada                continue
2909f219e013SMasahiro Yamada
2910*90c36d8aSUlf Magnusson            while stack:
2911f219e013SMasahiro Yamada                if item._has_auto_menu_dep_on(stack[-1]):
2912f219e013SMasahiro Yamada                    # The item should not be viewed as a choice item, so don't
2913*90c36d8aSUlf Magnusson                    # set item.is_choice_sym
2914f219e013SMasahiro Yamada                    stack.append(item)
2915f219e013SMasahiro Yamada                    break
2916f219e013SMasahiro Yamada                else:
2917f219e013SMasahiro Yamada                    stack.pop()
2918f219e013SMasahiro Yamada            else:
2919*90c36d8aSUlf Magnusson                item.is_choice_sym = True
2920f219e013SMasahiro Yamada                self.actual_symbols.append(item)
2921f219e013SMasahiro Yamada                stack.append(item)
2922f219e013SMasahiro Yamada
2923f219e013SMasahiro Yamada    def _cache_ret(self, selection):
2924f219e013SMasahiro Yamada        # As None is used to indicate the lack of a cached value we can't use
2925f219e013SMasahiro Yamada        # that to cache the fact that the choice has no selection. Instead, we
2926f219e013SMasahiro Yamada        # use the symbolic constant NO_SELECTION.
2927f219e013SMasahiro Yamada        if selection is None:
2928f219e013SMasahiro Yamada            self.cached_selection = NO_SELECTION
2929f219e013SMasahiro Yamada        else:
2930f219e013SMasahiro Yamada            self.cached_selection = selection
2931f219e013SMasahiro Yamada
2932f219e013SMasahiro Yamada        return selection
2933f219e013SMasahiro Yamada
2934f219e013SMasahiro Yamada    def _invalidate(self):
2935f219e013SMasahiro Yamada        self.cached_selection = None
2936*90c36d8aSUlf Magnusson        self.cached_visibility = None
2937f219e013SMasahiro Yamada
2938f219e013SMasahiro Yamada    def _unset_user_value(self):
2939f219e013SMasahiro Yamada        self._invalidate()
2940f219e013SMasahiro Yamada        self.user_val = None
2941f219e013SMasahiro Yamada        self.user_mode = None
2942f219e013SMasahiro Yamada
2943*90c36d8aSUlf Magnusson    def _make_conf(self, append_fn):
2944*90c36d8aSUlf Magnusson        _make_block_conf(self.block, append_fn)
2945f219e013SMasahiro Yamada
2946f219e013SMasahiro Yamadaclass Comment(Item):
2947f219e013SMasahiro Yamada
2948f219e013SMasahiro Yamada    """Represents a comment statement."""
2949f219e013SMasahiro Yamada
2950f219e013SMasahiro Yamada    #
2951f219e013SMasahiro Yamada    # Public interface
2952f219e013SMasahiro Yamada    #
2953f219e013SMasahiro Yamada
2954f219e013SMasahiro Yamada    def get_config(self):
2955f219e013SMasahiro Yamada        """Returns the Config instance this comment is from."""
2956f219e013SMasahiro Yamada        return self.config
2957f219e013SMasahiro Yamada
2958f219e013SMasahiro Yamada    def get_text(self):
2959f219e013SMasahiro Yamada        """Returns the text of the comment."""
2960f219e013SMasahiro Yamada        return self.text
2961f219e013SMasahiro Yamada
2962f219e013SMasahiro Yamada    def get_parent(self):
2963f219e013SMasahiro Yamada        """Returns the menu or choice statement that contains the comment, or
2964f219e013SMasahiro Yamada        None if the comment is at the top level. Note that if statements are
2965f219e013SMasahiro Yamada        treated as syntactic sugar and do not have an explicit class
2966f219e013SMasahiro Yamada        representation."""
2967f219e013SMasahiro Yamada        return self.parent
2968f219e013SMasahiro Yamada
2969f219e013SMasahiro Yamada    def get_location(self):
2970f219e013SMasahiro Yamada        """Returns the location of the comment as a (filename, linenr) tuple,
2971f219e013SMasahiro Yamada        where filename is a string and linenr an int."""
2972f219e013SMasahiro Yamada        return (self.filename, self.linenr)
2973f219e013SMasahiro Yamada
2974*90c36d8aSUlf Magnusson    def get_visibility(self):
2975*90c36d8aSUlf Magnusson        """Returns the visibility of the comment. See also
2976*90c36d8aSUlf Magnusson        Symbol.get_visibility()."""
2977*90c36d8aSUlf Magnusson        return self.config._eval_expr(self.dep_expr)
2978f219e013SMasahiro Yamada
2979*90c36d8aSUlf Magnusson    def get_referenced_symbols(self, refs_from_enclosing=False):
2980*90c36d8aSUlf Magnusson        """See Symbol.get_referenced_symbols()."""
2981*90c36d8aSUlf Magnusson        return self.all_referenced_syms if refs_from_enclosing else \
2982*90c36d8aSUlf Magnusson               self.referenced_syms
2983*90c36d8aSUlf Magnusson
2984*90c36d8aSUlf Magnusson    def __str__(self):
2985*90c36d8aSUlf Magnusson        """Returns a string containing various information about the
2986*90c36d8aSUlf Magnusson        comment."""
2987*90c36d8aSUlf Magnusson        dep_str = self.config._expr_val_str(self.orig_deps,
2988*90c36d8aSUlf Magnusson                                            "(no dependencies)")
2989*90c36d8aSUlf Magnusson
2990*90c36d8aSUlf Magnusson        additional_deps_str = " " + \
2991*90c36d8aSUlf Magnusson          self.config._expr_val_str(self.deps_from_containing,
2992f219e013SMasahiro Yamada                                    "(no additional dependencies)")
2993f219e013SMasahiro Yamada
2994*90c36d8aSUlf Magnusson        return _lines("Comment",
2995f219e013SMasahiro Yamada                      "Text: "         + str(self.text),
2996f219e013SMasahiro Yamada                      "Dependencies: " + dep_str,
2997*90c36d8aSUlf Magnusson                      "Additional dependencies from enclosing menus and "
2998*90c36d8aSUlf Magnusson                        "ifs:",
2999f219e013SMasahiro Yamada                      additional_deps_str,
3000f219e013SMasahiro Yamada                      "Location: {0}:{1}".format(self.filename, self.linenr))
3001f219e013SMasahiro Yamada
3002f219e013SMasahiro Yamada    #
3003f219e013SMasahiro Yamada    # Private methods
3004f219e013SMasahiro Yamada    #
3005f219e013SMasahiro Yamada
3006f219e013SMasahiro Yamada    def __init__(self):
3007f219e013SMasahiro Yamada        """Comment constructor -- not intended to be called directly by
3008*90c36d8aSUlf Magnusson        Kconfiglib clients."""
3009f219e013SMasahiro Yamada
3010f219e013SMasahiro Yamada        self.text = None
3011f219e013SMasahiro Yamada        self.dep_expr = None
3012*90c36d8aSUlf Magnusson        self.config = None
3013*90c36d8aSUlf Magnusson        self.parent = None
3014f219e013SMasahiro Yamada
3015f219e013SMasahiro Yamada        # Dependency expression without dependencies from enclosing menus and
3016*90c36d8aSUlf Magnusson        # ifs propagated
3017f219e013SMasahiro Yamada        self.orig_deps = None
3018f219e013SMasahiro Yamada
3019*90c36d8aSUlf Magnusson        # Dependencies inherited from containing menus and ifs
3020f219e013SMasahiro Yamada        self.deps_from_containing = None
3021f219e013SMasahiro Yamada        # The set of symbols referenced by this comment (see
3022f219e013SMasahiro Yamada        # get_referenced_symbols())
3023f219e013SMasahiro Yamada        self.referenced_syms = set()
3024f219e013SMasahiro Yamada        # Like 'referenced_syms', but includes symbols from
3025*90c36d8aSUlf Magnusson        # dependencies inherited from enclosing menus and ifs
3026f219e013SMasahiro Yamada        self.all_referenced_syms = None
3027f219e013SMasahiro Yamada
3028f219e013SMasahiro Yamada        self.filename = None
3029f219e013SMasahiro Yamada        self.linenr = None
3030f219e013SMasahiro Yamada
3031*90c36d8aSUlf Magnusson    def _make_conf(self, append_fn):
3032f219e013SMasahiro Yamada        if self.config._eval_expr(self.dep_expr) != "n":
3033*90c36d8aSUlf Magnusson            append_fn("\n#\n# {0}\n#".format(self.text))
3034f219e013SMasahiro Yamada
3035*90c36d8aSUlf Magnussonclass Kconfig_Syntax_Error(Exception):
3036*90c36d8aSUlf Magnusson    """Exception raised for syntax errors."""
3037*90c36d8aSUlf Magnusson    pass
3038f219e013SMasahiro Yamada
3039*90c36d8aSUlf Magnussonclass Internal_Error(Exception):
3040*90c36d8aSUlf Magnusson    """Exception raised for internal errors."""
3041*90c36d8aSUlf Magnusson    pass
3042*90c36d8aSUlf Magnusson
3043*90c36d8aSUlf Magnusson#
3044*90c36d8aSUlf Magnusson# Public functions
3045*90c36d8aSUlf Magnusson#
3046*90c36d8aSUlf Magnusson
3047*90c36d8aSUlf Magnussondef tri_less(v1, v2):
3048*90c36d8aSUlf Magnusson    """Returns True if the tristate v1 is less than the tristate v2, where "n",
3049*90c36d8aSUlf Magnusson    "m" and "y" are ordered from lowest to highest."""
3050*90c36d8aSUlf Magnusson    return TRI_TO_INT[v1] < TRI_TO_INT[v2]
3051*90c36d8aSUlf Magnusson
3052*90c36d8aSUlf Magnussondef tri_less_eq(v1, v2):
3053*90c36d8aSUlf Magnusson    """Returns True if the tristate v1 is less than or equal to the tristate
3054*90c36d8aSUlf Magnusson    v2, where "n", "m" and "y" are ordered from lowest to highest."""
3055*90c36d8aSUlf Magnusson    return TRI_TO_INT[v1] <= TRI_TO_INT[v2]
3056*90c36d8aSUlf Magnusson
3057*90c36d8aSUlf Magnussondef tri_greater(v1, v2):
3058*90c36d8aSUlf Magnusson    """Returns True if the tristate v1 is greater than the tristate v2, where
3059*90c36d8aSUlf Magnusson    "n", "m" and "y" are ordered from lowest to highest."""
3060*90c36d8aSUlf Magnusson    return TRI_TO_INT[v1] > TRI_TO_INT[v2]
3061*90c36d8aSUlf Magnusson
3062*90c36d8aSUlf Magnussondef tri_greater_eq(v1, v2):
3063*90c36d8aSUlf Magnusson    """Returns True if the tristate v1 is greater than or equal to the tristate
3064*90c36d8aSUlf Magnusson    v2, where "n", "m" and "y" are ordered from lowest to highest."""
3065*90c36d8aSUlf Magnusson    return TRI_TO_INT[v1] >= TRI_TO_INT[v2]
3066*90c36d8aSUlf Magnusson
3067*90c36d8aSUlf Magnusson#
3068*90c36d8aSUlf Magnusson# Internal classes
3069*90c36d8aSUlf Magnusson#
3070*90c36d8aSUlf Magnusson
3071*90c36d8aSUlf Magnussonclass _Feed(object):
3072*90c36d8aSUlf Magnusson
3073*90c36d8aSUlf Magnusson    """Class for working with sequences in a stream-like fashion; handy for
3074*90c36d8aSUlf Magnusson    tokens."""
3075*90c36d8aSUlf Magnusson
3076*90c36d8aSUlf Magnusson    # This would be more helpful on the item classes, but would remove some
3077*90c36d8aSUlf Magnusson    # flexibility
3078*90c36d8aSUlf Magnusson    __slots__ = ['items', 'length', 'i']
3079f219e013SMasahiro Yamada
3080f219e013SMasahiro Yamada    def __init__(self, items):
3081f219e013SMasahiro Yamada        self.items = items
3082f219e013SMasahiro Yamada        self.length = len(self.items)
3083f219e013SMasahiro Yamada        self.i = 0
3084f219e013SMasahiro Yamada
3085f219e013SMasahiro Yamada    def get_next(self):
3086f219e013SMasahiro Yamada        if self.i >= self.length:
3087f219e013SMasahiro Yamada            return None
3088f219e013SMasahiro Yamada        item = self.items[self.i]
3089f219e013SMasahiro Yamada        self.i += 1
3090f219e013SMasahiro Yamada        return item
3091f219e013SMasahiro Yamada
3092f219e013SMasahiro Yamada    def peek_next(self):
3093f219e013SMasahiro Yamada        return None if self.i >= self.length else self.items[self.i]
3094f219e013SMasahiro Yamada
3095f219e013SMasahiro Yamada    def check(self, token):
3096f219e013SMasahiro Yamada        """Check if the next token is 'token'. If so, remove it from the token
3097f219e013SMasahiro Yamada        feed and return True. Otherwise, leave it in and return False."""
3098*90c36d8aSUlf Magnusson        if self.i < self.length and self.items[self.i] == token:
3099f219e013SMasahiro Yamada            self.i += 1
3100f219e013SMasahiro Yamada            return True
3101f219e013SMasahiro Yamada        return False
3102f219e013SMasahiro Yamada
3103*90c36d8aSUlf Magnusson    def unget_all(self):
3104*90c36d8aSUlf Magnusson        self.i = 0
3105f219e013SMasahiro Yamada
3106*90c36d8aSUlf Magnussonclass _FileFeed(object):
3107f219e013SMasahiro Yamada
3108*90c36d8aSUlf Magnusson    """Feeds lines from a file. Keeps track of the filename and current line
3109*90c36d8aSUlf Magnusson    number. Joins any line ending in \\ with the following line. We need to be
3110*90c36d8aSUlf Magnusson    careful to get the line number right in the presence of continuation
3111*90c36d8aSUlf Magnusson    lines."""
3112f219e013SMasahiro Yamada
3113*90c36d8aSUlf Magnusson    __slots__ = ['filename', 'lines', 'length', 'linenr']
3114f219e013SMasahiro Yamada
3115*90c36d8aSUlf Magnusson    def __init__(self, filename):
3116f219e013SMasahiro Yamada        self.filename = _clean_up_path(filename)
3117*90c36d8aSUlf Magnusson        with open(filename, "r") as f:
3118*90c36d8aSUlf Magnusson            # No interleaving of I/O and processing yet. Don't know if it would
3119*90c36d8aSUlf Magnusson            # help.
3120*90c36d8aSUlf Magnusson            self.lines = f.readlines()
3121*90c36d8aSUlf Magnusson        self.length = len(self.lines)
3122*90c36d8aSUlf Magnusson        self.linenr = 0
3123f219e013SMasahiro Yamada
3124*90c36d8aSUlf Magnusson    def get_next(self):
3125*90c36d8aSUlf Magnusson        if self.linenr >= self.length:
3126*90c36d8aSUlf Magnusson            return None
3127*90c36d8aSUlf Magnusson        line = self.lines[self.linenr]
3128*90c36d8aSUlf Magnusson        self.linenr += 1
3129*90c36d8aSUlf Magnusson        while line.endswith("\\\n"):
3130*90c36d8aSUlf Magnusson            line = line[:-2] + self.lines[self.linenr]
3131*90c36d8aSUlf Magnusson            self.linenr += 1
3132*90c36d8aSUlf Magnusson        return line
3133f219e013SMasahiro Yamada
3134*90c36d8aSUlf Magnusson    def peek_next(self):
3135*90c36d8aSUlf Magnusson        linenr = self.linenr
3136*90c36d8aSUlf Magnusson        if linenr >= self.length:
3137*90c36d8aSUlf Magnusson            return None
3138*90c36d8aSUlf Magnusson        line = self.lines[linenr]
3139*90c36d8aSUlf Magnusson        while line.endswith("\\\n"):
3140*90c36d8aSUlf Magnusson            linenr += 1
3141*90c36d8aSUlf Magnusson            line = line[:-2] + self.lines[linenr]
3142*90c36d8aSUlf Magnusson        return line
3143*90c36d8aSUlf Magnusson
3144*90c36d8aSUlf Magnusson    def unget(self):
3145*90c36d8aSUlf Magnusson        self.linenr -= 1
3146*90c36d8aSUlf Magnusson        while self.lines[self.linenr].endswith("\\\n"):
3147*90c36d8aSUlf Magnusson            self.linenr -= 1
3148*90c36d8aSUlf Magnusson
3149*90c36d8aSUlf Magnusson    def next_nonblank(self):
3150*90c36d8aSUlf Magnusson        """Removes lines up to and including the next non-blank (not all-space)
3151*90c36d8aSUlf Magnusson        line and returns it. Returns None if there are no more non-blank
3152*90c36d8aSUlf Magnusson        lines."""
3153*90c36d8aSUlf Magnusson        while 1:
3154*90c36d8aSUlf Magnusson            line = self.get_next()
3155*90c36d8aSUlf Magnusson            if line is None or not line.isspace():
3156*90c36d8aSUlf Magnusson                return line
3157f219e013SMasahiro Yamada
3158f219e013SMasahiro Yamada#
3159*90c36d8aSUlf Magnusson# Internal functions
3160f219e013SMasahiro Yamada#
3161f219e013SMasahiro Yamada
3162*90c36d8aSUlf Magnussondef _get_visibility(sc):
3163*90c36d8aSUlf Magnusson    """Symbols and Choices have a "visibility" that acts as an upper bound on
3164*90c36d8aSUlf Magnusson    the values a user can set for them, corresponding to the visibility in e.g.
3165*90c36d8aSUlf Magnusson    'make menuconfig'. This function calculates the visibility for the Symbol
3166*90c36d8aSUlf Magnusson    or Choice 'sc' -- the logic is nearly identical."""
3167*90c36d8aSUlf Magnusson    if sc.cached_visibility is None:
3168*90c36d8aSUlf Magnusson        vis = "n"
3169*90c36d8aSUlf Magnusson        for _, cond_expr in sc.prompts:
3170*90c36d8aSUlf Magnusson            vis = sc.config._eval_max(vis, cond_expr)
3171f219e013SMasahiro Yamada
3172*90c36d8aSUlf Magnusson        if isinstance(sc, Symbol) and sc.is_choice_sym:
3173*90c36d8aSUlf Magnusson            vis = sc.config._eval_min(vis, _get_visibility(sc.parent))
3174f219e013SMasahiro Yamada
3175*90c36d8aSUlf Magnusson        # Promote "m" to "y" if we're dealing with a non-tristate
3176*90c36d8aSUlf Magnusson        if vis == "m" and sc.type != TRISTATE:
3177*90c36d8aSUlf Magnusson            vis = "y"
3178f219e013SMasahiro Yamada
3179*90c36d8aSUlf Magnusson        sc.cached_visibility = vis
3180f219e013SMasahiro Yamada
3181*90c36d8aSUlf Magnusson    return sc.cached_visibility
3182f219e013SMasahiro Yamada
3183*90c36d8aSUlf Magnussondef _make_and(e1, e2):
3184*90c36d8aSUlf Magnusson    """Constructs an AND (&&) expression. Performs trivial simplification.
3185*90c36d8aSUlf Magnusson    Nones equate to 'y'.
3186*90c36d8aSUlf Magnusson
3187*90c36d8aSUlf Magnusson    Note: returns None if e1 == e2 == None."""
3188*90c36d8aSUlf Magnusson    if e1 is None or e1 == "y":
3189*90c36d8aSUlf Magnusson        return e2
3190*90c36d8aSUlf Magnusson    if e2 is None or e2 == "y":
3191*90c36d8aSUlf Magnusson        return e1
3192*90c36d8aSUlf Magnusson
3193*90c36d8aSUlf Magnusson    # Prefer to merge argument lists if possible to reduce the number of nodes
3194*90c36d8aSUlf Magnusson
3195*90c36d8aSUlf Magnusson    if isinstance(e1, tuple) and e1[0] == AND:
3196*90c36d8aSUlf Magnusson        if isinstance(e2, tuple) and e2[0] == AND:
3197*90c36d8aSUlf Magnusson            return (AND, e1[1] + e2[1])
3198*90c36d8aSUlf Magnusson        return (AND, e1[1] + [e2])
3199*90c36d8aSUlf Magnusson
3200*90c36d8aSUlf Magnusson    if isinstance(e2, tuple) and e2[0] == AND:
3201*90c36d8aSUlf Magnusson        return (AND, e2[1] + [e1])
3202*90c36d8aSUlf Magnusson
3203*90c36d8aSUlf Magnusson    return (AND, [e1, e2])
3204*90c36d8aSUlf Magnusson
3205*90c36d8aSUlf Magnussondef _make_or(e1, e2):
3206*90c36d8aSUlf Magnusson    """Constructs an OR (||) expression. Performs trivial simplification and
3207*90c36d8aSUlf Magnusson    avoids Nones. Nones equate to 'y', which is usually what we want, but needs
3208*90c36d8aSUlf Magnusson    to be kept in mind."""
3209*90c36d8aSUlf Magnusson
3210*90c36d8aSUlf Magnusson    # Perform trivial simplification and avoid None's (which
3211*90c36d8aSUlf Magnusson    # correspond to y's)
3212*90c36d8aSUlf Magnusson    if e1 is None or e2 is None or e1 == "y" or e2 == "y":
3213*90c36d8aSUlf Magnusson        return "y"
3214*90c36d8aSUlf Magnusson    if e1 == "n":
3215*90c36d8aSUlf Magnusson        return e2
3216*90c36d8aSUlf Magnusson
3217*90c36d8aSUlf Magnusson    # Prefer to merge argument lists if possible to reduce the number of nodes
3218*90c36d8aSUlf Magnusson
3219*90c36d8aSUlf Magnusson    if isinstance(e1, tuple) and e1[0] == OR:
3220*90c36d8aSUlf Magnusson        if isinstance(e2, tuple) and e2[0] == OR:
3221*90c36d8aSUlf Magnusson            return (OR, e1[1] + e2[1])
3222*90c36d8aSUlf Magnusson        return (OR, e1[1] + [e2])
3223*90c36d8aSUlf Magnusson
3224*90c36d8aSUlf Magnusson    if isinstance(e2, tuple) and e2[0] == OR:
3225*90c36d8aSUlf Magnusson        return (OR, e2[1] + [e1])
3226*90c36d8aSUlf Magnusson
3227*90c36d8aSUlf Magnusson    return (OR, [e1, e2])
3228*90c36d8aSUlf Magnusson
3229*90c36d8aSUlf Magnussondef _get_expr_syms_rec(expr, res):
3230*90c36d8aSUlf Magnusson    """_get_expr_syms() helper. Recurses through expressions."""
3231*90c36d8aSUlf Magnusson    if isinstance(expr, Symbol):
3232*90c36d8aSUlf Magnusson        res.add(expr)
3233*90c36d8aSUlf Magnusson    elif isinstance(expr, str):
3234*90c36d8aSUlf Magnusson        return
3235*90c36d8aSUlf Magnusson    elif expr[0] == AND or expr[0] == OR:
3236*90c36d8aSUlf Magnusson        for term in expr[1]:
3237*90c36d8aSUlf Magnusson            _get_expr_syms_rec(term, res)
3238*90c36d8aSUlf Magnusson    elif expr[0] == NOT:
3239*90c36d8aSUlf Magnusson        _get_expr_syms_rec(expr[1], res)
3240*90c36d8aSUlf Magnusson    elif expr[0] == EQUAL or expr[0] == UNEQUAL:
3241*90c36d8aSUlf Magnusson        if isinstance(expr[1], Symbol):
3242*90c36d8aSUlf Magnusson            res.add(expr[1])
3243*90c36d8aSUlf Magnusson        if isinstance(expr[2], Symbol):
3244*90c36d8aSUlf Magnusson            res.add(expr[2])
3245*90c36d8aSUlf Magnusson    else:
3246*90c36d8aSUlf Magnusson        _internal_error("Internal error while fetching symbols from an "
3247*90c36d8aSUlf Magnusson                        "expression with token stream {0}.".format(expr))
3248*90c36d8aSUlf Magnusson
3249*90c36d8aSUlf Magnussondef _get_expr_syms(expr):
3250*90c36d8aSUlf Magnusson    """Returns the set() of symbols appearing in expr."""
3251*90c36d8aSUlf Magnusson    res = set()
3252*90c36d8aSUlf Magnusson    if expr is not None:
3253*90c36d8aSUlf Magnusson        _get_expr_syms_rec(expr, res)
3254*90c36d8aSUlf Magnusson    return res
3255*90c36d8aSUlf Magnusson
3256*90c36d8aSUlf Magnussondef _str_val(obj):
3257*90c36d8aSUlf Magnusson    """Returns the value of obj as a string. If obj is not a string (constant
3258*90c36d8aSUlf Magnusson    symbol), it must be a Symbol."""
3259*90c36d8aSUlf Magnusson    return obj if isinstance(obj, str) else obj.get_value()
3260*90c36d8aSUlf Magnusson
3261*90c36d8aSUlf Magnussondef _make_block_conf(block, append_fn):
3262*90c36d8aSUlf Magnusson    """Returns a list of .config strings for a block (list) of items."""
3263*90c36d8aSUlf Magnusson
3264*90c36d8aSUlf Magnusson    # Collect the substrings in a list and later use join() instead of += to
3265*90c36d8aSUlf Magnusson    # build the final .config contents. With older Python versions, this yields
3266*90c36d8aSUlf Magnusson    # linear instead of quadratic complexity.
3267*90c36d8aSUlf Magnusson    for item in block:
3268*90c36d8aSUlf Magnusson        item._make_conf(append_fn)
3269*90c36d8aSUlf Magnusson
3270*90c36d8aSUlf Magnussondef _sym_str_string(sym_or_str):
3271*90c36d8aSUlf Magnusson    if isinstance(sym_or_str, str):
3272*90c36d8aSUlf Magnusson        return '"' + sym_or_str + '"'
3273*90c36d8aSUlf Magnusson    return sym_or_str.name
3274*90c36d8aSUlf Magnusson
3275*90c36d8aSUlf Magnussondef _intersperse(lst, op):
3276*90c36d8aSUlf Magnusson    """_expr_to_str() helper. Gets the string representation of each expression
3277*90c36d8aSUlf Magnusson    in lst and produces a list where op has been inserted between the
3278*90c36d8aSUlf Magnusson    elements."""
3279*90c36d8aSUlf Magnusson    if not lst:
3280f219e013SMasahiro Yamada        return ""
3281*90c36d8aSUlf Magnusson
3282*90c36d8aSUlf Magnusson    res = []
3283*90c36d8aSUlf Magnusson
3284*90c36d8aSUlf Magnusson    def handle_sub_expr(expr):
3285*90c36d8aSUlf Magnusson        no_parens = isinstance(expr, (str, Symbol)) or \
3286*90c36d8aSUlf Magnusson                    expr[0] in (EQUAL, UNEQUAL) or \
3287*90c36d8aSUlf Magnusson                    PRECEDENCE[op] <= PRECEDENCE[expr[0]]
3288*90c36d8aSUlf Magnusson        if not no_parens:
3289*90c36d8aSUlf Magnusson            res.append("(")
3290*90c36d8aSUlf Magnusson        res.extend(_expr_to_str_rec(expr))
3291*90c36d8aSUlf Magnusson        if not no_parens:
3292*90c36d8aSUlf Magnusson            res.append(")")
3293*90c36d8aSUlf Magnusson
3294*90c36d8aSUlf Magnusson    op_str = OP_TO_STR[op]
3295*90c36d8aSUlf Magnusson
3296*90c36d8aSUlf Magnusson    handle_sub_expr(lst[0])
3297*90c36d8aSUlf Magnusson    for expr in lst[1:]:
3298*90c36d8aSUlf Magnusson        res.append(op_str)
3299*90c36d8aSUlf Magnusson        handle_sub_expr(expr)
3300*90c36d8aSUlf Magnusson
3301*90c36d8aSUlf Magnusson    return res
3302*90c36d8aSUlf Magnusson
3303*90c36d8aSUlf Magnussondef _expr_to_str_rec(expr):
3304*90c36d8aSUlf Magnusson    if expr is None:
3305*90c36d8aSUlf Magnusson        return [""]
3306*90c36d8aSUlf Magnusson
3307*90c36d8aSUlf Magnusson    if isinstance(expr, (Symbol, str)):
3308*90c36d8aSUlf Magnusson        return [_sym_str_string(expr)]
3309*90c36d8aSUlf Magnusson
3310*90c36d8aSUlf Magnusson    if expr[0] in (AND, OR):
3311*90c36d8aSUlf Magnusson        return _intersperse(expr[1], expr[0])
3312*90c36d8aSUlf Magnusson
3313*90c36d8aSUlf Magnusson    if expr[0] == NOT:
3314*90c36d8aSUlf Magnusson        need_parens = not isinstance(expr[1], (str, Symbol))
3315*90c36d8aSUlf Magnusson
3316*90c36d8aSUlf Magnusson        res = ["!"]
3317*90c36d8aSUlf Magnusson        if need_parens:
3318*90c36d8aSUlf Magnusson            res.append("(")
3319*90c36d8aSUlf Magnusson        res.extend(_expr_to_str_rec(expr[1]))
3320*90c36d8aSUlf Magnusson        if need_parens:
3321*90c36d8aSUlf Magnusson            res.append(")")
3322*90c36d8aSUlf Magnusson        return res
3323*90c36d8aSUlf Magnusson
3324*90c36d8aSUlf Magnusson    if expr[0] in (EQUAL, UNEQUAL):
3325*90c36d8aSUlf Magnusson        return [_sym_str_string(expr[1]),
3326*90c36d8aSUlf Magnusson                OP_TO_STR[expr[0]],
3327*90c36d8aSUlf Magnusson                _sym_str_string(expr[2])]
3328*90c36d8aSUlf Magnusson
3329*90c36d8aSUlf Magnussondef _expr_to_str(expr):
3330*90c36d8aSUlf Magnusson    return "".join(_expr_to_str_rec(expr))
3331f219e013SMasahiro Yamada
3332f219e013SMasahiro Yamadadef _indentation(line):
3333*90c36d8aSUlf Magnusson    """Returns the length of the line's leading whitespace, treating tab stops
3334*90c36d8aSUlf Magnusson    as being spaced 8 characters apart."""
3335*90c36d8aSUlf Magnusson    line = line.expandtabs()
3336*90c36d8aSUlf Magnusson    return len(line) - len(line.lstrip())
3337f219e013SMasahiro Yamada
3338f219e013SMasahiro Yamadadef _deindent(line, indent):
3339f219e013SMasahiro Yamada    """Deindent 'line' by 'indent' spaces."""
3340f219e013SMasahiro Yamada    line = line.expandtabs()
3341f219e013SMasahiro Yamada    if len(line) <= indent:
3342f219e013SMasahiro Yamada        return line
3343f219e013SMasahiro Yamada    return line[indent:]
3344f219e013SMasahiro Yamada
3345f219e013SMasahiro Yamadadef _is_base_n(s, n):
3346f219e013SMasahiro Yamada    try:
3347f219e013SMasahiro Yamada        int(s, n)
3348f219e013SMasahiro Yamada        return True
3349f219e013SMasahiro Yamada    except ValueError:
3350f219e013SMasahiro Yamada        return False
3351f219e013SMasahiro Yamada
3352*90c36d8aSUlf Magnussondef _lines(*args):
3353*90c36d8aSUlf Magnusson    """Returns a string consisting of all arguments, with newlines inserted
3354f219e013SMasahiro Yamada    between them."""
3355f219e013SMasahiro Yamada    return "\n".join(args)
3356f219e013SMasahiro Yamada
3357f219e013SMasahiro Yamadadef _comment(s):
3358f219e013SMasahiro Yamada    """Returns a new string with "#" inserted before each line in 's'."""
3359f219e013SMasahiro Yamada    if not s:
3360f219e013SMasahiro Yamada        return "#"
3361f219e013SMasahiro Yamada    res = "".join(["#" + line for line in s.splitlines(True)])
3362f219e013SMasahiro Yamada    if s.endswith("\n"):
3363f219e013SMasahiro Yamada        return res + "#"
3364f219e013SMasahiro Yamada    return res
3365f219e013SMasahiro Yamada
3366f219e013SMasahiro Yamadadef _clean_up_path(path):
3367*90c36d8aSUlf Magnusson    """Strips an initial "./" and any trailing slashes from 'path'."""
3368f219e013SMasahiro Yamada    if path.startswith("./"):
3369f219e013SMasahiro Yamada        path = path[2:]
3370*90c36d8aSUlf Magnusson    return path.rstrip("/")
3371f219e013SMasahiro Yamada
3372*90c36d8aSUlf Magnussondef _stderr_msg(msg, filename, linenr):
3373f219e013SMasahiro Yamada    if filename is not None:
3374*90c36d8aSUlf Magnusson        sys.stderr.write("{0}:{1}: ".format(_clean_up_path(filename), linenr))
3375*90c36d8aSUlf Magnusson    sys.stderr.write(msg + "\n")
3376f219e013SMasahiro Yamada
3377*90c36d8aSUlf Magnussondef _tokenization_error(s, filename, linenr):
3378*90c36d8aSUlf Magnusson    loc = "" if filename is None else "{0}:{1}: ".format(filename, linenr)
3379*90c36d8aSUlf Magnusson    raise Kconfig_Syntax_Error("{0}Couldn't tokenize '{1}'"
3380*90c36d8aSUlf Magnusson                               .format(loc, s.strip()))
3381f219e013SMasahiro Yamada
3382f219e013SMasahiro Yamadadef _parse_error(s, msg, filename, linenr):
3383*90c36d8aSUlf Magnusson    loc = "" if filename is None else "{0}:{1}: ".format(filename, linenr)
3384*90c36d8aSUlf Magnusson    raise Kconfig_Syntax_Error("{0}Couldn't parse '{1}'{2}"
3385*90c36d8aSUlf Magnusson                               .format(loc, s.strip(),
3386*90c36d8aSUlf Magnusson                                       "." if msg is None else ": " + msg))
3387f219e013SMasahiro Yamada
3388f219e013SMasahiro Yamadadef _internal_error(msg):
3389*90c36d8aSUlf Magnusson    raise Internal_Error(msg +
3390*90c36d8aSUlf Magnusson      "\nSorry! You may want to send an email to ulfalizer a.t Google's "
3391*90c36d8aSUlf Magnusson      "email service to tell me about this. Include the message above and the "
3392*90c36d8aSUlf Magnusson      "stack trace and describe what you were doing.")
3393f219e013SMasahiro Yamada
3394*90c36d8aSUlf Magnusson#
3395*90c36d8aSUlf Magnusson# Internal global constants
3396*90c36d8aSUlf Magnusson#
3397f219e013SMasahiro Yamada
3398*90c36d8aSUlf Magnusson# Tokens
3399*90c36d8aSUlf Magnusson(T_AND, T_OR, T_NOT,
3400*90c36d8aSUlf Magnusson T_OPEN_PAREN, T_CLOSE_PAREN,
3401*90c36d8aSUlf Magnusson T_EQUAL, T_UNEQUAL,
3402*90c36d8aSUlf Magnusson T_MAINMENU, T_MENU, T_ENDMENU,
3403*90c36d8aSUlf Magnusson T_SOURCE, T_CHOICE, T_ENDCHOICE,
3404*90c36d8aSUlf Magnusson T_COMMENT, T_CONFIG, T_MENUCONFIG,
3405*90c36d8aSUlf Magnusson T_HELP, T_IF, T_ENDIF, T_DEPENDS, T_ON,
3406*90c36d8aSUlf Magnusson T_OPTIONAL, T_PROMPT, T_DEFAULT,
3407*90c36d8aSUlf Magnusson T_BOOL, T_TRISTATE, T_HEX, T_INT, T_STRING,
3408*90c36d8aSUlf Magnusson T_DEF_BOOL, T_DEF_TRISTATE,
3409*90c36d8aSUlf Magnusson T_SELECT, T_RANGE, T_OPTION, T_ALLNOCONFIG_Y, T_ENV,
3410*90c36d8aSUlf Magnusson T_DEFCONFIG_LIST, T_MODULES, T_VISIBLE) = range(39)
3411f219e013SMasahiro Yamada
3412*90c36d8aSUlf Magnusson# The leading underscore before the function assignments below prevent pydoc
3413*90c36d8aSUlf Magnusson# from listing them. The constants could be hidden too, but they're fairly
3414*90c36d8aSUlf Magnusson# obviously internal anyway, so don't bother spamming the code.
3415f219e013SMasahiro Yamada
3416*90c36d8aSUlf Magnusson# Keyword to token map. Note that the get() method is assigned directly as a
3417*90c36d8aSUlf Magnusson# small optimization.
3418*90c36d8aSUlf Magnusson_get_keyword = \
3419*90c36d8aSUlf Magnusson  {"mainmenu": T_MAINMENU, "menu": T_MENU, "endmenu": T_ENDMENU,
3420*90c36d8aSUlf Magnusson   "endif": T_ENDIF, "endchoice": T_ENDCHOICE, "source": T_SOURCE,
3421*90c36d8aSUlf Magnusson   "choice": T_CHOICE, "config": T_CONFIG, "comment": T_COMMENT,
3422*90c36d8aSUlf Magnusson   "menuconfig": T_MENUCONFIG, "help": T_HELP, "if": T_IF,
3423*90c36d8aSUlf Magnusson   "depends": T_DEPENDS, "on": T_ON, "optional": T_OPTIONAL,
3424*90c36d8aSUlf Magnusson   "prompt": T_PROMPT, "default": T_DEFAULT, "bool": T_BOOL, "boolean": T_BOOL,
3425*90c36d8aSUlf Magnusson   "tristate": T_TRISTATE, "int": T_INT, "hex": T_HEX, "def_bool": T_DEF_BOOL,
3426*90c36d8aSUlf Magnusson   "def_tristate": T_DEF_TRISTATE, "string": T_STRING, "select": T_SELECT,
3427*90c36d8aSUlf Magnusson   "range": T_RANGE, "option": T_OPTION, "allnoconfig_y": T_ALLNOCONFIG_Y,
3428*90c36d8aSUlf Magnusson   "env": T_ENV, "defconfig_list": T_DEFCONFIG_LIST, "modules": T_MODULES,
3429*90c36d8aSUlf Magnusson   "visible": T_VISIBLE}.get
3430*90c36d8aSUlf Magnusson
3431*90c36d8aSUlf Magnusson# Strings to use for True and False
3432*90c36d8aSUlf MagnussonBOOL_STR = {False: "false", True: "true"}
3433*90c36d8aSUlf Magnusson
3434*90c36d8aSUlf Magnusson# Tokens after which identifier-like lexemes are treated as strings. T_CHOICE
3435*90c36d8aSUlf Magnusson# is included to avoid symbols being registered for named choices.
3436*90c36d8aSUlf MagnussonSTRING_LEX = frozenset((T_BOOL, T_TRISTATE, T_INT, T_HEX, T_STRING, T_CHOICE,
3437*90c36d8aSUlf Magnusson                        T_PROMPT, T_MENU, T_COMMENT, T_SOURCE, T_MAINMENU))
3438*90c36d8aSUlf Magnusson
3439*90c36d8aSUlf Magnusson# Matches the initial token on a line; see _tokenize(). Also eats trailing
3440*90c36d8aSUlf Magnusson# whitespace as an optimization.
3441*90c36d8aSUlf Magnusson_initial_token_re_match = re.compile(r"[^\w]*(\w+)\s*").match
3442*90c36d8aSUlf Magnusson
3443*90c36d8aSUlf Magnusson# Matches an identifier/keyword optionally preceded by whitespace. Also eats
3444*90c36d8aSUlf Magnusson# trailing whitespace as an optimization.
3445*90c36d8aSUlf Magnusson_id_keyword_re_match = re.compile(r"\s*([\w./-]+)\s*").match
3446*90c36d8aSUlf Magnusson
3447*90c36d8aSUlf Magnusson# Regular expressions for parsing .config files
3448*90c36d8aSUlf Magnusson_set_re_match = re.compile(r"CONFIG_(\w+)=(.*)").match
3449*90c36d8aSUlf Magnusson_unset_re_match = re.compile(r"# CONFIG_(\w+) is not set").match
3450*90c36d8aSUlf Magnusson
3451*90c36d8aSUlf Magnusson# Regular expression for finding $-references to symbols in strings
3452*90c36d8aSUlf Magnusson_sym_ref_re_search = re.compile(r"\$[A-Za-z0-9_]+").search
3453*90c36d8aSUlf Magnusson
3454*90c36d8aSUlf Magnusson# Integers representing symbol types
3455*90c36d8aSUlf MagnussonUNKNOWN, BOOL, TRISTATE, STRING, HEX, INT = range(6)
3456*90c36d8aSUlf Magnusson
3457*90c36d8aSUlf Magnusson# Strings to use for types
3458*90c36d8aSUlf MagnussonTYPENAME = {UNKNOWN: "unknown", BOOL: "bool", TRISTATE: "tristate",
3459*90c36d8aSUlf Magnusson            STRING: "string", HEX: "hex", INT: "int"}
3460*90c36d8aSUlf Magnusson
3461*90c36d8aSUlf Magnusson# Token to type mapping
3462*90c36d8aSUlf MagnussonTOKEN_TO_TYPE = {T_BOOL: BOOL, T_TRISTATE: TRISTATE, T_STRING: STRING,
3463*90c36d8aSUlf Magnusson                 T_INT: INT, T_HEX: HEX}
3464*90c36d8aSUlf Magnusson
3465*90c36d8aSUlf Magnusson# Default values for symbols of different types (the value the symbol gets if
3466*90c36d8aSUlf Magnusson# it is not assigned a user value and none of its 'default' clauses kick in)
3467*90c36d8aSUlf MagnussonDEFAULT_VALUE = {BOOL: "n", TRISTATE: "n", STRING: "", INT: "", HEX: ""}
3468*90c36d8aSUlf Magnusson
3469*90c36d8aSUlf Magnusson# Indicates that no item is selected in a choice statement
3470*90c36d8aSUlf MagnussonNO_SELECTION = 0
3471*90c36d8aSUlf Magnusson
3472*90c36d8aSUlf Magnusson# Integers representing expression types
3473*90c36d8aSUlf MagnussonAND, OR, NOT, EQUAL, UNEQUAL = range(5)
3474*90c36d8aSUlf Magnusson
3475*90c36d8aSUlf Magnusson# Map from tristate values to integers
3476*90c36d8aSUlf MagnussonTRI_TO_INT = {"n": 0, "m": 1, "y": 2}
3477*90c36d8aSUlf Magnusson
3478*90c36d8aSUlf Magnusson# Printing-related stuff
3479*90c36d8aSUlf Magnusson
3480*90c36d8aSUlf MagnussonOP_TO_STR = {AND: " && ", OR: " || ", EQUAL: " = ", UNEQUAL: " != "}
3481*90c36d8aSUlf MagnussonPRECEDENCE = {OR: 0, AND: 1, NOT: 2}
3482