mirror of
https://github.com/boostorg/spirit.git
synced 2026-01-19 04:42:11 +00:00
It is better to manage warnings on our side to know what warnings we need to fix or suppress, and the only thing that header does is disabling deprecation warnings on MSVC and ICC which we would prefer to not show to users.
248 lines
7.6 KiB
C++
248 lines
7.6 KiB
C++
/*=============================================================================
|
|
Copyright (c) 2001-2010 Joel de Guzman
|
|
Copyright (c) 2001-2010 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 generation of AST from which we generate
|
|
// a simple byte code representation being interpreted by a similar virtual
|
|
// machine.
|
|
//
|
|
// [ JDG April 28, 2008 ]
|
|
// [ HK May 05, 2008 ]
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include <iostream>
|
|
#include <vector>
|
|
#include <string>
|
|
|
|
#include "calc2_ast_vm.hpp"
|
|
|
|
#include <boost/spirit/include/qi.hpp>
|
|
#include <boost/spirit/include/karma.hpp>
|
|
#include <boost/fusion/include/adapt_struct.hpp>
|
|
|
|
using namespace boost::spirit;
|
|
using namespace boost::spirit::ascii;
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Our calculator parser grammar
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
template <typename Iterator>
|
|
struct calculator
|
|
: qi::grammar<Iterator, expression_ast(), space_type>
|
|
{
|
|
calculator() : calculator::base_type(expression)
|
|
{
|
|
expression =
|
|
term [_val = _1]
|
|
>> *( ('+' >> term [_val += _1])
|
|
| ('-' >> term [_val -= _1])
|
|
)
|
|
;
|
|
|
|
term =
|
|
factor [_val = _1]
|
|
>> *( ('*' >> factor [_val *= _1])
|
|
| ('/' >> factor [_val /= _1])
|
|
)
|
|
;
|
|
|
|
factor =
|
|
uint_ [_val = _1]
|
|
| '(' >> expression [_val = _1] >> ')'
|
|
| ('-' >> factor [_val = neg(_1)])
|
|
| ('+' >> factor [_val = pos(_1)])
|
|
;
|
|
}
|
|
|
|
qi::rule<Iterator, expression_ast(), space_type> expression, term, factor;
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// The Virtual Machine
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
class vmachine
|
|
{
|
|
public:
|
|
union element {
|
|
int code;
|
|
char bytes[sizeof(int)];
|
|
};
|
|
|
|
vmachine(unsigned stackSize = 4096)
|
|
: stack(stackSize)
|
|
, stack_ptr(stack.begin())
|
|
{
|
|
}
|
|
|
|
int top() const { return stack_ptr[-1]; };
|
|
void execute(std::vector<element> const& code);
|
|
|
|
private:
|
|
std::vector<int> stack;
|
|
std::vector<int>::iterator stack_ptr;
|
|
};
|
|
|
|
void vmachine::execute(std::vector<element> const& code)
|
|
{
|
|
std::vector<element>::const_iterator pc = code.begin();
|
|
stack_ptr = stack.begin();
|
|
|
|
while ((*pc).code && pc != code.end())
|
|
{
|
|
switch ((*pc++).code)
|
|
{
|
|
case op_neg:
|
|
stack_ptr[-1] = -stack_ptr[-1];
|
|
break;
|
|
|
|
case op_add:
|
|
--stack_ptr;
|
|
stack_ptr[-1] += stack_ptr[0];
|
|
break;
|
|
|
|
case op_sub:
|
|
--stack_ptr;
|
|
stack_ptr[-1] -= stack_ptr[0];
|
|
break;
|
|
|
|
case op_mul:
|
|
--stack_ptr;
|
|
stack_ptr[-1] *= stack_ptr[0];
|
|
break;
|
|
|
|
case op_div:
|
|
--stack_ptr;
|
|
stack_ptr[-1] /= stack_ptr[0];
|
|
break;
|
|
|
|
case op_int:
|
|
*stack_ptr++ = (*pc++).code;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// We need to tell fusion about our binary_op and unary_op structs
|
|
// to make them a first-class fusion citizen
|
|
//
|
|
// Note: we register the members exactly in the same sequence as we need them
|
|
// in the grammar
|
|
BOOST_FUSION_ADAPT_STRUCT(
|
|
binary_op,
|
|
(expression_ast, left)
|
|
(expression_ast, right)
|
|
(int, op)
|
|
)
|
|
|
|
BOOST_FUSION_ADAPT_STRUCT(
|
|
unary_op,
|
|
(expression_ast, right)
|
|
(int, op)
|
|
)
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Our AST grammar for the generator, this just dumps the AST as a expression
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
template <typename OuputIterator, typename Delimiter>
|
|
struct generate_byte_code
|
|
: karma::grammar<OuputIterator, expression_ast(), Delimiter>
|
|
{
|
|
generate_byte_code() : generate_byte_code::base_type(ast_node)
|
|
{
|
|
ast_node %= int_node | binary_node | unary_node;
|
|
int_node %= dword(op_int) << dword;
|
|
binary_node %= ast_node << ast_node << byte_;
|
|
unary_node %= ast_node << byte_;
|
|
}
|
|
|
|
karma::rule<OuputIterator, expression_ast(), Delimiter> ast_node;
|
|
karma::rule<OuputIterator, int(), Delimiter> int_node;
|
|
karma::rule<OuputIterator, binary_op(), Delimiter> binary_node;
|
|
karma::rule<OuputIterator, unary_op(), Delimiter> unary_node;
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// helper function helping to deduce the delimiter type
|
|
template <typename Delimiter>
|
|
bool generate_vm_code(expression_ast const& ast,
|
|
std::vector<vmachine::element>& code, Delimiter const& d)
|
|
{
|
|
// Our generator grammar definitions
|
|
typedef char* output_iterator_type;
|
|
typedef generate_byte_code<output_iterator_type, Delimiter> generate_byte_code;
|
|
|
|
char* outbuffer = (*code.begin()).bytes;
|
|
generate_byte_code gen_vm;
|
|
return karma::generate_delimited(outbuffer, gen_vm, d, ast);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Main program
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
int
|
|
main()
|
|
{
|
|
std::cout << "/////////////////////////////////////////////////////////\n\n";
|
|
std::cout << "Compile simple expressions to bytecode...\n\n";
|
|
std::cout << "/////////////////////////////////////////////////////////\n\n";
|
|
std::cout << "Type an expression...or [q or Q] to quit\n\n";
|
|
|
|
// Our parser grammar definitions
|
|
typedef std::string::const_iterator iterator_type;
|
|
typedef calculator<iterator_type> calculator;
|
|
|
|
calculator calc;
|
|
|
|
std::string str;
|
|
while (std::getline(std::cin, str))
|
|
{
|
|
if (str.empty() || str[0] == 'q' || str[0] == 'Q')
|
|
break;
|
|
|
|
expression_ast ast;
|
|
std::string::const_iterator iter = str.begin();
|
|
std::string::const_iterator end = str.end();
|
|
bool r = qi::phrase_parse(iter, end, calc, space, ast);
|
|
|
|
if (r && iter == end)
|
|
{
|
|
// we assume a vm code size of 4096 is sufficient
|
|
std::vector<vmachine::element> code (4096);
|
|
r = generate_vm_code(ast, code, pad(4));
|
|
|
|
if (r)
|
|
{
|
|
vmachine vm;
|
|
vm.execute(code);
|
|
std::cout << "\nresult = " << vm.top() << std::endl;
|
|
std::cout << "-------------------------\n";
|
|
}
|
|
else
|
|
{
|
|
std::cout << "-------------------------\n";
|
|
std::cout << "Generating failed\n";
|
|
std::cout << "-------------------------\n";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
std::string rest(iter, end);
|
|
std::cout << "-------------------------\n";
|
|
std::cout << "Parsing failed\n";
|
|
std::cout << "stopped at: \": " << rest << "\"\n";
|
|
std::cout << "-------------------------\n";
|
|
}
|
|
}
|
|
|
|
std::cout << "Bye... :-) \n\n";
|
|
return 0;
|
|
}
|
|
|
|
|