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