From: Paolo Bonzini <pbonzini@redhat.com>
To: qemu-devel@nongnu.org
Cc: thuth@redhat.com, Yang Zhong <yang.zhong@intel.com>
Subject: [Qemu-devel] [PULL 06/54] minikconfig: add semantic analysis
Date: Mon, 4 Mar 2019 19:19:26 +0100 [thread overview]
Message-ID: <1551723614-1823-7-git-send-email-pbonzini@redhat.com> (raw)
In-Reply-To: <1551723614-1823-1-git-send-email-pbonzini@redhat.com>
There are three parts in the semantic analysis:
1) evaluating expressions. This is done as a simple visit
of the Expr nodes.
2) ordering clauses. This is done by constructing a graph of variables.
There is an edge from X to Y if Y depends on X, if X selects Y, or if
X appears in a conditional selection of Y; in other words, if the value
of X can affect the value of Y. Each clause has a "destination" variable
whose value can be affected by the clause, and clauses will be processed
according to a topological sorting of their destination variables.
Defaults are processed after all other clauses with the same destination.
3) deriving the value of the variables. This is done by processing
the clauses in the topological order provided by the previous step.
A "depends on" clause will force a variable to False, a "select" clause
will force a variable to True, an assignment will force a variable
to its RHS. A default will set a variable to its RHS if it has not
been set before. Because all variables have a default, after visiting
all clauses all variables will have been set.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Message-Id: <20190123065618.3520-25-yang.zhong@intel.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
scripts/minikconf.py | 144 +++++++++++++++++++++++++++++++++++++++++++++++----
1 file changed, 135 insertions(+), 9 deletions(-)
diff --git a/scripts/minikconf.py b/scripts/minikconf.py
index f0cc3b9..d89fb09 100644
--- a/scripts/minikconf.py
+++ b/scripts/minikconf.py
@@ -14,7 +14,8 @@ from __future__ import print_function
import os
import sys
-__all__ = [ 'KconfigParserError', 'KconfigData', 'KconfigParser' ]
+__all__ = [ 'KconfigDataError', 'KconfigParserError',
+ 'KconfigData', 'KconfigParser' ]
def debug_print(*args):
#print('# ' + (' '.join(str(x) for x in args)))
@@ -30,6 +31,13 @@ def debug_print(*args):
# just its name).
# -------------------------------------------
+class KconfigDataError(Exception):
+ def __init__(self, msg):
+ self.msg = msg
+
+ def __str__(self):
+ return self.msg
+
class KconfigData:
class Expr:
def __and__(self, rhs):
@@ -39,6 +47,12 @@ class KconfigData:
def __invert__(self):
return KconfigData.NOT(self)
+ # Abstract methods
+ def add_edges_to(self, var):
+ pass
+ def evaluate(self):
+ assert False
+
class AND(Expr):
def __init__(self, lhs, rhs):
self.lhs = lhs
@@ -46,6 +60,12 @@ class KconfigData:
def __str__(self):
return "(%s && %s)" % (self.lhs, self.rhs)
+ def add_edges_to(self, var):
+ self.lhs.add_edges_to(var)
+ self.rhs.add_edges_to(var)
+ def evaluate(self):
+ return self.lhs.evaluate() and self.rhs.evaluate()
+
class OR(Expr):
def __init__(self, lhs, rhs):
self.lhs = lhs
@@ -53,35 +73,85 @@ class KconfigData:
def __str__(self):
return "(%s || %s)" % (self.lhs, self.rhs)
+ def add_edges_to(self, var):
+ self.lhs.add_edges_to(var)
+ self.rhs.add_edges_to(var)
+ def evaluate(self):
+ return self.lhs.evaluate() or self.rhs.evaluate()
+
class NOT(Expr):
def __init__(self, lhs):
self.lhs = lhs
def __str__(self):
return "!%s" % (self.lhs)
+ def add_edges_to(self, var):
+ self.lhs.add_edges_to(var)
+ def evaluate(self):
+ return not self.lhs.evaluate()
+
class Var(Expr):
def __init__(self, name):
self.name = name
self.value = None
+ self.outgoing = set()
+ self.clauses_for_var = list()
def __str__(self):
return self.name
+ def has_value(self):
+ return not (self.value is None)
+ def set_value(self, val, clause):
+ self.clauses_for_var.append(clause)
+ if self.has_value() and self.value != val:
+ print("The following clauses were found for " + self.name)
+ for i in self.clauses_for_var:
+ print(" " + str(i), file=sys.stderr)
+ raise KconfigDataError('contradiction between clauses when setting %s' % self)
+ debug_print("=> %s is now %s" % (self.name, val))
+ self.value = val
+
+ # depth first search of the dependency graph
+ def dfs(self, visited, f):
+ if self in visited:
+ return
+ visited.add(self)
+ for v in self.outgoing:
+ v.dfs(visited, f)
+ f(self)
+
+ def add_edges_to(self, var):
+ self.outgoing.add(var)
+ def evaluate(self):
+ if not self.has_value():
+ raise KconfigDataError('cycle found including %s' % self)
+ return self.value
+
class Clause:
def __init__(self, dest):
self.dest = dest
+ def priority(self):
+ return 0
+ def process(self):
+ pass
class AssignmentClause(Clause):
def __init__(self, dest, value):
KconfigData.Clause.__init__(self, dest)
self.value = value
def __str__(self):
- return "%s=%s" % (self.dest, 'y' if self.value else 'n')
+ return "CONFIG_%s=%s" % (self.dest, 'y' if self.value else 'n')
+
+ def process(self):
+ self.dest.set_value(self.value, self)
class DefaultClause(Clause):
def __init__(self, dest, value, cond=None):
KconfigData.Clause.__init__(self, dest)
self.value = value
self.cond = cond
+ if not (self.cond is None):
+ self.cond.add_edges_to(self.dest)
def __str__(self):
value = 'y' if self.value else 'n'
if self.cond is None:
@@ -89,20 +159,38 @@ class KconfigData:
else:
return "config %s default %s if %s" % (self.dest, value, self.cond)
+ def priority(self):
+ # Defaults are processed just before leaving the variable
+ return -1
+ def process(self):
+ if not self.dest.has_value() and \
+ (self.cond is None or self.cond.evaluate()):
+ self.dest.set_value(self.value, self)
+
class DependsOnClause(Clause):
def __init__(self, dest, expr):
KconfigData.Clause.__init__(self, dest)
self.expr = expr
+ self.expr.add_edges_to(self.dest)
def __str__(self):
return "config %s depends on %s" % (self.dest, self.expr)
+ def process(self):
+ if not self.expr.evaluate():
+ self.dest.set_value(False, self)
+
class SelectClause(Clause):
def __init__(self, dest, cond):
KconfigData.Clause.__init__(self, dest)
self.cond = cond
+ self.cond.add_edges_to(self.dest)
def __str__(self):
return "select %s if %s" % (self.dest, self.cond)
+ def process(self):
+ if self.cond.evaluate():
+ self.dest.set_value(True, self)
+
def __init__(self):
self.previously_included = []
self.incl_info = None
@@ -120,11 +208,54 @@ class KconfigData:
undef = True
return undef
+ def compute_config(self):
+ if self.check_undefined():
+ raise KconfigDataError("there were undefined symbols")
+ return None
+
+ debug_print("Input:")
+ for clause in self.clauses:
+ debug_print(clause)
+
+ debug_print("\nDependency graph:")
+ for i in self.referenced_vars:
+ debug_print(i, "->", [str(x) for x in self.referenced_vars[i].outgoing])
+
+ # The reverse of the depth-first order is the topological sort
+ dfo = dict()
+ visited = set()
+ debug_print("\n")
+ def visit_fn(var):
+ debug_print(var, "has DFS number", len(dfo))
+ dfo[var] = len(dfo)
+
+ for name, v in self.referenced_vars.items():
+ self.do_default(v, False)
+ v.dfs(visited, visit_fn)
+
+ # Put higher DFS numbers and higher priorities first. This
+ # places the clauses in topological order and places defaults
+ # after assignments and dependencies.
+ self.clauses.sort(key=lambda x: (-dfo[x.dest], -x.priority()))
+
+ debug_print("\nSorted clauses:")
+ for clause in self.clauses:
+ debug_print(clause)
+ clause.process()
+
+ debug_print("")
+ values = dict()
+ for name, v in self.referenced_vars.items():
+ debug_print("Evaluating", name)
+ values[name] = v.evaluate()
+
+ return values
+
# semantic actions -------------
def do_declaration(self, var):
if (var in self.defined_vars):
- raise Exception('variable "' + var + '" defined twice')
+ raise KconfigDataError('variable "' + var + '" defined twice')
self.defined_vars.add(var.name)
@@ -201,9 +332,6 @@ class KconfigParser:
data = KconfigData()
parser = KconfigParser(data)
parser.parse_file(fp)
- if data.check_undefined():
- raise KconfigParserError(parser, "there were undefined symbols")
-
return data
def __init__(self, data):
@@ -392,7 +520,6 @@ class KconfigParser:
self.tok == TOK_SELECT or self.tok == TOK_BOOL or \
self.tok == TOK_IMPLY:
self.parse_property(var)
- self.data.do_default(var, False)
# for nicer error message
if self.tok != TOK_SOURCE and self.tok != TOK_CONFIG and \
@@ -520,5 +647,4 @@ class KconfigParser:
if __name__ == '__main__':
fname = len(sys.argv) > 1 and sys.argv[1] or 'Kconfig.test'
data = KconfigParser.parse(open(fname, 'r'))
- for i in data.clauses:
- print i
+ print data.compute_config()
--
1.8.3.1
next prev parent reply other threads:[~2019-03-04 18:20 UTC|newest]
Thread overview: 72+ messages / expand[flat|nested] mbox.gz Atom feed top
2019-03-04 18:19 [Qemu-devel] [PULL 00/54] Kconfig conversion, excluding ARM and MIPS Paolo Bonzini
2019-03-04 18:19 ` [Qemu-devel] [PULL 01/54] block: fix recursion in hw/block/dataplane Paolo Bonzini
2019-03-04 18:19 ` [Qemu-devel] [PULL 02/54] 9pfs: remove unnecessary conditionals Paolo Bonzini
2019-03-04 18:19 ` [Qemu-devel] [PULL 03/54] xtensa: rename CONFIG_XTENSA_FPGA to CONFIG_XTENSA_XTFPGA Paolo Bonzini
2019-03-04 18:19 ` [Qemu-devel] [PULL 04/54] minikconfig: add parser skeleton Paolo Bonzini
2019-03-04 18:19 ` [Qemu-devel] [PULL 05/54] minikconfig: add AST Paolo Bonzini
2019-03-04 18:19 ` Paolo Bonzini [this message]
2019-03-04 18:19 ` [Qemu-devel] [PULL 07/54] hw/display: make edid configurable Paolo Bonzini
2019-03-04 18:19 ` [Qemu-devel] [PULL 08/54] kconfig: introduce kconfig files Paolo Bonzini
2019-03-04 18:19 ` [Qemu-devel] [PULL 09/54] build: switch to Kconfig Paolo Bonzini
2019-03-04 18:19 ` [Qemu-devel] [PULL 10/54] minikconfig: implement allnoconfig and defconfig modes Paolo Bonzini
2019-03-04 18:19 ` [Qemu-devel] [PULL 11/54] kconfig: introduce CONFIG_TEST_DEVICES Paolo Bonzini
2019-03-04 18:19 ` [Qemu-devel] [PULL 12/54] ide: express dependencies with Kconfig Paolo Bonzini
2019-03-04 18:19 ` [Qemu-devel] [PULL 13/54] hw/pci/Makefile.objs: make pcie configurable Paolo Bonzini
2019-03-04 18:19 ` [Qemu-devel] [PULL 14/54] isa: express dependencies with kconfig Paolo Bonzini
2019-03-04 18:19 ` [Qemu-devel] [PULL 15/54] build: convert pci.mak to Kconfig Paolo Bonzini
2019-03-04 18:19 ` [Qemu-devel] [PULL 16/54] build: convert sound.mak " Paolo Bonzini
2019-03-04 18:19 ` [Qemu-devel] [PULL 17/54] scsi: express dependencies with Kconfig Paolo Bonzini
2019-03-04 18:19 ` [Qemu-devel] [PULL 18/54] build: convert usb.mak to Kconfig Paolo Bonzini
2019-03-04 18:19 ` [Qemu-devel] [PULL 19/54] i386: express dependencies with Kconfig Paolo Bonzini
2019-03-04 18:19 ` [Qemu-devel] [PULL 20/54] i2c: " Paolo Bonzini
2019-03-04 18:19 ` [Qemu-devel] [PULL 21/54] ptimer: " Paolo Bonzini
2019-03-04 18:19 ` [Qemu-devel] [PULL 22/54] display: express dependencies with kconfig Paolo Bonzini
2019-03-04 18:19 ` [Qemu-devel] [PULL 23/54] hyperv: " Paolo Bonzini
2019-03-04 18:19 ` [Qemu-devel] [PULL 24/54] vfio: express vfio dependencies with Kconfig Paolo Bonzini
2019-03-04 18:19 ` [Qemu-devel] [PULL 25/54] virtio: express virtio " Paolo Bonzini
2019-03-04 18:19 ` [Qemu-devel] [PULL 26/54] tpm: express " Paolo Bonzini
2019-03-04 18:19 ` [Qemu-devel] [PULL 27/54] isa: express SuperIO " Paolo Bonzini
2019-03-04 18:19 ` [Qemu-devel] [PULL 28/54] ssi: express dependencies with kconfig Paolo Bonzini
2019-03-04 18:19 ` [Qemu-devel] [PULL 29/54] sd: " Paolo Bonzini
2019-03-04 18:19 ` [Qemu-devel] [PULL 30/54] ipmi: " Paolo Bonzini
2019-03-04 18:19 ` [Qemu-devel] [PULL 31/54] i386-softmmu.mak: remove all CONFIG_* except boards definitions Paolo Bonzini
2019-03-04 18:19 ` [Qemu-devel] [PULL 32/54] ppc64: Express dependencies of 'pseries' and 'powernv' machines with kconfig Paolo Bonzini
2019-03-04 18:19 ` [Qemu-devel] [PULL 33/54] ppc: Express dependencies of the 'prep' and '40p' " Paolo Bonzini
2019-03-04 18:19 ` [Qemu-devel] [PULL 34/54] ppc: Express dependencies of the Mac " Paolo Bonzini
2019-03-04 18:19 ` [Qemu-devel] [PULL 35/54] ppc: Express dependencies of the Sam460EX " Paolo Bonzini
2019-03-04 18:19 ` [Qemu-devel] [PULL 36/54] ppc: Express dependencies of the embedded " Paolo Bonzini
2019-03-04 18:19 ` [Qemu-devel] [PULL 37/54] alpha-softmmu.mak: express dependencies with Kconfig Paolo Bonzini
2019-03-04 18:19 ` [Qemu-devel] [PULL 38/54] cris-softmmu.mak: " Paolo Bonzini
2019-03-04 18:19 ` [Qemu-devel] [PULL 39/54] hppa-softmmu.mak: " Paolo Bonzini
2019-03-04 18:20 ` [Qemu-devel] [PULL 40/54] lm32-softmmu.mak: " Paolo Bonzini
2019-03-04 23:55 ` Philippe Mathieu-Daudé
2019-03-04 18:20 ` [Qemu-devel] [PULL 41/54] m68k-softmmu.mak: " Paolo Bonzini
2019-03-04 18:20 ` [Qemu-devel] [PULL 42/54] microblaze-softmmu.mak: " Paolo Bonzini
2019-03-04 18:20 ` [Qemu-devel] [PULL 43/54] moxie-softmmu.mak: " Paolo Bonzini
2019-03-04 18:20 ` [Qemu-devel] [PULL 44/54] nios2-softmmu.mak: " Paolo Bonzini
2019-03-04 18:20 ` [Qemu-devel] [PULL 45/54] or1k-softmmu.mak: " Paolo Bonzini
2019-03-04 18:20 ` [Qemu-devel] [PULL 46/54] riscv-softmmu.mak: replace CONFIG_* with Kconfig "select" directives Paolo Bonzini
2019-03-04 18:20 ` [Qemu-devel] [PULL 47/54] s390x: express dependencies with Kconfig Paolo Bonzini
2019-03-04 18:20 ` [Qemu-devel] [PULL 48/54] sh4-softmmu.mak: " Paolo Bonzini
2019-03-04 18:20 ` [Qemu-devel] [PULL 49/54] sparc-softmmu.mak: " Paolo Bonzini
2019-03-04 18:20 ` [Qemu-devel] [PULL 50/54] sparc64-softmmu.mak: " Paolo Bonzini
2019-03-04 18:20 ` [Qemu-devel] [PULL 51/54] unicore32-softmmu.mak: " Paolo Bonzini
2019-03-04 18:20 ` [Qemu-devel] [PULL 52/54] xtensa-softmmu.mak: " Paolo Bonzini
2019-03-04 18:20 ` [Qemu-devel] [PULL 53/54] .travis.yml: test that no-default-device builds do not regress Paolo Bonzini
2019-03-04 18:20 ` [Qemu-devel] [PULL 54/54] kconfig: add documentation Paolo Bonzini
2019-03-04 20:16 ` [Qemu-devel] [PULL 00/54] Kconfig conversion, excluding ARM and MIPS no-reply
2019-03-05 9:32 ` Peter Maydell
2019-03-05 19:45 ` Thomas Huth
2019-03-05 20:08 ` Eric Blake
2019-03-05 20:29 ` Philippe Mathieu-Daudé
2019-03-06 10:12 ` Thomas Huth
2019-03-08 20:52 ` Eric Blake
2019-03-06 10:57 ` Paolo Bonzini
2019-03-06 11:04 ` Thomas Huth
2019-03-06 11:14 ` Philippe Mathieu-Daudé
2019-03-06 11:15 ` Paolo Bonzini
2019-03-07 13:25 ` Peter Maydell
2019-03-07 13:47 ` Philippe Mathieu-Daudé
2019-03-07 14:09 ` Paolo Bonzini
2019-03-07 13:55 ` Paolo Bonzini
2019-03-08 16:23 ` no-reply
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1551723614-1823-7-git-send-email-pbonzini@redhat.com \
--to=pbonzini@redhat.com \
--cc=qemu-devel@nongnu.org \
--cc=thuth@redhat.com \
--cc=yang.zhong@intel.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).