Skip to content

Commit

Permalink
Add function _commands_from_def to 16_latex.convert.ipynb
Browse files Browse the repository at this point in the history
  • Loading branch information
hyunjongkimmath committed Feb 1, 2024
1 parent dae8f57 commit ec76e9b
Show file tree
Hide file tree
Showing 3 changed files with 205 additions and 55 deletions.
164 changes: 134 additions & 30 deletions nbs/16_latex.convert.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -3156,6 +3156,22 @@
"See `nbs/_tests/latex_examples/commands_example/main.tex` for some examples of custom commands."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#| export\n",
"def _argument_detection(group_num: int) -> str:\n",
" \"\"\"\n",
" Helper function to `regex_pattern_detecting_command`, and `_commands_from_def`\n",
"\n",
" This basically helps detect balanced curly braces for invocations of commands.\n",
" \"\"\"\n",
" return \"\\{((?>[^{}]+|\\{(?1)\\})*)\\}\".replace(\"1\", str(group_num))"
]
},
{
"cell_type": "code",
"execution_count": null,
Expand All @@ -3177,8 +3193,8 @@
" \"\"\"\n",
" preamble = remove_comments(preamble)\n",
" latex_commands = _commands_from_newcommand_and_declaremathoperator(preamble)\n",
" # tex_commands = _commands_from_def(preamble)\n",
" return latex_commands\n",
" tex_commands = _commands_from_def(preamble)\n",
" return latex_commands + tex_commands\n",
"\n",
"\n",
"def _commands_from_newcommand_and_declaremathoperator(\n",
Expand All @@ -3187,6 +3203,8 @@
" \"\"\"\n",
" Get custom commands from invocations of `\\newcommand` and `DeclareMathOperator`\n",
" in the preamble.\n",
"\n",
" Helper function to `custom_commands`\n",
" \"\"\"\n",
" # newcommand_regex = regex.compile(\n",
" # r'(?<!%)\\s*\\\\(?:(?:re)?newcommand|DeclareMathOperator)\\s*\\{\\\\\\s*(\\w+)\\s*\\}\\s*(?:\\[(\\d+)\\]\\s*(?:\\[(\\w+)\\])?)?\\s*\\{((?>[^{}]+|\\{(?4)\\})*)\\}', re.MULTILINE)\n",
Expand Down Expand Up @@ -3216,14 +3234,30 @@
" return commands\n",
"\n",
"\n",
"# def _commands_from_def(\n",
"# preamble: str\n",
"# ) -> list[tuple[str, int, Union[str, None], str]]: # Each tuple consists of 1. the name of the custom command 2. the number of parameters 3. The default argument if specified or `None` otherwise, and 4. the display text of the command.\n",
"# \"\"\"\n",
"# \"\"\"\n",
"# def_regex = regex.compile(\n",
"# r'(?<!%)\\s*\\\\def\\s*(\\\\[a-z0-9]+)'\n",
"# )\n"
"def _commands_from_def(\n",
" preamble: str\n",
" ) -> list[tuple[str, int, Union[str, None], str]]: # Each tuple consists of 1. the name of the custom command 2. the number of parameters 3. The default argument if specified or `None` otherwise, and 4. the display text of the command.\n",
" \"\"\"\n",
" \"\"\"\n",
" def_command_identifying = r'(?<!%)\\s*\\\\def\\s*'\n",
" command_name_identifying = r'\\\\\\s*(\\w+)\\s*'\n",
" command_def = _argument_detection(2)\n",
" def_regex = regex.compile(\n",
" f\"{def_command_identifying}{command_name_identifying}{command_def}\"\n",
" )\n",
" return [(match.group(1), 0, None, match.group(2))\n",
" for match in def_regex.finditer(preamble)]\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#| hide\n",
"text = r\"\\def\\calh{{\\mathcal H}}\"\n",
"test_eq(_commands_from_def(text), [('calh', 0, None, '{\\\\mathcal H}')])"
]
},
{
Expand Down Expand Up @@ -3281,26 +3315,20 @@
"text_12 = r'\\DeclareMathOperator{\\tConf}{\\widetilde{Conf}}'\n",
"test_eq(custom_commands(text_12), [('tConf', 0, None, r'\\widetilde{Conf}')])\n",
"\n",
"# TODO: \n",
"# `\\def` commands\n",
"# \\def is a bit complicated because arguments can either be provided with []\n",
"# or can be provided with {}.\n",
"# text_13 = r'\\def\\A{{\\cO_{K}}}'\n",
"text_13 = r'\\def\\A{{\\cO_{K}}}'\n",
"test_eq(custom_commands(text_13), [('A', 0, None, '{\\cO_{K}}')])\n",
"\n",
"# newcommand and renewcommand don't require {} for the\n",
"# command name, cf. https://arxiv.org/abs/1703.05365\n",
"text_14 = r'\\newcommand\\A{{\\mathbb A}}'\n",
"test_eq(custom_commands(text_14), [('A', 0, None, r'{\\mathbb A}')])\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# TODO: use a regexp pattern like this one to extract balanced curly braces\n",
"# \\\\mat\\{((?>[^{}]+|\\{(?1)\\})*)\\}\\{((?>[^{}]+|\\{(?2)\\})*)\\}"
"test_eq(custom_commands(text_14), [('A', 0, None, r'{\\mathbb A}')])\n",
"\n",
"# A test for https://arxiv.org/abs/0902.4637\n",
"text_15 = r'\\newcommand{\\til}[1]{{\\widetilde{#1}}}'\n",
"test_eq(custom_commands(text_15), [('til', 1, None, '{\\\\widetilde{#1}}')])\n"
]
},
{
Expand All @@ -3324,7 +3352,6 @@
" command_name, num_parameters, optional_arg, _ = command_tuple\n",
" backslash_name = fr\"\\\\{command_name}\"\n",
" optional_argument_detection = fr\"(?:\\[(.*?)\\])?\" if optional_arg is not None else \"\"\n",
" argument_detection = r\"\"\n",
" if optional_arg is not None:\n",
" trailing_arguments = [_argument_detection(i) for i in range(2, 1+num_parameters)]\n",
" trailing_args_pattern = \"\\\\s*\".join(trailing_arguments)\n",
Expand All @@ -3339,8 +3366,6 @@
" pattern = f\"{backslash_name}(?![^\\W_])\"\n",
" return regex.compile(pattern)\n",
"\n",
"def _argument_detection(group_num: int):\n",
" return \"\\{((?>[^{}]+|\\{(?1)\\})*)\\}\".replace(\"1\", str(group_num))\n",
" "
]
},
Expand Down Expand Up @@ -3412,9 +3437,24 @@
"text = r'\\delta should not be detected.'\n",
"match = pattern.search(text)\n",
"assert match is None\n",
"# test_eq(replace_command_in_text(text, command_tuple), r'\\delta should be replaced. \\delta should not.')"
"# test_eq(replace_command_in_text(text, command_tuple), r'\\delta should be replaced. \\delta should not.')\n",
"\n",
"# In the following example, the command takes one argument, but sometimes the command\n",
"# is `\\del` \n",
"command_tuple = ('til', 1, None, '{\\\\widetilde{#1}}')\n",
"pattern = regex_pattern_detecting_command(command_tuple)\n",
"text = r'\\til \\calh_g'\n",
"match = pattern.search(text)\n",
"# start, end = match.span()\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
Expand Down Expand Up @@ -3444,12 +3484,17 @@
" text)\n",
" return text\n",
"\n",
"\n",
"def _replace_command(\n",
" match: regex.match,\n",
" command_tuple: tuple[str, int, Union[None, str], str],\n",
" command_pattern: regex.Pattern,\n",
" replace_pattern: re.Pattern) -> str:\n",
" \"\"\"Replace the matched command with the display text\"\"\"\n",
" \"\"\"\n",
" Replace the matched command with the display text\n",
" \n",
" This is a helper function to `replace_command_in_text`.\n",
" \"\"\"\n",
" command_name, num_parameters, optional_arg, display_text = command_tuple\n",
" start, end = match.span()\n",
" matched_string_to_replace = match.string[start:end]\n",
Expand All @@ -3459,9 +3504,36 @@
" return replaced_string\n",
" else:\n",
" return regex.sub(command_pattern, replace_pattern, matched_string_to_replace)\n",
"\n",
"\n",
"# def _replace_nonexplicit_instances_of_command(\n",
"# text: str,\n",
"# command_tuple: tuple[str, int, Union[None, str], str], # Consists of 1. the name of the custom command 2. the number of parameters 3. The default argument if specified or `None` otherwise, and 4. the display text of the command.\n",
"# ) -> str:\n",
"# \"\"\"\n",
"# Replace the nonexplicitly instances of a custom command. \n",
"\n",
"# Sometimes, a LaTeX command is used nonexplicitly, i.e. the arguments are not\n",
"# explicitly typed with surrounding curly braces `{}`. An example of this phenomenon\n",
"# is a command named `\\til` defined by `\\newcommand{\\til}[1]{{\\widetilde{#1}}}`\n",
"# that is later invoked using `$\\til \\calh_g$`.\n",
"\n",
"# This function is only a workaround.\n",
"\n",
"# This is a helper function to `replace_command_in_text`.\n",
"# \"\"\"\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#| hide\n"
]
},
{
"cell_type": "code",
"execution_count": null,
Expand Down Expand Up @@ -3528,18 +3600,33 @@
"def replace_commands_in_text(\n",
" text: str, # The text in which to replace the commands. This should not include the preamble of a latex document.\n",
" command_tuples: tuple[str, int, Union[None, str], str], # An output of `custom_commands`. Each tuple Consists of 1. the name of the custom command 2. the number of parameters 3. The default argument if specified or `None` otherwise, and 4. the display text of the command.\n",
" repeat: int = 1 # The number of times to repeat replacing the commands throughout the text. Defaults to `1`, in which custom commands are replaced throughout the entire document once. If set to -1, then this function attempts to replace custom commands until no commands to replace are found. \n",
" ) -> str:\n",
" \"\"\"\n",
" Replaces all invocations of the specified commands in `text` with the\n",
" display text with the arguments used in the display text.\n",
"\n",
" Assumes that '\\1', '\\2', '\\3', etc. are not part of the display text. \n",
"\n",
" If `repeat` is set to `-1`, then this function attempts to replace\n",
" custom commands until no commands to replace are found. However, this\n",
" might cause infinite loops for some documents.\n",
"\n",
" \"\"\"\n",
" for command_tuple in command_tuples:\n",
" text = replace_command_in_text(text, command_tuple)\n",
" while repeat != 0:\n",
" for command_tuple in command_tuples:\n",
" text = replace_command_in_text(text, command_tuple)\n",
" repeat -= 1\n",
" return text"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The `replace_comands_in_text` function replaces custom commands from a (the main part of) a LaTeX document."
]
},
{
"cell_type": "code",
"execution_count": null,
Expand All @@ -3560,6 +3647,23 @@
" Note that it is not over $\\mathbb{F}_7$ and not over $\\mathbb{F}_2$.''')\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Note that some writers define custom commands using other custom commands. By default, the `replace_commands_in_text` function replaces custom commands just once. In the following example, there is a custom command that is defined using another custom command and the function replace the \"outer\" custom command:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# TODO: continue this example\n",
"text = r''''''"
]
},
{
"cell_type": "code",
"execution_count": null,
Expand Down
2 changes: 2 additions & 0 deletions trouver/_modidx.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@
'trouver/latex/convert.py'),
'trouver.latex.convert._change_counters': ( 'latex.convert.html#_change_counters',
'trouver/latex/convert.py'),
'trouver.latex.convert._commands_from_def': ( 'latex.convert.html#_commands_from_def',
'trouver/latex/convert.py'),
'trouver.latex.convert._commands_from_newcommand_and_declaremathoperator': ( 'latex.convert.html#_commands_from_newcommand_and_declaremathoperator',
'trouver/latex/convert.py'),
'trouver.latex.convert._consider_part_to_add': ( 'latex.convert.html#_consider_part_to_add',
Expand Down
Loading

0 comments on commit ec76e9b

Please sign in to comment.