Move files
This commit is contained in:
parent
3cf28379e8
commit
00cc8225c5
1 changed files with 31 additions and 49 deletions
|
|
@ -1,785 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2019 Philippe Faist
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
#
|
||||
|
||||
r"""
|
||||
Provides classes and helper functions to describe a LaTeX context of known
|
||||
macros and environments, specifying how they should be parsed by
|
||||
:py:mod:`pylatexenc.latexwalker`.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
|
||||
The entire module :py:mod:`pylatexenc.macrospec` was introduced in
|
||||
`pylatexenc 2.0`.
|
||||
"""
|
||||
|
||||
|
||||
import sys
|
||||
|
||||
if sys.version_info.major > 2:
|
||||
# Py3
|
||||
def unicode(s):
|
||||
return s
|
||||
|
||||
_basestring = str
|
||||
_str_from_unicode = lambda x: x
|
||||
_unicode_from_str = lambda x: x
|
||||
else:
|
||||
# Py2
|
||||
_basestring = basestring
|
||||
_str_from_unicode = lambda x: unicode(x).encode("utf-8")
|
||||
_unicode_from_str = lambda x: x.decode("utf-8")
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
from ._argparsers import (
|
||||
MacroStandardArgsParser,
|
||||
ParsedMacroArgs,
|
||||
ParsedVerbatimArgs,
|
||||
VerbatimArgsParser,
|
||||
)
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
class MacroSpec(object):
|
||||
r"""
|
||||
Stores the specification of a macro.
|
||||
|
||||
This stores the macro name and instructions on how to parse the macro
|
||||
arguments.
|
||||
|
||||
.. py:attribute:: macroname
|
||||
|
||||
The name of the macro, without the leading backslash.
|
||||
|
||||
.. py:attribute:: args_parser
|
||||
|
||||
The parser instance that can understand this macro's arguments. For
|
||||
standard LaTeX macros this is usually a
|
||||
:py:class:`MacroStandardArgsParser` instance.
|
||||
|
||||
If you specify a string, then for convenience this is interpreted as an
|
||||
argspec argument for :py:class:`MacroStandardArgsParser` and such an
|
||||
instance is automatically created.
|
||||
"""
|
||||
|
||||
def __init__(self, macroname, args_parser=MacroStandardArgsParser(), **kwargs):
|
||||
super(MacroSpec, self).__init__(**kwargs)
|
||||
self.macroname = macroname
|
||||
if isinstance(args_parser, _basestring):
|
||||
self.args_parser = MacroStandardArgsParser(args_parser)
|
||||
else:
|
||||
self.args_parser = args_parser
|
||||
|
||||
def parse_args(self, *args, **kwargs):
|
||||
r"""
|
||||
Shorthand for calling the :py:attr:`args_parser`\ 's `parse_args()` method.
|
||||
See :py:class:`MacroStandardArgsParser`.
|
||||
"""
|
||||
return self.args_parser.parse_args(*args, **kwargs)
|
||||
|
||||
def __repr__(self):
|
||||
return "MacroSpec(macroname=%r, args_parser=%r)" % (
|
||||
self.macroname,
|
||||
self.args_parser,
|
||||
)
|
||||
|
||||
|
||||
class EnvironmentSpec(object):
|
||||
r"""
|
||||
Stores the specification of a LaTeX environment.
|
||||
|
||||
This stores the environment name and instructions on how to parse any
|
||||
arguments provided after ``\begin{environment}<args>``.
|
||||
|
||||
.. py:attribute:: environmentname
|
||||
|
||||
The name of the environment, i.e., the argument of ``\begin{...}`` and
|
||||
``\end{...}``.
|
||||
|
||||
.. py:attribute:: args_parser
|
||||
|
||||
The parser instance that can understand this environment's arguments.
|
||||
For standard LaTeX environment this is usually a
|
||||
:py:class:`MacroStandardArgsParser` instance.
|
||||
|
||||
If you specify a string, then for convenience this is interpreted as an
|
||||
argspec argument for :py:class:`MacroStandardArgsParser` and such an
|
||||
instance is automatically created.
|
||||
|
||||
.. py:attribute:: is_math_mode
|
||||
|
||||
A boolean that indicates whether or not the contents is to be interpreted
|
||||
in Math Mode. This would be True for environments like
|
||||
``\begin{equation}``, ``\begin{align}``, etc., but False for
|
||||
``\begin{figure}``, etc.
|
||||
|
||||
.. note::
|
||||
|
||||
Starred variants of environments (as in ``\begin{equation*}``) must not
|
||||
be specified using an argspec as for macros (e.g., `argspec='*'`).
|
||||
Rather, we need to define a separate environment spec for the starred
|
||||
variant with the star in the name itself (``EnvironmentSpec('equation*',
|
||||
None)``) because the star really is part of the environment name. If you
|
||||
happened to use ``EnvironmentSpec('equation', '*')``, then the parser
|
||||
would recognize the expression ``\begin{equation}*`` but not
|
||||
``\begin{equation*}``.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
environmentname,
|
||||
args_parser=MacroStandardArgsParser(),
|
||||
is_math_mode=False,
|
||||
**kwargs
|
||||
):
|
||||
super(EnvironmentSpec, self).__init__(**kwargs)
|
||||
self.environmentname = environmentname
|
||||
if isinstance(args_parser, _basestring):
|
||||
self.args_parser = MacroStandardArgsParser(args_parser)
|
||||
else:
|
||||
self.args_parser = args_parser
|
||||
self.is_math_mode = is_math_mode
|
||||
|
||||
def parse_args(self, *args, **kwargs):
|
||||
r"""
|
||||
Shorthand for calling the :py:attr:`args_parser`\ 's `parse_args()` method.
|
||||
See :py:class:`MacroStandardArgsParser`.
|
||||
"""
|
||||
return self.args_parser.parse_args(*args, **kwargs)
|
||||
|
||||
def __repr__(self):
|
||||
return (
|
||||
"EnvironmentSpec(environmentname=%r, args_parser=%r, is_math_mode=%r)"
|
||||
% (self.environmentname, self.args_parser, self.is_math_mode)
|
||||
)
|
||||
|
||||
|
||||
class SpecialsSpec(object):
|
||||
r"""
|
||||
Specification of a LaTeX "special char sequence": an active char, a
|
||||
ligature, or some other non-macro char sequence that has a special meaning.
|
||||
|
||||
For instance, '&', '~', and '``' are considered as "specials".
|
||||
|
||||
.. py:attribute:: specials_chars
|
||||
|
||||
The string (one or several characters) that has a special meaning. E.g.,
|
||||
'&', '~', '``', etc.
|
||||
|
||||
.. py:attribute:: args_parser
|
||||
|
||||
A parser (e.g. :py:class:`MacroStandardArgsParser`) that is invoked when
|
||||
the specials is encountered. Can/should be set to `None` if the specials
|
||||
should not parse any arguments (e.g. '~').
|
||||
"""
|
||||
|
||||
def __init__(self, specials_chars, args_parser=None, **kwargs):
|
||||
super(SpecialsSpec, self).__init__(**kwargs)
|
||||
self.specials_chars = specials_chars
|
||||
self.args_parser = args_parser
|
||||
|
||||
def parse_args(self, *args, **kwargs):
|
||||
r"""
|
||||
Basically a shorthand for calling the :py:attr:`args_parser`\ 's
|
||||
`parse_args()` method. See :py:class:`MacroStandardArgsParser`.
|
||||
|
||||
If however the py:attr:`args_parser` attribute is `None`, then this
|
||||
method returns `None`.
|
||||
"""
|
||||
if self.args_parser is None:
|
||||
return None
|
||||
return self.args_parser.parse_args(*args, **kwargs)
|
||||
|
||||
def __repr__(self):
|
||||
return "SpecialsSpec(specials_chars=%r, args_parser=%r)" % (
|
||||
self.specials_chars,
|
||||
self.args_parser,
|
||||
)
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
def std_macro(macname, *args, **kwargs):
|
||||
r"""
|
||||
Return a macro specification for the given macro. Syntax::
|
||||
|
||||
spec = std_macro(macname, argspec)
|
||||
# or
|
||||
spec = std_macro(macname, optarg, numargs)
|
||||
# or
|
||||
spec = std_macro( (macname, argspec), )
|
||||
# or
|
||||
spec = std_macro( (macname, optarg, numargs), )
|
||||
# or
|
||||
spec = std_macro( spec ) # spec is already a `MacroSpec` -- no-op
|
||||
|
||||
- `macname` is the name of the macro, without the leading backslash.
|
||||
|
||||
- `argspec` is a string either characters "\*", "{" or "[", in which star
|
||||
indicates an optional asterisk character (e.g. starred macro variants),
|
||||
each curly brace specifies a mandatory argument and each square bracket
|
||||
specifies an optional argument in square brackets. For example, "{{\*[{"
|
||||
expects two mandatory arguments, then an optional star, an optional
|
||||
argument in square brackets, and then another mandatory argument.
|
||||
|
||||
`argspec` may also be `None`, which is the same as ``argspec=''``.
|
||||
|
||||
- `optarg` may be one of `True`, `False`, or `None`, corresponding to these
|
||||
possibilities:
|
||||
|
||||
+ if `True`, the macro expects as first argument an optional argument in
|
||||
square brackets. Then, `numargs` specifies the number of additional
|
||||
mandatory arguments to the command, given in usual curly braces (or
|
||||
simply as one TeX token like a single macro)
|
||||
|
||||
+ if `False`, the macro only expects a number of mandatory arguments given
|
||||
by `numargs`. The mandatory arguments are given in usual curly braces
|
||||
(or simply as one TeX token like a single macro)
|
||||
|
||||
+ if `None`, then `numargs` is a string like `argspec` above. I.e.,
|
||||
``std_macro(macname, None, argspec)`` is the same as
|
||||
``std_macro(macname, argspec)``.
|
||||
|
||||
- `numargs`: depends on `optarg`, see above.
|
||||
|
||||
To make environment specifications (:py:class:`EnvironmentSpec`) instead of
|
||||
a macro specification, use the function :py:func:`std_environment()`
|
||||
instead.
|
||||
|
||||
The helper function :py:func:`std_environment()` is a shorthand for calling
|
||||
this function with additional keyword arguments. An optional keyword
|
||||
argument `make_environment_spec=True` to the present function may be
|
||||
specified to return an `EnvironmentSpec` instead of a `MacroSpec`. In this
|
||||
case, you can further specify the `environment_is_math_mode=True|False` to
|
||||
specify whether of not the environment represents a math mode.
|
||||
"""
|
||||
|
||||
if isinstance(macname, tuple):
|
||||
if len(args) != 0:
|
||||
raise TypeError(
|
||||
"No positional arguments expected if first argument is a tuple"
|
||||
)
|
||||
args = tuple(macname[1:])
|
||||
macname = macname[0]
|
||||
|
||||
if isinstance(macname, MacroSpec):
|
||||
if len(args) != 0:
|
||||
raise TypeError(
|
||||
"No positional arguments expected if first argument is a MacroSpec"
|
||||
)
|
||||
return macname
|
||||
|
||||
if isinstance(macname, EnvironmentSpec):
|
||||
if len(args) != 0:
|
||||
raise TypeError(
|
||||
"No positional arguments expected if first argument is a EnvironmentSpec"
|
||||
)
|
||||
return macname
|
||||
|
||||
if len(args) == 1:
|
||||
# std_macro(macname, argspec)
|
||||
argspec = args[0]
|
||||
elif len(args) != 2:
|
||||
raise TypeError(
|
||||
"Wrong number of arguments for std_macro, macname={!r}, args={!r}".format(
|
||||
macname, args
|
||||
)
|
||||
)
|
||||
elif not args[0] and isinstance(args[1], _basestring):
|
||||
# argspec given in numargs
|
||||
argspec = args[1]
|
||||
else:
|
||||
argspec = ""
|
||||
if args[0]:
|
||||
argspec = "["
|
||||
argspec += "{" * args[1]
|
||||
|
||||
if kwargs.get("make_environment_spec", False):
|
||||
return EnvironmentSpec(
|
||||
macname,
|
||||
args_parser=MacroStandardArgsParser(argspec),
|
||||
is_math_mode=kwargs.get("environment_is_math_mode", False),
|
||||
)
|
||||
return MacroSpec(macname, args_parser=MacroStandardArgsParser(argspec))
|
||||
|
||||
|
||||
def std_environment(envname, *args, **kwargs):
|
||||
r"""
|
||||
Return an environment specification for the given environment. Syntax::
|
||||
|
||||
spec = std_environment(envname, argspec, is_math_mode=True|False)
|
||||
# or
|
||||
spec = std_environment(envname, optarg, numargs, is_math_mode=True|False)
|
||||
# or
|
||||
spec = std_environment( (envname, argspec), is_math_mode=True|False)
|
||||
# or
|
||||
spec = std_environment( (envname, optarg, numargs), is_math_mode=True|False)
|
||||
# or
|
||||
spec = std_environment( spec ) # spec is already a `EnvironmentSpec` -- no-op
|
||||
|
||||
- `envname` is the name of the environment, i.e., the argument to
|
||||
``\begin{...}``.
|
||||
|
||||
- `argspec` is a string either characters "\*", "{" or "[", in which star
|
||||
indicates an optional asterisk character (e.g. starred environment
|
||||
variants), each curly brace specifies a mandatory argument and each square
|
||||
bracket specifies an optional argument in square brackets. For example,
|
||||
"{{\*[{" expects two mandatory arguments, then an optional star, an
|
||||
optional argument in square brackets, and then another mandatory argument.
|
||||
|
||||
`argspec` may also be `None`, which is the same as ``argspec=''``.
|
||||
|
||||
.. note::
|
||||
|
||||
See :py:class:`EnvironmentSpec` for an important remark about starred
|
||||
variants for environments. TL;DR: a starred verison of an environment is
|
||||
defined as a separate `EnvironmentSpec` with the star in the name and
|
||||
*not* using an ``argspec='*'``.
|
||||
|
||||
- `optarg` may be one of `True`, `False`, or `None`, corresponding to these
|
||||
possibilities:
|
||||
|
||||
+ if `True`, the environment expects as first argument an optional argument in
|
||||
square brackets. Then, `numargs` specifies the number of additional
|
||||
mandatory arguments to the command, given in usual curly braces (or
|
||||
simply as one TeX token like a single environment)
|
||||
|
||||
+ if `False`, the environment only expects a number of mandatory arguments given
|
||||
by `numargs`. The mandatory arguments are given in usual curly braces
|
||||
(or simply as one TeX token like a single environment)
|
||||
|
||||
+ if `None`, then `numargs` is a string like `argspec` above. I.e.,
|
||||
``std_environment(envname, None, argspec)`` is the same as
|
||||
``std_environment(envname, argspec)``.
|
||||
|
||||
- `numargs`: depends on `optarg`, see above.
|
||||
|
||||
- `is_math_mode`: if set to True, then the environment represents a math
|
||||
mode environment (e.g., 'equation', 'align', 'gather', etc.), i.e., whose
|
||||
contents should be parsed in an appropriate math mode. Note that
|
||||
`is_math_mode` *must* be given as a keyword argument, in contrast to all
|
||||
other arguments which must be positional (non-keyword) arguments.
|
||||
"""
|
||||
is_math_mode = kwargs.pop("is_math_mode", False)
|
||||
kwargs2 = dict(kwargs)
|
||||
kwargs2.update(make_environment_spec=True, environment_is_math_mode=is_math_mode)
|
||||
return std_macro(envname, *args, **kwargs2)
|
||||
|
||||
|
||||
def std_specials(specials_chars):
|
||||
r"""
|
||||
Return a latex specials specification for the given character sequence. Syntax::
|
||||
|
||||
spec = std_specials(specials_chars)
|
||||
|
||||
where `specials_chars` is the sequence of characters that has a special
|
||||
LaTeX meaning, e.g. ``&`` or ``''``.
|
||||
|
||||
This helper function only allows to create specs for simple specials without
|
||||
any argument parsing. For more complicated specials, you can instantiate a
|
||||
:py:class:`SpecialsSpec` directly.
|
||||
"""
|
||||
return SpecialsSpec(specials_chars, args_parser=None)
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
class LatexContextDb(object):
|
||||
r"""
|
||||
Store a database of specifications of known macros, environments, and other
|
||||
latex specials. This might be, e.g., how many arguments a macro accepts, or
|
||||
how to determine the text representation of a macro or environment.
|
||||
|
||||
When used with :py:class:`pylatexenc.latexwalker.LatexWalker`, the
|
||||
specifications describe mostly rules for parsing arguments of macros and
|
||||
environments, and which sequences of characters to consider as "latex
|
||||
specials". Specifications for macros, environments, and other specials are
|
||||
stored as :py:class:`MacroSpec`, :py:class:`EnvironmentSpec`, and
|
||||
:py:class:`SpecialsSpec` instances, respectively.
|
||||
When used with :py:class:`pylatexenc.latex2text.LatexNodes2Text`, the
|
||||
specifications for macros, environments, and other specials are stored as
|
||||
:py:class:`pylatexenc.latex2text.MacroTextSpec` ,
|
||||
:py:class:`pylatexenc.latex2text.EnvironmentTextSpec`, and
|
||||
:py:class:`pylatexenc.latex2text.SpecialsTextSpec` instances, respectively.
|
||||
|
||||
In fact, the objects stored in this database may be of any type, except that
|
||||
macro specifications must have an attribute `macroname`, environment
|
||||
specifications must have an attribute `environmentname`, and specials
|
||||
specification must have an attribute `specials_chars`.
|
||||
|
||||
The `LatexContextDb` instance is meant to be (pseudo-)immutable. Once
|
||||
constructed and all the definitions added with
|
||||
:py:meth:`add_context_category()`, one should refrain from modifying it
|
||||
directly after providing it to, e.g., a
|
||||
:py:class:`~pylatexenc.latexwalker.LatexWalker` object. The reason is that
|
||||
the latex walker keeps track of what the latex context was when parsing
|
||||
nodes, and modifying the context will modify that stored information, too.
|
||||
Instead of being tempted to modify the object, create a new one with
|
||||
:py:meth:`filter_context()`.
|
||||
|
||||
See :py:func:`pylatexenc.latexwalker.get_default_latex_context_db()` for the
|
||||
default latex context for `latexwalker` with a default collection of known
|
||||
latex macros and environments.
|
||||
See :py:func:`pylatexenc.latex2text.get_default_latex_context_db()` for the
|
||||
default latex context for `latex2text` with a set of text replacements for a
|
||||
collection of known macros and environments.
|
||||
"""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super(LatexContextDb, self).__init__(**kwargs)
|
||||
|
||||
self.category_list = []
|
||||
self.d = {}
|
||||
|
||||
self.unknown_macro_spec = None
|
||||
self.unknown_environment_spec = None
|
||||
self.unknown_specials_spec = None
|
||||
|
||||
def add_context_category(
|
||||
self,
|
||||
category,
|
||||
macros=[],
|
||||
environments=[],
|
||||
specials=[],
|
||||
prepend=False,
|
||||
insert_before=None,
|
||||
insert_after=None,
|
||||
):
|
||||
r"""
|
||||
Register a category of macro and environment specifications in the context
|
||||
database.
|
||||
|
||||
The category name `category` must not already exist in the database.
|
||||
|
||||
The argument `macros` is an iterable (e.g., a list) of macro
|
||||
specification objects. The argument `environments` is an iterable
|
||||
(e.g., a list) of environment spec objects. Similarly, the `specials`
|
||||
argument is an iterable of latex specials spec instances.
|
||||
|
||||
If you specify `prepend=True`, then macro and environment lookups will
|
||||
prioritize this category over other categories. Categories are normally
|
||||
searched for in the order they are registered to the database; if you
|
||||
specify `prepend=True`, then the new category is prepended to the
|
||||
existing list so that it is searched first.
|
||||
|
||||
If `insert_before` is not `None`, then it must be a string; the
|
||||
definitions are inserted in the category list immediately before the
|
||||
given category name, or at the beginning of the list if the given
|
||||
category doesn't exist. If `insert_after` is not `None`, then it must
|
||||
be a string; the definitions are inserted in the category list
|
||||
immediately after the given category name, or at the end of the list if
|
||||
the given category doesn't exist.
|
||||
|
||||
You may only specify one of `prepend=True`, `insert_before='...'` or
|
||||
`insert_after='...'`.
|
||||
"""
|
||||
|
||||
if category in self.category_list:
|
||||
raise ValueError(
|
||||
"Category {} is already registered in the context database".format(
|
||||
category
|
||||
)
|
||||
)
|
||||
|
||||
# ensure only one of these options is set
|
||||
if len([x for x in (prepend, insert_before, insert_after) if x]) > 1:
|
||||
raise TypeError(
|
||||
"add_context_category(): You may only specify one of "
|
||||
"prepend=True, insert_before=... or insert_after=..."
|
||||
)
|
||||
|
||||
if prepend:
|
||||
self.category_list.insert(0, category)
|
||||
elif insert_before:
|
||||
if insert_before in self.category_list:
|
||||
i = self.category_list.index(insert_before)
|
||||
else:
|
||||
i = 0
|
||||
self.category_list.insert(i, category)
|
||||
elif insert_after:
|
||||
if insert_after in self.category_list:
|
||||
i = (
|
||||
self.category_list.index(insert_after) + 1
|
||||
) # insert after found category
|
||||
else:
|
||||
i = len(self.category_list)
|
||||
self.category_list.insert(i, category)
|
||||
else:
|
||||
self.category_list.append(category)
|
||||
|
||||
self.d[category] = {
|
||||
"macros": dict((m.macroname, m) for m in macros),
|
||||
"environments": dict((e.environmentname, e) for e in environments),
|
||||
"specials": dict((s.specials_chars, s) for s in specials),
|
||||
}
|
||||
|
||||
def set_unknown_macro_spec(self, macrospec):
|
||||
r"""
|
||||
Set the macro spec to use when encountering a macro that is not in the
|
||||
database.
|
||||
"""
|
||||
self.unknown_macro_spec = macrospec
|
||||
|
||||
def set_unknown_environment_spec(self, environmentspec):
|
||||
r"""
|
||||
Set the environment spec to use when encountering a LaTeX environment that
|
||||
is not in the database.
|
||||
"""
|
||||
self.unknown_environment_spec = environmentspec
|
||||
|
||||
def set_unknown_specials_spec(self, specialsspec):
|
||||
r"""
|
||||
Set the latex specials spec to use when encountering a LaTeX environment
|
||||
that is not in the database.
|
||||
"""
|
||||
self.unknown_specials_spec = specialsspec
|
||||
|
||||
def categories(self):
|
||||
r"""
|
||||
Return a list of valid category names that are registered in the current
|
||||
database context.
|
||||
"""
|
||||
return list(self.category_list)
|
||||
|
||||
def get_macro_spec(self, macroname):
|
||||
r"""
|
||||
Look up a macro specification by macro name. The macro name is searched for
|
||||
in all categories one by one and the first match is returned.
|
||||
|
||||
Returns a macro spec instance that matches the given `macroname`. If
|
||||
the macro name was not found, we return the default macro specification
|
||||
set by :py:meth:`set_unknown_macro_spec()` or `None` if no such spec was
|
||||
set.
|
||||
"""
|
||||
for cat in self.category_list:
|
||||
# search categories in the given order
|
||||
if macroname in self.d[cat]["macros"]:
|
||||
return self.d[cat]["macros"][macroname]
|
||||
return self.unknown_macro_spec
|
||||
|
||||
def get_environment_spec(self, environmentname):
|
||||
r"""
|
||||
Look up an environment specification by environment name. The environment
|
||||
name is searched for in all categories one by one and the first match is
|
||||
returned.
|
||||
|
||||
Returns the environment spec. If the environment name was not found, we
|
||||
return the default environment specification set by
|
||||
:py:meth:`set_unknown_environment_spec()` or `None` if no such spec was
|
||||
set.
|
||||
"""
|
||||
for cat in self.category_list:
|
||||
# search categories in the given order
|
||||
if environmentname in self.d[cat]["environments"]:
|
||||
return self.d[cat]["environments"][environmentname]
|
||||
return self.unknown_environment_spec
|
||||
|
||||
def get_specials_spec(self, specials_chars):
|
||||
r"""
|
||||
Look up a "latex specials" specification by character sequence. The
|
||||
sequence name is searched for in all categories one by one and the first
|
||||
match is returned.
|
||||
|
||||
If you are parsing a chunk of LaTeX code, you should use
|
||||
:py:meth:`test_for_specials()` instead. Unlike
|
||||
:py:meth:`test_for_specials()`, :py:meth:`get_specials_spec()` returns
|
||||
the first match regardless of matched length. [Rationale: we only need
|
||||
to worry about matching the longest specials sequence when parsing LaTeX
|
||||
code. Calling `get_specials_spec()` means one has already parsed the
|
||||
sequence and one is looking up additional specs on it.]
|
||||
|
||||
Returns the specials spec. If the latex specials was not found, we
|
||||
return the default latex specials specification set by
|
||||
:py:meth:`set_unknown_specials_spec()` or `None` if no such spec was
|
||||
set.
|
||||
"""
|
||||
for cat in self.category_list:
|
||||
# search categories in the given order
|
||||
if specials_chars in self.d[cat]["specials"]:
|
||||
return self.d[cat]["specials"][specials_chars]
|
||||
return self.unknown_specials_spec
|
||||
|
||||
def test_for_specials(self, s, pos, parsing_state=None):
|
||||
r"""
|
||||
Test the given position in the string for any LaTeX specials. The lookup
|
||||
proceeds by searching for in all categories one by one and the first
|
||||
match is returned, except that the longest match accross all categories
|
||||
is returned. For instance, a match of '``' in a later category will
|
||||
take precedence over a match of '`' in a earlier-searched category.
|
||||
|
||||
Returns a specials spec instance, or `None` if no specials are detected
|
||||
at the position `pos`.
|
||||
"""
|
||||
best_match_len = 0
|
||||
best_match_s = None
|
||||
for cat in self.category_list:
|
||||
# search categories in the given order
|
||||
for specials_chars in self.d[cat]["specials"].keys():
|
||||
if len(specials_chars) > best_match_len and s.startswith(
|
||||
specials_chars, pos
|
||||
):
|
||||
best_match_s = self.d[cat]["specials"][specials_chars]
|
||||
best_match_len = len(specials_chars)
|
||||
|
||||
return best_match_s # this is None if no match
|
||||
|
||||
def iter_macro_specs(self, categories=None):
|
||||
r"""
|
||||
Yield the macro specs corresponding to all macros in the given categories.
|
||||
|
||||
If `categories` is `None`, then the known macro specs from all
|
||||
categories are provided in one long iterable sequence. Otherwise,
|
||||
`categories` should be a list or iterable of category names (e.g.,
|
||||
'latex-base') of macro specs to return.
|
||||
|
||||
The macro specs from the different categories specified are concatenated
|
||||
into one long sequence which is yielded spec by spec.
|
||||
"""
|
||||
|
||||
if categories is None:
|
||||
categories = self.category_list
|
||||
|
||||
for c in categories:
|
||||
if c not in self.category_list:
|
||||
raise ValueError(
|
||||
"Invalid latex macro spec db category: {!r} (Expected one of {!r})".format(
|
||||
c, self.category_list
|
||||
)
|
||||
)
|
||||
for spec in self.d[c]["macros"].values():
|
||||
yield spec
|
||||
|
||||
def iter_environment_specs(self, categories=None):
|
||||
r"""
|
||||
Yield the environment specs corresponding to all environments in the given
|
||||
categories.
|
||||
|
||||
If `categories` is `None`, then the known environment specs from all
|
||||
categories are provided in one long iterable sequence. Otherwise,
|
||||
`categories` should be a list or iterable of category names (e.g.,
|
||||
'latex-base') of environment specs to return.
|
||||
|
||||
The environment specs from the different categories specified are
|
||||
concatenated into one long sequence which is yielded spec by spec.
|
||||
"""
|
||||
|
||||
if categories is None:
|
||||
categories = self.category_list
|
||||
|
||||
for c in categories:
|
||||
if c not in self.category_list:
|
||||
raise ValueError(
|
||||
"Invalid latex environment spec db category: {!r} (Expected one of {!r})".format(
|
||||
c, self.category_list
|
||||
)
|
||||
)
|
||||
for spec in self.d[c]["environments"].values():
|
||||
yield spec
|
||||
|
||||
def iter_specials_specs(self, categories=None):
|
||||
r"""
|
||||
Yield the specials specs corresponding to all environments in the given
|
||||
categories.
|
||||
|
||||
If `categories` is `None`, then the known specials specs from all
|
||||
categories are provided in one long iterable sequence. Otherwise,
|
||||
`categories` should be a list or iterable of category names (e.g.,
|
||||
'latex-base') of specials specs to return.
|
||||
|
||||
The specials specs from the different categories specified are
|
||||
concatenated into one long sequence which is yielded spec by spec.
|
||||
"""
|
||||
|
||||
if categories is None:
|
||||
categories = self.category_list
|
||||
|
||||
for c in categories:
|
||||
if c not in self.category_list:
|
||||
raise ValueError(
|
||||
"Invalid latex environment spec db category: {!r} (Expected one of {!r})".format(
|
||||
c, self.category_list
|
||||
)
|
||||
)
|
||||
for spec in self.d[c]["specials"].values():
|
||||
yield spec
|
||||
|
||||
def filter_context(self, keep_categories=[], exclude_categories=[], keep_which=[]):
|
||||
r"""
|
||||
Return a new :py:class:`LatexContextDb` instance where we only keep
|
||||
certain categories of macro and environment specifications.
|
||||
|
||||
If `keep_categories` is set to a nonempty list, then the returned
|
||||
context will not contain any definitions that do not correspond to the
|
||||
specified categories.
|
||||
|
||||
If `exclude_categories` is set to a nonempty list, then the returned
|
||||
context will not contain any definitions that correspond to the
|
||||
specified categories.
|
||||
|
||||
It is explicitly fine to have category names in `keep_categories` and
|
||||
`exclude_categories` that don't exist in the present object
|
||||
(cf. :py:meth:`categories()`).
|
||||
|
||||
The argument `keep_which`, if non-empty, specifies which definitions to
|
||||
keep. It should be a subset of the list ['macros', 'environments',
|
||||
'specials'].
|
||||
|
||||
The returned context will make a copy of the dictionaries that store the
|
||||
macro and environment specifications, but the specification classes (and
|
||||
corresponding argument parsers) might correspond to the same instances.
|
||||
I.e., the returned context is not a full deep copy.
|
||||
"""
|
||||
|
||||
new_context = LatexContextDb()
|
||||
|
||||
new_context.unknown_macro_spec = self.unknown_macro_spec
|
||||
new_context.unknown_environment_spec = self.unknown_environment_spec
|
||||
new_context.unknown_specials_spec = self.unknown_specials_spec
|
||||
|
||||
keep_macros = not keep_which or "macros" in keep_which
|
||||
keep_environments = not keep_which or "environments" in keep_which
|
||||
keep_specials = not keep_which or "specials" in keep_which
|
||||
|
||||
for cat in self.category_list:
|
||||
if keep_categories and cat not in keep_categories:
|
||||
continue
|
||||
if exclude_categories and cat in exclude_categories:
|
||||
continue
|
||||
|
||||
# include this category
|
||||
new_context.add_context_category(
|
||||
cat,
|
||||
macros=self.d[cat]["macros"].values() if keep_macros else [],
|
||||
environments=self.d[cat]["environments"].values()
|
||||
if keep_environments
|
||||
else [],
|
||||
specials=self.d[cat]["specials"].values() if keep_specials else [],
|
||||
)
|
||||
|
||||
return new_context
|
||||
Loading…
Reference in a new issue