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