mirror of
https://github.com/ksherlock/merlin-utils.git
synced 2024-12-26 06:29:34 +00:00
325 lines
5.8 KiB
Plaintext
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);
|
|
}
|
|
|
|
|
|
|