Skip to content
1 change: 1 addition & 0 deletions src/devana/code_generation/printers/default/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,4 @@
from .functionprinter import FunctionPrinter
from .functiontypeprinter import FunctionTypePrinter
from .attributeprinter import AttributePrinter, AttributeDeclarationPrinter
from .conceptprinter import ConceptPrinter
8 changes: 7 additions & 1 deletion src/devana/code_generation/printers/default/classprinter.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,9 +181,15 @@ def print(self, source: ClassInfo, config: Optional[PrinterConfiguration] = None
parameters.append(self.printer_dispatcher.print(p, config, source))
parameters = ','.join(parameters)
template_prefix = f"template<{parameters}>"
if source.template.requires:
template_prefix += " requires"
for req in source.template.requires:
if isinstance(req, str):
template_prefix += f" {req}"
continue
template_prefix += f" {self.printer_dispatcher.print(req, config, source)}"

specialisation_values = []

for s in source.template.specialisation_values:
if isinstance(s, str):
specialisation_values.append(s)
Expand Down
33 changes: 33 additions & 0 deletions src/devana/code_generation/printers/default/conceptprinter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
from typing import Optional
from devana.code_generation.printers.icodeprinter import ICodePrinter
from devana.syntax_abstraction.conceptinfo import ConceptInfo
from devana.code_generation.printers.dispatcherinjectable import DispatcherInjectable
from devana.code_generation.printers.configuration import PrinterConfiguration
from devana.code_generation.printers.formatter import Formatter


class ConceptPrinter(ICodePrinter, DispatcherInjectable):
"""Printer for concept declaration."""

def print(self, source: ConceptInfo, config: Optional[PrinterConfiguration] = None,
context: Optional = None) -> str:
if config is None:
config = PrinterConfiguration()
formatter = Formatter(config)
if source.is_requirement:
parameters = []
for p in source.parameters:
parameters.append(self.printer_dispatcher.print(p, config, source))
parameters = ', '.join(parameters)
if len(parameters) > 0:
return f"{source.name}<{parameters}>"
return source.name
parameters = []
for p in source.template.parameters:
parameters.append(self.printer_dispatcher.print(p, config, source))
parameters = ', '.join(parameters)
template_prefix = f"template<{parameters}>"
formatter.print_line(template_prefix)

formatter.print_line(f"concept {source.name} = {source.body};")
return formatter.text
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
from devana.code_generation.printers.default.commentprinter import CommentPrinter
from devana.code_generation.printers.default.functiontypeprinter import FunctionTypePrinter
from devana.code_generation.printers.default.stubtypeprinter import StubTypePrinter
from devana.code_generation.printers.default.conceptprinter import ConceptPrinter
from devana.syntax_abstraction.conceptinfo import ConceptInfo
from devana.syntax_abstraction.classinfo import ClassInfo
from devana.syntax_abstraction.templateinfo import GenericTypeParameter
from devana.syntax_abstraction.typedefinfo import TypedefInfo
Expand Down Expand Up @@ -72,5 +74,5 @@ def create_default_printer() -> CodePrinter:
printer.register(StubTypePrinter, StubType)
printer.register(AttributePrinter, Attribute)
printer.register(AttributeDeclarationPrinter, AttributeDeclaration)

printer.register(ConceptPrinter, ConceptInfo)
return printer
14 changes: 14 additions & 0 deletions src/devana/code_generation/printers/default/functionprinter.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@ def print(self, source: FunctionInfo, config: Optional[PrinterConfiguration] = N
parameters.append(self.printer_dispatcher.print(p, config, source))
parameters = ','.join(parameters)
template_prefix = f"template<{parameters}>"
if source.template.requires:
template_prefix += " requires"
for req in source.template.requires:
if isinstance(req, str):
template_prefix += f" {req}"
continue
template_prefix += f" {self.printer_dispatcher.print(req, config, source)}"

specialisation_values = []

Expand Down Expand Up @@ -71,6 +78,13 @@ def print(self, source: FunctionInfo, config: Optional[PrinterConfiguration] = N
result = f"{return_type} {name}{template_suffix}({args})"
else:
result = f"{name}{template_suffix}({args})"
if source.requires is not None:
result += " requires"
for req in source.requires:
if isinstance(req, str):
result += f" {req}"
continue
result += f" {self.printer_dispatcher.print(req, config, source)}"

if source.modification.is_static:
result = "static " + result
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
from devana.code_generation.printers.icodeprinter import ICodePrinter
from devana.syntax_abstraction.templateinfo import TemplateInfo
from devana.code_generation.printers.dispatcherinjectable import DispatcherInjectable
from devana.syntax_abstraction.conceptinfo import ConceptInfo


class TemplateParameterPrinter(ICodePrinter, DispatcherInjectable):
"""Printer for template parameter."""

def print(self, source: TemplateInfo.TemplateParameter, _1=None, _2=None) -> str:
if isinstance(source.specifier, ConceptInfo):
text = f"{self.printer_dispatcher.print(source.specifier)} {source.name}"
else:
text = f"{source.specifier} {source.name}"

text = f"{source.specifier} {source.name}"
if source.is_variadic:
return f"{text}..."
if source.default_value:
Expand Down
18 changes: 18 additions & 0 deletions src/devana/code_generation/printers/default/usingprinter.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,24 @@ def print(self, source: Using, config: Optional[PrinterConfiguration] = None,

if source.associated_comment:
formatter.print_line(self.printer_dispatcher.print(source.associated_comment, config, source))

template_prefix = ""
if source.template:
parameters = []
for p in source.template.parameters:
parameters.append(self.printer_dispatcher.print(p, config, source))
parameters = ','.join(parameters)
template_prefix = f"template<{parameters}>"
if source.template.requires:
template_prefix += " requires"
for req in source.template.requires:
if isinstance(req, str):
template_prefix += f" {req}"
continue
template_prefix += f" {self.printer_dispatcher.print(req, config, source)}"
if template_prefix:
formatter.print_line(template_prefix)

formatter.line = f"using {source.name} = {self.printer_dispatcher.print(source.type_info, config, source)};"
formatter.next_line()
return formatter.text
4 changes: 4 additions & 0 deletions src/devana/syntax_abstraction/classinfo.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from devana.syntax_abstraction.comment import Comment
from devana.syntax_abstraction.attribute import DescriptiveByAttributes, AttributeDeclaration
from devana.syntax_abstraction._external_source import create_external
from devana.syntax_abstraction.conceptinfo import ConceptInfo
from devana.utility.lazy import LazyNotInit, lazy_invoke
from devana.utility.init_params import init_params
from devana.utility.traits import IBasicCreatable, ICursorValidate, IFromCursorCreatable, IFromParamsCreatable
Expand Down Expand Up @@ -207,6 +208,7 @@ def from_params( # pylint: disable=unused-argument, arguments-renamed
template: Optional[TemplateInfo] = None,
associated_comment: Optional[Comment] = None,
prefix: Optional[str] = None,
requires: Optional[List[Union[str, ConceptInfo]]] = None,
access_specifier: Optional[AccessSpecifier] = None,
type: Optional[MethodType] = None, # noqa pylint: disable=redefined-builtin
) -> "MethodInfo":
Expand Down Expand Up @@ -305,6 +307,7 @@ def from_params( # pylint: disable=unused-argument, arguments-renamed
template: Optional[TemplateInfo] = None,
associated_comment: Optional[Comment] = None,
prefix: Optional[str] = None,
requires: Optional[List[Union[str, ConceptInfo]]] = None,
access_specifier: Optional[AccessSpecifier] = None,
type: Optional[MethodType] = None, # noqa pylint: disable=redefined-builtin
initializer_list: Optional[List[InitializerInfo]] = None,
Expand Down Expand Up @@ -416,6 +419,7 @@ def from_params( # pylint: disable=unused-argument, arguments-differ
template: Optional[TemplateInfo] = None,
associated_comment: Optional[Comment] = None,
prefix: Optional[str] = None,
requires: Optional[List[Union[str, ConceptInfo]]] = None,
access_specifier: Optional[AccessSpecifier] = None,
) -> "DestructorInfo":
return cls(None, parent)
Expand Down
143 changes: 143 additions & 0 deletions src/devana/syntax_abstraction/conceptinfo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
from __future__ import annotations
from typing import Optional, List, Any
from clang import cindex

from devana.syntax_abstraction.organizers.codecontainer import CodeContainer
from devana.syntax_abstraction.typeexpression import TypeExpression
from devana.syntax_abstraction.codepiece import CodePiece
from devana.syntax_abstraction.syntax import ISyntaxElement
from devana.utility.lazy import LazyNotInit, lazy_invoke
from devana.utility.init_params import init_params
from devana.utility.errors import ParserError


class ConceptInfo(CodeContainer):
"""Represents a C++ concept, either as a full definition or as a requirement."""

def __init__(self, cursor: Optional[cindex.Cursor] = None, parent: Optional[CodeContainer] = None):
super().__init__(cursor, parent)
if cursor is None:
from devana.syntax_abstraction.templateinfo import TemplateInfo # pylint: disable=import-outside-toplevel

self._name = "DefaultConcept"
self._body = "true"
self._template = TemplateInfo.from_params(parameters=[
TemplateInfo.TemplateParameter.create_default()
])
self._parameters = []
self._is_requirement = False
else:
if not self.is_cursor_valid(cursor):
raise ParserError(f"It is not a valid type cursor: {cursor.kind}.")
self._name = LazyNotInit
self._body = LazyNotInit
self._template = LazyNotInit
self._parameters = LazyNotInit
self._is_requirement = LazyNotInit

def __repr__(self):
return f"{type(self).__name__}:{self.name} ({super().__repr__()})"

@classmethod
def create_default(cls, parent: Optional = None) -> "ConceptInfo":
return cls(None, parent)

@classmethod
def from_cursor(cls, cursor: cindex.Cursor, parent: Optional = None) -> Optional["ConceptInfo"]:
if cls.is_cursor_valid(cursor):
return cls(cursor, parent)
return None

@classmethod
@init_params(skip={"parent"})
def from_params( # pylint: disable=unused-argument
cls,
parent: Optional[ISyntaxElement] = None,
content: Optional[List[Any]] = None,
namespace: Optional[str] = None,
name: Optional[str] = None,
body: Optional[str] = None,
template: Optional[ISyntaxElement] = None,
parameters: Optional[List[TypeExpression]] = None,
is_requirement: Optional[bool] = None
) -> "ConceptInfo":
return cls(None, parent)

@staticmethod
def is_cursor_valid(cursor: cindex.Cursor) -> bool:
return cursor.kind == cindex.CursorKind.CONCEPT_DECL

@property
@lazy_invoke
def name(self) -> str:
self._name = self._cursor.spelling
return self._name

@name.setter
def name(self, value) -> None:
self._name = value

@property
@lazy_invoke
def template(self) -> ISyntaxElement:
"""Template associated with this concept."""
from devana.syntax_abstraction.templateinfo import TemplateInfo # pylint: disable=import-outside-toplevel
self._template = TemplateInfo.from_cursor(self._cursor)
return self._template

@template.setter
def template(self, value: ISyntaxElement) -> None:
self._template = value

@property
@lazy_invoke
def body(self) -> str:
"""The body of the concept, which defines its constraint expression."""
self._body = ""
for child in self._cursor.get_children():
if child.kind != cindex.CursorKind.TEMPLATE_TYPE_PARAMETER:
self._body = CodePiece(child).text
break
return self._body

@body.setter
def body(self, value: str) -> None:
self._body = value

@property
@lazy_invoke
def parameters(self) -> List[TypeExpression]:
"""Retrieves the concept parameters '<...>'."""
from devana.syntax_abstraction.templateinfo import TemplateInfo # pylint: disable=import-outside-toplevel
if not isinstance(self.parent, TemplateInfo.TemplateParameter):
return []
# Probably without a cursor from the parent it will not be possible to extract it.
# I get a mental breakdown every time I see the number -1, when i want to extract parameters in the normal way.
# Fuck it for now.
return []

@parameters.setter
def parameters(self, value: List[TypeExpression]) -> None:
self._parameters = value

@property
@lazy_invoke
def is_requirement(self) -> bool:
"""Determines whether this ConceptInfo instance is acting as a requirement."""
from devana.syntax_abstraction.functioninfo import FunctionInfo # pylint: disable=import-outside-toplevel
from devana.syntax_abstraction.templateinfo import TemplateInfo # pylint: disable=import-outside-toplevel
self._is_requirement = isinstance(
self.parent, (
TemplateInfo.TemplateParameter,
TemplateInfo, FunctionInfo
)
)
return self._is_requirement

@is_requirement.setter
def is_requirement(self, value: bool) -> None:
self._is_requirement = value

@property
def _content_types(self) -> List:
return [ConceptInfo]
Loading
Loading