Własny język programowania. Część 2: Parser + wyrażenia = kalkulator

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

Parsowanie czynników

Czynnik jest jedną z trzech rzeczy:

  • liczbą
  • nazwą zmiennej
  • nawiasem zawierającym "sumę"

Skąd mamy wiedzieć którą z nich jest czynnik? Wystarczy spojrzeć na pierwszy znak: jeśli jest cyfrą, to czynnik jest liczbą; jeśli jest literą, to jest nazwą zmiennej; a jeśli jest nawiasem otwierającym, to jest "sumą" po której następuje nawias zamykający:

1
2
3
4
5
6
7
8
9
10
11
12
Expression* Parser::parse_term()
{
  char c = look_ahead();
  if (isdigit(c))
    return parse_Constant();
  else if (isalpha(c))
    return parse_Variable();
  else if (c == '(')
    return parse_paren();
  else
    throw Not_parsed();
}

Parsowanie stałej liczbowej i nazwy zmiennej

Stała liczbowa (zwana też literałem) to ciąg kolejnych cyfr, więc:

Łatwo to zakodować przy użyciu pętli, która odczytuje kolejne cyfry. Jeśli znakiem pod wskaźnikiem nie jest cyfra, znaczy, że przeczytaliśmy już całą liczbę, a wskaźnik ustawiony jest na pierwszym znaku za liczbą (a więc tak jak chcieliśmy). To, że liczba ma przynajmniej jedną cyfrę, zapewnia metoda parse_term, która uruchamia metodę parse_Constant tylko wtedy, gdy zobaczy cyfrę:

1
2
3
4
5
6
7
8
9
10
11
Expression* Parser::parse_Constant()
{
  int n = 0;
  while (isdigit(input[position]))
  {
    n *= 10;
    n += input[position] - '0';
    position++;
  }
  return new Constant(n);
}

Z nazwą zmiennej sprawa ma się analogicznie:

1
2
3
4
5
6
7
8
9
10
Expression* Parser::parse_Variable()
{
  string s;
  while (isalnum(input[position]))
  {
    s.push_back(input[position]);
    position++;
  }
  return new Variable(s);
}

Parsowanie podwyrażeń w nawiasach

Czynnikiem może być też "suma" otoczona nawiasami '(' i ')'. Parsowanie jej to pominięcie nawiasu otwierającego, sparsowanie sumy, a następnie sprawdzenie, czy za sumą znajduje się nawias zamykający:

Kod wygląda następująco:

1
2
3
4
5
6
7
8
9
10
11
12
13
Expression* Parser::parse_paren()
{
  position++; // parse_term zapewnia, że wskaźnik
              // stoi na nawiasie otwierającym '('
  Expression* e = parse_sum();
  if (look_ahead() == ')')
  {
    position++;
    return e;
  }
  else
    throw Not_parsed();
}

Wiersz position++; przesuwa wskaźnik znad znaku ')' nad pierwszy znak za nawiasem.

Kalkulator

Skoro umiemy już parsować wyrażenia do drzew wyrażeń i obliczać wartość drzew, możemy połączyć wszystko w kalkulator -- program, który pyta użytkownika o wyrażenie i oblicza jego wartość:

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
int main()
{
  cout << "Wpisz wyrażenie: ";
 
  string input;
  getline(cin, input);
  
  try
  {
    Parser parser(input);
    Expression* e = parser.parse_Expression();
 
    Memory m;
    cout << "wynik: " << e->eval(m) << endl;
  }
  catch (Not_parsed)
  {
    cout << "BŁĄD: Wyrażenie się nie parsuje" << endl;
  }
  catch (Variable_not_found)
  {
    cout << "BŁĄD: Kalkulator nie używa zmiennych" << endl;
  }
 
  return 0;
}

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

Copyright © 2008-2010 Wrocławski Portal Informatyczny

design: rafalpolito.com