?login_element?

Subversion Repositories NedoOS

Rev

Rev 129 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download

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