diff --git a/src/math_spec/typesetting/__init__.py b/src/math_spec/typesetting/__init__.py index ab8c6e5b..38d1e585 100644 --- a/src/math_spec/typesetting/__init__.py +++ b/src/math_spec/typesetting/__init__.py @@ -151,7 +151,7 @@ def typeset( blocks = [format_.note(format_.escape(schema.description))] if schema.description else [] if legend: - blocks += [format_.glossary(group.title, group.entries) for group in walk.glossaries(noticed)] + blocks += [format_.section(title, format_.glossary(entries)) for title, entries in walk.glossaries(noticed)] blocks += [format_.note(text) for text in walk.convention_notes()] blocks += [format_.note(text) for text in walk.translation_notes(noticed)] blocks += [format_.note(text) for text in walk.position_notes(noticed)] @@ -194,7 +194,7 @@ def typeset_declaration( Raises: ValueError: *fmt* names no format. LanguageError: A model that does not compile; it does not print. - SchemaError: *name* is declared as none of the four, or as two — a + SchemaError: *name* is declared as none of the five, or as two — a constraint may share a variable's name; or a symbol table entry names nothing in the model. """ diff --git a/src/math_spec/typesetting/format.py b/src/math_spec/typesetting/format.py index 4b024614..b6cf4613 100644 --- a/src/math_spec/typesetting/format.py +++ b/src/math_spec/typesetting/format.py @@ -137,14 +137,6 @@ class Entry: meaning: str -@dataclass(frozen=True) -class Glossary: - """One legend section: its title, and the entries under it.""" - - title: str - entries: list[Entry] - - #: The one notation author prose carries: a name in backticks, set in monospace. _CODE_SPAN = re.compile(r'`([^`]+)`') @@ -169,8 +161,6 @@ class Format(Protocol): operators: ClassVar[Mapping[OperatorName, str]] #: The em dash in prose: TeX and Typst read ``---`` as one, Markdown does not. dash: ClassVar[str] - #: Between the rows of a ``cases`` block. - cases_row: ClassVar[str] # -- atoms ------------------------------------------------------------- @@ -255,7 +245,9 @@ def equation(self, line: Line) -> str: def equations(self, lines: list[Line], *, numbered: bool) -> str: ... - def glossary(self, title: str, entries: list[Entry]) -> str: ... + def glossary(self, entries: list[Entry]) -> str: + """A legend section's rows; :meth:`section` sets its title, as it does for the equations.""" + ... def section(self, title: str, body: str) -> str: ... diff --git a/src/math_spec/typesetting/latex.py b/src/math_spec/typesetting/latex.py index 3e3296f5..38c5c634 100644 --- a/src/math_spec/typesetting/latex.py +++ b/src/math_spec/typesetting/latex.py @@ -47,7 +47,6 @@ class LatexFormat: notation: ClassVar[Notation] = 'latex' #: TeX's own em-dash ligature. dash: ClassVar[str] = '---' - cases_row: ClassVar[str] = r' \\ ' operators: ClassVar[Mapping[OperatorName, str]] = {name: latex for name, (latex, _) in OPERATOR_SPELLINGS.items()} @@ -102,7 +101,7 @@ def set_of(self, members: str, condition: str) -> str: return rf'\{{ {members} {self.operators["such_that"]} {condition} \}}' def cases(self, arms: list[tuple[str, str]]) -> str: - rows = self.cases_row.join(f'{value} & {condition}' for value, condition in arms) + rows = r' \\ '.join(f'{value} & {condition}' for value, condition in arms) return rf'\begin{{cases}} {rows} \end{{cases}}' def summation(self, domain: str, body: str) -> str: @@ -125,9 +124,9 @@ def equations(self, lines: list[Line], *, numbered: bool) -> str: body = ' \\\\\n'.join(aligned_rows(lines, self, gap=' && ')) return f'\\begin{{{environment}}}\n{body}\n\\end{{{environment}}}' - def glossary(self, title: str, entries: list[Entry]) -> str: + def glossary(self, entries: list[Entry]) -> str: rows = '\n'.join(rf'\item[{{{self.math(e.symbol)}}}] {e.meaning}' for e in entries) - return f'\\paragraph{{{title}}}\n\\begin{{description}}\n{rows}\n\\end{{description}}' + return f'\\begin{{description}}\n{rows}\n\\end{{description}}' def section(self, title: str, body: str) -> str: return f'\\paragraph{{{title}}}\n{body}' diff --git a/src/math_spec/typesetting/markdown.py b/src/math_spec/typesetting/markdown.py index 7f80a280..9fb6b17c 100644 --- a/src/math_spec/typesetting/markdown.py +++ b/src/math_spec/typesetting/markdown.py @@ -86,9 +86,9 @@ def equations(self, lines: list[Line], *, numbered: bool) -> str: return '\n\n'.join(blocks) @override - def glossary(self, title: str, entries: list[Entry]) -> str: + def glossary(self, entries: list[Entry]) -> str: rows = '\n'.join(f'| {_cell(self.math(e.symbol))} | {_cell(e.meaning)} |' for e in entries) - return f'#### {title}\n\n| Symbol | Meaning |\n|---|---|\n{rows}' + return f'| Symbol | Meaning |\n|---|---|\n{rows}' @override def section(self, title: str, body: str) -> str: diff --git a/src/math_spec/typesetting/typst.py b/src/math_spec/typesetting/typst.py index d83a1aeb..b3c7cce0 100644 --- a/src/math_spec/typesetting/typst.py +++ b/src/math_spec/typesetting/typst.py @@ -55,7 +55,6 @@ class TypstFormat: notation: ClassVar[Notation] = 'typst' #: Typst applies the same substitution TeX does. dash: ClassVar[str] = '---' - cases_row: ClassVar[str] = ', ' operators: ClassVar[Mapping[OperatorName, str]] = {name: typst for name, (_, typst) in OPERATOR_SPELLINGS.items()} @@ -110,7 +109,7 @@ def set_of(self, members: str, condition: str) -> str: return f'{{{members} {self.operators["such_that"]} {condition}}}' def cases(self, arms: list[tuple[str, str]]) -> str: - return 'cases({})'.format(self.cases_row.join(f'{value} & {condition}' for value, condition in arms)) + return 'cases({})'.format(', '.join(f'{value} & {condition}' for value, condition in arms)) def summation(self, domain: str, body: str) -> str: return f'sum_({domain}) {body}' @@ -133,9 +132,8 @@ def equations(self, lines: list[Line], *, numbered: bool) -> str: numbering = '#set math.equation(numbering: "(1)")\n' if numbered else '' return f'{numbering}$ {body} $' - def glossary(self, title: str, entries: list[Entry]) -> str: - rows = '\n'.join(f'/ {self.math(e.symbol)}: {e.meaning}' for e in entries) - return f'== {title}\n{rows}' + def glossary(self, entries: list[Entry]) -> str: + return '\n'.join(f'/ {self.math(e.symbol)}: {e.meaning}' for e in entries) def section(self, title: str, body: str) -> str: return f'== {title}\n{body}' diff --git a/src/math_spec/typesetting/walk.py b/src/math_spec/typesetting/walk.py index a65c4846..d82e2266 100644 --- a/src/math_spec/typesetting/walk.py +++ b/src/math_spec/typesetting/walk.py @@ -58,7 +58,7 @@ TranslatedPredicate, VariableDefined, ) -from math_spec.typesetting.format import Entry, Glossary, Line, OperatorName +from math_spec.typesetting.format import Entry, Line, OperatorName if TYPE_CHECKING: import datetime @@ -1018,7 +1018,7 @@ def _sorted(self, dims: frozenset[str]) -> list[str]: # -- legend ------------------------------------------------------------ - def glossaries(self, noticed: Noticed) -> list[Glossary]: + def glossaries(self, noticed: Noticed) -> list[tuple[str, list[Entry]]]: fmt = self.format sets = [ self._entry( @@ -1041,13 +1041,8 @@ def glossaries(self, noticed: Noticed) -> list[Glossary]: for e, block in self.schema.expressions.items() if e in self._defined() ] - groups = ( - Glossary('Sets', sets), - Glossary('Parameters', parameters), - Glossary('Variables', variables), - Glossary('Definitions', definitions), - ) - return [group for group in groups if group.entries] + groups = (('Sets', sets), ('Parameters', parameters), ('Variables', variables), ('Definitions', definitions)) + return [(title, entries) for title, entries in groups if entries] def _entry(self, symbol: str, what: str, description: str | None) -> Entry: meaning = f'{what} {self.format.dash} {self.format.escape(description)}' if description else what