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