Binary min/max function calls in X3 calc ample
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.*.swp | |
.*.swo | |
test | |
*.o |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*============================================================================= | |
Copyright (c) 2001-2014 Joel de Guzman | |
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) | |
=============================================================================*/ | |
#if !defined(BOOST_SPIRIT_X3_CALC9_AST_HPP) | |
#define BOOST_SPIRIT_X3_CALC9_AST_HPP | |
#include <boost/spirit/home/x3/support/ast/variant.hpp> | |
#include <boost/spirit/home/x3/support/ast/position_tagged.hpp> | |
#include <boost/fusion/include/io.hpp> | |
#include <boost/optional.hpp> | |
#include <list> | |
namespace client { namespace ast | |
{ | |
/////////////////////////////////////////////////////////////////////////// | |
// The AST | |
/////////////////////////////////////////////////////////////////////////// | |
namespace x3 = boost::spirit::x3; | |
struct nil {}; | |
struct unary; | |
struct expression; | |
struct function_call; | |
struct variable : x3::position_tagged | |
{ | |
variable(std::string const& name = "") : name(name) {} | |
std::string name; | |
}; | |
struct operand : | |
x3::variant< | |
nil | |
, unsigned int | |
, variable | |
, x3::forward_ast<unary> | |
, x3::forward_ast<expression> | |
, x3::forward_ast<function_call> | |
> | |
{ | |
using base_type::base_type; | |
using base_type::operator=; | |
}; | |
enum optoken | |
{ | |
op_plus, | |
op_minus, | |
op_times, | |
op_divide, | |
op_positive, | |
op_negative, | |
op_not, | |
op_equal, | |
op_not_equal, | |
op_less, | |
op_less_equal, | |
op_greater, | |
op_greater_equal, | |
op_and, | |
op_or | |
}; | |
enum funtoken | |
{ | |
fun_min, | |
fun_max, | |
}; | |
struct unary | |
{ | |
optoken operator_; | |
operand operand_; | |
}; | |
struct operation : x3::position_tagged | |
{ | |
optoken operator_; | |
operand operand_; | |
}; | |
struct expression : x3::position_tagged | |
{ | |
operand first; | |
std::list<operation> rest; | |
}; | |
struct function_call : x3::position_tagged | |
{ | |
funtoken fun; | |
std::list<operand> args; | |
}; | |
struct assignment : x3::position_tagged | |
{ | |
variable lhs; | |
expression rhs; | |
}; | |
struct variable_declaration | |
{ | |
assignment assign; | |
}; | |
struct if_statement; | |
struct while_statement; | |
struct statement_list; | |
struct statement : | |
x3::variant< | |
variable_declaration | |
, assignment | |
, boost::recursive_wrapper<if_statement> | |
, boost::recursive_wrapper<while_statement> | |
, boost::recursive_wrapper<statement_list> | |
> | |
{ | |
using base_type::base_type; | |
using base_type::operator=; | |
}; | |
struct statement_list : std::list<statement> {}; | |
struct if_statement | |
{ | |
expression condition; | |
statement then; | |
boost::optional<statement> else_; | |
}; | |
struct while_statement | |
{ | |
expression condition; | |
statement body; | |
}; | |
// print functions for debugging | |
inline std::ostream& operator<<(std::ostream& out, nil) | |
{ | |
out << "nil"; | |
return out; | |
} | |
inline std::ostream& operator<<(std::ostream& out, variable const& var) | |
{ | |
out << var.name; return out; | |
} | |
}} | |
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*============================================================================= | |
Copyright (c) 2001-2014 Joel de Guzman | |
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) | |
=============================================================================*/ | |
#if !defined(BOOST_SPIRIT_X3_CALC9_AST_ADAPTED_HPP) | |
#define BOOST_SPIRIT_X3_CALC9_AST_ADAPTED_HPP | |
#include "ast.hpp" | |
#include <boost/fusion/include/adapt_struct.hpp> | |
BOOST_FUSION_ADAPT_STRUCT(client::ast::unary, | |
operator_, operand_ | |
) | |
BOOST_FUSION_ADAPT_STRUCT(client::ast::operation, | |
operator_, operand_ | |
) | |
BOOST_FUSION_ADAPT_STRUCT(client::ast::expression, | |
first, rest | |
) | |
BOOST_FUSION_ADAPT_STRUCT(client::ast::function_call, | |
fun, args | |
) | |
BOOST_FUSION_ADAPT_STRUCT(client::ast::variable_declaration, | |
assign | |
) | |
BOOST_FUSION_ADAPT_STRUCT(client::ast::assignment, | |
lhs, rhs | |
) | |
BOOST_FUSION_ADAPT_STRUCT(client::ast::if_statement, | |
condition, then, else_ | |
) | |
BOOST_FUSION_ADAPT_STRUCT(client::ast::while_statement, | |
condition, body | |
) | |
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*============================================================================= | |
Copyright (c) 2001-2014 Joel de Guzman | |
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) | |
=============================================================================*/ | |
#if !defined(BOOST_SPIRIT_X3_CALC9_COMMON_HPP) | |
#define BOOST_SPIRIT_X3_CALC9_COMMON_HPP | |
#include <boost/spirit/home/x3.hpp> | |
namespace client { namespace parser | |
{ | |
using x3::raw; | |
using x3::lexeme; | |
using x3::alpha; | |
using x3::alnum; | |
struct identifier_class; | |
typedef x3::rule<identifier_class, std::string> identifier_type; | |
identifier_type const identifier = "identifier"; | |
auto const identifier_def = raw[lexeme[(alpha | '_') >> *(alnum | '_')]]; | |
BOOST_SPIRIT_DEFINE(identifier) | |
}} | |
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*============================================================================= | |
Copyright (c) 2001-2014 Joel de Guzman | |
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) | |
=============================================================================*/ | |
#include "compiler.hpp" | |
#include "vm.hpp" | |
#include <boost/foreach.hpp> | |
#include <boost/variant/apply_visitor.hpp> | |
#include <boost/assert.hpp> | |
#include <boost/lexical_cast.hpp> | |
#include <iostream> | |
#include <set> | |
#include <iostream> | |
namespace client { namespace code_gen | |
{ | |
void program::op(int a) | |
{ | |
code.push_back(a); | |
} | |
void program::op(int a, int b) | |
{ | |
code.push_back(a); | |
code.push_back(b); | |
} | |
void program::op(int a, int b, int c) | |
{ | |
code.push_back(a); | |
code.push_back(b); | |
code.push_back(c); | |
} | |
int const* program::find_var(std::string const& name) const | |
{ | |
auto i = variables.find(name); | |
if (i == variables.end()) | |
return 0; | |
return &i->second; | |
} | |
void program::add_var(std::string const& name) | |
{ | |
std::size_t n = variables.size(); | |
variables[name] = int(n); | |
} | |
void program::print_variables(std::vector<int> const& stack) const | |
{ | |
for (auto const& p : variables) | |
{ | |
std::cout << " " << p.first << ": " << stack[p.second] << std::endl; | |
} | |
} | |
void program::print_assembler() const | |
{ | |
auto pc = code.begin(); | |
std::vector<std::string> locals(variables.size()); | |
typedef std::pair<std::string, int> pair; | |
for (pair const& p : variables) | |
{ | |
locals[p.second] = p.first; | |
std::cout << "local " | |
<< p.first << ", @" << p.second << std::endl; | |
} | |
std::map<std::size_t, std::string> lines; | |
std::set<std::size_t> jumps; | |
while (pc != code.end()) | |
{ | |
std::string line; | |
std::size_t address = pc - code.begin(); | |
switch (*pc++) | |
{ | |
case op_neg: | |
line += " op_neg"; | |
break; | |
case op_not: | |
line += " op_not"; | |
break; | |
case op_add: | |
line += " op_add"; | |
break; | |
case op_sub: | |
line += " op_sub"; | |
break; | |
case op_mul: | |
line += " op_mul"; | |
break; | |
case op_div: | |
line += " op_div"; | |
break; | |
case op_eq: | |
line += " op_eq"; | |
break; | |
case op_neq: | |
line += " op_neq"; | |
break; | |
case op_lt: | |
line += " op_lt"; | |
break; | |
case op_lte: | |
line += " op_lte"; | |
break; | |
case op_gt: | |
line += " op_gt"; | |
break; | |
case op_gte: | |
line += " op_gte"; | |
break; | |
case op_and: | |
line += " op_and"; | |
break; | |
case op_or: | |
line += " op_or"; | |
break; | |
case op_load: | |
line += " op_load "; | |
line += boost::lexical_cast<std::string>(locals[*pc++]); | |
break; | |
case op_store: | |
line += " op_store "; | |
line += boost::lexical_cast<std::string>(locals[*pc++]); | |
break; | |
case op_int: | |
line += " op_int "; | |
line += boost::lexical_cast<std::string>(*pc++); | |
break; | |
case op_true: | |
line += " op_true"; | |
break; | |
case op_false: | |
line += " op_false"; | |
break; | |
case op_jump: | |
{ | |
line += " op_jump "; | |
std::size_t pos = (pc - code.begin()) + *pc++; | |
if (pos == code.size()) | |
line += "end"; | |
else | |
line += boost::lexical_cast<std::string>(pos); | |
jumps.insert(pos); | |
} | |
break; | |
case op_jump_if: | |
{ | |
line += " op_jump_if "; | |
std::size_t pos = (pc - code.begin()) + *pc++; | |
if (pos == code.size()) | |
line += "end"; | |
else | |
line += boost::lexical_cast<std::string>(pos); | |
jumps.insert(pos); | |
} | |
break; | |
case op_stk_adj: | |
line += " op_stk_adj "; | |
line += boost::lexical_cast<std::string>(*pc++); | |
break; | |
} | |
lines[address] = line; | |
} | |
std::cout << "start:" << std::endl; | |
for (auto const& l : lines) | |
{ | |
std::size_t pos = l.first; | |
if (jumps.find(pos) != jumps.end()) | |
std::cout << pos << ':' << std::endl; | |
std::cout << l.second << std::endl; | |
} | |
std::cout << "end:" << std::endl; | |
} | |
bool compiler::operator()(unsigned int x) const | |
{ | |
program.op(op_int, x); | |
return true; | |
} | |
bool compiler::operator()(bool x) const | |
{ | |
program.op(x ? op_true : op_false); | |
return true; | |
} | |
bool compiler::operator()(ast::variable const& x) const | |
{ | |
int const* p = program.find_var(x.name); | |
if (p == 0) | |
{ | |
error_handler(x, "Undeclared variable: " + x.name); | |
return false; | |
} | |
program.op(op_load, *p); | |
return true; | |
} | |
bool compiler::operator()(ast::operation const& x) const | |
{ | |
if (!boost::apply_visitor(*this, x.operand_)) | |
return false; | |
switch (x.operator_) | |
{ | |
case ast::op_plus: program.op(op_add); break; | |
case ast::op_minus: program.op(op_sub); break; | |
case ast::op_times: program.op(op_mul); break; | |
case ast::op_divide: program.op(op_div); break; | |
case ast::op_equal: program.op(op_eq); break; | |
case ast::op_not_equal: program.op(op_neq); break; | |
case ast::op_less: program.op(op_lt); break; | |
case ast::op_less_equal: program.op(op_lte); break; | |
case ast::op_greater: program.op(op_gt); break; | |
case ast::op_greater_equal: program.op(op_gte); break; | |
case ast::op_and: program.op(op_and); break; | |
case ast::op_or: program.op(op_or); break; | |
default: BOOST_ASSERT(0); return false; | |
} | |
return true; | |
} | |
bool compiler::operator()(ast::unary const& x) const | |
{ | |
if (!boost::apply_visitor(*this, x.operand_)) | |
return false; | |
switch (x.operator_) | |
{ | |
case ast::op_negative: program.op(op_neg); break; | |
case ast::op_not: program.op(op_not); break; | |
case ast::op_positive: break; | |
default: BOOST_ASSERT(0); return false; | |
} | |
return true; | |
} | |
bool compiler::operator()(ast::function_call const& x) const | |
{ | |
auto choice = [&](int opcode) { | |
BOOST_ASSERT(x.args.size() == 2); // TODO FIXME hardcoded binary builtin | |
auto it = x.args.begin(); | |
auto& a = *it++; | |
if (!boost::apply_visitor(*this, a)) | |
return false; | |
auto& b = *it++; | |
if (!boost::apply_visitor(*this, b)) | |
return false; | |
program.op(opcode); // the binary fold operation | |
program.op(op_jump_if, 0); | |
size_t const branch = program.size()-1; | |
if (!boost::apply_visitor(*this, a)) | |
return false; | |
program.op(op_jump, 0); | |
std::size_t continue_ = program.size()-1; | |
program[branch] = int(program.size()-branch); | |
if (!boost::apply_visitor(*this, b)) | |
return false; | |
program[continue_] = int(program.size()-continue_); | |
return true; | |
}; | |
switch (x.fun) { | |
case ast::fun_min: return choice(op_lt); | |
case ast::fun_max: return choice(op_gt); | |
default: BOOST_ASSERT(0); return false; | |
} | |
return true; | |
} | |
bool compiler::operator()(ast::expression const& x) const | |
{ | |
if (!boost::apply_visitor(*this, x.first)) | |
return false; | |
for (ast::operation const& oper : x.rest) | |
{ | |
if (!(*this)(oper)) | |
return false; | |
} | |
return true; | |
} | |
bool compiler::operator()(ast::assignment const& x) const | |
{ | |
if (!(*this)(x.rhs)) | |
return false; | |
int const* p = program.find_var(x.lhs.name); | |
if (p == 0) | |
{ | |
error_handler(x.lhs, "Undeclared variable: " + x.lhs.name); | |
return false; | |
} | |
program.op(op_store, *p); | |
return true; | |
} | |
bool compiler::operator()(ast::variable_declaration const& x) const | |
{ | |
int const* p = program.find_var(x.assign.lhs.name); | |
if (p != 0) | |
{ | |
error_handler(x.assign.lhs, "Duplicate variable: " + x.assign.lhs.name); | |
return false; | |
} | |
bool r = (*this)(x.assign.rhs); | |
if (r) // don't add the variable if the RHS fails | |
{ | |
program.add_var(x.assign.lhs.name); | |
program.op(op_store, *program.find_var(x.assign.lhs.name)); | |
} | |
return r; | |
} | |
bool compiler::operator()(ast::statement const& x) const | |
{ | |
return boost::apply_visitor(*this, x); | |
} | |
bool compiler::operator()(ast::statement_list const& x) const | |
{ | |
for (auto const& s : x) | |
{ | |
if (!(*this)(s)) | |
return false; | |
} | |
return true; | |
} | |
bool compiler::operator()(ast::if_statement const& x) const | |
{ | |
if (!(*this)(x.condition)) | |
return false; | |
program.op(op_jump_if, 0); // we shall fill this (0) in later | |
std::size_t skip = program.size()-1; // mark its position | |
if (!(*this)(x.then)) | |
return false; | |
program[skip] = int(program.size()-skip); // now we know where to jump to (after the if branch) | |
if (x.else_) // We got an alse | |
{ | |
program[skip] += 2; // adjust for the "else" jump | |
program.op(op_jump, 0); // we shall fill this (0) in later | |
std::size_t exit = program.size()-1; // mark its position | |
if (!(*this)(*x.else_)) | |
return false; | |
program[exit] = int(program.size()-exit); // now we know where to jump to (after the else branch) | |
} | |
return true; | |
} | |
bool compiler::operator()(ast::while_statement const& x) const | |
{ | |
std::size_t loop = program.size(); // mark our position | |
if (!(*this)(x.condition)) | |
return false; | |
program.op(op_jump_if, 0); // we shall fill this (0) in later | |
std::size_t exit = program.size()-1; // mark its position | |
if (!(*this)(x.body)) | |
return false; | |
program.op(op_jump, | |
int(loop-1) - int(program.size())); // loop back | |
program[exit] = int(program.size()-exit); // now we know where to jump to (to exit the loop) | |
return true; | |
} | |
bool compiler::start(ast::statement_list const& x) const | |
{ | |
program.clear(); | |
// op_stk_adj 0 for now. we'll know how many variables we'll have later | |
program.op(op_stk_adj, 0); | |
if (!(*this)(x)) | |
{ | |
program.clear(); | |
return false; | |
} | |
program[1] = int(program.nvars()); // now store the actual number of variables | |
return true; | |
} | |
}} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*============================================================================= | |
Copyright (c) 2001-2014 Joel de Guzman | |
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) | |
=============================================================================*/ | |
#if !defined(BOOST_SPIRIT_X3_CALC9_COMPILER_HPP) | |
#define BOOST_SPIRIT_X3_CALC9_COMPILER_HPP | |
#include "ast.hpp" | |
#include "error_handler.hpp" | |
#include <vector> | |
#include <map> | |
namespace client { namespace code_gen | |
{ | |
/////////////////////////////////////////////////////////////////////////// | |
// The Program | |
/////////////////////////////////////////////////////////////////////////// | |
struct program | |
{ | |
void op(int a); | |
void op(int a, int b); | |
void op(int a, int b, int c); | |
int& operator[](std::size_t i) { return code[i]; } | |
int operator[](std::size_t i) const { return code[i]; } | |
void clear() { code.clear(); variables.clear(); } | |
std::size_t size() const { return code.size(); } | |
std::vector<int> const& operator()() const { return code; } | |
std::size_t nvars() const { return variables.size(); } | |
int const* find_var(std::string const& name) const; | |
void add_var(std::string const& name); | |
void print_variables(std::vector<int> const& stack) const; | |
void print_assembler() const; | |
private: | |
std::map<std::string, int> variables; | |
std::vector<int> code; | |
}; | |
//////////////////////////////////////////////////////////////////////////// | |
// The Compiler | |
//////////////////////////////////////////////////////////////////////////// | |
struct compiler | |
{ | |
typedef bool result_type; | |
typedef std::function< | |
void(x3::position_tagged, std::string const&)> | |
error_handler_type; | |
template <typename ErrorHandler> | |
compiler( | |
client::code_gen::program& program | |
, ErrorHandler const& error_handler) | |
: program(program) | |
, error_handler( | |
[&](x3::position_tagged pos, std::string const& msg) | |
{ error_handler(pos, msg); } | |
) | |
{} | |
bool operator()(ast::nil) const { BOOST_ASSERT(0); return false; } | |
bool operator()(unsigned int x) const; | |
bool operator()(bool x) const; | |
bool operator()(ast::variable const& x) const; | |
bool operator()(ast::operation const& x) const; | |
bool operator()(ast::unary const& x) const; | |
bool operator()(ast::function_call const& x) const; | |
bool operator()(ast::expression const& x) const; | |
bool operator()(ast::assignment const& x) const; | |
bool operator()(ast::variable_declaration const& x) const; | |
bool operator()(ast::statement_list const& x) const; | |
bool operator()(ast::statement const& x) const; | |
bool operator()(ast::if_statement const& x) const; | |
bool operator()(ast::while_statement const& x) const; | |
bool start(ast::statement_list const& x) const; | |
client::code_gen::program& program; | |
error_handler_type error_handler; | |
}; | |
}} | |
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*============================================================================= | |
Copyright (c) 2001-2014 Joel de Guzman | |
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) | |
=============================================================================*/ | |
#if !defined(BOOST_SPIRIT_X3_CALC9_CONFIG_HPP) | |
#define BOOST_SPIRIT_X3_CALC9_CONFIG_HPP | |
#include <boost/spirit/home/x3.hpp> | |
#include "error_handler.hpp" | |
namespace client { namespace parser | |
{ | |
typedef std::string::const_iterator iterator_type; | |
typedef x3::phrase_parse_context<x3::ascii::space_type>::type phrase_context_type; | |
typedef error_handler<iterator_type> error_handler_type; | |
typedef x3::with_context< | |
error_handler_tag | |
, std::reference_wrapper<error_handler_type> const | |
, phrase_context_type>::type | |
context_type; | |
}} | |
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*============================================================================= | |
Copyright (c) 2001-2014 Joel de Guzman | |
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) | |
=============================================================================*/ | |
#if !defined(BOOST_SPIRIT_X3_CALC9_ERROR_HANDLER_HPP) | |
#define BOOST_SPIRIT_X3_CALC9_ERROR_HANDLER_HPP | |
#include <boost/spirit/home/x3.hpp> | |
#include <boost/spirit/home/x3/support/utility/error_reporting.hpp> | |
#include <boost/spirit/home/x3/support/ast/position_tagged.hpp> | |
#include "expression.hpp" | |
#include "statement.hpp" | |
namespace client { namespace parser | |
{ | |
namespace x3 = boost::spirit::x3; | |
//////////////////////////////////////////////////////////////////////////// | |
// Our error handler | |
//////////////////////////////////////////////////////////////////////////// | |
template <typename Iterator> | |
using error_handler = x3::error_handler<Iterator>; | |
// tag used to get our error handler from the context | |
using error_handler_tag = x3::error_handler_tag; | |
struct error_handler_base | |
{ | |
template <typename Iterator, typename Exception, typename Context> | |
x3::error_handler_result on_error( | |
Iterator& /*first*/, Iterator const& /*last*/ | |
, Exception const& x, Context const& context) | |
{ | |
std::string message = "Error! Expecting: " + x.which() + " here:"; | |
auto& error_handler = x3::get<error_handler_tag>(context).get(); | |
error_handler(x.where(), message); | |
return x3::error_handler_result::fail; | |
} | |
}; | |
}} | |
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*============================================================================= | |
Copyright (c) 2001-2014 Joel de Guzman | |
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) | |
=============================================================================*/ | |
#include "expression_def.hpp" | |
#include "config.hpp" | |
namespace client { namespace parser | |
{ | |
BOOST_SPIRIT_INSTANTIATE(expression_type, iterator_type, context_type) | |
}} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*============================================================================= | |
Copyright (c) 2001-2014 Joel de Guzman | |
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) | |
=============================================================================*/ | |
#if !defined(BOOST_SPIRIT_X3_CALC9_EXPRESSION_HPP) | |
#define BOOST_SPIRIT_X3_CALC9_EXPRESSION_HPP | |
#include <boost/spirit/home/x3.hpp> | |
#include "ast.hpp" | |
namespace client | |
{ | |
namespace x3 = boost::spirit::x3; | |
namespace parser | |
{ | |
struct expression_class; | |
typedef x3::rule<expression_class, ast::expression> expression_type; | |
BOOST_SPIRIT_DECLARE(expression_type) | |
} | |
parser::expression_type const& expression(); | |
} | |
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*============================================================================= | |
Copyright (c) 2001-2014 Joel de Guzman | |
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) | |
=============================================================================*/ | |
#if !defined(BOOST_SPIRIT_X3_CALC9_EXPRESSION_DEF_HPP) | |
#define BOOST_SPIRIT_X3_CALC9_EXPRESSION_DEF_HPP | |
#include <boost/spirit/home/x3.hpp> | |
#include <boost/spirit/home/x3/support/utility/annotate_on_success.hpp> | |
#include "ast.hpp" | |
#include "ast_adapted.hpp" | |
#include "expression.hpp" | |
#include "common.hpp" | |
#include "error_handler.hpp" | |
namespace client { namespace parser | |
{ | |
using x3::uint_; | |
using x3::char_; | |
using x3::bool_; | |
using x3::raw; | |
using x3::lexeme; | |
using namespace x3::ascii; | |
//////////////////////////////////////////////////////////////////////////// | |
// Tokens | |
//////////////////////////////////////////////////////////////////////////// | |
x3::symbols<ast::optoken> equality_op; | |
x3::symbols<ast::optoken> relational_op; | |
x3::symbols<ast::optoken> logical_op; | |
x3::symbols<ast::optoken> additive_op; | |
x3::symbols<ast::optoken> multiplicative_op; | |
x3::symbols<ast::optoken> unary_op; | |
x3::symbols<ast::funtoken> functions; | |
x3::symbols<> keywords; | |
void add_keywords() | |
{ | |
static bool once = false; | |
if (once) | |
return; | |
once = true; | |
logical_op.add | |
("&&", ast::op_and) | |
("||", ast::op_or) | |
; | |
equality_op.add | |
("==", ast::op_equal) | |
("!=", ast::op_not_equal) | |
; | |
relational_op.add | |
("<", ast::op_less) | |
("<=", ast::op_less_equal) | |
(">", ast::op_greater) | |
(">=", ast::op_greater_equal) | |
; | |
additive_op.add | |
("+", ast::op_plus) | |
("-", ast::op_minus) | |
; | |
multiplicative_op.add | |
("*", ast::op_times) | |
("/", ast::op_divide) | |
; | |
unary_op.add | |
("+", ast::op_positive) | |
("-", ast::op_negative) | |
("!", ast::op_not) | |
; | |
keywords.add | |
("var") | |
("true") | |
("false") | |
("if") | |
("else") | |
("while") | |
; | |
functions.add | |
("min", ast::fun_min) | |
("max", ast::fun_max) | |
; | |
} | |
//////////////////////////////////////////////////////////////////////////// | |
// Main expression grammar | |
//////////////////////////////////////////////////////////////////////////// | |
struct equality_expr_class; | |
struct relational_expr_class; | |
struct logical_expr_class; | |
struct additive_expr_class; | |
struct multiplicative_expr_class; | |
struct unary_expr_class; | |
struct function_call_class; | |
struct primary_expr_class; | |
typedef x3::rule<equality_expr_class, ast::expression> equality_expr_type; | |
typedef x3::rule<relational_expr_class, ast::expression> relational_expr_type; | |
typedef x3::rule<logical_expr_class, ast::expression> logical_expr_type; | |
typedef x3::rule<additive_expr_class, ast::expression> additive_expr_type; | |
typedef x3::rule<multiplicative_expr_class, ast::expression> multiplicative_expr_type; | |
typedef x3::rule<unary_expr_class, ast::operand> unary_expr_type; | |
typedef x3::rule<function_call_class, ast::function_call> function_call_type; | |
typedef x3::rule<primary_expr_class, ast::operand> primary_expr_type; | |
expression_type const expression = "expression"; | |
equality_expr_type const equality_expr = "equality_expr"; | |
relational_expr_type const relational_expr = "relational_expr"; | |
logical_expr_type const logical_expr = "logical_expr"; | |
additive_expr_type const additive_expr = "additive_expr"; | |
multiplicative_expr_type const multiplicative_expr = "multiplicative_expr"; | |
unary_expr_type const unary_expr = "unary_expr"; | |
function_call_type const function_call = "function_call"; | |
primary_expr_type const primary_expr = "primary_expr"; | |
auto const logical_expr_def = | |
equality_expr | |
>> *(logical_op > equality_expr) | |
; | |
auto const equality_expr_def = | |
relational_expr | |
>> *(equality_op > relational_expr) | |
; | |
auto const relational_expr_def = | |
additive_expr | |
>> *(relational_op > additive_expr) | |
; | |
auto const additive_expr_def = | |
multiplicative_expr | |
>> *(additive_op > multiplicative_expr) | |
; | |
auto const multiplicative_expr_def = | |
unary_expr | |
>> *(multiplicative_op > unary_expr) | |
; | |
auto const unary_expr_def = | |
primary_expr | |
| (unary_op > primary_expr) | |
; | |
auto const function_call_def = | |
functions | |
>> '(' >> expression % ',' >> ')' | |
; | |
auto const primary_expr_def = | |
uint_ | |
| bool_ | |
| function_call | |
| (!keywords >> identifier) | |
| ('(' > expression > ')') | |
; | |
auto const expression_def = logical_expr; | |
BOOST_SPIRIT_DEFINE( | |
expression | |
, logical_expr | |
, equality_expr | |
, relational_expr | |
, additive_expr | |
, multiplicative_expr | |
, unary_expr | |
, function_call | |
, primary_expr | |
) | |
struct unary_expr_class : x3::annotate_on_success {}; | |
struct function_call_class : x3::annotate_on_success {}; | |
struct primary_expr_class : x3::annotate_on_success {}; | |
}} | |
namespace client | |
{ | |
parser::expression_type const& expression() | |
{ | |
parser::add_keywords(); | |
return parser::expression; | |
} | |
} | |
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*============================================================================= | |
Copyright (c) 2001-2014 Joel de Guzman | |
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) | |
=============================================================================*/ | |
/////////////////////////////////////////////////////////////////////////////// | |
// | |
// Now we'll introduce boolean expressions and control structures. | |
// Is it obvious now what we are up to? ;-) | |
// | |
// [ JDG April 9, 2007 ] spirit2 | |
// [ JDG February 18, 2011 ] Pure attributes. No semantic actions. | |
// [ JDG June 6, 2014 ] Ported from qi calc8 example. | |
// | |
/////////////////////////////////////////////////////////////////////////////// | |
#include "ast.hpp" | |
#include "vm.hpp" | |
#include "compiler.hpp" | |
#include "statement.hpp" | |
#include "error_handler.hpp" | |
#include "config.hpp" | |
#include <iostream> | |
/////////////////////////////////////////////////////////////////////////////// | |
// Main program | |
/////////////////////////////////////////////////////////////////////////////// | |
int | |
main() | |
{ | |
std::cout << "/////////////////////////////////////////////////////////\n\n"; | |
std::cout << "Statement parser...\n\n"; | |
std::cout << "/////////////////////////////////////////////////////////\n\n"; | |
std::cout << "Type some statements... "; | |
std::cout << "An empty line ends input, compiles, runs and prints results\n\n"; | |
std::cout << "Example:\n\n"; | |
std::cout << " var a = 123;\n"; | |
std::cout << " var b = 456;\n"; | |
std::cout << " var c = a + b * 2;\n\n"; | |
std::cout << "-------------------------\n"; | |
std::string str; | |
std::string source; | |
while (std::getline(std::cin, str)) | |
{ | |
if (str.empty()) | |
break; | |
source += str + '\n'; | |
} | |
using client::parser::iterator_type; | |
iterator_type iter(source.begin()); | |
iterator_type end(source.end()); | |
client::vmachine vm; // Our virtual machine | |
client::code_gen::program program; // Our VM program | |
client::ast::statement_list ast; // Our AST | |
using boost::spirit::x3::with; | |
using client::parser::error_handler_type; | |
error_handler_type error_handler(iter, end, std::cerr); // Our error handler | |
// Our compiler | |
client::code_gen::compiler compile(program, error_handler); | |
// Our parser | |
auto const parser = | |
// we pass our error handler to the parser so we can access | |
// it later on in our on_error and on_sucess handlers | |
with<client::parser::error_handler_tag>(std::ref(error_handler)) | |
[ | |
client::statement() | |
]; | |
using boost::spirit::x3::ascii::space; | |
bool success = phrase_parse(iter, end, parser, space, ast); | |
std::cout << "-------------------------\n"; | |
if (success && iter == end) | |
{ | |
if (compile.start(ast)) | |
{ | |
std::cout << "Success\n"; | |
std::cout << "-------------------------\n"; | |
vm.execute(program()); | |
std::cout << "-------------------------\n"; | |
std::cout << "Assembler----------------\n\n"; | |
program.print_assembler(); | |
std::cout << "-------------------------\n"; | |
std::cout << "Results------------------\n\n"; | |
program.print_variables(vm.get_stack()); | |
} | |
else | |
{ | |
std::cout << "Compile failure\n"; | |
} | |
} | |
else | |
{ | |
std::cout << "Parse failure\n"; | |
} | |
std::cout << "-------------------------\n\n"; | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
all: test | |
CPPFLAGS=-isystem /usr/local/include | |
CPPFLAGS+=-std=c++14 -g -O3 -march=native -Wall -Wextra -pedantic | |
LDFLAGS=$(CXXFLAGS) -lboost_system | |
CXXFLAGS=$(CPPFLAGS) | |
%.o: %.cpp | |
$(CXX) -c $(CXXFLAGS) $< -o $@ | |
test: compiler.cpp expression.cpp main.cpp statement.cpp vm.cpp | |
$(CXX) $^ -o $@ $(LDFLAGS) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*============================================================================= | |
Copyright (c) 2001-2014 Joel de Guzman | |
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) | |
=============================================================================*/ | |
#include "statement_def.hpp" | |
#include "config.hpp" | |
namespace client { namespace parser | |
{ | |
BOOST_SPIRIT_INSTANTIATE(statement_type, iterator_type, context_type) | |
}} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*============================================================================= | |
Copyright (c) 2001-2014 Joel de Guzman | |
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) | |
=============================================================================*/ | |
#if !defined(BOOST_SPIRIT_X3_CALC9_STATEMENT_HPP) | |
#define BOOST_SPIRIT_X3_CALC9_STATEMENT_HPP | |
#include <boost/spirit/home/x3.hpp> | |
#include "ast.hpp" | |
namespace client | |
{ | |
namespace x3 = boost::spirit::x3; | |
namespace parser | |
{ | |
struct statement_class; | |
typedef x3::rule<statement_class, ast::statement_list> statement_type; | |
typedef statement_type::id statement_id; | |
BOOST_SPIRIT_DECLARE(statement_type) | |
} | |
parser::statement_type const& statement(); | |
} | |
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*============================================================================= | |
Copyright (c) 2001-2014 Joel de Guzman | |
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) | |
=============================================================================*/ | |
#if !defined(BOOST_SPIRIT_X3_CALC9_STATEMENT_DEF_HPP) | |
#define BOOST_SPIRIT_X3_CALC9_STATEMENT_DEF_HPP | |
#include <boost/spirit/home/x3.hpp> | |
#include <boost/spirit/home/x3/support/utility/annotate_on_success.hpp> | |
#include "ast.hpp" | |
#include "ast_adapted.hpp" | |
#include "statement.hpp" | |
#include "expression.hpp" | |
#include "common.hpp" | |
#include "error_handler.hpp" | |
namespace client { namespace parser | |
{ | |
using x3::raw; | |
using x3::lexeme; | |
using namespace x3::ascii; | |
struct statement_list_class; | |
struct variable_declaration_class; | |
struct assignment_class; | |
struct variable_class; | |
typedef x3::rule<statement_list_class, ast::statement_list> statement_list_type; | |
typedef x3::rule<variable_declaration_class, ast::variable_declaration> variable_declaration_type; | |
typedef x3::rule<assignment_class, ast::assignment> assignment_type; | |
typedef x3::rule<variable_class, ast::variable> variable_type; | |
statement_type const statement("statement"); | |
statement_list_type const statement_list("statement_list"); | |
variable_declaration_type const variable_declaration("variable_declaration"); | |
assignment_type const assignment("assignment"); | |
variable_type const variable("variable"); | |
// Import the expression rule | |
namespace { auto const& expression = client::expression(); } | |
auto const statement_list_def = | |
+(variable_declaration | assignment) | |
; | |
auto const variable_declaration_def = | |
lexeme["var" >> !(alnum | '_')] // make sure we have whole words | |
> assignment | |
; | |
auto const assignment_def = | |
variable | |
> '=' | |
> expression | |
> ';' | |
; | |
auto const variable_def = identifier; | |
auto const statement_def = statement_list; | |
BOOST_SPIRIT_DEFINE( | |
statement | |
, statement_list | |
, variable_declaration | |
, assignment | |
, variable | |
) | |
struct statement_class : error_handler_base, x3::annotate_on_success {}; | |
struct assignment_class : x3::annotate_on_success {}; | |
struct variable_class : x3::annotate_on_success {}; | |
}} | |
namespace client | |
{ | |
parser::statement_type const& statement() | |
{ | |
return parser::statement; | |
} | |
} | |
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*============================================================================= | |
Copyright (c) 2001-2014 Joel de Guzman | |
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) | |
=============================================================================*/ | |
#include "vm.hpp" | |
#include <boost/assert.hpp> | |
namespace client | |
{ | |
int vmachine::execute( | |
std::vector<int> const& code | |
, std::vector<int>::const_iterator pc | |
, std::vector<int>::iterator frame_ptr | |
) | |
{ | |
std::vector<int>::iterator stack_ptr = frame_ptr; | |
while (pc != code.end()) | |
{ | |
BOOST_ASSERT(pc != code.end()); | |
switch (*pc++) | |
{ | |
case op_neg: | |
stack_ptr[-1] = -stack_ptr[-1]; | |
break; | |
case op_not: | |
stack_ptr[-1] = !bool(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_eq: | |
--stack_ptr; | |
stack_ptr[-1] = bool(stack_ptr[-1] == stack_ptr[0]); | |
break; | |
case op_neq: | |
--stack_ptr; | |
stack_ptr[-1] = bool(stack_ptr[-1] != stack_ptr[0]); | |
break; | |
case op_lt: | |
--stack_ptr; | |
stack_ptr[-1] = bool(stack_ptr[-1] < stack_ptr[0]); | |
break; | |
case op_lte: | |
--stack_ptr; | |
stack_ptr[-1] = bool(stack_ptr[-1] <= stack_ptr[0]); | |
break; | |
case op_gt: | |
--stack_ptr; | |
stack_ptr[-1] = bool(stack_ptr[-1] > stack_ptr[0]); | |
break; | |
case op_gte: | |
--stack_ptr; | |
stack_ptr[-1] = bool(stack_ptr[-1] >= stack_ptr[0]); | |
break; | |
case op_and: | |
--stack_ptr; | |
stack_ptr[-1] = bool(stack_ptr[-1]) && bool(stack_ptr[0]); | |
break; | |
case op_or: | |
--stack_ptr; | |
stack_ptr[-1] = bool(stack_ptr[-1]) || bool(stack_ptr[0]); | |
break; | |
case op_load: | |
*stack_ptr++ = frame_ptr[*pc++]; | |
break; | |
case op_store: | |
--stack_ptr; | |
frame_ptr[*pc++] = stack_ptr[0]; | |
break; | |
case op_int: | |
*stack_ptr++ = *pc++; | |
break; | |
case op_true: | |
*stack_ptr++ = true; | |
break; | |
case op_false: | |
*stack_ptr++ = false; | |
break; | |
case op_jump: | |
pc += *pc; | |
break; | |
case op_jump_if: | |
if (!bool(stack_ptr[-1])) | |
pc += *pc; | |
else | |
++pc; | |
--stack_ptr; | |
break; | |
case op_stk_adj: | |
stack_ptr = stack.begin() + *pc++; | |
break; | |
case op_call: | |
{ | |
int nargs = *pc++; | |
int jump = *pc++; | |
// a function call is a recursive call to execute | |
int r = execute( | |
code | |
, code.begin() + jump | |
, stack_ptr - nargs | |
); | |
// cleanup after return from function | |
stack_ptr[-nargs] = r; // get return value | |
stack_ptr -= (nargs - 1); // the stack will now contain | |
// the return value | |
} | |
break; | |
case op_return: | |
return stack_ptr[-1]; | |
} | |
} | |
return -1; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*============================================================================= | |
Copyright (c) 2001-2014 Joel de Guzman | |
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) | |
=============================================================================*/ | |
#if !defined(BOOST_SPIRIT_X3_CALC9_VM_HPP) | |
#define BOOST_SPIRIT_X3_CALC9_VM_HPP | |
#include <vector> | |
namespace client | |
{ | |
/////////////////////////////////////////////////////////////////////////// | |
// The Virtual Machine | |
/////////////////////////////////////////////////////////////////////////// | |
enum byte_code | |
{ | |
op_neg, // negate the top stack entry | |
op_add, // add top two stack entries | |
op_sub, // subtract top two stack entries | |
op_mul, // multiply top two stack entries | |
op_div, // divide top two stack entries | |
op_not, // boolean negate the top stack entry | |
op_eq, // compare the top two stack entries for == | |
op_neq, // compare the top two stack entries for != | |
op_lt, // compare the top two stack entries for < | |
op_lte, // compare the top two stack entries for <= | |
op_gt, // compare the top two stack entries for > | |
op_gte, // compare the top two stack entries for >= | |
op_and, // logical and top two stack entries | |
op_or, // logical or top two stack entries | |
op_load, // load a variable | |
op_store, // store a variable | |
op_int, // push constant integer into the stack | |
op_true, // push constant 0 into the stack | |
op_false, // push constant 1 into the stack | |
op_jump_if, // jump to a relative position in the code if top stack | |
// evaluates to false | |
op_jump, // jump to a relative position in the code | |
op_stk_adj, // adjust the stack (for args and locals) | |
op_call, // function call | |
op_return // return from function | |
}; | |
class vmachine | |
{ | |
public: | |
vmachine(unsigned stackSize = 4096) | |
: stack(stackSize) | |
{ | |
} | |
int execute( | |
std::vector<int> const& code // the program code | |
, std::vector<int>::const_iterator pc // program counter | |
, std::vector<int>::iterator frame_ptr // start of arguments and locals | |
); | |
int execute(std::vector<int> const& code) | |
{ | |
return execute(code, code.begin(), stack.begin()); | |
}; | |
std::vector<int> const& get_stack() const { return stack; }; | |
private: | |
std::vector<int> stack; | |
}; | |
} | |
#endif | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment