377 lines
11 KiB
C++
377 lines
11 KiB
C++
/*=============================================================================
|
|
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/variant/apply_visitor.hpp>
|
|
#include <boost/assert.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 += locals[*pc++];
|
|
break;
|
|
|
|
case op_store:
|
|
line += " op_store ";
|
|
line += locals[*pc++];
|
|
break;
|
|
|
|
case op_int:
|
|
line += " op_int ";
|
|
line += std::to_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 += std::to_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 += std::to_string(pos);
|
|
jumps.insert(pos);
|
|
}
|
|
break;
|
|
|
|
case op_stk_adj:
|
|
line += " op_stk_adj ";
|
|
line += std::to_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::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;
|
|
}
|
|
}}
|