merlin-utils/script.re2c

325 lines
5.8 KiB
Plaintext

/* link script support */
/*
label opcode operand
*/
#include <string>
#include <unordered_map>
#include <stdexcept>
#include <cctype>
#include <cstdint>
#include "script.h"
/*!re2c
re2c:define:YYCTYPE = char;
re2c:yyfill:enable = 0;
// :-~ includes ; which interferes with comments.
ident = [:<-~][0-~]*;
ws = [ \t];
eof = "\x00";
number_prefix = [%$0-9];
ident_prefix = [:-~];
string_prefix = ['"];
*/
namespace {
std::unordered_map<std::string, opcode_t> 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 }
};
}
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, bool insensitive) {
const char *iter = YYCURSOR;
// const char *YYMARKER = nullptr;
std::string rv;
/*!re2c
* { throw std::invalid_argument("bad operand"); }
ident {
rv = std::string(iter, YYCURSOR);
if (insensitive)
for (char &c : rv) rv = std::toupper(c);
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");
}
uint32_t number_operand(const char *YYCURSOR, const std::unordered_map<std::string, uint32_t> &map, int flags) {
const char *cp = YYCURSOR;
// const char *YYMARKER = nullptr;
/*!re2c
* { throw std::invalid_argument("bad operand"); }
[;] | eof {
if (flags & OP_REQUIRED)
throw std::invalid_argument("missing operand");
return 0;
}
number_prefix {
return x_number_operand(cp);
}
ident_prefix {
std::string s = x_label_operand(cp, flags & OP_INSENSITIVE);
auto iter = map.find(s);
if (iter == map.end()) throw std::runtime_error("Bad symbol");
return iter->second;
}
*/
}
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");
}
std::string label_operand(const char *YYCURSOR, int flags) {
const char *cp = YYCURSOR;
// const char *YYMARKER = nullptr;
/*!re2c
* { throw std::invalid_argument("bad operand"); }
[;] | eof {
if (flags & OP_REQUIRED)
throw std::invalid_argument("missing operand");
return std::string();
}
ident_prefix {
return x_label_operand(cp, flags & OP_INSENSITIVE);
}
*/
}
std::string path_operand(const char *YYCURSOR, int flags) {
std::string rv;
const char *iter = YYCURSOR;
// const char *YYMARKER = nullptr;
/*!re2c
* { throw std::invalid_argument("bad operand"); }
[;] | eof {
if (flags & OP_REQUIRED)
throw std::invalid_argument("missing operand");
return rv;
}
string_prefix {
return x_string_operand(iter);
}
// don't allow leading quotes, eof, or comment chars
[^ \t\x00;*'"][^ \t\x00]* {
rv = std::string(iter, YYCURSOR);
goto exit;
}
*/
exit:
char c = *YYCURSOR;
if (isspace(c) || c == 0) return rv;
throw std::invalid_argument("bad operand");
}
std::string string_operand(const char *YYCURSOR, int flags) {
const char *cp = YYCURSOR;
// const char *YYMARKER = nullptr;
/*!re2c
* { throw std::invalid_argument("bad operand"); }
[;] | eof {
if (flags & OP_REQUIRED)
throw std::invalid_argument("missing operand");
return std::string();
}
string_prefix {
return x_string_operand(cp);
}
*/
}
void no_operand(const char *YYCURSOR) {
/*!re2c
* { throw std::invalid_argument("bad operand"); }
[;] | eof { return; }
*/
}
void parse_line(const char *YYCURSOR) {
label_t label;
opcode_t opcode = OP_NONE;
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;
void evaluate(label_t label, opcode_t opcode, const char *);
evaluate(label, opcode, YYCURSOR);
}