6

I am using MathQuill lib for the formula input and trying to restrict a number of operations. Is there any way I can differentiate between a string and a LaTex rule?

For example:

def check('\sum') => True  #LaTex rule
def check('\myCustomVar') => False  #NOT a LaTex rule

Any help will be appreciated!

  • Welcome to TeX.SX! What do you mean with LaTeX rule? A command sequence? And how do you want to check? In LaTeX? In another language? If in another language it may not be on-topic here. – TeXnician Jul 6 '17 at 13:03
  • @TeXnician, thank you. Yes I mean a command sequence. Sorry, I didn't know it's just for LaTex. I am looking for python or javascript solution. – exp Jul 6 '17 at 13:07
  • Well, a command sequence starts with a \ which is usually followed by characters a-z or A-Z (there are exceptions). But to check if it's valid, you would have to compile. But maybe you want to read the TeXBook and source2e (you'll find the most basic hundreds of command sequences there). – TeXnician Jul 6 '17 at 13:13
  • @TeXnician - thanks a lot! and sorry, that I am asking in the wrong place! – exp Jul 6 '17 at 13:17
  • 1
    I think this is comparable to “how do I get the word count of a LaTeX document” — it's a question that is likely to be answered at least partly via means outside of TeX/LaTeX itself; nevertheless it is of interest to users of TeX and LaTeX. (Also this is not a general programming question, because it requires domain-specific knowledge about what TeX control sequences have been defined. This in fact requires interacting with the TeX system, as latexdef does.) – ShreevatsaR Jul 7 '17 at 0:30
11

On Linux at least (don't know about Windows), there is the latexdef script by Martin Scharrer, which looks up LaTeX definitions from the command line:

latexdef section 

will print

\section
\long macro:->\@startsection {section}{1}{\z@ }{-3.5ex \@plus -1ex \@minus -.2ex}{2.3ex \@plus .2ex}{\normalfont \Large \bfseries }

whereas

latexdef sausage 

will print

\sausage
undefined

We can invoke latexdef from Python like so:

import subprocess, re

def latexdef(command_list, *args):
    '''
    call latexdef on a list of commands to be looked up
    *args can be used to pass options to latexdef
    '''
    p = subprocess.Popen(['latexdef'] + list(args) + command_list, \
                        stdout=subprocess.PIPE, \
                        stderr=subprocess.STDOUT)
    return p.communicate()[0].strip()

def are_commands(command_list, *args):
    '''
    look up multiple commands and return results in a dict
    '''
    result = latexdef(command_list, *args)
    frags = [ f.splitlines() for f in re.split(r'\n{2,}', result, re.MULTILINE) ]
    return { command[1:] : defn != 'undefined' for command, defn in frags }

def is_command(command, *args):
    '''
    look up a single command
    '''
    return are_commands([command],*args).values()[0]

if __name__ == '__main__':
    commands = "chapter section sausage".split()

    for command in commands:
        print command, is_command(command)

    print "\nwith book class loaded"

    for command in commands:
        print command, is_command(command, '-c', 'book')

    print "\nall at once, with class book"
    print are_commands(commands, '-c', 'book')

This prints

chapter False
section True
sausage False

with book class loaded
chapter True
section True
sausage False

all at once, with class book
{'sausage:': False, 'section:': True, 'chapter:': True}

Each single invocation of latexdef is rather slow, but time can be saved by looking up multiple commands in a single call. This is the purpose of are_commands, which returns the lookup result for each command in a dict.

Also note that latexdef is a Perl script, so depending on how important this is to you, it might make sense to translate the entire thing to Python, thus cutting out the middleman. But it is a longish script, and Perl is kind of hard on the eyes ...

4

This is not a real answer, but rather a longer comment. The given answer by Michael Palmer does work for most cases if those macros are defined by the core packages/classes.

However: There are some cases you might want to consider. A LaTeX rule how you formulate it probably means command sequence. The typical LaTeX command sequence (I'll call it "cmd" in the following examples) can be produced as the following ABNF:

cmd = "\" 1*ALPHA

But that's not sufficient. You should note that there are internal macros which you might want to in-/exclude separately. That would mean you would have to check for something like

cmd = "\" 1*(ALPHA | "@")

for internal macros. If such a command sequence is valid at the point it is used is context-dependent. Although this rule would check for the validity of the command itself, it mostly has to be used within a \makeatletter ... \makeatother environment to be valid (if your check should involve context).

And that your check should involve context can simply be shown by a command like \frac which is only a "valid LaTeX rule" when used in math mode. Or something like \meter which is only valid within siunitx's commands.

Another case is expl3. l3 commands are also valid in LaTeX if they are enclosed in \ExplSyntaxOn and \ExplSyntaxOff. They would be built with something like this:

cmd = "\" 1*(ALPHA | "_") ":" 0*ALPHA

which is actually not quite true as the characters after the colon are restricted, but it should suffice.

And it's getting even worse if you want to check the validity of user-defined macros within \csname ...\endcsname as the user has many more options here.

Update: The most interesting part after all would be to also check if the call is valid. That would mean that you would have to check the function's signature too and then the command's call. That would mean \frac would only be valid if it's called from within math mode and has two mandatory arguments. F.i. like $\frac{1}{2}$. That's the point where you probably want to compile a sample document, because a real parser would be very complex here.

All those methods have one caveat: You will not only get LaTeX command sequences, but also TeX ones. If you specifically try to get LaTeX ones but want to exclude TeX ones, you'll have a problem.

Update 2: As you were interested in implementation for a test: Here are some regular expression you can use to match. Only on full match you'll actually have a valid sequence in front of you. For the context-sensitive part you may want to work with lookaheads and lookbehinds.

  • standard LaTeX: \\[A-Za-z]*
  • internal LaTeX: \\[A-Za-z@]*
  • expl syntax: \\[A-za-z@_]*:[DNncVvoOxfTFpw]*
  • \csname commands: something like \\.*$
  • 1
    Did you have a look at the latexdef script? It actually invokes Tex -- the engine of your choice, actually -- to look up the command definitions. You can ask it to include any and all packages -- if TeX finds them, latexdef will find them, too. Yes, it is rather slow, and in many cases some regex-based heuristics will be faster and good enough; in real life, I would pre-screen all commands with a regex before invoking latexdef. However, I think it is hard to beat having TeX look things up for you. – Michael Palmer Jul 8 '17 at 18:24
  • 1
    @MichaelPalmer But will it find user-defined packages, own commands and so on too? (the script crashes on my linux machine every time and I didn't want to read perl) That's why my recommendation after all is to run/compile the document too. To explain my separate answer: I assumed (wrongly as I know by now) that MathQuill was something like MathJax which actually allows user commands so I wanted to provide a more general answer. And I also wanted to give the reader to consider that there are some oddities in TeX's macro syntax that might actually be a problem for a general test. – TeXnician Jul 8 '17 at 20:30
  • yes, on my machine, it does find anything that LaTeX will find, including my own packages. That is the beauty of it - as long as it doesn't crash ;) your point about missing validation of actual user input with arguments (as opposed to just macro name) is well taken though. – Michael Palmer Jul 8 '17 at 20:36
  • @MichaelPalmer That's why I pointed out that it's rather a long comment than a real answer. I really like your solution although I can't test it. – TeXnician Jul 8 '17 at 20:40
  • The question asked for a way to determine that \sum is defined in LaTeX, while \myCustomVar is not. I think a regex cannot capture that. :-) – ShreevatsaR Jul 8 '17 at 21:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.