| from __future__ import annotations |
| import abc |
| import typing |
| from collections.abc import ( |
| Iterable, |
| ) |
| |
| import libclinic |
| from libclinic import fail |
| from libclinic.function import ( |
| Module, Class, Function) |
| |
| if typing.TYPE_CHECKING: |
| from libclinic.app import Clinic |
| |
| |
| class Language(metaclass=abc.ABCMeta): |
| |
| start_line = "" |
| body_prefix = "" |
| stop_line = "" |
| checksum_line = "" |
| |
| def __init__(self, filename: str) -> None: |
| self.filename = filename |
| |
| @abc.abstractmethod |
| def render( |
| self, |
| clinic: Clinic, |
| signatures: Iterable[Module | Class | Function] |
| ) -> str: |
| ... |
| |
| def parse_line(self, line: str) -> None: |
| ... |
| |
| def validate(self) -> None: |
| def assert_only_one( |
| attr: str, |
| *additional_fields: str |
| ) -> None: |
| """ |
| Ensures that the string found at getattr(self, attr) |
| contains exactly one formatter replacement string for |
| each valid field. The list of valid fields is |
| ['dsl_name'] extended by additional_fields. |
| |
| e.g. |
| self.fmt = "{dsl_name} {a} {b}" |
| |
| # this passes |
| self.assert_only_one('fmt', 'a', 'b') |
| |
| # this fails, the format string has a {b} in it |
| self.assert_only_one('fmt', 'a') |
| |
| # this fails, the format string doesn't have a {c} in it |
| self.assert_only_one('fmt', 'a', 'b', 'c') |
| |
| # this fails, the format string has two {a}s in it, |
| # it must contain exactly one |
| self.fmt2 = '{dsl_name} {a} {a}' |
| self.assert_only_one('fmt2', 'a') |
| |
| """ |
| fields = ['dsl_name'] |
| fields.extend(additional_fields) |
| line: str = getattr(self, attr) |
| fcf = libclinic.FormatCounterFormatter() |
| fcf.format(line) |
| def local_fail(should_be_there_but_isnt: bool) -> None: |
| if should_be_there_but_isnt: |
| fail("{} {} must contain {{{}}} exactly once!".format( |
| self.__class__.__name__, attr, name)) |
| else: |
| fail("{} {} must not contain {{{}}}!".format( |
| self.__class__.__name__, attr, name)) |
| |
| for name, count in fcf.counts.items(): |
| if name in fields: |
| if count > 1: |
| local_fail(True) |
| else: |
| local_fail(False) |
| for name in fields: |
| if fcf.counts.get(name) != 1: |
| local_fail(True) |
| |
| assert_only_one('start_line') |
| assert_only_one('stop_line') |
| |
| field = "arguments" if "{arguments}" in self.checksum_line else "checksum" |
| assert_only_one('checksum_line', field) |
| |
| |
| class PythonLanguage(Language): |
| |
| language = 'Python' |
| start_line = "#/*[{dsl_name} input]" |
| body_prefix = "#" |
| stop_line = "#[{dsl_name} start generated code]*/" |
| checksum_line = "#/*[{dsl_name} end generated code: {arguments}]*/" |