digits -> letters
The abstract syntax of the language is specified by the following grammar:
Com ::= Ide := NExp assignment
| Com ; Com concatenation
| if BExp then Com else Com conditional
| while BExp do Com iteration
| begin Ide in Com end block with variable declaration
| begin Ide alias Ide in Com end block with alias declaration
The intended meaning of a command like
begin
x
in c
end
is that a new variable x is introduced, and it is local to the block.
The intended meaning of a command like
begin
x alias y
in c
end
is that a new name x is introduced, and it is associated to the same location
of the variable y. In other words, in c we can access the same variable by two names:
x and y.
NExp represents numerical expressions:
NExp ::= Num | Ide | NExp NOp NExp NOp ::= + | * | - | /BExp represents boolean expressions:
BExp ::= true | false | NExp COp NExp | not BExp | BExp BOp BExp COP ::= < | = BOP ::= and | orNum generates the natural numbers, that can be represented as sequences of digits starting with a digit different from 0:
Num ::= 0 | Non_Zero_Digit Seq_Digit Non_Zero_Digit ::= 1 | 2 | 3 | ... | 9 Digit ::= 0 | Non_Zero_Digit Seq_Digit ::= lambda | Digit Seq_DigitIde generates the identifiers, which can be choosen to simply be sequences of letters:
Ide ::= Letter | Letter Ide Letter ::= a | b | c | ... | zNote that the grammar is ambiguous, but we will not worry about that because we assume that it represents the abstract syntax.
We need trees with a different number of subtrees, up to three, depending on the command. For the assignment we need only one subtree; for the concatenation we need two subtrees, for the conditional we need three subtrees, etc. Hence we will declare a structure of the following kind:
class tree{
node* root;
tree* first;
tree* second;
tree* third;
public:
tree* subtree(int n){
switch (n) of {
case 1: return first;
case 2: return second;
case 3: return third;
}
}
...
}
The class node will be analogous to the homonimous class seen in the
notes about the evaluation of expressions.
class environment{
string ide;
location loc;
environment* next;
...
}
We can assume that locations are numbers representing memory addreses.
class environment{
location loc;
int value;
state* next;
...
}
Note: the program is written in C++like, meaning that we use features that we find convenient, even if they are not allowed in real C++ programs (for instance, the type string in the switch statement). Translating the program to a real C++ program should not be difficult.
int eval(tree* t, environment* r){
node* n = t->get_root();
string ty = n->get_type();
switch (ty) of {
case "num":
return n->get_value();
case "ide":
return r->lookup(n->get_ide());
case "op" :
int k1 = eval(t->get_left(), r);
int k2 = eval(t->get_right(), r);
switch (n->get_op()) of {
case "+":
return k1 + k2;
case "*":
return k1 * k2;
case "-":
return k1 - k2;
case "/":
return k1 / k2;
}
case "dec":
string x = n->get_ide();
int k = t->get_left()->get_root()->get_value();
environment r1 = r->add(x,k);
return eval(t->get_right(), r1);
}
}