/* link script support */ /* label opcode operand */ #include #include #include #include #include #include #include #include #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 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 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: 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); }