how to print vm commands in correct order

classic Classic list List threaded Threaded
9 messages Options
Reply | Threaded
Open this post in threaded view
|

how to print vm commands in correct order

kraftwerk1611
Hi,

A problem that I am having when writing to a vm file is that in order to write 'function x nLocals' command, you need to know the number of local variables for that function.

However by the time I have this information the vm code for subroutine body has already been written to the vm file. If I print the 'function x nLocals'  now then it ends up at the end of file.

Same happens with other vm commands of seven.jack file, which are appearing in reverse order for me like the following

call Output.printInt 1

function Main.main 0

What is the solution of this problem?

Also to generate vm code for expressions like (1+(2*3)), is it so that one has to know about two or three next tokens?

Thanks
Reply | Threaded
Open this post in threaded view
|

Re: how to print vm commands in correct order

cadet1620
Administrator
kraftwerk1611 wrote
A problem that I am having when writing to a vm file is that in order to write 'function x nLocals' command, you need to know the number of local variables for that function.

However by the time I have this information the vm code for subroutine body has already been written to the vm file. If I print the 'function x nLocals'  now then it ends up at the end of file.
What you need to so is write the function VM command immediately after compiling the <u>varDec</u>, before compiling the <u>statements</u>. At this point, the functions variables have been added to the aymbol table and SymbolTable.varCount(VAR) will return the required argument for the function VM command.
void compileSubroutine() {
    ...
    compileParameterList();
    ...
    vmWriter.writeFunction(name, localsize);
    compileSubroutineBody();
}

Same happens with other vm commands of seven.jack file, which are appearing in reverse order for me like the following
This sounds strange.  compileStatements() should be something very simple like
void compileStatements () {
    // Compiles <statement-list> := <statement> *
    //
    // ENTRY: Tokenizer positioned on the first statement.
    // EXIT:  Tokenizer positioned after the final statement.
    //
    // All commands start with a keyword

    while (tokenizer.tokenType() == TK_KEYWORD) {
        compileStatement();
    }
}

void compileStatement () {
    // Compiles <statement> := <let-statement> | <if-statement> |
    //     <while-statement> | <do-statement> | <return-statement>
    //
    // ENTRY: Tokenizer positioned on the statement.
    // EXIT:  Tokenizer positioned after the statement.

    switch (tokenizer.keyword()) {
        case TK_LET:
            compileLet();
            break;
        ...
        }
    }
}
Also to generate vm code for expressions like (1+(2*3)), is it so that one has to know about two or three next tokens?
This sort of expression will be handled without any lookahead required by compileExpression() calling compileTerm() which will recursively call compileExpression(), which will ...

There is no lookahead required except for in compileSubroutineCall() which needs to look ahead one token to differentiate between
    identifier(...)
and
    identifier.identifier(...)

--Mark
Reply | Threaded
Open this post in threaded view
|

Re: how to print vm commands in correct order

kraftwerk1611
Thank you.

The problem was solved changing locations of writeVM() functions. The method to write 'function x nLocals' was a tricky one. And (1+(2*3) was compiled correctly using code of Project 10 without any extra look ahead.

It turns out that I have to rely on Python global variables to keep track of values of different variables across function calls. Otherwise I will have to change method signatures of compileXXX() functions, which does not look right when producing only xml code, which does not need information about locals and arguments.

In my CompilationEngine the same functions produce both xml and vm outputs.

Anyway for now I am happy to see the compiler generate correct output.

function Main.main 0
push constant 1
push constant 2
push constant 3
call Math.multiply 2
add
call Output.printInt 1
pop temp 0
push constant 0
return

Reply | Threaded
Open this post in threaded view
|

Re: how to print vm commands in correct order

cadet1620
Administrator
Be careful using global variables in a recursive program.

Your compiler needs to be able to handle structures like
if (a) {
    if (b) {
        do thing1();
    } else {
        do thing2();
    }
} else {
    ...
}
In this case, compiling the inner if/else needs jump labels for the if part, else part and endif. These labels must not interfere the outer if/else jump labels.

Another problematic case can be in expressions like
f(a, g(b), c)
where a call is encountered while compiling a call. The nArgs for the call to g must not interfere with the nArgs for the call to f.

The solution to these problems is to use function local variables for things like the if labels and nArgs.

Note that this does not require you you to inline things like parsing expressionList. The return value from a function is effectively a local variable since it is on the stack.
// in compileSubroutineCall
    name = ...;
    ...
    nArgs = compileExpressionList();
    ...
    vmWriter.writeCall(name, nArgs);


Since the XML output is just a useful side effect of the compiler, it is better to change the signatures of a few of the compileXxx() routines if required to improve the readability (and correctness!) of the main objective.

--Mark
Reply | Threaded
Open this post in threaded view
|

Re: how to print vm commands in correct order

kraftwerk1611
Thanks.

When I got this reply, I had already encountered one problem you mentioned i.e f(a, g(b), c)

I have been trying to avoid global variables as much as I can.

However I am not sure how to avoid global variables  in situations like when a function a()  calls b(), then b() calls c(), and then c() calls d(). And there is suppose a subroutine symbol table that is defined in function a() and also used in function d() but functions b() and c() dont use it.

Reply | Threaded
Open this post in threaded view
|

Re: how to print vm commands in correct order

cadet1620
Administrator
kraftwerk1611 wrote
However I am not sure how to avoid global variables  in situations like when a function a()  calls b(), then b() calls c(), and then c() calls d(). And there is suppose a subroutine symbol table that is defined in function a() and also used in function d() but functions b() and c() dont use it.
During compilation, functions are completely independent. None of the arguments or local variables for function a() affect function b().

class MyClass {
    field ...
    static ...

    function § void a() {
        var a, b, c;
        ...
    }

    function § void b() {
        var x, y, z, a, b, c;
        ...
    }
}

When compiling, when the tokenizer gets to position § ('function' has been parsed and compileSubroutine() has been called) you need to call SymboolTable.startSubroutine().

SymboolTable.startSubroutine() throws away all symbol information for the previous subroutine, keeping only the symbol information for the class's fields and statics.

This is why the SymbolTable is recommended to be implemented using two hash tables (Python dictionaries). SymboolTable.startSubroutine() can simply dispose of the subroutine hash table and allocate a new hash table and set localIndex back to 0.

--Mark
Reply | Threaded
Open this post in threaded view
|

Re: how to print vm commands in correct order

kraftwerk1611
Thank you so much for the reply.

Not talking about symbol table but let's say some other variable in first function is assigned a value in it. And then this value is used by fourth function down in the call chain. How can this fourth function d() can have access to the value in first function without this value being passed from a() to b() to c() and then to d(), if this value is not stored in some global variable.

I know this question is not directly related to this compiler code and I was initially hesitant to mention it. But I did face this issue couple of times when writing project 11 code and could not think of any other way to do it except using global variable.
Reply | Threaded
Open this post in threaded view
|

Re: how to print vm commands in correct order

cadet1620
Administrator
Got it.  Functions/variables in the compiler itself, not in the Jack program being compiled...

In the OO model where this hierarchy of functions are all methods of the same object, then these this sort of shared data should be an object instance variable. In Python, those are the self.xxx variables.

In Python you see how they do get passed as arguments through the calling hierarchy because all the object methods have 'self' as an explicit first argument and you must use "self." to reference them.

In languages like C++ and Java, the "this" argument is also passed to every method, but it is done automatically by the compiler so you never see it in the argument list. "this" is also automatically used when referencing instance variables so you don't need to use it unless you have a name collision between a local variable and an instance variable.

--Mark
Reply | Threaded
Open this post in threaded view
|

Re: how to print vm commands in correct order

kraftwerk1611
This post was updated on .
Understood.

So well explained.
Thanks.