gsc-tool/src/gsc/preprocessor.cpp

1356 lines
35 KiB
C++
Raw Normal View History

// Copyright 2023 xensik. All rights reserved.
//
// Use of this source code is governed by a GNU GPLv3 license
// that can be found in the LICENSE file.
#include "stdinc.hpp"
#include "preprocessor.hpp"
#include "context.hpp"
#include "parser.hpp"
namespace xsk::gsc
{
preprocessor::preprocessor(context* ctx, std::string const& name, char const* data, usize size) : ctx_{ ctx }, curr_expr_{ 0 }, expand_{ 0 }, skip_{ false }
{
lexer_.push(lexer{ ctx, name, data, size });
defines_.reserve(4);
defines_.insert({ "__FILE__", { define::BUILTIN,/* false,*/ {}, {} }});
defines_.insert({ "__LINE__", { define::BUILTIN,/* false,*/ {}, {} }});
defines_.insert({ "__DATE__", { define::BUILTIN,/* false,*/ {}, {} }});
defines_.insert({ "__TIME__", { define::BUILTIN,/* false,*/ {}, {} }});
directives_.reserve(15);
directives_.insert({ "if", directive::IF });
directives_.insert({ "ifdef", directive::IFDEF });
directives_.insert({ "ifndef", directive::IFNDEF });
directives_.insert({ "elif", directive::ELIF });
directives_.insert({ "elifdef", directive::ELIFDEF });
directives_.insert({ "elifndef", directive::ELIFNDEF });
directives_.insert({ "else", directive::ELSE });
directives_.insert({ "endif", directive::ENDIF });
directives_.insert({ "define", directive::DEFINE });
directives_.insert({ "undef", directive::UNDEF });
directives_.insert({ "pragma", directive::PRAGMA });
directives_.insert({ "warning", directive::WARNING });
directives_.insert({ "error", directive::ERROR });
directives_.insert({ "line", directive::LINE });
directives_.insert({ "include", directive::INCLUDE });
directives_.insert({ "inline", directive::INLINE });
directives_.insert({ "using_animtree", directive::USINGTREE });
}
auto preprocessor::process() -> token
{
while (true)
{
auto tok = next_token();
if (tok.type == token::MACROBEGIN)
{
reject_.insert(tok.data);
expand_++;
continue;
}
if (tok.type == token::MACROEND)
{
reject_.erase(tok.data);
expand_--;
continue;
}
if (tok.type == token::SHARP)
{
if (!expand_ && (tok.space == spacing::null || tok.space == spacing::empty))
read_directive(tok);
else
read_hashtoken(tok);
continue;
}
if (skip_) continue;
if (tok.type == token::NAME)
{
auto const it = defines_.find(tok.data);
if (it != defines_.end() && (!expand_ || !reject_.contains(tok.data)))
{
expand(tok, it->second);
continue;
}
}
if (tok.type == token::NEWLINE)
continue;
return tok;
}
}
auto preprocessor::push_header(std::string const& file) -> void
{
try
{
auto name = fmt::format("{}.gsh", file);
for (auto& inc : includes_)
{
if (inc == name)
throw ppr_error(location{}, fmt::format("recursive header inclusion {} at {}", name, includes_.back()));
}
auto data = ctx_->load_header(name);
includes_.push_back(*std::get<0>(data));
lexer_.push(lexer{ ctx_, *std::get<0>(data), std::get<1>(data), std::get<2>(data) });
}
catch (std::exception const& e)
{
throw error(fmt::format("parsing header file '{}': {}", file, e.what()));
}
}
auto preprocessor::pop_header() -> void
{
if (lexer_.size() > 1)
{
lexer_.pop();
includes_.erase(includes_.end() - 1);
}
else
{
throw error("no headers to pop");
}
}
auto preprocessor::ban_header(location const& loc) -> void
{
if (lexer_.size() > 1)
{
throw comp_error(loc, "not allowed inside a gsh file");
}
}
auto preprocessor::skip_line() -> void
{
auto tok = read_token();
while (tok.type != token::NEWLINE)
tok = read_token();
}
auto preprocessor::next_token() -> token
{
if (!tokens_.empty())
{
auto tok = tokens_.front();
tokens_.pop_front();
return tok;
}
return read_token();
}
auto preprocessor::read_token() -> token
{
auto tok = lexer_.top().lex();
if (tok.type == token::EOS)
{
if (!indents_.empty())
{
skip_ = 0;
// clear indents
throw ppr_error(tok.pos, "missing #endif");
}
if (lexer_.size() > 1)
{
pop_header();
return read_token();
}
}
return tok;
}
auto preprocessor::read_directive(token& tok) -> void
{
auto next = read_token();
if (next.type == token::NEWLINE)
return;
expect(next, token::NAME);
auto const it = directives_.find(next.data);
if (it != directives_.end())
{
switch (it->second)
{
case directive::IF:
read_directive_if(next);
return;
case directive::IFDEF:
read_directive_ifdef(next);
return;
case directive::IFNDEF:
read_directive_ifndef(next);
return;
case directive::ELIF:
read_directive_elif(next);
return;
case directive::ELIFDEF:
read_directive_elifdef(next);
return;
case directive::ELIFNDEF:
read_directive_elifndef(next);
return;
case directive::ELSE:
read_directive_else(next);
return;
case directive::ENDIF:
read_directive_endif(next);
return;
case directive::PRAGMA:
read_directive_pragma(next);
return;
case directive::WARNING:
read_directive_warning(next);
return;
case directive::ERROR:
read_directive_error(next);
return;
case directive::LINE:
read_directive_line(next);
return;
case directive::DEFINE:
read_directive_define(next);
return;
case directive::UNDEF:
read_directive_undef(next);
return;
case directive::INCLUDE:
read_directive_include(tok, next);
return;
case directive::INLINE:
read_directive_inline(tok, next);
return;
case directive::USINGTREE:
read_directive_usingtree(tok, next);
return;
default:
break;
}
}
throw ppr_error(next.pos, fmt::format("invalid preprocessing directive '{}'", next.data));
}
auto preprocessor::read_directive_if(token&) -> void
{
auto skip = !evaluate();
indents_.push({ directive::IF, skip });
skip_ += skip ? 1 : 0;
}
auto preprocessor::read_directive_ifdef(token&) -> void
{
auto skip = false;
if (skip_)
{
skip_line();
}
else
{
auto tok = read_token();
expect(tok, token::NAME);
auto name = std::move(tok.data);
tok = read_token();
expect(tok, token::NEWLINE);
skip = !defines_.contains(name);
}
indents_.push({ directive::IFDEF, skip });
skip_ += skip ? 1 : 0;
}
auto preprocessor::read_directive_ifndef(token&) -> void
{
auto skip = false;
if (skip_)
{
skip_line();
}
else
{
auto tok = read_token();
expect(tok, token::NAME);
auto name = std::move(tok.data);
tok = read_token();
expect(tok, token::NEWLINE);
skip = defines_.contains(name);
}
indents_.push({ directive::IFNDEF, skip });
skip_ += skip ? 1 : 0;
}
auto preprocessor::read_directive_elif(token& tok) -> void
{
if (indents_.empty())
{
throw ppr_error(tok.pos, "#elif without #if");
}
auto dir = indents_.top();
indents_.pop();
skip_ -= dir.skip ? 1 : 0;
if (dir.type == directive::ELSE)
{
throw ppr_error(tok.pos, "#elif after #else");
}
auto skip = !evaluate();
indents_.push({ directive::ELIF, skip });
skip_ += skip ? 1 : 0;
}
auto preprocessor::read_directive_elifdef(token& tok) -> void
{
if (indents_.empty())
{
throw ppr_error(tok.pos, "#elifdef without #if");
}
auto dir = indents_.top();
indents_.pop();
skip_ -= dir.skip ? 1 : 0;
if (dir.type == directive::ELSE)
{
throw ppr_error(tok.pos, "#elifdef after #else");
}
auto skip = false;
if (skip_)
{
skip_line();
}
else
{
auto next = read_token();
expect(next, token::NAME);
auto name = std::move(next.data);
next = read_token();
expect(next, token::NEWLINE);
skip = !defines_.contains(name);
}
indents_.push({ directive::ELIFDEF, skip });
skip_ += skip ? 1 : 0;
}
auto preprocessor::read_directive_elifndef(token& tok) -> void
{
if (indents_.empty())
{
throw ppr_error(tok.pos, "#elifdef without #if");
}
auto dir = indents_.top();
indents_.pop();
skip_ -= dir.skip ? 1 : 0;
if (dir.type == directive::ELSE)
{
throw ppr_error(tok.pos, "#elifdef after #else");
}
auto skip = false;
if (skip_)
{
skip_line();
}
else
{
auto next = read_token();
expect(next, token::NAME);
auto name = std::move(next.data);
next = read_token();
expect(next, token::NEWLINE);
skip = defines_.contains(name);
}
indents_.push({ directive::ELIFNDEF, skip });
skip_ += skip ? 1 : 0;
}
auto preprocessor::read_directive_else(token& tok) -> void
{
auto next = read_token();
expect(next, token::NEWLINE);
if (indents_.empty())
{
throw ppr_error(tok.pos, "#else without #if");
}
auto dir = indents_.top();
indents_.pop();
skip_ -= dir.skip ? 1 : 0;
if (dir.type == directive::ELSE)
{
throw ppr_error(tok.pos, "#else after #else");
}
auto skip = !dir.skip;
indents_.push({ directive::ELSE, skip });
skip_ += skip ? 1 : 0;
}
auto preprocessor::read_directive_endif(token& tok) -> void
{
auto next = read_token();
expect(next, token::NEWLINE);
if (indents_.empty())
{
throw ppr_error(tok.pos, "#endif without #if");
}
auto dir = indents_.top();
indents_.pop();
skip_ -= dir.skip ? 1 : 0;
}
auto preprocessor::read_directive_define(token&) -> void
{
if (skip_) return skip_line();
auto next = read_token();
expect(next, token::NAME);
auto name = std::move(next.data);
if (defines_.contains(name))
{
throw ppr_error(next.pos, "macro redefinition");
}
next = read_token();
switch (next.type)
{
case token::NEWLINE:
defines_.insert({ name, define{ define::PLAIN,/* false,*/ {}, {} }});
break;
case token::LPAREN:
if (next.space == spacing::none)
{
auto params = std::vector<token>{};
auto last_comma = true;
auto last_elips = false;
while (true)
{
next = read_token();
if (next.type == token::RPAREN)
{
if (last_comma && !params.empty())
throw ppr_error(next.pos, "misplaced comma in macro param list");
break;
}
else if (next.type == token::NAME)
{
if (last_elips)
throw ppr_error(next.pos, "elipsis must be last in macro param list");
else if (!last_comma)
throw ppr_error(next.pos, "misplaced name in macro param list");
else
{
auto it = std::find_if(params.begin(), params.end(), [&next](token const& v) { return v.data == next.data; });
if (it != params.end())
{
throw ppr_error(next.pos, "duplicate macro parameter name");
}
params.push_back(next);
last_comma = false;
}
}
else if (next.type == token::ELLIPSIS)
{
// TODO: disabled
throw ppr_error(next.pos, "variadic macros not supported");
//
// if (!last_comma || last_elips)
// throw ppr_error(next.pos, "misplaced elipsis in macro param list");
// last_elips = true;
// last_comma = false;
}
else if (next.type == token::COMMA)
{
if (last_elips)
throw ppr_error(next.pos, "elipsis must be last in macro param list");
if (last_comma)
throw ppr_error(next.pos, "misplaced comma in macro param list");
else
last_comma = true;
}
else
throw ppr_error(next.pos, "unexpected token in macro param list");
}
auto exp = std::vector<token>{};
auto last_sharp = false;
next = read_token();
while (next.type != token::NEWLINE)
{
if (next.type == token::NAME)
{
auto it = std::find_if(params.begin(), params.end(), [&next](token const& v) { return v.data == next.data; });
if (it != params.end())
{
if (last_sharp)
exp.back().type = token::STRINGIZE;
next.type = token::MACROARG;
exp.push_back(std::move(next));
}
else
{
// check for #animtree ??
if (last_sharp)
throw ppr_error(next.pos, "'#' is not followed by a macro parameter");
exp.push_back(std::move(next));
}
// TODO: VAARGS, VAOPT
}
else if (next.type == token::SHARP)
{
if (!last_sharp)
{
last_sharp = true;
exp.push_back(std::move(next));
}
else if (next.space == spacing::none)
{
exp.back().type = token::PASTE;
}
else
{
throw ppr_error(next.pos, "'#' is not followed by a macro parameter");
}
}
else
{
exp.push_back(std::move(next));
}
if (exp.back().type != token::SHARP)
last_sharp = false;
next = read_token();
}
expect(next, token::NEWLINE);
if (!exp.empty())
{
if (exp.front().type == token::PASTE)
throw ppr_error(next.pos, "'##' cannot appear at start of macro expansion");
if (exp.back().type == token::PASTE)
throw ppr_error(next.pos, "'##' cannot appear at end of macro expansion");
if (exp.back().type == token::SHARP)
throw ppr_error(next.pos, "'#' is not followed by a macro parameter");
}
defines_.insert({ name, define{ define::FUNCTION, /*last_elips,*/ params, exp }});
break;
}
default:
if (next.space == spacing::back)
{
auto exp = std::vector<token>{};
exp.push_back(std::move(next));
next = read_token();
while (next.type != token::NEWLINE)
{
exp.push_back(std::move(next));
next = read_token();
}
expect(next, token::NEWLINE);
defines_.insert({ name, define{ define::OBJECT,/* false,*/ {}, exp }});
}
else
{
throw ppr_error(next.pos, "invalid macro definition");
}
break;
}
}
auto preprocessor::read_directive_undef(token& tok) -> void
{
if (skip_) return skip_line();
auto next = read_token();
expect(next, token::NAME);
auto name = std::move(next.data);
next = read_token();
expect(next, token::NEWLINE);
auto const it = defines_.find(name);
if (it != defines_.end())
{
if (it->second.type == define::BUILTIN)
throw ppr_error(tok.pos, "can't undefine builtin macro");
defines_.erase(it);
}
}
auto preprocessor::read_directive_pragma(token& tok) -> void
{
throw ppr_error(tok.pos, "#pragma directive not supported");
}
auto preprocessor::read_directive_warning(token& tok) -> void
{
throw ppr_error(tok.pos, "#warning directive not supported");
}
auto preprocessor::read_directive_error(token& tok) -> void
{
throw ppr_error(tok.pos, "#error directive not supported");
}
auto preprocessor::read_directive_line(token& tok) -> void
{
throw ppr_error(tok.pos, "#line directive not supported");
}
auto preprocessor::read_directive_include(token& hash, token& name) -> void
{
if (skip_) return;
name.pos.begin = hash.pos.begin;
tokens_.push_front(token{ token::INCLUDE, spacing::none, name.pos });
}
auto preprocessor::read_directive_inline(token& hash, token& name) -> void
{
if (skip_) return;
name.pos.begin = hash.pos.begin;
tokens_.push_front(token{ token::INLINE, spacing::none, name.pos });
}
auto preprocessor::read_directive_usingtree(token& hash, token& name) -> void
{
if (skip_) return;
name.pos.begin = hash.pos.begin;
tokens_.push_front(token{ token::USINGTREE, spacing::none, name.pos });
}
auto preprocessor::read_hashtoken(token& tok) -> void
{
if (skip_) return;
auto next = read_token();
if (next.type == token::NAME)
{
if (next.data == "animtree")
{
return read_hashtoken_animtree(tok, next);
}
}
// TODO: iw9 hash literals #d"src_game"
// if nothing match return '#'
tokens_.push_front(std::move(next));
tokens_.push_front(token{ token::HASH, tok.space, tok.pos });
}
auto preprocessor::read_hashtoken_animtree(token& hash, token& name) -> void
{
if (name.space == spacing::none)
{
name.pos.begin = hash.pos.begin;
tokens_.push_front(token{ token::ANIMTREE, spacing::none, name.pos });
}
else
{
// if '# animtree' return 2 tokens
tokens_.push_front(std::move(name));
tokens_.push_front(token{ token::HASH, hash.space, hash.pos });
}
}
auto preprocessor::expand(token& tok, define& def) -> void
{
if (def.type == define::PLAIN)
return;
if (def.type == define::BUILTIN)
{
if (tok.data == "__FILE__")
{
tokens_.push_front(token{ token::STRING, tok.space, tok.pos, *tok.pos.begin.filename });
}
else if (tok.data == "__LINE__")
{
tokens_.push_front(token{ token::STRING, tok.space, tok.pos, fmt::format("{}", tok.pos.begin.line) });
}
else if (tok.data == "__DATE__")
{
tokens_.push_front(token{ token::STRING, tok.space, tok.pos, tok.data }); // TODO!
}
else if (tok.data == "__TIME__")
{
tokens_.push_front(token{ token::STRING, tok.space, tok.pos, tok.data }); // TODO!
}
}
else if (def.type == define::OBJECT)
{
tokens_.push_front(token{ token::MACROEND, tok.space, tok.pos, tok.data });
for (auto it = def.exp.rbegin(); it != def.exp.rend(); ++it) tokens_.push_front(*it);
tokens_.push_front(token{ token::MACROBEGIN, tok.space, tok.pos, tok.data });
}
else if (def.type == define::FUNCTION)
{
auto next = next_token();
if (next.type != token::LPAREN)
{
tokens_.push_front(next);
tokens_.push_front(token{ token::MACROEND, tok.space, tok.pos, tok.data });
tokens_.push_front(tok);
tokens_.push_front(token{ token::MACROBEGIN, tok.space, tok.pos, tok.data });
return;
}
auto args = expand_params(tok, def);
auto exp = std::vector<token>{};
exp.reserve(def.exp.size());
for (auto i = 0u; i < def.exp.size(); i++)
{
if (def.exp[i].type == token::MACROARG)
{
auto const& name = def.exp[i].data;
for (auto n = 0u; n < def.args.size(); n++)
{
if (def.args[n].data == name)
{
for (auto t : args.at(n)) exp.push_back(t);
break;
}
}
}
else if (def.exp[i].type == token::MACROVAARGS)
{
// TODO:
// if (!def.vararg)
// throw ppr_error(def.exp[i].pos, "__VA_ARGS__ can only appear in the expansion of a variadic macro");
// for (auto t : args.back()) exp.push_back(t);
}
else if (def.exp[i].type == token::MACROVAOPT)
{
// TODO:
// if (!def.vararg)
// throw ppr_error(def.exp[i].pos, "__VA_OPT__ can only appear in the expansion of a variadic macro");
//
// if (!args.back().empty())
// {
// // paste opt
// }
}
else if (def.exp[i].type == token::STRINGIZE)
{
auto name = def.exp[i + 1].data;
auto str = std::string{};
for (auto n = 0u; n < def.args.size(); n++)
{
if (def.args[n].data == name)
{
for (size_t idx = 0; auto t : args.at(n))
{
if (idx != 0 && t.space == spacing::back)
str.append(" ");
str.append(t.to_string());
idx++;
}
break;
}
}
exp.push_back(token{ token::STRING, def.exp[i].space, def.exp[i].pos, str });
i++;
}
else if (def.exp[i].type == token::PASTE)
{
if (exp.back().type == token::NAME && def.exp[i+1].type == token::NAME)
{
exp.back().data.append(def.exp[i+1].data);
}
else
{
throw ppr_error(def.exp[i].pos, "paste can only be applied to identifiers");
}
i++;
}
else
{
exp.push_back(def.exp[i]);
}
}
tokens_.push_front(token{ token::MACROEND, tok.space, tok.pos, tok.data });
for (auto it = exp.rbegin(); it != exp.rend(); ++it) tokens_.push_front(*it);
tokens_.push_front(token{ token::MACROBEGIN, tok.space, tok.pos, tok.data });
}
}
auto preprocessor::expand_params(token& tok, define& def) -> std::vector<std::vector<token>>
{
auto nest_paren = 0;
auto args = std::vector<std::vector<token>>{};
args.push_back({});
while (true)
{
auto next = next_token();
if (next.type == token::EOS)
{
throw ppr_error(tok.pos, "unterminated function-like macro invocation");
}
else if (next.type == token::LPAREN)
{
nest_paren++;
args.back().push_back(next);
}
else if (next.type == token::RPAREN)
{
if (nest_paren == 0)
break;
else
{
nest_paren--;
args.back().push_back(next);
}
}
else if (next.type == token::COMMA && nest_paren == 0 /*&& !(def.vararg && args.size() > def.args.size())*/)
{
args.push_back({});
}
else
{
args.back().push_back(next);
}
}
if (def.args.empty() && args.size() == 1 && args[0].empty())
{
args.pop_back();
}
if (args.size() < def.args.size())
{
throw ppr_error(tok.pos, "too few arguments provided to function-like macro invocation");
}
if (/*!def.vararg &&*/ args.size() > def.args.size())
{
throw ppr_error(tok.pos, "too many arguments provided to function-like macro invocation");
}
// TODO: expand args
return args;
}
auto preprocessor::expect(token& tok, token::kind expected, spacing) -> void
{
if (tok.type != expected)
{
throw ppr_error(tok.pos, fmt::format("expected {} found {}", (u8)expected, (u8)tok.type));
}
}
auto preprocessor::evaluate() -> bool
{
if (skip_)
{
skip_line();
return 0;
}
// get expression tokens
auto tok = read_token();
while (tok.type != token::NEWLINE)
{
tokens_.push_back(tok);
tok = read_token();
}
expect(tok, token::NEWLINE);
tokens_.push_back(tok);
// expand expression and add tokens to expr_ list
bool last_def = false, last_paren = false;
tok = next_token();
while (tok.type != token::NEWLINE)
{
if (tok.type == token::MACROBEGIN)
{
reject_.insert(tok.data);
expand_++;
}
else if (tok.type == token::MACROEND)
{
reject_.erase(tok.data);
expand_--;
}
else if (tok.type == token::LPAREN && last_def && !last_paren)
{
last_paren = true;
expr_.push_back(std::move(tok));
}
else if (tok.type == token::NAME)
{
if (tok.data == "defined")
{
last_def = true;
tok.type = token::DEFINED;
expr_.push_back(std::move(tok));
}
else if (last_def || (last_paren && last_def))
{
expr_.push_back(std::move(tok));
}
else
{
last_def = false;
last_paren = false;
auto const it = defines_.find(tok.data);
if (it != defines_.end() && (!expand_ || !reject_.contains(tok.data)))
{
expand(tok, it->second);
}
else // macro not defined
{
expr_.push_back(token{ token::FALSE, tok.space, tok.pos });
}
}
}
else
{
last_def = false;
last_paren = false;
expr_.push_back(std::move(tok));
}
tok = next_token();
}
expr_.push_back(std::move(tok));
curr_expr_ = 0;
auto result = static_cast<bool>(eval_expr());
if (eval_peek().type != token::NEWLINE)
{
throw ppr_error(eval_peek().pos, "expected end of line in preprocessor expression");
}
expr_.clear();
return result;
}
auto preprocessor::eval_next() -> token&
{
if (!eval_atend()) curr_expr_++;
return eval_prev();
}
auto preprocessor::eval_peek() -> token&
{
return expr_.at(curr_expr_);
}
auto preprocessor::eval_prev() -> token&
{
return expr_.at(curr_expr_ - 1);
}
auto preprocessor::eval_atend() -> bool
{
return eval_peek().type == token::NEWLINE;
}
auto preprocessor::eval_check(token::kind type) -> bool
{
if (eval_atend()) return false;
return eval_peek().type == type;
}
auto preprocessor::eval_match(token::kind type) -> bool
{
if (eval_check(type))
{
eval_next();
return true;
}
return false;
}
auto preprocessor::eval_consume(token::kind type, std::string_view msg)
{
if (eval_check(type)) return eval_next();
throw ppr_error(eval_peek().pos, fmt::format("{}", msg));
}
auto preprocessor::eval_expr() -> i32
{
auto cond = eval_expr_or();
while (eval_match(token::QMARK))
{
auto lval = eval_expr();
eval_consume(token::COLON, "expected ':' to match '?' ");
auto rval = eval_expr();
cond = cond ? lval : rval;
}
return cond;
}
auto preprocessor::eval_expr_or() -> i32
{
auto lval = eval_expr_and();
while (eval_match(token::OR))
{
auto rval = eval_expr_and();
lval = lval || rval;
}
return lval;
}
auto preprocessor::eval_expr_and() -> i32
{
auto lval = eval_expr_bwor();
while (eval_match(token::AND))
{
auto rval = eval_expr_bwor();
lval = lval && rval;
}
return lval;
}
auto preprocessor::eval_expr_bwor() -> i32
{
auto lval = eval_expr_bwexor();
while (eval_match(token::BITOR))
{
auto rval = eval_expr_bwexor();
lval = lval | rval;
}
return lval;
}
auto preprocessor::eval_expr_bwexor() -> i32
{
auto lval = eval_expr_bwand();
while (eval_match(token::BITEXOR))
{
auto rval = eval_expr_bwand();
lval = lval ^ rval;
}
return lval;
}
auto preprocessor::eval_expr_bwand() -> i32
{
auto lval = eval_expr_eq();
while (eval_match(token::BITAND))
{
auto rval = eval_expr_eq();
lval = lval & rval;
}
return lval;
}
auto preprocessor::eval_expr_eq() -> i32
{
auto lval = eval_expr_lge();
while (eval_match(token::EQ) || eval_match(token::NE))
{
auto oper = eval_prev();
auto rval = eval_expr_lge();
switch (oper.type)
{
case token::EQ:
lval = lval == rval;
break;
case token::NE:
lval = lval != rval;
break;
default:
break;
}
}
return lval;
}
auto preprocessor::eval_expr_lge() -> i32
{
auto lval = eval_expr_shift();
while (eval_match(token::GT) || eval_match(token::GE) || eval_match(token::LT) || eval_match(token::LE))
{
auto oper = eval_prev();
auto rval = eval_expr_shift();
switch (oper.type)
{
case token::GT:
lval = lval > rval;
break;
case token::GE:
lval = lval >= rval;
break;
case token::LT:
lval = lval < rval;
break;
case token::LE:
lval = lval <= rval;
break;
default:
break;
}
}
return lval;
}
auto preprocessor::eval_expr_shift() -> i32
{
auto lval = eval_expr_add();
while (eval_match(token::SHL) || eval_match(token::SHR))
{
auto oper = eval_prev();
auto rval = eval_expr_add();
switch (oper.type)
{
case token::SHL:
lval = lval << rval;
break;
case token::SHR:
lval = lval >> rval;
break;
default:
break;
}
}
return lval;
}
auto preprocessor::eval_expr_add() -> i32
{
auto lval = eval_expr_factor();
while (eval_match(token::PLUS) || eval_match(token::MINUS))
{
auto oper = eval_prev();
auto rval = eval_expr_factor();
switch (oper.type)
{
case token::PLUS:
lval = lval + rval;
break;
case token::MINUS:
lval = lval - rval;
break;
default:
break;
}
}
return lval;
}
auto preprocessor::eval_expr_factor() -> i32
{
auto lval = eval_expr_unary();
while (eval_match(token::STAR) || eval_match(token::DIV) || eval_match(token::MOD))
{
auto oper = eval_prev();
auto rval = eval_expr_unary();
switch (oper.type)
{
case token::STAR:
lval = lval * rval;
break;
case token::DIV:
if (rval == 0)
throw ppr_error(oper.pos, "division by 0");
lval = lval / rval;
break;
case token::MOD:
if (rval == 0)
throw ppr_error(oper.pos, "division by 0");
lval = lval % rval;
break;
default:
break;
}
}
return lval;
}
auto preprocessor::eval_expr_unary() -> i32
{
if (eval_match(token::BANG) || eval_match(token::TILDE) || eval_match(token::PLUS) || eval_match(token::MINUS))
{
auto oper = eval_prev();
auto rval = eval_expr_unary();
switch (oper.type)
{
case token::BANG:
return !rval;
case token::TILDE:
return ~rval;
case token::PLUS:
return rval;
case token::MINUS:
return -rval;
default:
break;
}
}
return eval_expr_primary();
}
auto preprocessor::eval_expr_primary() -> i32
{
if (eval_match(token::TRUE))
return 1;
if (eval_match(token::FALSE))
return 0;
if (eval_match(token::FLT))
return static_cast<i32>(std::stof(eval_prev().data));
if (eval_match(token::INT))
return static_cast<i32>(std::stoi(eval_prev().data));
if (eval_match(token::LPAREN))
{
auto val = eval_expr();
eval_consume(token::RPAREN, "expect ')' after expression.");
return val;
}
if (eval_match(token::DEFINED))
{
if (eval_match(token::NAME) || eval_match(token::LPAREN))
{
auto val = eval_prev();
if (val.type == token::NAME)
{
return defines_.contains(val.data);
}
else if (eval_match(token::NAME))
{
val = eval_prev();
eval_consume(token::RPAREN, "expect ')' after defined( identifier.");
return defines_.contains(val.data);
}
throw ppr_error(eval_peek().pos, "expect identifier after defined(.");
}
throw ppr_error(eval_peek().pos, "expect identifier or '(' after defined.");
}
throw ppr_error(eval_peek().pos, "invalid preprocessor expression");
}
} // namespace xsk::gsc