defer operand parsing to eval. also adds symbol table lookup.

This commit is contained in:
Kelvin Sherlock 2019-12-14 12:43:12 -05:00
parent 3c6be110b1
commit e7e77f4b5c
3 changed files with 177 additions and 236 deletions

162
link.cpp
View File

@ -63,6 +63,44 @@ namespace {
std::vector<omf::segment> segments;
std::vector<std::vector<pending_reloc>> relocations;
std::unordered_map<std::string, uint32_t> file_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 },
};
}
@ -120,6 +158,8 @@ namespace {
unsigned kind = 0x0000;
unsigned sav = 0;
bool end = false;
bool fas = false;
int ovr = OVR_OFF;
size_t pos_offset = 0;
size_t len_offset = 0;
@ -545,16 +585,24 @@ static bool op_after_end(opcode_t op) {
}
}
static uint32_t eval(operand_t op) {
if (std::holds_alternative<uint32_t>(op)) return std::get<uint32_t>(op);
std::string &name = std::get<std::string>(op);
auto iter = local_symbol_table.find(name);
if (iter == local_symbol_table.end()) throw std::runtime_error("Bad symbol");
return iter->second;
extern uint32_t number_operand(const char *cursor, int flags = OP_REQUIRED);
extern uint32_t number_operand(const char *cursor, const std::unordered_map<std::string, uint32_t> &, int flags = OP_REQUIRED);
extern int ovr_operand(const char *cursor);
extern std::string label_operand(const char *cursor, int flags = OP_REQUIRED);
extern std::string string_operand(const char *cursor, int flags = OP_REQUIRED);
extern std::string path_operand(const char *cursor, int flags = OP_REQUIRED);
extern void no_operand(const char *cursor);
static std::string basename(const std::string &str) {
auto ix = str.find_last_of("/:");
if (ix == str.npos) return str;
return str.substr(0, ix);
}
void evaluate(label_t label, opcode_t opcode, operand_t operand) {
void evaluate(label_t label, opcode_t opcode, const char *cursor) {
// todo - should move operand parsing to here.
@ -562,8 +610,11 @@ void evaluate(label_t label, opcode_t opcode, operand_t operand) {
case OP_DO:
if (active_bits & 0x80000000) throw std::runtime_error("too much do do");
active_bits <<= 1;
active_bits |= eval(operand) ? 1 : 0;
active = (active_bits & (active_bits + 1)) == 0;
if (active) {
uint32_t value = number_operand(cursor, local_symbol_table);
active_bits |= value ? 1 : 0;
active = (active_bits & (active_bits + 1)) == 0;
}
return;
break;
@ -623,22 +674,20 @@ void evaluate(label_t label, opcode_t opcode, operand_t operand) {
case OP_TYP:
// todo - should evaluate with file type dictionary.
ftype = eval(operand);
ftype = number_operand(cursor, file_types, OP_REQUIRED | OP_INSENSITIVE);
break;
case OP_ADR:
atype = eval(operand);
atype = number_operand(cursor, local_symbol_table);
break;
case OP_KND:
kind = eval(operand);
kind = number_operand(cursor, local_symbol_table);
break;
case OP_LKV: {
/* specify linker version */
/* 0 = binary, 1 = Linker.GS, 2 = Linker.XL, 3 = convert to OMF object file */
uint32_t value = eval(operand);
uint32_t value = number_operand(cursor, local_symbol_table);
switch (value) {
case 0: throw std::runtime_error("binary linker not supported");
case 3: throw std::runtime_error("object file linker not supported");
@ -654,7 +703,7 @@ void evaluate(label_t label, opcode_t opcode, operand_t operand) {
case OP_VER: {
/* OMF version, 1 or 2 */
uint32_t value = eval(operand);
uint32_t value = number_operand(cursor, local_symbol_table);
if (value != 2)
throw std::runtime_error("bad OMF version");
@ -662,29 +711,40 @@ void evaluate(label_t label, opcode_t opcode, operand_t operand) {
break;
}
case OP_LNK:
case OP_LNK: {
if (end) throw std::runtime_error("link after end");
//link_unit(std::get<std::string>(operand));
std::string path = path_operand(cursor);
process_unit(path);
break;
}
case OP_SAV:
case OP_SAV: {
if (end) throw std::runtime_error("save after end");
if (save_file.empty()) save_file = std::get<std::string>(operand);
/* if linker version 1, save to disk */
/* if linker version 2, finish the current segment */
if (lkv == 1) {
std::string path = path_operand(cursor);
/* use 1st SAV as the path */
if (save_file.empty()) save_file = path;
/*
lkv 0 = binary linker (unsupported)
lkv 1 = 1 segment GS linker
lkv 2 = multi-segment GS linker
lkv 3 = convert REL to OMF object file (unsupported)
*/
if (lkv == 1 || lkv == 2 || lkv == 3) {
auto &seg = segments.back();
seg.segname = std::get<std::string>(operand);
std::string base = basename(path);
seg.segname = std::move(base);
seg.kind = kind;
}
if (lkv == 1) {
finish();
end = true;
}
if (lkv == 2) {
auto &seg = segments.back();
seg.segname = std::get<std::string>(operand);
seg.kind = kind;
/* add a new segment */
segments.emplace_back();
relocations.emplace_back();
@ -692,9 +752,9 @@ void evaluate(label_t label, opcode_t opcode, operand_t operand) {
}
++sav;
break;
}
case OP_KBD: {
std::string prompt;
char buffer[256];
if (!isatty(STDIN_FILENO)) return;
@ -703,8 +763,7 @@ void evaluate(label_t label, opcode_t opcode, operand_t operand) {
if (local_symbol_table.find(label) != local_symbol_table.end())
return;
if (std::holds_alternative<std::string>(operand))
prompt = std::get<std::string>(operand);
std::string prompt = string_operand(cursor, OP_OPTIONAL);
if (prompt.empty()) prompt = "Give value for " + label;
prompt += ": ";
@ -713,12 +772,11 @@ void evaluate(label_t label, opcode_t opcode, operand_t operand) {
char *cp = fgets(buffer, sizeof(buffer), stdin);
if (!cp) return;
if (!cp) throw std::runtime_error("Bad input");
operand_t number_operand(const char *YYCURSOR, bool required);
operand_t op = number_operand(cp, true);
uint32_t value = number_operand(cp, local_symbol_table, true);
define(label, eval(op), LBL_KBD);
define(label, value, LBL_KBD);
break;
}
@ -726,10 +784,10 @@ void evaluate(label_t label, opcode_t opcode, operand_t operand) {
// POS label << sets label = current segment offset
// POS << resets pos byte counter.
if (std::holds_alternative<std::monostate>(operand)) {
std::string label = label_operand(cursor, OP_OPTIONAL);
if (label.empty()) {
pos_offset = segments.back().data.size();
} else {
std::string label = std::get<std::string>(operand);
uint32_t value = segments.back().data.size() - pos_offset;
define(label, value, LBL_POS);
@ -739,30 +797,44 @@ void evaluate(label_t label, opcode_t opcode, operand_t operand) {
case OP_LEN: {
// LEN label
// sets label = length of most recent file linked
std::string label = std::get<std::string>(operand);
std::string label = label_operand(cursor);
uint32_t value = len_offset;
define(label, value, LBL_LEN);
break;
}
case OP_EQ:
define(label, eval(operand), LBL_EQ);
define(label, number_operand(cursor, local_symbol_table), LBL_EQ);
break;
case OP_EQU:
define(label, eval(operand), LBL_EQU);
define(label, number_operand(cursor, local_symbol_table), LBL_EQU);
break;
case OP_GEQ:
define(label, eval(operand), LBL_GEQ);
define(label, number_operand(cursor, local_symbol_table), LBL_GEQ);
break;
case OP_FAS:
/* fast linker, only 1 file allowed */
fas = true;
break;
case OP_OVR:
case OP_PUT:
case OP_IF:
ovr = ovr_operand(cursor);
break;
case OP_ASM:
case OP_PUT: {
std::string path = path_operand(cursor);
break;
}
case OP_IF: {
std::string path = path_operand(cursor);
break;
}
case OP_ASM: {
std::string path = path_operand(cursor);
break;
}
default:
throw std::runtime_error("opcode not yet supported");
}

View File

@ -3,10 +3,8 @@
#include <cstdint>
#include <string>
#include <variant>
typedef std::string label_t;
typedef std::variant<std::monostate, uint32_t, std::string> operand_t;
enum opcode_t {
@ -23,4 +21,10 @@ enum {
OVR_OFF = 0
};
enum {
OP_OPTIONAL = 0,
OP_REQUIRED = 1,
OP_INSENSITIVE = 2
};
#endif

View File

@ -11,7 +11,6 @@
#include <string>
#include <unordered_map>
#include <stdexcept>
#include <variant>
#include <cctype>
#include <cstdio>
@ -51,45 +50,6 @@ namespace {
{ "=", 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 },
};
}
@ -138,7 +98,7 @@ exit:
throw std::invalid_argument("bad operand");
}
static std::string x_label_operand(const char *YYCURSOR) {
static std::string x_label_operand(const char *YYCURSOR, bool insensitive) {
const char *iter = YYCURSOR;
// const char *YYMARKER = nullptr;
std::string rv;
@ -147,6 +107,8 @@ static std::string x_label_operand(const char *YYCURSOR) {
ident {
rv = std::string(iter, YYCURSOR);
if (insensitive)
for (char &c : rv) rv = std::toupper(c);
goto exit;
}
*/
@ -177,64 +139,33 @@ exit:
throw std::invalid_argument("bad operand");
}
uint32_t number_operand(const char *YYCURSOR, const std::unordered_map<std::string, uint32_t> &map, int flags) {
/* not static - called from link.cpp */
operand_t number_operand(const char *YYCURSOR, bool required = true) {
const char *iter = YYCURSOR;
const char *cp = YYCURSOR;
// const char *YYMARKER = nullptr;
/*!re2c
* { throw std::invalid_argument("bad operand"); }
[;*] | eof {
if (!required) return 0;
throw std::invalid_argument("missing operand");
if (flags & OP_REQUIRED)
throw std::invalid_argument("missing operand");
return 0;
}
number_prefix {
return x_number_operand(iter);
return x_number_operand(cp);
}
ident_prefix { return x_label_operand(iter); }
*/
}
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");
}
number_prefix { return x_number_operand(iter); }
[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;
goto exit;
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;
}
*/
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 ovr_operand(const char *YYCURSOR) {
int rv = 0;
const char *YYMARKER = nullptr;
@ -257,49 +188,44 @@ static int ovr_operand(const char *YYCURSOR) {
throw std::invalid_argument("bad operand");
}
static std::string label_operand(const char *YYCURSOR, bool required = true) {
std::string rv;
const char *iter = YYCURSOR;
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 (!required) return rv;
throw std::invalid_argument("missing operand");
if (flags & OP_REQUIRED)
throw std::invalid_argument("missing operand");
return std::string();
}
ident {
rv = std::string(iter, YYCURSOR);
goto exit;
ident_prefix {
return x_label_operand(cp, flags & OP_INSENSITIVE);
}
*/
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 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 (!required) return rv;
throw std::invalid_argument("missing operand");
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
[^;*\x00'"][^ \t]* {
. [^ \t]* {
rv = std::string(iter, YYCURSOR);
goto exit;
}
['] [^']* ['] | ["] [^"]* ["] {
rv = std::string(iter+1, YYCURSOR-1);
goto exit;
}
*/
@ -310,8 +236,27 @@ exit:
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"); }
static void no_operand(const char *YYCURSOR) {
[;*] | 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; }
@ -319,35 +264,10 @@ static void no_operand(const char *YYCURSOR) {
}
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;
@ -398,64 +318,9 @@ operand:
while (isspace(*YYCURSOR)) ++YYCURSOR;
iter = YYCURSOR;
void evaluate(label_t label, opcode_t opcode, const char *);
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:
operand = std::string(YYCURSOR);
break;
default:
no_operand(YYCURSOR);
break;
}
void evaluate(label_t label, opcode_t opcode, operand_t operand);
evaluate(label, opcode, operand);
evaluate(label, opcode, YYCURSOR);
}