?login_element?

Subversion Repositories NedoOS

Rev

Rev 539 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. /*
  2.  
  3.   SjASMPlus Z80 Cross Compiler
  4.  
  5.   This is modified source of SjASM by Aprisobal - aprisobal@tut.by
  6.  
  7.   Copyright (c) 2006 Sjoerd Mastijn
  8.  
  9.   This software is provided 'as-is', without any express or implied warranty.
  10.   In no event will the authors be held liable for any damages arising from the
  11.   use of this software.
  12.  
  13.   Permission is granted to anyone to use this software for any purpose,
  14.   including commercial applications, and to alter it and redistribute it freely,
  15.   subject to the following restrictions:
  16.  
  17.   1. The origin of this software must not be misrepresented; you must not claim
  18.          that you wrote the original software. If you use this software in a product,
  19.          an acknowledgment in the product documentation would be appreciated but is
  20.          not required.
  21.  
  22.   2. Altered source versions must be plainly marked as such, and must not be
  23.          misrepresented as being the original software.
  24.  
  25.   3. This notice may not be removed or altered from any source distribution.
  26.  
  27. */
  28.  
  29. // reader.cpp
  30.  
  31. #include "sjdefs.h"
  32.  
  33. //enum EDelimiterType          { DT_NONE, DT_QUOTES, DT_APOSTROPHE, DT_ANGLE, DT_COUNT };
  34. static const char delimiters_b[] = { ' ',    '"',       '\'',          '<',      0 };
  35. static const char delimiters_e[] = { ' ',    '"',       '\'',          '>',      0 };
  36. static const std::array<EDelimiterType, 3> delimiters_all = {DT_QUOTES, DT_APOSTROPHE, DT_ANGLE};
  37. static const std::array<EDelimiterType, 3> delimiters_noAngle = {DT_QUOTES, DT_APOSTROPHE, DT_COUNT};
  38.  
  39. int cmphstr(char*& p1, const char* p2, bool allowParenthesisEnd) {
  40.         unsigned int i = 0;
  41.         // check initial non-alpha chars without deciding the upper/lower case of test
  42.         while (p2[i] && !isalpha((byte)p2[i])) {
  43.                 if (p1[i] != p2[i]) return 0;
  44.                 ++i;
  45.         }
  46.         // now the first alpha char will make whole test upper or lower case like.
  47.         if (isupper((byte)p1[i])) {
  48.                 while (p2[i]) {
  49.                         if (p1[i] != toupper((byte)p2[i])) return 0;
  50.                         ++i;
  51.                 }
  52.         } else {
  53.                 while (p2[i]) {
  54.                         if (p1[i] != p2[i]) return 0;
  55.                         ++i;
  56.                 }
  57.         }
  58.         if (p1[i]) {            // there is some character after the first word
  59.                 // whitespace, EOL-comment and block-comment-start keep the match valid
  60.                 // also starting parenthesis when allowParenthesisEnd
  61.                 if (
  62.                         !White(p1[i]) && \
  63.                         !(';' == p1[i]) && \
  64.                         !('/' == p1[i] && '/' == p1[i+1]) && \
  65.                         !('/' == p1[i] && '*' == p1[i+1]) && \
  66.                         !(allowParenthesisEnd && '(' == p1[i])
  67.                 ) {
  68.                         return 0;       // anything else invalidates the found match
  69.                 }
  70.         }
  71.         // space, tab, enter, \0, ... => "match"
  72.         p1 += i;
  73.         return 1;
  74. }
  75.  
  76. bool White(const char c) {
  77.         return c && (c&255) <= ' ';
  78. }
  79.  
  80. bool White() {
  81.         return White(*lp);
  82. }
  83.  
  84. int SkipBlanks(char*& p) {
  85.         while (White(*p)) ++p;
  86.         return (*p == 0);
  87. }
  88.  
  89. int SkipBlanks() {
  90.         return SkipBlanks(lp);
  91. }
  92.  
  93. void SkipParam(char*& p) {
  94.         while (*p && (*p != ',')) ++p;
  95. }
  96.  
  97. void SkipToEol(char*& p) {
  98.         while (*p) ++p;
  99. }
  100.  
  101. int NeedEQU() {
  102.         char* olp = lp;
  103.         SkipBlanks();
  104.         /*if (*lp=='=') { ++lp; return 1; }*/
  105.         /* cut: if (*lp=='=') { ++lp; return 1; } */
  106.         if (*lp == '.') {
  107.                 ++lp;
  108.         }
  109.         if (cmphstr(lp, "equ")) {
  110.                 return 1;
  111.         }
  112.         lp = olp;
  113.         return 0;
  114. }
  115.  
  116. int NeedDEFL() {
  117.         char* olp = lp;
  118.         SkipBlanks();
  119.         if (*lp == '=') {
  120.                 ++lp;
  121.                 return 1;
  122.         }
  123.         if (*lp == '.') {
  124.                 ++lp;
  125.         }
  126.         if (cmphstr(lp, "defl")) {
  127.                 return 1;
  128.         }
  129.         lp = olp;
  130.         return 0;
  131. }
  132.  
  133. bool NeedIoC() {
  134.         SkipBlanks();
  135.         if ('(' != lp[0] || 'c' != tolower((byte)lp[1]) || ')' != lp[2]) return false;
  136.         lp += 3;
  137.         return true;
  138. }
  139.  
  140. bool isMacroNext() {    // checks if ".macro" directive is ahead (but doesn't consume it)
  141.         if (SkipBlanks()) return false;
  142.         char* p = lp;
  143.         if ('.' == *p) ++p;
  144.         return cmphstr(p, "macro");
  145. }
  146.  
  147. bool anyComma(char*& p) {
  148.         SkipBlanks(p);
  149.         if (*p != ',') return false;
  150.         ++p;
  151.         return true;
  152. }
  153.  
  154. bool comma(char*& p) {
  155.         SkipBlanks(p);
  156.         if (',' != p[0] || ',' == p[1]) return false;   // detect double-comma as FALSE state
  157.         ++p;
  158.         return true;
  159. }
  160.  
  161. bool doubleComma(char* & p) {
  162.         SkipBlanks(p);
  163.         if (',' != p[0] || ',' != p[1]) return false;
  164.         p += 2;
  165.         return true;
  166. }
  167.  
  168. bool nonMaComma(char* & p) {
  169.         if (Options::syx.isMultiArgPlainComma()) return false;  // comma is also multi-arg => FALSE here
  170.         return comma(p);
  171. }
  172.  
  173. bool relaxedMaComma(char* & p) {
  174.         SkipBlanks(p);
  175.         if (',' != p[0]) return false;          // no comma
  176.         if (',' == p[1]) {
  177.                 // double comma detected, accept it only in --syntax=a mode
  178.                 if (Options::syx.isMultiArgPlainComma()) return false;
  179.                 p += 2;
  180.                 return true;
  181.         }
  182.         // single comma is enough in relaxed way, even in --syntax=a mode
  183.         ++p;
  184.         return true;
  185. }
  186.  
  187. //enum EBracketType          { BT_NONE, BT_ROUND, BT_CURLY, BT_SQUARE, BT_COUNT };
  188. static const char brackets_b[] = { 0,      '(',      '{',      '[',       0 };
  189. static const char brackets_e[] = { 0,      ')',      '}',      ']',       0 };
  190. static int expectedAddressClosingBracket = -1;
  191.  
  192. // memory-address bracket opener (only "(" and "[" types supported)
  193. EBracketType OpenBracket(char*& p) {
  194.         SkipBlanks(p);
  195.         if (2 == Options::syx.MemoryBrackets && brackets_b[BT_ROUND] == *p) return BT_NONE;             // disabled "()"
  196.         for (const EBracketType bt : {BT_ROUND, BT_SQUARE}) {
  197.                 if (brackets_b[bt] == *p) {
  198.                         expectedAddressClosingBracket = brackets_e[bt];
  199.                         ++p;
  200.                         return bt;
  201.                 }
  202.         }
  203.         expectedAddressClosingBracket = -1;
  204.         return BT_NONE;
  205. }
  206.  
  207. int CloseBracket(char*& p) {
  208.         SkipBlanks(p);
  209.         if (*p != expectedAddressClosingBracket) return 0;
  210.         expectedAddressClosingBracket = -1;
  211.         ++p;
  212.         return 1;
  213. }
  214.  
  215. char* ParenthesesEnd(char* p) {
  216.         int depth = 0;  char pc;
  217.         if (SkipBlanks(p) || '(' != *p) return nullptr;
  218.         while (0 != (pc = *p++)) {
  219.                 if ('(' == pc) ++depth;
  220.                 else if (')' == pc && 0 == --depth) {
  221.                         SkipBlanks(p);
  222.                         return p;
  223.                 }
  224.         }
  225.         return nullptr;
  226. }
  227.  
  228. char nidtemp[LINEMAX], *nidsubp = nidtemp;
  229.  
  230. //TODO v2.x: review GetID usage and make it more clear where are which characters legal
  231. // add GetLabel where appropriate, handle "@" + "." modifiers more consistently and transparently)
  232. char* GetID(char*& p) {
  233.         char* np = nidtemp;
  234.         if (SkipBlanks(p) || (!isLabelStart(p, false) && *p != '.')) return nullptr;
  235.         while (islabchar((byte)*p)) *np++ = *p++;
  236.         *np = 0;
  237.         return nidtemp;
  238. }
  239.  
  240. void ResetGrowSubId() {
  241.         nidsubp = nidtemp;                      // reset temporary ID, starting new one
  242.         *nidsubp = 0;
  243. }
  244.  
  245. char* GrowSubId(char* & p) {    // appends next part of ID
  246.         // The caller function ReplaceDefineInternal already assures the first char of ID is (isalpha() || '_')
  247.         // so there are no extra tests here to verify validity of first character (like GetID(..) must do)
  248.         const bool isSubwordSubstitution = Options::syx.IsSubwordSubstitution;  // help compiler -O2
  249.         bool startsAtUnderscore = ('_' == *p);
  250.         // add sub-parts delimiter in separate step (i.e. new ID grows like: "a", "a_", "a_b", ...)
  251.         while (islabchar(*p)) {
  252.                 *nidsubp++ = *p++;
  253.                 // break at sub-word boundaries when new underscore block starts or ends
  254.                 if (isSubwordSubstitution && (('_' == *p) != startsAtUnderscore)) break;
  255.         }
  256.         if (nidtemp+LINEMAX <= nidsubp) Error("ID too long, buffer overflow detected.", NULL, FATAL);
  257.         *nidsubp = 0;
  258.         return nidtemp[0] ? nidtemp : nullptr;  // return non-empty string or nullptr
  259. }
  260.  
  261. char* GrowSubIdByExtraChar(char* & p) { // append the next char even if not a legal label/ID char
  262.         // the caller function is responsible for all the validation, this just adds single char
  263.         *nidsubp++ = *p++;
  264.         if (nidtemp+LINEMAX <= nidsubp) Error("ID too long, buffer overflow detected.", NULL, FATAL);
  265.         *nidsubp = 0;
  266.         if (!nidtemp[0]) return NULL;   // result is empty string, return NULL rather
  267.         return nidtemp;
  268. }
  269.  
  270. char instrtemp[LINEMAX];
  271.  
  272. char* getinstr(char*& p) {
  273.         char* np = instrtemp;
  274.         SkipBlanks(p);
  275.         if (!isalpha((byte)*p) && *p != '.') {
  276.                 return 0;
  277.         } else {
  278.                 *np = *p; ++p; ++np;
  279.         }
  280.         while (*p) {
  281.                 if (!isalnum((byte)*p) && *p != '_') {
  282.                         break;
  283.                 } /////////////////////////////////////
  284.                 *np = *p; ++p; ++np;
  285.         }
  286.         *np = 0;
  287.         // colon after instruction can't happen, only if it was meant as label at beginning of line
  288.         if (':' == *p) return nullptr;
  289.         if (!Options::syx.CaseInsensitiveInstructions) return instrtemp;
  290.         // lowercase the retrieved "instruction" string when option "--syntax=i" is used
  291.         while (instrtemp <= --np) {
  292.                 *np = tolower((byte)*np);
  293.         }
  294.         return instrtemp;
  295. }
  296.  
  297. int check8(aint val) {
  298.         if (val < -256 || val > 255) {
  299.                 char buffer[64];
  300.                 sprintf(buffer, "value 0x%X is truncated to 8bit value: 0x%02X", val, val&0xFF);
  301.                 Warning(buffer);
  302.                 return 0;
  303.         }
  304.         return 1;
  305. }
  306.  
  307. int check8o(aint val)
  308. {
  309.         if (val < -128 || val > 127) {
  310.                 char buffer[32];
  311.                 sprintf(buffer,"Offset out of range (%+i)", val);
  312.                 Error(buffer, nullptr, IF_FIRST);
  313.                 return 0;
  314.         }
  315.         return 1;
  316. }
  317.  
  318. int check16(aint val) {
  319.         if (val < -65536 || val > 65535) {
  320.                 char buffer[64];
  321.                 sprintf(buffer, "value 0x%X is truncated to 16bit value: 0x%04X", val, val&0xFFFF);
  322.                 Warning(buffer);
  323.                 return 0;
  324.         }
  325.         return 1;
  326. }
  327.  
  328. int check16u(aint val) {
  329.         if (val < 0 || val > 65535) {
  330.                 char buffer[64];
  331.                 sprintf(buffer, "value 0x%X is truncated to 16bit value: 0x%04X", val, val&0xFFFF);
  332.                 Warning(buffer);
  333.                 return 0;
  334.         }
  335.         return 1;
  336. }
  337.  
  338. int check24(aint val) {
  339.         if (val < -16777216 || val > 16777215) {
  340.                 char buffer[64];
  341.                 sprintf(buffer, "value 0x%X is truncated to 24bit value: 0x%06X", val, val&0xFFFFFF);
  342.                 Warning(buffer);
  343.                 return 0;
  344.         }
  345.         return 1;
  346. }
  347.  
  348. void checkLowMemory(byte hiByte, byte lowByte) {
  349.         if (hiByte || Relocation::type) return;
  350.         // for addresses 0..255 issue warning
  351.         WarningById(W_READ_LOW_MEM, lowByte);
  352. }
  353.  
  354. int need(char*& p, char c) {
  355.         SkipBlanks(p);
  356.         if (*p != c) {
  357.                 return 0;
  358.         }
  359.         ++p; return 1;
  360. }
  361.  
  362. int needa(char*& p, const char* c1, int r1, const char* c2, int r2, const char* c3, int r3, bool allowParenthesisEnd) {
  363.         //  SkipBlanks(p);
  364.         if (!isalpha((byte)*p)) {
  365.                 return 0;
  366.         }
  367.         if (cmphstr(p, c1, allowParenthesisEnd)) {
  368.                 return r1;
  369.         }
  370.         if (c2 && cmphstr(p, c2, allowParenthesisEnd)) {
  371.                 return r2;
  372.         }
  373.         if (c3 && cmphstr(p, c3, allowParenthesisEnd)) {
  374.                 return r3;
  375.         }
  376.         return 0;
  377. }
  378.  
  379. int need(char*& p, const char* c) {
  380.         SkipBlanks(p);
  381.         while (*c) {
  382.                 if (*p != *c) {
  383.                         c += 2; continue;
  384.                 }
  385.                 ++c;
  386.                 if (*c == ' ') {
  387.                         ++p; return *(c - 1);
  388.                 }
  389.                 if (*c == '_' && *(p + 1) != *(c - 1)) {
  390.                         ++p; return *(c - 1);
  391.                 }
  392.                 if (*(p + 1) == *c) {
  393.                         p += 2; return *(c - 1) + *c;
  394.                 }
  395.                 ++c;
  396.         }
  397.         return 0;
  398. }
  399.  
  400. int getval(int p) {
  401.         assert(('0' <= p && p <= '9') || ('A' <= p && p <= 'Z') || ('a' <= p && p <= 'z'));
  402.         if (p <= '9') return p - '0';
  403.         return (p|0x20) - 'a' + 10;
  404. }
  405.  
  406. const char* getNumericValueLastErr = NULL;
  407. const char* const getNumericValueErr_syntax = "Syntax error";
  408. const char* const getNumericValueErr_digit = "Digit not in base";
  409. const char* const getNumericValueErr_no_digit = "Missing next digit";
  410. const char* const getNumericValueErr_overflow = "Overflow";
  411.  
  412. bool GetNumericValue_ProcessLastError(const char* const srcLine) {
  413.         if (NULL == getNumericValueLastErr) return false;
  414.         Error(getNumericValueLastErr, srcLine, SUPPRESS);
  415.         // Overflow type error lets assembler to emit truncated machine code (return "false" here)
  416.         return (getNumericValueErr_overflow != getNumericValueLastErr);
  417. }
  418.  
  419. bool GetNumericValue_TwoBased(char*& p, const char* const pend, aint& val, const int shiftBase) {
  420.         if (shiftBase < 1 || 5 < shiftBase) Error("Internal error, wrong base", NULL, FATAL);
  421.         getNumericValueLastErr = NULL;
  422.         val = 0;
  423.         if (pend <= p) {                // no actual digits between format specifiers
  424.                 getNumericValueLastErr = getNumericValueErr_syntax;
  425.                 return false;
  426.         }
  427.         aint digit;
  428.         const int base = 1<<shiftBase;
  429.         const aint overflowMask = (~0UL)<<(32-shiftBase);
  430.         while (p < pend) {
  431.                 const byte charDigit = *p++;
  432.                 if ('\'' == charDigit && isalnum((byte)*p)) continue;
  433.                 if (0 == charDigit || !isalnum(charDigit)) {
  434.                         getNumericValueLastErr = getNumericValueErr_no_digit;
  435.                         break;
  436.                 }
  437.                 if (base <= (digit = getval(charDigit))) {
  438.                         getNumericValueLastErr = getNumericValueErr_digit;
  439.                         break;
  440.                 }
  441.                 if (val & overflowMask) getNumericValueLastErr = getNumericValueErr_overflow;
  442.                 val = (val<<shiftBase) + digit;
  443.         }
  444.         return (NULL == getNumericValueLastErr);
  445. }
  446.  
  447. bool GetNumericValue_IntBased(char*& p, const char* const pend, aint& val, const int base) {
  448.         if (base < 2 || 36 < base) Error("Internal error, wrong base", NULL, FATAL);
  449.         getNumericValueLastErr = NULL;
  450.         val = 0;
  451.         if (pend <= p) {                // no actual digits between format specifiers
  452.                 getNumericValueLastErr = getNumericValueErr_syntax;
  453.                 return false;
  454.         }
  455.         aint digit;
  456.         while (p < pend) {
  457.                 const byte charDigit = *p++;
  458.                 if ('\'' == charDigit && isalnum((byte)*p)) continue;
  459.                 if (0 == charDigit || !isalnum(charDigit)) {
  460.                         getNumericValueLastErr = getNumericValueErr_no_digit;
  461.                         break;
  462.                 }
  463.                 if (base <= (digit = getval(charDigit))) {
  464.                         getNumericValueLastErr = getNumericValueErr_digit;
  465.                         break;
  466.                 }
  467.                 const uint32_t oval = static_cast<uint32_t>(val);
  468.                 val = (val * base) + digit;
  469.                 if (static_cast<uint32_t>(val) < oval) getNumericValueLastErr = getNumericValueErr_overflow;
  470.         }
  471.         return (NULL == getNumericValueLastErr);
  472. }
  473.  
  474. // parses number literals, forces result to be confined into 32b (even on 64b platforms,
  475. // to have stable results in listings/tests across platforms).
  476. int GetConstant(char*& op, aint& val) {
  477.         // the input string has been already detected as numeric literal by ParseExpPrim
  478.         assert(isdigit((byte)*op) || '#' == *op || '$' == *op || '%' == *op);
  479.         // find end of the numeric literal (pointer is beyond last alfa/digit character
  480.         char* pend = op;
  481.         if ('#' == *pend || '$' == *pend || '%' == *pend) ++pend;
  482.         while (isalnum((byte)*pend) || ('\'' == *pend && isalnum((byte)pend[1]))) ++pend;
  483.         char* const hardEnd = pend;
  484.         bool has_decimal_part = ('.' == *hardEnd) && isalnum((byte)hardEnd[1]);
  485.         // check if the format is defined by prefix (#, $, %, 0x, 0X, 0b, 0B, 0q, 0Q)
  486.         char* p = op;
  487.         int shiftBase = 0, base = 0;
  488.         if ('#' == *p || '$' == *p) {
  489.                 shiftBase = 4;
  490.                 ++p;
  491.         } else if ('0' == p[0] && 'x' == (p[1]|0x20)) {
  492.                 shiftBase = 4;
  493.                 p += 2;
  494.         } else if ('0' == p[0] && 'b' == (p[1]|0x20) && 'h' != (pend[-1]|0x20) ) {
  495.                 shiftBase = 1;          // string 0b800h is hexadecimal, not binary (legacy compatibility)
  496.                 p += 2;
  497.         } else if ('0' == p[0] && 'q' == (p[1]|0x20)) {
  498.                 shiftBase = 3;
  499.                 p += 2;
  500.         } else if ('%' == *p) {
  501.                 shiftBase = 1;
  502.                 ++p;
  503.         }
  504.         // if the base is still undecided, check for suffix format specifier
  505.         if (0 == shiftBase) {
  506.                 switch (pend[-1]|0x20) {
  507.                         case 'h': --pend; shiftBase = 4;  has_decimal_part = false; break;
  508.                         case 'q': --pend; shiftBase = 3;  has_decimal_part = false; break;
  509.                         case 'o': --pend; shiftBase = 3;  has_decimal_part = false; break;
  510.                         case 'b': --pend; shiftBase = 1;  has_decimal_part = false; break;
  511.                         case 'd': --pend;      base = 10; has_decimal_part = false; break;
  512.                         default:
  513.                                 base = 10;
  514.                                 break;
  515.                 }
  516.         }
  517.         if ('\'' == *p || '\'' == pend[-1]) {   // digit-group tick can't be first/last digit
  518.                 Error(getNumericValueErr_no_digit, op, SUPPRESS);
  519.                 return 0;
  520.         }
  521.         // parse the number into value
  522.         if (0 < shiftBase) {
  523.                 if (!GetNumericValue_TwoBased(p, pend, val, shiftBase) && GetNumericValue_ProcessLastError(op))
  524.                         return 0;
  525.         } else {
  526.                 if (!GetNumericValue_IntBased(p, pend, val, base) && GetNumericValue_ProcessLastError(op))
  527.                         return 0;
  528.         }
  529.         // check for possible decimal part and warn about it appropriately (can be user error or Lua string formatting)
  530.         if (!has_decimal_part) {
  531.                 // no decimal part detected, all is done here
  532.                 op = hardEnd;
  533.                 return 1;
  534.         }
  535.         // possible decimal part detected, try to parse it just to throw it away (with warnings if enabled)
  536.         p = hardEnd + 1;
  537.         assert(isalnum((byte)*p));
  538.         pend = hardEnd + 2;
  539.         while (isalnum((byte)*pend) || ('\'' == *pend && isalnum((byte)pend[1]))) ++pend;
  540.         aint fractionVal;
  541.         if (0 < shiftBase) {
  542.                 GetNumericValue_TwoBased(p, pend, fractionVal, shiftBase);
  543.         } else {
  544.                 GetNumericValue_IntBased(p, pend, fractionVal, base);
  545.         }
  546.         // ignore overflow errors in fractional part, the value is thrown away any way, just report it as non-zero
  547.         if (getNumericValueErr_overflow == getNumericValueLastErr) {
  548.                 fractionVal = 1;
  549.                 getNumericValueLastErr = nullptr;
  550.         } else if (nullptr != getNumericValueLastErr) {         // but report other syntax errors
  551.                 GetNumericValue_ProcessLastError(hardEnd);
  552.                 return 0;
  553.         }
  554.         // warn about zero/non-zero fractional part in the numeral string
  555.         WarningById(fractionVal ? W_NON_ZERO_DECIMAL : W_ZERO_DECIMAL, op);
  556.         op = pend;
  557.         return 1;
  558. }
  559.  
  560. // parse single character of double-quoted string (backslash does escape characters)
  561. int GetCharConstInDoubleQuotes(char*& op, aint& val) {
  562.         if ('"' == *op || !*op) return 0;               // closing quotes or no more characters, return 0
  563.         if ((val = *op++) != '\\') return 1;    // un-escaped character, just return it
  564.         switch (val = *op++) {
  565.         case '\\':
  566.         case '\'':
  567.         case '\"':
  568.         case '\?':
  569.                 return 1;
  570.         case '0':
  571.                 val = 0;
  572.                 return 1;
  573.         case 'n':
  574.         case 'N':
  575.                 val = 10;
  576.                 return 1;
  577.         case 't':
  578.         case 'T':
  579.                 val = 9;
  580.                 return 1;
  581.         case 'v':
  582.         case 'V':
  583.                 val = 11;
  584.                 return 1;
  585.         case 'b':
  586.         case 'B':
  587.                 val = 8;
  588.                 return 1;
  589.         case 'r':
  590.         case 'R':
  591.                 val = 13;
  592.                 return 1;
  593.         case 'f':
  594.         case 'F':
  595.                 val = 12;
  596.                 return 1;
  597.         case 'a':
  598.         case 'A':
  599.                 val = 7;
  600.                 return 1;
  601.         case 'e':
  602.         case 'E':
  603.                 val = 27;
  604.                 return 1;
  605.         case 'd':
  606.         case 'D':
  607.                 val = 127;
  608.                 return 1;
  609.         default:
  610.                 break;
  611.         }
  612.         // return backslash as char value in case of unknown escape sequence
  613.         // (to mimick older versions of sjasmplus like 1.07-1.10 behaviour)
  614.         --op;
  615.         val = '\\';
  616.         Warning("Unknown escape", op-1);
  617.         return 1;
  618. }
  619.  
  620. // parse single character of apostrophe-quoted string (no escaping, double '' is apostrophe itself)
  621. int GetCharConstInApostrophes(char*& op, aint& val) {
  622.         if ('\'' == op[0] && '\'' == op[1]) {   // '' converts to actual apostrophe as value
  623.                 val = '\'';
  624.                 op += 2;
  625.                 return 1;
  626.         }
  627.         if ('\'' == *op || !*op) return 0;              // closing apostrophe or no more characters, return 0
  628.         // normal character, just return it
  629.         val = *op++;
  630.         return 1;
  631. }
  632.  
  633. // parse single/double quoted string literal as single value ('012' == 0x00303132)
  634. int GetCharConst(char*& p, aint& val) {
  635.         const char * const op = p;              // for error reporting
  636.         char buffer[128];
  637.         int bytes = 0, strRes;
  638.         if (!(strRes = GetCharConstAsString(p, buffer, bytes))) return 0;               // no string detected
  639.         val = 0;
  640.         if (strRes < 0) return 0;               // some syntax/max_size error happened
  641.         for (int ii = 0; ii < bytes; ++ii) val = (val << 8) + (255&buffer[ii]);
  642.         if (0 == bytes) {
  643.                 Warning("Empty string literal converted to value 0!", op);
  644.         } else if (4 < bytes) {
  645.                 const char oldCh = *p;
  646.                 *p = 0;                                         // shorten the string literal for warning display
  647.                 sprintf(buffer, "String literal truncated to 0x%X", val);
  648.                 Warning(buffer, op);
  649.                 *p = oldCh;                                     // restore it
  650.         }
  651.         return 1;
  652. }
  653.  
  654. // returns (adjusts also "p" and "ei", and fills "e"):
  655. //  -2 = buffer full
  656. //  -1 = syntax error (missing quote/apostrophe)
  657. //   0 = no string literal detected at p[0]
  658. //   1 = string literal in single quotes (apostrophe)
  659. //   2 = string literal in double quotes (")
  660. template <class strT> int GetCharConstAsString(char* & p, strT e[], int & ei, int max_ei, int add) {
  661.         if ('"' != *p && '\'' != *p) return 0;
  662.         const char* const elementP = p;
  663.         const bool quotes = ('"' == *p++);
  664.         aint val;
  665.         while (ei < max_ei && (quotes ? GetCharConstInDoubleQuotes(p, val) : GetCharConstInApostrophes(p, val))) {
  666.                 e[ei++] = (val + add) & 255;
  667.         }
  668.         if ((quotes ? '"' : '\'') != *p) {      // too many/invalid arguments or zero-terminator can lead to this
  669.                 if (*p) return -2;                              // too many arguments
  670.                 Error("Syntax error", elementP, SUPPRESS);      // zero-terminator
  671.                 return -1;
  672.         }
  673.         ++p;
  674.         return 1 + quotes;
  675. }
  676.  
  677. // make sure both specialized instances for `char` and `int` are available for whole app
  678. template int GetCharConstAsString<char>(char* & p, char e[], int & ei, int max_ei, int add);
  679. template int GetCharConstAsString<int>(char* & p, int e[], int & ei, int max_ei, int add);
  680.  
  681. int GetBytes(char*& p, int e[], int add, int dc) {
  682.         aint val;
  683.         int t = 0, strRes;
  684.         // reset alternate result flag in ParseExpression part of code
  685.         Relocation::isResultAffected = false;
  686.         do {
  687.                 const int oldT = t;
  688.                 char* const oldP = p;
  689.                 if (SkipBlanks(p)) {
  690.                         Error("Expression expected", NULL, SUPPRESS);
  691.                         break;
  692.                 }
  693.                 if (0 != (strRes = GetCharConstAsString(p, e, t, 128, add))) {
  694.                         // string literal parsed (both types)
  695.                         if (strRes < 0) break;          // syntax error happened
  696.                         // single byte "strings" may have further part of expression, detect it here
  697.                         if (1 == t - oldT && !SkipBlanks(p) && ',' != *p) {
  698.                                 // expression with single char detected (like 'a'|128), revert the string parsing
  699.                                 t = oldT;
  700.                                 p = oldP;               // and continue with the last code-path trying to parse expression
  701.                         } else {        // string literal (not expression), handle the extra string literal logic
  702.                                 if (oldT == t) {
  703.                                         Warning("Empty string", p-2);
  704.                                 } else if (dc) {
  705.                                         // mark last "string" byte with |128: single char in quotes *is* string
  706.                                         // but single char in apostrophes *is not* (!) (no |128 then)
  707.                                         int maxLengthNotString = (1 == strRes);         // 0 for quotes, 1 for apostrophes
  708.                                         if (maxLengthNotString < (t - oldT)) e[t - 1] |= 128;
  709.                                 }
  710.                                 continue;
  711.                         }
  712.                 }
  713.                 if (ParseExpressionNoSyntaxError(p, val)) {
  714.                         check8(val);
  715.                         Relocation::resolveRelocationAffected(t, Relocation::HIGH);
  716.                         e[t++] = (val + add) & 255;
  717.                 } else {
  718.                         Error("Syntax error", p, SUPPRESS);
  719.                         break;
  720.                 }
  721.         } while(comma(p) && t < 128);
  722.         Relocation::checkAndWarn();
  723.         e[t] = -1;
  724.         if (t == 128 && *p) Error("Over 128 bytes defined in single DB/DC/... Values over", p, SUPPRESS);
  725.         return t;
  726. }
  727.  
  728. void GetStructText(char*& p, aint len, byte* data, const byte* initData) {
  729.         assert(1 <= len && len <= CStructureEntry2::TEXT_MAX_SIZE && nullptr != data);
  730.         // reset alternate result flag in ParseExpression part of code
  731.         Relocation::isResultAffected = false;
  732.         // "{}" is always required to keep the syntax less ambiguous
  733.         // (prototype code was trying to be more relaxed, but it was quickly becoming too complicated)
  734.         // (the relaxed sub-struct boundaries are confusing, eating "{}" a bit unexpectedly (to user))
  735.         if (!need(p, '{')) {
  736.                 if (nullptr == initData) {
  737.                         Error("TEXT field value must be enclosed in curly braces, missing '{'", p);
  738.                 } else {
  739.                         memcpy(data, initData, len);
  740.                 }
  741.                 return;
  742.         }
  743.         aint ii = 0, val;
  744.         do {
  745.                 // if no more chars/lines to be parsed, or ending curly brace incoming, finish the loop
  746.                 if (!PrepareNonBlankMultiLine(p) || '}' == *p) break;
  747.                 const int oldIi = ii;
  748.                 char* const oldP = p;
  749.                 int strRes;
  750.                 if (0 < (strRes = GetCharConstAsString(p, data, ii, len))) {
  751.                         // string literal parsed (both types)
  752.                         // single byte "strings" may have further part of expression, detect it here
  753.                         if (1 == ii - oldIi && !SkipBlanks(p) && ',' != *p && '}' != *p) {
  754.                                 // expression with single char detected (like 'a'|128), revert the string parsing
  755.                                 ii = oldIi;
  756.                                 p = oldP;               // and continue with the last code-path trying to parse expression
  757.                         } else {                        // string literal (not expression) parsed OK
  758.                                 continue;
  759.                         }
  760.                 }
  761.                 if (-1 == strRes) break;                // syntax error happened
  762.                 if (-2 == strRes || len <= ii) {
  763.                         Error("Maximum length of struct text reached. Values over", p, SUPPRESS);
  764.                         break;
  765.                 }
  766.                 if (ParseExpressionNoSyntaxError(p, val)) {
  767.                         check8(val);
  768.                         data[ii++] = byte(val);
  769.                 } else {
  770.                         Error("Syntax error", p, SUPPRESS);
  771.                         break;
  772.                 }
  773.         } while (comma(p));
  774.         if (!PrepareNonBlankMultiLine(p) || !need(p, '}')) {
  775.                 Error("TEXT field value must be enclosed in curly braces, missing '}'", p);
  776.                 return;
  777.         }
  778.         Relocation::checkAndWarn();
  779.         // some bytes were initialized explicitly
  780.         if (nullptr != initData) {
  781.                 // init remaining bytes from initData
  782.                 while (ii < len) {
  783.                         data[ii] = initData[ii];
  784.                         ++ii;
  785.                 }
  786.         } else {
  787.                 // init remaining bytes by last byte (or zero if none was defined)
  788.                 byte filler = 0 < ii ? data[ii - 1] : 0;
  789.                 while (ii < len) data[ii++] = filler;
  790.         }
  791. }
  792.  
  793. int GetBits(char*& p, int e[]) {
  794.         EDelimiterType dt = DelimiterBegins(p, delimiters_noAngle);     //also skip blanks
  795.         static int one = 0;             // the warning about multi-chars should be emitted only once per pass
  796.         static bool zeroInDgWarning = false;
  797.         int bytes = 0;
  798.         while (*p && (dt == DT_NONE || delimiters_e[dt] != *p)) {
  799.                 if (128 <= bytes) {
  800.                         Error("Over 128 bytes defined in DG. Bits over", p, SUPPRESS);
  801.                         break;
  802.                 }
  803.                 // collect whole byte (eight bits)
  804.                 int value = 1, pch;
  805.                 while (value < 256 && *p && (pch = 255 & (*p++))) {
  806.                         if (White(pch)) continue;               // skip spaces
  807.                         value <<= 1;
  808.                         if ('-' == pch || '.' == pch || '_' == pch) continue;
  809.                         value |= 1;
  810.                         if (LASTPASS != pass) continue;
  811.                         if (0 < one && one != pch) {
  812.                                 Warning("[DG] multiple characters used for 'ones'");
  813.                                 one = -1;                                       // emit this warning only once
  814.                         } else if (!one) one = pch;             // remember char used first time for "ones"
  815.                         if ('0' == pch && !zeroInDgWarning) {
  816.                                 zeroInDgWarning = true;
  817.                                 Warning("[DG] character '0' in DG works as value 1");
  818.                         }
  819.                 }
  820.                 if (value < 256) {              // there was not eight characters, ended prematurely
  821.                         Error("[DG] byte needs eight characters", substitutedLine, SUPPRESS);
  822.                 } else {
  823.                         e[bytes++] = value & 255;
  824.                 }
  825.                 SkipBlanks(p);
  826.         }
  827.         if (0 < one) one = 0;           // reset "ones" type if everything was OK this time
  828.         e[bytes] = -1;
  829.         if (dt == DT_NONE) return bytes;
  830.         if (delimiters_e[dt] != *p)     Error("No closing delimiter", NULL, SUPPRESS);
  831.         else                                            ++p;
  832.         return bytes;
  833. }
  834.  
  835. int GetBytesHexaText(char*& p, int e[]) {
  836.         const char* const op_full = p;
  837.         int bytes = 0;
  838.         do {
  839.                 EDelimiterType dt = DelimiterBegins(p, delimiters_noAngle);     //also skip blanks
  840.                 if (!*p) Error("no arguments");
  841.                 while (*p && (dt == DT_NONE || delimiters_e[dt] != *p)) {
  842.                         const char* const op = p;
  843.                         // collect whole byte = two hexa digits
  844.                         aint val;
  845.                         if (!GetNumericValue_TwoBased(p, p+2, val, 4) && GetNumericValue_ProcessLastError(op)) {
  846.                                 return 0;               // total failure, don't emit anything
  847.                         }
  848.                         if (128 <= bytes) {
  849.                                 Error("Over 128 bytes defined in DH/DEFH/HEX. Values over", op, SUPPRESS);
  850.                                 break;
  851.                         }
  852.                         e[bytes++] = val & 255;
  853.                         SkipBlanks(p);                  // skip spaces
  854.                         if (dt == DT_NONE && ',' == *p) break;  // loop through multi arguments in outer do-while loop
  855.                 }
  856.                 if (dt != DT_NONE) {
  857.                         if (delimiters_e[dt] == *p)     {
  858.                                 ++p;
  859.                                 SkipBlanks(p);
  860.                         } else Error("No closing delimiter", op_full, SUPPRESS);
  861.                 }
  862.         } while (comma(p));
  863.         e[bytes] = -1;
  864.         return bytes;
  865. }
  866.  
  867. static EDelimiterType delimiterOfLastFileName = DT_NONE;
  868.  
  869. static char* GetFileName(char*& p, const char* pathPrefix, bool convertslashes) {
  870.         bool slashConverted = false;
  871.         char* newFn = new char[LINEMAX+1], * result = newFn;
  872.         if (NULL == newFn) ErrorOOM();
  873.         // prepend the filename with path-prefix, if some was requested
  874.         if (pathPrefix) {
  875.                 while (*pathPrefix) {
  876.                         *newFn = *pathPrefix;
  877.                         if (convertslashes && pathBadSlash == *newFn) *newFn = pathGoodSlash;
  878.                         ++newFn, ++pathPrefix;
  879.                         if (LINEMAX <= newFn-result) Error("Filename too long!", NULL, FATAL);
  880.                 }
  881.         }
  882.         // check if some and which delimiter is used for this filename (does advance over white chars)
  883.         // and remember type of detected delimiter (for GetDelimiterOfLastFileName function)
  884.         delimiterOfLastFileName = DelimiterAnyBegins(p);
  885.         const char deliE = delimiters_e[delimiterOfLastFileName];       // expected ending delimiter
  886.         // copy all characters until zero or delimiter-end character is reached
  887.         while (*p && deliE != *p) {
  888.                 *newFn = *p;            // copy character
  889.                 if (convertslashes && pathBadSlash == *newFn) slashConverted = (*newFn = pathGoodSlash);
  890.                 ++newFn, ++p;
  891.                 if (LINEMAX <= newFn-result) Error("Filename too long!", NULL, FATAL);
  892.         }
  893.         *newFn = 0;                             // add string terminator at end of file name
  894.         // verify + skip end-delimiter (if other than space)
  895.         if (' ' != deliE) {
  896.                 if (deliE == *p) {
  897.                         ++p;
  898.                 } else {
  899.                         const char delimiterTxt[2] = { deliE, 0 };
  900.                         Error("No closing delimiter", delimiterTxt, SUPPRESS);
  901.                         result[0] = 0;  // return "empty" string filename
  902.                 }
  903.         }
  904.         SkipBlanks(p);                  // skip blanks any way
  905.         if (slashConverted) WarningById(W_BACKSLASH, bp);
  906.         return result;
  907. }
  908.  
  909. char* GetOutputFileName(char*& p, bool convertslashes) {
  910.         return GetFileName(p, Options::OutPrefix, convertslashes);
  911. }
  912.  
  913. char* GetFileName(char*& p, bool convertslashes) {
  914.         return GetFileName(p, nullptr, convertslashes);
  915. }
  916.  
  917. EDelimiterType GetDelimiterOfLastFileName() {
  918.         // DT_NONE if no GetFileName was called
  919.         return delimiterOfLastFileName;
  920. }
  921.  
  922. bool isLabelStart(const char *p, bool modifiersAllowed) {
  923.         if (modifiersAllowed) {
  924.                 if ('.' == *p || '@' == *p) return isLabelStart(p + 1, false);
  925.         }
  926.         return *p && (isalpha((byte)*p) || '_' == *p);
  927. }
  928.  
  929. int islabchar(char p) {
  930.         if (isalnum((byte)p) || p == '_' || p == '.' || p == '?' || p == '!' || p == '#' || p == '@') {
  931.                 return 1;
  932.         }
  933.         return 0;
  934. }
  935.  
  936. EStructureMembers GetStructMemberId(char*& p) {
  937.         if (*p == '#') {
  938.                 ++p;
  939.                 if (*p == '#') {
  940.                         ++p;
  941.                         return SMEMBALIGN;
  942.                 }
  943.                 return SMEMBBLOCK;
  944.         }
  945.         //  if (*p=='.') ++p;
  946.         switch (*p * 2 + *(p + 1)) {
  947.         case 'b'*2+'y':
  948.         case 'B'*2+'Y':
  949.                 if (cmphstr(p, "byte")) return SMEMBBYTE;
  950.                 break;
  951.         case 'w'*2+'o':
  952.         case 'W'*2+'O':
  953.                 if (cmphstr(p, "word")) return SMEMBWORD;
  954.                 break;
  955.         case 'b'*2+'l':
  956.         case 'B'*2+'L':
  957.                 if (cmphstr(p, "block")) return SMEMBBLOCK;
  958.                 break;
  959.         case 'd'*2+'b':
  960.         case 'D'*2+'B':
  961.                 if (cmphstr(p, "db")) return SMEMBBYTE;
  962.                 break;
  963.         case 'd'*2+'w':
  964.         case 'D'*2+'W':
  965.                 if (cmphstr(p, "dw")) return SMEMBWORD;
  966.                 if (cmphstr(p, "dword")) return SMEMBDWORD;
  967.                 break;
  968.         case 'd'*2+'s':
  969.         case 'D'*2+'S':
  970.                 if (cmphstr(p, "ds")) return SMEMBBLOCK;
  971.                 break;
  972.         case 'd'*2+'d':
  973.         case 'D'*2+'D':
  974.                 if (cmphstr(p, "dd")) return SMEMBDWORD;
  975.                 break;
  976.         case 'a'*2+'l':
  977.         case 'A'*2+'L':
  978.                 if (cmphstr(p, "align")) return SMEMBALIGN;
  979.                 break;
  980.         case 'd'*2+'e':
  981.         case 'D'*2+'E':
  982.                 if (cmphstr(p, "defs")) return SMEMBBLOCK;
  983.                 if (cmphstr(p, "defb")) return SMEMBBYTE;
  984.                 if (cmphstr(p, "defw")) return SMEMBWORD;
  985.                 if (cmphstr(p, "defd")) return SMEMBDWORD;
  986.                 break;
  987.         case 'd'*2+'2':
  988.         case 'D'*2+'2':
  989.                 if (cmphstr(p, "d24")) return SMEMBD24;
  990.                 break;
  991.         case 't'*2+'e':
  992.         case 'T'*2+'E':
  993.                 if (cmphstr(p, "text")) return SMEMBTEXT;
  994.                 break;
  995.         default:
  996.                 break;
  997.         }
  998.         return SMEMBUNKNOWN;
  999. }
  1000.  
  1001. int GetMacroArgumentValue(char* & src, char* & dst) {
  1002.         SkipBlanks(src);
  1003.         const char* const dstOrig = dst, * const srcOrig = src;
  1004.         const char* dstStopTrim = dst;
  1005.         while (*src && ',' != *src) {
  1006.                 // check if there is some kind of delimiter next (string literal or angle brackets expression)
  1007.                 // the angle-bracket can only be used around whole argument (i.e. '<' must be first char)
  1008.                 EDelimiterType delI = DelimiterBegins(src, srcOrig==src ? delimiters_all : delimiters_noAngle, false);
  1009.                 // CPP numeric literals heuristic (eating digit-group apostrophes as regular digit char)
  1010.                 if (DT_APOSTROPHE == delI && srcOrig < src) {
  1011.                         const char prevC = 0x20 | src[-1];      // also lowercase any ASCII letter
  1012.                         if (('0' <= prevC && prevC <= '9') || ('a' <= prevC && prevC <= 'f')) {
  1013.                                 // ahead of apostrophe is character which can work as digit (up to hexa system)
  1014.                                 // this heuristic doesn't bother to be super precise, because string-apostrophe
  1015.                                 // should be on word boundary any way, not preceded by *any* digit of any base.
  1016.                                 const char nextC = 0x20 | src[1];
  1017.                                 if (('0' <= nextC && nextC <= '9') || ('a' <= nextC && nextC <= 'f')) {
  1018.                                         // by same logic, if also char after the apostrophe is any digit (up to base16)
  1019.                                         // turn the apostrophe from delimiter into digit-grouping char
  1020.                                         delI = DT_NONE;
  1021.                                 }
  1022.                         }
  1023.                 }
  1024.                 if (DT_NONE == delI) {          // no delimiter found, ordinary expression, copy char by char
  1025.                         *dst++ = *src++;
  1026.                         continue;
  1027.                 }
  1028.                 // some delimiter found - parse those properly by their type
  1029.                 if (DT_ANGLE != delI) *dst++ = *src;    // quotes are part of parsed value, angles are NOT
  1030.                 ++src;                                                                  // advance over delimiter
  1031.                 const char endCh = delimiters_e[delI];  // set expected ending delimiter
  1032.                 while (*src) {
  1033.                         // handle escape sequences by the type of delimiter
  1034.                         switch (delI) {
  1035.                         case DT_ANGLE:
  1036.                                 if (('!' == *src && '!' == src[1]) || ('!' == *src && '>' == src[1])) {
  1037.                                         *dst++ = src[1]; src += 2;      // escape sequence is converted into final char
  1038.                                         continue;
  1039.                                 }
  1040.                                 break;
  1041.                         case DT_QUOTES:
  1042.                                 if ('\\' == *src && src[1]) {
  1043.                                         *dst++ = *src++;        *dst++ = *src++;
  1044.                                         continue;                                       // copy escape + escaped char (*any* non zero char)
  1045.                                 }
  1046.                                 break;
  1047.                         case DT_APOSTROPHE:
  1048.                                 if ('\'' == *src && '\'' == src[1]) {
  1049.                                         *dst++ = *src++;        *dst++ = *src++;
  1050.                                         continue;                                       // copy two apostrophes (escaped apostrophe)
  1051.                                 }
  1052.                                 break;
  1053.                         default:        Error("Internal error. GetMacroArgumentValue()", NULL, FATAL);
  1054.                         }
  1055.                         if (endCh == *src) break;                       // ending delimiter found
  1056.                         *dst++ = *src++;                                        // just copy character
  1057.                 }
  1058.                 // ending delimiter must be identical to endCh
  1059.                 if (endCh != *src) {
  1060.                         *dst = 0;                                                       // zero terminator of resulting string value
  1061.                         return 0;
  1062.                 }
  1063.                 // set ending delimiter for quotes and apostrophe (angles are stripped from value)
  1064.                 if (DT_QUOTES == delI || DT_APOSTROPHE == delI) *dst++ = endCh;
  1065.                 dstStopTrim = dst;                                              // should not trim right spaces beyond this point
  1066.                 ++src;                                                                  // advance over delimiter
  1067.         }
  1068.         while (dstStopTrim < dst && White(dst[-1])) --dst;      // trim the right size space from value
  1069.         *dst = 0;                                                                       // zero terminator of resulting string value
  1070.         if (! *dstOrig) Warning("[Macro argument parser] empty value", srcOrig);
  1071.         return 1;
  1072. }
  1073.  
  1074. EDelimiterType DelimiterBegins(char*& src, const std::array<EDelimiterType, 3> delimiters, bool advanceSrc) {
  1075.         if ((0 == *src) || (advanceSrc && SkipBlanks(src))) return DT_NONE;
  1076.         for (const auto dt : delimiters) {
  1077.                 if (delimiters_b[dt] != *src) continue;
  1078.                 if (advanceSrc) ++src;
  1079.                 return dt;
  1080.         }
  1081.         return DT_NONE;
  1082. }
  1083.  
  1084. EDelimiterType DelimiterAnyBegins(char*& src, bool advanceSrc) {
  1085.         return DelimiterBegins(src, delimiters_all, advanceSrc);
  1086. }
  1087.  
  1088. //eof reader.cpp
  1089.