From 5d83b9a130690f879d5f33e991beabe69cb88bc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 4 Aug 2021 12:31:01 +0400 Subject: qapi: replace if condition list with dict {'all': [...]} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the simple list sugar form with a recursive structure that will accept other operators in the following commits (all, any or not). Signed-off-by: Marc-André Lureau Message-Id: <20210804083105.97531-7-marcandre.lureau@redhat.com> Reviewed-by: Markus Armbruster [Accidental code motion undone. Degenerate :forms: comment dropped. Helper _check_if() moved. Error messages tweaked. ui.json updated. Accidental changes to qapi-schema-test.json dropped.] Signed-off-by: Markus Armbruster --- scripts/qapi/common.py | 23 ++++++++++++++----- scripts/qapi/expr.py | 60 +++++++++++++++++++++++++++++--------------------- scripts/qapi/schema.py | 2 +- 3 files changed, 54 insertions(+), 31 deletions(-) (limited to 'scripts/qapi') diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py index ddc54e4368..3d7272a702 100644 --- a/scripts/qapi/common.py +++ b/scripts/qapi/common.py @@ -13,7 +13,8 @@ import re from typing import ( - List, + Any, + Dict, Match, Optional, Union, @@ -199,17 +200,29 @@ def guardend(name: str) -> str: name=c_fname(name).upper()) -def cgen_ifcond(ifcond: Union[str, List[str]]) -> str: +def cgen_ifcond(ifcond: Union[str, Dict[str, Any]]) -> str: if not ifcond: return '' - return '(' + ') && ('.join(ifcond) + ')' + if isinstance(ifcond, str): + return ifcond + oper, operands = next(iter(ifcond.items())) + oper = {'all': '&&'}[oper] + operands = [cgen_ifcond(o) for o in operands] + return '(' + (') ' + oper + ' (').join(operands) + ')' -def docgen_ifcond(ifcond: Union[str, List[str]]) -> str: + +def docgen_ifcond(ifcond: Union[str, Dict[str, Any]]) -> str: # TODO Doc generated for conditions needs polish if not ifcond: return '' - return ' and '.join(ifcond) + if isinstance(ifcond, str): + return ifcond + + oper, operands = next(iter(ifcond.items())) + oper = {'all': ' and '}[oper] + operands = [docgen_ifcond(o) for o in operands] + return '(' + oper.join(operands) + ')' def gen_if(cond: str) -> str: diff --git a/scripts/qapi/expr.py b/scripts/qapi/expr.py index cf98923fa6..d7a34655a7 100644 --- a/scripts/qapi/expr.py +++ b/scripts/qapi/expr.py @@ -259,14 +259,9 @@ def check_flags(expr: _JSONObject, info: QAPISourceInfo) -> None: def check_if(expr: _JSONObject, info: QAPISourceInfo, source: str) -> None: """ - Normalize and validate the ``if`` member of an object. + Validate the ``if`` member of an object. - The ``if`` member may be either a ``str`` or a ``List[str]``. - A ``str`` value will be normalized to ``List[str]``. - - :forms: - :sugared: ``Union[str, List[str]]`` - :canonical: ``List[str]`` + The ``if`` member may be either a ``str`` or a dict. :param expr: The expression containing the ``if`` member to validate. :param info: QAPI schema source file information. @@ -275,31 +270,46 @@ def check_if(expr: _JSONObject, info: QAPISourceInfo, source: str) -> None: :raise QAPISemError: When the "if" member fails validation, or when there are no non-empty conditions. - :return: None, ``expr`` is normalized in-place as needed. + :return: None """ - ifcond = expr.get('if') - if ifcond is None: - return - if isinstance(ifcond, list): - if not ifcond: - raise QAPISemError( - info, "'if' condition [] of %s is useless" % source) - else: - # Normalize to a list - ifcond = expr['if'] = [ifcond] + def _check_if(cond: Union[str, object]) -> None: + if isinstance(cond, str): + if not cond.strip(): + raise QAPISemError( + info, + "'if' condition '%s' of %s makes no sense" + % (cond, source)) + return - for elt in ifcond: - if not isinstance(elt, str): + if not isinstance(cond, dict): raise QAPISemError( info, - "'if' condition of %s must be a string or a list of strings" - % source) - if not elt.strip(): + "'if' condition of %s must be a string or an object" % source) + if len(cond) != 1: raise QAPISemError( info, - "'if' condition '%s' of %s makes no sense" - % (elt, source)) + "'if' condition dict of %s must have one key: " + "'all'" % source) + check_keys(cond, info, "'if' condition", [], + ["all"]) + + oper, operands = next(iter(cond.items())) + if not operands: + raise QAPISemError( + info, "'if' condition [] of %s is useless" % source) + + if oper in ("all") and not isinstance(operands, list): + raise QAPISemError( + info, "'%s' condition of %s must be an array" % (oper, source)) + for operand in operands: + _check_if(operand) + + ifcond = expr.get('if') + if ifcond is None: + return + + _check_if(ifcond) def normalize_members(members: object) -> None: diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index a9345af7b7..229d24fce9 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -32,7 +32,7 @@ from .parser import QAPISchemaParser class QAPISchemaIfCond: def __init__(self, ifcond=None): - self.ifcond = ifcond or [] + self.ifcond = ifcond or {} def cgen(self): return cgen_ifcond(self.ifcond) -- cgit v1.2.3