calc3_lexer.cpp
来自「Boost provides free peer-reviewed portab」· C++ 代码 · 共 204 行
CPP
204 行
/*============================================================================= Copyright (c) 2001-2007 Joel de Guzman Copyright (c) 2001-2008 Hartmut Kaiser Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)=============================================================================*//////////////////////////////////////////////////////////////////////////////////// A calculator example demonstrating the grammar and semantic actions// using phoenix to do the actual expression evaluation. The parser is// essentially an "interpreter" that evaluates expressions on the fly.//// Additionally this examples shows how to build and use a lexer based on // Ben Hansons Lexertl (http://www.benhanson.net/lexertl.html). This way the// parser matches the grammar against the tokens generated by the lexer // component and not against the input character stream.//// Even if the benefits of using a lexer for this small calculator grammar may // not outweight the corresponding overhead, we provide this example because // it allows to concentrate on the essentials without having to understand// the semantics first.//// [ JDG June 29, 2002 ] spirit1// [ JDG March 5, 2007 ] spirit2/////////////////////////////////////////////////////////////////////////////////#include <boost/config/warning_disable.hpp>#include <boost/spirit/include/qi.hpp>#include <boost/spirit/include/lex_lexer_lexertl.hpp>#include <boost/spirit/include/phoenix_operator.hpp>#include <iostream>#include <string>using namespace boost::spirit;using namespace boost::spirit::qi;using namespace boost::spirit::lex;using namespace boost::spirit::ascii;using namespace boost::spirit::arg_names;///////////////////////////////////////////////////////////////////////////////// Our token definition// This class is used to define all the tokens to be recognized by the lexer. ///////////////////////////////////////////////////////////////////////////////template <typename Lexer>struct calculator_tokens : lexer_def<Lexer>{ template <typename Self> void def (Self& self) { // unsigned integer token definition ui = "[1-9][0-9]*"; // whitespace token definitions ws = "[ \\t\\f\\v]+"; c_comment = "\\/\\*[^*]*\\*+([^/*][^*]*\\*+)*\\/"; // build token set skipper = ws | c_comment; // += is allowed as well // associate the tokens and the token set with the lexer // default lexer state self = token_def<>('+') | '-' | '*' | '/' | '(' | ')'; self += ui; // still default state // The token_set 'skipper' get's assigned to a separate lexer state // which allows to use it separately from the main tokenization // (it is used as the skipper parser below) self("SKIPPER") = skipper; // lexer state "SKIPPER" } // This are the tokens to be recognized by the lexer. token_def<unsigned int> ui; // matched tokens will have a unsigned int token_def<> ws, c_comment; // attribute will not be used // This is the only token set explicitly defined by this lexer because it // needs to be accessible from the outside (used as skip parser below). typename Lexer::token_set skipper;};///////////////////////////////////////////////////////////////////////////////// Our calculator grammar//// The difference to the original example (calc3.cpp) is that we are // specifying a second template parameter referring to the lexer. Further, we // use a defined tokenset from above as the skip parser.///////////////////////////////////////////////////////////////////////////////template <typename Iterator, typename Lexer>struct calculator : grammar<Iterator, int(), typename Lexer::token_set>{ template <typename TokenDef> calculator(TokenDef const& tok) : calculator::base_type(expression) { // grammar expression = term [_val = _1] >> *( ('+' >> term [_val += _1]) | ('-' >> term [_val -= _1]) ) ; term = factor [_val = _1] >> *( ('*' >> factor [_val *= _1]) | ('/' >> factor [_val /= _1]) ) ; factor = tok.ui [_val = _1] | '(' >> expression [_val = _1] >> ')' | ('-' >> factor [_val = -_1]) | ('+' >> factor [_val = _1]) ; } rule<Iterator, int(), typename Lexer::token_set> expression, term, factor;};///////////////////////////////////////////////////////////////////////////////// Main program///////////////////////////////////////////////////////////////////////////////intmain(){ std::cout << "/////////////////////////////////////////////////////////\n\n"; std::cout << "Expression parser...\n\n"; std::cout << "/////////////////////////////////////////////////////////\n\n"; std::cout << "Type an expression...or [q or Q] to quit\n\n"; // iterator type used to expose the underlying input stream typedef std::string::const_iterator base_iterator_type; // This is the lexer token type to use. The second template parameter lists // all attribute types used for token_def's during token definition (see // calculator_tokens<> above). Here we use the predefined lexertl token // type, but any compatible token type may be used. typedef lexertl_token< base_iterator_type, boost::mpl::vector<unsigned int> > token_type; // This is the lexer type to use to tokenize the input. // Here we use the lexertl based lexer engine. typedef lexertl_lexer<token_type> lexer_type; // This is the token definition type (derived from the given lexer type). typedef calculator_tokens<lexer_type> calculator_tokens; // this is the iterator type exposed by the lexer typedef lexer<calculator_tokens>::iterator_type iterator_type; // this is the type of the grammar to parse typedef calculator<iterator_type, lexer_type> calculator; // now we use the types defined above to create the lexer and grammar // object instances needed to invoke the parsing process calculator_tokens tokens; // Our token definition calculator calc(tokens); // Our grammar definition lexer<calculator_tokens> lex(tokens); // Our lexer // get input line by line and feed the parser to evaluate the expressions // read in from the input std::string str; int result; while (std::getline(std::cin, str)) { if (str.empty() || str[0] == 'q' || str[0] == 'Q') break; // At this point we generate the iterator pair used to expose the // tokenized input stream. iterator_type iter = lex.begin(str.begin(), str.end()); iterator_type end = lex.end(); // Parsing is done based on the the token stream, not the character // stream read from the input. // Note, how we use the token_set defined above as the skip parser. bool r = phrase_parse(iter, end, calc, result, tokens.skipper); if (r && iter == end) { std::cout << "-------------------------\n"; std::cout << "Parsing succeeded\n"; std::cout << "result = " << result << std::endl; std::cout << "-------------------------\n"; } else { std::cout << "-------------------------\n"; std::cout << "Parsing failed\n"; std::cout << "-------------------------\n"; } } std::cout << "Bye... :-) \n\n"; return 0;}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?