Implement basic concept parsing
- requires clauses are collected into a Value and not interpreted at this time
This commit is contained in:
parent
e23c4db96d
commit
e935959ad3
@ -83,6 +83,7 @@ class PlyLexer:
|
|||||||
"char16_t",
|
"char16_t",
|
||||||
"char32_t",
|
"char32_t",
|
||||||
"class",
|
"class",
|
||||||
|
"concept",
|
||||||
"const",
|
"const",
|
||||||
"constexpr",
|
"constexpr",
|
||||||
"const_cast",
|
"const_cast",
|
||||||
@ -121,6 +122,7 @@ class PlyLexer:
|
|||||||
"public",
|
"public",
|
||||||
"register",
|
"register",
|
||||||
"reinterpret_cast",
|
"reinterpret_cast",
|
||||||
|
"requires",
|
||||||
"return",
|
"return",
|
||||||
"short",
|
"short",
|
||||||
"signed",
|
"signed",
|
||||||
|
@ -22,6 +22,7 @@ from .types import (
|
|||||||
AutoSpecifier,
|
AutoSpecifier,
|
||||||
BaseClass,
|
BaseClass,
|
||||||
ClassDecl,
|
ClassDecl,
|
||||||
|
Concept,
|
||||||
DecltypeSpecifier,
|
DecltypeSpecifier,
|
||||||
DecoratedType,
|
DecoratedType,
|
||||||
EnumDecl,
|
EnumDecl,
|
||||||
@ -537,7 +538,7 @@ class CxxParser:
|
|||||||
self._finish_class_decl(old_state)
|
self._finish_class_decl(old_state)
|
||||||
|
|
||||||
#
|
#
|
||||||
# Template parsing
|
# Template and concept parsing
|
||||||
#
|
#
|
||||||
|
|
||||||
def _parse_template_type_parameter(
|
def _parse_template_type_parameter(
|
||||||
@ -640,6 +641,8 @@ class CxxParser:
|
|||||||
self._parse_using(tok, doxygen, template)
|
self._parse_using(tok, doxygen, template)
|
||||||
elif tok.type == "friend":
|
elif tok.type == "friend":
|
||||||
self._parse_friend_decl(tok, doxygen, template)
|
self._parse_friend_decl(tok, doxygen, template)
|
||||||
|
elif tok.type == "concept":
|
||||||
|
self._parse_concept(tok, doxygen, template)
|
||||||
else:
|
else:
|
||||||
self._parse_declarations(tok, doxygen, template)
|
self._parse_declarations(tok, doxygen, template)
|
||||||
|
|
||||||
@ -750,6 +753,32 @@ class CxxParser:
|
|||||||
self.state, TemplateInst(typename, extern, doxygen)
|
self.state, TemplateInst(typename, extern, doxygen)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def _parse_concept(
|
||||||
|
self,
|
||||||
|
tok: LexToken,
|
||||||
|
doxygen: typing.Optional[str],
|
||||||
|
template: TemplateDecl,
|
||||||
|
) -> None:
|
||||||
|
name = self._next_token_must_be("NAME")
|
||||||
|
self._next_token_must_be("=")
|
||||||
|
|
||||||
|
# not trying to understand this for now
|
||||||
|
raw_constraint = self._create_value(self._consume_value_until([], ",", ";"))
|
||||||
|
|
||||||
|
state = self.state
|
||||||
|
if isinstance(state, ClassBlockState):
|
||||||
|
raise CxxParseError("concept cannot be defined in a class")
|
||||||
|
|
||||||
|
self.visitor.on_concept(
|
||||||
|
state,
|
||||||
|
Concept(
|
||||||
|
template=template,
|
||||||
|
name=name.value,
|
||||||
|
raw_constraint=raw_constraint,
|
||||||
|
doxygen=doxygen,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
#
|
#
|
||||||
# Attributes
|
# Attributes
|
||||||
#
|
#
|
||||||
|
@ -34,6 +34,7 @@ from dataclasses import dataclass, field
|
|||||||
|
|
||||||
from .types import (
|
from .types import (
|
||||||
ClassDecl,
|
ClassDecl,
|
||||||
|
Concept,
|
||||||
EnumDecl,
|
EnumDecl,
|
||||||
Field,
|
Field,
|
||||||
ForwardDecl,
|
ForwardDecl,
|
||||||
@ -113,6 +114,9 @@ class NamespaceScope:
|
|||||||
using_alias: typing.List[UsingAlias] = field(default_factory=list)
|
using_alias: typing.List[UsingAlias] = field(default_factory=list)
|
||||||
ns_alias: typing.List[NamespaceAlias] = field(default_factory=list)
|
ns_alias: typing.List[NamespaceAlias] = field(default_factory=list)
|
||||||
|
|
||||||
|
#: Concepts
|
||||||
|
concepts: typing.List[Concept] = field(default_factory=list)
|
||||||
|
|
||||||
#: Explicit template instantiations
|
#: Explicit template instantiations
|
||||||
template_insts: typing.List[TemplateInst] = field(default_factory=list)
|
template_insts: typing.List[TemplateInst] = field(default_factory=list)
|
||||||
|
|
||||||
@ -243,6 +247,9 @@ class SimpleCxxVisitor:
|
|||||||
def on_namespace_end(self, state: SNamespaceBlockState) -> None:
|
def on_namespace_end(self, state: SNamespaceBlockState) -> None:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def on_concept(self, state: SNonClassBlockState, concept: Concept) -> None:
|
||||||
|
state.user_data.concepts.append(concept)
|
||||||
|
|
||||||
def on_namespace_alias(
|
def on_namespace_alias(
|
||||||
self, state: SNonClassBlockState, alias: NamespaceAlias
|
self, state: SNonClassBlockState, alias: NamespaceAlias
|
||||||
) -> None:
|
) -> None:
|
||||||
|
@ -551,6 +551,31 @@ class TemplateInst:
|
|||||||
doxygen: typing.Optional[str] = None
|
doxygen: typing.Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Concept:
|
||||||
|
"""
|
||||||
|
Preliminary support for consuming headers that contain concepts, but
|
||||||
|
not trying to actually make sense of them at this time. If this is
|
||||||
|
something you care about, pull requests are welcomed!
|
||||||
|
|
||||||
|
.. code-block:: c++
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
concept Meowable = is_meowable<T>;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
concept Addable = requires (T x) { x + x; };
|
||||||
|
"""
|
||||||
|
|
||||||
|
template: TemplateDecl
|
||||||
|
name: str
|
||||||
|
|
||||||
|
#: In the future this will be removed if we fully parse the expression
|
||||||
|
raw_constraint: Value
|
||||||
|
|
||||||
|
doxygen: typing.Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class ForwardDecl:
|
class ForwardDecl:
|
||||||
"""
|
"""
|
||||||
|
@ -8,6 +8,7 @@ else:
|
|||||||
|
|
||||||
|
|
||||||
from .types import (
|
from .types import (
|
||||||
|
Concept,
|
||||||
EnumDecl,
|
EnumDecl,
|
||||||
Field,
|
Field,
|
||||||
ForwardDecl,
|
ForwardDecl,
|
||||||
@ -89,6 +90,14 @@ class CxxVisitor(Protocol):
|
|||||||
Called when a ``namespace`` alias is encountered
|
Called when a ``namespace`` alias is encountered
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
def on_concept(self, state: NonClassBlockState, concept: Concept) -> None:
|
||||||
|
"""
|
||||||
|
.. code-block:: c++
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
concept Meowable = is_meowable<T>;
|
||||||
|
"""
|
||||||
|
|
||||||
def on_forward_decl(self, state: State, fdecl: ForwardDecl) -> None:
|
def on_forward_decl(self, state: State, fdecl: ForwardDecl) -> None:
|
||||||
"""
|
"""
|
||||||
Called when a forward declaration is encountered
|
Called when a forward declaration is encountered
|
||||||
@ -254,6 +263,9 @@ class NullVisitor:
|
|||||||
def on_namespace_end(self, state: NamespaceBlockState) -> None:
|
def on_namespace_end(self, state: NamespaceBlockState) -> None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def on_concept(self, state: NonClassBlockState, concept: Concept) -> None:
|
||||||
|
return None
|
||||||
|
|
||||||
def on_namespace_alias(
|
def on_namespace_alias(
|
||||||
self, state: NonClassBlockState, alias: NamespaceAlias
|
self, state: NonClassBlockState, alias: NamespaceAlias
|
||||||
) -> None:
|
) -> None:
|
||||||
|
403
tests/test_concepts.py
Normal file
403
tests/test_concepts.py
Normal file
@ -0,0 +1,403 @@
|
|||||||
|
from cxxheaderparser.simple import NamespaceScope, ParsedData, parse_string
|
||||||
|
from cxxheaderparser.tokfmt import Token
|
||||||
|
from cxxheaderparser.types import (
|
||||||
|
Concept,
|
||||||
|
Function,
|
||||||
|
FundamentalSpecifier,
|
||||||
|
NameSpecifier,
|
||||||
|
PQName,
|
||||||
|
Parameter,
|
||||||
|
TemplateArgument,
|
||||||
|
TemplateDecl,
|
||||||
|
TemplateNonTypeParam,
|
||||||
|
TemplateSpecialization,
|
||||||
|
TemplateTypeParam,
|
||||||
|
Type,
|
||||||
|
Value,
|
||||||
|
Variable,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_concept_basic_constraint() -> None:
|
||||||
|
content = """
|
||||||
|
template <class T, class U>
|
||||||
|
concept Derived = std::is_base_of<U, T>::value;
|
||||||
|
|
||||||
|
template <Derived<Base> T> void f(T); // T is constrained by Derived<T, Base>
|
||||||
|
"""
|
||||||
|
data = parse_string(content, cleandoc=True)
|
||||||
|
|
||||||
|
assert data == ParsedData(
|
||||||
|
namespace=NamespaceScope(
|
||||||
|
functions=[
|
||||||
|
Function(
|
||||||
|
return_type=Type(
|
||||||
|
typename=PQName(segments=[FundamentalSpecifier(name="void")])
|
||||||
|
),
|
||||||
|
name=PQName(segments=[NameSpecifier(name="f")]),
|
||||||
|
parameters=[
|
||||||
|
Parameter(
|
||||||
|
type=Type(
|
||||||
|
typename=PQName(segments=[NameSpecifier(name="T")])
|
||||||
|
)
|
||||||
|
)
|
||||||
|
],
|
||||||
|
template=TemplateDecl(
|
||||||
|
params=[
|
||||||
|
TemplateNonTypeParam(
|
||||||
|
type=Type(
|
||||||
|
typename=PQName(
|
||||||
|
segments=[
|
||||||
|
NameSpecifier(
|
||||||
|
name="Derived",
|
||||||
|
specialization=TemplateSpecialization(
|
||||||
|
args=[
|
||||||
|
TemplateArgument(
|
||||||
|
arg=Type(
|
||||||
|
typename=PQName(
|
||||||
|
segments=[
|
||||||
|
NameSpecifier(
|
||||||
|
name="Base"
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
),
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
),
|
||||||
|
name="T",
|
||||||
|
)
|
||||||
|
]
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
concepts=[
|
||||||
|
Concept(
|
||||||
|
template=TemplateDecl(
|
||||||
|
params=[
|
||||||
|
TemplateTypeParam(typekey="class", name="T"),
|
||||||
|
TemplateTypeParam(typekey="class", name="U"),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
name="Derived",
|
||||||
|
raw_constraint=Value(
|
||||||
|
tokens=[
|
||||||
|
Token(value="std"),
|
||||||
|
Token(value="::"),
|
||||||
|
Token(value="is_base_of"),
|
||||||
|
Token(value="<"),
|
||||||
|
Token(value="U"),
|
||||||
|
Token(value=","),
|
||||||
|
Token(value="T"),
|
||||||
|
Token(value=">"),
|
||||||
|
Token(value="::"),
|
||||||
|
Token(value="value"),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_concept_basic_constraint2() -> None:
|
||||||
|
content = """
|
||||||
|
template <class T> constexpr bool is_meowable = true;
|
||||||
|
|
||||||
|
template <class T> constexpr bool is_cat = true;
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
concept Meowable = is_meowable<T>;
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
concept BadMeowableCat = is_meowable<T> && is_cat<T>;
|
||||||
|
"""
|
||||||
|
data = parse_string(content, cleandoc=True)
|
||||||
|
|
||||||
|
assert data == ParsedData(
|
||||||
|
namespace=NamespaceScope(
|
||||||
|
variables=[
|
||||||
|
Variable(
|
||||||
|
name=PQName(segments=[NameSpecifier(name="is_meowable")]),
|
||||||
|
type=Type(
|
||||||
|
typename=PQName(segments=[FundamentalSpecifier(name="bool")])
|
||||||
|
),
|
||||||
|
value=Value(tokens=[Token(value="true")]),
|
||||||
|
constexpr=True,
|
||||||
|
template=TemplateDecl(
|
||||||
|
params=[TemplateTypeParam(typekey="class", name="T")]
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Variable(
|
||||||
|
name=PQName(segments=[NameSpecifier(name="is_cat")]),
|
||||||
|
type=Type(
|
||||||
|
typename=PQName(segments=[FundamentalSpecifier(name="bool")])
|
||||||
|
),
|
||||||
|
value=Value(tokens=[Token(value="true")]),
|
||||||
|
constexpr=True,
|
||||||
|
template=TemplateDecl(
|
||||||
|
params=[TemplateTypeParam(typekey="class", name="T")]
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
concepts=[
|
||||||
|
Concept(
|
||||||
|
template=TemplateDecl(
|
||||||
|
params=[TemplateTypeParam(typekey="class", name="T")]
|
||||||
|
),
|
||||||
|
name="Meowable",
|
||||||
|
raw_constraint=Value(
|
||||||
|
tokens=[
|
||||||
|
Token(value="is_meowable"),
|
||||||
|
Token(value="<"),
|
||||||
|
Token(value="T"),
|
||||||
|
Token(value=">"),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Concept(
|
||||||
|
template=TemplateDecl(
|
||||||
|
params=[TemplateTypeParam(typekey="class", name="T")]
|
||||||
|
),
|
||||||
|
name="BadMeowableCat",
|
||||||
|
raw_constraint=Value(
|
||||||
|
tokens=[
|
||||||
|
Token(value="is_meowable"),
|
||||||
|
Token(value="<"),
|
||||||
|
Token(value="T"),
|
||||||
|
Token(value=">"),
|
||||||
|
Token(value="&&"),
|
||||||
|
Token(value="is_cat"),
|
||||||
|
Token(value="<"),
|
||||||
|
Token(value="T"),
|
||||||
|
Token(value=">"),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_concept_basic_requires() -> None:
|
||||||
|
content = """
|
||||||
|
template <typename T>
|
||||||
|
concept Hashable = requires(T a) {
|
||||||
|
{ std::hash<T>{}(a) } -> std::convertible_to<std::size_t>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <Hashable T> void f(T) {}
|
||||||
|
"""
|
||||||
|
data = parse_string(content, cleandoc=True)
|
||||||
|
|
||||||
|
assert data == ParsedData(
|
||||||
|
namespace=NamespaceScope(
|
||||||
|
functions=[
|
||||||
|
Function(
|
||||||
|
return_type=Type(
|
||||||
|
typename=PQName(segments=[FundamentalSpecifier(name="void")])
|
||||||
|
),
|
||||||
|
name=PQName(segments=[NameSpecifier(name="f")]),
|
||||||
|
parameters=[
|
||||||
|
Parameter(
|
||||||
|
type=Type(
|
||||||
|
typename=PQName(segments=[NameSpecifier(name="T")])
|
||||||
|
)
|
||||||
|
)
|
||||||
|
],
|
||||||
|
has_body=True,
|
||||||
|
template=TemplateDecl(
|
||||||
|
params=[
|
||||||
|
TemplateNonTypeParam(
|
||||||
|
type=Type(
|
||||||
|
typename=PQName(
|
||||||
|
segments=[NameSpecifier(name="Hashable")]
|
||||||
|
)
|
||||||
|
),
|
||||||
|
name="T",
|
||||||
|
)
|
||||||
|
]
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
concepts=[
|
||||||
|
Concept(
|
||||||
|
template=TemplateDecl(
|
||||||
|
params=[TemplateTypeParam(typekey="typename", name="T")]
|
||||||
|
),
|
||||||
|
name="Hashable",
|
||||||
|
raw_constraint=Value(
|
||||||
|
tokens=[
|
||||||
|
Token(value="requires"),
|
||||||
|
Token(value="("),
|
||||||
|
Token(value="T"),
|
||||||
|
Token(value="a"),
|
||||||
|
Token(value=")"),
|
||||||
|
Token(value="{"),
|
||||||
|
Token(value="{"),
|
||||||
|
Token(value="std"),
|
||||||
|
Token(value="::"),
|
||||||
|
Token(value="hash"),
|
||||||
|
Token(value="<"),
|
||||||
|
Token(value="T"),
|
||||||
|
Token(value=">"),
|
||||||
|
Token(value="{"),
|
||||||
|
Token(value="}"),
|
||||||
|
Token(value="("),
|
||||||
|
Token(value="a"),
|
||||||
|
Token(value=")"),
|
||||||
|
Token(value="}"),
|
||||||
|
Token(value="->"),
|
||||||
|
Token(value="std"),
|
||||||
|
Token(value="::"),
|
||||||
|
Token(value="convertible_to"),
|
||||||
|
Token(value="<"),
|
||||||
|
Token(value="std"),
|
||||||
|
Token(value="::"),
|
||||||
|
Token(value="size_t"),
|
||||||
|
Token(value=">"),
|
||||||
|
Token(value=";"),
|
||||||
|
Token(value="}"),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_concept_nested_requirements() -> None:
|
||||||
|
content = """
|
||||||
|
template<class T>
|
||||||
|
concept Semiregular = DefaultConstructible<T> &&
|
||||||
|
CopyConstructible<T> && CopyAssignable<T> && Destructible<T> &&
|
||||||
|
requires(T a, std::size_t n)
|
||||||
|
{
|
||||||
|
requires Same<T*, decltype(&a)>; // nested: "Same<...> evaluates to true"
|
||||||
|
{ a.~T() } noexcept; // compound: "a.~T()" is a valid expression that doesn't throw
|
||||||
|
requires Same<T*, decltype(new T)>; // nested: "Same<...> evaluates to true"
|
||||||
|
requires Same<T*, decltype(new T[n])>; // nested
|
||||||
|
{ delete new T }; // compound
|
||||||
|
{ delete new T[n] }; // compound
|
||||||
|
};
|
||||||
|
"""
|
||||||
|
data = parse_string(content, cleandoc=True)
|
||||||
|
|
||||||
|
assert data == ParsedData(
|
||||||
|
namespace=NamespaceScope(
|
||||||
|
concepts=[
|
||||||
|
Concept(
|
||||||
|
template=TemplateDecl(
|
||||||
|
params=[TemplateTypeParam(typekey="class", name="T")]
|
||||||
|
),
|
||||||
|
name="Semiregular",
|
||||||
|
raw_constraint=Value(
|
||||||
|
tokens=[
|
||||||
|
Token(value="DefaultConstructible"),
|
||||||
|
Token(value="<"),
|
||||||
|
Token(value="T"),
|
||||||
|
Token(value=">"),
|
||||||
|
Token(value="&&"),
|
||||||
|
Token(value="CopyConstructible"),
|
||||||
|
Token(value="<"),
|
||||||
|
Token(value="T"),
|
||||||
|
Token(value=">"),
|
||||||
|
Token(value="&&"),
|
||||||
|
Token(value="CopyAssignable"),
|
||||||
|
Token(value="<"),
|
||||||
|
Token(value="T"),
|
||||||
|
Token(value=">"),
|
||||||
|
Token(value="&&"),
|
||||||
|
Token(value="Destructible"),
|
||||||
|
Token(value="<"),
|
||||||
|
Token(value="T"),
|
||||||
|
Token(value=">"),
|
||||||
|
Token(value="&&"),
|
||||||
|
Token(value="requires"),
|
||||||
|
Token(value="("),
|
||||||
|
Token(value="T"),
|
||||||
|
Token(value="a"),
|
||||||
|
Token(value=","),
|
||||||
|
Token(value="std"),
|
||||||
|
Token(value="::"),
|
||||||
|
Token(value="size_t"),
|
||||||
|
Token(value="n"),
|
||||||
|
Token(value=")"),
|
||||||
|
Token(value="{"),
|
||||||
|
Token(value="requires"),
|
||||||
|
Token(value="Same"),
|
||||||
|
Token(value="<"),
|
||||||
|
Token(value="T"),
|
||||||
|
Token(value="*"),
|
||||||
|
Token(value=","),
|
||||||
|
Token(value="decltype"),
|
||||||
|
Token(value="("),
|
||||||
|
Token(value="&"),
|
||||||
|
Token(value="a"),
|
||||||
|
Token(value=")"),
|
||||||
|
Token(value=">"),
|
||||||
|
Token(value=";"),
|
||||||
|
Token(value="{"),
|
||||||
|
Token(value="a"),
|
||||||
|
Token(value="."),
|
||||||
|
Token(value="~T"),
|
||||||
|
Token(value="("),
|
||||||
|
Token(value=")"),
|
||||||
|
Token(value="}"),
|
||||||
|
Token(value="noexcept"),
|
||||||
|
Token(value=";"),
|
||||||
|
Token(value="requires"),
|
||||||
|
Token(value="Same"),
|
||||||
|
Token(value="<"),
|
||||||
|
Token(value="T"),
|
||||||
|
Token(value="*"),
|
||||||
|
Token(value=","),
|
||||||
|
Token(value="decltype"),
|
||||||
|
Token(value="("),
|
||||||
|
Token(value="new"),
|
||||||
|
Token(value="T"),
|
||||||
|
Token(value=")"),
|
||||||
|
Token(value=">"),
|
||||||
|
Token(value=";"),
|
||||||
|
Token(value="requires"),
|
||||||
|
Token(value="Same"),
|
||||||
|
Token(value="<"),
|
||||||
|
Token(value="T"),
|
||||||
|
Token(value="*"),
|
||||||
|
Token(value=","),
|
||||||
|
Token(value="decltype"),
|
||||||
|
Token(value="("),
|
||||||
|
Token(value="new"),
|
||||||
|
Token(value="T"),
|
||||||
|
Token(value="["),
|
||||||
|
Token(value="n"),
|
||||||
|
Token(value="]"),
|
||||||
|
Token(value=")"),
|
||||||
|
Token(value=">"),
|
||||||
|
Token(value=";"),
|
||||||
|
Token(value="{"),
|
||||||
|
Token(value="delete"),
|
||||||
|
Token(value="new"),
|
||||||
|
Token(value="T"),
|
||||||
|
Token(value="}"),
|
||||||
|
Token(value=";"),
|
||||||
|
Token(value="{"),
|
||||||
|
Token(value="delete"),
|
||||||
|
Token(value="new"),
|
||||||
|
Token(value="T"),
|
||||||
|
Token(value="["),
|
||||||
|
Token(value="n"),
|
||||||
|
Token(value="]"),
|
||||||
|
Token(value="}"),
|
||||||
|
Token(value=";"),
|
||||||
|
Token(value="}"),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
)
|
Loading…
x
Reference in New Issue
Block a user