?login_element?

Subversion Repositories NedoOS

Rev

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

  1. /*
  2.  
  3.   SjASMPlus Z80 Cross Compiler
  4.  
  5.   This is modified sources of SjASM by Aprisobal - aprisobal@tut.by
  6.  
  7.   Copyright (c) 2005 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. // parser.cpp
  30.  
  31. #include "sjdefs.h"
  32.  
  33. static bool synerr = true;      // flag whether ParseExpression should report syntax error with Error()
  34.  
  35. static int ParseExpressionEntry(char*& p, aint& nval);
  36.  
  37. static int ParseExpPrim(char*& p, aint& nval) {
  38.         int res = 0;
  39.         if (SkipBlanks(p)) {
  40.                 return 0;
  41.         }
  42.         if (*p == '(') {
  43.                 ++p;
  44.                 res = ParseExpressionEntry(p, nval);
  45.                 if (!need(p, ')')) {
  46.                         Error("')' expected");
  47.                         return 0;
  48.                 }
  49.         } else if (DeviceID && *p == '{') {             // read WORD/BYTE from virtual device memory
  50.                 char* const readMemP = p;
  51.                 const int byteOnly = cmphstr(++p, "b");
  52.                 // switch off alternative relocation evaluation for the address value
  53.                 const bool oldAreLabelsOffset = Relocation::areLabelsOffset;
  54.                 Relocation::areLabelsOffset = false;
  55.                 int addressParseRes = ParseExpressionEntry(p, nval);
  56.                 Relocation::areLabelsOffset = oldAreLabelsOffset;       // restore alternative evaluation
  57.                 if (!addressParseRes) return 0; // some syntax error inside the address expression
  58.                 if (!need(p, '}')) {
  59.                         Error("'}' expected", readMemP, SUPPRESS);
  60.                         return 0;
  61.                 }
  62.                 if (nval < 0 || (0xFFFE + byteOnly) < nval) {
  63.                         Error("Address in {..} must fetch bytes from 0x0000..0xFFFF range", readMemP);
  64.                         nval = 0;
  65.                         return 1;                                               // and return zero value as result (avoid "syntax error")
  66.                 }
  67.                 res = int(MemGetByte(nval));
  68.                 if (!byteOnly) res += int(MemGetByte(nval + 1)) << 8;
  69.                 nval = res;
  70.                 return 1;
  71.         } else if (isdigit((byte)*p) && GetTemporaryLabelValue(p, nval, true)) {        // temporary label with underscore suffix
  72.                 return 1;
  73.         } else if (isdigit((byte)*p) || (*p == '#' && isalnum((byte)*(p + 1))) || (*p == '$' && isalnum((byte)*(p + 1))) || *p == '%') {
  74.                 return GetConstant(p, nval);
  75.         } else if (isLabelStart(p)) {
  76.                 return GetLabelValue(p, nval);
  77.         } else if (*p == '?' && isLabelStart(p+1)) {
  78.                 // this is undocumented "?<symbol>" operator, seems as workaround for labels like "not"
  79.                 // This is deprecated and will be removed in v2.x of sjasmplus
  80.                 // (where keywords will be reserved and such label would be invalid any way)
  81.                 Warning("?<symbol> operator is deprecated and will be removed in v2.x", p);
  82.                 ++p;
  83.                 return GetLabelValue(p, nval);
  84.         } else if (DISP_NONE != PseudoORG && '$' == p[0] && '$' == p[1] && '$' == p[2]) {
  85.                 if ('$' == p[3]) {              // "$$$$" operator to get physical memory page inside DISP block
  86.                         p += 4;
  87.                         nval = DeviceID ? Page->Number : LABEL_PAGE_UNDEFINED;
  88.                         return 1;
  89.                 }
  90.                 // "$$$" operator to get physical address inside DISP block
  91.                 p += 3;
  92.                 nval = adrdisp;         // this is never affected by relocation
  93.                 return 1;
  94.         } else if (DeviceID && *p == '$' && *(p + 1) == '$') {
  95.                 p += 2;
  96.                 if (isLabelStart(p)) return GetLabelPage(p, nval);
  97.                 if (DISP_NONE != PseudoORG && LABEL_PAGE_UNDEFINED != dispPageNum) {
  98.                         // enforce explicit request of fake DISP page
  99.                         nval = dispPageNum;
  100.                 } else {
  101.                         // current page
  102.                         nval = Page->Number;
  103.                 }
  104.                 return 1;
  105.         } else if (*p == '$') {
  106.                 ++p;
  107.                 nval = CurAddress;
  108.                 if (Relocation::type && Relocation::areLabelsOffset && DISP_INSIDE_RELOCATE != PseudoORG) {
  109.                         nval += Relocation::alternative_offset;
  110.                 }
  111.                 return 1;
  112.         } else if (!(res = GetCharConst(p, nval))) {
  113.                 if (synerr) Error("Syntax error", p, IF_FIRST);
  114.                 return 0;
  115.         }
  116.         return res;
  117. }
  118.  
  119. static int ParseExpUnair(char*& p, aint& nval) {
  120.         SkipBlanks(p);
  121.         char* oldP = p;
  122.         if (cmphstr(p, "norel", true)) {
  123.                 // switch off alternative relocation evaluation for the following part of expression
  124.                 const bool oldAreLabelsOffset = Relocation::areLabelsOffset;
  125.                 Relocation::areLabelsOffset = false;
  126.                 int norelParseRes = ParseExpPrim(p, nval);                      // higher priority than other unary
  127.                 Relocation::areLabelsOffset = oldAreLabelsOffset;       // restore alternative evaluation
  128.                 if (norelParseRes) return 1;
  129.                 // "norel" operator didn't parse successfully, try to ignore it (will treat it as label)
  130.                 p = oldP;
  131.         }
  132.         if (cmphstr(p, "exist", true)) {
  133.                 int existEval = 0, hasParentheses = need(p, '(');
  134.                 if (hasParentheses || isLabelStart(p)) {
  135.                         existEval = LabelExist(p, nval);
  136.                 }
  137.                 if (existEval && hasParentheses) {
  138.                         existEval = need(p, ')');                                               // check closing parenthesis
  139.                 }
  140.                 if (existEval) return 1;
  141.                 p = oldP;
  142.         }
  143.         aint right;
  144.         int oper;
  145.         if ((oper = need(p, "! ~ + - ")) || \
  146.                 (oper = needa(p, "not", '!', "low", 'l', "high", 'h', true)) || \
  147.                 (oper = needa(p, "abs", 'a', nullptr, 0, nullptr, 0, true)) ) {
  148.                 switch (oper) {
  149.                 case '!':
  150.                         if (!ParseExpUnair(p, right)) return 0;
  151.                         nval = -!right;
  152.                         break;
  153.                 case '~':
  154.                         if (!ParseExpUnair(p, right)) return 0;
  155.                         nval = ~right;
  156.                         break;
  157.                 case '+':
  158.                         if (!ParseExpUnair(p, right)) return 0;
  159.                         nval = right;
  160.                         break;
  161.                 case '-':
  162.                         if (!ParseExpUnair(p, right)) return 0;
  163.                         nval = ~right + 1;
  164.                         break;
  165.                 case 'l':
  166.                         if (!ParseExpUnair(p, right)) return 0;
  167.                         nval = right & 255;
  168.                         break;
  169.                 case 'h':
  170.                         if (!ParseExpUnair(p, right)) return 0;
  171.                         nval = (right >> 8) & 255;
  172.                         break;
  173.                 case 'a':
  174.                         if (!ParseExpUnair(p, right)) return 0;
  175.                         nval = abs(right);
  176.                         break;
  177.                 default: Error("internal error", nullptr, FATAL); break;        // unreachable
  178.                 }
  179.                 return 1;
  180.         } else {
  181.                 return ParseExpPrim(p, nval);
  182.         }
  183. }
  184.  
  185. static int ParseExpMul(char*& p, aint& nval) {
  186.         aint left, right;
  187.         int oper;
  188.         if (!ParseExpUnair(p, left)) return 0;
  189.         while ((oper = need(p, "* / % ")) || (oper = needa(p, "mod", '%'))) {
  190.                 if (!ParseExpUnair(p, right)) return 0;
  191.                 switch (oper) {
  192.                 case '*':
  193.                         left *= right; break;
  194.                 case '/':
  195.                         left = right ? left / right : 0;
  196.                         if (!right) Error("Division by zero");
  197.                         break;
  198.                 case '%':
  199.                         left = right ? left % right : 0;
  200.                         if (!right) Error("Division by zero");
  201.                         break;
  202.                 default: Error("internal error", nullptr, FATAL); break;        // unreachable
  203.                 }
  204.         }
  205.         nval = left;
  206.         return 1;
  207. }
  208.  
  209. static int ParseExpAdd(char*& p, aint& nval) {
  210.         aint left, right;
  211.         int oper;
  212.         if (!ParseExpMul(p, left)) return 0;
  213.         while ((oper = need(p, "+ - "))) {
  214.                 if (!ParseExpMul(p, right)) return 0;
  215.                 if ('-' == oper) right = -right;
  216.                 left += right;
  217.         }
  218.         nval = left;
  219.         return 1;
  220. }
  221.  
  222. static int ParseExpShift(char*& p, aint& nval) {
  223.         aint left, right;
  224.         uint32_t l;
  225.         int oper;
  226.         if (!ParseExpAdd(p, left)) return 0;
  227.         while ((oper = need(p, "<<>>")) || (oper = needa(p, "shl", '<' + '<', "shr", '>' + '>'))) {
  228.                 if (oper == '>' + '>' && *p == '>') {
  229.                         ++p;
  230.                         oper += '>';
  231.                 }
  232.                 if (!ParseExpAdd(p, right)) return 0;
  233.                 switch (oper) {
  234.                 case '<'+'<':
  235.                         left <<= right; break;
  236.                 case '>'+'>':
  237.                         left >>= right; break;
  238.                 case '>'+'>'+'>':
  239.                         l = left; l >>= right; left = l; break;
  240.                 default: Error("internal error", nullptr, FATAL); break;        // unreachable
  241.                 }
  242.         }
  243.         nval = left;
  244.         return 1;
  245. }
  246.  
  247. static int ParseExpMinMax(char*& p, aint& nval) {
  248.         aint left, right;
  249.         int oper;
  250.         if (!ParseExpShift(p, left)) return 0;
  251.         while ((oper = need(p, "<?>?"))) {
  252.                 if (!ParseExpShift(p, right)) return 0;
  253.                 switch (oper) {
  254.                 case '<'+'?':
  255.                         left = left < right ? left : right; break;
  256.                 case '>'+'?':
  257.                         left = left > right ? left : right; break;
  258.                 default: Error("internal error", nullptr, FATAL); break;        // unreachable
  259.                 }
  260.         }
  261.         nval = left;
  262.         return 1;
  263. }
  264.  
  265. static int ParseExpCmp(char*& p, aint& nval) {
  266.         aint left, right;
  267.         int oper;
  268.         if (!ParseExpMinMax(p, left)) return 0;
  269.         while ((oper = need(p, "<=>=< > "))) {
  270.                 if (!ParseExpMinMax(p, right)) return 0;
  271.                 switch (oper) {
  272.                 case '<':
  273.                         left = -(left < right); break;
  274.                 case '>':
  275.                         left = -(left > right); break;
  276.                 case '<'+'=':
  277.                         left = -(left <= right); break;
  278.                 case '>'+'=':
  279.                         left = -(left >= right); break;
  280.                 default: Error("internal error", nullptr, FATAL); break;        // unreachable
  281.                 }
  282.         }
  283.         nval = left;
  284.         return 1;
  285. }
  286.  
  287. static int ParseExpEqu(char*& p, aint& nval) {
  288.         aint left, right;
  289.         int oper;
  290.         if (!ParseExpCmp(p, left)) return 0;
  291.         while ((oper = need(p, "=_==!="))) {
  292.                 if (!ParseExpCmp(p, right)) return 0;
  293.                 left = (('!'+'=') == oper) ? -(left != right) : -(left == right);
  294.         }
  295.         nval = left;
  296.         return 1;
  297. }
  298.  
  299. static int ParseExpBitAnd(char*& p, aint& nval) {
  300.         aint left, right;
  301.         if (!ParseExpEqu(p, left)) return 0;
  302.         while (need(p, "&_") || needa(p, "and", '&')) {
  303.                 if (!ParseExpEqu(p, right)) return 0;
  304.                 left &= right;
  305.         }
  306.         nval = left;
  307.         return 1;
  308. }
  309.  
  310. static int ParseExpBitXor(char*& p, aint& nval) {
  311.         aint left, right;
  312.         if (!ParseExpBitAnd(p, left)) return 0;
  313.         while (need(p, "^ ") || needa(p, "xor", '^')) {
  314.                 if (!ParseExpBitAnd(p, right)) return 0;
  315.                 left ^= right;
  316.         }
  317.         nval = left;
  318.         return 1;
  319. }
  320.  
  321. static int ParseExpBitOr(char*& p, aint& nval) {
  322.         aint left, right;
  323.         if (!ParseExpBitXor(p, left)) return 0;
  324.         while (need(p, "|_") || needa(p, "or", '|')) {
  325.                 if (!ParseExpBitXor(p, right)) return 0;
  326.                 left |= right;
  327.         }
  328.         nval = left;
  329.         return 1;
  330. }
  331.  
  332. static int ParseExpLogAnd(char*& p, aint& nval) {
  333.         aint left, right;
  334.         if (!ParseExpBitOr(p, left)) return 0;
  335.         while (need(p, "&&")) {
  336.                 if (!ParseExpBitOr(p, right)) return 0;
  337.                 left = -(left && right);
  338.         }
  339.         nval = left;
  340.         return 1;
  341. }
  342.  
  343. static int ParseExpLogOr(char*& p, aint& nval) {
  344.         aint left, right;
  345.         if (!ParseExpLogAnd(p, left)) return 0;
  346.         while (need(p, "||")) {
  347.                 if (!ParseExpLogAnd(p, right)) return 0;
  348.                 left = -(left || right);
  349.         }
  350.         nval = left;
  351.         return 1;
  352. }
  353.  
  354. static int ParseExpressionEntry(char*& p, aint& nval) {
  355.         if (ParseExpLogOr(p, nval)) return 1;
  356.         nval = 0;
  357.         return 0;
  358. }
  359.  
  360. int ParseExpression(char*& p, aint& nval) {
  361.         // if relocation is active, do the full evaluation in syntax-error-OFF mode with alternative
  362.         // label values, and remember the result (to compare it with regular evaluation afterward)
  363.         aint relocationVal = 0;
  364.         int relocationRes = 0;
  365.         if (Relocation::type) {
  366.                 char* altP = p;
  367.                 bool osynerr = synerr;
  368.                 synerr = false;
  369.                 Relocation::areLabelsOffset = true;
  370.                 relocationRes = ParseExpressionEntry(altP, relocationVal);
  371.                 Relocation::areLabelsOffset = false;
  372.                 synerr = osynerr;
  373.         }
  374.         // if relocation is off, or the alternative run did finish already, do regular evaluation
  375.         int res = ParseExpressionEntry(p, nval);
  376.         // set the Relocation::isResultAffected if the two alternative results are different
  377.         if (res && relocationRes) {
  378.                 const bool isAffected = (relocationVal != nval);
  379.                 Relocation::isResultAffected |= isAffected;
  380.                 Relocation::deltaType =
  381.                         (Relocation::alternative_offset == (relocationVal - nval)) ? Relocation::REGULAR :
  382.                         ((Relocation::HIGH == Relocation::type) && ((Relocation::alternative_offset >> 8) == (relocationVal - nval))) ? Relocation::HIGH :
  383.                         Relocation::OFF;
  384.         }
  385.         return res;
  386. }
  387.  
  388. int ParseExpressionNoSyntaxError(char*& lp, aint& val) {
  389.         bool osynerr = synerr;
  390.         synerr = false;
  391.         int ret_val = ParseExpression(lp, val);
  392.         synerr = osynerr;
  393.         return ret_val;
  394. }
  395.  
  396. static int ParseExpressionInSubstitution(char *& lp, aint& val) {
  397.         assert(!IsSubstituting);
  398.         IsSubstituting = true;
  399.         int ret_val = ParseExpressionNoSyntaxError(lp, val);
  400.         IsSubstituting = false;
  401.         return ret_val;
  402. }
  403.  
  404. // returns 0 on syntax error, 1 on expression which is not enclosed in parentheses
  405. // 2 when whole expression is in [] or () (--syntax=b/B affects when "2" is reported)
  406. int ParseExpressionMemAccess(char*& p, aint& nval) {
  407.         const EBracketType bt = OpenBracket(p);
  408.         // if round parenthesis starts the expression, calculate pointer where it ends (and move "p" back on "(")
  409.         char* const expectedEndBracket = (BT_ROUND == bt) ? ParenthesesEnd(--p) : nullptr;
  410.         if (!ParseExpression(p, nval)) return 0;        // evaluate expression
  411.         if (BT_NONE == bt) return 1;                            // no parentheses are always "value"
  412.         if (BT_ROUND == bt) return (expectedEndBracket == p) ? 2 : 1;   // round parentheses are "memory" when end is as expected
  413.         if (CloseBracket(p)) return 2;                          // square brackets must be closed properly, then it is "memory"
  414.         return 0;       // curly brackets are not detect by OpenBracket, but if they would, it would work same as square here
  415. }
  416.  
  417. void ParseAlignArguments(char* & src, aint & alignment, aint & fill) {
  418.         SkipBlanks(src);
  419.         const char * const oldSrc = src;
  420.         fill = -1;
  421.         if (!ParseExpression(src, alignment)) {
  422.                 alignment = -1;
  423.                 return;
  424.         }
  425.         if (Relocation::type) {
  426.                 WarningById(W_RELOCATABLE_ALIGN);
  427.         }
  428.         // check if alignment value is power of two (0..15-th power only)
  429.         if (alignment < 1 || (1<<15) < alignment || (alignment & (alignment-1))) {
  430.                 Error("[ALIGN] Illegal align", oldSrc, SUPPRESS);
  431.                 alignment = 0;
  432.                 return;
  433.         }
  434.         if (!comma(src)) return;
  435.         if (!ParseExpressionEntry(lp, fill)) {
  436.                 Error("[ALIGN] fill-byte expected after comma", bp, IF_FIRST);
  437.                 fill = -1;
  438.         } else if (fill < 0 || 255 < fill) {
  439.                 Error("[ALIGN] Illegal align fill-byte", oldSrc, SUPPRESS);
  440.                 fill = -1;
  441.         }
  442. }
  443.  
  444. static bool ReplaceDefineInternal(char* lp, char* const nl) {
  445.         int definegereplaced = 0,dr;
  446.         char* rp = nl,* nid;
  447.         const char* ver;
  448.         bool isDefDir = false;  // to remember if one of DEFINE-related directives was used
  449.         bool afterNonAlphaNum, afterNonAlphaNumNext = true;
  450.         char defarrayCountTxt[16] = { 0 };
  451.         while (*lp && ((rp - nl) < LINEMAX)) {
  452.                 const char c1 = lp[0], c2 = lp[1];
  453.                 afterNonAlphaNum = afterNonAlphaNumNext;
  454.                 afterNonAlphaNumNext = !isalnum((byte)c1);
  455.                 if (c1 == '/' && c2 == '*') {   // block-comment local beginning (++block_nesting)
  456.                         lp += 2;
  457.                         ++comlin;
  458.                         continue;
  459.                 }
  460.                 if (comlin) {
  461.                         if (c1 == '*' && c2 == '/') {
  462.                                 lp += 2;
  463.                                 // insert space into line, if the block ending may have affected parsing of line
  464.                                 if (1 == comlin) {
  465.                                         *rp++ = ' ';            // ^^ otherwise this line is completely commented out
  466.                                 }
  467.                                 --comlin;       // decrement block comment counter
  468.                         } else {
  469.                                 ++lp;           // just skip all characters inside comment block
  470.                         }
  471.                         continue;
  472.                 }
  473.                 // for following code (0 == comlin) (unless it has its own parse loop)
  474.  
  475.                 // single line comments -> finish
  476.                 if (c1 == ';' || (c1 == '/' && c2 == '/')) {
  477.                         // set empty eol line comment, if the source of data is still the original "line" buffer
  478.                         if (!eolComment && line <= lp && lp < line+LINEMAX) eolComment = lp;
  479.                         break;
  480.                 }
  481.  
  482.                 // strings parsing
  483.                 if (afterNonAlphaNum && (c1 == '"' || c1 == '\'')) {
  484.                         *rp++ = *lp++;                          // copy the string delimiter (" or ')
  485.                         // apostrophe inside apostrophes ('') will parse as end + start of another string
  486.                         // which sort of "accidentally" leads to correct final results
  487.                         while (*lp && c1 != *lp) {      // until end of current string is reached (or line ends)
  488.                                 // inside double quotes the backslash should escape (anything after it)
  489.                                 if ('"' == c1 && '\\' == *lp && lp[1]) *rp++ = *lp++;   // copy escaping backslash extra
  490.                                 *rp++ = *lp++;                  // copy string character
  491.                         }
  492.                         if (*lp) *rp++ = *lp++;         // copy the ending string delimiter (" or ')
  493.                         continue;
  494.                 }
  495.  
  496.                 if (!isLabelStart(lp, false)) {
  497.                         *rp++ = *lp++;
  498.                         continue;
  499.                 }
  500.  
  501.                 // update "is define-related directive" for remainder of the line
  502.                 char* kp = lp;
  503.                 isDefDir |= afterNonAlphaNum && (cmphstr(kp, "define+") || cmphstr(kp, "define")
  504.                         || cmphstr(kp, "undefine") || cmphstr(kp, "defarray+") || cmphstr(kp, "defarray")
  505.                         || cmphstr(kp, "ifdef") || cmphstr(kp, "ifndef"));
  506.                 // if DEFINE-related directive was used, only macro-arguments are substituted
  507.                 // in the remaining part of the line, the define-based substitution is inhibited till EOL
  508.  
  509.                 // The following loop is recursive-like macro/define substitution, the `*lp` here points
  510.                 // at alphabet/underscore char, marking start of "id" string, and it will be parsed by
  511.                 // sub-id parts, delimited by underscores, each combination of consecutive sub-ids may
  512.                 // be substituted by some macro argument or define.
  513.  
  514.                 //TODO - maybe consider the substitution search to go downward, from longest term to shortest subterm
  515.                 ResetGrowSubId();
  516.                 char* nextSubIdLp = lp, * wholeIdLp = lp;
  517.                 do { //while(islabchar(*lp));
  518.                         nid = GrowSubId(lp);            // grow the current sub-id part by part, checking each combination for substitution
  519.                         // defines/macro arguments can substitute in the middle of ID only if they don't start with underscore
  520.                         const bool canSubstituteInside = '_' != nid[0] || nextSubIdLp == wholeIdLp;
  521.                         if (macrolabp && canSubstituteInside && (ver = MacroDefineTable.getverv(nid))) {
  522.                                 dr = 2;                 // macro argument substitution is possible
  523.                         } else if (!isDefDir && canSubstituteInside && (ver = DefineTable.Get(nid))) {
  524.                                 dr = 1;                 // DEFINE substitution is possible
  525.                                 //handle DEFARRAY case
  526.                                 if (DefineTable.DefArrayList) {
  527.                                         ver = nid;      // in case of some error, just copy the array id "as is"
  528.                                         CStringsList* a = DefineTable.DefArrayList;
  529.                                         while (White(*lp)) GrowSubIdByExtraChar(lp);
  530.                                         aint val;
  531.                                         if ('[' != *lp) Error("[ARRAY] Expression error", nextSubIdLp, SUPPRESS);
  532.                                         if ('[' == *lp && '#' == lp[1] && ']' == lp[2]) {       // calculate size of defarray
  533.                                                 lp += 3;
  534.                                                 val = 0;
  535.                                                 while (a) {
  536.                                                         ++val;
  537.                                                         a = a->next;
  538.                                                 }
  539.                                                 sprintf(defarrayCountTxt, "%d", val);
  540.                                                 ver = defarrayCountTxt;
  541.                                         } else {
  542.                                                 char* expLp = lp + ('[' == *lp);        // the '[' will become part of subId in didParseBrackets
  543.                                                 IsLabelNotFound = false;
  544.                                                 bool didParseBrackets = '[' == *lp && GrowSubIdByExtraChar(lp) && ParseExpressionInSubstitution(lp, val) && ']' == *lp;
  545.                                                 if (didParseBrackets && !IsLabelNotFound) {
  546.                                                         // expression was successfully parsed and all values were known
  547.                                                         ++lp;
  548.                                                         while (0 < val && a) {
  549.                                                                 a = a->next;
  550.                                                                 --val;
  551.                                                         }
  552.                                                         if (val < 0 || NULL == a) {
  553.                                                                 *defarrayCountTxt = 0;          // substitute with empty string
  554.                                                                 ver = defarrayCountTxt;
  555.                                                                 Error("[ARRAY] index not in 0..<Size-1> range", nextSubIdLp, SUPPRESS);
  556.                                                         } else {
  557.                                                                 ver = a->string;        // substitute with array value
  558.                                                         }
  559.                                                 } else {        // no substition of array possible at this time (index eval / syntax error)
  560.                                                         lp = expLp;                             // restore lp in case expression parser went ahead a lot
  561.                                                         dr = -1;// write into output, but don't count as replacement
  562.                                                 }
  563.                                         }
  564.                                 }
  565.                         } else {
  566.                                 dr = 0;                 // no possible substitution found
  567.                                 ver = nid;
  568.                         }
  569.                         // check if no substition was found, and there's no more chars to extend SubId
  570.                         if (0 == dr && !islabchar(*lp)) {
  571.                                 lp = nextSubIdLp;               // was fully extended, no match, "eat" first subId
  572.                                 ResetGrowSubId();
  573.                                 ver = GrowSubId(lp);    // find the first SubId again, for the copy
  574.                                 dr = -1;                                // write into output, but don't count as replacement
  575.                         }
  576.                         if (0 < dr) definegereplaced = 1;               // above zero => count as replacement
  577.                         if (0 != dr) {                          // any non-zero dr => write to the output
  578.                                 while (*ver && ((rp - nl) < LINEMAX)) *rp++ = *ver++;           // replace the string into target buffer
  579.                                 // reset subId parser to catch second+ subId in current Id
  580.                                 ResetGrowSubId();
  581.                                 nextSubIdLp = lp;
  582.                         }
  583.                         // continue with extending the subId, if there's still something to parse
  584.                 } while(islabchar(*lp));
  585.         } // while(*lp)
  586.         // add line terminator to the output buffer
  587.         *rp++ = 0;
  588.         if (LINEMAX <= (rp - nl)) {
  589.                 Error("line too long after macro expansion", nl, SUPPRESS);
  590.         }
  591.         // check if whole line is just blanks, then return just empty one
  592.         rp = nl;
  593.         if (SkipBlanks(rp)) *nl = 0;
  594.         substitutedLine = nl;           // set global pointer to the latest substituted version
  595.         return definegereplaced;
  596. }
  597.  
  598. char* ReplaceDefine(char* lp) {
  599.         // do first replacement into sline buffer (and if no define replace done, just return it)
  600.         if (!ReplaceDefineInternal(lp, sline)) return sline;
  601.         // Some define were replaced, line is in "sline", now ping-pong it between sline and sline2
  602.         int defineReplaceRecursion = 0;
  603.         while (defineReplaceRecursion++ < 10) {
  604.                 if (!ReplaceDefineInternal(sline, sline2)) return sline2;
  605.                 if (!ReplaceDefineInternal(sline2, sline)) return sline;
  606.         }
  607.         Error("Unable to finish substitions, line after 20th iteration", sline, SUPPRESS);
  608.         return sline;
  609. }
  610.  
  611. void SetLastParsedLabel(const char* label) {
  612.         if (LastParsedLabel) free(LastParsedLabel);
  613.         if (nullptr != label) {
  614.                 LastParsedLabel = STRDUP(label);
  615.                 if (nullptr == LastParsedLabel) ErrorOOM();
  616.                 LastParsedLabelLine = CompiledCurrentLine;
  617.         } else {
  618.                 LastParsedLabel = nullptr;
  619.                 LastParsedLabelLine = 0;
  620.         }
  621. }
  622.  
  623. void ParseLabel() {
  624.         if (White()) return;
  625.         if (Options::syx.IsPseudoOpBOF && ParseDirective(true)) {
  626.                 if (!SkipBlanks()) Error("Unexpected", lp);
  627.                 return;
  628.         }
  629.         char temp[LINEMAX], * tp = temp, * ttp;
  630.         aint val, equPageNum = LABEL_PAGE_UNDEFINED, smcOffset = 0;
  631.         // copy the label name into `temp` array
  632.         while (*lp && !White() && *lp != ':' && *lp != '=' && *lp != '+') {
  633.                 *tp = *lp; ++tp; ++lp;
  634.         }
  635.         *tp = 0;
  636.         // handle the special SMC_offset syntax "<label>+<single_digit>"
  637.         if ('+' == lp[0] && isdigit(byte(lp[1])) && !isalnum(byte(lp[2]))) {
  638.                 smcOffset = lp[1] - '0';
  639.                 lp += 2;
  640.         }
  641.         // handle the special SMC_offset syntax "<label>+*" to target significant immediate of the instruction
  642.         if ('+' == lp[0] && '*' == lp[1] && !isalnum(byte(lp[2]))) {
  643.                 assert(!sourcePosStack.empty());
  644.                 smcOffset = 1;                                  // heuristic value 1 for first pass or when something fails
  645.                 lp += 2;
  646.                 if (1 == pass) {
  647.                         // put the current source position into smart-smc (if first pass, or missing record)
  648.                         smartSmcLines.push_back(sourcePosStack.back());
  649.                         smartSmcLines.back().colBegin = ~0U;            // mark as unresolved
  650.                 } else {
  651.                         if ((smartSmcLines.size() <= smartSmcIndex)
  652.                                 || smartSmcLines.at(smartSmcIndex) != sourcePosStack.back()) {
  653.                                 Error("mismatch of smart-SMC positions between passes");
  654.                         } else {
  655.                                 auto & smartSmcLine = smartSmcLines.at(smartSmcIndex);
  656.                                 if (~0U == smartSmcLine.colBegin) {
  657.                                         Error("unresolved smart-SMC symbol (no significant target)");
  658.                                 } else {
  659.                                         smcOffset = smartSmcLine.colBegin;      // use the smart value from previous pass
  660.                                         smartSmcLine.colBegin = ~0U;            // mark as unsolved again
  661.                                 }
  662.                         }
  663.                 }
  664.                 ++smartSmcIndex;
  665.         }
  666.         if (*lp == ':') ++lp;   // eat the optional colon after label
  667.         tp = temp;
  668.         SkipBlanks();
  669.         IsLabelNotFound = false;
  670.         if (isdigit((byte)*tp)) {
  671.                 if (smcOffset) {
  672.                         Error("Temporary label can't use SMC-offset");
  673.                         return;
  674.                 }
  675.                 ttp = tp;
  676.                 while (*ttp && isdigit((byte)*ttp)) ++ttp;
  677.                 if (*ttp) {
  678.                         Error("Invalid temporary label (not a number)", temp);
  679.                         return;
  680.                 }
  681.                 if (NeedEQU() || NeedDEFL()) {
  682.                         Error("Number labels are allowed as address labels only, not for DEFL/=/EQU", temp, SUPPRESS);
  683.                         return;
  684.                 }
  685.                 val = atoi(tp);
  686.                 if (!TemporaryLabelTable.InsertRefresh(val)) {
  687.                         Error("Temporary labels flow differs in this pass (missing/new temporary label or final pass source difference)");
  688.                 }
  689.         } else {
  690.                 if (isMacroNext()) {
  691.                         if (smcOffset) Error("Macro name can't use SMC-offset");
  692.                         else SetLastParsedLabel(tp);    // store raw label into "last parsed" without adding module/etc
  693.                         return;                                 // and don't add it to labels table at all
  694.                 }
  695.                 bool IsDEFL = NeedDEFL(), IsEQU = NeedEQU();
  696.                 if (IsDEFL || IsEQU) {
  697.                         Relocation::isResultAffected = false;
  698.                         if (!ParseExpressionNoSyntaxError(lp, val)) {
  699.                                 Error("Expression error", lp);
  700.                                 val = 0;
  701.                         }
  702.                         if (IsLabelNotFound && IsDEFL) Error("Forward reference", NULL, EARLY);
  703.                         // check for explicit page defined by EQU
  704.                         if (IsEQU && comma(lp)) {
  705.                                 if (!ParseExpressionNoSyntaxError(lp, equPageNum)) {
  706.                                         Error("Expression error", lp);
  707.                                         equPageNum = LABEL_PAGE_UNDEFINED;
  708.                                 }
  709.                         }
  710.  
  711.                         // after EQU/DEFL expressions there must be <EOL>, anything else is syntax error
  712.                         // this was added in v1.17.1 after realizing the line `label=$+1 and 7`
  713.                         // does evaluate whole "$+1 and 7" as expression, not as instruction `and`
  714.                         // (same problem exists with and/or/xor and if you have macro named after operator)
  715.                         if (!SkipBlanks(lp)) {
  716.                                 Error("Unexpected", lp);
  717.                                 SkipToEol(lp);
  718.                         }
  719.  
  720.                 } else {
  721.                         int gl = 0;
  722.                         char* p = lp,* n;
  723.                         SkipBlanks(p);
  724.                         if (*p == '@') {
  725.                                 ++p; gl = 1;
  726.                         }
  727.                         if ((n = GetID(p)) && StructureTable.Emit(n, tp, p, gl)) {
  728.                                 if (smcOffset) Error("Structure instance can't use SMC-offset");
  729.                                 lp = p;
  730.                                 // this was instancing STRUCT, make it also define "main" label for future "local" ones
  731.                                 tp = ValidateLabel(tp, true);
  732.                                 if (tp) delete[] tp;
  733.                                 return;
  734.                         }
  735.                         val = CurAddress;
  736.                 }
  737.                 val += smcOffset;
  738.                 ttp = tp;
  739.                 if (!(tp = ValidateLabel(tp, true))) {
  740.                         return;
  741.                 }
  742.                 // Copy label name to last parsed label variable
  743.                 if (!IsDEFL) SetLastParsedLabel(tp);
  744.                 unsigned traits = (IsEQU ? LABEL_IS_EQU : 0) | (IsDEFL ? LABEL_IS_DEFL : 0) | (smcOffset ? LABEL_IS_SMC : 0);
  745.                 if (pass == LASTPASS) {
  746.  
  747.                         SLabelTableEntry* label = LabelTable.Find(tp, true);
  748.                         if (nullptr == label && IsDEFL) {       // DEFL labels can be defined as late as needed (including pass3)
  749.                                 if (LabelTable.Insert(tp, val, traits)) label = LabelTable.Find(tp, true);
  750.                         }
  751.                         if (nullptr == label) {         // should have been already defined before last pass
  752.                                 Error("Label not found", tp);
  753.                                 delete[] tp;
  754.                                 return;
  755.                         }
  756.                         if (IsDEFL) {           //re-set DEFL value
  757.                                 LabelTable.Insert(tp, val, traits);
  758.                         } else if (IsSldExportActive()) {
  759.                                 // SLD (Source Level Debugging) tracing-data logging
  760.                                 WriteToSldFile(IsEQU ? -1 : label->page, val, IsEQU ? 'D' : 'F', tp);   //version 0
  761.                                 WriteToSldFile(IsEQU ? -1 : label->page, val, 'L', ExportLabelToSld(ttp, label));       //version 1
  762.                         }
  763.  
  764.                         if (val != label->value) {
  765.                                 char* buf = new char[LINEMAX];
  766.  
  767.                                 SPRINTF2(buf, LINEMAX, "previous value %u not equal %u", label->value, val);
  768.                                 Warning("Label has different value in pass 3", buf);
  769.                                 LabelTable.Update(tp, val);
  770.  
  771.                                 delete[] buf;
  772.                         }
  773.                 } else if (pass == 2 && !LabelTable.Insert(tp, val, traits, equPageNum)) {
  774.                         if (!LabelTable.Update(tp, val)) assert(false); // unreachable, update will always work after insert failed
  775.                 } else if (pass == 1 && !LabelTable.Insert(tp, val, traits, equPageNum)) {
  776.                         Error("Duplicate label", tp, EARLY);
  777.                 }
  778.  
  779. // TODO v2.x: this is too complicated in current version: Unreal/Cspect already expect
  780. // EQU/DEFL to be current page or "ROM" = not a big deal as they did change in v1.x course already.
  781. // But also struct labels are set as EQU ones, so this has to split, and many other details.
  782. // (will also need more than LABEL_PAGE_UNDEFINED value to deal with more states)
  783. //              if (IsEQU && comma(lp)) {       // Device extension: "<label> EQU <address>,<page number>"
  784. //                      if (!DeviceID) {
  785. //                              Error("EQU can set page to label only in device mode", line);
  786. //                              SkipToEol(lp);
  787. //                      } else if (!ParseExpression(lp, oval)) {        // try to read page number into "oval"
  788. //                              Error("Expression error", lp);
  789. //                              oval = -1;
  790. //                      } else if (oval < 0 || Device->PagesCount <= oval) {
  791. //                              ErrorInt("Invalid page number", oval);
  792. //                              oval = -1;
  793. //                      } else {
  794. //                              if (val < 0 || 0xFFFF < val) Warning("The EQU address is outside of 16bit range", line);
  795. //                              CLabelTableEntry* equLabel = LabelTable.Find(tp, true); // must be already defined + found
  796. //                              equLabel->page = oval;                  // set it's page number
  797. //                      }
  798. //              }
  799.  
  800.                 delete[] tp;
  801.         }
  802. }
  803.  
  804. int ParseMacro() {
  805.         int gl = 0, r = 0;
  806.         char* p = lp, *n;
  807.         SkipBlanks(p);
  808.         if (*p == '@') {
  809.                 gl = 1; ++p;
  810.         }
  811.         if (!(n = GetID(p))) {
  812.                 return 0;
  813.         }
  814.  
  815.         if (!gl) r = MacroTable.Emit(n, p);             // global '@' operator inhibits macros
  816.         if (r == 2) return 1;   // successfully emitted
  817.         if (r == 1) {                   // error reported
  818.                 lp = p;
  819.                 return 0;
  820.         }
  821.  
  822.         // not a macro, see if it's structure
  823.         if (StructureTable.Emit(n, nullptr, p, gl)) {
  824.                 lp = p;
  825.                 return 1;
  826.         }
  827.  
  828.         return 0;
  829. }
  830.  
  831. void ParseInstruction() {
  832.         if ('@' == *lp) ++lp;           // skip single '@', if it was used to inhibit macro expansion
  833.         if (ParseDirective()) return;
  834.         Z80::GetOpCode();
  835. }
  836.  
  837. static const byte win2dos[] = //taken from HorrorWord %)))
  838. {
  839.         0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
  840.         0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,
  841.         0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xF0, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE,
  842.         0xDF, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF1, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0x20,
  843.         0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
  844.         0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,
  845.         0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
  846.         0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF
  847. };
  848.  
  849. //#define DEBUG_COUT_PARSE_LINE
  850.  
  851. // returns 1 when already fully processed (part of DUP/etc)
  852. int PrepareLine() {
  853.         ListSilentOrExternalEmits();
  854.  
  855.         ++CompiledCurrentLine;
  856.         if (!RepeatStack.empty()) {
  857.                 SRepeatStack& dup = RepeatStack.top();
  858.                 if (!dup.IsInWork) {
  859.                         lp = line;
  860.                         dup.Pointer->next = new CStringsList(lp);
  861.                         dup.Pointer = dup.Pointer->next;
  862. #ifdef DEBUG_COUT_PARSE_LINE
  863.                         fprintf(stderr, ">%d %d %c%ld-%d [%s]\n", pass, CurSourcePos.line,
  864.                                         (!RepeatStack.empty() && RepeatStack.top().IsInWork ? '!' : '.'),RepeatStack.size(),
  865.                                         (!RepeatStack.empty() ? RepeatStack.top().Level : 0), line);
  866. #endif
  867.                         // check if there's some label at beginning of the line, skip it
  868.                         if (islabchar(*lp)) {
  869.                                 // if directives are enabled at beginning of line, check if it is nested DUP/REPT/WHILE/EDUP...
  870.                                 if (Options::syx.IsPseudoOpBOF && ParseDirective_REPT()) return 1;
  871.                                 // skip label chars and trailing colon
  872.                                 while (islabchar(*lp)) ++lp;
  873.                                 if (':' == *lp) ++lp;
  874.                         }
  875.                         // catch any nested DUP/WHILE/REPT and EDUP directives
  876.                         ParseDirective_REPT();
  877.                         return 1;
  878.                 }
  879.         }
  880. #ifdef DEBUG_COUT_PARSE_LINE
  881.         fprintf(stderr, "|%d %d %c%ld-%d [%s]\n", pass, CurSourcePos.line,
  882.                         (!RepeatStack.empty() && RepeatStack.top().IsInWork ? '!' : '.'), RepeatStack.size(),
  883.                         (!RepeatStack.empty() ? RepeatStack.top().Level : 0), line);
  884. #endif
  885.         lp = ReplaceDefine(line);
  886.  
  887. #ifdef DEBUG_COUT_PARSE_LINE
  888.         fprintf(stderr,"rdOut [%s]->[%s] %d\n", line, lp, comlin);
  889. #endif
  890.  
  891.         // update current address by memory wrapping, current page, etc... (before the label is defined)
  892.         if (DeviceID)   Device->CheckPage(CDevice::CHECK_NO_EMIT);
  893.         ListAddress = CurAddress;
  894.  
  895.         if (!ConvertEncoding) {
  896.                 byte* lp2 = (byte*) lp;
  897.                 while (*lp2) {
  898.                         if (128 <= *lp2) {
  899.                                 *lp2 = win2dos[(*lp2) - 128];
  900.                         }
  901.                         ++lp2;
  902.                 }
  903.         }
  904.         return 0;
  905. }
  906.  
  907. bool PrepareNonBlankMultiLine(char*& p) {
  908.         // loop while the current line is blank-only (read further lines until EOF or non-blank char)
  909.         while (SkipBlanks(p)) {
  910.                 // if inside macro system, but without any more macro-lines in buffer, act as if "EOF"
  911.                 // (to not leak into reading actual file while the macro is executing)
  912.                 if (listmacro && nullptr == lijstp) return false;
  913.                 // list the current (old) line
  914.                 ListFile();
  915.                 // read the next line
  916.                 if (!ReadLine()) return false;
  917.                 PrepareLine();
  918.                 p = lp;
  919.         }
  920.         return true;
  921. }
  922.  
  923. void ParseLine(bool parselabels) {
  924.  
  925.         if (PrepareLine()) return;
  926.  
  927.         if (eolComment && IsSldExportActive()) SldTrackComments();
  928.  
  929.         if (!*lp) {
  930.                 char *srcNonWhiteChar = line;
  931.                 SkipBlanks(srcNonWhiteChar);
  932.                 // check if only "end-line" comment remained, treat that one as "empty" line too
  933.                 if (';' == srcNonWhiteChar[0] || ('/' == srcNonWhiteChar[0] && '/' == srcNonWhiteChar[1]))
  934.                         srcNonWhiteChar = lp;                   // force srcNonWhiteChar to point to 0
  935.                 if (*srcNonWhiteChar || comlin) {       // non-empty source line turned into nothing
  936.                         ListFile(true);                                 // or empty source inside comment-block -> "skipped"
  937.                 } else {
  938.                         ListFile();                                             // empty source line outside of block-comment -> "normal"
  939.                 }
  940.                 return;
  941.         }
  942.  
  943.         if (parselabels) ParseLabel();
  944.         if (!SkipBlanks()) ParseMacro();
  945.         if (!SkipBlanks()) ParseInstruction();
  946.         if (!SkipBlanks()) Error("Unexpected", lp);
  947.         ListFile();
  948. }
  949.  
  950. void ParseLineSafe(bool parselabels) {
  951.         char* tmp = NULL, * tmp2 = NULL;
  952.         char* rp = lp;
  953.         if (sline[0] > 0) {
  954.                 tmp = STRDUP(sline);
  955.                 if (tmp == NULL) ErrorOOM();
  956.         }
  957.         if (sline2[0] > 0) {
  958.                 tmp2 = STRDUP(sline2);
  959.                 if (tmp2 == NULL) ErrorOOM();
  960.         }
  961.  
  962.         ParseLine(parselabels);
  963.  
  964.         *sline = 0;
  965.         *sline2 = 0;
  966.  
  967.         if (tmp2 != NULL) {
  968.                 STRCPY(sline2, LINEMAX2, tmp2);
  969.                 free(tmp2);
  970.         }
  971.         if (tmp != NULL) {
  972.                 STRCPY(sline, LINEMAX2, tmp);
  973.                 free(tmp);
  974.         }
  975.         lp = rp;
  976. }
  977.  
  978. void ParseStructLabel(CStructure* st) {
  979.         char* tp, temp[LINEMAX];
  980.         if (PreviousIsLabel) {
  981.                 free(PreviousIsLabel);
  982.                 PreviousIsLabel = nullptr;
  983.         }
  984.         if (White()) {
  985.                 return;
  986.         }
  987.         tp = temp;
  988.         if (*lp == '.') {
  989.                 ++lp;
  990.         }
  991.         while (*lp && islabchar(*lp)) {
  992.                 *tp = *lp; ++tp; ++lp;
  993.         }
  994.         *tp = 0;
  995.         if (*lp == ':') {
  996.                 ++lp;
  997.         }
  998.         tp = temp; SkipBlanks();
  999.         if (isdigit((byte)*tp)) {
  1000.                 Error("[STRUCT] Number labels not allowed within structs"); return;
  1001.         }
  1002.         PreviousIsLabel = STRDUP(tp);
  1003.         if (PreviousIsLabel == NULL) ErrorOOM();
  1004.         st->AddLabel(tp);
  1005. }
  1006.  
  1007. void ParseStructMember(CStructure* st) {
  1008.         aint val, len;
  1009.         bp = lp;
  1010.         Relocation::isResultAffected = false;
  1011.         Relocation::EType deltaType = Relocation::OFF;
  1012.         switch (GetStructMemberId(lp)) {
  1013.         case SMEMBBLOCK:
  1014.                 if (!ParseExpression(lp, len)) {
  1015.                         len = 1;
  1016.                         Error("[STRUCT] Expression expected");
  1017.                 }
  1018.                 if (comma(lp)) {
  1019.                         if (!ParseExpression(lp, val)) {
  1020.                                 val = 0;
  1021.                                 Error("[STRUCT] Expression expected");
  1022.                         }
  1023.                         check8(val);
  1024.                         val &= 255;
  1025.                 } else {
  1026.                         val = -1;
  1027.                 }
  1028.                 st->AddMember(new CStructureEntry2(st->noffset, len, val, deltaType, SMEMBBLOCK));
  1029.                 break;
  1030.         case SMEMBBYTE:
  1031.                 if (!ParseExpression(lp, val)) val = 0;
  1032.                 check8(val);
  1033.                 deltaType = Relocation::isResultAffected ? Relocation::deltaType : Relocation::OFF;
  1034.                 st->AddMember(new CStructureEntry2(st->noffset, 1, val, deltaType, SMEMBBYTE));
  1035.                 Relocation::resolveRelocationAffected(INT_MAX, Relocation::HIGH);       // clear flags + warn when can't be relocated
  1036.                 break;
  1037.         case SMEMBWORD:
  1038.                 if (!ParseExpression(lp, val)) val = 0;
  1039.                 check16(val);
  1040.                 deltaType = Relocation::isResultAffected ? Relocation::deltaType : Relocation::OFF;
  1041.                 st->AddMember(new CStructureEntry2(st->noffset, 2, val, deltaType, SMEMBWORD));
  1042.                 Relocation::resolveRelocationAffected(INT_MAX); // clear flags + warn when can't be relocated
  1043.                 break;
  1044.         case SMEMBD24:
  1045.                 if (!ParseExpression(lp, val)) val = 0;
  1046.                 check24(val);
  1047.                 st->AddMember(new CStructureEntry2(st->noffset, 3, val, deltaType, SMEMBD24));
  1048.                 break;
  1049.         case SMEMBDWORD:
  1050.                 if (!ParseExpression(lp, val)) val = 0;
  1051.                 st->AddMember(new CStructureEntry2(st->noffset, 4, val, deltaType, SMEMBDWORD));
  1052.                 break;
  1053.         case SMEMBTEXT:
  1054.                 {
  1055.                         if (!ParseExpression(lp, len) || len < 1 || CStructureEntry2::TEXT_MAX_SIZE < len) {
  1056.                                 Error("[STRUCT] Expression for length of text expected (1..8192)");
  1057.                                 SkipToEol(lp);
  1058.                                 break;
  1059.                         }
  1060.                         byte* textData = new byte[len]();       // zero-initialized for stable binary results
  1061.                         if (nullptr == textData) ErrorOOM();
  1062.                         if (comma(lp)) {                // if comma then init data array explicitly
  1063.                                 GetStructText(lp, len, textData);
  1064.                         } else if (SkipBlanks(lp)) {
  1065.                                 // if empty without comma, init with the zeroed values
  1066.                         } else {
  1067.                                 Error("[STRUCT] Comma expected", lp, SUPPRESS); // syntax error
  1068.                         }
  1069.                         st->AddMember(new CStructureEntry2(st->noffset, len, textData));
  1070.                 }
  1071.                 break;
  1072.         case SMEMBALIGN:
  1073.         {
  1074.                 aint val, fill;
  1075.                 ParseAlignArguments(lp, val, fill);
  1076.                 if (-1 == val) val = 4;
  1077.                 if (st->maxAlignment < val) st->maxAlignment = val;     // update structure "max alignment"
  1078.                 aint bytesToAdvance = (~st->noffset + 1) & (val - 1);
  1079.                 if (bytesToAdvance < 1) break;          // already aligned, nothing to do
  1080.                 // create alignment block
  1081.                 st->AddMember(new CStructureEntry2(st->noffset, bytesToAdvance, fill, deltaType, SMEMBBLOCK));
  1082.                 break;
  1083.         }
  1084.         default:
  1085.                 char* pp = lp,* n;
  1086.                 int gl = 0;
  1087.                 CStructure* s;
  1088.                 SkipBlanks(pp);
  1089.                 if (*pp == '@') {
  1090.                         ++pp;
  1091.                         gl = 1;
  1092.                 }
  1093.                 if ((n = GetID(pp)) && (s = StructureTable.zoek(n, gl))) {
  1094.                         char* structName = st->naam;    // need copy of pointer so cmphstr can advance it in case of match
  1095.                         if (cmphstr(structName, n)) {
  1096.                                 Error("[STRUCT] Can't include itself", NULL);
  1097.                                 SkipToEol(pp);
  1098.                                 lp = pp;
  1099.                                 break;
  1100.                         }
  1101.                         if (s->maxAlignment && ((~st->noffset + 1) & (s->maxAlignment - 1))) {
  1102.                                 // Inserted structure did use ALIGN in definition and it is misaligned here
  1103.                                 char warnTxt[LINEMAX];
  1104.                                 SPRINTF3(warnTxt, LINEMAX,
  1105.                                                  "Struct %s did use ALIGN %d in definition, but here it is misaligned by %d bytes",
  1106.                                                  s->naam, s->maxAlignment, ((~st->noffset + 1) & (s->maxAlignment - 1)));
  1107.                                 Warning(warnTxt);
  1108.                         }
  1109.                         lp = pp;
  1110.                         st->CopyLabels(s);
  1111.                         st->CopyMembers(s, lp);
  1112.                 }
  1113.                 break;
  1114.         }
  1115.         Relocation::checkAndWarn();
  1116. }
  1117.  
  1118. void ParseStructLine(CStructure* st) {
  1119.         ++CompiledCurrentLine;
  1120.         lp = ReplaceDefine(line);
  1121.         if (!*lp) return;
  1122.         ParseStructLabel(st);
  1123.         if (SkipBlanks()) return;
  1124.         ParseStructMember(st);
  1125.         if (SkipBlanks()) return;
  1126.         if (*lp) Error("[STRUCT] Unexpected", lp);
  1127. }
  1128.  
  1129. //eof parser.cpp
  1130.