tutorial 3 -- Calculator α
This time is an example of calcucator.
The Grammar File
%token Number<int> Add Sub Mul Div;
%namespace calc;
%dont_use_stl;
Expr<int> : [Identity] Term(0)
| [MakeAdd] Expr(0) Add Term(1)
| [MakeSub] Expr(0) Sub Term(1)
;
Term<int> : [Identity] Number(0)
| [MakeMul] Term(0) Mul Number(1)
| [MakeDiv] Term(0) Div Number(1)
;
If you experienced usage of BNF, you can easily understand almost all. As described above, the element not followed by "(digit)" is not used in the semantic action.
The Handling File
#include "calc0.hpp"
#include <iostream>
class unexpected_char : public std::exception {};
template < class It >
class scanner {
public:
typedef int char_type;
public:
scanner( It b, It e ) : b_(b), e_(e), c_(b), unget_(EOF) { }
calc::Token get( int& v )
{
int c;
do {
c = getc();
} while( isspace( c ) );
// symbols
switch( c ) {
case '+': return calc::token_Add;
case '-': return calc::token_Sub;
case '*': return calc::token_Mul;
case '/': return calc::token_Div;
case EOF: return calc::token_eof;
}
// integers
if( isdigit( c ) ) {
int n = 0;
while( c != EOF && isdigit( c ) ) {
n *= 10;
n += c - '0';
c = getc();
}
ungetc( c );
v = n;
return calc::token_Number;
}
std::cerr << char(c) << std::endl;
throw unexpected_char();
}
private:
char_type getc()
{
int c;
if( unget_ != EOF ) {
c = unget_;
unget_ = EOF;
} else if( c_ == e_ ) {
c = EOF;
} else {
c = *c_++;
}
return c;
}
void ungetc( char_type c )
{
if( c != EOF ) {
unget_ = c;
}
}
private:
It b_;
It e_;
It c_;
char_type unget_;
};
struct SemanticAction {
void syntax_error(){}
void stack_overflow(){}
void downcast( int& x, int y ) { x = y; }
void upcast( int& x, int y ) { x = y; }
int Identity( int n ) { return n; }
int MakeAdd( int x, int y )
{
std::cerr << "expr " << x << " + " << y << std::endl;
return x + y ;
}
int MakeSub( int x, int y )
{
std::cerr << "expr " << x << " - " << y << std::endl;
return x - y ;
}
int MakeMul( int x, int y )
{
std::cerr << "expr " << x << " * " << y << std::endl;
return x * y ;
}
int MakeDiv( int x, int y )
{
std::cerr << "expr " << x << " / " << y << std::endl;
return x / y ;
}
};
int main( int, char** )
{
// The scanner
typedef std::istreambuf_iterator<char> is_iterator;
is_iterator b( std::cin );
is_iterator e;
scanner< is_iterator > s( b, e );
SemanticAction sa;
calc::Parser< int, SemanticAction > parser( sa );
calc::Token token;
for(;;) {
int v;
token = s.get( v );
if( parser.post( token, v ) ) { break; }
}
int v;
if( parser.accept( v ) ) {
std::cerr << "accpeted\n";
std::cerr << v << std::endl;
}
return 0;
}
This time, for a scanner was necessary, so I prepared a handwritten scanner.
There is no difficult stuffs. Read the source carefully.
If handwriting is troublesome for you, you can use boost::regex etc.
At semantic actions, there is no new type items.
The main function introduced some new items as below:
The return value of parser.post
Returns true when the whole was accepted or error was detected; otherwise returns false.
The return value of parser.accept
parser.accept is valid only after parser.post returned true.
If error occurs, v becomes an indefinite value and the function returns false.
Otherwise it is successful, v is assigned the value of the left side of the root grammar, and
the function returns true.
If you experienced yacc etc., there is no difficult thing.
Execution
% ./calc0 8+3*7 ^D expr 3 * 7 expr 8 + 21 accepted 29