mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
C, add expression parsing and expr role
This commit is contained in:
parent
815a9c657d
commit
b2ca906830
@ -674,6 +674,38 @@ Explicit ref: :c:var:`Data.@data.a`. Short-hand ref: :c:var:`Data.a`.
|
||||
.. versionadded:: 3.0
|
||||
|
||||
|
||||
Inline Expressions and Types
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. rst:role:: c:expr
|
||||
c:texpr
|
||||
|
||||
Insert a C expression or type either as inline code (``cpp:expr``)
|
||||
or inline text (``cpp:texpr``). For example::
|
||||
|
||||
.. c:var:: int a = 42
|
||||
|
||||
.. c:function:: int f(int i)
|
||||
|
||||
An expression: :c:expr:`a * f(a)` (or as text: :c:texpr:`a * f(a)`).
|
||||
|
||||
A type: :c:expr:`const Data*`
|
||||
(or as text :c:texpr:`const Data*`).
|
||||
|
||||
will be rendered as follows:
|
||||
|
||||
.. c:var:: int a = 42
|
||||
|
||||
.. c:function:: int f(int i)
|
||||
|
||||
An expression: :c:expr:`a * f(a)` (or as text: :c:texpr:`a * f(a)`).
|
||||
|
||||
A type: :c:expr:`const Data*`
|
||||
(or as text :c:texpr:`const Data*`).
|
||||
|
||||
.. versionadded:: 3.0
|
||||
|
||||
|
||||
.. _cpp-domain:
|
||||
|
||||
The C++ Domain
|
||||
|
@ -14,8 +14,9 @@ from typing import (
|
||||
)
|
||||
from typing import cast
|
||||
|
||||
from docutils import nodes
|
||||
from docutils.nodes import Element, Node
|
||||
from docutils import nodes, utils
|
||||
from docutils.nodes import Element, Node, TextElement, system_message
|
||||
from docutils.parsers.rst.states import Inliner
|
||||
|
||||
from sphinx import addnodes
|
||||
from sphinx.addnodes import pending_xref, desc_signature
|
||||
@ -30,7 +31,10 @@ from sphinx.roles import XRefRole
|
||||
from sphinx.util import logging
|
||||
from sphinx.util.cfamily import (
|
||||
NoOldIdError, ASTBase, verify_description_mode, StringifyTransform,
|
||||
BaseParser, DefinitionError, identifier_re, anon_identifier_re
|
||||
BaseParser, DefinitionError, UnsupportedMultiCharacterCharLiteral,
|
||||
identifier_re, anon_identifier_re, integer_literal_re, octal_literal_re,
|
||||
hex_literal_re, binary_literal_re, float_literal_re,
|
||||
char_literal_re
|
||||
)
|
||||
from sphinx.util.docfields import Field, TypedField
|
||||
from sphinx.util.nodes import make_refnode
|
||||
@ -49,6 +53,24 @@ _keywords = [
|
||||
'_Thread_local', 'thread_local',
|
||||
]
|
||||
|
||||
# these are ordered by preceedence
|
||||
_expression_bin_ops = [
|
||||
['||'],
|
||||
['&&'],
|
||||
['|'],
|
||||
['^'],
|
||||
['&'],
|
||||
['==', '!='],
|
||||
['<=', '>=', '<', '>'],
|
||||
['<<', '>>'],
|
||||
['+', '-'],
|
||||
['*', '/', '%'],
|
||||
['.*', '->*']
|
||||
]
|
||||
_expression_unary_ops = ["++", "--", "*", "&", "+", "-", "!", "~"]
|
||||
_expression_assignment_ops = ["=", "*=", "/=", "%=", "+=", "-=",
|
||||
">>=", "<<=", "&=", "^=", "|="]
|
||||
|
||||
_max_id = 1
|
||||
_id_prefix = [None, 'c.', 'Cv2.']
|
||||
# Ids are used in lookup keys which are used across pickled files,
|
||||
@ -69,7 +91,307 @@ class _DuplicateSymbolError(Exception):
|
||||
return "Internal C duplicate symbol error:\n%s" % self.symbol.dump(0)
|
||||
|
||||
|
||||
##############################################################################################
|
||||
################################################################################
|
||||
# Expressions and Literals
|
||||
################################################################################
|
||||
|
||||
class ASTBooleanLiteral(ASTBase):
|
||||
def __init__(self, value: bool) -> None:
|
||||
self.value = value
|
||||
|
||||
def _stringify(self, transform: StringifyTransform) -> str:
|
||||
if self.value:
|
||||
return 'true'
|
||||
else:
|
||||
return 'false'
|
||||
|
||||
def describe_signature(self, signode: desc_signature, mode: str,
|
||||
env: "BuildEnvironment", symbol: "Symbol") -> None:
|
||||
signode.append(nodes.Text(str(self)))
|
||||
|
||||
|
||||
class ASTNumberLiteral(ASTBase):
|
||||
def __init__(self, data: str) -> None:
|
||||
self.data = data
|
||||
|
||||
def _stringify(self, transform: StringifyTransform) -> str:
|
||||
return self.data
|
||||
|
||||
def describe_signature(self, signode: desc_signature, mode: str,
|
||||
env: "BuildEnvironment", symbol: "Symbol") -> None:
|
||||
txt = str(self)
|
||||
signode.append(nodes.Text(txt, txt))
|
||||
|
||||
|
||||
class ASTCharLiteral(ASTBase):
|
||||
def __init__(self, prefix: str, data: str) -> None:
|
||||
self.prefix = prefix # may be None when no prefix
|
||||
self.data = data
|
||||
decoded = data.encode().decode('unicode-escape')
|
||||
if len(decoded) == 1:
|
||||
self.value = ord(decoded)
|
||||
else:
|
||||
raise UnsupportedMultiCharacterCharLiteral(decoded)
|
||||
|
||||
def _stringify(self, transform: StringifyTransform) -> str:
|
||||
if self.prefix is None:
|
||||
return "'" + self.data + "'"
|
||||
else:
|
||||
return self.prefix + "'" + self.data + "'"
|
||||
|
||||
def describe_signature(self, signode: desc_signature, mode: str,
|
||||
env: "BuildEnvironment", symbol: "Symbol") -> None:
|
||||
txt = str(self)
|
||||
signode.append(nodes.Text(txt, txt))
|
||||
|
||||
|
||||
class ASTStringLiteral(ASTBase):
|
||||
def __init__(self, data: str) -> None:
|
||||
self.data = data
|
||||
|
||||
def _stringify(self, transform: StringifyTransform) -> str:
|
||||
return self.data
|
||||
|
||||
def describe_signature(self, signode: desc_signature, mode: str,
|
||||
env: "BuildEnvironment", symbol: "Symbol") -> None:
|
||||
txt = str(self)
|
||||
signode.append(nodes.Text(txt, txt))
|
||||
|
||||
|
||||
class ASTParenExpr(ASTBase):
|
||||
def __init__(self, expr):
|
||||
self.expr = expr
|
||||
|
||||
def _stringify(self, transform: StringifyTransform) -> str:
|
||||
return '(' + transform(self.expr) + ')'
|
||||
|
||||
def get_id(self, version: int) -> str:
|
||||
return self.expr.get_id(version)
|
||||
|
||||
def describe_signature(self, signode: desc_signature, mode: str,
|
||||
env: "BuildEnvironment", symbol: "Symbol") -> None:
|
||||
signode.append(nodes.Text('(', '('))
|
||||
self.expr.describe_signature(signode, mode, env, symbol)
|
||||
signode.append(nodes.Text(')', ')'))
|
||||
|
||||
|
||||
class ASTBinOpExpr(ASTBase):
|
||||
def __init__(self, exprs, ops):
|
||||
assert len(exprs) > 0
|
||||
assert len(exprs) == len(ops) + 1
|
||||
self.exprs = exprs
|
||||
self.ops = ops
|
||||
|
||||
def _stringify(self, transform: StringifyTransform) -> str:
|
||||
res = []
|
||||
res.append(transform(self.exprs[0]))
|
||||
for i in range(1, len(self.exprs)):
|
||||
res.append(' ')
|
||||
res.append(self.ops[i - 1])
|
||||
res.append(' ')
|
||||
res.append(transform(self.exprs[i]))
|
||||
return ''.join(res)
|
||||
|
||||
def describe_signature(self, signode: desc_signature, mode: str,
|
||||
env: "BuildEnvironment", symbol: "Symbol") -> None:
|
||||
self.exprs[0].describe_signature(signode, mode, env, symbol)
|
||||
for i in range(1, len(self.exprs)):
|
||||
signode.append(nodes.Text(' '))
|
||||
signode.append(nodes.Text(self.ops[i - 1]))
|
||||
signode.append(nodes.Text(' '))
|
||||
self.exprs[i].describe_signature(signode, mode, env, symbol)
|
||||
|
||||
|
||||
class ASTAssignmentExpr(ASTBase):
|
||||
def __init__(self, exprs, ops):
|
||||
assert len(exprs) > 0
|
||||
assert len(exprs) == len(ops) + 1
|
||||
self.exprs = exprs
|
||||
self.ops = ops
|
||||
|
||||
def _stringify(self, transform: StringifyTransform) -> str:
|
||||
res = []
|
||||
res.append(transform(self.exprs[0]))
|
||||
for i in range(1, len(self.exprs)):
|
||||
res.append(' ')
|
||||
res.append(self.ops[i - 1])
|
||||
res.append(' ')
|
||||
res.append(transform(self.exprs[i]))
|
||||
return ''.join(res)
|
||||
|
||||
def describe_signature(self, signode: desc_signature, mode: str,
|
||||
env: "BuildEnvironment", symbol: "Symbol") -> None:
|
||||
self.exprs[0].describe_signature(signode, mode, env, symbol)
|
||||
for i in range(1, len(self.exprs)):
|
||||
signode.append(nodes.Text(' '))
|
||||
signode.append(nodes.Text(self.ops[i - 1]))
|
||||
signode.append(nodes.Text(' '))
|
||||
self.exprs[i].describe_signature(signode, mode, env, symbol)
|
||||
|
||||
|
||||
class ASTCastExpr(ASTBase):
|
||||
def __init__(self, typ, expr):
|
||||
self.typ = typ
|
||||
self.expr = expr
|
||||
|
||||
def _stringify(self, transform: StringifyTransform) -> str:
|
||||
res = ['(']
|
||||
res.append(transform(self.typ))
|
||||
res.append(')')
|
||||
res.append(transform(self.expr))
|
||||
return ''.join(res)
|
||||
|
||||
def describe_signature(self, signode: desc_signature, mode: str,
|
||||
env: "BuildEnvironment", symbol: "Symbol") -> None:
|
||||
signode.append(nodes.Text('('))
|
||||
self.typ.describe_signature(signode, mode, env, symbol)
|
||||
signode.append(nodes.Text(')'))
|
||||
self.expr.describe_signature(signode, mode, env, symbol)
|
||||
|
||||
|
||||
class ASTUnaryOpExpr(ASTBase):
|
||||
def __init__(self, op, expr):
|
||||
self.op = op
|
||||
self.expr = expr
|
||||
|
||||
def _stringify(self, transform: StringifyTransform) -> str:
|
||||
return transform(self.op) + transform(self.expr)
|
||||
|
||||
def describe_signature(self, signode: desc_signature, mode: str,
|
||||
env: "BuildEnvironment", symbol: "Symbol") -> None:
|
||||
signode.append(nodes.Text(self.op))
|
||||
self.expr.describe_signature(signode, mode, env, symbol)
|
||||
|
||||
|
||||
class ASTSizeofType(ASTBase):
|
||||
def __init__(self, typ):
|
||||
self.typ = typ
|
||||
|
||||
def _stringify(self, transform: StringifyTransform) -> str:
|
||||
return "sizeof(" + transform(self.typ) + ")"
|
||||
|
||||
def describe_signature(self, signode: desc_signature, mode: str,
|
||||
env: "BuildEnvironment", symbol: "Symbol") -> None:
|
||||
signode.append(nodes.Text('sizeof('))
|
||||
self.typ.describe_signature(signode, mode, env, symbol)
|
||||
signode.append(nodes.Text(')'))
|
||||
|
||||
|
||||
class ASTSizeofExpr(ASTBase):
|
||||
def __init__(self, expr):
|
||||
self.expr = expr
|
||||
|
||||
def _stringify(self, transform: StringifyTransform) -> str:
|
||||
return "sizeof " + transform(self.expr)
|
||||
|
||||
def describe_signature(self, signode: desc_signature, mode: str,
|
||||
env: "BuildEnvironment", symbol: "Symbol") -> None:
|
||||
signode.append(nodes.Text('sizeof '))
|
||||
self.expr.describe_signature(signode, mode, env, symbol)
|
||||
|
||||
|
||||
class ASTAlignofExpr(ASTBase):
|
||||
def __init__(self, typ):
|
||||
self.typ = typ
|
||||
|
||||
def _stringify(self, transform: StringifyTransform) -> str:
|
||||
return "alignof(" + transform(self.typ) + ")"
|
||||
|
||||
def describe_signature(self, signode: desc_signature, mode: str,
|
||||
env: "BuildEnvironment", symbol: "Symbol") -> None:
|
||||
signode.append(nodes.Text('alignof('))
|
||||
self.typ.describe_signature(signode, mode, env, symbol)
|
||||
signode.append(nodes.Text(')'))
|
||||
|
||||
|
||||
class ASTPostfixCallExpr(ASTBase):
|
||||
def __init__(self, lst: Union["ASTParenExprList", "ASTBracedInitList"]) -> None:
|
||||
self.lst = lst
|
||||
|
||||
def _stringify(self, transform: StringifyTransform) -> str:
|
||||
return transform(self.lst)
|
||||
|
||||
def describe_signature(self, signode: desc_signature, mode: str,
|
||||
env: "BuildEnvironment", symbol: "Symbol") -> None:
|
||||
self.lst.describe_signature(signode, mode, env, symbol)
|
||||
|
||||
|
||||
class ASTPostfixArray(ASTBase):
|
||||
def __init__(self, expr):
|
||||
self.expr = expr
|
||||
|
||||
def _stringify(self, transform: StringifyTransform) -> str:
|
||||
return '[' + transform(self.expr) + ']'
|
||||
|
||||
def describe_signature(self, signode: desc_signature, mode: str,
|
||||
env: "BuildEnvironment", symbol: "Symbol") -> None:
|
||||
signode.append(nodes.Text('['))
|
||||
self.expr.describe_signature(signode, mode, env, symbol)
|
||||
signode.append(nodes.Text(']'))
|
||||
|
||||
|
||||
class ASTPostfixInc(ASTBase):
|
||||
def _stringify(self, transform: StringifyTransform) -> str:
|
||||
return '++'
|
||||
|
||||
def describe_signature(self, signode: desc_signature, mode: str,
|
||||
env: "BuildEnvironment", symbol: "Symbol") -> None:
|
||||
signode.append(nodes.Text('++'))
|
||||
|
||||
|
||||
class ASTPostfixDec(ASTBase):
|
||||
def _stringify(self, transform: StringifyTransform) -> str:
|
||||
return '--'
|
||||
|
||||
def describe_signature(self, signode: desc_signature, mode: str,
|
||||
env: "BuildEnvironment", symbol: "Symbol") -> None:
|
||||
signode.append(nodes.Text('--'))
|
||||
|
||||
|
||||
class ASTPostfixMember(ASTBase):
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
|
||||
def _stringify(self, transform: StringifyTransform) -> str:
|
||||
return '.' + transform(self.name)
|
||||
|
||||
def describe_signature(self, signode: desc_signature, mode: str,
|
||||
env: "BuildEnvironment", symbol: "Symbol") -> None:
|
||||
signode.append(nodes.Text('.'))
|
||||
self.name.describe_signature(signode, 'noneIsName', env, symbol)
|
||||
|
||||
|
||||
class ASTPostfixMemberOfPointer(ASTBase):
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
|
||||
def _stringify(self, transform: StringifyTransform) -> str:
|
||||
return '->' + transform(self.name)
|
||||
|
||||
def describe_signature(self, signode: desc_signature, mode: str,
|
||||
env: "BuildEnvironment", symbol: "Symbol") -> None:
|
||||
signode.append(nodes.Text('->'))
|
||||
self.name.describe_signature(signode, 'noneIsName', env, symbol)
|
||||
|
||||
|
||||
class ASTPostfixExpr(ASTBase):
|
||||
def __init__(self, prefix, postFixes):
|
||||
assert len(postFixes) > 0
|
||||
self.prefix = prefix
|
||||
self.postFixes = postFixes
|
||||
|
||||
def _stringify(self, transform: StringifyTransform) -> str:
|
||||
res = [transform(self.prefix)]
|
||||
for p in self.postFixes:
|
||||
res.append(transform(p))
|
||||
return ''.join(res)
|
||||
|
||||
def describe_signature(self, signode: desc_signature, mode: str,
|
||||
env: "BuildEnvironment", symbol: "Symbol") -> None:
|
||||
self.prefix.describe_signature(signode, mode, env, symbol)
|
||||
for p in self.postFixes:
|
||||
p.describe_signature(signode, mode, env, symbol)
|
||||
|
||||
|
||||
class ASTFallbackExpr(ASTBase):
|
||||
def __init__(self, expr):
|
||||
@ -452,7 +774,9 @@ class ASTDeclaratorPtr(ASTBase):
|
||||
return self.next.function_params
|
||||
|
||||
def require_space_after_declSpecs(self) -> bool:
|
||||
return True
|
||||
return self.const or self.volatile or self.restrict or \
|
||||
len(self.attrs) > 0 or \
|
||||
self.next.require_space_after_declSpecs()
|
||||
|
||||
def _stringify(self, transform: StringifyTransform) -> str:
|
||||
res = ['*']
|
||||
@ -598,9 +922,7 @@ class ASTDeclaratorNameBitField(ASTBase):
|
||||
if self.declId:
|
||||
res.append(transform(self.declId))
|
||||
res.append(" : ")
|
||||
# TODO: when size is properly parsed
|
||||
# res.append(transform(self.size))
|
||||
res.append(self.size)
|
||||
res.append(transform(self.size))
|
||||
return ''.join(res)
|
||||
|
||||
def describe_signature(self, signode: desc_signature, mode: str,
|
||||
@ -609,11 +931,58 @@ class ASTDeclaratorNameBitField(ASTBase):
|
||||
if self.declId:
|
||||
self.declId.describe_signature(signode, mode, env, symbol)
|
||||
signode += nodes.Text(' : ', ' : ')
|
||||
# TODO: when size is properly parsed
|
||||
# self.size.describe_signature(signode, mode, env, symbol)
|
||||
self.size.describe_signature(signode, mode, env, symbol)
|
||||
signode += nodes.Text(self.size, self.size)
|
||||
|
||||
|
||||
class ASTParenExprList(ASTBase):
|
||||
def __init__(self, exprs: List[Any]) -> None:
|
||||
self.exprs = exprs
|
||||
|
||||
def _stringify(self, transform: StringifyTransform) -> str:
|
||||
exprs = [transform(e) for e in self.exprs]
|
||||
return '(%s)' % ', '.join(exprs)
|
||||
|
||||
def describe_signature(self, signode: desc_signature, mode: str,
|
||||
env: "BuildEnvironment", symbol: "Symbol") -> None:
|
||||
verify_description_mode(mode)
|
||||
signode.append(nodes.Text('('))
|
||||
first = True
|
||||
for e in self.exprs:
|
||||
if not first:
|
||||
signode.append(nodes.Text(', '))
|
||||
else:
|
||||
first = False
|
||||
e.describe_signature(signode, mode, env, symbol)
|
||||
signode.append(nodes.Text(')'))
|
||||
|
||||
|
||||
class ASTBracedInitList(ASTBase):
|
||||
def __init__(self, exprs: List[Any], trailingComma: bool) -> None:
|
||||
self.exprs = exprs
|
||||
self.trailingComma = trailingComma
|
||||
|
||||
def _stringify(self, transform: StringifyTransform) -> str:
|
||||
exprs = [transform(e) for e in self.exprs]
|
||||
trailingComma = ',' if self.trailingComma else ''
|
||||
return '{%s%s}' % (', '.join(exprs), trailingComma)
|
||||
|
||||
def describe_signature(self, signode: desc_signature, mode: str,
|
||||
env: "BuildEnvironment", symbol: "Symbol") -> None:
|
||||
verify_description_mode(mode)
|
||||
signode.append(nodes.Text('{'))
|
||||
first = True
|
||||
for e in self.exprs:
|
||||
if not first:
|
||||
signode.append(nodes.Text(', '))
|
||||
else:
|
||||
first = False
|
||||
e.describe_signature(signode, mode, env, symbol)
|
||||
if self.trailingComma:
|
||||
signode.append(nodes.Text(','))
|
||||
signode.append(nodes.Text('}'))
|
||||
|
||||
|
||||
class ASTInitializer(ASTBase):
|
||||
def __init__(self, value: Any, hasAssign: bool = True) -> None:
|
||||
self.value = value
|
||||
@ -1526,6 +1895,25 @@ class DefinitionParser(BaseParser):
|
||||
|
||||
_prefix_keys = ('struct', 'enum', 'union')
|
||||
|
||||
def _parse_string(self):
|
||||
if self.current_char != '"':
|
||||
return None
|
||||
startPos = self.pos
|
||||
self.pos += 1
|
||||
escape = False
|
||||
while True:
|
||||
if self.eof:
|
||||
self.fail("Unexpected end during inside string.")
|
||||
elif self.current_char == '"' and not escape:
|
||||
self.pos += 1
|
||||
break
|
||||
elif self.current_char == '\\':
|
||||
escape = True
|
||||
else:
|
||||
escape = False
|
||||
self.pos += 1
|
||||
return self.definition[startPos:self.pos]
|
||||
|
||||
def _parse_attribute(self) -> Any:
|
||||
return None
|
||||
# self.skip_ws()
|
||||
@ -1586,23 +1974,340 @@ class DefinitionParser(BaseParser):
|
||||
|
||||
return None
|
||||
|
||||
def _parse_literal(self):
|
||||
# -> integer-literal
|
||||
# | character-literal
|
||||
# | floating-literal
|
||||
# | string-literal
|
||||
# | boolean-literal -> "false" | "true"
|
||||
self.skip_ws()
|
||||
if self.skip_word('true'):
|
||||
return ASTBooleanLiteral(True)
|
||||
if self.skip_word('false'):
|
||||
return ASTBooleanLiteral(False)
|
||||
for regex in [float_literal_re, binary_literal_re, hex_literal_re,
|
||||
integer_literal_re, octal_literal_re]:
|
||||
pos = self.pos
|
||||
if self.match(regex):
|
||||
while self.current_char in 'uUlLfF':
|
||||
self.pos += 1
|
||||
return ASTNumberLiteral(self.definition[pos:self.pos])
|
||||
|
||||
string = self._parse_string()
|
||||
if string is not None:
|
||||
return ASTStringLiteral(string)
|
||||
|
||||
# character-literal
|
||||
if self.match(char_literal_re):
|
||||
prefix = self.last_match.group(1) # may be None when no prefix
|
||||
data = self.last_match.group(2)
|
||||
try:
|
||||
return ASTCharLiteral(prefix, data)
|
||||
except UnicodeDecodeError as e:
|
||||
self.fail("Can not handle character literal. Internal error was: %s" % e)
|
||||
except UnsupportedMultiCharacterCharLiteral:
|
||||
self.fail("Can not handle character literal"
|
||||
" resulting in multiple decoded characters.")
|
||||
return None
|
||||
|
||||
def _parse_paren_expression(self):
|
||||
# "(" expression ")"
|
||||
if self.current_char != '(':
|
||||
return None
|
||||
self.pos += 1
|
||||
res = self._parse_expression()
|
||||
self.skip_ws()
|
||||
if not self.skip_string(')'):
|
||||
self.fail("Expected ')' in end of parenthesized expression.")
|
||||
return ASTParenExpr(res)
|
||||
|
||||
def _parse_primary_expression(self):
|
||||
# literal
|
||||
# "(" expression ")"
|
||||
# id-expression -> we parse this with _parse_nested_name
|
||||
self.skip_ws()
|
||||
res = self._parse_literal()
|
||||
if res is not None:
|
||||
return res
|
||||
res = self._parse_paren_expression()
|
||||
if res is not None:
|
||||
return res
|
||||
return self._parse_nested_name()
|
||||
|
||||
def _parse_initializer_list(self, name: str, open: str, close: str
|
||||
) -> Tuple[List[Any], bool]:
|
||||
# Parse open and close with the actual initializer-list inbetween
|
||||
# -> initializer-clause '...'[opt]
|
||||
# | initializer-list ',' initializer-clause '...'[opt]
|
||||
# TODO: designators
|
||||
self.skip_ws()
|
||||
if not self.skip_string_and_ws(open):
|
||||
return None, None
|
||||
if self.skip_string(close):
|
||||
return [], False
|
||||
|
||||
exprs = []
|
||||
trailingComma = False
|
||||
while True:
|
||||
self.skip_ws()
|
||||
expr = self._parse_expression()
|
||||
self.skip_ws()
|
||||
exprs.append(expr)
|
||||
self.skip_ws()
|
||||
if self.skip_string(close):
|
||||
break
|
||||
if not self.skip_string_and_ws(','):
|
||||
self.fail("Error in %s, expected ',' or '%s'." % (name, close))
|
||||
if self.current_char == close and close == '}':
|
||||
self.pos += 1
|
||||
trailingComma = True
|
||||
break
|
||||
return exprs, trailingComma
|
||||
|
||||
def _parse_paren_expression_list(self) -> ASTParenExprList:
|
||||
# -> '(' expression-list ')'
|
||||
# though, we relax it to also allow empty parens
|
||||
# as it's needed in some cases
|
||||
#
|
||||
# expression-list
|
||||
# -> initializer-list
|
||||
exprs, trailingComma = self._parse_initializer_list("parenthesized expression-list",
|
||||
'(', ')')
|
||||
if exprs is None:
|
||||
return None
|
||||
return ASTParenExprList(exprs)
|
||||
|
||||
def _parse_braced_init_list(self) -> ASTBracedInitList:
|
||||
# -> '{' initializer-list ','[opt] '}'
|
||||
# | '{' '}'
|
||||
exprs, trailingComma = self._parse_initializer_list("braced-init-list", '{', '}')
|
||||
if exprs is None:
|
||||
return None
|
||||
return ASTBracedInitList(exprs, trailingComma)
|
||||
|
||||
def _parse_postfix_expression(self):
|
||||
# -> primary
|
||||
# | postfix "[" expression "]"
|
||||
# | postfix "[" braced-init-list [opt] "]"
|
||||
# | postfix "(" expression-list [opt] ")"
|
||||
# | postfix "." id-expression
|
||||
# | postfix "->" id-expression
|
||||
# | postfix "++"
|
||||
# | postfix "--"
|
||||
|
||||
prefix = self._parse_primary_expression()
|
||||
|
||||
# and now parse postfixes
|
||||
postFixes = [] # type: List[Any]
|
||||
while True:
|
||||
self.skip_ws()
|
||||
if self.skip_string_and_ws('['):
|
||||
expr = self._parse_expression()
|
||||
self.skip_ws()
|
||||
if not self.skip_string(']'):
|
||||
self.fail("Expected ']' in end of postfix expression.")
|
||||
postFixes.append(ASTPostfixArray(expr))
|
||||
continue
|
||||
if self.skip_string('.'):
|
||||
if self.skip_string('*'):
|
||||
# don't steal the dot
|
||||
self.pos -= 2
|
||||
elif self.skip_string('..'):
|
||||
# don't steal the dot
|
||||
self.pos -= 3
|
||||
else:
|
||||
name = self._parse_nested_name()
|
||||
postFixes.append(ASTPostfixMember(name))
|
||||
continue
|
||||
if self.skip_string('->'):
|
||||
if self.skip_string('*'):
|
||||
# don't steal the arrow
|
||||
self.pos -= 3
|
||||
else:
|
||||
name = self._parse_nested_name()
|
||||
postFixes.append(ASTPostfixMemberOfPointer(name))
|
||||
continue
|
||||
if self.skip_string('++'):
|
||||
postFixes.append(ASTPostfixInc())
|
||||
continue
|
||||
if self.skip_string('--'):
|
||||
postFixes.append(ASTPostfixDec())
|
||||
continue
|
||||
lst = self._parse_paren_expression_list()
|
||||
if lst is not None:
|
||||
postFixes.append(ASTPostfixCallExpr(lst))
|
||||
continue
|
||||
break
|
||||
if len(postFixes) == 0:
|
||||
return prefix
|
||||
else:
|
||||
return ASTPostfixExpr(prefix, postFixes)
|
||||
|
||||
def _parse_unary_expression(self):
|
||||
# -> postfix
|
||||
# | "++" cast
|
||||
# | "--" cast
|
||||
# | unary-operator cast -> (* | & | + | - | ! | ~) cast
|
||||
# The rest:
|
||||
# | "sizeof" unary
|
||||
# | "sizeof" "(" type-id ")"
|
||||
# | "alignof" "(" type-id ")"
|
||||
self.skip_ws()
|
||||
for op in _expression_unary_ops:
|
||||
# TODO: hmm, should we be able to backtrack here?
|
||||
if self.skip_string(op):
|
||||
expr = self._parse_cast_expression()
|
||||
return ASTUnaryOpExpr(op, expr)
|
||||
if self.skip_word_and_ws('sizeof'):
|
||||
if self.skip_string_and_ws('('):
|
||||
typ = self._parse_type(named=False)
|
||||
self.skip_ws()
|
||||
if not self.skip_string(')'):
|
||||
self.fail("Expecting ')' to end 'sizeof'.")
|
||||
return ASTSizeofType(typ)
|
||||
expr = self._parse_unary_expression()
|
||||
return ASTSizeofExpr(expr)
|
||||
if self.skip_word_and_ws('alignof'):
|
||||
if not self.skip_string_and_ws('('):
|
||||
self.fail("Expecting '(' after 'alignof'.")
|
||||
typ = self._parse_type(named=False)
|
||||
self.skip_ws()
|
||||
if not self.skip_string(')'):
|
||||
self.fail("Expecting ')' to end 'alignof'.")
|
||||
return ASTAlignofExpr(typ)
|
||||
return self._parse_postfix_expression()
|
||||
|
||||
def _parse_cast_expression(self):
|
||||
# -> unary | "(" type-id ")" cast
|
||||
pos = self.pos
|
||||
self.skip_ws()
|
||||
if self.skip_string('('):
|
||||
try:
|
||||
typ = self._parse_type(False)
|
||||
if not self.skip_string(')'):
|
||||
self.fail("Expected ')' in cast expression.")
|
||||
expr = self._parse_cast_expression()
|
||||
return ASTCastExpr(typ, expr)
|
||||
except DefinitionError as exCast:
|
||||
self.pos = pos
|
||||
try:
|
||||
return self._parse_unary_expression()
|
||||
except DefinitionError as exUnary:
|
||||
errs = []
|
||||
errs.append((exCast, "If type cast expression"))
|
||||
errs.append((exUnary, "If unary expression"))
|
||||
raise self._make_multi_error(errs, "Error in cast expression.")
|
||||
else:
|
||||
return self._parse_unary_expression()
|
||||
|
||||
def _parse_logical_or_expression(self):
|
||||
# logical-or = logical-and ||
|
||||
# logical-and = inclusive-or &&
|
||||
# inclusive-or = exclusive-or |
|
||||
# exclusive-or = and ^
|
||||
# and = equality &
|
||||
# equality = relational ==, !=
|
||||
# relational = shift <, >, <=, >=
|
||||
# shift = additive <<, >>
|
||||
# additive = multiplicative +, -
|
||||
# multiplicative = pm *, /, %
|
||||
# pm = cast .*, ->*
|
||||
def _parse_bin_op_expr(self, opId):
|
||||
if opId + 1 == len(_expression_bin_ops):
|
||||
def parser():
|
||||
return self._parse_cast_expression()
|
||||
else:
|
||||
def parser():
|
||||
return _parse_bin_op_expr(self, opId + 1)
|
||||
exprs = []
|
||||
ops = []
|
||||
exprs.append(parser())
|
||||
while True:
|
||||
self.skip_ws()
|
||||
pos = self.pos
|
||||
oneMore = False
|
||||
for op in _expression_bin_ops[opId]:
|
||||
if not self.skip_string(op):
|
||||
continue
|
||||
if op == '&' and self.current_char == '&':
|
||||
# don't split the && 'token'
|
||||
self.pos -= 1
|
||||
# and btw. && has lower precedence, so we are done
|
||||
break
|
||||
try:
|
||||
expr = parser()
|
||||
exprs.append(expr)
|
||||
ops.append(op)
|
||||
oneMore = True
|
||||
break
|
||||
except DefinitionError:
|
||||
self.pos = pos
|
||||
if not oneMore:
|
||||
break
|
||||
return ASTBinOpExpr(exprs, ops)
|
||||
return _parse_bin_op_expr(self, 0)
|
||||
|
||||
def _parse_conditional_expression_tail(self, orExprHead):
|
||||
# -> "?" expression ":" assignment-expression
|
||||
return None
|
||||
|
||||
def _parse_assignment_expression(self):
|
||||
# -> conditional-expression
|
||||
# | logical-or-expression assignment-operator initializer-clause
|
||||
# -> conditional-expression ->
|
||||
# logical-or-expression
|
||||
# | logical-or-expression "?" expression ":" assignment-expression
|
||||
# | logical-or-expression assignment-operator initializer-clause
|
||||
exprs = []
|
||||
ops = []
|
||||
orExpr = self._parse_logical_or_expression()
|
||||
exprs.append(orExpr)
|
||||
# TODO: handle ternary with _parse_conditional_expression_tail
|
||||
while True:
|
||||
oneMore = False
|
||||
self.skip_ws()
|
||||
for op in _expression_assignment_ops:
|
||||
if not self.skip_string(op):
|
||||
continue
|
||||
expr = self._parse_logical_or_expression()
|
||||
exprs.append(expr)
|
||||
ops.append(op)
|
||||
oneMore = True
|
||||
if not oneMore:
|
||||
break
|
||||
if len(ops) == 0:
|
||||
return orExpr
|
||||
else:
|
||||
return ASTAssignmentExpr(exprs, ops)
|
||||
|
||||
def _parse_constant_expression(self):
|
||||
# -> conditional-expression
|
||||
orExpr = self._parse_logical_or_expression()
|
||||
# TODO: use _parse_conditional_expression_tail
|
||||
return orExpr
|
||||
|
||||
def _parse_expression(self):
|
||||
# -> assignment-expression
|
||||
# | expression "," assignment-expresion
|
||||
# TODO: actually parse the second production
|
||||
return self._parse_assignment_expression()
|
||||
|
||||
def _parse_expression_fallback(self, end, parser, allow=True):
|
||||
# Stupidly "parse" an expression.
|
||||
# 'end' should be a list of characters which ends the expression.
|
||||
|
||||
# first try to use the provided parser
|
||||
# TODO: copy-paste and adapt real expression parser
|
||||
# prevPos = self.pos
|
||||
# try:
|
||||
# return parser()
|
||||
# except DefinitionError as e:
|
||||
# # some places (e.g., template parameters) we really don't want to use fallback,
|
||||
# # and for testing we may want to globally disable it
|
||||
# if not allow or not self.allowFallbackExpressionParsing:
|
||||
# raise
|
||||
# self.warn("Parsing of expression failed. Using fallback parser."
|
||||
# " Error was:\n%s" % e)
|
||||
# self.pos = prevPos
|
||||
prevPos = self.pos
|
||||
try:
|
||||
return parser()
|
||||
except DefinitionError as e:
|
||||
# some places (e.g., template parameters) we really don't want to use fallback,
|
||||
# and for testing we may want to globally disable it
|
||||
if not allow or not self.allowFallbackExpressionParsing:
|
||||
raise
|
||||
self.warn("Parsing of expression failed. Using fallback parser."
|
||||
" Error was:\n%s" % e)
|
||||
self.pos = prevPos
|
||||
# and then the fallback scanning
|
||||
assert end is not None
|
||||
self.skip_ws()
|
||||
@ -1831,7 +2536,7 @@ class DefinitionParser(BaseParser):
|
||||
continue
|
||||
|
||||
def parser():
|
||||
return self._parse_expression(inTemplate=False)
|
||||
return self._parse_expression()
|
||||
|
||||
value = self._parse_expression_fallback([']'], parser)
|
||||
if not self.skip_string(']'):
|
||||
@ -1846,9 +2551,7 @@ class DefinitionParser(BaseParser):
|
||||
if named and paramMode == 'type' and typed:
|
||||
self.skip_ws()
|
||||
if self.skip_string(':'):
|
||||
# TODO: copy-paste and adapt proper parser
|
||||
# size = self._parse_constant_expression(inTemplate=False)
|
||||
size = self.read_rest().strip()
|
||||
size = self._parse_constant_expression()
|
||||
return ASTDeclaratorNameBitField(declId=declId, size=size)
|
||||
return ASTDeclaratorNameParam(declId=declId, arrayOps=arrayOps,
|
||||
param=param)
|
||||
@ -1939,10 +2642,9 @@ class DefinitionParser(BaseParser):
|
||||
if not self.skip_string('='):
|
||||
return None
|
||||
|
||||
# TODO: implement
|
||||
# bracedInit = self._parse_braced_init_list()
|
||||
# if bracedInit is not None:
|
||||
# return ASTInitializer(bracedInit)
|
||||
bracedInit = self._parse_braced_init_list()
|
||||
if bracedInit is not None:
|
||||
return ASTInitializer(bracedInit)
|
||||
|
||||
if outer == 'member':
|
||||
fallbackEnd = [] # type: List[str]
|
||||
@ -2077,7 +2779,7 @@ class DefinitionParser(BaseParser):
|
||||
self.skip_ws()
|
||||
|
||||
def parser():
|
||||
return self._parse_constant_expression(inTemplate=False)
|
||||
return self._parse_constant_expression()
|
||||
|
||||
initVal = self._parse_expression_fallback([], parser)
|
||||
init = ASTInitializer(initVal)
|
||||
@ -2119,6 +2821,26 @@ class DefinitionParser(BaseParser):
|
||||
self.assert_end()
|
||||
return name
|
||||
|
||||
def parse_expression(self):
|
||||
pos = self.pos
|
||||
try:
|
||||
expr = self._parse_expression()
|
||||
self.skip_ws()
|
||||
self.assert_end()
|
||||
except DefinitionError as exExpr:
|
||||
self.pos = pos
|
||||
try:
|
||||
expr = self._parse_type(False)
|
||||
self.skip_ws()
|
||||
self.assert_end()
|
||||
except DefinitionError as exType:
|
||||
header = "Error when parsing (type) expression."
|
||||
errs = []
|
||||
errs.append((exExpr, "If expression"))
|
||||
errs.append((exType, "If type"))
|
||||
raise self._make_multi_error(errs, header)
|
||||
return expr
|
||||
|
||||
|
||||
def _make_phony_error_name() -> ASTNestedName:
|
||||
return ASTNestedName([ASTIdentifier("PhonyNameDueToError")], rooted=False)
|
||||
@ -2365,6 +3087,44 @@ class CXRefRole(XRefRole):
|
||||
return title, target
|
||||
|
||||
|
||||
class CExprRole:
|
||||
def __init__(self, asCode: bool) -> None:
|
||||
if asCode:
|
||||
# render the expression as inline code
|
||||
self.class_type = 'c-expr'
|
||||
self.node_type = nodes.literal # type: Type[TextElement]
|
||||
else:
|
||||
# render the expression as inline text
|
||||
self.class_type = 'c-texpr'
|
||||
self.node_type = nodes.inline
|
||||
|
||||
def __call__(self, typ: str, rawtext: str, text: str, lineno: int,
|
||||
inliner: Inliner, options: Dict = {}, content: List[str] = []
|
||||
) -> Tuple[List[Node], List[system_message]]:
|
||||
class Warner:
|
||||
def warn(self, msg: str) -> None:
|
||||
inliner.reporter.warning(msg, line=lineno)
|
||||
text = utils.unescape(text).replace('\n', ' ')
|
||||
env = inliner.document.settings.env
|
||||
parser = DefinitionParser(text, Warner())
|
||||
# attempt to mimic XRefRole classes, except that...
|
||||
classes = ['xref', 'c', self.class_type]
|
||||
try:
|
||||
ast = parser.parse_expression()
|
||||
except DefinitionError as ex:
|
||||
Warner().warn('Unparseable C expression: %r\n%s' % (text, ex))
|
||||
# see below
|
||||
return [self.node_type(text, text, classes=classes)], []
|
||||
parentSymbol = env.temp_data.get('cpp:parent_symbol', None)
|
||||
if parentSymbol is None:
|
||||
parentSymbol = env.domaindata['c']['root_symbol']
|
||||
# ...most if not all of these classes should really apply to the individual references,
|
||||
# not the container node
|
||||
signode = self.node_type(classes=classes)
|
||||
ast.describe_signature(signode, 'markType', env, parentSymbol)
|
||||
return [signode], []
|
||||
|
||||
|
||||
class CDomain(Domain):
|
||||
"""C language domain."""
|
||||
name = 'c'
|
||||
@ -2399,6 +3159,8 @@ class CDomain(Domain):
|
||||
'enum': CXRefRole(),
|
||||
'enumerator': CXRefRole(),
|
||||
'type': CXRefRole(),
|
||||
'expr': CExprRole(asCode=True),
|
||||
'texpr': CExprRole(asCode=False)
|
||||
}
|
||||
initial_data = {
|
||||
'root_symbol': Symbol(None, None, None, None),
|
||||
|
@ -36,7 +36,10 @@ from sphinx.transforms.post_transforms import ReferencesResolver
|
||||
from sphinx.util import logging
|
||||
from sphinx.util.cfamily import (
|
||||
NoOldIdError, ASTBase, verify_description_mode, StringifyTransform,
|
||||
BaseParser, DefinitionError, identifier_re, anon_identifier_re
|
||||
BaseParser, DefinitionError, UnsupportedMultiCharacterCharLiteral,
|
||||
identifier_re, anon_identifier_re, integer_literal_re, octal_literal_re,
|
||||
hex_literal_re, binary_literal_re, float_literal_re,
|
||||
char_literal_re
|
||||
)
|
||||
from sphinx.util.docfields import Field, GroupedField
|
||||
from sphinx.util.docutils import SphinxDirective
|
||||
@ -296,37 +299,6 @@ T = TypeVar('T')
|
||||
nested-name
|
||||
"""
|
||||
|
||||
_integer_literal_re = re.compile(r'[1-9][0-9]*')
|
||||
_octal_literal_re = re.compile(r'0[0-7]*')
|
||||
_hex_literal_re = re.compile(r'0[xX][0-9a-fA-F][0-9a-fA-F]*')
|
||||
_binary_literal_re = re.compile(r'0[bB][01][01]*')
|
||||
_integer_suffix_re = re.compile(r'')
|
||||
_float_literal_re = re.compile(r'''(?x)
|
||||
[+-]?(
|
||||
# decimal
|
||||
([0-9]+[eE][+-]?[0-9]+)
|
||||
| ([0-9]*\.[0-9]+([eE][+-]?[0-9]+)?)
|
||||
| ([0-9]+\.([eE][+-]?[0-9]+)?)
|
||||
# hex
|
||||
| (0[xX][0-9a-fA-F]+[pP][+-]?[0-9a-fA-F]+)
|
||||
| (0[xX][0-9a-fA-F]*\.[0-9a-fA-F]+([pP][+-]?[0-9a-fA-F]+)?)
|
||||
| (0[xX][0-9a-fA-F]+\.([pP][+-]?[0-9a-fA-F]+)?)
|
||||
)
|
||||
''')
|
||||
_char_literal_re = re.compile(r'''(?x)
|
||||
((?:u8)|u|U|L)?
|
||||
'(
|
||||
(?:[^\\'])
|
||||
| (\\(
|
||||
(?:['"?\\abfnrtv])
|
||||
| (?:[0-7]{1,3})
|
||||
| (?:x[0-9a-fA-F]{2})
|
||||
| (?:u[0-9a-fA-F]{4})
|
||||
| (?:U[0-9a-fA-F]{8})
|
||||
))
|
||||
)'
|
||||
''')
|
||||
|
||||
_string_re = re.compile(r"[LuU8]?('([^'\\]*(?:\\.[^'\\]*)*)'"
|
||||
r'|"([^"\\]*(?:\\.[^"\\]*)*)")', re.S)
|
||||
_visibility_re = re.compile(r'\b(public|private|protected)\b')
|
||||
@ -716,15 +688,6 @@ class ASTNumberLiteral(ASTBase):
|
||||
signode.append(nodes.Text(txt, txt))
|
||||
|
||||
|
||||
class UnsupportedMultiCharacterCharLiteral(Exception):
|
||||
@property
|
||||
def decoded(self) -> str:
|
||||
warnings.warn('%s.decoded is deprecated. '
|
||||
'Coerce the instance to a string instead.' % self.__class__.__name__,
|
||||
RemovedInSphinx40Warning, stacklevel=2)
|
||||
return str(self)
|
||||
|
||||
|
||||
class ASTCharLiteral(ASTBase):
|
||||
def __init__(self, prefix: str, data: str) -> None:
|
||||
self.prefix = prefix # may be None when no prefix
|
||||
@ -4561,8 +4524,8 @@ class DefinitionParser(BaseParser):
|
||||
return ASTBooleanLiteral(True)
|
||||
if self.skip_word('false'):
|
||||
return ASTBooleanLiteral(False)
|
||||
for regex in [_float_literal_re, _binary_literal_re, _hex_literal_re,
|
||||
_integer_literal_re, _octal_literal_re]:
|
||||
for regex in [float_literal_re, binary_literal_re, hex_literal_re,
|
||||
integer_literal_re, octal_literal_re]:
|
||||
pos = self.pos
|
||||
if self.match(regex):
|
||||
while self.current_char in 'uUlLfF':
|
||||
@ -4574,7 +4537,7 @@ class DefinitionParser(BaseParser):
|
||||
return ASTStringLiteral(string)
|
||||
|
||||
# character-literal
|
||||
if self.match(_char_literal_re):
|
||||
if self.match(char_literal_re):
|
||||
prefix = self.last_match.group(1) # may be None when no prefix
|
||||
data = self.last_match.group(2)
|
||||
try:
|
||||
|
@ -31,6 +31,35 @@ identifier_re = re.compile(r'''(?x)
|
||||
)
|
||||
[a-zA-Z0-9_]*\b
|
||||
''')
|
||||
integer_literal_re = re.compile(r'[1-9][0-9]*')
|
||||
octal_literal_re = re.compile(r'0[0-7]*')
|
||||
hex_literal_re = re.compile(r'0[xX][0-9a-fA-F][0-9a-fA-F]*')
|
||||
binary_literal_re = re.compile(r'0[bB][01][01]*')
|
||||
float_literal_re = re.compile(r'''(?x)
|
||||
[+-]?(
|
||||
# decimal
|
||||
([0-9]+[eE][+-]?[0-9]+)
|
||||
| ([0-9]*\.[0-9]+([eE][+-]?[0-9]+)?)
|
||||
| ([0-9]+\.([eE][+-]?[0-9]+)?)
|
||||
# hex
|
||||
| (0[xX][0-9a-fA-F]+[pP][+-]?[0-9a-fA-F]+)
|
||||
| (0[xX][0-9a-fA-F]*\.[0-9a-fA-F]+([pP][+-]?[0-9a-fA-F]+)?)
|
||||
| (0[xX][0-9a-fA-F]+\.([pP][+-]?[0-9a-fA-F]+)?)
|
||||
)
|
||||
''')
|
||||
char_literal_re = re.compile(r'''(?x)
|
||||
((?:u8)|u|U|L)?
|
||||
'(
|
||||
(?:[^\\'])
|
||||
| (\\(
|
||||
(?:['"?\\abfnrtv])
|
||||
| (?:[0-7]{1,3})
|
||||
| (?:x[0-9a-fA-F]{2})
|
||||
| (?:u[0-9a-fA-F]{4})
|
||||
| (?:U[0-9a-fA-F]{8})
|
||||
))
|
||||
)'
|
||||
''')
|
||||
|
||||
|
||||
def verify_description_mode(mode: str) -> None:
|
||||
@ -79,6 +108,15 @@ class ASTBase:
|
||||
return '<%s>' % self.__class__.__name__
|
||||
|
||||
|
||||
class UnsupportedMultiCharacterCharLiteral(Exception):
|
||||
@property
|
||||
def decoded(self) -> str:
|
||||
warnings.warn('%s.decoded is deprecated. '
|
||||
'Coerce the instance to a string instead.' % self.__class__.__name__,
|
||||
RemovedInSphinx40Warning, stacklevel=2)
|
||||
return str(self)
|
||||
|
||||
|
||||
class DefinitionError(Exception):
|
||||
@property
|
||||
def description(self) -> str:
|
||||
|
@ -36,3 +36,11 @@ directives
|
||||
|
||||
- :c:member:`A.@data.a`
|
||||
- :c:member:`A.a`
|
||||
|
||||
- :c:expr:`unsigned int`
|
||||
- :c:texpr:`unsigned int`
|
||||
|
||||
.. c:var:: A a
|
||||
|
||||
- :c:expr:`a->b`
|
||||
- :c:texpr:`a->b`
|
||||
|
@ -86,17 +86,34 @@ def check(name, input, idDict, output=None):
|
||||
|
||||
|
||||
def test_expressions():
|
||||
return # TODO
|
||||
def exprCheck(expr, id, id4=None):
|
||||
ids = 'IE1CIA%s_1aE'
|
||||
idDict = {2: ids % expr, 3: ids % id}
|
||||
if id4 is not None:
|
||||
idDict[4] = ids % id4
|
||||
check('class', 'template<> C<a[%s]>' % expr, idDict)
|
||||
def exprCheck(expr, output=None):
|
||||
parser = DefinitionParser(expr, None)
|
||||
parser.allowFallbackExpressionParsing = False
|
||||
ast = parser.parse_expression()
|
||||
parser.assert_end()
|
||||
# first a simple check of the AST
|
||||
if output is None:
|
||||
output = expr
|
||||
res = str(ast)
|
||||
if res != output:
|
||||
print("")
|
||||
print("Input: ", input)
|
||||
print("Result: ", res)
|
||||
print("Expected: ", output)
|
||||
raise DefinitionError("")
|
||||
# type expressions
|
||||
exprCheck('int*')
|
||||
exprCheck('int *const*')
|
||||
exprCheck('int *volatile*')
|
||||
exprCheck('int *restrict*')
|
||||
exprCheck('int *(*)(double)')
|
||||
exprCheck('const int*')
|
||||
|
||||
# actual expressions
|
||||
|
||||
# primary
|
||||
exprCheck('nullptr', 'LDnE')
|
||||
exprCheck('true', 'L1E')
|
||||
exprCheck('false', 'L0E')
|
||||
exprCheck('true')
|
||||
exprCheck('false')
|
||||
ints = ['5', '0', '075', '0x0123456789ABCDEF', '0XF', '0b1', '0B1']
|
||||
unsignedSuffix = ['', 'u', 'U']
|
||||
longSuffix = ['', 'l', 'L', 'll', 'LL']
|
||||
@ -104,9 +121,9 @@ def test_expressions():
|
||||
for u in unsignedSuffix:
|
||||
for l in longSuffix:
|
||||
expr = i + u + l
|
||||
exprCheck(expr, 'L' + expr + 'E')
|
||||
exprCheck(expr)
|
||||
expr = i + l + u
|
||||
exprCheck(expr, 'L' + expr + 'E')
|
||||
exprCheck(expr)
|
||||
for suffix in ['', 'f', 'F', 'l', 'L']:
|
||||
for e in [
|
||||
'5e42', '5e+42', '5e-42',
|
||||
@ -114,134 +131,90 @@ def test_expressions():
|
||||
'.5', '.5e42', '.5e+42', '.5e-42',
|
||||
'5.0', '5.0e42', '5.0e+42', '5.0e-42']:
|
||||
expr = e + suffix
|
||||
exprCheck(expr, 'L' + expr + 'E')
|
||||
exprCheck(expr)
|
||||
for e in [
|
||||
'ApF', 'Ap+F', 'Ap-F',
|
||||
'A.', 'A.pF', 'A.p+F', 'A.p-F',
|
||||
'.A', '.ApF', '.Ap+F', '.Ap-F',
|
||||
'A.B', 'A.BpF', 'A.Bp+F', 'A.Bp-F']:
|
||||
expr = "0x" + e + suffix
|
||||
exprCheck(expr, 'L' + expr + 'E')
|
||||
exprCheck('"abc\\"cba"', 'LA8_KcE') # string
|
||||
exprCheck('this', 'fpT')
|
||||
exprCheck(expr)
|
||||
exprCheck('"abc\\"cba"') # string
|
||||
# character literals
|
||||
for p, t in [('', 'c'), ('u8', 'c'), ('u', 'Ds'), ('U', 'Di'), ('L', 'w')]:
|
||||
exprCheck(p + "'a'", t + "97")
|
||||
exprCheck(p + "'\\n'", t + "10")
|
||||
exprCheck(p + "'\\012'", t + "10")
|
||||
exprCheck(p + "'\\0'", t + "0")
|
||||
exprCheck(p + "'\\x0a'", t + "10")
|
||||
exprCheck(p + "'\\x0A'", t + "10")
|
||||
exprCheck(p + "'\\u0a42'", t + "2626")
|
||||
exprCheck(p + "'\\u0A42'", t + "2626")
|
||||
exprCheck(p + "'\\U0001f34c'", t + "127820")
|
||||
exprCheck(p + "'\\U0001F34C'", t + "127820")
|
||||
for p in ['', 'u8', 'u', 'U', 'L']:
|
||||
exprCheck(p + "'a'")
|
||||
exprCheck(p + "'\\n'")
|
||||
exprCheck(p + "'\\012'")
|
||||
exprCheck(p + "'\\0'")
|
||||
exprCheck(p + "'\\x0a'")
|
||||
exprCheck(p + "'\\x0A'")
|
||||
exprCheck(p + "'\\u0a42'")
|
||||
exprCheck(p + "'\\u0A42'")
|
||||
exprCheck(p + "'\\U0001f34c'")
|
||||
exprCheck(p + "'\\U0001F34C'")
|
||||
|
||||
# TODO: user-defined lit
|
||||
exprCheck('(... + Ns)', '(... + Ns)', id4='flpl2Ns')
|
||||
exprCheck('(Ns + ...)', '(Ns + ...)', id4='frpl2Ns')
|
||||
exprCheck('(Ns + ... + 0)', '(Ns + ... + 0)', id4='fLpl2NsL0E')
|
||||
exprCheck('(5)', 'L5E')
|
||||
exprCheck('C', '1C')
|
||||
exprCheck('(5)')
|
||||
exprCheck('C')
|
||||
# postfix
|
||||
exprCheck('A(2)', 'cl1AL2EE')
|
||||
exprCheck('A[2]', 'ix1AL2E')
|
||||
exprCheck('a.b.c', 'dtdt1a1b1c')
|
||||
exprCheck('a->b->c', 'ptpt1a1b1c')
|
||||
exprCheck('i++', 'pp1i')
|
||||
exprCheck('i--', 'mm1i')
|
||||
exprCheck('dynamic_cast<T&>(i)++', 'ppdcR1T1i')
|
||||
exprCheck('static_cast<T&>(i)++', 'ppscR1T1i')
|
||||
exprCheck('reinterpret_cast<T&>(i)++', 'pprcR1T1i')
|
||||
exprCheck('const_cast<T&>(i)++', 'ppccR1T1i')
|
||||
exprCheck('typeid(T).name', 'dtti1T4name')
|
||||
exprCheck('typeid(a + b).name', 'dttepl1a1b4name')
|
||||
exprCheck('A(2)')
|
||||
exprCheck('A[2]')
|
||||
exprCheck('a.b.c')
|
||||
exprCheck('a->b->c')
|
||||
exprCheck('i++')
|
||||
exprCheck('i--')
|
||||
# unary
|
||||
exprCheck('++5', 'pp_L5E')
|
||||
exprCheck('--5', 'mm_L5E')
|
||||
exprCheck('*5', 'deL5E')
|
||||
exprCheck('&5', 'adL5E')
|
||||
exprCheck('+5', 'psL5E')
|
||||
exprCheck('-5', 'ngL5E')
|
||||
exprCheck('!5', 'ntL5E')
|
||||
exprCheck('~5', 'coL5E')
|
||||
exprCheck('sizeof...(a)', 'sZ1a')
|
||||
exprCheck('sizeof(T)', 'st1T')
|
||||
exprCheck('sizeof -42', 'szngL42E')
|
||||
exprCheck('alignof(T)', 'at1T')
|
||||
exprCheck('noexcept(-42)', 'nxngL42E')
|
||||
# new-expression
|
||||
exprCheck('new int', 'nw_iE')
|
||||
exprCheck('new volatile int', 'nw_ViE')
|
||||
exprCheck('new int[42]', 'nw_AL42E_iE')
|
||||
exprCheck('new int()', 'nw_ipiE')
|
||||
exprCheck('new int(5, 42)', 'nw_ipiL5EL42EE')
|
||||
exprCheck('::new int', 'nw_iE')
|
||||
exprCheck('new int{}', 'nw_iilE')
|
||||
exprCheck('new int{5, 42}', 'nw_iilL5EL42EE')
|
||||
# delete-expression
|
||||
exprCheck('delete p', 'dl1p')
|
||||
exprCheck('delete [] p', 'da1p')
|
||||
exprCheck('::delete p', 'dl1p')
|
||||
exprCheck('::delete [] p', 'da1p')
|
||||
exprCheck('++5')
|
||||
exprCheck('--5')
|
||||
exprCheck('*5')
|
||||
exprCheck('&5')
|
||||
exprCheck('+5')
|
||||
exprCheck('-5')
|
||||
exprCheck('!5')
|
||||
exprCheck('~5')
|
||||
exprCheck('sizeof(T)')
|
||||
exprCheck('sizeof -42')
|
||||
exprCheck('alignof(T)')
|
||||
# cast
|
||||
exprCheck('(int)2', 'cviL2E')
|
||||
exprCheck('(int)2')
|
||||
# binary op
|
||||
exprCheck('5 || 42', 'ooL5EL42E')
|
||||
exprCheck('5 && 42', 'aaL5EL42E')
|
||||
exprCheck('5 | 42', 'orL5EL42E')
|
||||
exprCheck('5 ^ 42', 'eoL5EL42E')
|
||||
exprCheck('5 & 42', 'anL5EL42E')
|
||||
exprCheck('5 || 42')
|
||||
exprCheck('5 && 42')
|
||||
exprCheck('5 | 42')
|
||||
exprCheck('5 ^ 42')
|
||||
exprCheck('5 & 42')
|
||||
# ['==', '!=']
|
||||
exprCheck('5 == 42', 'eqL5EL42E')
|
||||
exprCheck('5 != 42', 'neL5EL42E')
|
||||
exprCheck('5 == 42')
|
||||
exprCheck('5 != 42')
|
||||
# ['<=', '>=', '<', '>']
|
||||
exprCheck('5 <= 42', 'leL5EL42E')
|
||||
exprCheck('5 >= 42', 'geL5EL42E')
|
||||
exprCheck('5 < 42', 'ltL5EL42E')
|
||||
exprCheck('5 > 42', 'gtL5EL42E')
|
||||
exprCheck('5 <= 42')
|
||||
exprCheck('5 >= 42')
|
||||
exprCheck('5 < 42')
|
||||
exprCheck('5 > 42')
|
||||
# ['<<', '>>']
|
||||
exprCheck('5 << 42', 'lsL5EL42E')
|
||||
exprCheck('5 >> 42', 'rsL5EL42E')
|
||||
exprCheck('5 << 42')
|
||||
exprCheck('5 >> 42')
|
||||
# ['+', '-']
|
||||
exprCheck('5 + 42', 'plL5EL42E')
|
||||
exprCheck('5 - 42', 'miL5EL42E')
|
||||
exprCheck('5 + 42')
|
||||
exprCheck('5 - 42')
|
||||
# ['*', '/', '%']
|
||||
exprCheck('5 * 42', 'mlL5EL42E')
|
||||
exprCheck('5 / 42', 'dvL5EL42E')
|
||||
exprCheck('5 % 42', 'rmL5EL42E')
|
||||
exprCheck('5 * 42')
|
||||
exprCheck('5 / 42')
|
||||
exprCheck('5 % 42')
|
||||
# ['.*', '->*']
|
||||
exprCheck('5 .* 42', 'dsL5EL42E')
|
||||
exprCheck('5 ->* 42', 'pmL5EL42E')
|
||||
# conditional
|
||||
# TODO
|
||||
# assignment
|
||||
exprCheck('a = 5', 'aS1aL5E')
|
||||
exprCheck('a *= 5', 'mL1aL5E')
|
||||
exprCheck('a /= 5', 'dV1aL5E')
|
||||
exprCheck('a %= 5', 'rM1aL5E')
|
||||
exprCheck('a += 5', 'pL1aL5E')
|
||||
exprCheck('a -= 5', 'mI1aL5E')
|
||||
exprCheck('a >>= 5', 'rS1aL5E')
|
||||
exprCheck('a <<= 5', 'lS1aL5E')
|
||||
exprCheck('a &= 5', 'aN1aL5E')
|
||||
exprCheck('a ^= 5', 'eO1aL5E')
|
||||
exprCheck('a |= 5', 'oR1aL5E')
|
||||
|
||||
# Additional tests
|
||||
# a < expression that starts with something that could be a template
|
||||
exprCheck('A < 42', 'lt1AL42E')
|
||||
check('function', 'template<> void f(A<B, 2> &v)',
|
||||
{2: "IE1fR1AI1BX2EE", 3: "IE1fR1AI1BXL2EEE", 4: "IE1fvR1AI1BXL2EEE"})
|
||||
exprCheck('A<1>::value', 'N1AIXL1EEE5valueE')
|
||||
check('class', "template<int T = 42> A", {2: "I_iE1A"})
|
||||
check('enumerator', 'A = std::numeric_limits<unsigned long>::max()', {2: "1A"})
|
||||
|
||||
exprCheck('operator()()', 'clclE')
|
||||
exprCheck('operator()<int>()', 'clclIiEE')
|
||||
|
||||
# pack expansion
|
||||
exprCheck('a(b(c, 1 + d...)..., e(f..., g))', 'cl1aspcl1b1cspplL1E1dEcl1esp1f1gEE')
|
||||
exprCheck('a = 5')
|
||||
exprCheck('a *= 5')
|
||||
exprCheck('a /= 5')
|
||||
exprCheck('a %= 5')
|
||||
exprCheck('a += 5')
|
||||
exprCheck('a -= 5')
|
||||
exprCheck('a >>= 5')
|
||||
exprCheck('a <<= 5')
|
||||
exprCheck('a &= 5')
|
||||
exprCheck('a ^= 5')
|
||||
exprCheck('a |= 5')
|
||||
|
||||
|
||||
def test_type_definitions():
|
||||
@ -249,6 +222,9 @@ def test_type_definitions():
|
||||
|
||||
check('type', "bool *b", {1: 'b'})
|
||||
check('type', "bool *const b", {1: 'b'})
|
||||
check('type', "bool *const *b", {1: 'b'})
|
||||
check('type', "bool *volatile *b", {1: 'b'})
|
||||
check('type', "bool *restrict *b", {1: 'b'})
|
||||
check('type', "bool *volatile const b", {1: 'b'})
|
||||
check('type', "bool *volatile const b", {1: 'b'})
|
||||
check('type', "bool *volatile const *b", {1: 'b'})
|
||||
@ -394,38 +370,22 @@ def test_anon_definitions():
|
||||
|
||||
|
||||
def test_initializers():
|
||||
return # TODO
|
||||
idsMember = {1: 'v__T', 2: '1v'}
|
||||
idsFunction = {1: 'f__T', 2: '1f1T'}
|
||||
idsTemplate = {2: 'I_1TE1fv', 4: 'I_1TE1fvv'}
|
||||
idsMember = {1: 'v'}
|
||||
idsFunction = {1: 'f'}
|
||||
# no init
|
||||
check('member', 'T v', idsMember)
|
||||
check('function', 'void f(T v)', idsFunction)
|
||||
check('function', 'template<T v> void f()', idsTemplate)
|
||||
# with '=', assignment-expression
|
||||
check('member', 'T v = 42', idsMember)
|
||||
check('function', 'void f(T v = 42)', idsFunction)
|
||||
check('function', 'template<T v = 42> void f()', idsTemplate)
|
||||
# with '=', braced-init
|
||||
check('member', 'T v = {}', idsMember)
|
||||
check('function', 'void f(T v = {})', idsFunction)
|
||||
check('function', 'template<T v = {}> void f()', idsTemplate)
|
||||
check('member', 'T v = {42, 42, 42}', idsMember)
|
||||
check('function', 'void f(T v = {42, 42, 42})', idsFunction)
|
||||
check('function', 'template<T v = {42, 42, 42}> void f()', idsTemplate)
|
||||
check('member', 'T v = {42, 42, 42,}', idsMember)
|
||||
check('function', 'void f(T v = {42, 42, 42,})', idsFunction)
|
||||
check('function', 'template<T v = {42, 42, 42,}> void f()', idsTemplate)
|
||||
check('member', 'T v = {42, 42, args...}', idsMember)
|
||||
check('function', 'void f(T v = {42, 42, args...})', idsFunction)
|
||||
check('function', 'template<T v = {42, 42, args...}> void f()', idsTemplate)
|
||||
# without '=', braced-init
|
||||
check('member', 'T v{}', idsMember)
|
||||
check('member', 'T v{42, 42, 42}', idsMember)
|
||||
check('member', 'T v{42, 42, 42,}', idsMember)
|
||||
check('member', 'T v{42, 42, args...}', idsMember)
|
||||
# other
|
||||
check('member', 'T v = T{}', idsMember)
|
||||
# TODO: designator-list
|
||||
|
||||
|
||||
def test_attributes():
|
||||
|
Loading…
Reference in New Issue
Block a user