326 lines
11 KiB
C++
326 lines
11 KiB
C++
#include "compiler/treeifier/ast/helper.hh"
|
|
#include <map>
|
|
#include <algorithm>
|
|
|
|
enum precedence_t {
|
|
NONE,
|
|
POSTFIX,
|
|
PREFIX,
|
|
MULT,
|
|
ADD,
|
|
SHIFT,
|
|
COMP,
|
|
EQU,
|
|
BIN_AND,
|
|
BIN_XOR,
|
|
BIN_OR,
|
|
BOOL_AND,
|
|
BOOL_OR,
|
|
TERNARY,
|
|
ASSIGN,
|
|
PAREN,
|
|
CALL_START,
|
|
};
|
|
|
|
struct op_data_t {
|
|
precedence_t precedence;
|
|
size_t op_n;
|
|
std::string name;
|
|
bool assoc;
|
|
};
|
|
|
|
op_data_t sizeof_data { precedence_t::PREFIX, 1, "sizeof", true };
|
|
|
|
std::map<operator_t, op_data_t> pre_ops {
|
|
{ operator_t::INCREASE, { precedence_t::PREFIX, 1, "inc_pre" } },
|
|
{ operator_t::DECREASE, { precedence_t::PREFIX, 1, "dec_pre" } },
|
|
{ operator_t::ADD, { precedence_t::PREFIX, 1, "positive" } },
|
|
{ operator_t::SUBTRACT, { precedence_t::PREFIX, 1, "negative" } },
|
|
{ operator_t::BITWISE_NEGATIVE, { precedence_t::PREFIX, 1, "flip" } },
|
|
{ operator_t::MULTIPLY, { precedence_t::PREFIX, 1, "dereference" } },
|
|
{ operator_t::AND, { precedence_t::PREFIX, 1, "reference" } },
|
|
};
|
|
std::map<operator_t, op_data_t> bin_ops {
|
|
{ operator_t::ADD, { precedence_t::ADD, 2, "add" } },
|
|
{ operator_t::SUBTRACT, { precedence_t::ADD, 2, "subtract" } },
|
|
{ operator_t::MULTIPLY, { precedence_t::MULT, 2, "multiply" } },
|
|
{ operator_t::DIVIDE, { precedence_t::MULT, 2, "divide" } },
|
|
{ operator_t::MODULO, { precedence_t::MULT, 2, "modulo" } },
|
|
{ operator_t::INCREASE, { precedence_t::POSTFIX, 1, "inc_post" } },
|
|
{ operator_t::DECREASE, { precedence_t::POSTFIX, 1, "dec_post" } },
|
|
{ (operator_t)-1, sizeof_data },
|
|
};
|
|
|
|
map_t op_to_map(located_t<op_data_t> op) {
|
|
return {
|
|
{ "$_name", "$_operator" },
|
|
{ "ops", array_t() },
|
|
{ "location", conv::loc_to_map(op.location) },
|
|
{ "op", op.name },
|
|
};
|
|
}
|
|
|
|
bool pop(std::vector<located_t<op_data_t>> &op_stack, array_t &res) {
|
|
if (op_stack.empty()) return false;
|
|
|
|
auto map = op_to_map(op_stack.back());
|
|
auto op_n = op_stack.back().op_n;
|
|
auto loc = op_stack.back().location;
|
|
op_stack.pop_back();
|
|
|
|
if (res.size() < op_n) return false;
|
|
|
|
auto &ops = map["ops"].array();
|
|
|
|
|
|
for (size_t i = 0; i < op_n; i++) {
|
|
ops.push_back(res.back());
|
|
loc = loc.intersect(conv::map_to_loc(res.back().map()["location"].string()));
|
|
res.pop_back();
|
|
}
|
|
|
|
map["location"] = conv::loc_to_map(loc);
|
|
|
|
std::reverse(ops.begin(), ops.end());
|
|
res.push_back(map);
|
|
|
|
return true;
|
|
}
|
|
bool pop_paren(std::vector<located_t<op_data_t>> &op_stack, array_t &res) {
|
|
bool has_paren = false;
|
|
for (const auto &op : op_stack) {
|
|
if (op.precedence == precedence_t::PAREN) {
|
|
has_paren = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!has_paren) return false;
|
|
|
|
while (true) {
|
|
if (op_stack.back().precedence == precedence_t::PAREN) break;
|
|
if (!pop(op_stack, res)) return false;
|
|
}
|
|
|
|
op_stack.pop_back();
|
|
return true;
|
|
}
|
|
bool pop_call(size_t n, location_t loc, std::vector<located_t<op_data_t>> &op_stack, array_t &res) {
|
|
map_t call = {
|
|
{ "$_name", "$_call" },
|
|
};
|
|
|
|
array_t &args = call["args"].array({});
|
|
|
|
while (true) {
|
|
if (op_stack.back().precedence == precedence_t::CALL_START) break;
|
|
if (!pop(op_stack, res)) return false;
|
|
}
|
|
loc = loc.intersect(op_stack.back().location);
|
|
op_stack.pop_back();
|
|
call["location"] = conv::loc_to_map(loc);
|
|
|
|
for (size_t i = 0; i <= n; i++) {
|
|
args.push_back(res.back());
|
|
res.pop_back();
|
|
}
|
|
|
|
std::reverse(args.begin(), args.end());
|
|
|
|
call["func"] = res.back();
|
|
res.pop_back();
|
|
res.push_back(call);
|
|
|
|
return true;
|
|
}
|
|
bool pop_until(const op_data_t &data, tree_helper_t &h, std::vector<located_t<op_data_t>> &op_stack, array_t &res) {
|
|
while (!op_stack.empty()) {
|
|
auto &back_data = op_stack.back();
|
|
if (data.assoc ?
|
|
back_data.precedence >= data.precedence :
|
|
back_data.precedence > data.precedence
|
|
) break;
|
|
|
|
if (!pop(op_stack, res)) return h.err("Expected an expression on the right side of this operator.");
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool ast::parse_exp_var(ast_ctx_t &ctx, size_t &res_i, map_t &out) {
|
|
tree_helper_t h(ctx, res_i);
|
|
|
|
if (h.curr().is_identifier()) {
|
|
out["content"] = h.curr().identifier();
|
|
out["location"] = conv::loc_to_map(h.loc());
|
|
return h.submit(true);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
bool ast::parse_exp_int_lit(ast_ctx_t &ctx, size_t &res_i, map_t &out) {
|
|
tree_helper_t h(ctx, res_i);
|
|
|
|
if (h.curr().is_int_literal()) {
|
|
auto &arr = out["content"].array({});
|
|
for (auto b : h.curr().literal()) arr.push_back((float)b);
|
|
out["location"] = conv::loc_to_map(h.loc());
|
|
return h.submit(true);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
bool ast::parse_exp_str_lit(ast_ctx_t &ctx, size_t &res_i, map_t &out) {
|
|
tree_helper_t h(ctx, res_i);
|
|
|
|
if (h.curr().is_str_literal()) {
|
|
auto &arr = out["content"].array({});
|
|
for (auto b : h.curr().literal()) arr.push_back((float)b);
|
|
out["location"] = conv::loc_to_map(h.loc());
|
|
return h.submit(true);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool ast::parse_exp(ast_ctx_t &ctx, size_t &res_i, map_t &out) {
|
|
tree_helper_t h(ctx, res_i);
|
|
|
|
bool last_val = false;
|
|
map_t val;
|
|
std::vector<located_t<op_data_t>> op_stack;
|
|
std::vector<size_t> call_args_n;
|
|
auto res = array_t();
|
|
|
|
|
|
while (true) {
|
|
if (h.ended()) break;
|
|
|
|
if (!last_val && h.curr().is_identifier("sizeof")) {
|
|
op_stack.push_back({ h.loc(), sizeof_data });
|
|
h.advance("Expected a value on the right side of the operator.");
|
|
continue;
|
|
}
|
|
if (!last_val && h.push_parse(ctx.group("$_exp_val"), res)) {
|
|
last_val = true;
|
|
continue;
|
|
}
|
|
if (h.curr().is_operator()) {
|
|
auto op = h.curr()._operator();
|
|
if (last_val) {
|
|
if (op == operator_t::PAREN_OPEN) {
|
|
h.advance("Expected an argument.");
|
|
call_args_n.push_back(0);
|
|
op_stack.push_back({ h.loc(), { precedence_t::CALL_START } });
|
|
last_val = false;
|
|
}
|
|
else if (op == operator_t::COMMA) {
|
|
if (call_args_n.size() == 0) break;
|
|
h.advance("Expected an argument.");
|
|
|
|
pop_until({ .precedence = precedence_t::CALL_START, .assoc = true }, h, op_stack, res);
|
|
call_args_n.back()++;
|
|
last_val = false;
|
|
}
|
|
else if (op == operator_t::PAREN_CLOSE) {
|
|
bool is_call = false, is_paren = false;
|
|
|
|
for (auto i = op_stack.rbegin(); i != op_stack.rend(); i++) {
|
|
if (i->precedence == precedence_t::PAREN) {
|
|
is_paren = true;
|
|
break;
|
|
}
|
|
else if (i->precedence == precedence_t::CALL_START) {
|
|
is_call = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (is_call) pop_call(call_args_n.back(), h.loc(), op_stack, res);
|
|
else if (is_paren) pop_paren(op_stack, res);
|
|
else break;
|
|
|
|
if (!h.try_advance()) break;
|
|
}
|
|
else if (op == operator_t::COLON) {
|
|
h.advance("Expected a type.");
|
|
pop_until({ .precedence = precedence_t::PREFIX, .assoc = true }, h, op_stack, res);
|
|
map_t cast = {
|
|
{ "$_name", "$_cast" },
|
|
{ "exp", res.back() },
|
|
};
|
|
|
|
res.pop_back();
|
|
h.force_parse(parse_type, "Expected a type.", cast["type"].map({}));
|
|
res.push_back(cast);
|
|
}
|
|
else if (op == operator_t::DOT || op == operator_t::PTR_MEMBER) {
|
|
h.advance("Expected an identifier.");
|
|
pop_until({ .precedence = precedence_t::POSTFIX, .assoc = true }, h, op_stack, res);
|
|
|
|
map_t member_access = {
|
|
{ "exp", res.back() },
|
|
{ "is_ptr", op == operator_t::PTR_MEMBER },
|
|
};
|
|
h.force_parse(parse_identifier, "Expected an identifier.", member_access["name"].map({}));
|
|
member_access["location"] = conv::loc_to_map(
|
|
conv::map_to_loc(member_access["name"].map()["location"].string()).intersect(
|
|
conv::map_to_loc(res.back().map()["location"].string())
|
|
)
|
|
);
|
|
res.pop_back();
|
|
res.push_back(member_access);
|
|
}
|
|
else if (bin_ops.find(op) != bin_ops.end()) {
|
|
auto data = bin_ops[op];
|
|
pop_until(data, h, op_stack, res);
|
|
op_stack.push_back({ h.loc(), data });
|
|
|
|
if (data.op_n == 1) {
|
|
last_val = true;
|
|
if (!pop(op_stack, res)) return h.err("Expected an expression on the right side of this operator.");
|
|
if (h.try_advance()) break;
|
|
}
|
|
else {
|
|
last_val = false;
|
|
h.advance("Expected a value on the right side of the operator.");
|
|
}
|
|
}
|
|
else break;
|
|
}
|
|
else {
|
|
if (op == operator_t::PAREN_OPEN) {
|
|
op_stack.push_back({ h.loc(), { precedence_t::PAREN } });
|
|
h.advance("Expected a value.");
|
|
last_val = false;
|
|
}
|
|
else if (pre_ops.find(op) != pre_ops.end()) {
|
|
op_stack.push_back({ h.loc(), pre_ops[op] });
|
|
h.advance("Expected a value on the right side of the operator.");
|
|
}
|
|
else break;
|
|
}
|
|
continue;
|
|
}
|
|
else break;
|
|
}
|
|
|
|
if (res.size() == 0) return false;
|
|
|
|
while (!op_stack.empty()) {
|
|
if (op_stack.back().precedence == precedence_t::PAREN) throw message_t::error("Unclosed paren.", op_stack.back().location);
|
|
if (op_stack.back().precedence == precedence_t::CALL_START) throw message_t::error("Unclosed call.", op_stack.back().location);
|
|
if (!pop(op_stack, res)) return h.err("Expected an expression on the right side of this operator.");
|
|
}
|
|
|
|
out = res.front().map();
|
|
|
|
return h.submit(false);
|
|
}
|
|
bool ast::parse_stat_exp(ast_ctx_t &ctx, size_t &i, map_t &res) {
|
|
tree_helper_t h(ctx, i);
|
|
if (!h.parse(parse_exp, res)) return false;
|
|
if (!h.ended() && h.curr().is_operator(operator_t::SEMICOLON)) return h.submit(true);
|
|
|
|
ctx.messages.push(message_t::error("Expected a semicolon.", h.loc(1)));
|
|
return h.submit(false);
|
|
}
|