@@ -46,7 +46,7 @@
from .common import c_name
from .error import QAPISemError
-from .parser import QAPIDoc
+from .parser import ParsedExpression
from .source import QAPISourceInfo
@@ -517,7 +517,7 @@ class ExpressionType(str, Enum):
}
-def check_exprs(exprs: List[_JSObject]) -> List[_JSObject]:
+def check_exprs(exprs: List[ParsedExpression]) -> List[ParsedExpression]:
"""
Validate and normalize a list of parsed QAPI schema expressions. [RW]
@@ -526,49 +526,33 @@ def check_exprs(exprs: List[_JSObject]) -> List[_JSObject]:
:param exprs: The list of expressions to normalize/validate.
"""
- for expr_elem in exprs:
- # Expression
- assert isinstance(expr_elem['expr'], dict)
- expr: Expression = expr_elem['expr']
- for key in expr.keys():
- assert isinstance(key, str)
-
- # QAPISourceInfo
- assert isinstance(expr_elem['info'], QAPISourceInfo)
- info: QAPISourceInfo = expr_elem['info']
-
- # Optional[QAPIDoc]
- tmp = expr_elem.get('doc')
- assert tmp is None or isinstance(tmp, QAPIDoc)
- doc: Optional[QAPIDoc] = tmp
-
+ for expr in exprs:
for kind in ExpressionType:
- if kind in expr:
+ if kind in expr.expr:
meta = kind
break
else:
- raise QAPISemError(info, "expression is missing metatype")
+ raise QAPISemError(expr.info, "expression is missing metatype")
if meta == ExpressionType.INCLUDE:
continue
- name = cast(str, expr[meta]) # asserted right below:
- check_name_is_str(name, info, "'%s'" % meta.value)
- info.set_defn(meta.value, name)
- check_defn_name_str(name, info, meta.value)
+ name = cast(str, expr.expr[meta]) # asserted right below:
+ check_name_is_str(name, expr.info, "'%s'" % meta.value)
+ expr.info.set_defn(meta.value, name)
+ check_defn_name_str(name, expr.info, meta.value)
- if doc:
- if doc.symbol != name:
- raise QAPISemError(
- info, "documentation comment is for '%s'" % doc.symbol)
- doc.check_expr(expr)
- elif info.pragma.doc_required:
- raise QAPISemError(info,
- "documentation comment required")
+ if expr.doc:
+ if expr.doc.symbol != name:
+ msg = f"documentation comment is for '{expr.doc.symbol}'"
+ raise QAPISemError(expr.info, msg)
+ expr.doc.check_expr(expr.expr)
+ elif expr.info.pragma.doc_required:
+ raise QAPISemError(expr.info, "documentation comment required")
- _CHECK_FN[meta](expr, info)
- check_if(expr, info, meta.value)
- check_features(expr.get('features'), info)
- check_flags(expr, info)
+ _CHECK_FN[meta](expr.expr, expr.info)
+ check_if(expr.expr, expr.info, meta.value)
+ check_features(expr.expr.get('features'), expr.info)
+ check_flags(expr.expr, expr.info)
return exprs
@@ -19,9 +19,8 @@
import os
import re
from typing import (
- Any,
- Dict,
List,
+ NamedTuple,
Optional,
Set,
Type,
@@ -34,13 +33,18 @@
from .source import QAPISourceInfo
-Expression = Dict[str, Any]
_Value = Union[List[object], 'OrderedDict[str, object]', str, bool]
# Necessary imprecision: mypy does not (yet?) support recursive types;
# so we must stub out that recursion with 'object'.
# Note, we do not support numerics or null in this parser.
+class ParsedExpression(NamedTuple):
+ expr: 'OrderedDict[str, object]'
+ info: QAPISourceInfo
+ doc: Optional['QAPIDoc']
+
+
class QAPIParseError(QAPISourceError):
"""Error class for all QAPI schema parsing errors."""
T = TypeVar('T', bound='QAPIParseError')
@@ -87,7 +91,7 @@ def __init__(self,
self.line_pos = 0
# Parser output:
- self.exprs: List[Expression] = []
+ self.exprs: List[ParsedExpression] = []
self.docs: List[QAPIDoc] = []
# Showtime!
@@ -139,8 +143,7 @@ def _parse(self) -> None:
"value of 'include' must be a string")
incl_fname = os.path.join(os.path.dirname(self._fname),
include)
- self.exprs.append({'expr': {'include': incl_fname},
- 'info': info})
+ self._add_expr(OrderedDict({'include': incl_fname}), info)
exprs_include = self._include(include, info, incl_fname,
self._included)
if exprs_include:
@@ -157,20 +160,21 @@ def _parse(self) -> None:
for name, value in pragma.items():
self._pragma(name, value, info)
else:
- expr_elem = {'expr': expr,
- 'info': info}
- if cur_doc:
- if not cur_doc.symbol:
- raise QAPISemError(
- cur_doc.info, "definition documentation required")
- expr_elem['doc'] = cur_doc
- self.exprs.append(expr_elem)
+ if cur_doc and not cur_doc.symbol:
+ raise QAPISemError(
+ cur_doc.info, "definition documentation required")
+ self._add_expr(expr, info, cur_doc)
cur_doc = None
self.reject_expr_doc(cur_doc)
def _parse_error(self, msg: str) -> QAPIParseError:
return QAPIParseError.make(self, msg)
+ def _add_expr(self, expr: 'OrderedDict[str, object]',
+ info: QAPISourceInfo,
+ doc: Optional['QAPIDoc'] = None) -> None:
+ self.exprs.append(ParsedExpression(expr, info, doc))
+
@classmethod
def reject_expr_doc(cls, doc: Optional['QAPIDoc']) -> None:
if doc and doc.symbol:
@@ -1106,9 +1106,9 @@ def _def_event(self, expr, info, doc):
def _def_exprs(self, exprs):
for expr_elem in exprs:
- expr = expr_elem['expr']
- info = expr_elem['info']
- doc = expr_elem.get('doc')
+ expr = expr_elem.expr
+ info = expr_elem.info
+ doc = expr_elem.doc
if 'enum' in expr:
self._def_enum_type(expr, info, doc)
elif 'struct' in expr:
This is an immutable, named, typed tuple; it's nicer than arbitrary dicts for passing data around when using strict typing. Turn parser.exprs into a list of ParsedExpressions instead, and adjust expr.py to match. Signed-off-by: John Snow <jsnow@redhat.com> --- scripts/qapi/expr.py | 56 +++++++++++++++--------------------------- scripts/qapi/parser.py | 32 +++++++++++++----------- scripts/qapi/schema.py | 6 ++--- 3 files changed, 41 insertions(+), 53 deletions(-)