Non è necessario analizzare una stringa di notazione prefisso da sinistra a destra per valutarla. Non è necessario convertirlo in un AST (o qualsiasi altro albero) per la valutazione. Valutarlo in realtà può essere abbastanza simile alla valutazione dell'RPN, in effetti.
Con RPN, segui una struttura abbastanza semplice di:
while there's more input
if the next input is an operand, push it on the stack
else (it should be an operator) evaluate it, using operands on the stack
Con la notazione del prefisso, in genere si utilizza la ricorsione anziché uno stack esplicito. Il modello assomiglia a:
while there's more input
get an input (should be an operator)
make N recursive calls to get the operands for that operator
apply operator to operands to get result
Ad esempio, ecco un codice (funzionante) per farlo in C ++:
#include <iostream>
#include <vector>
#include <string>
#include <sstream>
#include <map>
#include <iterator>
using namespace std; // really would *not* normally do this, but...
void define_var(map<string, int> &v, istringstream& iss) {
std::string name;
int value;
iss >> name >> value;
v[name] = value;
}
int do_op(char op, int val1, int val2) {
switch (op) {
case '+': return val1 + val2;
case '-': return val1 - val2;
case '*': return val1 * val2;
case '/': return val1 / val2;
default:
string error("Unknown operator: ");
error += op;
throw runtime_error(error);
}
}
bool isoperator(char ch) {
return ch == '+' || ch == '-' || ch == '*' || ch == '/';
}
char getop(istream &is) {
char ch;
while (isspace(ch = is.peek()))
is.get(ch);
ch = is.peek();
return ch;
}
int eval(istream &is, map<string, int> const &v) {
// evaluate an expression. It consists of:
// an operator followed by operands, or
// a number, or
// a variable.
//
char ch = getop(is);
if (isoperator(ch)) {
is.get(ch);
int val1 = eval(is, v);
int val2 = eval(is, v);
return do_op(ch, val1, val2);
}
if (isdigit(ch)) {
int val;
is >> val;
return val;
}
string var_name;
is >> var_name;
map<string, int>::const_iterator p = v.find(var_name);
if (p==v.end()) {
string problem("Unknown variable: ");
problem +=var_name;
throw runtime_error(problem.c_str());
}
return p->second;
}
// used only for dumping out the variables.
namespace std {
ostream &operator<<(ostream &os, pair<string, int> const &v) {
return os << v.first << ": " << v.second;
}
}
int main() {
map<string, int> v;
string temp;
cout << endl << "> ";
while (getline(cin, temp)) {
istringstream iss(temp);
string op;
iss >> op;
if (op == "quit")
break;
else if (op == "def")
define_var(v, iss);
else if (op == "show_vars")
std::copy(v.begin(), v.end(), ostream_iterator<pair<string, int> >(cout, "\n"));
else {
// Technically, this isn't right -- it only ungets one
// character, not the whole string.
// For example, this would interpret "this+ 2 3" as "+ 2 3"
// and give 5 instead of an error message. Shouldn't affect
// correct input though.
//
iss.unget();
cout << endl << eval(iss, v) << endl;
}
cout << endl << "> ";
}
}
Questo è un po 'più lungo / più complesso della maggior parte dei valutatori postfix, ma almeno una parte della complessità extra è perché fa un po' più della maggior parte. In particolare, supporta la definizione di variabili, l'assegnazione di valori e il dumping di tutti i valori quando hai finito (dove la maggior parte dei valutatori postfix ho visto solo valutare una singola espressione, quindi stampare ciò che è in cima alla pila).