ast: add emitter and resolver
This commit is contained in:
parent
d8b8bc7850
commit
d4e6c1c717
6
Makefile
6
Makefile
@ -6,7 +6,9 @@ CXX = $(TARGET)clang++
|
||||
|
||||
SRC = main.cpp
|
||||
SRC += lexer.cpp
|
||||
SRC += ast.cpp
|
||||
SRC += ast_printer.cpp
|
||||
SRC += ast_resolver.cpp
|
||||
SRC += ast_emitter.cpp
|
||||
SRC += parser.cpp
|
||||
SRC += stmt_string.cpp
|
||||
OBJ = $(patsubst %.cpp,%.o,$(SRC))
|
||||
@ -23,7 +25,7 @@ main: $(OBJ)
|
||||
$(CXX) $(LDFLAGS) $^ -o $@
|
||||
|
||||
clean:
|
||||
rm -f *.o *.d *.gch
|
||||
rm -f *.o *.d *.gch main
|
||||
|
||||
.SUFFIXES:
|
||||
.INTERMEDIATE:
|
||||
|
69
ast.hpp
69
ast.hpp
@ -1,72 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <ostream>
|
||||
#include <string_view>
|
||||
|
||||
#include "visitor.hpp"
|
||||
#include "expr.hpp"
|
||||
#include "num.hpp"
|
||||
#include "stmt.hpp"
|
||||
#include <unordered_map>
|
||||
|
||||
namespace dsp {
|
||||
|
||||
struct ast_printer_t : visitor_t<void>
|
||||
{
|
||||
ast_printer_t(std::ostream& os)
|
||||
: os(os) {}
|
||||
namespace ast {
|
||||
|
||||
std::ostream& os;
|
||||
|
||||
void visit(const binary_t * binary) const;
|
||||
void visit(const grouping_t * grouping) const;
|
||||
void visit(const identifier_t * identifier) const;
|
||||
void visit(const literal_t * literal) const;
|
||||
void visit(const unary_t * unary) const;
|
||||
|
||||
void visit(const op::alu_t * alu) const;
|
||||
void visit(const op::mov_ram_x_t * mov_ram_x) const;
|
||||
void visit(const op::mov_mul_p_t * mov_mul_p) const;
|
||||
void visit(const op::mov_ram_p_t * mov_ram_p) const;
|
||||
void visit(const op::mov_ram_y_t * mov_ram_y) const;
|
||||
void visit(const op::clr_a_t * clr_a) const;
|
||||
void visit(const op::mov_alu_a_t * mov_alu_a) const;
|
||||
void visit(const op::mov_ram_a_t * mov_ram_a) const;
|
||||
void visit(const op::mov_imm_d1_t * mov_imm_d1) const;
|
||||
void visit(const op::mov_ram_d1_t * mov_ram_d1) const;
|
||||
void visit(const op::control_word_t * control_word) const;
|
||||
|
||||
void visit(const load::mvi_t * mvi) const;
|
||||
void visit(const load::mvi_cond_t * mvi_cond) const;
|
||||
|
||||
void visit(const dma::src_d0_imm_t * src_d0_imm) const;
|
||||
void visit(const dma::d0_dst_imm_t * d0_dst_imm) const;
|
||||
void visit(const dma::src_d0_ram_t * src_d0_ram) const;
|
||||
void visit(const dma::d0_dst_ram_t * d0_dst_ram) const;
|
||||
|
||||
void visit(const jump::jmp_t * jmp) const;
|
||||
void visit(const jump::jmp_cond_t * jmp_cond) const;
|
||||
|
||||
void visit(const loop::btm_t * btm) const;
|
||||
void visit(const loop::lps_t * lps) const;
|
||||
|
||||
void visit(const end::end_t * end) const;
|
||||
void visit(const end::endi_t * endi) const;
|
||||
|
||||
void visit(const nop::nop_t * nop) const;
|
||||
|
||||
void visit(const assign_t * assign) const;
|
||||
void visit(const label_t * label) const;
|
||||
|
||||
void parenthesize(const std::string_view s, const expr_t * a) const;
|
||||
void parenthesize(const std::string_view s1, const std::string_view s2, const expr_t * a) const;
|
||||
void parenthesize(const std::string_view s1, const std::string_view s2, const std::string_view s3, const expr_t * a) const;
|
||||
void parenthesize(const std::string_view s1, const expr_t * a, const std::string_view s2) const;
|
||||
void parenthesize(const std::string_view s1, const expr_t * a, const std::string_view s2, const std::string_view s3) const;
|
||||
void parenthesize(const std::string_view s, const expr_t * a, const expr_t * b) const;
|
||||
void parenthesize(const std::string_view s) const;
|
||||
void parenthesize(const std::string_view s1, const std::string_view s2) const;
|
||||
void parenthesize(const std::string_view s1, const std::string_view s2, const std::string_view s3) const;
|
||||
void parenthesize(const std::string_view s1, const std::string_view s2, const std::string_view s3, const std::string_view s4) const;
|
||||
};
|
||||
using addresses_t = std::unordered_map<std::string_view, uint32_t>;
|
||||
using variables_t = std::unordered_map<std::string_view, uint32_t>;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
223
ast_emitter.cpp
Normal file
223
ast_emitter.cpp
Normal file
@ -0,0 +1,223 @@
|
||||
#include <unordered_map>
|
||||
|
||||
#include "ast_emitter.hpp"
|
||||
|
||||
namespace dsp {
|
||||
|
||||
namespace ast {
|
||||
|
||||
// expressions
|
||||
|
||||
uint32_t emitter_t::visit(const binary_t * binary) const
|
||||
{
|
||||
uint32_t l = binary->left->accept(this);
|
||||
uint32_t r = binary->right->accept(this);
|
||||
|
||||
using enum dsp::token_t::type_t;
|
||||
|
||||
switch (binary->oper.type) {
|
||||
case plus: return l + r; // add
|
||||
case minus: return l - r; // subtract
|
||||
case star: return l * r; // multiply
|
||||
case slash: return l / r; // divide
|
||||
case percent: return l % r; // modulus
|
||||
case ampersand: return l & r; // logical and
|
||||
case bar: return l | r; // logical or
|
||||
case carot: return l ^ r; // logical xor
|
||||
case left_shift: return l << r; // logical left shift
|
||||
case right_shift: return l >> r; // logical right shift
|
||||
default:
|
||||
throw std::runtime_error("invalid binary operand");
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t emitter_t::visit(const grouping_t * grouping) const
|
||||
{
|
||||
return grouping->expr->accept(this);
|
||||
}
|
||||
|
||||
uint32_t emitter_t::visit(const identifier_t * identifier) const
|
||||
{
|
||||
if (variables.contains(identifier->name.lexeme)) {
|
||||
return variables.at(identifier->name.lexeme);
|
||||
} else {
|
||||
throw std::runtime_error("undefined identifier");
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t emitter_t::visit(const literal_t * literal) const
|
||||
{
|
||||
return literal->value;
|
||||
}
|
||||
|
||||
uint32_t emitter_t::visit(const unary_t * unary) const
|
||||
{
|
||||
uint32_t r = unary->right->accept(this);
|
||||
|
||||
using enum dsp::token_t::type_t;
|
||||
|
||||
switch (unary->oper.type) {
|
||||
case tilde: return ~r;
|
||||
case plus: return +r;
|
||||
case minus: return -r;
|
||||
default:
|
||||
throw std::runtime_error("invalid binary operand");
|
||||
}
|
||||
}
|
||||
|
||||
// instructions
|
||||
|
||||
uint32_t emitter_t::visit(const op::alu_t * alu) const
|
||||
{
|
||||
return alu->code() | alu->bits();
|
||||
}
|
||||
|
||||
uint32_t emitter_t::visit(const op::mov_ram_x_t * mov_ram_x) const
|
||||
{
|
||||
return mov_ram_x->code() | mov_ram_x->bits();
|
||||
}
|
||||
|
||||
uint32_t emitter_t::visit(const op::mov_mul_p_t * mov_mul_p) const
|
||||
{
|
||||
return mov_mul_p->code() | mov_mul_p->bits();
|
||||
}
|
||||
|
||||
uint32_t emitter_t::visit(const op::mov_ram_p_t * mov_ram_p) const
|
||||
{
|
||||
return mov_ram_p->code() | mov_ram_p->bits();
|
||||
}
|
||||
|
||||
uint32_t emitter_t::visit(const op::mov_ram_y_t * mov_ram_y) const
|
||||
{
|
||||
return mov_ram_y->code() | mov_ram_y->bits();
|
||||
}
|
||||
|
||||
uint32_t emitter_t::visit(const op::clr_a_t * clr_a) const
|
||||
{
|
||||
return clr_a->code() | clr_a->bits();
|
||||
}
|
||||
|
||||
uint32_t emitter_t::visit(const op::mov_alu_a_t * mov_alu_a) const
|
||||
{
|
||||
return mov_alu_a->code() | mov_alu_a->bits();
|
||||
}
|
||||
|
||||
uint32_t emitter_t::visit(const op::mov_ram_a_t * mov_ram_a) const
|
||||
{
|
||||
return mov_ram_a->code() | mov_ram_a->bits();
|
||||
}
|
||||
|
||||
uint32_t emitter_t::visit(const op::mov_imm_d1_t * mov_imm_d1) const
|
||||
{
|
||||
num_t value = mov_imm_d1->imm.expr->accept(this);
|
||||
if (mov_imm_d1->imm.in_range(value))
|
||||
return mov_imm_d1->code() | mov_imm_d1->bits() | value;
|
||||
else
|
||||
throw std::runtime_error("out of range");
|
||||
}
|
||||
|
||||
uint32_t emitter_t::visit(const op::mov_ram_d1_t * mov_ram_d1) const
|
||||
{
|
||||
return mov_ram_d1->code() | mov_ram_d1->bits();
|
||||
}
|
||||
|
||||
uint32_t emitter_t::visit(const op::control_word_t * control_word) const
|
||||
{
|
||||
uint32_t word = 0;
|
||||
for (auto& op : control_word->ops) { word |= dynamic_cast<const stmt_t *>(op)->accept(this); }
|
||||
return word;
|
||||
}
|
||||
|
||||
uint32_t emitter_t::visit(const load::mvi_t * mvi) const
|
||||
{
|
||||
return mvi->code() | mvi->bits();
|
||||
}
|
||||
|
||||
uint32_t emitter_t::visit(const load::mvi_cond_t * mvi_cond) const
|
||||
{
|
||||
return mvi_cond->code() | mvi_cond->bits();
|
||||
}
|
||||
|
||||
uint32_t emitter_t::visit(const dma::src_d0_imm_t * src_d0_imm) const
|
||||
{
|
||||
num_t value = src_d0_imm->imm.expr->accept(this);
|
||||
if (src_d0_imm->imm.in_range(value))
|
||||
return src_d0_imm->code() | src_d0_imm->bits() | value;
|
||||
else
|
||||
throw std::runtime_error("out of range");
|
||||
}
|
||||
|
||||
uint32_t emitter_t::visit(const dma::d0_dst_imm_t * d0_dst_imm) const
|
||||
{
|
||||
num_t value = d0_dst_imm->imm.expr->accept(this);
|
||||
if (d0_dst_imm->imm.in_range(value))
|
||||
return d0_dst_imm->code() | d0_dst_imm->bits() | value;
|
||||
else
|
||||
throw std::runtime_error("out of range");
|
||||
}
|
||||
|
||||
uint32_t emitter_t::visit(const dma::src_d0_ram_t * src_d0_ram) const
|
||||
{
|
||||
return src_d0_ram->code() | src_d0_ram->bits();
|
||||
}
|
||||
|
||||
uint32_t emitter_t::visit(const dma::d0_dst_ram_t * d0_dst_ram) const
|
||||
{
|
||||
return d0_dst_ram->code() | d0_dst_ram->bits();
|
||||
}
|
||||
|
||||
uint32_t emitter_t::visit(const jump::jmp_t * jmp) const
|
||||
{
|
||||
return jmp->code() | jmp->bits();
|
||||
}
|
||||
|
||||
uint32_t emitter_t::visit(const jump::jmp_cond_t * jmp_cond) const
|
||||
{
|
||||
return jmp_cond->code() | jmp_cond->bits();
|
||||
}
|
||||
|
||||
uint32_t emitter_t::visit(const loop::btm_t * btm) const
|
||||
{
|
||||
return btm->code() | btm->bits();
|
||||
}
|
||||
|
||||
uint32_t emitter_t::visit(const loop::lps_t * lps) const
|
||||
{
|
||||
return lps->code() | lps->bits();
|
||||
}
|
||||
|
||||
uint32_t emitter_t::visit(const end::end_t * end) const
|
||||
{
|
||||
return end->code() | end->bits();
|
||||
}
|
||||
|
||||
uint32_t emitter_t::visit(const end::endi_t * endi) const
|
||||
{
|
||||
return endi->code() | endi->bits();
|
||||
}
|
||||
|
||||
uint32_t emitter_t::visit(const nop::nop_t * nop) const
|
||||
{
|
||||
return nop->code() | nop->bits();
|
||||
}
|
||||
|
||||
uint32_t emitter_t::visit(const assign_t * assign) const
|
||||
{
|
||||
if (variables.contains(assign->name.lexeme)) {
|
||||
throw std::runtime_error("assignment redefinition is not allowed");
|
||||
} else {
|
||||
num_t value = assign->value->accept(this);
|
||||
variables.insert({assign->name.lexeme, value});
|
||||
}
|
||||
return 0xffff'ffff; // lazy hack
|
||||
}
|
||||
|
||||
uint32_t emitter_t::visit(const label_t * label) const
|
||||
{
|
||||
(void)label;
|
||||
return 0xffff'ffff; // lazy hack
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
67
ast_emitter.hpp
Normal file
67
ast_emitter.hpp
Normal file
@ -0,0 +1,67 @@
|
||||
#pragma once
|
||||
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
|
||||
#include "visitor.hpp"
|
||||
#include "expr.hpp"
|
||||
#include "stmt.hpp"
|
||||
#include "ast.hpp"
|
||||
|
||||
namespace dsp {
|
||||
|
||||
namespace ast {
|
||||
|
||||
struct emitter_t : visitor_t<uint32_t>
|
||||
{
|
||||
emitter_t(variables_t& variables, const addresses_t& addresses)
|
||||
: variables(variables), addresses(addresses) {}
|
||||
|
||||
variables_t& variables;
|
||||
const addresses_t& addresses;
|
||||
|
||||
uint32_t visit(const binary_t * binary) const;
|
||||
uint32_t visit(const grouping_t * grouping) const;
|
||||
uint32_t visit(const identifier_t * identifier) const;
|
||||
uint32_t visit(const literal_t * literal) const;
|
||||
uint32_t visit(const unary_t * unary) const;
|
||||
|
||||
uint32_t visit(const op::alu_t * alu) const;
|
||||
uint32_t visit(const op::mov_ram_x_t * mov_ram_x) const;
|
||||
uint32_t visit(const op::mov_mul_p_t * mov_mul_p) const;
|
||||
uint32_t visit(const op::mov_ram_p_t * mov_ram_p) const;
|
||||
uint32_t visit(const op::mov_ram_y_t * mov_ram_y) const;
|
||||
uint32_t visit(const op::clr_a_t * clr_a) const;
|
||||
uint32_t visit(const op::mov_alu_a_t * mov_alu_a) const;
|
||||
uint32_t visit(const op::mov_ram_a_t * mov_ram_a) const;
|
||||
uint32_t visit(const op::mov_imm_d1_t * mov_imm_d1) const;
|
||||
uint32_t visit(const op::mov_ram_d1_t * mov_ram_d1) const;
|
||||
uint32_t visit(const op::control_word_t * control_word) const;
|
||||
|
||||
uint32_t visit(const load::mvi_t * mvi) const;
|
||||
uint32_t visit(const load::mvi_cond_t * mvi_cond) const;
|
||||
|
||||
uint32_t visit(const dma::src_d0_imm_t * src_d0_imm) const;
|
||||
uint32_t visit(const dma::d0_dst_imm_t * d0_dst_imm) const;
|
||||
uint32_t visit(const dma::src_d0_ram_t * src_d0_ram) const;
|
||||
uint32_t visit(const dma::d0_dst_ram_t * d0_dst_ram) const;
|
||||
|
||||
uint32_t visit(const jump::jmp_t * jmp) const;
|
||||
uint32_t visit(const jump::jmp_cond_t * jmp_cond) const;
|
||||
|
||||
uint32_t visit(const loop::btm_t * btm) const;
|
||||
uint32_t visit(const loop::lps_t * lps) const;
|
||||
|
||||
uint32_t visit(const end::end_t * end) const;
|
||||
uint32_t visit(const end::endi_t * endi) const;
|
||||
|
||||
uint32_t visit(const nop::nop_t * nop) const;
|
||||
|
||||
uint32_t visit(const assign_t * assign) const;
|
||||
uint32_t visit(const label_t * label) const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,89 +1,91 @@
|
||||
#include "ast.hpp"
|
||||
#include "ast_printer.hpp"
|
||||
#include "stmt_string.hpp"
|
||||
|
||||
namespace dsp {
|
||||
|
||||
void ast_printer_t::visit(const binary_t * binary) const
|
||||
namespace ast {
|
||||
|
||||
void printer_t::visit(const binary_t * binary) const
|
||||
{
|
||||
parenthesize((binary->oper).lexeme, binary->left, binary->right);
|
||||
}
|
||||
|
||||
void ast_printer_t::visit(const grouping_t * grouping) const
|
||||
void printer_t::visit(const grouping_t * grouping) const
|
||||
{
|
||||
parenthesize("grouping", grouping->expr);
|
||||
}
|
||||
|
||||
void ast_printer_t::visit(const identifier_t * identifier) const
|
||||
void printer_t::visit(const identifier_t * identifier) const
|
||||
{
|
||||
parenthesize("identifier", identifier->name.lexeme);
|
||||
}
|
||||
|
||||
void ast_printer_t::visit(const literal_t * literal) const
|
||||
void printer_t::visit(const literal_t * literal) const
|
||||
{
|
||||
os << std::to_string(literal->value);
|
||||
}
|
||||
|
||||
void ast_printer_t::visit(const unary_t * unary) const
|
||||
void printer_t::visit(const unary_t * unary) const
|
||||
{
|
||||
parenthesize((unary->oper).lexeme, unary->right);
|
||||
}
|
||||
|
||||
void ast_printer_t::parenthesize(const std::string_view s, const expr_t * a) const
|
||||
void printer_t::parenthesize(const std::string_view s, const expr_t * a) const
|
||||
{
|
||||
os << '(' << s << ' ';
|
||||
a->accept(this);
|
||||
os << ')';
|
||||
}
|
||||
|
||||
void ast_printer_t::parenthesize(const std::string_view s1, const std::string_view s2, const expr_t * a) const
|
||||
void printer_t::parenthesize(const std::string_view s1, const std::string_view s2, const expr_t * a) const
|
||||
{
|
||||
os << '(' << s1 << ' ' << s2 << ' ';
|
||||
a->accept(this);
|
||||
os << ')';
|
||||
}
|
||||
|
||||
void ast_printer_t::parenthesize(const std::string_view s1, const std::string_view s2, const std::string_view s3, const expr_t * a) const
|
||||
void printer_t::parenthesize(const std::string_view s1, const std::string_view s2, const std::string_view s3, const expr_t * a) const
|
||||
{
|
||||
os << '(' << s1 << ' ' << s2 << ' ' << s3 << ' ';
|
||||
a->accept(this);
|
||||
os << ')';
|
||||
}
|
||||
|
||||
void ast_printer_t::parenthesize(const std::string_view s1, const expr_t * a, const std::string_view s2) const
|
||||
void printer_t::parenthesize(const std::string_view s1, const expr_t * a, const std::string_view s2) const
|
||||
{
|
||||
os << '(' << s1 << ' ';
|
||||
a->accept(this);
|
||||
os << ' ' << s2 << ')';
|
||||
}
|
||||
|
||||
void ast_printer_t::parenthesize(const std::string_view s1, const expr_t * a, const std::string_view s2, const std::string_view s3) const
|
||||
void printer_t::parenthesize(const std::string_view s1, const expr_t * a, const std::string_view s2, const std::string_view s3) const
|
||||
{
|
||||
os << '(' << s1 << ' ';
|
||||
a->accept(this);
|
||||
os << ' ' << s2 << ' ' << s3 << ')';
|
||||
}
|
||||
|
||||
void ast_printer_t::parenthesize(const std::string_view s) const
|
||||
void printer_t::parenthesize(const std::string_view s) const
|
||||
{
|
||||
os << '(' << s << ')';
|
||||
}
|
||||
|
||||
void ast_printer_t::parenthesize(const std::string_view s1, const std::string_view s2) const
|
||||
void printer_t::parenthesize(const std::string_view s1, const std::string_view s2) const
|
||||
{
|
||||
os << '(' << s1 << ' ' << s2 << ')';
|
||||
}
|
||||
|
||||
void ast_printer_t::parenthesize(const std::string_view s1, const std::string_view s2, const std::string_view s3) const
|
||||
void printer_t::parenthesize(const std::string_view s1, const std::string_view s2, const std::string_view s3) const
|
||||
{
|
||||
os << '(' << s1 << ' ' << s2 << ' ' << s3 << ')';
|
||||
}
|
||||
|
||||
void ast_printer_t::parenthesize(const std::string_view s1, const std::string_view s2, const std::string_view s3, const std::string_view s4) const
|
||||
void printer_t::parenthesize(const std::string_view s1, const std::string_view s2, const std::string_view s3, const std::string_view s4) const
|
||||
{
|
||||
os << '(' << s1 << ' ' << s2 << ' ' << s3 << ' ' << s4 << ')';
|
||||
}
|
||||
|
||||
void ast_printer_t::parenthesize(const std::string_view s, const expr_t * a, const expr_t * b) const
|
||||
void printer_t::parenthesize(const std::string_view s, const expr_t * a, const expr_t * b) const
|
||||
{
|
||||
os << '(' << s << ' ';
|
||||
a->accept(this);
|
||||
@ -94,64 +96,64 @@ void ast_printer_t::parenthesize(const std::string_view s, const expr_t * a, con
|
||||
|
||||
// instructions
|
||||
|
||||
void ast_printer_t::visit(const op::alu_t * alu) const
|
||||
void printer_t::visit(const op::alu_t * alu) const
|
||||
{
|
||||
parenthesize(op::alu_type_string[static_cast<int>(alu->type)]);
|
||||
}
|
||||
|
||||
void ast_printer_t::visit(const op::mov_ram_x_t * mov_ram_x) const
|
||||
void printer_t::visit(const op::mov_ram_x_t * mov_ram_x) const
|
||||
{
|
||||
parenthesize("mov", op::xy_src_string[static_cast<int>(mov_ram_x->src)], "x");
|
||||
}
|
||||
|
||||
void ast_printer_t::visit(const op::mov_mul_p_t * mov_mul_p) const
|
||||
void printer_t::visit(const op::mov_mul_p_t * mov_mul_p) const
|
||||
{
|
||||
(void)mov_mul_p;
|
||||
parenthesize("mov mul p");
|
||||
}
|
||||
|
||||
void ast_printer_t::visit(const op::mov_ram_p_t * mov_ram_p) const
|
||||
void printer_t::visit(const op::mov_ram_p_t * mov_ram_p) const
|
||||
{
|
||||
parenthesize("mov", op::xy_src_string[static_cast<int>(mov_ram_p->src)], "p");
|
||||
}
|
||||
|
||||
void ast_printer_t::visit(const op::mov_ram_y_t * mov_ram_y) const
|
||||
void printer_t::visit(const op::mov_ram_y_t * mov_ram_y) const
|
||||
{
|
||||
parenthesize("mov", op::xy_src_string[static_cast<int>(mov_ram_y->src)], "y");
|
||||
}
|
||||
|
||||
void ast_printer_t::visit(const op::clr_a_t * clr_a) const
|
||||
void printer_t::visit(const op::clr_a_t * clr_a) const
|
||||
{
|
||||
(void)clr_a;
|
||||
parenthesize("clr a");
|
||||
}
|
||||
|
||||
void ast_printer_t::visit(const op::mov_alu_a_t * mov_alu_a) const
|
||||
void printer_t::visit(const op::mov_alu_a_t * mov_alu_a) const
|
||||
{
|
||||
(void)mov_alu_a;
|
||||
parenthesize("mov alu a");
|
||||
}
|
||||
|
||||
void ast_printer_t::visit(const op::mov_ram_a_t * mov_ram_a) const
|
||||
void printer_t::visit(const op::mov_ram_a_t * mov_ram_a) const
|
||||
{
|
||||
parenthesize("mov", op::xy_src_string[static_cast<int>(mov_ram_a->src)], "a");
|
||||
}
|
||||
|
||||
void ast_printer_t::visit(const op::mov_imm_d1_t * mov_imm_d1) const
|
||||
void printer_t::visit(const op::mov_imm_d1_t * mov_imm_d1) const
|
||||
{
|
||||
parenthesize("mov",
|
||||
mov_imm_d1->imm.expr,
|
||||
op::d1_dest_string[static_cast<int>(mov_imm_d1->dest)]);
|
||||
}
|
||||
|
||||
void ast_printer_t::visit(const op::mov_ram_d1_t * mov_ram_d1) const
|
||||
void printer_t::visit(const op::mov_ram_d1_t * mov_ram_d1) const
|
||||
{
|
||||
parenthesize("mov",
|
||||
op::d1_src_string[static_cast<int>(mov_ram_d1->src)],
|
||||
op::d1_dest_string[static_cast<int>(mov_ram_d1->dest)]);
|
||||
}
|
||||
|
||||
void ast_printer_t::visit(const op::control_word_t * control_word) const
|
||||
void printer_t::visit(const op::control_word_t * control_word) const
|
||||
{
|
||||
os << "(control_word ";
|
||||
for (const auto& op : control_word->ops) {
|
||||
@ -161,12 +163,12 @@ void ast_printer_t::visit(const op::control_word_t * control_word) const
|
||||
os << ')';
|
||||
}
|
||||
|
||||
void ast_printer_t::visit(const load::mvi_t * mvi) const
|
||||
void printer_t::visit(const load::mvi_t * mvi) const
|
||||
{
|
||||
parenthesize("mvi", mvi->imm.expr, load::dest_string[static_cast<int>(mvi->dest)]);
|
||||
}
|
||||
|
||||
void ast_printer_t::visit(const load::mvi_cond_t * mvi_cond) const
|
||||
void printer_t::visit(const load::mvi_cond_t * mvi_cond) const
|
||||
{
|
||||
parenthesize("mvi",
|
||||
mvi_cond->imm.expr,
|
||||
@ -180,7 +182,7 @@ static std::string dma_hold_add(bool hold, dma::add_mode_t add)
|
||||
+ dma::add_mode_string[static_cast<int>(add)];
|
||||
}
|
||||
|
||||
void ast_printer_t::visit(const dma::src_d0_imm_t * src_d0_imm) const
|
||||
void printer_t::visit(const dma::src_d0_imm_t * src_d0_imm) const
|
||||
{
|
||||
parenthesize(dma_hold_add(src_d0_imm->hold, src_d0_imm->add),
|
||||
dma::src_string[static_cast<int>(src_d0_imm->src)],
|
||||
@ -188,7 +190,7 @@ void ast_printer_t::visit(const dma::src_d0_imm_t * src_d0_imm) const
|
||||
src_d0_imm->imm.expr);
|
||||
}
|
||||
|
||||
void ast_printer_t::visit(const dma::d0_dst_imm_t * d0_dst_imm) const
|
||||
void printer_t::visit(const dma::d0_dst_imm_t * d0_dst_imm) const
|
||||
{
|
||||
parenthesize(dma_hold_add(d0_dst_imm->hold, d0_dst_imm->add),
|
||||
"d0",
|
||||
@ -196,7 +198,7 @@ void ast_printer_t::visit(const dma::d0_dst_imm_t * d0_dst_imm) const
|
||||
d0_dst_imm->imm.expr);
|
||||
}
|
||||
|
||||
void ast_printer_t::visit(const dma::src_d0_ram_t * src_d0_ram) const
|
||||
void printer_t::visit(const dma::src_d0_ram_t * src_d0_ram) const
|
||||
{
|
||||
parenthesize(dma_hold_add(src_d0_ram->hold, src_d0_ram->add),
|
||||
dma::src_string[static_cast<int>(src_d0_ram->src)],
|
||||
@ -204,7 +206,7 @@ void ast_printer_t::visit(const dma::src_d0_ram_t * src_d0_ram) const
|
||||
dma::length_ram_string[static_cast<int>(src_d0_ram->ram)]);
|
||||
}
|
||||
|
||||
void ast_printer_t::visit(const dma::d0_dst_ram_t * d0_dst_ram) const
|
||||
void printer_t::visit(const dma::d0_dst_ram_t * d0_dst_ram) const
|
||||
{
|
||||
parenthesize(dma_hold_add(d0_dst_ram->hold, d0_dst_ram->add),
|
||||
"d0",
|
||||
@ -212,56 +214,58 @@ void ast_printer_t::visit(const dma::d0_dst_ram_t * d0_dst_ram) const
|
||||
dma::length_ram_string[static_cast<int>(d0_dst_ram->ram)]);
|
||||
}
|
||||
|
||||
void ast_printer_t::visit(const jump::jmp_t * jmp) const
|
||||
void printer_t::visit(const jump::jmp_t * jmp) const
|
||||
{
|
||||
parenthesize("jmp", jmp->imm.expr);
|
||||
}
|
||||
|
||||
void ast_printer_t::visit(const jump::jmp_cond_t * jmp_cond) const
|
||||
void printer_t::visit(const jump::jmp_cond_t * jmp_cond) const
|
||||
{
|
||||
parenthesize("jmp",
|
||||
jump::cond_string[static_cast<int>(jmp_cond->cond)],
|
||||
jmp_cond->imm.expr);
|
||||
}
|
||||
|
||||
void ast_printer_t::visit(const loop::btm_t * btm) const
|
||||
void printer_t::visit(const loop::btm_t * btm) const
|
||||
{
|
||||
(void)btm;
|
||||
parenthesize("btm");
|
||||
}
|
||||
|
||||
void ast_printer_t::visit(const loop::lps_t * lps) const
|
||||
void printer_t::visit(const loop::lps_t * lps) const
|
||||
{
|
||||
(void)lps;
|
||||
parenthesize("lps");
|
||||
}
|
||||
|
||||
void ast_printer_t::visit(const end::end_t * end) const
|
||||
void printer_t::visit(const end::end_t * end) const
|
||||
{
|
||||
(void)end;
|
||||
parenthesize("end");
|
||||
}
|
||||
|
||||
void ast_printer_t::visit(const end::endi_t * endi) const
|
||||
void printer_t::visit(const end::endi_t * endi) const
|
||||
{
|
||||
(void)endi;
|
||||
parenthesize("endi");
|
||||
}
|
||||
|
||||
void ast_printer_t::visit(const nop::nop_t * nop) const
|
||||
void printer_t::visit(const nop::nop_t * nop) const
|
||||
{
|
||||
(void)nop;
|
||||
parenthesize("nop");
|
||||
}
|
||||
|
||||
void ast_printer_t::visit(const assign_t * assign) const
|
||||
void printer_t::visit(const assign_t * assign) const
|
||||
{
|
||||
parenthesize("assign", assign->name.lexeme, assign->value);
|
||||
}
|
||||
|
||||
void ast_printer_t::visit(const label_t * label) const
|
||||
void printer_t::visit(const label_t * label) const
|
||||
{
|
||||
parenthesize("label", label->name.lexeme);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
76
ast_printer.hpp
Normal file
76
ast_printer.hpp
Normal file
@ -0,0 +1,76 @@
|
||||
#pragma once
|
||||
|
||||
#include <ostream>
|
||||
#include <string_view>
|
||||
|
||||
#include "visitor.hpp"
|
||||
#include "expr.hpp"
|
||||
#include "num.hpp"
|
||||
#include "stmt.hpp"
|
||||
|
||||
namespace dsp {
|
||||
|
||||
namespace ast {
|
||||
|
||||
struct printer_t : visitor_t<void>
|
||||
{
|
||||
printer_t(std::ostream& os)
|
||||
: os(os) {}
|
||||
|
||||
std::ostream& os;
|
||||
|
||||
void visit(const binary_t * binary) const;
|
||||
void visit(const grouping_t * grouping) const;
|
||||
void visit(const identifier_t * identifier) const;
|
||||
void visit(const literal_t * literal) const;
|
||||
void visit(const unary_t * unary) const;
|
||||
|
||||
void visit(const op::alu_t * alu) const;
|
||||
void visit(const op::mov_ram_x_t * mov_ram_x) const;
|
||||
void visit(const op::mov_mul_p_t * mov_mul_p) const;
|
||||
void visit(const op::mov_ram_p_t * mov_ram_p) const;
|
||||
void visit(const op::mov_ram_y_t * mov_ram_y) const;
|
||||
void visit(const op::clr_a_t * clr_a) const;
|
||||
void visit(const op::mov_alu_a_t * mov_alu_a) const;
|
||||
void visit(const op::mov_ram_a_t * mov_ram_a) const;
|
||||
void visit(const op::mov_imm_d1_t * mov_imm_d1) const;
|
||||
void visit(const op::mov_ram_d1_t * mov_ram_d1) const;
|
||||
void visit(const op::control_word_t * control_word) const;
|
||||
|
||||
void visit(const load::mvi_t * mvi) const;
|
||||
void visit(const load::mvi_cond_t * mvi_cond) const;
|
||||
|
||||
void visit(const dma::src_d0_imm_t * src_d0_imm) const;
|
||||
void visit(const dma::d0_dst_imm_t * d0_dst_imm) const;
|
||||
void visit(const dma::src_d0_ram_t * src_d0_ram) const;
|
||||
void visit(const dma::d0_dst_ram_t * d0_dst_ram) const;
|
||||
|
||||
void visit(const jump::jmp_t * jmp) const;
|
||||
void visit(const jump::jmp_cond_t * jmp_cond) const;
|
||||
|
||||
void visit(const loop::btm_t * btm) const;
|
||||
void visit(const loop::lps_t * lps) const;
|
||||
|
||||
void visit(const end::end_t * end) const;
|
||||
void visit(const end::endi_t * endi) const;
|
||||
|
||||
void visit(const nop::nop_t * nop) const;
|
||||
|
||||
void visit(const assign_t * assign) const;
|
||||
void visit(const label_t * label) const;
|
||||
|
||||
void parenthesize(const std::string_view s, const expr_t * a) const;
|
||||
void parenthesize(const std::string_view s1, const std::string_view s2, const expr_t * a) const;
|
||||
void parenthesize(const std::string_view s1, const std::string_view s2, const std::string_view s3, const expr_t * a) const;
|
||||
void parenthesize(const std::string_view s1, const expr_t * a, const std::string_view s2) const;
|
||||
void parenthesize(const std::string_view s1, const expr_t * a, const std::string_view s2, const std::string_view s3) const;
|
||||
void parenthesize(const std::string_view s, const expr_t * a, const expr_t * b) const;
|
||||
void parenthesize(const std::string_view s) const;
|
||||
void parenthesize(const std::string_view s1, const std::string_view s2) const;
|
||||
void parenthesize(const std::string_view s1, const std::string_view s2, const std::string_view s3) const;
|
||||
void parenthesize(const std::string_view s1, const std::string_view s2, const std::string_view s3, const std::string_view s4) const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
186
ast_resolver.cpp
Normal file
186
ast_resolver.cpp
Normal file
@ -0,0 +1,186 @@
|
||||
#include "ast_resolver.hpp"
|
||||
|
||||
namespace dsp {
|
||||
|
||||
namespace ast {
|
||||
|
||||
// expressions
|
||||
|
||||
void resolver_t::visit(const binary_t * binary) const
|
||||
{
|
||||
(void)binary;
|
||||
}
|
||||
|
||||
void resolver_t::visit(const grouping_t * grouping) const
|
||||
{
|
||||
(void)grouping;
|
||||
}
|
||||
|
||||
void resolver_t::visit(const identifier_t * identifier) const
|
||||
{
|
||||
(void)identifier;
|
||||
}
|
||||
|
||||
void resolver_t::visit(const literal_t * literal) const
|
||||
{
|
||||
(void)literal;
|
||||
}
|
||||
|
||||
void resolver_t::visit(const unary_t * unary) const
|
||||
{
|
||||
(void)unary;
|
||||
}
|
||||
|
||||
// instructions
|
||||
|
||||
void resolver_t::visit(const op::alu_t * alu) const
|
||||
{
|
||||
(void)alu;
|
||||
}
|
||||
|
||||
void resolver_t::visit(const op::mov_ram_x_t * mov_ram_x) const
|
||||
{
|
||||
(void)mov_ram_x;
|
||||
}
|
||||
|
||||
void resolver_t::visit(const op::mov_mul_p_t * mov_mul_p) const
|
||||
{
|
||||
(void)mov_mul_p;
|
||||
}
|
||||
|
||||
void resolver_t::visit(const op::mov_ram_p_t * mov_ram_p) const
|
||||
{
|
||||
(void)mov_ram_p;
|
||||
}
|
||||
|
||||
void resolver_t::visit(const op::mov_ram_y_t * mov_ram_y) const
|
||||
{
|
||||
(void)mov_ram_y;
|
||||
}
|
||||
|
||||
void resolver_t::visit(const op::clr_a_t * clr_a) const
|
||||
{
|
||||
(void)clr_a;
|
||||
}
|
||||
|
||||
void resolver_t::visit(const op::mov_alu_a_t * mov_alu_a) const
|
||||
{
|
||||
(void)mov_alu_a;
|
||||
}
|
||||
|
||||
void resolver_t::visit(const op::mov_ram_a_t * mov_ram_a) const
|
||||
{
|
||||
(void)mov_ram_a;
|
||||
}
|
||||
|
||||
void resolver_t::visit(const op::mov_imm_d1_t * mov_imm_d1) const
|
||||
{
|
||||
(void)mov_imm_d1;
|
||||
}
|
||||
|
||||
void resolver_t::visit(const op::mov_ram_d1_t * mov_ram_d1) const
|
||||
{
|
||||
(void)mov_ram_d1;
|
||||
}
|
||||
|
||||
void resolver_t::visit(const op::control_word_t * control_word) const
|
||||
{
|
||||
(void)control_word;
|
||||
pc.increment();
|
||||
}
|
||||
|
||||
void resolver_t::visit(const load::mvi_t * mvi) const
|
||||
{
|
||||
(void)mvi;
|
||||
pc.increment();
|
||||
}
|
||||
|
||||
void resolver_t::visit(const load::mvi_cond_t * mvi_cond) const
|
||||
{
|
||||
(void)mvi_cond;
|
||||
pc.increment();
|
||||
}
|
||||
|
||||
void resolver_t::visit(const dma::src_d0_imm_t * src_d0_imm) const
|
||||
{
|
||||
(void)src_d0_imm;
|
||||
pc.increment();
|
||||
}
|
||||
|
||||
void resolver_t::visit(const dma::d0_dst_imm_t * d0_dst_imm) const
|
||||
{
|
||||
(void)d0_dst_imm;
|
||||
pc.increment();
|
||||
}
|
||||
|
||||
void resolver_t::visit(const dma::src_d0_ram_t * src_d0_ram) const
|
||||
{
|
||||
(void)src_d0_ram;
|
||||
pc.increment();
|
||||
}
|
||||
|
||||
void resolver_t::visit(const dma::d0_dst_ram_t * d0_dst_ram) const
|
||||
{
|
||||
(void)d0_dst_ram;
|
||||
pc.increment();
|
||||
}
|
||||
|
||||
void resolver_t::visit(const jump::jmp_t * jmp) const
|
||||
{
|
||||
(void)jmp;
|
||||
pc.increment();
|
||||
}
|
||||
|
||||
void resolver_t::visit(const jump::jmp_cond_t * jmp_cond) const
|
||||
{
|
||||
(void)jmp_cond;
|
||||
pc.increment();
|
||||
}
|
||||
|
||||
void resolver_t::visit(const loop::btm_t * btm) const
|
||||
{
|
||||
(void)btm;
|
||||
pc.increment();
|
||||
}
|
||||
|
||||
void resolver_t::visit(const loop::lps_t * lps) const
|
||||
{
|
||||
(void)lps;
|
||||
pc.increment();
|
||||
}
|
||||
|
||||
void resolver_t::visit(const end::end_t * end) const
|
||||
{
|
||||
(void)end;
|
||||
pc.increment();
|
||||
}
|
||||
|
||||
void resolver_t::visit(const end::endi_t * endi) const
|
||||
{
|
||||
(void)endi;
|
||||
pc.increment();
|
||||
}
|
||||
|
||||
void resolver_t::visit(const nop::nop_t * nop) const
|
||||
{
|
||||
(void)nop;
|
||||
pc.increment();
|
||||
}
|
||||
|
||||
void resolver_t::visit(const assign_t * assign) const
|
||||
{
|
||||
(void)assign;
|
||||
}
|
||||
|
||||
void resolver_t::visit(const label_t * label) const
|
||||
{
|
||||
if (addresses.contains(label->name.lexeme)) {
|
||||
throw std::runtime_error("label redefinition is not allowed");
|
||||
} else {
|
||||
addresses.insert({label->name.lexeme, pc.value});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
77
ast_resolver.hpp
Normal file
77
ast_resolver.hpp
Normal file
@ -0,0 +1,77 @@
|
||||
#pragma once
|
||||
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "visitor.hpp"
|
||||
#include "expr.hpp"
|
||||
#include "stmt.hpp"
|
||||
#include "ast.hpp"
|
||||
|
||||
namespace dsp {
|
||||
|
||||
namespace ast {
|
||||
|
||||
struct pc_t
|
||||
{
|
||||
pc_t()
|
||||
: value() {}
|
||||
|
||||
uint32_t value;
|
||||
void increment() { value += 1; }
|
||||
};
|
||||
|
||||
struct resolver_t : visitor_t<void>
|
||||
{
|
||||
resolver_t(pc_t& pc, addresses_t& addresses)
|
||||
: pc(pc), addresses(addresses) {}
|
||||
|
||||
pc_t& pc;
|
||||
addresses_t& addresses;
|
||||
|
||||
void visit(const binary_t * binary) const;
|
||||
void visit(const grouping_t * grouping) const;
|
||||
void visit(const identifier_t * identifier) const;
|
||||
void visit(const literal_t * literal) const;
|
||||
void visit(const unary_t * unary) const;
|
||||
|
||||
void visit(const op::alu_t * alu) const;
|
||||
void visit(const op::mov_ram_x_t * mov_ram_x) const;
|
||||
void visit(const op::mov_mul_p_t * mov_mul_p) const;
|
||||
void visit(const op::mov_ram_p_t * mov_ram_p) const;
|
||||
void visit(const op::mov_ram_y_t * mov_ram_y) const;
|
||||
void visit(const op::clr_a_t * clr_a) const;
|
||||
void visit(const op::mov_alu_a_t * mov_alu_a) const;
|
||||
void visit(const op::mov_ram_a_t * mov_ram_a) const;
|
||||
void visit(const op::mov_imm_d1_t * mov_imm_d1) const;
|
||||
void visit(const op::mov_ram_d1_t * mov_ram_d1) const;
|
||||
void visit(const op::control_word_t * control_word) const;
|
||||
|
||||
void visit(const load::mvi_t * mvi) const;
|
||||
void visit(const load::mvi_cond_t * mvi_cond) const;
|
||||
|
||||
void visit(const dma::src_d0_imm_t * src_d0_imm) const;
|
||||
void visit(const dma::d0_dst_imm_t * d0_dst_imm) const;
|
||||
void visit(const dma::src_d0_ram_t * src_d0_ram) const;
|
||||
void visit(const dma::d0_dst_ram_t * d0_dst_ram) const;
|
||||
|
||||
void visit(const jump::jmp_t * jmp) const;
|
||||
void visit(const jump::jmp_cond_t * jmp_cond) const;
|
||||
|
||||
void visit(const loop::btm_t * btm) const;
|
||||
void visit(const loop::lps_t * lps) const;
|
||||
|
||||
void visit(const end::end_t * end) const;
|
||||
void visit(const end::endi_t * endi) const;
|
||||
|
||||
void visit(const nop::nop_t * nop) const;
|
||||
|
||||
void visit(const assign_t * assign) const;
|
||||
void visit(const label_t * label) const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
4
expr.hpp
4
expr.hpp
@ -11,7 +11,7 @@ namespace dsp {
|
||||
struct expr_t
|
||||
{
|
||||
virtual void accept(visitor_t<void> const * visitor) const = 0;
|
||||
virtual std::string accept(visitor_t<std::string> const * visitor) const = 0;
|
||||
virtual uint32_t accept(visitor_t<uint32_t> const * visitor) const = 0;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
@ -20,7 +20,7 @@ struct expr_accept_t : expr_t {
|
||||
return visitor->visit(static_cast<const T*>(this));
|
||||
}
|
||||
|
||||
virtual std::string accept(visitor_t<std::string> const * visitor) const
|
||||
virtual uint32_t accept(visitor_t<uint32_t> const * visitor) const
|
||||
{
|
||||
return visitor->visit(static_cast<const T*>(this));
|
||||
}
|
||||
|
29
main.cpp
29
main.cpp
@ -2,10 +2,13 @@
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <optional>
|
||||
#include <bitset>
|
||||
|
||||
#include "lexer.hpp"
|
||||
#include "token.hpp"
|
||||
#include "ast.hpp"
|
||||
#include "ast_printer.hpp"
|
||||
#include "ast_resolver.hpp"
|
||||
#include "ast_emitter.hpp"
|
||||
#include "parser.hpp"
|
||||
|
||||
namespace dsp {
|
||||
@ -21,13 +24,25 @@ static void run(std::string source)
|
||||
std::string_view buf(source);
|
||||
lexer_t lexer(buf);
|
||||
std::vector<token_t> tokens = lexer.lex_tokens();
|
||||
parser_t parser(tokens);
|
||||
std::optional<stmt_t *> stmt_o = parser.statement();
|
||||
if (stmt_o) {
|
||||
dsp::ast_printer_t p(std::cout);
|
||||
(*stmt_o)->accept(&p);
|
||||
}
|
||||
parser_t pass1(tokens);
|
||||
parser_t pass2(tokens);
|
||||
ast::printer_t printer(std::cout);
|
||||
ast::pc_t pc;
|
||||
ast::addresses_t addresses;
|
||||
ast::resolver_t resolver(pc, addresses);
|
||||
while (auto stmt_o = pass1.statement()) {
|
||||
(*stmt_o)->accept(&printer);
|
||||
std::cout << std::endl << std::flush;
|
||||
(*stmt_o)->accept(&resolver);
|
||||
}
|
||||
ast::variables_t variables;
|
||||
ast::emitter_t emitter(variables, addresses);
|
||||
while (auto stmt_o = pass2.statement()) {
|
||||
uint32_t output = (*stmt_o)->accept(&emitter);
|
||||
if (output != 0xffff'ffff) {
|
||||
std::cout << std::bitset<32>(output) << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void run_prompt()
|
||||
|
11
stmt.hpp
11
stmt.hpp
@ -12,7 +12,7 @@ namespace dsp {
|
||||
struct stmt_t
|
||||
{
|
||||
virtual void accept(visitor_t<void> const * visitor) const = 0;
|
||||
virtual std::string accept(visitor_t<std::string> const * visitor) const = 0;
|
||||
virtual uint32_t accept(visitor_t<uint32_t> const * visitor) const = 0;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
@ -21,7 +21,7 @@ struct stmt_accept_t : stmt_t {
|
||||
return visitor->visit(static_cast<const T*>(this));
|
||||
}
|
||||
|
||||
virtual std::string accept(visitor_t<std::string> const * visitor) const
|
||||
virtual uint32_t accept(visitor_t<uint32_t> const * visitor) const
|
||||
{
|
||||
return visitor->visit(static_cast<const T*>(this));
|
||||
}
|
||||
@ -36,6 +36,13 @@ struct imm_t {
|
||||
|
||||
static constexpr bool sign = S;
|
||||
static constexpr int bits = N;
|
||||
|
||||
bool in_range(num_t value) const
|
||||
{
|
||||
constexpr num_t max = (1L << (bits - static_cast<num_t>(sign))) - 1;
|
||||
constexpr num_t min = sign ? -(max + 1) : 0;
|
||||
return value <= max && value >= min;
|
||||
}
|
||||
};
|
||||
|
||||
template <int N>
|
||||
|
Loading…
x
Reference in New Issue
Block a user