Własny język programowania. Część 3: Reprezentacja i interpretacja języka programowania

13.07.2010 - Maciej Piróg
TrudnośćTrudność

Interpretacja

Mamy już klasy, które przechowują w pamięci program w postaci drzewa. Teraz wystarczy dopisać metodę, która interpretuje poszczególne węzły i liście tego drzewa.

Efekt wykonania programu zależy od stanu pamięci, tj. wartości wszystkich zmiennych. Przypomnijmy, że pamięć definiujemy jako:

1
typedef map<string, int> Memory;

Teraz dodamy wirtualną metodę, która odpowiada za interpretację danej instrukcji:

1
2
3
4
5
class Program
{
  public:
    virtual void eval(Memory& m) = 0;
};

Interpretacja instrukcji skip jest banalna. Skoro intrukcja nic nie robi, metoda obliczająca wynik jej działania jest pusta:

1
2
3
4
5
class Skip : public Program
{
  public:
    virtual void eval(Memory& m) { }
};

Instrukcja przypisania oblicza wartość wyrażenia i aktualizuje pamięć:

1
2
3
4
5
6
7
8
9
10
11
12
13
class Assign : public Program
{
    string var;
    Expression* expr;
 
  public:
    Assign(string v, Expression* e) : var(v), expr(e) { }
 
    virtual void eval(Memory& m)
    {
      m[var] = expr->eval(m);
    }
};

Instrukcje read i write wczytują i wypisują na ekran zmienne:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class Read : public Program
{
    string var;
 
  public:
    Read(string v) : var(v) { }
 
    virtual void eval(Memory& m)
    {
      int n;
      cin >> n;
      m[var] = n;
    }
};
 
class Write : public Program
{
    string var;
 
  public:
    Write(string v) : var(v) { }
 
    virtual void eval(Memory& m)
    {
      Memory::iterator it = m.find(var);
      if (it == m.end())
        throw Variable_not_found();
      cout << m[var] << endl;
    }
};

By zinterpretować złożenie, wystarczy zinterpetować po kolei jego elementy:

1
2
3
4
5
6
7
8
9
10
11
12
13
class Composition : public Program
{
    Program* left, * right;
 
  public:
    Composition(Program* l, Program* r) : left(l), right(r) { }
 
    virtual void eval(Memory& m)
    {
      left->eval(m);
      right->eval(m);
    }
};

Interpretacja warunku? Oczywiście najpierw oblicamy wartość wyrażenia, a potem, w zależności od wyniku, odpowiednią wartość.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class If : public Program
{
    Program* branch_then, * branch_else;
    Expression* condition;
 
  public:
    If(Expression* c, Program* t, Program* e)
      : condition(c), branch_then(t), branch_else(e) { }
 
    virtual void eval(Memory& m)
    {
      if (condition->eval(m) != 0)
        branch_then->eval(m);
      else
        branch_else->eval(m);
    }
};

Pozostała jeszcze pętla. Najpierw obliczamy wartość wyrażenia. Jeśli jest ono równe 0, to interpretacja jest zakończona. W przeciwnym wypadku uruchamiamy ciało pętli, by po jego zakończeniu powtórzyć całą procedurę.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class While : public Program
{
    Program *body;
    Expression* condition;
 
  public:
    While(Expression* c, Program* b)
      : condition(c), body(b) { }
 
    virtual void eval(Memory& m)
    {
      if (condition->eval(m) != 0)
      {
        body->eval(m);
        this->eval(m);
      }
    }
};

5
Twoja ocena: Brak Ocena: 5 (1 ocena)

Copyright © 2008-2010 Wrocławski Portal Informatyczny

design: rafalpolito.com