10.2.对变量的处理

\(11.2\)Handling Variables

1.Program compilation

  Remember that the VM language doesn't have symbolic variables, it only has local, this and so on. In order to resolve this pseudocode into final executable VM code, we have to map these symbolic variables on what is called virtual memory segments. And to do this, we:

  • need the variable properties.
  • also have to know the index of the variable of its kind(first, second, etc.).

  And the properties of variables include:

  • name: It's an identifier.

  • type: int, char, boolean and all class name.

  • kind: There are two kinds of variables, and we view them separately:

    • class level: field, static variables.
    • subroutine level: argument and local variables.
  • scope, which is the region of the code in which it is recognized.

2.Symbol table

  We handle the property of variables using a symbol table:

  • A method is always designed to operate on the current object which is called this. Therefore, the symbol table of method always begins with this entry, which represents the properties of current object.
  • this is always treated as argument 0
  • The type of current object is the name of the class to which this subroutine belongs.

  The class level symbol table can be reset each time we begin to compile a new class, for classes are standalone compilation unity. Something similar happens when we compile subroutines.

  The code writer is going to encounter all sorts of variable declaration commands, of which in Jack we have three: local, field and static.

  Then it's going to elucidate all important properties of the declared variables, and add the information to the respective symbol table. It generates no code whatsoever beyond updating the symbol tables.

  • If we are defining a field or a static variable, the code writer will add a new row to the end of the class level symbol table.
  • If we are defining local or argument, the code writer will update the symbol table of the subroutine which is currently being compiled.
  • Arguments are being defined as part of the parameter list of the method's signature. When the code writer goes through the parameter list, it adds the respective lines to the subroutine's symbol table.

  What about using variables within the context of expressions or statements. Take let dx = x - other.getX(); as an example:

  • For each variable in the code, we look up this variable in the subroutine level symbol table.

    • If we find it there, we know which property we have to use.
    • If don't, we revert to looking it up in the class level symbol table.
    • If don't either, we can conclude that the variable is undefined and throw an error message.

  How can the statements be translated into VM code? Take let y = y +dy for example:

  1. In the process of generating code, the code generator will look up the respective table.
  2. It finds out that y stands for the second field of the current object, so it will be translated into this 1
  3. dy stands for the second local variable, so it will be translated into local 1

3.Handling variables in general

  High level of programming languages vary in terms of:

  • variable types
  • variable kinds
  • nested scoping rules

  And the symbol table generation and usage we discussed right now can be easily entended to handle any number of possible variables types and kinds.

  Some languages, like Java, feature unlimited scoping. this means that whenever you define a block of code with a pair of curly brackets, you can define variables within this code which are recognized only within that block. x in this scope doesn't mean the same as x in that block.

  To represent the information in our symbol table mechanism, we use a linked list of symbol tables:

  • When start compiling the class, we first create an linked list.
  • The first symbol table we add to the list is the class level symbol table.
  • When we start compiling a method, we create the method symbol table like before.
  • Whenever we have another scoping region, we add another symbol table to the linked list.

  When you encounter some variable x in the code we:

  1. Look up x in the current scope, which is also the first table.
  2. If fail, we move to the next symbol table.
  3. We simply go on downstream until we get to the class level symbol table.