Skip to content

Instantly share code, notes, and snippets.

@sehe
Last active October 12, 2015 02:05
Show Gist options
  • Save sehe/878c623638750cf32404 to your computer and use it in GitHub Desktop.
Save sehe/878c623638750cf32404 to your computer and use it in GitHub Desktop.
Rudimentary stone parser in Boost Spirit
all:test
CPPFLAGS+=-std=c++1y -Wall -pedantic
CPPFLAGS+=-g -O3
#BOOST_DIR=/mnt/LARGE/MODULAR_BOOST/modular-boost
BOOST_DIR=/home/sehe/custom/boost
CPPFLAGS+=-isystem /home/sehe/custom/nonius/include
CPPFLAGS+=-isystem $(BOOST_DIR)
# CPPFLAGS+=-fopenmp
CPPFLAGS+=-pthread
CPPFLAGS+=-march=native
LDFLAGS+=-L $(BOOST_DIR)/stage/lib/ -Wl,-rpath,$(BOOST_DIR)/stage/lib
LDFLAGS+=-lboost_system -lboost_regex -lboost_thread -lboost_iostreams -lboost_serialization
LDFLAGS+=-lboost_date_time
# CXX=g++-5
# CXX=/usr/lib/gcc-snapshot/bin/g++
# CC=/usr/lib/gcc-snapshot/bin/gcc
CXX=clang++-3.6 -stdlib=libc++
# CC=clang
%.S:%.cpp
$(CXX) $(CPPFLAGS) $^ -S -masm=intel -o - | egrep -v '\s*\.' | c++filt > $@
%.o:%.cpp
$(CXX) $(CPPFLAGS) $^ -c -o $@
%:%.o
$(CXX) $(CPPFLAGS) $^ -o $@ $(LDFLAGS)
#include <iostream>
#include <vector>
// a little hack to get X3 DEBUG to "be nice"
#define BOOST_SPIRIT_X3_DEBUG
namespace stone { namespace ast { template<typename... Ts> static std::ostream& operator<<(std::ostream& os, std::vector<Ts...> const&) {
return os << "(astvector)"; // TODO FIXME
} } }
namespace std { using ::stone::ast::operator<<; }
#include <boost/fusion/adapted/struct.hpp>
#include <boost/optional/optional_io.hpp>
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/include/support_istream_iterator.hpp>
#include <boost/phoenix.hpp>
namespace x3 = boost::spirit::x3;
namespace stone { namespace ast {
using boost::variant;
namespace {
template <typename Tag> struct Literal;
namespace Tags {
struct NumberLiteral;
struct StringLiteral;
}
template <> struct Literal<Tags::NumberLiteral> { double value; };
template <> struct Literal<Tags::StringLiteral> { std::string value; };
}
namespace {
using Identifier = std::string;
using Number = Literal<Tags::NumberLiteral>;
using String = Literal<Tags::StringLiteral>;
struct UnaryExpr;
struct BinaryExpr;
using Expression = boost::make_recursive_variant<
Number,
Identifier,
String,
boost::recursive_variant_, // refers to Expression itself
boost::recursive_wrapper<UnaryExpr>,
boost::recursive_wrapper<BinaryExpr>
>::type;
struct UnaryExpr {
char op; // -
Expression expr;
};
struct BinaryExpr {
char op; // +-/*
Expression lhs, rhs;
};
using Simple = Expression;
}
namespace {
struct IfStatement;
struct WhileLoop;
using Statement = boost::make_recursive_variant<
boost::recursive_wrapper<IfStatement>,
boost::recursive_wrapper<WhileLoop>,
Simple
>::type;
using Block = std::vector<Statement>;
struct IfStatement {
Expression condition;
Block true_branch;
boost::optional<Block> false_branch;
};
struct WhileLoop {
Expression condition;
Block body;
};
}
using Program = Block;
} }
#include <boost/type_index.hpp>
namespace stone { namespace operations {
struct dump_ast {
using result_type = void;
std::ostream& os_;
template <typename... Ts> void operator()(boost::variant<Ts...> const& v) {
boost::apply_visitor(*this, v);
}
void operator()(ast::Program const& v) {
os_ << "{ ";
for (auto& s : v) {
(*this)(s);
os_ << "; ";
}
os_ << "} ";
}
void operator()(ast::Number const& v) { os_ << v.value; }
void operator()(ast::String const& v) { os_ << '"' << v.value << '"'; }
void operator()(ast::Identifier const& v) { os_ << '[' << v << ']'; }
void operator()(ast::WhileLoop const& stmt) {
os_ << "while ";
(*this)(stmt.condition);
(*this)(stmt.body);
}
void operator()(ast::IfStatement const& stmt) {
os_ << "if ";
(*this)(stmt.condition);
(*this)(stmt.true_branch);
if (stmt.false_branch) {
os_ << " else ";
(*this)(*stmt.false_branch);
}
}
void operator()(ast::UnaryExpr const& e) {
os_ << "(" << e.op << " ";
(*this)(e.expr);
os_ << ")";
}
void operator()(ast::BinaryExpr const& e) {
os_ << "(";
(*this)(e.lhs);
os_ << e.op << " ";
(*this)(e.rhs);
os_ << ")";
}
};
} }
BOOST_FUSION_ADAPT_TPL_STRUCT( (Tag), (stone::ast::Literal) (Tag), value)
BOOST_FUSION_ADAPT_STRUCT(stone::ast::IfStatement, condition, true_branch, false_branch)
BOOST_FUSION_ADAPT_STRUCT(stone::ast::WhileLoop, condition, body)
BOOST_FUSION_ADAPT_STRUCT(stone::ast::BinaryExpr, lhs, op, rhs)
BOOST_FUSION_ADAPT_STRUCT(stone::ast::UnaryExpr, op, expr)
namespace stone { namespace grammar {
using boost::fusion::at_c;
x3::rule<class Start, ast::Program> start = "start";
x3::rule<class Program, ast::Program> program = "program";
x3::rule<class Block, ast::Block> block = "block";
x3::rule<class Statement, ast::Statement> statement = "statement";
x3::rule<class IfStatement, ast::IfStatement> ifstatement = "ifstatement";
x3::rule<class WhileLoop, ast::WhileLoop> whileloop = "whileloop";
x3::rule<class Simple, ast::Simple> simple = "simple";
x3::rule<class Expression, ast::Expression> expression = "expression";
x3::rule<class UnaryExpr, ast::UnaryExpr> unaryexpr = "unaryexpr";
x3::rule<class Identifier, ast::Identifier> identifier = "identifier";
x3::rule<class Number, ast::Number> number = "number";
x3::rule<class String, ast::String> string = "string";
// precedence helpers
x3::rule<class E_Top, ast::Expression> e_top = "e_top";
x3::rule<class E_Factor, ast::Expression> e_factor = "e_factor";
x3::rule<class E_Term, ast::Expression> e_term = "e_term";
x3::rule<class E_Simple, ast::Expression> e_simple = "e_simple";
const auto start_def = x3::skip(x3::blank) [program];
const auto program_def = -statement % (x3::eol|';');
const auto statement_def = ifstatement | whileloop | simple;
const auto simple_def = !x3::eoi >> expression;
const auto ifstatement_def = "if" >> expression >> block >> -("else" >> block);
const auto whileloop_def = "while" >> expression >> block;
const auto block_def = '{' >> program >> '}';
const auto expression_def = e_top;
const auto e_simple_def = '(' >> expression >> ')' | number | string | identifier;
auto const make_binop = [](auto& ctx) {
auto& lhs = at_c<0>(_attr(ctx));
auto& op = at_c<1>(_attr(ctx));
auto& rhs = at_c<2>(_attr(ctx));
_val(ctx) = ast::BinaryExpr { op, lhs, rhs };
};
auto const propagate = [](auto& ctx) { _val(ctx) = _attr(ctx); }; // _val = _1
const auto e_top_def = (e_factor >> x3::char_("*/") >> e_top) [make_binop] | e_factor [ propagate ];
const auto e_factor_def = (e_term >> x3::char_("-+") >> e_factor) [make_binop] | e_term [ propagate ];
const auto e_term_def = unaryexpr | e_simple;
const auto unaryexpr_def = x3::char_('-') >> e_simple;
const auto number_def = x3::real_parser<double, x3::ureal_policies<double> >();
const auto string_def = x3::lexeme [ '"' >> *~x3::char_('"') >> '"' ];
const auto identifier_def = x3::lexeme [ x3::alpha >> *(x3::alnum | x3::char_('_')) ];
BOOST_SPIRIT_DEFINE(start, program, statement, simple,
ifstatement, whileloop, block, expression, e_simple,
e_top, e_factor, e_term, unaryexpr,
number, string, identifier)
} }
#include <fstream>
int main(int argc, char **argv) {
using It = boost::spirit::istream_iterator;
stone::operations::dump_ast dumper{std::cout};
for (auto const fname : boost::make_iterator_range(argv+1, argv+argc)) {
std::ifstream ifs(fname, std::ios::binary);
It first(ifs >> std::noskipws), last;
stone::ast::Program parsed_ast;
bool ok = x3::parse(first, last, stone::grammar::start, parsed_ast);
if (ok)
{
std::cout << "File " << fname << " was parsed succesfully: ";
dumper(parsed_ast);
std::cout << "\n";
}
else
std::cout << "File " << fname << " failed to parse\n";
if (first!=last)
std::cout << "Warning: trailing unparsed input '" << std::string(first, last) << "'\n";
}
}
1; if IsValid {
make
noise;
while 2 * (8+1)/"something else that is unimportant" {
shoot; cannon
}
} else { cry_in_7_corners_; }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment