0

I am writing a small piece of code which parses latex files and gives me the newcommands defined. A test case would be this simple latex file:

% +--------------------------------------------------------------------+
% |                                                                    |
% |  New particle stuff                                                |
% |                                                                    |
% +--------------------------------------------------------------------+
\newcommand*{\Hmp}{\ensuremath{H^{\mp}}\xspace}
\newcommand*{\susy}[1]{\ensuremath{\tilde{#1}}\xspace}
\newcommand*{\susy2}[1,2]{\ensuremath{\tilde{#1}\tilde{#2}}\xspace}

There might be a more complicated case where the command expands several lines so I need to keep track of different steps like if the command needs to be incremented with more lines or when the command has finished and is ready to be finished.

The thing is that, within a couple of nested if/else statements the scope of the variables seems lost and the variables are not updated anymore. Here is what am I am doing:

    macros = []
    warg_keep = re.compile("newcommand\*\{(.*)\}\[(.*)\]\{(.*)")
    woarg_keep = re.compile("newcommand\*\{(.*)\}\{(.*)")
    warg_one = re.compile("newcommand\*\{(.*)\}\[(.*)\]\{(.*)\}")
    woarg_one = woarg = re.compile("newcommand\*\{(.*)\}\{(.*)\}") 
    keep = False
    for line in open(file).readlines():
        line = line.strip()
        if len(line) == 0 or line[0] == "%":
            continue
        if not keep:
            newcommand = {"key":"","command":"","args":[]}
        added = False
        if "newcommand" in line:
            if line[-1] == "%":
                clean_line = line[0:-1]
                keep = True
                newcommand = get_cmd_from_line(warg_keep,woarg_keep,clean_line)
            else:
                newcommand = get_cmd_from_line(warg_one, woarg_one, line)
                added = True
        elif keep:
            # Now it dos not matter how it ends, the command will always be added the line without the
            # last character, it can be either % or } but it shouldn't be added
            newcommand["command"] += line[0:-1]
            # End the keep
            if line[-1] != "%":
                keep = False
                added = True
        elif added:
            macros.append(newcommand)

The issue is when I assign the newcommand variable the value I get from the get_cmg_from_line function (which I have tested works perfectly) it doesn't update the newcommand variable but if I move it over the previous if then it recognizes it and updates it. The same thing happens with the keep and added variables.

I have searched for this and found a lot of things about scopes/if/functions etc. which I alrady knew and since ifs shouldn't define scope I don't know why this is happening... am I missing something stupid? How should I update the value of the newcommand variable? Since it might get updated with new lines coming. The only solution I see is to flatten the code but I would like to maintain it like this.

EDIT: I changed a little bit the original code to accommodate extra features of the text but without flattening the code it doesn't work either. So the code on top is not working for the reason I mention. The code below works perfectly and passes all tests:

macros = []
warg_keep = re.compile("newcommand\*\{(.*)\}\[(.*)\]\{(.*)")
woarg_keep = re.compile("newcommand\*\{(.*)\}\{(.*)")
warg_one = re.compile("newcommand\*\{(.*)\}\[(.*)\]\{(.*)\}")
woarg_one = woarg = re.compile("newcommand\*\{(.*)\}\{(.*)\}")
keep = False
for line in open(file).readlines():
    line = line.strip()
    if len(line) == 0 or line[0] == "%":
        continue
    if not keep:
        newcommand = {"key":"","command":"","args":[]}
    added = False
    if "newcommand" in line and line [-1] == "%":
        clean_line = line[0:-1]
        keep = True
        newcommand = get_cmd_from_line(warg_keep,woarg_keep,clean_line)
    if "newcommand" in line and line[-1] != "%":
        newcommand = get_cmd_from_line(warg_one, woarg_one, line)
        added = True
    if not "newcommand" in line and keep:
        # Now it dos not matter how it ends, the command will always be added the line without the
        # last character, it can be either % or } but it shouldn't be added
        newcommand["command"] += line[0:-1]            
    if not "newcommand" in line and keep and line[-1] != "%":
        # End the keep
        keep = False
        added = True
    if added:
        macros.append(newcommand)

2 Answers 2

3

On a cursory inspection, my first guess is that elif keep and elif added should be if keep and if added, respectively.

Another possibility is that you are expecting newcommand to accumulate from one line to the next, but you are resetting it on each pass. Should newcommand = { … } be moved in front of the for line in …:?

Sign up to request clarification or add additional context in comments.

5 Comments

In order to get the new command I need to inspect the line. I have been debugging the code and it does what it should in the sense that it goes into the blocks and the function is called and newcommand is correctly associated (!!) but after going out of the block all variables (newcommand, keep and added) have still the old values.
What is your script supposed to do when keep is set to True in the first if line[-1] == "%"? Should it also go inside elif keep or do that on the next line only? Because that does not work in Python, as mentioned in the answer.
No, if keep is set to true it should only be used for the next lines, thre is no case when "newcommand" is in the line and keep should be set to true. keep is only used to know if the next lines after "newcommand" appears should be used to increment the newcommand variables. As mentioend, debugging the code step by step it works as it should be the variables assignements are note kept outside the second block. If I move them to one abouve, or flatten the code, then it works... this points to me that I am not uncerstanding something about the scope in blocks.
There is no block scope. All local variables are function scope. Whatever your problem is, it isn't scope.
Well, I am not sure what the issue is, but flattening the code works perfectly. I will edit the question with the answer in case someone can figure out what the issue is, it would really good to know.
0

The scope of local variables in Python (like many other scripting languages) is at function level, and not at block level.

Example:

def function():
    x = 5
    if True:
        y = 8
    print(x)
    print(y)

function()
# -> 5
# -> 8

1 Comment

Yes I know, this is we I expect this to work but it seems that within the second block the variables are not recognized. Even within pycharm they appear grayed out, like they are not being used.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.