333 lines
8.0 KiB
C++
333 lines
8.0 KiB
C++
/*
|
|
expression → term ;
|
|
term → factor ( ( "-" | "+" ) factor )* ;
|
|
factor → unary ( ( "/" | "*" | "%" ) unary )* ;
|
|
unary → ( "~" | "+" | "-" ) unary
|
|
| shift ;
|
|
shift → andl ( ( "<<" | ">>" ) andl )*
|
|
andl → orl ( "&" orl )*
|
|
orl → andl ( ( "|" | "^" ) andl )*
|
|
primary → NUMBER
|
|
| "(" expression ")" ;
|
|
*/
|
|
|
|
#include <string>
|
|
#include <optional>
|
|
#include <cassert>
|
|
|
|
#include "parser.hpp"
|
|
#include "num.hpp"
|
|
#include "error.hpp"
|
|
|
|
namespace dsp {
|
|
|
|
using enum token_t::type_t;
|
|
|
|
bool parser_t::at_end_p()
|
|
{
|
|
return peek().type == eof;
|
|
}
|
|
|
|
parse_error_t parser_t::error(const token_t& token, const std::string message)
|
|
{
|
|
dsp::error(token, message);
|
|
return parse_error_t(message);
|
|
}
|
|
|
|
token_t& parser_t::previous()
|
|
{
|
|
return tokens[current_ix-1];
|
|
}
|
|
|
|
token_t& parser_t::peek()
|
|
{
|
|
return tokens[current_ix];
|
|
}
|
|
|
|
token_t& parser_t::advance()
|
|
{
|
|
if (!at_end_p()) current_ix++;
|
|
return previous();
|
|
}
|
|
|
|
bool parser_t::check(enum token_t::type_t token_type)
|
|
{
|
|
if (at_end_p()) return false;
|
|
return peek().type == token_type;
|
|
}
|
|
|
|
bool parser_t::match(enum token_t::type_t token_type)
|
|
{
|
|
if (check(token_type)) {
|
|
advance();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
template <typename... Targs>
|
|
bool parser_t::match(enum token_t::type_t token_type, Targs... args)
|
|
{
|
|
return match(token_type) || match(args...);
|
|
}
|
|
|
|
token_t parser_t::consume(enum token_t::type_t token_type, const std::string error_message)
|
|
{
|
|
if (check(token_type)) return advance();
|
|
|
|
throw error(peek(), error_message);
|
|
}
|
|
|
|
expr_t * parser_t::expression()
|
|
{
|
|
return term();
|
|
}
|
|
|
|
expr_t * parser_t::term()
|
|
{
|
|
expr_t * left = factor();
|
|
while (match(minus, plus)) {
|
|
token_t oper = previous();
|
|
expr_t * right = factor();
|
|
left = new binary_t(left, oper, right);
|
|
}
|
|
return left;
|
|
}
|
|
|
|
expr_t * parser_t::factor()
|
|
{
|
|
expr_t * left = unary();
|
|
while (match(slash, star, percent)) {
|
|
token_t oper = previous();
|
|
expr_t * right = unary();
|
|
left = new binary_t(left, oper, right);
|
|
}
|
|
return left;
|
|
}
|
|
|
|
expr_t * parser_t::unary()
|
|
{
|
|
if (match(tilde, plus, minus)) {
|
|
token_t oper = previous();
|
|
expr_t * right = unary();
|
|
return new unary_t(oper, right);
|
|
}
|
|
return shift();
|
|
}
|
|
|
|
expr_t * parser_t::shift()
|
|
{
|
|
expr_t * left = andl();
|
|
while (match(left_shift, right_shift)) {
|
|
token_t oper = previous();
|
|
expr_t * right = andl();
|
|
left = new binary_t(left, oper, right);
|
|
}
|
|
return left;
|
|
}
|
|
|
|
expr_t * parser_t::andl()
|
|
{
|
|
expr_t * left = orl();
|
|
while (match(ampersand)) {
|
|
token_t oper = previous();
|
|
expr_t * right = orl();
|
|
left = new binary_t(left, oper, right);
|
|
}
|
|
return left;
|
|
}
|
|
|
|
expr_t * parser_t::orl()
|
|
{
|
|
expr_t * left = primary();
|
|
while (match(bar, carot)) {
|
|
token_t oper = previous();
|
|
expr_t * right = primary();
|
|
left = new binary_t(left, oper, right);
|
|
}
|
|
return left;
|
|
}
|
|
|
|
expr_t * parser_t::primary()
|
|
{
|
|
if (match(number)) return new literal_t(std::get<num_t>(previous().literal));
|
|
|
|
if (match(left_paren)) {
|
|
expr_t * expr = expression();
|
|
consume(right_paren, "expected ')' after expression");
|
|
return new grouping_t(expr);
|
|
}
|
|
|
|
throw error(peek(), "expected expression");
|
|
}
|
|
|
|
/*
|
|
void parser_t::synchronize()
|
|
{
|
|
advance();
|
|
while (!at_end_p()) {
|
|
if (previous().type == eol) return;
|
|
advance();
|
|
}
|
|
}
|
|
*/
|
|
|
|
std::optional<op::op_t *> parser_t::alu()
|
|
{
|
|
using namespace dsp::op;
|
|
|
|
if (match(_and)) return {new alu_t(alu_type_t::andl)};
|
|
else if (match(_or )) return {new alu_t(alu_type_t::orl)};
|
|
else if (match(_xor)) return {new alu_t(alu_type_t::xorl)};
|
|
else if (match(_add)) return {new alu_t(alu_type_t::add)};
|
|
else if (match(_sub)) return {new alu_t(alu_type_t::sub)};
|
|
else if (match(_ad2)) return {new alu_t(alu_type_t::ad2)};
|
|
else if (match(_sr )) return {new alu_t(alu_type_t::sr)};
|
|
else if (match(_rr )) return {new alu_t(alu_type_t::rr)};
|
|
else if (match(_sl )) return {new alu_t(alu_type_t::sl)};
|
|
else if (match(_rl )) return {new alu_t(alu_type_t::rl)};
|
|
else if (match(_rl8)) return {new alu_t(alu_type_t::rl8)};
|
|
else return {};
|
|
}
|
|
|
|
bool parser_t::xyd1_src()
|
|
{
|
|
const bool mc = match(_mc0) || match(_mc1) || match(_mc2) || match(_mc3);
|
|
const bool m = match(_m0 ) || match(_m1 ) || match(_m2 ) || match(_m3 );
|
|
const bool al = match(_alh) || match(_all);
|
|
return mc || m || al;
|
|
}
|
|
|
|
static op::xy_src_t xy_src(const token_t& token)
|
|
{
|
|
using namespace dsp::op;
|
|
|
|
switch (token.type) {
|
|
case _mc0: return xy_src_t::mc0;
|
|
case _mc1: return xy_src_t::mc1;
|
|
case _mc2: return xy_src_t::mc2;
|
|
case _mc3: return xy_src_t::mc3;
|
|
case _m0: return xy_src_t::m0;
|
|
case _m1: return xy_src_t::m1;
|
|
case _m2: return xy_src_t::m2;
|
|
case _m3: return xy_src_t::m3;
|
|
default: assert(false); __builtin_unreachable();
|
|
}
|
|
}
|
|
|
|
static op::d1_src_t d1_src(const token_t& token)
|
|
{
|
|
using namespace dsp::op;
|
|
|
|
switch (token.type) {
|
|
case _mc0: return d1_src_t::mc0;
|
|
case _mc1: return d1_src_t::mc1;
|
|
case _mc2: return d1_src_t::mc2;
|
|
case _mc3: return d1_src_t::mc3;
|
|
case _m0: return d1_src_t::m0;
|
|
case _m1: return d1_src_t::m1;
|
|
case _m2: return d1_src_t::m2;
|
|
case _m3: return d1_src_t::m3;
|
|
case _alh: return d1_src_t::alh;
|
|
case _all: return d1_src_t::all;
|
|
default: assert(false); __builtin_unreachable();
|
|
}
|
|
}
|
|
|
|
std::optional<op::d1_dest_t> parser_t::d1_dest()
|
|
{
|
|
using namespace dsp::op;
|
|
|
|
if (match(_rx)) return {d1_dest_t::rx};
|
|
else if (match(_pl)) return {d1_dest_t::pl};
|
|
else if (match(_ra0)) return {d1_dest_t::ra0};
|
|
else if (match(_wa0)) return {d1_dest_t::wa0};
|
|
else if (match(_lop)) return {d1_dest_t::lop};
|
|
else if (match(_top)) return {d1_dest_t::top};
|
|
else if (match(_ct0)) return {d1_dest_t::ct0};
|
|
else if (match(_ct1)) return {d1_dest_t::ct1};
|
|
else if (match(_ct2)) return {d1_dest_t::ct2};
|
|
else if (match(_ct3)) return {d1_dest_t::ct3};
|
|
else return {};
|
|
}
|
|
|
|
std::optional<op::op_t *> parser_t::xyd1_bus()
|
|
{
|
|
if (match(_mov)) {
|
|
if (match(_alu)) {
|
|
consume(comma, "expected `,` after `mov alu`");
|
|
consume(_a, "expected `a` after `mov alu,`");
|
|
return {new op::mov_alu_a_t()};
|
|
} else if (match(_mul)) {
|
|
consume(comma, "expected ',' after `mov mul`");
|
|
consume(_p, "expected 'p' after `mov mul,`");
|
|
return {new op::mov_mul_p_t()};
|
|
} else if (xyd1_src()) {
|
|
const token_t& src_token = previous();
|
|
consume(comma, "expected `,` after mov src operand");
|
|
// this is starting to feel a bit ugly...
|
|
bool d1 = src_token.type == _alh || src_token.type == _alh;
|
|
if (!d1 && match(_y)) return {new op::mov_ram_y_t(xy_src(src_token))};
|
|
else if (!d1 && match(_a)) return {new op::mov_ram_a_t(xy_src(src_token))};
|
|
else if (!d1 && match(_x)) return {new op::mov_ram_x_t(xy_src(src_token))};
|
|
else if (!d1 && match(_p)) return {new op::mov_ram_p_t(xy_src(src_token))};
|
|
else if (auto dest_o = d1_dest()) return {new op::mov_ram_d1_t(d1_src(src_token), *dest_o)};
|
|
else
|
|
throw error(peek(), "expected x-bus, y-bus, or d-bus destination operand");
|
|
} else {
|
|
expr_t * expr = expression();
|
|
simm_t<8> simm = simm_t<8>(expr);
|
|
if (auto dest_o = d1_dest())
|
|
return {new op::mov_imm_d1_t(simm, *dest_o)};
|
|
else
|
|
throw error(peek(), "expected d1 destination operand");
|
|
}
|
|
} else if (match(_clr)) {
|
|
consume(_a, "expected `a` after `clr`");
|
|
return {new op::clr_a_t()};
|
|
} else {
|
|
return {};
|
|
}
|
|
}
|
|
|
|
std::optional<stmt_t *> parser_t::op()
|
|
{
|
|
std::vector<const op::op_t *> ops;
|
|
while (true) {
|
|
if (auto stmt_o = alu() ) ops.emplace_back(*stmt_o);
|
|
else if (auto stmt_o = xyd1_bus()) ops.emplace_back(*stmt_o);
|
|
else break;
|
|
}
|
|
if (ops.size() != 0)
|
|
return {new op::instruction_t(ops)};
|
|
else
|
|
return {};
|
|
}
|
|
|
|
std::optional<stmt_t *> parser_t::instruction()
|
|
{
|
|
// "nop"
|
|
// op
|
|
// load
|
|
// dma
|
|
// jump
|
|
// loop
|
|
// end
|
|
return {};
|
|
}
|
|
|
|
std::optional<stmt_t *> parser_t::instruction_statement()
|
|
{
|
|
// label
|
|
// instruction
|
|
// newline
|
|
return {};
|
|
}
|
|
|
|
stmt_t * parser_t::statement()
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
}
|