merlin-utils/script.re2c
2019-12-12 23:50:43 -05:00

515 lines
9.0 KiB
Plaintext

/* link script support */
/*
label opcode operand
*/
#include <string>
#include <unordered_map>
#include <stdexcept>
#include <variant>
#include <cctype>
#include <cstdio>
#include <cstdint>
#include <err.h>
#include "script.h"
/*!re2c
re2c:define:YYCTYPE = char;
re2c:yyfill:enable = 0;
// :-~ includes ; which interferes with comments.
ident = [:<-~][0-~]*;
ws = [ \t];
eof = "\x00";
*/
namespace {
std::unordered_map<std::string, int> opcodes = {
#define x(op) { #op, OP_##op },
#include "ops.h"
#undef x
/* aliases */
{ "AUX", OP_ADR },
{ "REZ", OP_RES },
{ "LIN", OP_LNK },
{ "KIN", OP_KND },
{ "=", OP_EQ }
};
std::unordered_map<std::string, int> types = {
{ "NON", 0x00 },
{ "BAD", 0x01 },
{ "BIN", 0x06 },
{ "TXT", 0x04 },
{ "DIR", 0x0f },
{ "ADB", 0x19 },
{ "AWP", 0x1a },
{ "ASP", 0x1b },
{ "GSB", 0xab },
{ "TDF", 0xac },
{ "BDF", 0xad },
{ "SRC", 0xb0 },
{ "OBJ", 0xb1 },
{ "LIB", 0xb2 },
{ "S16", 0xb3 },
{ "RTL", 0xb4 },
{ "EXE", 0xb5 },
{ "PIF", 0xb6 },
{ "TIF", 0xb7 },
{ "NDA", 0xb8 },
{ "CDA", 0xb9 },
{ "TOL", 0xba },
{ "DRV", 0xbb },
{ "DOC", 0xbf },
{ "PNT", 0xc0 },
{ "PIC", 0xc1 },
{ "FON", 0xcb },
{ "PAS", 0xef },
{ "CMD", 0xf0 },
{ "LNK", 0xf8 },
{ "BAS", 0xfc },
{ "VAR", 0xfd },
{ "REL", 0xfe },
{ "SYS", 0xff },
};
}
static uint32_t number_operand(const char *YYCURSOR, bool required = true) {
const char *iter = YYCURSOR;
const char *YYMARKER = nullptr;
uint32_t rv = 0;
/*!re2c
* { throw std::invalid_argument("bad operand"); }
[;*] | eof {
if (!required) return rv;
throw std::invalid_argument("missing operand");
}
'%' [01]+ {
++iter;
for(;iter < YYCURSOR; ++iter) {
rv <<= 1;
rv |= *iter - '0';
}
goto exit;
}
'$' [A-Fa-f0-9]+ {
++iter;
for(;iter < YYCURSOR; ++iter) {
char c = *iter | 0x20;
rv <<= 4;
if (c <= '9') rv |= c - '0';
else rv |= c - 'a' + 10;
}
goto exit;
}
[0-9]+ {
for(;iter < YYCURSOR; ++iter) {
rv *= 10;
rv += *iter - '0';
}
goto exit;
}
ident {
std::string s(iter, YYCURSOR);
//look up symbol, verify it's an absolute value, etc
}
*/
exit:
char c = *YYCURSOR;
if (isspace(c) || c == 0) return rv;
throw std::invalid_argument("bad operand");
}
static uint32_t type_operand(const char *YYCURSOR, bool required = true) {
const char *iter = YYCURSOR;
const char *YYMARKER = nullptr;
uint32_t rv = 0;
/*!re2c
* { throw std::invalid_argument("bad operand"); }
[;*] | eof {
if (!required) return rv;
throw std::invalid_argument("missing operand");
}
'%' [01]+ {
++iter;
for(;iter < YYCURSOR; ++iter) {
rv <<= 1;
rv |= *iter - '0';
}
goto exit;
}
'$' [A-Fa-f0-9]+ {
++iter;
for(;iter < YYCURSOR; ++iter) {
char c = *iter | 0x20;
rv <<= 4;
if (c <= '9') rv |= c - '0';
else rv |= c - 'a' + 10;
}
goto exit;
}
[0-9]+ {
for(;iter < YYCURSOR; ++iter) {
rv *= 10;
rv += *iter - '0';
}
goto exit;
}
[A-Za-z][A-Za-z0-9]{2} {
std::string s(iter, YYCURSOR);
for(char &c : s) c = std::toupper(c);
auto iter = types.find(s);
if (iter == types.end()) {
throw std::invalid_argument("bad operand");
}
rv = iter->second;
}
*/
exit:
char c = *YYCURSOR;
if (isspace(c) || c == 0) return rv;
throw std::invalid_argument("bad operand");
}
static int x_number_operand(const char *YYCURSOR) {
const char *iter = YYCURSOR;
const char *YYMARKER = nullptr;
uint32_t rv = 0;
/*!re2c
* { throw std::invalid_argument("bad operand"); }
'%' [01]+ {
++iter;
for(;iter < YYCURSOR; ++iter) {
rv <<= 1;
rv |= *iter - '0';
}
goto exit;
}
'$' [A-Fa-f0-9]+ {
++iter;
for(;iter < YYCURSOR; ++iter) {
char c = *iter | 0x20;
rv <<= 4;
if (c <= '9') rv |= c - '0';
else rv |= c - 'a' + 10;
}
goto exit;
}
[0-9]+ {
for(;iter < YYCURSOR; ++iter) {
rv *= 10;
rv += *iter - '0';
}
goto exit;
}
*/
exit:
char c = *YYCURSOR;
if (isspace(c) || c == 0) return rv;
throw std::invalid_argument("bad operand");
}
static std::string x_label_operand(const char *YYCURSOR) {
const char *iter = YYCURSOR;
const char *YYMARKER = nullptr;
std::string rv;
/*!re2c
* { throw std::invalid_argument("bad operand"); }
ident {
std::string s(iter, YYCURSOR);
goto exit;
}
*/
exit:
char c = *YYCURSOR;
if (isspace(c) || c == 0) {
//look up symbol, verify it's an absolute value, etc
return rv;
}
throw std::invalid_argument("bad operand");
}
static std::string x_string_operand(const char *YYCURSOR) {
const char *iter = YYCURSOR;
std::string rv;
/*!re2c
* { throw std::invalid_argument("bad operand"); }
['] [^']* ['] | ["] [^"]* ["] {
rv = std::string(iter+1, YYCURSOR-1);
goto exit;
}
*/
exit:
char c = *YYCURSOR;
if (isspace(c) || c == 0) return rv;
throw std::invalid_argument("bad operand");
}
static int ovr_operand(const char *YYCURSOR) {
int rv = 0;
const char *YYMARKER = nullptr;
/*!re2c
* { throw std::invalid_argument("bad operand"); }
[;*] | eof {
return OVR_NONE;
}
'ALL' {
rv = OVR_ALL;
}
'OFF' {
rv = OVR_OFF;
}
*/
char c = *YYCURSOR;
if (isspace(c) || c == 0) return rv;
throw std::invalid_argument("bad operand");
}
static std::string label_operand(const char *YYCURSOR, bool required = true) {
std::string rv;
const char *iter = YYCURSOR;
const char *YYMARKER = nullptr;
/*!re2c
* { throw std::invalid_argument("bad operand"); }
[;*] | eof {
if (!required) return rv;
throw std::invalid_argument("missing operand");
}
ident {
rv = std::string(iter, YYCURSOR);
goto exit;
}
*/
exit:
char c = *YYCURSOR;
if (isspace(c) || c == 0) return rv;
throw std::invalid_argument("bad operand");
}
static std::string path_operand(const char *YYCURSOR, bool required = true) {
std::string rv;
const char *iter = YYCURSOR;
const char *YYMARKER = nullptr;
/*!re2c
* { throw std::invalid_argument("bad operand"); }
[;*] | eof {
if (!required) return rv;
throw std::invalid_argument("missing operand");
}
// don't allow leading quotes, eof, or comment chars
[^;*\x00'"][^ \t]* {
rv = std::string(iter, YYCURSOR);
goto exit;
}
['] [^']* ['] | ["] [^"]* ["] {
rv = std::string(iter+1, YYCURSOR-1);
goto exit;
}
*/
exit:
char c = *YYCURSOR;
if (isspace(c) || c == 0) return rv;
throw std::invalid_argument("bad operand");
}
static void no_operand(const char *YYCURSOR) {
/*!re2c
* { throw std::invalid_argument("bad operand"); }
[;*] | eof { return; }
*/
}
static std::string string_operand(const char *YYCURSOR, bool required = true) {
std::string rv;
const char *iter = YYCURSOR;
const char *YYMARKER = nullptr;
/*!re2c
* { throw std::invalid_argument("bad operand"); }
[;*] | eof {
if (!required) return rv;
throw std::invalid_argument("missing operand");
}
['] [^']* ['] | ["] [^"]* ["] {
rv = std::string(iter+1, YYCURSOR-1);
goto exit;
}
*/
exit:
char c = *YYCURSOR;
if (isspace(c) || c == 0) return rv;
throw std::invalid_argument("bad operand");
}
void parse_line(const char *YYCURSOR) {
label_t label;
opcode_t opcode = OP_NONE;
operand_t operand;
const char *iter = YYCURSOR;
const char *YYMARKER = nullptr;
/*!re2c
* { throw std::invalid_argument("bad label"); }
[;*] | eof {
return;
}
ws { goto opcode; }
ident / (ws|eof) {
label = std::string(iter, YYCURSOR);
goto opcode;
}
*/
opcode:
while (isspace(*YYCURSOR)) ++YYCURSOR;
iter = YYCURSOR;
/*!re2c
* { throw std::invalid_argument("bad opcode"); }
[;*]|eof { return; }
'=' / (ws|eof) { opcode = OP_EQ; goto operand; }
[A-Za-z]+ / (ws|eof) {
size_t l = YYCURSOR - iter;
if (l > 3) l = 3;
std::string s(iter, iter + l);
for (char &c : s) c = std::toupper(c);
auto iter = opcodes.find(s);
if (iter == opcodes.end()) {
throw std::invalid_argument("bad opcode");
}
opcode = iter->second;
goto operand;
}
*/
operand:
while (isspace(*YYCURSOR)) ++YYCURSOR;
iter = YYCURSOR;
switch(opcode) {
case OP_LNK:
case OP_PUT:
case OP_ASM:
case OP_SAV:
case OP_LIB:
case OP_IF:
case OP_PFX:
case OP_IMP:
case OP_RES:
case OP_FIL:
operand = path_operand(YYCURSOR);
break;
case OP_ORG:
case OP_ADR:
case OP_DS:
case OP_KND:
case OP_VER:
case OP_ALI:
case OP_LKV:
case OP_DO:
case OP_EQU:
case OP_EQ:
case OP_GEQ:
operand = number_operand(YYCURSOR);
break;
case OP_TYP:
operand = type_operand(YYCURSOR);
break;
case OP_OVR:
operand = ovr_operand(YYCURSOR);
break;
case OP_POS:
case OP_LEN: {
std::string tmp = label_operand(YYCURSOR, false);
if (!tmp.empty()) operand = std::move(tmp);
break;
}
case OP_KBD: {
std::string tmp = string_operand(YYCURSOR, false);
if (!tmp.empty()) operand = std::move(tmp);
break;
}
case OP_CMD:
str_operand = std::string(YYCURSOR);
break;
default:
no_operand(YYCURSOR);
break;
}
void evaluate(label_t label, opcode_t opcode, operand_t operand);
evaluate(label, opcode, opcode);
}