merlin-utils/script.re2c
Kelvin Sherlock a4dfcec1f1 WIP
2019-12-09 13:02:15 -05:00

634 lines
11 KiB
Plaintext

/* link script support */
/*
label opcode operand
*/
#include <string>
#include <unordered_map>
#include <stdexcept>
#include <cctype>
#include <cstdio>
#include <cstdint>
#include <err.h>
/*!re2c
re2c:define:YYCTYPE = char;
re2c:yyfill:enable = 0;
// :-~ includes ; which interferes with comments.
ident = [:<-~][0-~]*;
ws = [ \t];
eof = "\x00";
*/
enum {
CMD_NONE = 0,
#define x(op) CMD_##op,
#include "ops.h"
#undef x
CMD_EQ
};
static std::unordered_map<std::string, int> commands = {
#define x(op) { #op, CMD_##op },
#include "ops.h"
#undef x
/* aliases */
{ "AUX", CMD_ADR },
{ "REZ", CMD_RES },
{ "LIN", CMD_LNK },
{ "KIN", CMD_KND },
{ "=", CMD_EQ }
};
static 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) {
char *iter = YYCURSOR;
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) {
char *iter = YYCURSOR;
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;
}
*/
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) {
char *iter = YYCURSOR;
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) {
char *iter = YYCURSOR;
std::string rv;
/*!re2c
* { throw std::invalid_argument("bad operand"); }
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 std::string x_string_operand(const char *YYCURSOR) {
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;
/*!re2c
* { throw std::invalid_argument("bad operand"); }
[;*] | eof {
return 1;
}
'ALL' {
rv = -1;
}
'OFF' {
rv = 0;
}
*/
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;
char *iter = YYCURSOR;
/*!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;
}
*/
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;
char *iter = YYCURSOR;
/*!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;
}
*/
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;
char *iter = YYCURSOR;
/*!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;
}
*/
char c = *YYCURSOR;
if (isspace(c) || c == 0) return rv;
throw std::invalid_argument("bad operand");
}
static void parse_line(const char *YYCURSOR) {
unsigned cmd = 0;
std::string label;
const char *iter = YYCURSOR;
/*!re2c
* { throw std::invalid_argument("bad label") }
[;*] | eof {
return;
}
ws { goto opcode }
ident / (ws|eof) {
label(iter, YYCURSOR);
goto opcode;
}
*/
opcode:
while (isspace(*YYCURSOR)) ++YYCURSOR;
iter = YYCURSOR;
/*!re2c
* { throw std::invalid_argument("bad opcode"); }
[;*]|eof { return 0; }
'=' / (ws|eof) { cmd = CMD_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 in s): c = std::toupper(c);
auto iter = commands.find(s);
if (!iter == commands.end()) {
throw std::invalid_argument("bad opcode");
}
cmd = *iter;
goto operand;
}
*/
operand:
while (isspace(*YYCURSOR)) ++YYCURSOR;
iter = YYCURSOR;
std::string str_operand;
long int_operand;
switch(cmd) {
case CMD_LNK:
case CMD_PUT:
case CMD_ASM:
case CMD_SAV:
case CMD_LIB:
case CMD_IF:
case CMD_PFX:
case CMD_IMP:
case CMD_RES:
case CMD_FIL:
str_operand = path_operand(YYCURSOR);
break;
case CMD_ORG:
case CMD_ADR:
case CMD_DS:
case CMD_KND:
case CMD_VER:
case CMD_ALI:
case CMD_LKV:
case CMD_DO:
case CMD_EQU:
case CMD_EQ:
case CMD_GEQ:
int_operand = number_operand(YYCURSOR);
break;
case CMD_TYP:
int_operand = type_operand(YYCURSOR);
break;
case CMD_OVR:
int_operand = ovr_operand(YYCURSOR);
break;
case CMD_POS:
case CMD_LEN:
str_operand = label_operand(YYCURSOR, false);
break;
case CMD_KBD:
str_operand = string_operand(YYCURSOR, false);
break;
case CMD_CMD:
str_operand = string(YYCURSOR);
break;
default:
no_operand(YYCURSOR);
break;
}
switch(cmd) {
case CMD_NONE:
case CMD_NOL: /* asm: default list off */
case CMD_PUT: /* check if source file is outdated */
case CMD_OVR: /* override outdated check and force assembly */
case CMD_FAS: /* fast linker. only one SAV allowed */
/* nop */
break;
case CMD_LNK:
/* link file ... */
break;
case CMD_ORG:
/* set the link origin. also used as aux type */
break;
case CMD_ADR:
/* set the file aux type */
break;
case CMD_SAV:
/* save linked file. for xl linker, specifies segment name */
break;
case CMD_TYP:
/* set the file type */
break;
case CMD_EXT:
/* print addresses of all resolved externals (not just errors)
disabled after each SAV
*/
break;
case CMD_ENT:
/* print the entry list */
/* flag symbol w ? if unused */
break;
case CMD_DAT:
/* print the current date and time */
break;
case CMD_END:
/* end of command file (optional) */
break;
case CMD_LIB:
/* search directory for unresolved symbols */
break;
case CMD_LKV:
/* specify linker version */
/* 0 = binary, 1 = Linker.GS, 2 = Linker.XL, 3 = convert to OMF object file */
switch (int_operand) {
case 0: throw std::runtime_error("binary linker not supported");
case 3: throw std::runtime_error("object file linker not supported");
case 1:
case 2:
/* default file type = S16 */
break;
default:
throw std::runtime_error("bad linker version");
}
break;
case CMD_VER:
/*specify OMF version. 1 or 2 */
break;
case CMD_KND:
/* set the OMF kind flag */
/* 8-bit for v 1, 16-bit for v2 */
break;
case CMD_ALI:
/* OMF align field. default = 0 */
break;
case CMD_DS:
/* OMF RESSPC field */
break;
case CMD_LEN:
/* Puts "LABEL" in symbol dictionary a san ENTry whose value is equal to the number of bytes of the last linked file. */
break;
case CMD_POS:
case CMD_ASM:
default:
throw std::runtime_error("opcode not supported");
}
}
int process_link_file(const std::string &path) {
FILE *fp;
fp = fopen(path, "r");
if (!fp) {
warn("Unable to open %s", path.c_str());
return -1;
}
int no = 1;
int errors = 0;
const char *line = NULL;
size_t cap = 0;
for(;; ++no) {
ssize_t len = getline(&line, &cap, fp);
if (len == 0) break;
if (len < 0) {
warn("read error");
++errors;
break;
}
/* strip trailing ws */
while (len && isspace(line[len-1])) --len;
line[len] = 0;
if (len == 0) continue;
try {
parse_line(line);
} catch (std::exception &ex) {
fprintf(stderr, "%s in line: %d\n", ex.what(), no);
fprintf(stderr, "%s\n", line);
if (++errors >= 10) {
fputs("Too many errors, aborting\n", stderr);
break;
}
}
}
fclose(fp);
free(line);
return errors ? -1 : 0;
}