feature(preprocessor): implement preprocessor & parse command (#48)

This commit is contained in:
Xenxo Espasandín 2023-01-30 13:48:27 +01:00 committed by GitHub
parent e4e884d110
commit bc0cc6d0f5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 3685 additions and 2302 deletions

View File

@ -26,7 +26,7 @@ A utility to compile & decompile IW engine game scripts.
## Usage ## Usage
``./gsc-tool.exe <mode> <game> <path>`` ``./gsc-tool.exe <mode> <game> <path>``
**modes**: `asm`, `disasm`, `comp`, `decomp` **modes**: `asm`, `disasm`, `comp`, `decomp`, `parse`
- *note:* zonetool files (*.cgsc*, *.cgsc.stack*) use: `zasm`, `zdisasm`, `zcomp`, `zdecomp` modes - *note:* zonetool files (*.cgsc*, *.cgsc.stack*) use: `zasm`, `zdisasm`, `zcomp`, `zdecomp` modes
**games**: `iw5`, `iw6`, `iw7`, `iw8`, `iw9`, `s1`, `s2`, `s4`, `h1`, `h2`, `t6` **games**: `iw5`, `iw6`, `iw7`, `iw8`, `iw9`, `s1`, `s2`, `s4`, `h1`, `h2`, `t6`
@ -42,6 +42,7 @@ Example: ``./gsc-tool.exe comp iw5 ./data/iw5/my_fancy_script.gsc``
|`disasm` |dissasemble a `file.gscbin`|`file.gscasm`| |`disasm` |dissasemble a `file.gscbin`|`file.gscasm`|
|`comp` |compile a `file.gsc` |`file.gscbin`| |`comp` |compile a `file.gsc` |`file.gscbin`|
|`decomp` |decompile a `file.gscbin` |`file.gsc` | |`decomp` |decompile a `file.gscbin` |`file.gsc` |
|`parse` |parse a `file.gsc` |`file.gsc` |
## File Format ## File Format
If you need to extract scripts from fastfiles or game memory, use [Zonetool](https://github.com/ZoneTool/zonetool) or [Jekyll](https://github.com/EthanC/Jekyll). If you need to extract scripts from fastfiles or game memory, use [Zonetool](https://github.com/ZoneTool/zonetool) or [Jekyll](https://github.com/EthanC/Jekyll).

View File

@ -20,9 +20,10 @@
%define parse.error detailed %define parse.error detailed
%define parse.lac full %define parse.lac full
%locations %locations
%lex-param { xsk::gsc::lexer& lexer } %lex-param { xsk::gsc::context const* ctx_ }
%lex-param { xsk::gsc::preprocessor& ppr }
%parse-param { xsk::gsc::context const* ctx_ } %parse-param { xsk::gsc::context const* ctx_ }
%parse-param { xsk::gsc::lexer& lexer } %parse-param { xsk::gsc::preprocessor& ppr }
%parse-param { xsk::gsc::program::ptr& ast } %parse-param { xsk::gsc::program::ptr& ast }
%parse-param { std::uint32_t index } %parse-param { std::uint32_t index }
@ -33,30 +34,23 @@
#pragma warning(disable:4127) #pragma warning(disable:4127)
#endif #endif
#include "context.hpp" #include "context.hpp"
namespace xsk::gsc { class lexer; } namespace xsk::gsc { class preprocessor; }
} }
%code top %code top
{ {
#include "stdinc.hpp" #include "stdinc.hpp"
#include "parser.hpp" #include "parser.hpp"
#include "lexer.hpp" #include "preprocessor.hpp"
using namespace xsk::gsc; using namespace xsk::gsc;
namespace xsk::gsc namespace xsk::gsc
{ {
auto GSClex(lexer& lexer) -> parser::symbol_type; auto GSClex(context const* ctx_, preprocessor& ppr) -> parser::symbol_type;
auto parse_switch(stmt_switch& stm) -> void; auto parse_switch(stmt_switch& stm) -> void;
} }
} }
%token SH_DEFINE "#define" %token HASH "#"
%token SH_UNDEF "#undef"
%token SH_IFDEF "#ifdef"
%token SH_IFNDEF "#ifndef"
%token SH_IF "#if"
%token SH_ELIF "#elif"
%token SH_ELSE "#else"
%token SH_ENDIF "#endif"
%token DEVBEGIN "/#" %token DEVBEGIN "/#"
%token DEVEND "#/" %token DEVEND "#/"
%token INLINE "#inline" %token INLINE "#inline"
@ -108,6 +102,7 @@ namespace xsk::gsc
%token RBRACKET "]" %token RBRACKET "]"
%token COMMA "," %token COMMA ","
%token DOT "." %token DOT "."
%token ELLIPSIS "..."
%token DOUBLECOLON "::" %token DOUBLECOLON "::"
%token COLON ":" %token COLON ":"
%token SEMICOLON ";" %token SEMICOLON ";"
@ -289,7 +284,7 @@ program
; ;
inline inline
: INLINE expr_path SEMICOLON { lexer.push_header($2->value); } : INLINE expr_path SEMICOLON { ppr.push_header($2->value); }
; ;
include include
@ -306,12 +301,12 @@ declaration
decl_usingtree decl_usingtree
: USINGTREE LPAREN expr_string RPAREN SEMICOLON : USINGTREE LPAREN expr_string RPAREN SEMICOLON
{ lexer.ban_header(@$); $$ = make_decl_usingtree(@$, std::move($3)); } { ppr.ban_header(@$); $$ = make_decl_usingtree(@$, std::move($3)); }
; ;
decl_function decl_function
: expr_identifier LPAREN expr_parameters RPAREN stmt_comp : expr_identifier LPAREN expr_parameters RPAREN stmt_comp
{ lexer.ban_header(@$); $$ = make_decl_function(@$, std::move($1), std::move($3), std::move($5)); } { ppr.ban_header(@$); $$ = make_decl_function(@$, std::move($1), std::move($3), std::move($5)); }
; ;
stmt stmt
@ -352,6 +347,10 @@ stmt_list
{ $$ = std::move($1); $$->list.push_back(std::move($2)); } { $$ = std::move($1); $$->list.push_back(std::move($2)); }
| stmt | stmt
{ $$ = make_stmt_list(@$); $$->list.push_back(std::move($1)); } { $$ = make_stmt_list(@$); $$->list.push_back(std::move($1)); }
| stmt_list SEMICOLON
{ $$ = std::move($1); }
| SEMICOLON
{ $$ = make_stmt_list(@$); }
; ;
stmt_or_dev_list stmt_or_dev_list
@ -359,6 +358,10 @@ stmt_or_dev_list
{ $$ = std::move($1); $$->list.push_back(std::move($2)); } { $$ = std::move($1); $$->list.push_back(std::move($2)); }
| stmt_or_dev | stmt_or_dev
{ $$ = make_stmt_list(@$); $$->list.push_back(std::move($1)); } { $$ = make_stmt_list(@$); $$->list.push_back(std::move($1)); }
| stmt_or_dev_list SEMICOLON
{ $$ = std::move($1); }
| SEMICOLON
{ $$ = make_stmt_list(@$); }
; ;
stmt_dev stmt_dev
@ -979,4 +982,207 @@ auto parse_switch(stmt_switch& stm) -> void
stm.body->block = std::move(body); stm.body->block = std::move(body);
} }
extern std::unordered_map<token::kind, parser::token::token_kind_type> const tok_to_parser;
extern std::unordered_map<std::string_view, parser::token::token_kind_type> const keyword_map;
auto map_token(context const* ctx_, token& tok) -> parser::symbol_type
{
if (tok.type == token::NAME)
{
tok.data = ctx_->make_token(tok.data);
auto const it = keyword_map.find(tok.data);
if (it != keyword_map.end())
{
if (it->second == parser::token::WAITFRAME)
{
if (ctx_->props() & props::waitframe)
return parser::symbol_type(it->second, tok.pos);
}
else if (it->second == parser::token::ISDEFINED || it->second == parser::token::ISTRUE)
{
if (ctx_->props() & props::boolfuncs)
{
parser::symbol_type(it->second, tok.pos);
}
}
else
{
return parser::symbol_type(it->second, tok.pos);
}
}
return parser::symbol_type(parser::token::IDENTIFIER, std::move(tok.data), tok.pos);
}
else if (tok.type == token::PATH ||tok.type == token::STRING ||tok.type == token::ISTRING || tok.type == token::INT ||tok.type == token::FLT)
{
auto it = tok_to_parser.find(tok.type);
if (it != tok_to_parser.end())
{
return parser::symbol_type(it->second, std::move(tok.data), tok.pos);
}
}
else
{
auto it = tok_to_parser.find(tok.type);
if (it != tok_to_parser.end())
{
return parser::symbol_type(it->second, tok.pos);
}
}
throw error(fmt::format("unmapped token! {}", (u8)tok.type));
}
auto GSClex(context const* ctx_, preprocessor& ppr) -> parser::symbol_type
{
auto tok = ppr.process();
return map_token(ctx_, tok);
}
std::unordered_map<token::kind, parser::token::token_kind_type> const tok_to_parser
{{
{ token::NAME, parser::token::IDENTIFIER },
{ token::PATH, parser::token::PATH },
{ token::STRING, parser::token::STRING },
{ token::ISTRING, parser::token::ISTRING },
{ token::INT, parser::token::INTEGER },
{ token::FLT, parser::token::FLOAT },
{ token::PLUS, parser::token::ADD },
{ token::MINUS, parser::token::SUB },
{ token::STAR, parser::token::MUL },
{ token::DIV, parser::token::DIV },
{ token::MOD, parser::token::MOD },
{ token::BITOR, parser::token::BITWISE_OR },
{ token::BITAND, parser::token::BITWISE_AND },
{ token::BITEXOR, parser::token::BITWISE_EXOR },
{ token::ASSIGN, parser::token::ASSIGN },
{ token::PLUSEQ, parser::token::ASSIGN_ADD },
{ token::MINUSEQ, parser::token::ASSIGN_SUB },
{ token::STAREQ, parser::token::ASSIGN_MUL },
{ token::DIVEQ, parser::token::ASSIGN_DIV },
{ token::MODEQ, parser::token::ASSIGN_MOD },
{ token::BITOREQ, parser::token::ASSIGN_BW_OR },
{ token::BITANDEQ, parser::token::ASSIGN_BW_AND },
{ token::BITEXOREQ, parser::token::ASSIGN_BW_EXOR },
{ token::SHLEQ, parser::token::ASSIGN_LSHIFT },
{ token::SHREQ, parser::token::ASSIGN_RSHIFT },
{ token::TILDE, parser::token::COMPLEMENT },
{ token::BANG, parser::token::NOT },
{ token::GT, parser::token::GREATER },
{ token::LT, parser::token::LESS },
{ token::GE, parser::token::GREATER_EQUAL },
{ token::LE, parser::token::LESS_EQUAL },
{ token::NE, parser::token::INEQUALITY },
{ token::EQ, parser::token::EQUALITY },
{ token::OR, parser::token::OR },
{ token::AND, parser::token::AND },
{ token::SHL, parser::token::LSHIFT },
{ token::SHR, parser::token::RSHIFT },
{ token::INC, parser::token::INCREMENT },
{ token::DEC, parser::token::DECREMENT },
{ token::QMARK, parser::token::QMARK },
{ token::DOT, parser::token::DOT },
{ token::ELLIPSIS, parser::token::ELLIPSIS },
{ token::COMMA, parser::token::COMMA },
{ token::COLON, parser::token::COLON },
{ token::SEMICOLON, parser::token::SEMICOLON },
{ token::DOUBLECOLON, parser::token::DOUBLECOLON },
{ token::LBRACKET, parser::token::LBRACKET },
{ token::RBRACKET, parser::token::RBRACKET },
{ token::LBRACE, parser::token::LBRACE },
{ token::RBRACE, parser::token::RBRACE },
{ token::LPAREN, parser::token::LPAREN },
{ token::RPAREN, parser::token::RPAREN },
{ token::DEVBEGIN, parser::token::DEVBEGIN },
{ token::DEVEND, parser::token::DEVEND },
{ token::INLINE, parser::token::INLINE },
{ token::INCLUDE, parser::token::INCLUDE },
{ token::USINGTREE, parser::token::USINGTREE },
{ token::ANIMTREE, parser::token::ANIMTREE },
{ token::ENDON, parser::token::ENDON },
{ token::NOTIFY, parser::token::NOTIFY },
{ token::WAIT, parser::token::WAIT },
{ token::WAITTILL, parser::token::WAITTILL },
{ token::WAITTILLMATCH, parser::token::WAITTILLMATCH },
{ token::WAITTILLFRAMEEND, parser::token::WAITTILLFRAMEEND },
{ token::WAITFRAME, parser::token::WAITFRAME },
{ token::IF, parser::token::IF },
{ token::ELSE, parser::token::ELSE },
{ token::DO, parser::token::DO },
{ token::WHILE, parser::token::WHILE },
{ token::FOR, parser::token::FOR },
{ token::FOREACH, parser::token::FOREACH },
{ token::IN, parser::token::IN },
{ token::SWITCH, parser::token::SWITCH },
{ token::CASE, parser::token::CASE },
{ token::DEFAULT, parser::token::DEFAULT },
{ token::BREAK, parser::token::BREAK },
{ token::CONTINUE, parser::token::CONTINUE },
{ token::RETURN, parser::token::RETURN },
{ token::BREAKPOINT, parser::token::BREAKPOINT },
{ token::PROFBEGIN, parser::token::PROFBEGIN },
{ token::PROFEND, parser::token::PROFEND },
{ token::THREAD, parser::token::THREAD },
{ token::CHILDTHREAD, parser::token::CHILDTHREAD },
{ token::THISTHREAD, parser::token::THISTHREAD },
{ token::CALL, parser::token::CALL },
{ token::TRUE, parser::token::TRUE },
{ token::FALSE, parser::token::FALSE },
{ token::UNDEFINED, parser::token::UNDEFINED },
{ token::SIZE, parser::token::SIZE },
{ token::GAME, parser::token::GAME },
{ token::SELF, parser::token::SELF },
{ token::ANIM, parser::token::ANIM },
{ token::LEVEL, parser::token::LEVEL },
{ token::ISDEFINED, parser::token::ISDEFINED },
{ token::ISTRUE, parser::token::ISTRUE },
{ token::EOS, parser::token::GSCEOF },
{ token::HASH, parser::token::HASH }
}};
std::unordered_map<std::string_view, parser::token::token_kind_type> const keyword_map
{{
{ "endon", parser::token::ENDON },
{ "notify", parser::token::NOTIFY },
{ "wait", parser::token::WAIT },
{ "waittill", parser::token::WAITTILL },
{ "waittillmatch", parser::token::WAITTILLMATCH },
{ "waittillframeend", parser::token::WAITTILLFRAMEEND },
{ "waitframe", parser::token::WAITFRAME },
{ "if", parser::token::IF },
{ "else", parser::token::ELSE },
{ "do", parser::token::DO },
{ "while", parser::token::WHILE },
{ "for", parser::token::FOR },
{ "foreach", parser::token::FOREACH },
{ "in", parser::token::IN },
{ "switch", parser::token::SWITCH },
{ "case", parser::token::CASE },
{ "default", parser::token::DEFAULT },
{ "break", parser::token::BREAK },
{ "continue", parser::token::CONTINUE },
{ "return", parser::token::RETURN },
{ "breakpoint", parser::token::BREAKPOINT },
{ "prof_begin", parser::token::PROFBEGIN },
{ "prof_end", parser::token::PROFEND },
{ "thread", parser::token::THREAD },
{ "childthread", parser::token::CHILDTHREAD },
{ "thisthread", parser::token::THISTHREAD },
{ "call", parser::token::CALL },
{ "true", parser::token::TRUE },
{ "false", parser::token::FALSE },
{ "undefined", parser::token::UNDEFINED },
{ "size", parser::token::SIZE },
{ "game", parser::token::GAME },
{ "self", parser::token::SELF },
{ "anim", parser::token::ANIM },
{ "level", parser::token::LEVEL },
{ "isdefined", parser::token::ISDEFINED },
{ "istrue", parser::token::ISTRUE },
}};
} // namespace xsk::gsc } // namespace xsk::gsc

View File

@ -6,7 +6,6 @@
#include "stdinc.hpp" #include "stdinc.hpp"
#include "assembler.hpp" #include "assembler.hpp"
#include "context.hpp" #include "context.hpp"
#include "utils/string.hpp"
namespace xsk::gsc namespace xsk::gsc
{ {

View File

@ -5,7 +5,6 @@
#include "stdinc.hpp" #include "stdinc.hpp"
#include "context.hpp" #include "context.hpp"
#include "utils/string.hpp"
namespace xsk::gsc namespace xsk::gsc
{ {

View File

@ -47,7 +47,7 @@ auto decompiler::decompile_function(function const& func) -> void
decompile_instruction(*inst); decompile_instruction(*inst);
} }
if (stack_.size()) if (!stack_.empty())
{ {
throw decomp_error("stack isn't empty at function end"); throw decomp_error("stack isn't empty at function end");
} }
@ -59,7 +59,7 @@ auto decompiler::decompile_function(function const& func) -> void
decompile_statements(*func_->body->block); decompile_statements(*func_->body->block);
process_function(*func_); process_function(*func_);
program_->declarations.push_back(decl{ move(func_) }); program_->declarations.push_back(decl{ std::move(func_) });
} }
auto decompiler::decompile_instruction(instruction const& inst) -> void auto decompiler::decompile_instruction(instruction const& inst) -> void

View File

@ -11,195 +11,42 @@
namespace xsk::gsc namespace xsk::gsc
{ {
std::unordered_map<std::string_view, parser::token::token_kind_type> const keyword_map lexer::lexer(context const* ctx, std::string const& name, char const* data, usize size) : ctx_{ ctx }, reader_{ data, size }, loc_{ &name }, buflen_{ 0 }, spacing_{ spacing::null }, indev_{ false }
{{
{ "#define", parser::token::SH_DEFINE },
{ "#undef", parser::token::SH_UNDEF },
{ "#ifdef", parser::token::SH_IFDEF },
{ "#ifndef", parser::token::SH_IFNDEF },
{ "#if", parser::token::SH_IF },
{ "#elif", parser::token::SH_ELIF },
{ "#else", parser::token::SH_ELSE },
{ "#endif", parser::token::SH_ENDIF },
{ "#inline", parser::token::INLINE },
{ "#include", parser::token::INCLUDE },
{ "#using_animtree", parser::token::USINGTREE },
{ "#animtree", parser::token::ANIMTREE },
{ "endon", parser::token::ENDON },
{ "notify", parser::token::NOTIFY },
{ "wait", parser::token::WAIT },
{ "waittill", parser::token::WAITTILL },
{ "waittillmatch", parser::token::WAITTILLMATCH },
{ "waittillframeend", parser::token::WAITTILLFRAMEEND },
{ "waitframe", parser::token::WAITFRAME },
{ "if", parser::token::IF },
{ "else", parser::token::ELSE },
{ "do", parser::token::DO },
{ "while", parser::token::WHILE },
{ "for", parser::token::FOR },
{ "foreach", parser::token::FOREACH },
{ "in", parser::token::IN },
{ "switch", parser::token::SWITCH },
{ "case", parser::token::CASE },
{ "default", parser::token::DEFAULT },
{ "break", parser::token::BREAK },
{ "continue", parser::token::CONTINUE },
{ "return", parser::token::RETURN },
{ "breakpoint", parser::token::BREAKPOINT },
{ "prof_begin", parser::token::PROFBEGIN },
{ "prof_end", parser::token::PROFEND },
{ "thread", parser::token::THREAD },
{ "childthread", parser::token::CHILDTHREAD },
{ "thisthread", parser::token::THISTHREAD },
{ "call", parser::token::CALL },
{ "true", parser::token::TRUE },
{ "false", parser::token::FALSE },
{ "undefined", parser::token::UNDEFINED },
{ "size", parser::token::SIZE },
{ "game", parser::token::GAME },
{ "self", parser::token::SELF },
{ "anim", parser::token::ANIM },
{ "level", parser::token::LEVEL },
{ "isdefined", parser::token::ISDEFINED },
{ "istrue", parser::token::ISTRUE },
}};
auto GSClex(lexer& lexer) -> parser::symbol_type
{
return lexer.lex();
}
charbuf::charbuf() : length{ 0 }
{
data = static_cast<char*>(std::malloc(max_buf_size));
}
charbuf::~charbuf()
{
if (data) std::free(data);
}
auto charbuf::push(char c) -> bool
{
if (length >= max_buf_size)
return false;
data[length++] = c;
return true;
}
reader::reader() : buffer_pos{ 0 }, bytes_remaining{ 0 }, last_byte{ 0 }, current_byte{ 0 }, state{ reader::end }
{ {
} }
auto reader::init(char const* data, usize size) -> void auto lexer::lex() -> token
{ {
if (data && size) buflen_ = 0;
{
state = reader::ok;
buffer_pos = data;
bytes_remaining = static_cast<u32>(size);
last_byte = 0;
current_byte = *data;
}
else
{
state = reader::end;
buffer_pos = 0;
bytes_remaining = 0;
last_byte = 0;
current_byte = 0;
}
}
auto reader::advance() -> void
{
++buffer_pos;
if (bytes_remaining-- == 1)
{
state = reader::end;
bytes_remaining = 0;
last_byte = current_byte;
current_byte = 0;
}
else
{
last_byte = current_byte;
current_byte = *buffer_pos;
}
}
lexer::lexer(context const* ctx, std::string const& name, char const* data, usize size)
: ctx_{ ctx }, loc_{ location{ &name } }, locs_{ std::stack<location>{} }, readers_{ std::stack<reader>{} }, header_top_{ 0 }, indev_{ false }, clean_{ true }
{
reader_.init(data, size);
}
auto lexer::push_header(std::string const& file) -> void
{
try
{
if (header_top_++ >= 10)
throw comp_error(loc_, "maximum gsh depth exceeded '10'");
auto data = ctx_->header_file_data(file + ".gsh");
readers_.push(reader_);
locs_.push(loc_);
loc_.initialize(std::get<0>(data));
reader_.init(std::get<1>(data), std::get<2>(data));
clean_ = true;
}
catch (std::exception const& e)
{
throw error(fmt::format("parsing header file '{}': {}", file, e.what()));
}
}
auto lexer::pop_header() -> void
{
header_top_--;
loc_ = locs_.top();
locs_.pop();
reader_ = readers_.top();
readers_.pop();
}
auto lexer::ban_header(location const& loc) -> void
{
if (header_top_ > 0)
{
throw comp_error(loc, "not allowed inside a gsh file");
}
}
auto lexer::lex() -> parser::symbol_type
{
buffer_.length = 0;
state_ = state::start;
while (true) while (true)
{ {
auto const& state = reader_.state;
auto& last = reader_.last_byte; auto& last = reader_.last_byte;
auto& curr = reader_.current_byte; auto& curr = reader_.curr_byte;
auto path = false; auto path = false;
auto localize = false;
loc_.step(); loc_.step();
if (state == reader::end) if (reader_.ended())
{ {
if (indev_) if (indev_)
throw comp_error(loc_, "unmatched devblock start ('/#')"); throw comp_error(loc_, "unmatched devblock start ('/#')");
if (header_top_ > 0) if (curr == 0 && last != '\n')
pop_header(); {
curr = -1;
return token{ token::NEWLINE, spacing_, loc_ };
}
else else
return parser::make_GSCEOF(loc_); return token{ token::EOS, spacing_, loc_ };
} }
if (clean_ && last != 0 && last != ' ' && last != '\t' && last != '\n') if (last == 0 || last == '\n')
clean_ = false; spacing_ = spacing::null;
else if (last == ' ' || last == '\t')
spacing_ = (spacing_ == spacing::null) ? spacing::empty : spacing::back;
else
spacing_ = spacing::none;
advance(); advance();
@ -213,18 +60,17 @@ auto lexer::lex() -> parser::symbol_type
case '\n': case '\n':
loc_.lines(); loc_.lines();
loc_.step(); loc_.step();
clean_ = true; return token{ token::NEWLINE, spacing_, loc_ };
continue;
case '\\': case '\\':
throw comp_error(loc_, "invalid token ('\\')"); throw comp_error(loc_, "invalid token ('\\')");
case '/': case '/':
if (curr != '=' && curr != '#' && curr != '@' && curr != '*' && curr != '/') if (curr != '=' && curr != '#' && curr != '@' && curr != '*' && curr != '/')
return parser::make_DIV(loc_); return token{ token::DIV, spacing_, loc_ };
advance(); advance();
if (last == '=') if (last == '=')
return parser::make_ASSIGN_DIV(loc_); return token{ token::DIVEQ, spacing_, loc_ };
if (last == '#') if (last == '#')
{ {
@ -234,13 +80,13 @@ auto lexer::lex() -> parser::symbol_type
if (ctx_->build() == build::dev) if (ctx_->build() == build::dev)
{ {
indev_ = true; indev_ = true;
return parser::make_DEVBEGIN(loc_); return token{ token::DEVBEGIN, spacing_, loc_ };
} }
else else
{ {
while (true) while (true)
{ {
if (state == reader::end) if (reader_.ended())
throw comp_error(loc_, "unmatched devblock start ('/#')"); throw comp_error(loc_, "unmatched devblock start ('/#')");
if (curr == '\n') if (curr == '\n')
@ -262,7 +108,7 @@ auto lexer::lex() -> parser::symbol_type
{ {
while (true) while (true)
{ {
if (state == reader::end) if (reader_.ended())
throw comp_error(loc_, "unmatched script doc comment start ('/@')"); throw comp_error(loc_, "unmatched script doc comment start ('/@')");
if (curr == '\n') if (curr == '\n')
@ -283,7 +129,7 @@ auto lexer::lex() -> parser::symbol_type
{ {
while (true) while (true)
{ {
if (state == reader::end) if (reader_.ended())
throw comp_error(loc_, "unmatched multiline comment start ('/*')"); throw comp_error(loc_, "unmatched multiline comment start ('/*')");
if (curr == '\n') if (curr == '\n')
@ -304,7 +150,7 @@ auto lexer::lex() -> parser::symbol_type
{ {
while (true) while (true)
{ {
if (state == reader::end) if (reader_.ended())
break; break;
if (curr == '\n') if (curr == '\n')
@ -322,164 +168,148 @@ auto lexer::lex() -> parser::symbol_type
advance(); advance();
indev_ = false; indev_ = false;
return parser::make_DEVEND(loc_); return token{ token::DEVEND, spacing_, loc_ };
} }
buffer_.push(last); return token{ token::SHARP, spacing_, loc_ };
advance();
while (state == reader::ok)
{
if (last != ' ' || last != '\t')
break;
advance();
}
if (state == reader::end || !((last > 64 && last < 91) || (last > 96 && last < 123)))
throw comp_error(loc_, "invalid preprocessor directive ('#')");
state_ = state::preprocessor;
goto lex_name;
case '*': case '*':
if (curr != '=' && curr != '/') if (curr != '=' && curr != '/')
return parser::make_MUL(loc_); return token{ token::STAR, spacing_, loc_ };
advance(); advance();
if (last == '=') if (last == '=')
return parser::make_ASSIGN_MUL(loc_); return token{ token::STAREQ, spacing_, loc_ };
throw comp_error(loc_, "unmatched multiline comment end ('*/')"); throw comp_error(loc_, "unmatched multiline comment end ('*/')");
case '"': case '"':
state_ = state::string;
goto lex_string; goto lex_string;
case '.': case '.':
if (curr < '0' || curr > '9') if (curr < '0' || curr > '9')
return parser::make_DOT(loc_); return token{ token::DOT, spacing_, loc_ };
goto lex_number; goto lex_number;
case '(': case '(':
return parser::make_LPAREN(loc_); return token{ token::LPAREN, spacing_, loc_ };
case ')': case ')':
return parser::make_RPAREN(loc_); return token{ token::RPAREN, spacing_, loc_ };
case '{': case '{':
return parser::make_LBRACE(loc_); return token{ token::LBRACE, spacing_, loc_ };
case '}': case '}':
return parser::make_RBRACE(loc_); return token{ token::RBRACE, spacing_, loc_ };
case '[': case '[':
return parser::make_LBRACKET(loc_); return token{ token::LBRACKET, spacing_, loc_ };
case ']': case ']':
return parser::make_RBRACKET(loc_); return token{ token::RBRACKET, spacing_, loc_ };
case ',': case ',':
return parser::make_COMMA(loc_); return token{ token::COMMA, spacing_, loc_ };
case ';': case ';':
return parser::make_SEMICOLON(loc_); return token{ token::SEMICOLON, spacing_, loc_ };
case ':': case ':':
if (curr != ':') if (curr != ':')
return parser::make_COLON(loc_); return token{ token::COLON, spacing_, loc_ };
advance(); advance();
return parser::make_DOUBLECOLON(loc_); return token{ token::DOUBLECOLON, spacing_, loc_ };
case '?': case '?':
return parser::make_QMARK(loc_); return token{ token::QMARK, spacing_, loc_ };
case '=': case '=':
if (curr != '=') if (curr != '=')
return parser::make_ASSIGN(loc_); return token{ token::ASSIGN, spacing_, loc_ };
advance(); advance();
return parser::make_EQUALITY(loc_); return token{ token::EQ, spacing_, loc_ };
case '+': case '+':
if (curr != '+' && curr != '=') if (curr != '+' && curr != '=')
return parser::make_ADD(loc_); return token{ token::PLUS, spacing_, loc_ };
advance(); advance();
if (last == '+') if (last == '+')
return parser::make_INCREMENT(loc_); return token{ token::INC, spacing_, loc_ };
return parser::make_ASSIGN_ADD(loc_); return token{ token::PLUSEQ, spacing_, loc_ };
case '-': case '-':
if (curr != '-' && curr != '=') if (curr != '-' && curr != '=')
return parser::make_SUB(loc_); return token{ token::MINUS, spacing_, loc_ };
advance(); advance();
if (last == '-') if (last == '-')
return parser::make_DECREMENT(loc_); return token{ token::DEC, spacing_, loc_ };
return parser::make_ASSIGN_SUB(loc_); return token{ token::MINUSEQ, spacing_, loc_ };
case '%': case '%':
if (curr != '=') if (curr != '=')
return parser::make_MOD(loc_); return token{ token::MOD, spacing_, loc_ };
advance(); advance();
return parser::make_ASSIGN_MOD(loc_); return token{ token::MODEQ, spacing_, loc_ };
case '|': case '|':
if (curr != '|' && curr != '=') if (curr != '|' && curr != '=')
return parser::make_BITWISE_OR(loc_); return token{ token::BITOR, spacing_, loc_ };
advance(); advance();
if (last == '|') if (last == '|')
return parser::make_OR(loc_); return token{ token::OR, spacing_, loc_ };
return parser::make_ASSIGN_BW_OR(loc_); return token{ token::BITOREQ, spacing_, loc_ };
case '&': case '&':
if (curr != '&' && curr != '=' && curr != '"') if (curr != '&' && curr != '=' && curr != '"')
return parser::make_BITWISE_AND(loc_); return token{ token::BITAND, spacing_, loc_ };
advance(); advance();
if (last == '&') if (last == '&')
return parser::make_AND(loc_); return token{ token::AND, spacing_, loc_ };
if (last == '=') if (last == '=')
return parser::make_ASSIGN_BW_AND(loc_); return token{ token::BITANDEQ, spacing_, loc_ };
state_ = state::localize; localize = true;
goto lex_string; goto lex_string;
case '^': case '^':
if (curr != '=') if (curr != '=')
return parser::make_BITWISE_EXOR(loc_); return token{ token::BITEXOR, spacing_, loc_ };
advance(); advance();
return parser::make_ASSIGN_BW_EXOR(loc_); return token{ token::BITEXOREQ, spacing_, loc_ };
case '!': case '!':
if (curr != '=') if (curr != '=')
return parser::make_NOT(loc_); return token{ token::BANG, spacing_, loc_ };
advance(); advance();
return parser::make_INEQUALITY(loc_); return token{ token::NE, spacing_, loc_ };
case '~': case '~':
return parser::make_COMPLEMENT(loc_); return token{ token::TILDE, spacing_, loc_ };
case '<': case '<':
if (curr != '<' && curr != '=') if (curr != '<' && curr != '=')
return parser::make_LESS(loc_); return token{ token::LT, spacing_, loc_ };
advance(); advance();
if (last == '=') if (last == '=')
return parser::make_LESS_EQUAL(loc_); return token{ token::LE, spacing_, loc_ };
if (curr != '=') if (curr != '=')
return parser::make_LSHIFT(loc_); return token{ token::SHL, spacing_, loc_ };
advance(); advance();
return parser::make_ASSIGN_LSHIFT(loc_); return token{ token::SHLEQ, spacing_, loc_ };
case '>': case '>':
if (curr != '>' && curr != '=') if (curr != '>' && curr != '=')
return parser::make_GREATER(loc_); return token{ token::GT, spacing_, loc_ };
advance(); advance();
if (last == '=') if (last == '=')
return parser::make_GREATER_EQUAL(loc_); return token{ token::GE, spacing_, loc_ };
if (curr != '=') if (curr != '=')
return parser::make_RSHIFT(loc_); return token{ token::SHR, spacing_, loc_ };
advance(); advance();
return parser::make_ASSIGN_RSHIFT(loc_); return token{ token::SHREQ, spacing_, loc_ };
default: default:
if (last >= '0' && last <= '9') if (last >= '0' && last <= '9')
goto lex_number; goto lex_number;
@ -492,7 +322,7 @@ auto lexer::lex() -> parser::symbol_type
lex_string: lex_string:
while (true) while (true)
{ {
if (state == reader::end) if (reader_.ended())
throw comp_error(loc_, "unmatched string start ('\"')"); throw comp_error(loc_, "unmatched string start ('\"')");
if (curr == '"') if (curr == '"')
@ -508,7 +338,7 @@ lex_string:
{ {
advance(); advance();
if (state == reader::end) if (reader_.ended())
throw comp_error(loc_, "invalid token ('\')"); throw comp_error(loc_, "invalid token ('\')");
char c = curr; char c = curr;
@ -522,26 +352,25 @@ lex_string:
default: break; default: break;
} }
if (!buffer_.push(c)) push(c);
throw comp_error(loc_, "max string size exceeded");
} }
else if (!buffer_.push(curr)) else
throw comp_error(loc_, "max string size exceeded"); push(curr);
advance(); advance();
} }
if (state_ == state::localize) if (localize)
return parser::make_ISTRING(std::string{ buffer_.data, buffer_.length }, loc_); return token{ token::ISTRING, spacing_, loc_, std::string{ &buffer_[0], buflen_ } };
return parser::make_STRING(std::string{ buffer_.data, buffer_.length }, loc_); return token{ token::STRING, spacing_, loc_, std::string{ &buffer_[0], buflen_ } };
lex_name: lex_name:
buffer_.push(last); push(last);
while (true) while (true)
{ {
if (state == reader::end) if (reader_.ended())
break; break;
if (!(curr == '\\' || curr == '_' || (curr > 64 && curr < 91) || (curr > 96 && curr < 123) || (curr > 47 && curr < 58))) if (!(curr == '\\' || curr == '_' || (curr > 64 && curr < 91) || (curr > 96 && curr < 123) || (curr > 47 && curr < 58)))
@ -553,95 +382,35 @@ lex_name:
throw comp_error(loc_, "invalid path '\\\\'"); throw comp_error(loc_, "invalid path '\\\\'");
path = true; path = true;
if (!buffer_.push('/')) push('/');
throw comp_error(loc_, "max string size exceeded");
} }
else if (!buffer_.push(curr)) else
throw comp_error(loc_, "max string size exceeded"); push(curr);
advance(); advance();
} }
if (state_ == state::preprocessor)
{
auto token = parser::token::GSCUNDEF;
if (buffer_.length < 16)
{
auto const itr = keyword_map.find(std::string_view(buffer_.data, buffer_.length));
if (itr != keyword_map.end())
{
if (itr->second > parser::token::SH_ENDIF)
return parser::symbol_type(itr->second, loc_);
token = itr->second;
}
}
preprocessor_run(token);
state_ = state::start;
continue;
}
else
{
if (buffer_.data[0] != '_')
{
for (auto i = 0u; i < buffer_.length; i++)
{
auto c = buffer_.data[i];
if (c > 64 && c < 91)
buffer_.data[i] = c + 32;
}
}
if (buffer_.length < 17)
{
auto const itr = keyword_map.find(std::string_view(buffer_.data, buffer_.length));
if (itr != keyword_map.end())
{
if (itr->second == parser::token::WAITFRAME)
{
if (ctx_->props() & props::waitframe)
return parser::symbol_type(itr->second, loc_);
}
else if (itr->second == parser::token::ISDEFINED || itr->second == parser::token::ISTRUE)
{
if (ctx_->props() & props::boolfuncs)
return parser::symbol_type(itr->second, loc_);
}
else
{
return parser::symbol_type(itr->second, loc_);
}
}
}
if (path) if (path)
{ {
if (buffer_.data[buffer_.length - 1] == '/') if (buffer_[buflen_ - 1] == '/')
throw comp_error(loc_, "invalid path end '\\'"); throw comp_error(loc_, "invalid path end '\\'");
return parser::make_PATH(ctx_->make_token(std::string_view(buffer_.data, buffer_.length)), loc_); return token{ token::PATH, spacing_, loc_, ctx_->make_token(std::string_view{ &buffer_[0], buflen_ }) };
} }
return parser::make_IDENTIFIER(ctx_->make_token(std::string_view(buffer_.data, buffer_.length)), loc_); return token{ token::NAME, spacing_, loc_, std::string{ &buffer_[0], buflen_ } };
}
lex_number: lex_number:
if (last == '.' || last != '0' || (last == '0' && (curr != 'o' && curr != 'b' && curr != 'x'))) if (last == '.' || last != '0' || (last == '0' && (curr != 'o' && curr != 'b' && curr != 'x')))
{ {
buffer_.push(last); push(last);
auto dot = last == '.' ? 1 : 0; auto dot = last == '.' ? 1 : 0;
auto flt = 0; auto flt = 0;
while (true) while (true)
{ {
if (state == reader::end) if (reader_.ended())
break; break;
if (curr == '\'' && (last == '\'' || last == 'f' || last == '.')) if (curr == '\'' && (last == '\'' || last == 'f' || last == '.'))
@ -663,22 +432,20 @@ lex_number:
else if (!(curr > 47 && curr < 58)) else if (!(curr > 47 && curr < 58))
break; break;
if (!buffer_.push(curr)) push(curr);
throw comp_error(loc_, "number literal size exceeded");
advance(); advance();
} }
if (last == '\'') if (last == '\'')
throw comp_error(loc_, "invalid number literal"); throw comp_error(loc_, "invalid number literal");
if (dot > 1 || flt > 1 || (flt && buffer_.data[buffer_.length - 1] != 'f')) if (dot > 1 || flt > 1 || (flt && buffer_[buflen_ - 1] != 'f'))
throw comp_error(loc_, "invalid number literal"); throw comp_error(loc_, "invalid number literal");
if (dot || flt) if (dot || flt)
return parser::make_FLOAT(std::string{ buffer_.data, buffer_.length }, loc_); return token{ token::FLT, spacing_, loc_, std::string{ &buffer_[0], buflen_ } };
return parser::make_INTEGER(std::string{ buffer_.data, buffer_.length }, loc_); return token{ token::INT, spacing_, loc_, std::string{ &buffer_[0], buflen_ } };
} }
else if (curr == 'o') else if (curr == 'o')
{ {
@ -686,7 +453,7 @@ lex_number:
while (true) while (true)
{ {
if (state == reader::end) if (reader_.ended())
break; break;
if ((curr == '\'' && (last == '\'' || last == 'o')) || (curr == 'o' && last == '\'')) if ((curr == '\'' && (last == '\'' || last == 'o')) || (curr == 'o' && last == '\''))
@ -701,26 +468,26 @@ lex_number:
if (!(curr > 47 && curr < 56)) if (!(curr > 47 && curr < 56))
break; break;
if (!buffer_.push(curr)) push(curr);
throw error("gsc lexer: out of memory");
advance(); advance();
} }
if (last == '\'' || buffer_.length <= 0) if (last == '\'' || buflen_ <= 0)
throw comp_error(loc_, "invalid octal literal"); throw comp_error(loc_, "invalid octal literal");
return parser::make_INTEGER(utils::string::oct_to_dec(buffer_.data), loc_); push('\0');
return token{ token::INT, spacing_, loc_, utils::string::oct_to_dec(&buffer_[0]) };
} }
else if (curr == 'b') else if (curr == 'b')
{ {
buffer_.push(last); push(last);
buffer_.push(curr); push(curr);
advance(); advance();
while (true) while (true)
{ {
if (state == reader::end) if (reader_.ended())
break; break;
if ((curr == '\'' && (last == '\'' || last == 'b')) || (curr == 'b' && last == '\'')) if ((curr == '\'' && (last == '\'' || last == 'b')) || (curr == 'b' && last == '\''))
@ -735,26 +502,26 @@ lex_number:
if (curr != '0' && curr != '1') if (curr != '0' && curr != '1')
break; break;
if (!buffer_.push(curr)) push(curr);
throw comp_error(loc_, "number literal size exceeded");
advance(); advance();
} }
if (last == '\'' || buffer_.length < 3) if (last == '\'' || buflen_ < 3)
throw comp_error(loc_, "invalid binary literal"); throw comp_error(loc_, "invalid binary literal");
return parser::make_INTEGER(utils::string::bin_to_dec(buffer_.data), loc_); push('\0');
return token{ token::INT, spacing_, loc_, utils::string::bin_to_dec(&buffer_[0]) };
} }
else if (curr == 'x') else if (curr == 'x')
{ {
buffer_.push(last); push(last);
buffer_.push(curr); push(curr);
advance(); advance();
while (true) while (true)
{ {
if (state == reader::end) if (reader_.ended())
break; break;
if ((curr == '\'' && (last == '\'' || last == 'x')) || (curr == 'x' && last == '\'')) if ((curr == '\'' && (last == '\'' || last == 'x')) || (curr == 'x' && last == '\''))
@ -769,36 +536,44 @@ lex_number:
if (!((curr > 47 && curr < 58) || (curr > 64 && curr < 71) || (curr > 96 && curr < 103))) if (!((curr > 47 && curr < 58) || (curr > 64 && curr < 71) || (curr > 96 && curr < 103)))
break; break;
if (!buffer_.push(curr)) push(curr);
throw error("gsc lexer: out of memory");
advance(); advance();
} }
if (last == '\'' || buffer_.length < 3) if (last == '\'' || buflen_ < 3)
throw comp_error(loc_, "invalid hexadecimal literal"); throw comp_error(loc_, "invalid hexadecimal literal");
return parser::make_INTEGER(utils::string::hex_to_dec(buffer_.data), loc_); push('\0');
return token{ token::INT, spacing_, loc_, utils::string::hex_to_dec(&buffer_[0]) };
} }
throw error("UNEXPECTED LEXER INTERNAL ERROR"); throw error("UNEXPECTED LEXER INTERNAL ERROR");
} }
} }
auto lexer::push(char c) -> void
{
if (buflen_ >= 0x1000)
throw error("lexer: max literal size exceeded");
buffer_[buflen_++] = c;
}
auto lexer::advance() -> void auto lexer::advance() -> void
{ {
reader_.advance(); reader_.advance();
loc_.end.column++; loc_.end.column++;
if (reader_.current_byte == '\\') [[unlikely]] if (reader_.curr_byte == '\\') [[unlikely]]
preprocessor_wrap(); linewrap();
} }
auto lexer::preprocessor_wrap() -> void auto lexer::linewrap() -> void
{ {
while (reader_.current_byte == '\\') while (reader_.curr_byte == '\\')
{ {
if (reader_.bytes_remaining == 1) if (reader_.available == 1)
throw comp_error(loc_, "invalid token ('\\')"); throw comp_error(loc_, "invalid token ('\\')");
if (reader_.buffer_pos[1] != '\r' && reader_.buffer_pos[1] != '\n') if (reader_.buffer_pos[1] != '\r' && reader_.buffer_pos[1] != '\n')
@ -806,71 +581,27 @@ auto lexer::preprocessor_wrap() -> void
if (reader_.buffer_pos[1] == '\r') if (reader_.buffer_pos[1] == '\r')
{ {
if (reader_.bytes_remaining <= 3 || reader_.buffer_pos[2] != '\n') if (reader_.available <= 3 || reader_.buffer_pos[2] != '\n')
throw comp_error(loc_, "invalid token ('\\')"); throw comp_error(loc_, "invalid token ('\\')");
reader_.buffer_pos += 3; reader_.buffer_pos += 3;
reader_.bytes_remaining -= 3; reader_.available -= 3;
} }
if ((reader_.buffer_pos[1] == '\n')) if ((reader_.buffer_pos[1] == '\n'))
{ {
if (reader_.bytes_remaining == 2) if (reader_.available == 2)
throw comp_error(loc_, "invalid token ('\\')"); throw comp_error(loc_, "invalid token ('\\')");
reader_.buffer_pos += 2; reader_.buffer_pos += 2;
reader_.bytes_remaining -= 2; reader_.available -= 2;
} }
if (reader_.bytes_remaining == 0) reader_.curr_byte = reader_.available ? *reader_.buffer_pos : 0;
{
reader_.state = reader::end;
reader_.current_byte = 0;
}
else
{
reader_.current_byte = *reader_.buffer_pos;
}
loc_.lines(); loc_.lines();
loc_.step(); loc_.step();
} }
} }
auto lexer::preprocessor_run(parser::token::token_kind_type token) -> void
{
if (!clean_)
throw comp_error(loc_, "invalid token ('#')");
switch (token)
{
case parser::token::SH_DEFINE:
throw comp_error(loc_, "unimplemented preprocessor directive");
break;
case parser::token::SH_UNDEF:
throw comp_error(loc_, "unimplemented preprocessor directive");
break;
case parser::token::SH_IFDEF:
throw comp_error(loc_, "unimplemented preprocessor directive");
break;
case parser::token::SH_IFNDEF:
throw comp_error(loc_, "unimplemented preprocessor directive");
break;
case parser::token::SH_IF:
throw comp_error(loc_, "unimplemented preprocessor directive");
break;
case parser::token::SH_ELIF:
throw comp_error(loc_, "unimplemented preprocessor directive");
break;
case parser::token::SH_ELSE:
throw comp_error(loc_, "unimplemented preprocessor directive");
break;
case parser::token::SH_ENDIF:
throw comp_error(loc_, "unimplemented preprocessor directive");
break;
default:
throw comp_error(loc_, "unknown preprocessor directive");
}
}
} // namespace xsk::gsc } // namespace xsk::gsc

View File

@ -6,76 +6,28 @@
#pragma once #pragma once
#include "misc/types.hpp" #include "misc/types.hpp"
#include "parser.hpp"
namespace xsk::gsc namespace xsk::gsc
{ {
constexpr usize max_buf_size = 0x2000;
struct charbuf
{
char* data;
usize length;
charbuf();
~charbuf();
auto push(char c) -> bool;
};
struct reader
{
enum state_type : u8 { end, ok };
char const* buffer_pos;
u32 bytes_remaining;
char last_byte;
char current_byte;
state_type state;
reader();
reader(reader const& obj)
{
std::memcpy(this, &obj, sizeof(reader));
}
reader& operator=(reader const& obj)
{
std::memcpy(this, &obj, sizeof(reader));
return *this;
}
auto init(char const* data, usize size) -> void;
auto advance() -> void;
};
class lexer class lexer
{ {
enum class state : u8 { start, string, localize, preprocessor };
context const* ctx_; context const* ctx_;
reader reader_; lookahead reader_;
charbuf buffer_;
location loc_; location loc_;
std::stack<location> locs_; usize buflen_;
std::stack<reader> readers_; spacing spacing_;
u32 header_top_;
state state_;
bool indev_; bool indev_;
bool clean_; std::array<char, 0x1000> buffer_;
public: public:
lexer(context const* ctx, std::string const& name, char const* data, usize size); lexer(context const* ctx, std::string const& name, char const* data, usize size);
auto lex() -> parser::symbol_type; auto lex() -> token;
auto push_header(std::string const& file) -> void;
auto pop_header() -> void;
auto ban_header(location const& loc) -> void;
private: private:
auto push(char c) -> void;
auto advance() -> void; auto advance() -> void;
auto preprocessor_wrap() -> void; auto linewrap() -> void;
auto preprocessor_run(parser::token::token_kind_type token) -> void;
}; };
} // namespace xsk::gsc } // namespace xsk::gsc

20
src/gsc/misc/define.hpp Normal file
View File

@ -0,0 +1,20 @@
// 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.
#pragma once
namespace xsk::gsc
{
struct define
{
enum kind { PLAIN, BUILTIN, OBJECT, FUNCTION };
kind type;
std::vector<token> args;
std::vector<token> exp;
};
} // namespace xsk::gsc

View File

@ -0,0 +1,19 @@
// 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.
#pragma once
namespace xsk::gsc
{
struct directive
{
enum kind : u8 { IF, IFDEF, IFNDEF, ELIF, ELIFDEF, ELIFNDEF, ELSE, ENDIF, DEFINE, UNDEF, PRAGMA, WARNING, ERROR, LINE, INCLUDE, INLINE, INSERT, USINGTREE };
kind type;
bool skip;
};
} // namespace xsk::gsc

View File

@ -22,6 +22,10 @@ disasm_error::disasm_error(std::string const& what) : std::runtime_error(fmt::fo
{ {
} }
ppr_error::ppr_error(location const& loc, std::string const& what) : std::runtime_error(fmt::format("[ERROR]:preprocessor:{}: {}", loc.print(), what))
{
}
comp_error::comp_error(location const& loc, std::string const& what) : std::runtime_error(fmt::format("[ERROR]:compiler:{}: {}", loc.print(), what)) comp_error::comp_error(location const& loc, std::string const& what) : std::runtime_error(fmt::format("[ERROR]:compiler:{}: {}", loc.print(), what))
{ {
} }

View File

@ -26,6 +26,12 @@ public:
disasm_error(std::string const& what); disasm_error(std::string const& what);
}; };
class ppr_error : public std::runtime_error
{
public:
ppr_error(location const& loc, std::string const& what);
};
class comp_error : public std::runtime_error class comp_error : public std::runtime_error
{ {
public: public:

View File

@ -0,0 +1,40 @@
// 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 "lookahead.hpp"
namespace xsk::gsc
{
lookahead::lookahead(char const* data, usize size) : buffer_pos{ 0 }, available{ 0 }, last_byte{ 0 }, curr_byte{ 0 }
{
if (data && size)
{
buffer_pos = data;
available = size;
last_byte = 0;
curr_byte = *data;
}
}
auto lookahead::advance() -> void
{
++buffer_pos;
if (available-- == 1)
{
available = 0;
last_byte = curr_byte;
curr_byte = 0;
}
else
{
last_byte = curr_byte;
curr_byte = *buffer_pos;
}
}
} // namespace xsk::gsc

View File

@ -0,0 +1,23 @@
// 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.
#pragma once
namespace xsk::gsc
{
struct lookahead
{
char const* buffer_pos;
usize available;
char last_byte;
char curr_byte;
lookahead(char const* data, usize size);
auto advance() -> void;
auto ended() { return available == 0; };
};
} // namespace xsk::gsc

19
src/gsc/misc/space.hpp Normal file
View File

@ -0,0 +1,19 @@
// 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.
#pragma once
namespace xsk::gsc
{
enum class spacing : u8
{
none = 0, // no space between tokens
null = 1, // token just after new line
back = 2, // token after space
empty = 4, // token after new line + space
};
} // namespace xsk::gsc

14
src/gsc/misc/token.cpp Normal file
View File

@ -0,0 +1,14 @@
// 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 "location.hpp"
#include "space.hpp"
#include "token.hpp"
namespace xsk::gsc
{
} // namespace xsk::gsc

40
src/gsc/misc/token.hpp Normal file
View File

@ -0,0 +1,40 @@
// 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.
#pragma once
namespace xsk::gsc
{
struct token
{
enum kind : u8
{
PLUS, MINUS, STAR, DIV, MOD, BITOR, BITAND, BITEXOR, SHL, SHR,
ASSIGN, PLUSEQ, MINUSEQ, STAREQ, DIVEQ, MODEQ, BITOREQ, BITANDEQ, BITEXOREQ, SHLEQ, SHREQ,
INC, DEC, GT, LT, GE, LE, NE, EQ, OR, AND, TILDE, BANG, QMARK, COLON, SHARP, COMMA,
DOT, ELLIPSIS, SEMICOLON, DOUBLECOLON, LBRACKET, RBRACKET, LBRACE, RBRACE, LPAREN, RPAREN,
NAME, PATH, STRING, ISTRING, INT, FLT,
DEVBEGIN, DEVEND, INLINE, INCLUDE, USINGTREE, ANIMTREE, ENDON, NOTIFY, WAIT,
WAITTILL, WAITTILLMATCH, WAITTILLFRAMEEND, WAITFRAME, IF, ELSE, DO, WHILE,
FOR, FOREACH, IN, SWITCH, CASE, DEFAULT, BREAK, CONTINUE, RETURN, BREAKPOINT,
PROFBEGIN, PROFEND, THREAD, CHILDTHREAD, THISTHREAD, CALL, TRUE, FALSE, UNDEFINED,
SIZE, GAME, SELF, ANIM, LEVEL, ISDEFINED, ISTRUE,
HASH, NEWLINE, EOS, DEFINED, MACROBEGIN, MACROEND,
};
kind type;
spacing space;
location pos;
std::string data;
token(kind type, spacing space, location pos) : type{ type }, space{ space }, pos{ pos }, data{} {}
token(kind type, spacing space, location pos, std::string data) : type{ type }, space{ space }, pos{ pos }, data{ std::move(data) } {}
};
} // namespace xsk::gsc

View File

@ -10,6 +10,11 @@
#include "assembly.hpp" #include "assembly.hpp"
#include "location.hpp" #include "location.hpp"
#include "exception.hpp" #include "exception.hpp"
#include "lookahead.hpp"
#include "directive.hpp"
#include "space.hpp"
#include "token.hpp"
#include "define.hpp"
#include "ast.hpp" #include "ast.hpp"
namespace xsk::gsc namespace xsk::gsc

File diff suppressed because it is too large Load Diff

View File

@ -45,14 +45,14 @@
#ifndef YY_GSC_PARSER_HPP_INCLUDED #ifndef YY_GSC_PARSER_HPP_INCLUDED
# define YY_GSC_PARSER_HPP_INCLUDED # define YY_GSC_PARSER_HPP_INCLUDED
// "%code requires" blocks. // "%code requires" blocks.
#line 30 "parser.ypp" #line 31 "parser.ypp"
#ifdef _MSC_VER #ifdef _MSC_VER
#pragma warning(disable:4065) #pragma warning(disable:4065)
#pragma warning(disable:4127) #pragma warning(disable:4127)
#endif #endif
#include "context.hpp" #include "context.hpp"
namespace xsk::gsc { class lexer; } namespace xsk::gsc { class preprocessor; }
#line 58 "parser.hpp" #line 58 "parser.hpp"
@ -706,118 +706,112 @@ namespace xsk { namespace gsc {
GSCEOF = 0, // "end of file" GSCEOF = 0, // "end of file"
GSCerror = 1, // error GSCerror = 1, // error
GSCUNDEF = 2, // "invalid token" GSCUNDEF = 2, // "invalid token"
SH_DEFINE = 3, // "#define" HASH = 3, // "#"
SH_UNDEF = 4, // "#undef" DEVBEGIN = 4, // "/#"
SH_IFDEF = 5, // "#ifdef" DEVEND = 5, // "#/"
SH_IFNDEF = 6, // "#ifndef" INLINE = 6, // "#inline"
SH_IF = 7, // "#if" INCLUDE = 7, // "#include"
SH_ELIF = 8, // "#elif" USINGTREE = 8, // "#using_animtree"
SH_ELSE = 9, // "#else" ANIMTREE = 9, // "#animtree"
SH_ENDIF = 10, // "#endif" ENDON = 10, // "endon"
DEVBEGIN = 11, // "/#" NOTIFY = 11, // "notify"
DEVEND = 12, // "#/" WAIT = 12, // "wait"
INLINE = 13, // "#inline" WAITTILL = 13, // "waittill"
INCLUDE = 14, // "#include" WAITTILLMATCH = 14, // "waittillmatch"
USINGTREE = 15, // "#using_animtree" WAITTILLFRAMEEND = 15, // "waittillframeend"
ANIMTREE = 16, // "#animtree" WAITFRAME = 16, // "waitframe"
ENDON = 17, // "endon" IF = 17, // "if"
NOTIFY = 18, // "notify" ELSE = 18, // "else"
WAIT = 19, // "wait" DO = 19, // "do"
WAITTILL = 20, // "waittill" WHILE = 20, // "while"
WAITTILLMATCH = 21, // "waittillmatch" FOR = 21, // "for"
WAITTILLFRAMEEND = 22, // "waittillframeend" FOREACH = 22, // "foreach"
WAITFRAME = 23, // "waitframe" IN = 23, // "in"
IF = 24, // "if" SWITCH = 24, // "switch"
ELSE = 25, // "else" CASE = 25, // "case"
DO = 26, // "do" DEFAULT = 26, // "default"
WHILE = 27, // "while" BREAK = 27, // "break"
FOR = 28, // "for" CONTINUE = 28, // "continue"
FOREACH = 29, // "foreach" RETURN = 29, // "return"
IN = 30, // "in" BREAKPOINT = 30, // "breakpoint"
SWITCH = 31, // "switch" PROFBEGIN = 31, // "prof_begin"
CASE = 32, // "case" PROFEND = 32, // "prof_end"
DEFAULT = 33, // "default" THREAD = 33, // "thread"
BREAK = 34, // "break" CHILDTHREAD = 34, // "childthread"
CONTINUE = 35, // "continue" THISTHREAD = 35, // "thisthread"
RETURN = 36, // "return" CALL = 36, // "call"
BREAKPOINT = 37, // "breakpoint" TRUE = 37, // "true"
PROFBEGIN = 38, // "prof_begin" FALSE = 38, // "false"
PROFEND = 39, // "prof_end" UNDEFINED = 39, // "undefined"
THREAD = 40, // "thread" SIZE = 40, // "size"
CHILDTHREAD = 41, // "childthread" GAME = 41, // "game"
THISTHREAD = 42, // "thisthread" SELF = 42, // "self"
CALL = 43, // "call" ANIM = 43, // "anim"
TRUE = 44, // "true" LEVEL = 44, // "level"
FALSE = 45, // "false" ISDEFINED = 45, // "isdefined"
UNDEFINED = 46, // "undefined" ISTRUE = 46, // "istrue"
SIZE = 47, // "size" LPAREN = 47, // "("
GAME = 48, // "game" RPAREN = 48, // ")"
SELF = 49, // "self" LBRACE = 49, // "{"
ANIM = 50, // "anim" RBRACE = 50, // "}"
LEVEL = 51, // "level" LBRACKET = 51, // "["
ISDEFINED = 52, // "isdefined" RBRACKET = 52, // "]"
ISTRUE = 53, // "istrue" COMMA = 53, // ","
LPAREN = 54, // "(" DOT = 54, // "."
RPAREN = 55, // ")" ELLIPSIS = 55, // "..."
LBRACE = 56, // "{" DOUBLECOLON = 56, // "::"
RBRACE = 57, // "}" COLON = 57, // ":"
LBRACKET = 58, // "[" SEMICOLON = 58, // ";"
RBRACKET = 59, // "]" QMARK = 59, // "?"
COMMA = 60, // "," INCREMENT = 60, // "++"
DOT = 61, // "." DECREMENT = 61, // "--"
DOUBLECOLON = 62, // "::" LSHIFT = 62, // "<<"
COLON = 63, // ":" RSHIFT = 63, // ">>"
SEMICOLON = 64, // ";" OR = 64, // "||"
QMARK = 65, // "?" AND = 65, // "&&"
INCREMENT = 66, // "++" EQUALITY = 66, // "=="
DECREMENT = 67, // "--" INEQUALITY = 67, // "!="
LSHIFT = 68, // "<<" LESS_EQUAL = 68, // "<="
RSHIFT = 69, // ">>" GREATER_EQUAL = 69, // ">="
OR = 70, // "||" LESS = 70, // "<"
AND = 71, // "&&" GREATER = 71, // ">"
EQUALITY = 72, // "==" NOT = 72, // "!"
INEQUALITY = 73, // "!=" COMPLEMENT = 73, // "~"
LESS_EQUAL = 74, // "<=" ASSIGN = 74, // "="
GREATER_EQUAL = 75, // ">=" ASSIGN_ADD = 75, // "+="
LESS = 76, // "<" ASSIGN_SUB = 76, // "-="
GREATER = 77, // ">" ASSIGN_MUL = 77, // "*="
NOT = 78, // "!" ASSIGN_DIV = 78, // "/="
COMPLEMENT = 79, // "~" ASSIGN_MOD = 79, // "%="
ASSIGN = 80, // "=" ASSIGN_BW_OR = 80, // "|="
ASSIGN_ADD = 81, // "+=" ASSIGN_BW_AND = 81, // "&="
ASSIGN_SUB = 82, // "-=" ASSIGN_BW_EXOR = 82, // "^="
ASSIGN_MUL = 83, // "*=" ASSIGN_RSHIFT = 83, // ">>="
ASSIGN_DIV = 84, // "/=" ASSIGN_LSHIFT = 84, // "<<="
ASSIGN_MOD = 85, // "%=" BITWISE_OR = 85, // "|"
ASSIGN_BW_OR = 86, // "|=" BITWISE_AND = 86, // "&"
ASSIGN_BW_AND = 87, // "&=" BITWISE_EXOR = 87, // "^"
ASSIGN_BW_EXOR = 88, // "^=" ADD = 88, // "+"
ASSIGN_RSHIFT = 89, // ">>=" SUB = 89, // "-"
ASSIGN_LSHIFT = 90, // "<<=" MUL = 90, // "*"
BITWISE_OR = 91, // "|" DIV = 91, // "/"
BITWISE_AND = 92, // "&" MOD = 92, // "%"
BITWISE_EXOR = 93, // "^" PATH = 93, // "path"
ADD = 94, // "+" IDENTIFIER = 94, // "identifier"
SUB = 95, // "-" STRING = 95, // "string literal"
MUL = 96, // "*" ISTRING = 96, // "localized string"
DIV = 97, // "/" FLOAT = 97, // "float"
MOD = 98, // "%" INTEGER = 98, // "integer"
PATH = 99, // "path" SIZEOF = 99, // SIZEOF
IDENTIFIER = 100, // "identifier" ADD_ARRAY = 100, // ADD_ARRAY
STRING = 101, // "string literal" THEN = 101, // THEN
ISTRING = 102, // "localized string" TERN = 102, // TERN
FLOAT = 103, // "float" NEG = 103, // NEG
INTEGER = 104, // "integer" ANIMREF = 104, // ANIMREF
SIZEOF = 105, // SIZEOF PREINC = 105, // PREINC
ADD_ARRAY = 106, // ADD_ARRAY PREDEC = 106, // PREDEC
THEN = 107, // THEN POSTINC = 107, // POSTINC
TERN = 108, // TERN POSTDEC = 108 // POSTDEC
NEG = 109, // NEG
ANIMREF = 110, // ANIMREF
PREINC = 111, // PREINC
PREDEC = 112, // PREDEC
POSTINC = 113, // POSTINC
POSTDEC = 114 // POSTDEC
}; };
/// Backward compatibility alias (Bison 3.6). /// Backward compatibility alias (Bison 3.6).
typedef token_kind_type yytokentype; typedef token_kind_type yytokentype;
@ -834,211 +828,205 @@ namespace xsk { namespace gsc {
{ {
enum symbol_kind_type enum symbol_kind_type
{ {
YYNTOKENS = 115, ///< Number of tokens. YYNTOKENS = 109, ///< Number of tokens.
S_YYEMPTY = -2, S_YYEMPTY = -2,
S_YYEOF = 0, // "end of file" S_YYEOF = 0, // "end of file"
S_YYerror = 1, // error S_YYerror = 1, // error
S_YYUNDEF = 2, // "invalid token" S_YYUNDEF = 2, // "invalid token"
S_SH_DEFINE = 3, // "#define" S_HASH = 3, // "#"
S_SH_UNDEF = 4, // "#undef" S_DEVBEGIN = 4, // "/#"
S_SH_IFDEF = 5, // "#ifdef" S_DEVEND = 5, // "#/"
S_SH_IFNDEF = 6, // "#ifndef" S_INLINE = 6, // "#inline"
S_SH_IF = 7, // "#if" S_INCLUDE = 7, // "#include"
S_SH_ELIF = 8, // "#elif" S_USINGTREE = 8, // "#using_animtree"
S_SH_ELSE = 9, // "#else" S_ANIMTREE = 9, // "#animtree"
S_SH_ENDIF = 10, // "#endif" S_ENDON = 10, // "endon"
S_DEVBEGIN = 11, // "/#" S_NOTIFY = 11, // "notify"
S_DEVEND = 12, // "#/" S_WAIT = 12, // "wait"
S_INLINE = 13, // "#inline" S_WAITTILL = 13, // "waittill"
S_INCLUDE = 14, // "#include" S_WAITTILLMATCH = 14, // "waittillmatch"
S_USINGTREE = 15, // "#using_animtree" S_WAITTILLFRAMEEND = 15, // "waittillframeend"
S_ANIMTREE = 16, // "#animtree" S_WAITFRAME = 16, // "waitframe"
S_ENDON = 17, // "endon" S_IF = 17, // "if"
S_NOTIFY = 18, // "notify" S_ELSE = 18, // "else"
S_WAIT = 19, // "wait" S_DO = 19, // "do"
S_WAITTILL = 20, // "waittill" S_WHILE = 20, // "while"
S_WAITTILLMATCH = 21, // "waittillmatch" S_FOR = 21, // "for"
S_WAITTILLFRAMEEND = 22, // "waittillframeend" S_FOREACH = 22, // "foreach"
S_WAITFRAME = 23, // "waitframe" S_IN = 23, // "in"
S_IF = 24, // "if" S_SWITCH = 24, // "switch"
S_ELSE = 25, // "else" S_CASE = 25, // "case"
S_DO = 26, // "do" S_DEFAULT = 26, // "default"
S_WHILE = 27, // "while" S_BREAK = 27, // "break"
S_FOR = 28, // "for" S_CONTINUE = 28, // "continue"
S_FOREACH = 29, // "foreach" S_RETURN = 29, // "return"
S_IN = 30, // "in" S_BREAKPOINT = 30, // "breakpoint"
S_SWITCH = 31, // "switch" S_PROFBEGIN = 31, // "prof_begin"
S_CASE = 32, // "case" S_PROFEND = 32, // "prof_end"
S_DEFAULT = 33, // "default" S_THREAD = 33, // "thread"
S_BREAK = 34, // "break" S_CHILDTHREAD = 34, // "childthread"
S_CONTINUE = 35, // "continue" S_THISTHREAD = 35, // "thisthread"
S_RETURN = 36, // "return" S_CALL = 36, // "call"
S_BREAKPOINT = 37, // "breakpoint" S_TRUE = 37, // "true"
S_PROFBEGIN = 38, // "prof_begin" S_FALSE = 38, // "false"
S_PROFEND = 39, // "prof_end" S_UNDEFINED = 39, // "undefined"
S_THREAD = 40, // "thread" S_SIZE = 40, // "size"
S_CHILDTHREAD = 41, // "childthread" S_GAME = 41, // "game"
S_THISTHREAD = 42, // "thisthread" S_SELF = 42, // "self"
S_CALL = 43, // "call" S_ANIM = 43, // "anim"
S_TRUE = 44, // "true" S_LEVEL = 44, // "level"
S_FALSE = 45, // "false" S_ISDEFINED = 45, // "isdefined"
S_UNDEFINED = 46, // "undefined" S_ISTRUE = 46, // "istrue"
S_SIZE = 47, // "size" S_LPAREN = 47, // "("
S_GAME = 48, // "game" S_RPAREN = 48, // ")"
S_SELF = 49, // "self" S_LBRACE = 49, // "{"
S_ANIM = 50, // "anim" S_RBRACE = 50, // "}"
S_LEVEL = 51, // "level" S_LBRACKET = 51, // "["
S_ISDEFINED = 52, // "isdefined" S_RBRACKET = 52, // "]"
S_ISTRUE = 53, // "istrue" S_COMMA = 53, // ","
S_LPAREN = 54, // "(" S_DOT = 54, // "."
S_RPAREN = 55, // ")" S_ELLIPSIS = 55, // "..."
S_LBRACE = 56, // "{" S_DOUBLECOLON = 56, // "::"
S_RBRACE = 57, // "}" S_COLON = 57, // ":"
S_LBRACKET = 58, // "[" S_SEMICOLON = 58, // ";"
S_RBRACKET = 59, // "]" S_QMARK = 59, // "?"
S_COMMA = 60, // "," S_INCREMENT = 60, // "++"
S_DOT = 61, // "." S_DECREMENT = 61, // "--"
S_DOUBLECOLON = 62, // "::" S_LSHIFT = 62, // "<<"
S_COLON = 63, // ":" S_RSHIFT = 63, // ">>"
S_SEMICOLON = 64, // ";" S_OR = 64, // "||"
S_QMARK = 65, // "?" S_AND = 65, // "&&"
S_INCREMENT = 66, // "++" S_EQUALITY = 66, // "=="
S_DECREMENT = 67, // "--" S_INEQUALITY = 67, // "!="
S_LSHIFT = 68, // "<<" S_LESS_EQUAL = 68, // "<="
S_RSHIFT = 69, // ">>" S_GREATER_EQUAL = 69, // ">="
S_OR = 70, // "||" S_LESS = 70, // "<"
S_AND = 71, // "&&" S_GREATER = 71, // ">"
S_EQUALITY = 72, // "==" S_NOT = 72, // "!"
S_INEQUALITY = 73, // "!=" S_COMPLEMENT = 73, // "~"
S_LESS_EQUAL = 74, // "<=" S_ASSIGN = 74, // "="
S_GREATER_EQUAL = 75, // ">=" S_ASSIGN_ADD = 75, // "+="
S_LESS = 76, // "<" S_ASSIGN_SUB = 76, // "-="
S_GREATER = 77, // ">" S_ASSIGN_MUL = 77, // "*="
S_NOT = 78, // "!" S_ASSIGN_DIV = 78, // "/="
S_COMPLEMENT = 79, // "~" S_ASSIGN_MOD = 79, // "%="
S_ASSIGN = 80, // "=" S_ASSIGN_BW_OR = 80, // "|="
S_ASSIGN_ADD = 81, // "+=" S_ASSIGN_BW_AND = 81, // "&="
S_ASSIGN_SUB = 82, // "-=" S_ASSIGN_BW_EXOR = 82, // "^="
S_ASSIGN_MUL = 83, // "*=" S_ASSIGN_RSHIFT = 83, // ">>="
S_ASSIGN_DIV = 84, // "/=" S_ASSIGN_LSHIFT = 84, // "<<="
S_ASSIGN_MOD = 85, // "%=" S_BITWISE_OR = 85, // "|"
S_ASSIGN_BW_OR = 86, // "|=" S_BITWISE_AND = 86, // "&"
S_ASSIGN_BW_AND = 87, // "&=" S_BITWISE_EXOR = 87, // "^"
S_ASSIGN_BW_EXOR = 88, // "^=" S_ADD = 88, // "+"
S_ASSIGN_RSHIFT = 89, // ">>=" S_SUB = 89, // "-"
S_ASSIGN_LSHIFT = 90, // "<<=" S_MUL = 90, // "*"
S_BITWISE_OR = 91, // "|" S_DIV = 91, // "/"
S_BITWISE_AND = 92, // "&" S_MOD = 92, // "%"
S_BITWISE_EXOR = 93, // "^" S_PATH = 93, // "path"
S_ADD = 94, // "+" S_IDENTIFIER = 94, // "identifier"
S_SUB = 95, // "-" S_STRING = 95, // "string literal"
S_MUL = 96, // "*" S_ISTRING = 96, // "localized string"
S_DIV = 97, // "/" S_FLOAT = 97, // "float"
S_MOD = 98, // "%" S_INTEGER = 98, // "integer"
S_PATH = 99, // "path" S_SIZEOF = 99, // SIZEOF
S_IDENTIFIER = 100, // "identifier" S_ADD_ARRAY = 100, // ADD_ARRAY
S_STRING = 101, // "string literal" S_THEN = 101, // THEN
S_ISTRING = 102, // "localized string" S_TERN = 102, // TERN
S_FLOAT = 103, // "float" S_NEG = 103, // NEG
S_INTEGER = 104, // "integer" S_ANIMREF = 104, // ANIMREF
S_SIZEOF = 105, // SIZEOF S_PREINC = 105, // PREINC
S_ADD_ARRAY = 106, // ADD_ARRAY S_PREDEC = 106, // PREDEC
S_THEN = 107, // THEN S_POSTINC = 107, // POSTINC
S_TERN = 108, // TERN S_POSTDEC = 108, // POSTDEC
S_NEG = 109, // NEG S_YYACCEPT = 109, // $accept
S_ANIMREF = 110, // ANIMREF S_root = 110, // root
S_PREINC = 111, // PREINC S_program = 111, // program
S_PREDEC = 112, // PREDEC S_inline = 112, // inline
S_POSTINC = 113, // POSTINC S_include = 113, // include
S_POSTDEC = 114, // POSTDEC S_declaration = 114, // declaration
S_YYACCEPT = 115, // $accept S_decl_usingtree = 115, // decl_usingtree
S_root = 116, // root S_decl_function = 116, // decl_function
S_program = 117, // program S_stmt = 117, // stmt
S_inline = 118, // inline S_stmt_or_dev = 118, // stmt_or_dev
S_include = 119, // include S_stmt_list = 119, // stmt_list
S_declaration = 120, // declaration S_stmt_or_dev_list = 120, // stmt_or_dev_list
S_decl_usingtree = 121, // decl_usingtree S_stmt_dev = 121, // stmt_dev
S_decl_function = 122, // decl_function S_stmt_comp = 122, // stmt_comp
S_stmt = 123, // stmt S_stmt_expr = 123, // stmt_expr
S_stmt_or_dev = 124, // stmt_or_dev S_stmt_call = 124, // stmt_call
S_stmt_list = 125, // stmt_list S_stmt_assign = 125, // stmt_assign
S_stmt_or_dev_list = 126, // stmt_or_dev_list S_stmt_endon = 126, // stmt_endon
S_stmt_dev = 127, // stmt_dev S_stmt_notify = 127, // stmt_notify
S_stmt_comp = 128, // stmt_comp S_stmt_wait = 128, // stmt_wait
S_stmt_expr = 129, // stmt_expr S_stmt_waittill = 129, // stmt_waittill
S_stmt_call = 130, // stmt_call S_stmt_waittillmatch = 130, // stmt_waittillmatch
S_stmt_assign = 131, // stmt_assign S_stmt_waittillframeend = 131, // stmt_waittillframeend
S_stmt_endon = 132, // stmt_endon S_stmt_waitframe = 132, // stmt_waitframe
S_stmt_notify = 133, // stmt_notify S_stmt_if = 133, // stmt_if
S_stmt_wait = 134, // stmt_wait S_stmt_ifelse = 134, // stmt_ifelse
S_stmt_waittill = 135, // stmt_waittill S_stmt_while = 135, // stmt_while
S_stmt_waittillmatch = 136, // stmt_waittillmatch S_stmt_dowhile = 136, // stmt_dowhile
S_stmt_waittillframeend = 137, // stmt_waittillframeend S_stmt_for = 137, // stmt_for
S_stmt_waitframe = 138, // stmt_waitframe S_stmt_foreach = 138, // stmt_foreach
S_stmt_if = 139, // stmt_if S_stmt_switch = 139, // stmt_switch
S_stmt_ifelse = 140, // stmt_ifelse S_stmt_case = 140, // stmt_case
S_stmt_while = 141, // stmt_while S_stmt_default = 141, // stmt_default
S_stmt_dowhile = 142, // stmt_dowhile S_stmt_break = 142, // stmt_break
S_stmt_for = 143, // stmt_for S_stmt_continue = 143, // stmt_continue
S_stmt_foreach = 144, // stmt_foreach S_stmt_return = 144, // stmt_return
S_stmt_switch = 145, // stmt_switch S_stmt_breakpoint = 145, // stmt_breakpoint
S_stmt_case = 146, // stmt_case S_stmt_prof_begin = 146, // stmt_prof_begin
S_stmt_default = 147, // stmt_default S_stmt_prof_end = 147, // stmt_prof_end
S_stmt_break = 148, // stmt_break S_expr = 148, // expr
S_stmt_continue = 149, // stmt_continue S_expr_or_empty = 149, // expr_or_empty
S_stmt_return = 150, // stmt_return S_expr_assign = 150, // expr_assign
S_stmt_breakpoint = 151, // stmt_breakpoint S_expr_increment = 151, // expr_increment
S_stmt_prof_begin = 152, // stmt_prof_begin S_expr_decrement = 152, // expr_decrement
S_stmt_prof_end = 153, // stmt_prof_end S_expr_ternary = 153, // expr_ternary
S_expr = 154, // expr S_expr_binary = 154, // expr_binary
S_expr_or_empty = 155, // expr_or_empty S_expr_primitive = 155, // expr_primitive
S_expr_assign = 156, // expr_assign S_expr_complement = 156, // expr_complement
S_expr_increment = 157, // expr_increment S_expr_negate = 157, // expr_negate
S_expr_decrement = 158, // expr_decrement S_expr_not = 158, // expr_not
S_expr_ternary = 159, // expr_ternary S_expr_call = 159, // expr_call
S_expr_binary = 160, // expr_binary S_expr_method = 160, // expr_method
S_expr_primitive = 161, // expr_primitive S_expr_function = 161, // expr_function
S_expr_complement = 162, // expr_complement S_expr_pointer = 162, // expr_pointer
S_expr_negate = 163, // expr_negate S_expr_add_array = 163, // expr_add_array
S_expr_not = 164, // expr_not S_expr_parameters = 164, // expr_parameters
S_expr_call = 165, // expr_call S_expr_arguments = 165, // expr_arguments
S_expr_method = 166, // expr_method S_expr_arguments_no_empty = 166, // expr_arguments_no_empty
S_expr_function = 167, // expr_function S_expr_isdefined = 167, // expr_isdefined
S_expr_pointer = 168, // expr_pointer S_expr_istrue = 168, // expr_istrue
S_expr_add_array = 169, // expr_add_array S_expr_reference = 169, // expr_reference
S_expr_parameters = 170, // expr_parameters S_expr_tuple = 170, // expr_tuple
S_expr_arguments = 171, // expr_arguments S_expr_tuple_arguments = 171, // expr_tuple_arguments
S_expr_arguments_no_empty = 172, // expr_arguments_no_empty S_expr_tuple_types = 172, // expr_tuple_types
S_expr_isdefined = 173, // expr_isdefined S_expr_array = 173, // expr_array
S_expr_istrue = 174, // expr_istrue S_expr_field = 174, // expr_field
S_expr_reference = 175, // expr_reference S_expr_size = 175, // expr_size
S_expr_tuple = 176, // expr_tuple S_expr_paren = 176, // expr_paren
S_expr_tuple_arguments = 177, // expr_tuple_arguments S_expr_object = 177, // expr_object
S_expr_tuple_types = 178, // expr_tuple_types S_expr_thisthread = 178, // expr_thisthread
S_expr_array = 179, // expr_array S_expr_empty_array = 179, // expr_empty_array
S_expr_field = 180, // expr_field S_expr_undefined = 180, // expr_undefined
S_expr_size = 181, // expr_size S_expr_game = 181, // expr_game
S_expr_paren = 182, // expr_paren S_expr_self = 182, // expr_self
S_expr_object = 183, // expr_object S_expr_anim = 183, // expr_anim
S_expr_thisthread = 184, // expr_thisthread S_expr_level = 184, // expr_level
S_expr_empty_array = 185, // expr_empty_array S_expr_animation = 185, // expr_animation
S_expr_undefined = 186, // expr_undefined S_expr_animtree = 186, // expr_animtree
S_expr_game = 187, // expr_game S_expr_identifier_nosize = 187, // expr_identifier_nosize
S_expr_self = 188, // expr_self S_expr_identifier = 188, // expr_identifier
S_expr_anim = 189, // expr_anim S_expr_path = 189, // expr_path
S_expr_level = 190, // expr_level S_expr_istring = 190, // expr_istring
S_expr_animation = 191, // expr_animation S_expr_string = 191, // expr_string
S_expr_animtree = 192, // expr_animtree S_expr_vector = 192, // expr_vector
S_expr_identifier_nosize = 193, // expr_identifier_nosize S_expr_float = 193, // expr_float
S_expr_identifier = 194, // expr_identifier S_expr_integer = 194, // expr_integer
S_expr_path = 195, // expr_path S_expr_false = 195, // expr_false
S_expr_istring = 196, // expr_istring S_expr_true = 196 // expr_true
S_expr_string = 197, // expr_string
S_expr_vector = 198, // expr_vector
S_expr_float = 199, // expr_float
S_expr_integer = 200, // expr_integer
S_expr_false = 201, // expr_false
S_expr_true = 202 // expr_true
}; };
}; };
@ -2841,7 +2829,7 @@ switch (yykind)
}; };
/// Build a parser object. /// Build a parser object.
parser (xsk::gsc::context const* ctx__yyarg, xsk::gsc::lexer& lexer_yyarg, xsk::gsc::program::ptr& ast_yyarg, std::uint32_t index_yyarg); parser (xsk::gsc::context const* ctx__yyarg, xsk::gsc::preprocessor& ppr_yyarg, xsk::gsc::program::ptr& ast_yyarg, std::uint32_t index_yyarg);
virtual ~parser (); virtual ~parser ();
#if 201103L <= YY_CPLUSPLUS #if 201103L <= YY_CPLUSPLUS
@ -2934,121 +2922,16 @@ switch (yykind)
#if 201103L <= YY_CPLUSPLUS #if 201103L <= YY_CPLUSPLUS
static static
symbol_type symbol_type
make_SH_DEFINE (location_type l) make_HASH (location_type l)
{ {
return symbol_type (token::SH_DEFINE, std::move (l)); return symbol_type (token::HASH, std::move (l));
} }
#else #else
static static
symbol_type symbol_type
make_SH_DEFINE (const location_type& l) make_HASH (const location_type& l)
{ {
return symbol_type (token::SH_DEFINE, l); return symbol_type (token::HASH, l);
}
#endif
#if 201103L <= YY_CPLUSPLUS
static
symbol_type
make_SH_UNDEF (location_type l)
{
return symbol_type (token::SH_UNDEF, std::move (l));
}
#else
static
symbol_type
make_SH_UNDEF (const location_type& l)
{
return symbol_type (token::SH_UNDEF, l);
}
#endif
#if 201103L <= YY_CPLUSPLUS
static
symbol_type
make_SH_IFDEF (location_type l)
{
return symbol_type (token::SH_IFDEF, std::move (l));
}
#else
static
symbol_type
make_SH_IFDEF (const location_type& l)
{
return symbol_type (token::SH_IFDEF, l);
}
#endif
#if 201103L <= YY_CPLUSPLUS
static
symbol_type
make_SH_IFNDEF (location_type l)
{
return symbol_type (token::SH_IFNDEF, std::move (l));
}
#else
static
symbol_type
make_SH_IFNDEF (const location_type& l)
{
return symbol_type (token::SH_IFNDEF, l);
}
#endif
#if 201103L <= YY_CPLUSPLUS
static
symbol_type
make_SH_IF (location_type l)
{
return symbol_type (token::SH_IF, std::move (l));
}
#else
static
symbol_type
make_SH_IF (const location_type& l)
{
return symbol_type (token::SH_IF, l);
}
#endif
#if 201103L <= YY_CPLUSPLUS
static
symbol_type
make_SH_ELIF (location_type l)
{
return symbol_type (token::SH_ELIF, std::move (l));
}
#else
static
symbol_type
make_SH_ELIF (const location_type& l)
{
return symbol_type (token::SH_ELIF, l);
}
#endif
#if 201103L <= YY_CPLUSPLUS
static
symbol_type
make_SH_ELSE (location_type l)
{
return symbol_type (token::SH_ELSE, std::move (l));
}
#else
static
symbol_type
make_SH_ELSE (const location_type& l)
{
return symbol_type (token::SH_ELSE, l);
}
#endif
#if 201103L <= YY_CPLUSPLUS
static
symbol_type
make_SH_ENDIF (location_type l)
{
return symbol_type (token::SH_ENDIF, std::move (l));
}
#else
static
symbol_type
make_SH_ENDIF (const location_type& l)
{
return symbol_type (token::SH_ENDIF, l);
} }
#endif #endif
#if 201103L <= YY_CPLUSPLUS #if 201103L <= YY_CPLUSPLUS
@ -3816,6 +3699,21 @@ switch (yykind)
return symbol_type (token::DOT, l); return symbol_type (token::DOT, l);
} }
#endif #endif
#if 201103L <= YY_CPLUSPLUS
static
symbol_type
make_ELLIPSIS (location_type l)
{
return symbol_type (token::ELLIPSIS, std::move (l));
}
#else
static
symbol_type
make_ELLIPSIS (const location_type& l)
{
return symbol_type (token::ELLIPSIS, l);
}
#endif
#if 201103L <= YY_CPLUSPLUS #if 201103L <= YY_CPLUSPLUS
static static
symbol_type symbol_type
@ -4955,7 +4853,7 @@ switch (yykind)
/// Constants. /// Constants.
enum enum
{ {
yylast_ = 2411, ///< Last index in yytable_. yylast_ = 2289, ///< Last index in yytable_.
yynnts_ = 88, ///< Number of nonterminal symbols. yynnts_ = 88, ///< Number of nonterminal symbols.
yyfinal_ = 21 ///< Termination state number. yyfinal_ = 21 ///< Termination state number.
}; };
@ -4963,7 +4861,7 @@ switch (yykind)
// User arguments. // User arguments.
xsk::gsc::context const* ctx_; xsk::gsc::context const* ctx_;
xsk::gsc::lexer& lexer; xsk::gsc::preprocessor& ppr;
xsk::gsc::program::ptr& ast; xsk::gsc::program::ptr& ast;
std::uint32_t index; std::uint32_t index;
@ -5691,7 +5589,7 @@ switch (yykind)
#line 13 "parser.ypp" #line 13 "parser.ypp"
} } // xsk::gsc } } // xsk::gsc
#line 5695 "parser.hpp" #line 5593 "parser.hpp"

1080
src/gsc/preprocessor.cpp Normal file

File diff suppressed because it is too large Load Diff

85
src/gsc/preprocessor.hpp Normal file
View File

@ -0,0 +1,85 @@
// 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.
#pragma once
#include "misc/types.hpp"
#include "lexer.hpp"
namespace xsk::gsc
{
class preprocessor
{
context const* ctx_;
std::stack<lexer> lexer_;
std::stack<directive> indents_;
std::vector<std::string> includes_;
std::unordered_map<std::string_view, directive::kind> directives_;
std::unordered_map<std::string, define> defines_;
std::set<std::string> reject_;
std::deque<token> tokens_;
std::vector<token> expr_;
usize curr_expr_;
u32 expand_;
u32 skip_;
public:
preprocessor(context const* ctx, std::string const& name, char const* data, usize size);
auto process() -> token;
auto push_header(std::string const& file) -> void;
auto pop_header() -> void;
auto ban_header(location const& loc) -> void;
private:
auto skip_line() -> void;
auto next_token() -> token;
auto read_token() -> token;
auto read_directive(token& tok) -> void;
auto read_directive_if(token& tok) -> void;
auto read_directive_ifdef(token& tok) -> void;
auto read_directive_ifndef(token& tok) -> void;
auto read_directive_elif(token& tok) -> void;
auto read_directive_elifdef(token& tok) -> void;
auto read_directive_elifndef(token& tok) -> void;
auto read_directive_else(token& tok) -> void;
auto read_directive_endif(token& tok) -> void;
auto read_directive_define(token& tok) -> void;
auto read_directive_undef(token& tok) -> void;
auto read_directive_pragma(token& tok) -> void;
auto read_directive_warning(token& tok) -> void;
auto read_directive_error(token& tok) -> void;
auto read_directive_line(token& tok) -> void;
auto read_directive_include(token& hash, token& name) -> void;
auto read_directive_inline(token& hash, token& name) -> void;
auto read_directive_usingtree(token& hash, token& name) -> void;
auto read_hashtoken(token& hash) -> void;
auto read_hashtoken_animtree(token& hash, token& name) -> void;
auto expand(token& tok, define& def) -> void;
auto expect(token& tok, token::kind expected, spacing space = spacing::none) -> void;
auto evaluate() -> bool;
auto eval_next() -> token&;
auto eval_peek() -> token&;
auto eval_prev() -> token&;
auto eval_atend() -> bool;
auto eval_check(token::kind type) -> bool;
auto eval_match(token::kind type) -> bool;
auto eval_consume(token::kind type, std::string_view msg);
auto eval_expr() -> i32;
auto eval_expr_or() -> i32;
auto eval_expr_and() -> i32;
auto eval_expr_bwor() -> i32;
auto eval_expr_bwexor() -> i32;
auto eval_expr_bwand() -> i32;
auto eval_expr_eq() -> i32;
auto eval_expr_lge() -> i32;
auto eval_expr_shift() -> i32;
auto eval_expr_add() -> i32;
auto eval_expr_factor() -> i32;
auto eval_expr_unary() -> i32;
auto eval_expr_primary() -> i32;
};
} // namespace xsk::gsc

View File

@ -6,13 +6,14 @@
#include "stdinc.hpp" #include "stdinc.hpp"
#include "source.hpp" #include "source.hpp"
#include "context.hpp" #include "context.hpp"
#include "lexer.hpp" #include "preprocessor.hpp"
#include "parser.hpp"
#include "utils/string.hpp" #include "utils/string.hpp"
namespace xsk::gsc namespace xsk::gsc
{ {
source::source(context const* ctx) : ctx_{ ctx } source::source(context const* ctx) : ctx_{ ctx }, indent_{ 0 }
{ {
} }
@ -50,7 +51,6 @@ auto source::parse_assembly(u8 const* data, usize size) -> assembly::ptr
func->index = index; func->index = index;
func->name = line.substr(4); func->name = line.substr(4);
func->id = ctx_->token_id(func->name); func->id = ctx_->token_id(func->name);
} }
else if (line.substr(0, 4) == "end:") else if (line.substr(0, 4) == "end:")
{ {
@ -143,8 +143,8 @@ auto source::parse_program(std::string const& name, std::vector<u8> const& data)
auto source::parse_program(std::string const& name, u8 const* data, usize size) -> program::ptr auto source::parse_program(std::string const& name, u8 const* data, usize size) -> program::ptr
{ {
auto res = program::ptr{ nullptr }; auto res = program::ptr{ nullptr };
auto lxr = lexer{ ctx_, name, reinterpret_cast<char const*>(data), size }; auto ppr = preprocessor{ ctx_, name, reinterpret_cast<char const*>(data), size };
auto psr = parser{ ctx_, lxr, res, 0 }; auto psr = parser{ ctx_, ppr, res, 0 };
if (!psr.parse() && res != nullptr) if (!psr.parse() && res != nullptr)
return res; return res;
@ -341,6 +341,7 @@ auto source::dump_decl_usingtree(decl_usingtree const& dec) -> void
auto source::dump_decl_function(decl_function const& dec) -> void auto source::dump_decl_function(decl_function const& dec) -> void
{ {
indent_ = 0;
dump_expr_identifier(*dec.name); dump_expr_identifier(*dec.name);
fmt::format_to(std::back_inserter(buf_), "("); fmt::format_to(std::back_inserter(buf_), "(");
dump_expr_parameters(*dec.params); dump_expr_parameters(*dec.params);
@ -567,6 +568,12 @@ auto source::dump_stmt_wait(stmt_wait const& stm) -> void
dump_expr(stm.time); dump_expr(stm.time);
fmt::format_to(std::back_inserter(buf_), ";"); fmt::format_to(std::back_inserter(buf_), ";");
} }
else if (stm.time == node::expr_paren)
{
fmt::format_to(std::back_inserter(buf_), "wait");
dump_expr(stm.time);
fmt::format_to(std::back_inserter(buf_), ";");
}
else else
{ {
fmt::format_to(std::back_inserter(buf_), "wait("); fmt::format_to(std::back_inserter(buf_), "wait(");

View File

@ -12,8 +12,11 @@
#include <fstream> #include <fstream>
#include <functional> #include <functional>
#include <iostream> #include <iostream>
#include <list>
#include <map> #include <map>
#include <memory> #include <memory>
#include <queue>
#include <deque>
#include <regex> #include <regex>
#include <set> #include <set>
#include <sstream> #include <sstream>

View File

@ -31,7 +31,7 @@ namespace xsk
{ {
enum class encd { _, source, assembly, binary }; enum class encd { _, source, assembly, binary };
enum class mode { _, assemble, disassemble, compile, decompile }; enum class mode { _, assemble, disassemble, compile, decompile, parse };
enum class game { _, iw5ps, iw5xb, iw6ps, iw6xb, s1ps, s1xb, iw5, iw6, iw7, iw8, iw9, s1, s2, s4, h1, h2, t6 }; enum class game { _, iw5ps, iw5xb, iw6ps, iw6xb, s1ps, s1xb, iw5, iw6, iw7, iw8, iw9, s1, s2, s4, h1, h2, t6 };
std::unordered_map<std::string_view, encd> const exts = std::unordered_map<std::string_view, encd> const exts =
@ -48,6 +48,7 @@ std::unordered_map<std::string_view, mode> const modes =
{ "disasm", mode::disassemble }, { "disasm", mode::disassemble },
{ "comp", mode::compile }, { "comp", mode::compile },
{ "decomp", mode::decompile }, { "decomp", mode::decompile },
{ "parse", mode::parse },
}; };
std::unordered_map<std::string_view, game> const games = std::unordered_map<std::string_view, game> const games =
@ -359,6 +360,27 @@ auto decompile_file(game game, fs::path file, fs::path rel) -> void
} }
} }
auto parse_file(game game, fs::path file, fs::path rel) -> void
{
try
{
if (file.extension() != ".gsc")
throw std::runtime_error("expected .gsc file");
rel = fs::path{ games_rev.at(game) } / rel / file.filename();
auto data = utils::file::read(file);
auto prog = contexts[game]->source().parse_program(file.string(), data);
utils::file::save(fs::path{ "parsed" } / rel, contexts[game]->source().dump(*prog));
fmt::print("parsed {}\n", rel.generic_string());
}
catch (std::exception const& e)
{
std::cerr << fmt::format("{} at {}\n", e.what(), file.generic_string());
}
}
std::unordered_map<std::string, std::vector<std::uint8_t>> files; std::unordered_map<std::string, std::vector<std::uint8_t>> files;
auto read_file_cb(std::string const& name) -> std::pair<buffer, buffer> auto read_file_cb(std::string const& name) -> std::pair<buffer, buffer>
@ -541,6 +563,7 @@ auto init(game game) -> void
funcs[mode::disassemble] = disassemble_file; funcs[mode::disassemble] = disassemble_file;
funcs[mode::compile] = compile_file; funcs[mode::compile] = compile_file;
funcs[mode::decompile] = decompile_file; funcs[mode::decompile] = decompile_file;
funcs[mode::parse] = parse_file;
switch (game) switch (game)
{ {
@ -676,6 +699,11 @@ void decompile_file(game game, fs::path const& file, fs::path rel)
} }
} }
void parse_file(game, fs::path const&, fs::path)
{
std::cerr << fmt::format("not implemented for t6\n");
}
auto init_t6() -> void auto init_t6() -> void
{ {
if (!contexts.contains(game::t6)) if (!contexts.contains(game::t6))
@ -691,6 +719,7 @@ auto init(game game) -> void
funcs[mode::disassemble] = disassemble_file; funcs[mode::disassemble] = disassemble_file;
funcs[mode::compile] = compile_file; funcs[mode::compile] = compile_file;
funcs[mode::decompile] = decompile_file; funcs[mode::decompile] = decompile_file;
funcs[mode::parse] = parse_file;
switch (game) switch (game)
{ {
@ -780,7 +809,7 @@ auto parse_flags(u32 argc, char** argv, game& game, mode& mode, fs::path& path)
auto print_usage() -> void auto print_usage() -> void
{ {
std::cout << "usage: gsc-tool <mode> <game> <path>\n"; std::cout << "usage: gsc-tool <mode> <game> <path>\n";
std::cout << "\t* modes: asm, disasm, comp, decomp\n"; std::cout << "\t* modes: asm, disasm, comp, decomp, parse\n";
std::cout << "\t* games: iw5ps, iw5xb, iw6ps, iw6xb, s1ps, s1xb, iw5, iw6, iw7, iw8, iw9, s1, s2, s4, h1, h2, t6\n"; std::cout << "\t* games: iw5ps, iw5xb, iw6ps, iw6xb, s1ps, s1xb, iw5, iw6, iw7, iw8, iw9, s1, s2, s4, h1, h2, t6\n";
std::cout << "\t* paths: file or directory (recursive)\n"; std::cout << "\t* paths: file or directory (recursive)\n";
std::cin.get(); std::cin.get();

View File

@ -23,7 +23,8 @@ reader::reader(u8 const* data, u32 size, bool swap) : data_{ data }, size_{ size
template<> auto reader::read() -> i8 template<> auto reader::read() -> i8
{ {
if (pos_ + 1 > size_) return i8{}; if (pos_ + 1 > size_)
throw std::runtime_error("reader: out of bounds");
auto value = *reinterpret_cast<i8 const*>(data_ + pos_); auto value = *reinterpret_cast<i8 const*>(data_ + pos_);
pos_ += 1; pos_ += 1;
@ -32,7 +33,8 @@ template<> auto reader::read() -> i8
template<> auto reader::read() -> u8 template<> auto reader::read() -> u8
{ {
if (pos_ + 1 > size_) return u8{}; if (pos_ + 1 > size_)
throw std::runtime_error("reader: out of bounds");
auto value = *reinterpret_cast<u8 const*>(data_ + pos_); auto value = *reinterpret_cast<u8 const*>(data_ + pos_);
pos_ += 1; pos_ += 1;
@ -41,7 +43,8 @@ template<> auto reader::read() -> u8
template<> auto reader::read() -> i16 template<> auto reader::read() -> i16
{ {
if (pos_ + 2 > size_) return i16{}; if (pos_ + 2 > size_)
throw std::runtime_error("reader: out of bounds");
if (!swap_) if (!swap_)
{ {
@ -59,7 +62,8 @@ template<> auto reader::read() -> i16
template<> auto reader::read() -> u16 template<> auto reader::read() -> u16
{ {
if (pos_ + 2 > size_) return u16{}; if (pos_ + 2 > size_)
throw std::runtime_error("reader: out of bounds");
if (!swap_) if (!swap_)
{ {
@ -77,7 +81,8 @@ template<> auto reader::read() -> u16
template<> auto reader::read() -> i32 template<> auto reader::read() -> i32
{ {
if (pos_ + 4 > size_) return i32{}; if (pos_ + 4 > size_)
throw std::runtime_error("reader: out of bounds");
if (!swap_) if (!swap_)
{ {
@ -97,7 +102,8 @@ template<> auto reader::read() -> i32
template<> auto reader::read() -> u32 template<> auto reader::read() -> u32
{ {
if (pos_ + 4 > size_) return u32{}; if (pos_ + 4 > size_)
throw std::runtime_error("reader: out of bounds");
if (!swap_) if (!swap_)
{ {
@ -117,7 +123,8 @@ template<> auto reader::read() -> u32
template<> auto reader::read() -> i64 template<> auto reader::read() -> i64
{ {
if (pos_ + 8 > size_) return i64{}; if (pos_ + 8 > size_)
throw std::runtime_error("reader: out of bounds");
if (!swap_) if (!swap_)
{ {
@ -141,7 +148,8 @@ template<> auto reader::read() -> i64
template<> auto reader::read() -> u64 template<> auto reader::read() -> u64
{ {
if (pos_ + 8 > size_) return u64{}; if (pos_ + 8 > size_)
throw std::runtime_error("reader: out of bounds");
if (!swap_) if (!swap_)
{ {
@ -165,7 +173,8 @@ template<> auto reader::read() -> u64
template<> auto reader::read() -> f32 template<> auto reader::read() -> f32
{ {
if (pos_ + 4 > size_) return f32{}; if (pos_ + 4 > size_)
throw std::runtime_error("reader: out of bounds");
if (!swap_) if (!swap_)
{ {
@ -198,7 +207,7 @@ auto reader::read_bytes(u32 pos, u32 count) -> std::string
for (auto i = pos; i < pos + count; i++) for (auto i = pos; i < pos + count; i++)
{ {
data += fmt::format("{:02X} ", *reinterpret_cast<u8 const*>(data_ + i)); fmt::format_to(std::back_insert_iterator(data), "{:02X} ", *reinterpret_cast<u8 const*>(data_ + i));
} }
data.pop_back(); data.pop_back();

View File

@ -33,7 +33,8 @@ auto writer::clear() -> void
template<> auto writer::write(i8 data) -> void template<> auto writer::write(i8 data) -> void
{ {
if (pos_ + 1 > size_) return; if (pos_ + 1 > size_)
throw std::runtime_error("writer: out of bounds");
*reinterpret_cast<i8*>(data_ + pos_) = data; *reinterpret_cast<i8*>(data_ + pos_) = data;
pos_ += 1; pos_ += 1;
@ -41,7 +42,8 @@ template<> auto writer::write(i8 data) -> void
template<> auto writer::write(u8 data) -> void template<> auto writer::write(u8 data) -> void
{ {
if (pos_ + 1 > size_) return; if (pos_ + 1 > size_)
throw std::runtime_error("writer: out of bounds");
*reinterpret_cast<u8*>(data_ + pos_) = data; *reinterpret_cast<u8*>(data_ + pos_) = data;
pos_ += 1; pos_ += 1;
@ -49,7 +51,8 @@ template<> auto writer::write(u8 data) -> void
template<> auto writer::write(i16 data) -> void template<> auto writer::write(i16 data) -> void
{ {
if (pos_ + 2 > size_) return; if (pos_ + 2 > size_)
throw std::runtime_error("writer: out of bounds");
if (!swap_) if (!swap_)
{ {
@ -66,7 +69,8 @@ template<> auto writer::write(i16 data) -> void
template<> auto writer::write(u16 data) -> void template<> auto writer::write(u16 data) -> void
{ {
if (pos_ + 2 > size_) return; if (pos_ + 2 > size_)
throw std::runtime_error("writer: out of bounds");
if (!swap_) if (!swap_)
{ {
@ -83,7 +87,8 @@ template<> auto writer::write(u16 data) -> void
template<> auto writer::write(i32 data) -> void template<> auto writer::write(i32 data) -> void
{ {
if (pos_ + 4 > size_) return; if (pos_ + 4 > size_)
throw std::runtime_error("writer: out of bounds");
if (!swap_) if (!swap_)
{ {
@ -102,7 +107,8 @@ template<> auto writer::write(i32 data) -> void
template<> auto writer::write(u32 data) -> void template<> auto writer::write(u32 data) -> void
{ {
if (pos_ + 4 > size_) return; if (pos_ + 4 > size_)
throw std::runtime_error("writer: out of bounds");
if (!swap_) if (!swap_)
{ {
@ -121,7 +127,8 @@ template<> auto writer::write(u32 data) -> void
template<> auto writer::write(i64 data) -> void template<> auto writer::write(i64 data) -> void
{ {
if (pos_ + 8 > size_) return; if (pos_ + 8 > size_)
throw std::runtime_error("writer: out of bounds");
if (!swap_) if (!swap_)
{ {
@ -144,7 +151,8 @@ template<> auto writer::write(i64 data) -> void
template<> auto writer::write(u64 data) -> void template<> auto writer::write(u64 data) -> void
{ {
if (pos_ + 8 > size_) return; if (pos_ + 8 > size_)
throw std::runtime_error("writer: out of bounds");
if (!swap_) if (!swap_)
{ {
@ -167,7 +175,8 @@ template<> auto writer::write(u64 data) -> void
template<> auto writer::write(f32 data) -> void template<> auto writer::write(f32 data) -> void
{ {
if (pos_ + 4 > size_) return; if (pos_ + 4 > size_)
throw std::runtime_error("writer: out of bounds");
if (!swap_) if (!swap_)
{ {
@ -186,7 +195,8 @@ template<> auto writer::write(f32 data) -> void
auto writer::write_string(std::string const& data) -> void auto writer::write_string(std::string const& data) -> void
{ {
if (pos_ + data.size() > size_) return; if (pos_ + data.size() > size_)
throw std::runtime_error("writer: out of bounds");
std::memcpy(reinterpret_cast<void*>(data_ + pos_), data.data(), data.size()); std::memcpy(reinterpret_cast<void*>(data_ + pos_), data.data(), data.size());
pos_ += static_cast<u32>(data.size()); pos_ += static_cast<u32>(data.size());
@ -194,28 +204,13 @@ auto writer::write_string(std::string const& data) -> void
auto writer::write_cstr(std::string const& data) -> void auto writer::write_cstr(std::string const& data) -> void
{ {
if (pos_ + data.size() >= size_) return; if (pos_ + data.size() >= size_)
throw std::runtime_error("writer: out of bounds");
std::memcpy(reinterpret_cast<void*>(data_ + pos_), data.data(), data.size()); std::memcpy(reinterpret_cast<void*>(data_ + pos_), data.data(), data.size());
pos_ += static_cast<u32>(data.size() + 1); pos_ += static_cast<u32>(data.size() + 1);
} }
auto writer::read_bytes(u32 pos, u32 count) -> std::string
{
auto data = std::string{};
data.reserve(count * 3);
for (auto i = pos; i < pos + count; i++)
{
data += fmt::format("{:02X} ", *reinterpret_cast<const u8*>(data_ + i));
}
data.pop_back();
return data;
}
auto writer::is_avail() -> bool auto writer::is_avail() -> bool
{ {
return (pos_ < size_) ? true : false; return (pos_ < size_) ? true : false;

View File

@ -28,7 +28,6 @@ public:
auto write(T data) -> void; auto write(T data) -> void;
auto write_string(std::string const& data) -> void; auto write_string(std::string const& data) -> void;
auto write_cstr(std::string const& data) -> void; auto write_cstr(std::string const& data) -> void;
auto read_bytes(u32 pos, u32 count) -> std::string;
auto is_avail() -> bool; auto is_avail() -> bool;
auto seek(u32 size) -> void; auto seek(u32 size) -> void;
auto seek_neg(u32 size) -> void; auto seek_neg(u32 size) -> void;