• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

z00m128 / sjasmplus / 26128377223

19 May 2026 10:10PM UTC coverage: 97.798% (+0.5%) from 97.312%
26128377223

push

github

ped7g
CI: try switch to lcov for coverage

10128 of 10356 relevant lines covered (97.8%)

172537.11 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

99.35
/sjasm/parser.cpp
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) {
1,668,572✔
38
        int res = 0;
1,668,572✔
39
        if (SkipBlanks(p)) {
1,668,572✔
40
                return 0;
1,938✔
41
        }
42
        if (*p == '(') {
1,666,634✔
43
                ++p;
22,144✔
44
                res = ParseExpressionEntry(p, nval);
22,144✔
45
                if (!need(p, ')')) {
22,144✔
46
                        Error("')' expected");
15✔
47
                        return 0;
15✔
48
                }
49
        } else if (DeviceID && *p == '{') {                // read WORD/BYTE from virtual device memory
1,644,490✔
50
                char* const readMemP = p;
20,953✔
51
                const int byteOnly = cmphstr(++p, "b");
20,953✔
52
                // switch off alternative relocation evaluation for the address value
53
                const bool oldAreLabelsOffset = Relocation::areLabelsOffset;
20,953✔
54
                Relocation::areLabelsOffset = false;
20,953✔
55
                int addressParseRes = ParseExpressionEntry(p, nval);
20,953✔
56
                Relocation::areLabelsOffset = oldAreLabelsOffset;        // restore alternative evaluation
20,953✔
57
                if (!addressParseRes) return 0;        // some syntax error inside the address expression
20,953✔
58
                if (!need(p, '}')) {
20,944✔
59
                        Error("'}' expected", readMemP, SUPPRESS);
6✔
60
                        return 0;
6✔
61
                }
62
                if (nval < 0 || (0xFFFE + byteOnly) < nval) {
20,938✔
63
                        Error("Address in {..} must fetch bytes from 0x0000..0xFFFF range", readMemP);
18✔
64
                        nval = 0;
18✔
65
                        return 1;                                                // and return zero value as result (avoid "syntax error")
18✔
66
                }
67
                res = int(MemGetByte(nval));
20,920✔
68
                if (!byteOnly) res += int(MemGetByte(nval + 1)) << 8;
20,920✔
69
                nval = res;
20,920✔
70
                return 1;
20,920✔
71
        } else if (isdigit((byte)*p) && GetTemporaryLabelValue(p, nval, true)) {        // temporary label with underscore suffix
1,623,537✔
72
                return 1;
195✔
73
        } else if (isdigit((byte)*p) || (*p == '#' && isalnum((byte)*(p + 1))) || (*p == '$' && isalnum((byte)*(p + 1))) || *p == '%') {
1,623,342✔
74
                return GetConstant(p, nval);
867,222✔
75
        } else if (isLabelStart(p)) {
756,120✔
76
                return GetLabelValue(p, nval);
748,447✔
77
        } else if (*p == '?' && isLabelStart(p+1)) {
7,673✔
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);
9✔
82
                ++p;
9✔
83
                return GetLabelValue(p, nval);
9✔
84
        } else if ('$' == p[0] && '$' == p[1] && '$' == p[2] && '$' == p[3] && isLabelStart(p + 4)) {
7,664✔
85
                p += 4;                                                                // $$$$label variant returns physical page
69✔
86
                return GetLabelPhPage(p, nval);
69✔
87
        } else if ('$' == p[0] && '$' == p[1] && '$' == p[2] && isLabelStart(p + 3)) {
7,595✔
88
                p += 3;                                                                // $$$label variant returns physical value
72✔
89
                return GetLabelPhValue(p, nval);
72✔
90
        } else if (DISP_NONE != PseudoORG && '$' == p[0] && '$' == p[1] && '$' == p[2]) {
7,523✔
91
                if ('$' == p[3]) {                // "$$$$" operator to get physical memory page inside DISP block
24✔
92
                        p += 4;
12✔
93
                        nval = DeviceID ? Page->Number : LABEL_PAGE_UNDEFINED;
12✔
94
                        return 1;
12✔
95
                }
96
                // "$$$" operator to get physical address inside DISP block
97
                p += 3;
12✔
98
                nval = adrdisp;                // this is never affected by relocation
12✔
99
                return 1;
12✔
100
        } else if (DeviceID && *p == '$' && *(p + 1) == '$') {
7,499✔
101
                p += 2;
403✔
102
                if (isLabelStart(p)) return GetLabelPage(p, nval);
403✔
103
                if (DISP_NONE != PseudoORG && LABEL_PAGE_UNDEFINED != dispPageNum) {
235✔
104
                        // enforce explicit request of fake DISP page
105
                        nval = dispPageNum;
3✔
106
                } else {
107
                        // current page
108
                        nval = Page->Number;
232✔
109
                }
110
                return 1;
235✔
111
        } else if (*p == '$') {
7,096✔
112
                ++p;
1,724✔
113
                nval = CurAddress;
1,724✔
114
                if (Relocation::type && Relocation::areLabelsOffset && DISP_INSIDE_RELOCATE != PseudoORG) {
1,724✔
115
                        nval += Relocation::alternative_offset;
48✔
116
                }
117
                return 1;
1,724✔
118
        } else if (!(res = GetCharConst(p, nval))) {
5,372✔
119
                if (synerr) Error("Syntax error", p, IF_FIRST);
1,648✔
120
                return 0;
1,648✔
121
        }
122
        return res;
25,853✔
123
}
124

125
static int ParseExpUnair(char*& p, aint& nval) {
1,703,883✔
126
        SkipBlanks(p);
1,703,883✔
127
        char* oldP = p;
1,703,883✔
128
        if (cmphstr(p, "norel", true)) {
1,703,883✔
129
                // switch off alternative relocation evaluation for the following part of expression
130
                const bool oldAreLabelsOffset = Relocation::areLabelsOffset;
114✔
131
                Relocation::areLabelsOffset = false;
114✔
132
                int norelParseRes = ParseExpPrim(p, nval);                        // higher priority than other unary
114✔
133
                Relocation::areLabelsOffset = oldAreLabelsOffset;        // restore alternative evaluation
114✔
134
                if (norelParseRes) return 1;
114✔
135
                // "norel" operator didn't parse successfully, try to ignore it (will treat it as label)
136
                p = oldP;
6✔
137
        }
138
        if (cmphstr(p, "exist", true)) {
1,703,775✔
139
                int existEval = 0, hasParentheses = need(p, '(');
180✔
140
                if (hasParentheses || isLabelStart(p)) {
180✔
141
                        existEval = LabelExist(p, nval);
171✔
142
                }
143
                if (existEval && hasParentheses) {
180✔
144
                        existEval = need(p, ')');                                                // check closing parenthesis
21✔
145
                }
146
                if (existEval) return 1;
180✔
147
                p = oldP;
24✔
148
        }
149
        if (cmphstr(p, "sizeof", true)) {
1,703,619✔
150
                int sizeofEval = 0, hasParentheses = need(p, '(');
207✔
151
                if (hasParentheses || isLabelStart(p)) {                        // may tag label as +sizeof even when closing parenthesis
207✔
152
                        sizeofEval = GetLabelSize(p, nval);                                // is missing: OK, it's just extra trait causing extra tracking
207✔
153
                }
154
                if (hasParentheses) {
207✔
155
                        sizeofEval &= need(p, ')');
207✔
156
                }
157
                if (sizeofEval) return 1;        // valid label name (and valid optional parentheses)
207✔
158
                p = oldP;                                        // invalid label name or parentheses, try to ignore "sizeof"
6✔
159
        }
160
        aint right;
161
        int oper;
162
        if ((oper = need(p, "! ~ + - ")) || \
1,703,418✔
163
                (oper = needa(p, "not", '!', "low", 'l', "high", 'h', true)) || \
3,371,924✔
164
                (oper = needa(p, "abs", 'a', nullptr, 0, nullptr, 0, true)) ) {
1,668,506✔
165
                switch (oper) {
34,960✔
166
                case '!':
208✔
167
                        if (!ParseExpUnair(p, right)) return 0;
208✔
168
                        nval = -!right;
150✔
169
                        break;
150✔
170
                case '~':
18✔
171
                        if (!ParseExpUnair(p, right)) return 0;
18✔
172
                        nval = ~right;
9✔
173
                        break;
9✔
174
                case '+':
10,842✔
175
                        if (!ParseExpUnair(p, right)) return 0;
10,842✔
176
                        nval = right;
10,821✔
177
                        break;
10,821✔
178
                case '-':
1,726✔
179
                        if (!ParseExpUnair(p, right)) return 0;
1,726✔
180
                        nval = ~right + 1;
1,723✔
181
                        break;
1,723✔
182
                case 'l':
15,857✔
183
                        if (!ParseExpUnair(p, right)) return 0;
15,857✔
184
                        nval = right & 255;
15,854✔
185
                        break;
15,854✔
186
                case 'h':
6,261✔
187
                        if (!ParseExpUnair(p, right)) return 0;
6,261✔
188
                        nval = (right >> 8) & 255;
6,258✔
189
                        break;
6,258✔
190
                case 'a':
48✔
191
                        if (!ParseExpUnair(p, right)) return 0;
48✔
192
                        nval = abs(right);
48✔
193
                        break;
48✔
194
                default: Error("internal error", nullptr, FATAL); break;        // unreachable
×
195
                }
196
                return 1;
34,863✔
197
        } else {
198
                return ParseExpPrim(p, nval);
1,668,458✔
199
        }
200
}
201

202
static int ParseExpMul(char*& p, aint& nval) {
1,665,041✔
203
        aint left, right;
204
        int oper;
205
        if (!ParseExpUnair(p, left)) return 0;
1,665,041✔
206
        while ((oper = need(p, "* / % ")) || (oper = needa(p, "mod", '%'))) {
1,665,051✔
207
                if (!ParseExpUnair(p, right)) return 0;
3,882✔
208
                switch (oper) {
3,870✔
209
                case '*':
1,215✔
210
                        left *= right; break;
1,215✔
211
                case '/':
237✔
212
                        left = right ? left / right : 0;
237✔
213
                        if (!right) Error("Division by zero");
237✔
214
                        break;
237✔
215
                case '%':
2,418✔
216
                        left = right ? left % right : 0;
2,418✔
217
                        if (!right) Error("Division by zero");
2,418✔
218
                        break;
2,418✔
219
                default: Error("internal error", nullptr, FATAL); break;        // unreachable
×
220
                }
221
        }
222
        nval = left;
1,661,169✔
223
        return 1;
1,661,169✔
224
}
225

226
static int ParseExpAdd(char*& p, aint& nval) {
1,336,418✔
227
        aint left, right;
228
        int oper;
229
        if (!ParseExpMul(p, left)) return 0;
1,336,418✔
230
        while ((oper = need(p, "+ - "))) {
1,661,169✔
231
                if (!ParseExpMul(p, right)) return 0;
328,623✔
232
                if ('-' == oper) right = -right;
328,607✔
233
                left += right;
328,607✔
234
        }
235
        nval = left;
1,332,546✔
236
        return 1;
1,332,546✔
237
}
238

239
static int ParseExpShift(char*& p, aint& nval) {
1,316,860✔
240
        aint left, right;
241
        uint32_t l;
242
        int oper;
243
        if (!ParseExpAdd(p, left)) return 0;
1,316,860✔
244
        while ((oper = need(p, "<<>>")) || (oper = needa(p, "shl", '<' + '<', "shr", '>' + '>'))) {
1,332,546✔
245
                if (oper == '>' + '>' && *p == '>') {
19,558✔
246
                        ++p;
15✔
247
                        oper += '>';
15✔
248
                }
249
                if (!ParseExpAdd(p, right)) return 0;
19,558✔
250
                switch (oper) {
19,543✔
251
                case '<'+'<':
972✔
252
                        left <<= right; break;
972✔
253
                case '>'+'>':
18,559✔
254
                        left >>= right; break;
18,559✔
255
                case '>'+'>'+'>':
12✔
256
                        l = left; l >>= right; left = l; break;
12✔
257
                default: Error("internal error", nullptr, FATAL); break;        // unreachable
×
258
                }
259
        }
260
        nval = left;
1,312,988✔
261
        return 1;
1,312,988✔
262
}
263

264
static int ParseExpMinMax(char*& p, aint& nval) {
1,316,827✔
265
        aint left, right;
266
        int oper;
267
        if (!ParseExpShift(p, left)) return 0;
1,316,827✔
268
        while ((oper = need(p, "<?>?"))) {
1,312,988✔
269
                if (!ParseExpShift(p, right)) return 0;
33✔
270
                switch (oper) {
27✔
271
                case '<'+'?':
9✔
272
                        left = left < right ? left : right; break;
9✔
273
                case '>'+'?':
18✔
274
                        left = left > right ? left : right; break;
18✔
275
                default: Error("internal error", nullptr, FATAL); break;        // unreachable
×
276
                }
277
        }
278
        nval = left;
1,312,955✔
279
        return 1;
1,312,955✔
280
}
281

282
static int ParseExpCmp(char*& p, aint& nval) {
1,015,764✔
283
        aint left, right;
284
        int oper;
285
        if (!ParseExpMinMax(p, left)) return 0;
1,015,764✔
286
        while ((oper = need(p, "<=>=< > "))) {
1,312,955✔
287
                if (!ParseExpMinMax(p, right)) return 0;
301,063✔
288
                switch (oper) {
301,051✔
289
                case '<':
747✔
290
                        left = -(left < right); break;
747✔
291
                case '>':
33✔
292
                        left = -(left > right); break;
33✔
293
                case '<'+'=':
300,220✔
294
                        left = -(left <= right); break;
300,220✔
295
                case '>'+'=':
51✔
296
                        left = -(left >= right); break;
51✔
297
                default: Error("internal error", nullptr, FATAL); break;        // unreachable
×
298
                }
299
        }
300
        nval = left;
1,011,892✔
301
        return 1;
1,011,892✔
302
}
303

304
static int ParseExpEqu(char*& p, aint& nval) {
1,011,257✔
305
        aint left, right;
306
        int oper;
307
        if (!ParseExpCmp(p, left)) return 0;
1,011,257✔
308
        while ((oper = need(p, "=_==!="))) {
1,011,892✔
309
                if (!ParseExpCmp(p, right)) return 0;
4,507✔
310
                left = (('!'+'=') == oper) ? -(left != right) : -(left == right);
4,498✔
311
        }
312
        nval = left;
1,007,385✔
313
        return 1;
1,007,385✔
314
}
315

316
static int ParseExpBitAnd(char*& p, aint& nval) {
991,602✔
317
        aint left, right;
318
        if (!ParseExpEqu(p, left)) return 0;
991,602✔
319
        while (need(p, "&_") || needa(p, "and", '&')) {
1,007,385✔
320
                if (!ParseExpEqu(p, right)) return 0;
19,655✔
321
                left &= right;
19,649✔
322
        }
323
        nval = left;
987,730✔
324
        return 1;
987,730✔
325
}
326

327
static int ParseExpBitXor(char*& p, aint& nval) {
991,457✔
328
        aint left, right;
329
        if (!ParseExpBitAnd(p, left)) return 0;
991,457✔
330
        while (need(p, "^ ") || needa(p, "xor", '^')) {
987,730✔
331
                if (!ParseExpBitAnd(p, right)) return 0;
145✔
332
                left ^= right;
139✔
333
        }
334
        nval = left;
987,585✔
335
        return 1;
987,585✔
336
}
337

338
static int ParseExpBitOr(char*& p, aint& nval) {
991,320✔
339
        aint left, right;
340
        if (!ParseExpBitXor(p, left)) return 0;
991,320✔
341
        while (need(p, "|_") || needa(p, "or", '|')) {
987,585✔
342
                if (!ParseExpBitXor(p, right)) return 0;
137✔
343
                left |= right;
131✔
344
        }
345
        nval = left;
987,448✔
346
        return 1;
987,448✔
347
}
348

349
static int ParseExpLogAnd(char*& p, aint& nval) {
990,053✔
350
        aint left, right;
351
        if (!ParseExpBitOr(p, left)) return 0;
990,053✔
352
        while (need(p, "&&")) {
987,448✔
353
                if (!ParseExpBitOr(p, right)) return 0;
1,267✔
354
                left = -(left && right);
1,261✔
355
        }
356
        nval = left;
986,181✔
357
        return 1;
986,181✔
358
}
359

360
static int ParseExpLogOr(char*& p, aint& nval) {
990,020✔
361
        aint left, right;
362
        if (!ParseExpLogAnd(p, left)) return 0;
990,020✔
363
        while (need(p, "||")) {
986,181✔
364
                if (!ParseExpLogAnd(p, right)) return 0;
33✔
365
                left = -(left || right);
30✔
366
        }
367
        nval = left;
986,148✔
368
        return 1;
986,148✔
369
}
370

371
static int ParseExpressionEntry(char*& p, aint& nval) {
990,020✔
372
        if (ParseExpLogOr(p, nval)) return 1;
990,020✔
373
        nval = 0;
3,872✔
374
        return 0;
3,872✔
375
}
376

377
int ParseExpression(char*& p, aint& nval) {
931,840✔
378
        // if relocation is active, do the full evaluation in syntax-error-OFF mode with alternative
379
        // label values, and remember the result (to compare it with regular evaluation afterward)
380
        aint relocationVal = 0;
931,840✔
381
        int relocationRes = 0;
931,840✔
382
        if (Relocation::type) {
931,840✔
383
                char* altP = p;
14,978✔
384
                bool osynerr = synerr;
14,978✔
385
                synerr = false;
14,978✔
386
                Relocation::areLabelsOffset = true;
14,978✔
387
                relocationRes = ParseExpressionEntry(altP, relocationVal);
14,978✔
388
                Relocation::areLabelsOffset = false;
14,978✔
389
                synerr = osynerr;
14,978✔
390
        }
391
        // if relocation is off, or the alternative run did finish already, do regular evaluation
392
        int res = ParseExpressionEntry(p, nval);
931,840✔
393
        // set the Relocation::isResultAffected if the two alternative results are different
394
        if (res && relocationRes) {
931,840✔
395
                const bool isAffected = (relocationVal != nval);
14,621✔
396
                Relocation::isResultAffected |= isAffected;
14,621✔
397
                Relocation::deltaType =
14,621✔
398
                        (Relocation::alternative_offset == (relocationVal - nval)) ? Relocation::REGULAR :
26,096✔
399
                        ((Relocation::HIGH == Relocation::type) && ((Relocation::alternative_offset >> 8) == (relocationVal - nval))) ? Relocation::HIGH :
11,475✔
400
                        Relocation::OFF;
401
        }
402
        return res;
931,840✔
403
}
404

405
int ParseExpressionNoSyntaxError(char*& lp, aint& val) {
837,022✔
406
        bool osynerr = synerr;
837,022✔
407
        synerr = false;
837,022✔
408
        int ret_val = ParseExpression(lp, val);
837,022✔
409
        synerr = osynerr;
837,022✔
410
        return ret_val;
837,022✔
411
}
412

413
static int ParseExpressionInSubstitution(char *& lp, aint& val) {
15,632✔
414
        assert(!IsSubstituting);
15,632✔
415
        IsSubstituting = true;
15,632✔
416
        int ret_val = ParseExpressionNoSyntaxError(lp, val);
15,632✔
417
        IsSubstituting = false;
15,632✔
418
        return ret_val;
15,632✔
419
}
420

421
// returns 0 on syntax error, 1 on expression which is not enclosed in parentheses
422
// 2 when whole expression is in [] or () (--syntax=b/B affects when "2" is reported)
423
int ParseExpressionMemAccess(char*& p, aint& nval) {
13,582✔
424
        const EBracketType bt = OpenBracket(p);
13,582✔
425
        // if round parenthesis starts the expression, calculate pointer where it ends (and move "p" back on "(")
426
        char* const expectedEndBracket = (BT_ROUND == bt) ? ParenthesesEnd(--p) : nullptr;
13,582✔
427
        if (!ParseExpression(p, nval)) return 0;        // evaluate expression
13,582✔
428
        if (BT_NONE == bt) return 1;                                // no parentheses are always "value"
13,549✔
429
        if (BT_ROUND == bt) return (expectedEndBracket == p) ? 2 : 1;        // round parentheses are "memory" when end is as expected
3,846✔
430
        if (CloseBracket(p)) return 2;                                // square brackets must be closed properly, then it is "memory"
300✔
431
        return 0;        // curly brackets are not detect by OpenBracket, but if they would, it would work same as square here
18✔
432
}
433

434
void ParseAlignArguments(char* & src, aint & alignment, aint & fill) {
462✔
435
        SkipBlanks(src);
462✔
436
        const char * const oldSrc = src;
462✔
437
        fill = -1;
462✔
438
        if (!ParseExpression(src, alignment)) {
462✔
439
                alignment = -1;
42✔
440
                return;
42✔
441
        }
442
        if (Relocation::type) {
420✔
443
                WarningById(W_RELOCATABLE_ALIGN);
24✔
444
        }
445
        // check if alignment value is power of two (0..15-th power only)
446
        if (alignment < 1 || (1<<15) < alignment || (alignment & (alignment-1))) {
420✔
447
                Error("[ALIGN] Illegal align", oldSrc, SUPPRESS);
15✔
448
                alignment = 0;
15✔
449
                return;
15✔
450
        }
451
        if (!comma(src)) return;
405✔
452
        if (!ParseExpressionEntry(lp, fill)) {
105✔
453
                Error("[ALIGN] fill-byte expected after comma", bp, IF_FIRST);
3✔
454
                fill = -1;
3✔
455
        } else if (fill < 0 || 255 < fill) {
102✔
456
                Error("[ALIGN] Illegal align fill-byte", oldSrc, SUPPRESS);
6✔
457
                fill = -1;
6✔
458
        }
459
}
460

461
static constexpr const char GLUE_TAG = 10;                // '\n' CR abused for glues, can't be part of source line
462
static constexpr const char GLUE_CHAR = '_';
463
static bool glueToProcess = false;
464

465
static bool ReplaceDefineInternal(char* lp, char* const nl) {
729,626✔
466
        int definegereplaced = 0,dr;
729,626✔
467
        char* rp = nl,* nid;
729,626✔
468
        const char* ver;
469
        bool isDefDir = false;        // to remember if one of DEFINE-related directives was used
729,626✔
470
        bool afterNonAlphaNum, afterNonAlphaNumNext = true;
729,626✔
471
        char defarrayCountTxt[16] = { 0 };
729,626✔
472
        while (*lp && ((rp - nl) < LINEMAX)) {
7,781,893✔
473
                const char c1 = lp[0], c2 = lp[1];
7,130,833✔
474
                afterNonAlphaNum = afterNonAlphaNumNext;
7,130,833✔
475
                afterNonAlphaNumNext = !isalnum((byte)c1);
7,130,833✔
476
                if (c1 == '/' && c2 == '*') {        // block-comment local beginning (++block_nesting)
7,130,833✔
477
                        lp += 2;
327✔
478
                        ++comlin;
327✔
479
                        continue;
5,717,924✔
480
                }
481
                if (comlin) {
7,130,506✔
482
                        if (c1 == '*' && c2 == '/') {
6,834✔
483
                                lp += 2;
321✔
484
                                // insert space into line, if the block ending may have affected parsing of line
485
                                if (1 == comlin) {
321✔
486
                                        *rp++ = ' ';                // ^^ otherwise this line is completely commented out
297✔
487
                                }
488
                                --comlin;        // decrement block comment counter
321✔
489
                        } else {
490
                                ++lp;                // just skip all characters inside comment block
6,513✔
491
                        }
492
                        continue;
6,834✔
493
                }
494
                // for following code (0 == comlin) (unless it has its own parse loop)
495

496
                // single line comments -> finish
497
                if (c1 == ';' || (c1 == '/' && c2 == '/')) {
7,123,672✔
498
                        // set empty eol line comment, if the source of data is still the original "line" buffer
499
                        if (!eolComment && line <= lp && lp < line+LINEMAX) eolComment = lp;
78,566✔
500
                        break;
78,566✔
501
                }
502

503
                // strings parsing
504
                if (afterNonAlphaNum && (c1 == '"' || c1 == '\'')) {
7,045,106✔
505
                        *rp++ = *lp++;                                // copy the string delimiter (" or ')
37,743✔
506
                        // apostrophe inside apostrophes ('') will parse as end + start of another string
507
                        // which sort of "accidentally" leads to correct final results
508
                        while (*lp && c1 != *lp) {        // until end of current string is reached (or line ends)
166,265✔
509
                                // inside double quotes the backslash should escape (anything after it)
510
                                if ('"' == c1 && '\\' == *lp && lp[1]) *rp++ = *lp++;        // copy escaping backslash extra
128,522✔
511
                                *rp++ = *lp++;                        // copy string character
128,522✔
512
                        }
513
                        if (*lp) *rp++ = *lp++;                // copy the ending string delimiter (" or ')
37,743✔
514
                        continue;
37,743✔
515
                }
516

517
                if (!isLabelStart(lp, false)) {
7,007,363✔
518
                        *rp++ = *lp++;
5,673,020✔
519
                        continue;
5,673,020✔
520
                }
521

522
                // update "is define-related directive" for remainder of the line
523
                char* kp = lp;
1,334,343✔
524
                isDefDir |= afterNonAlphaNum && (cmphstr(kp, "define+") || cmphstr(kp, "define")
2,649,679✔
525
                        || cmphstr(kp, "undefine") || cmphstr(kp, "defarray+") || cmphstr(kp, "defarray")
1,315,336✔
526
                        || cmphstr(kp, "ifdef") || cmphstr(kp, "ifndef"));
1,314,958✔
527
                // if DEFINE-related directive was used, only macro-arguments are substituted
528
                // in the remaining part of the line, the define-based substitution is inhibited till EOL
529

530
                // The following loop is recursive-like macro/define substitution, the `*lp` here points
531
                // at alphabet/underscore char, marking start of "id" string, and it will be parsed by
532
                // sub-id parts, delimited by underscores, each combination of consecutive sub-ids may
533
                // be substituted by some macro argument or define.
534

535
                //TODO - maybe consider the substitution search to go downward, from longest term to shortest subterm
536
                ResetGrowSubId();
1,334,343✔
537
                char* nextSubIdLp = lp, * wholeIdLp = lp;
1,334,343✔
538
                do { //while(islabchar(*lp));
539
                        nid = GrowSubId(lp);                // grow the current sub-id part by part, checking each combination for substitution
2,093,591✔
540
                        // defines/macro arguments can substitute in the middle of ID only if they don't start with underscore
541
                        const bool canSubstituteInside = '_' != nid[0] || nextSubIdLp == wholeIdLp;
2,093,591✔
542
                        if (macrolabp && canSubstituteInside && (ver = MacroDefineTable.getverv(nid))) {
2,093,591✔
543
                                dr = 2;                        // macro argument substitution is possible
4,710✔
544
                        } else if (!isDefDir && canSubstituteInside && (ver = DefineTable.Get(nid))) {
2,088,881✔
545
                                dr = 1;                        // DEFINE substitution is possible
32,406✔
546
                                //handle DEFARRAY case
547
                                if (DefineTable.DefArrayList) {
32,406✔
548
                                        ver = nid;        // in case of some error, just copy the array id "as is"
15,650✔
549
                                        CStringsList* a = DefineTable.DefArrayList;
15,650✔
550
                                        while (White(*lp)) GrowSubIdByExtraChar(lp);
15,650✔
551
                                        aint val;
552
                                        if ('[' != *lp) Error("[ARRAY] Expression error", nextSubIdLp, SUPPRESS);
15,650✔
553
                                        if ('[' == *lp && '#' == lp[1] && ']' == lp[2]) {        // calculate size of defarray
15,650✔
554
                                                lp += 3;
15✔
555
                                                val = 0;
15✔
556
                                                while (a) {
87✔
557
                                                        ++val;
72✔
558
                                                        a = a->next;
72✔
559
                                                }
560
                                                SPRINTF1(defarrayCountTxt, 16, "%d", val);
15✔
561
                                                ver = defarrayCountTxt;
15✔
562
                                        } else {
563
                                                char* expLp = lp + ('[' == *lp);        // the '[' will become part of subId in didParseBrackets
15,635✔
564
                                                IsLabelNotFound = false;
15,635✔
565
                                                bool didParseBrackets = '[' == *lp && GrowSubIdByExtraChar(lp) && ParseExpressionInSubstitution(lp, val) && ']' == *lp;
15,635✔
566
                                                if (didParseBrackets && !IsLabelNotFound) {
15,635✔
567
                                                        // expression was successfully parsed and all values were known
568
                                                        ++lp;
15,419✔
569
                                                        while (0 < val && a) {
59,027✔
570
                                                                a = a->next;
43,608✔
571
                                                                --val;
43,608✔
572
                                                        }
573
                                                        if (val < 0 || NULL == a) {
15,419✔
574
                                                                *defarrayCountTxt = 0;                // substitute with empty string
15✔
575
                                                                ver = defarrayCountTxt;
15✔
576
                                                                Error("[ARRAY] index not in 0..<Size-1> range", nextSubIdLp, SUPPRESS);
15✔
577
                                                        } else {
578
                                                                ver = a->string;        // substitute with array value
15,404✔
579
                                                        }
580
                                                } else {        // no substitution of array possible at this time (index eval / syntax error)
581
                                                        lp = expLp;                                // restore lp in case expression parser went ahead a lot
216✔
582
                                                        dr = -1;// write into output, but don't count as replacement
216✔
583
                                                }
584
                                        }
585
                                }
586
                        } else {
587
                                dr = 0;                        // no possible substitution found
2,056,475✔
588
                                ver = nid;
2,056,475✔
589
                        }
590
                        // check if no substitution was found, and there's no more chars to extend SubId
591
                        if (0 == dr && !islabchar(*lp)) {
2,093,591✔
592
                                lp = nextSubIdLp;                // was fully extended, no match, "eat" first subId
1,472,319✔
593
                                ResetGrowSubId();
1,472,319✔
594
                                ver = GrowSubId(lp);        // find the first SubId again, for the copy
1,472,319✔
595
                                dr = -1;                                // write into output, but don't count as replacement
1,472,319✔
596
                        }
597
                        if (0 < dr) {
2,093,591✔
598
                                definegereplaced = 1;        // above zero => count as replacement
36,900✔
599
                                // look ahead in "to" array to convert any whitespace enclosed `_` to glue tag
600
                                char* gluep = rp;                // first skip whitespace ahead of substitution
36,900✔
601
                                while ((nl < gluep--) && (GLUE_TAG != gluep[0]) && (White(gluep[0]))) /* empty */;
143,500✔
602
                                // now check if there was whitespace, is glue char '_' and further whitespace ahead of it
603
                                if ((nl < gluep) && (gluep + 1 < rp) && (GLUE_CHAR == gluep[0]) && White(gluep[-1])) {
36,900✔
604
                                        gluep[0] = GLUE_TAG;// convert it to glue tag for later
33✔
605
                                        glueToProcess = true;
33✔
606
                                }
607
                                // look after in "from" array to convert any whitespace enclosed `_` to glue tag
608
                                for (gluep = lp; GLUE_TAG != gluep[0] && White(gluep[0]); ) ++gluep;
45,394✔
609
                                if ((lp < gluep) && (GLUE_CHAR == gluep[0]) && White(gluep[1])) {
36,900✔
610
                                        gluep[0] = GLUE_TAG;// convert it to glue tag for later
84✔
611
                                        glueToProcess = true;
84✔
612
                                }
613
                        }
614
                        if (0 != dr) {                                // any non-zero dr => write to the output
2,093,591✔
615
                                while (*ver && ((rp - nl) < LINEMAX)) *rp++ = *ver++;                // replace the string into target buffer
5,783,090✔
616
                                // reset subId parser to catch second+ subId in current Id
617
                                ResetGrowSubId();
1,509,435✔
618
                                nextSubIdLp = lp;
1,509,435✔
619
                        }
620
                        // continue with extending the subId, if there's still something to parse
621
                } while(islabchar(*lp));
2,093,591✔
622
        } // while(*lp)
623
        // add line terminator to the output buffer
624
        *rp++ = 0;
729,626✔
625
        if (LINEMAX <= (rp - nl)) {
729,626✔
626
                Error("line too long after macro expansion", nl, SUPPRESS);
6✔
627
        }
628
        // if no substitution happened, but there are glue bytes, glue it all together now
629
        if (0 == definegereplaced && glueToProcess) {
729,626✔
630
                // now glue the pieces together around each glue
631
                int dropChars = 0;
69✔
632
                for (rp = nl; *rp; ++rp) {
1,362✔
633
                        if (GLUE_TAG == *rp) {
1,293✔
634
                                char* leftp = rp + dropChars, * rightp = rp;
117✔
635
                                while (nl < leftp && White(leftp[-1])) --leftp;        // eat all whitespace to left
324✔
636
                                while (White(rightp[0])) ++rightp;                                // eat all whitespace to right
747✔
637
                                // leftp points at left-most whitespace char, rightp points at first non-whitespace or \0
638
                                if ((nl == leftp) || (0 == rightp[0])) rp[0] = GLUE_CHAR;        // nothing to glue with, restore
117✔
639
                                else {
640
                                        dropChars = (leftp - rightp);                                // discard this whitespace + glue block
102✔
641
                                        rp = rightp;
102✔
642
                                }
643
                        }
644
                        if (dropChars < 0) rp[dropChars] = rp[0];        // move other chars over removed areas
1,293✔
645
                }
646
                rp[dropChars] = 0;                // make sure there's zero terminator also for glued result
69✔
647
                glueToProcess = false;
69✔
648
                return true;                        // report modification
69✔
649
        }
650
        // check if whole line is just blanks, then return just empty one
651
        rp = nl;
729,557✔
652
        if (!glueToProcess && SkipBlanks(rp)) *nl = 0;                // only when glues were processed as well
729,557✔
653
        substitutedLine = nl;                // set global pointer to the latest substituted version
729,557✔
654
        return definegereplaced;
729,557✔
655
}
656

657
char* ReplaceDefine(char* src) {
702,205✔
658
        glueToProcess = false;
702,205✔
659
        char* to = sline;
702,205✔
660
        for (int maxIter = 31; maxIter--;) {
729,629✔
661
                if (!ReplaceDefineInternal(src, to)) return to;        // no more replacements
729,626✔
662
                // Some define were replaced, now ping-pong sline <-> sline2 buffers
663
                src = to;
27,424✔
664
                to = (sline == to) ? sline2 : sline;
27,424✔
665
        }
666
        Error("Unable to finish substitutions, line after last iteration", src, SUPPRESS);
3✔
667
        return src;
3✔
668
}
669

670
void SetLastParsedLabel(const char* label) {
26,522✔
671
        if (LastParsedLabel) free(LastParsedLabel);
26,522✔
672
        if (nullptr != label) {
26,522✔
673
                LastParsedLabel = STRDUP(label);
24,347✔
674
                if (nullptr == LastParsedLabel) ErrorOOM();
24,347✔
675
                LastParsedLabelLine = CompiledCurrentLine;
24,347✔
676
        } else {
677
                LastParsedLabel = nullptr;
2,175✔
678
                LastParsedLabelLine = 0;
2,175✔
679
        }
680
}
26,522✔
681

682
void ParseLabel() {
592,056✔
683
        if (White()) return;
593,290✔
684
        if (':' == *lp) {                                                        // detect possible SIZEOF boundary tag
365,482✔
685
                bool isLocal = ('.' == lp[1]);
96✔
686
                if (':' == lp[1 + isLocal]) {                        // tag detected, process it
96✔
687
                        LabelTable.SizeBoundary(isLocal ? CLabelTable::BOUNDARY_LOCAL : CLabelTable::BOUNDARY_MAIN);
87✔
688
                        lp += (2 + isLocal);
87✔
689
                        return;
87✔
690
                }
691
        }
692
        if (Options::syx.IsPseudoOpBOF && ParseDirective(true)) {
365,395✔
693
                if (!SkipBlanks()) Error("Unexpected", lp);
207✔
694
                return;
207✔
695
        }
696
        char temp[LINEMAX], * tp = temp, * ttp;
365,188✔
697
        aint val, equPageNum = LABEL_PAGE_UNDEFINED, smcOffset = 0;
365,188✔
698
        // copy the label name into `temp` array
699
        while (*lp && !White() && *lp != ':' && *lp != '=' && *lp != '+') {
1,635,043✔
700
                *tp = *lp; ++tp; ++lp;
1,269,855✔
701
        }
702
        *tp = 0;
365,188✔
703
        // handle the special SMC_offset syntax "<label>+<single_digit>"
704
        if ('+' == lp[0] && isdigit(byte(lp[1])) && !isalnum(byte(lp[2]))) {
365,188✔
705
                smcOffset = lp[1] - '0';
72✔
706
                lp += 2;
72✔
707
        }
708
        // handle the special SMC_offset syntax "<label>+*" to target significant immediate of the instruction
709
        if ('+' == lp[0] && '*' == lp[1] && !isalnum(byte(lp[2]))) {
365,188✔
710
                assert(!sourcePosStack.empty());
413✔
711
                smcOffset = 1;                                        // heuristic value 1 for first pass or when something fails
413✔
712
                lp += 2;
413✔
713
                if (1 == pass) {
413✔
714
                        // put the current source position into smart-smc (if first pass, or missing record)
715
                        smartSmcLines.push_back(sourcePosStack.back());
137✔
716
                        smartSmcLines.back().colBegin = ~0U;                // mark as unresolved
137✔
717
                } else {
718
                        if ((smartSmcLines.size() <= smartSmcIndex)
276✔
719
                                || smartSmcLines.at(smartSmcIndex) != sourcePosStack.back()) {
276✔
720
                                Error("mismatch of smart-SMC positions between passes");
10✔
721
                        } else {
722
                                auto & smartSmcLine = smartSmcLines.at(smartSmcIndex);
266✔
723
                                if (~0U == smartSmcLine.colBegin) {
266✔
724
                                        Error("unresolved smart-SMC symbol (no significant target)");
100✔
725
                                } else {
726
                                        smcOffset = smartSmcLine.colBegin;        // use the smart value from previous pass
166✔
727
                                        smartSmcLine.colBegin = ~0U;                // mark as unsolved again
166✔
728
                                }
729
                        }
730
                }
731
                ++smartSmcIndex;
413✔
732
        }
733
        if (*lp == ':') ++lp;        // eat the optional colon after label
365,188✔
734
        tp = temp;
365,188✔
735
        SkipBlanks();
365,188✔
736
        IsLabelNotFound = false;
365,188✔
737
        if (isdigit((byte)*tp)) {
365,188✔
738
                if (smcOffset) {
367✔
739
                        Error("Temporary label can't use SMC-offset");
12✔
740
                        return;
12✔
741
                }
742
                ttp = tp;
355✔
743
                while (*ttp && isdigit((byte)*ttp)) ++ttp;
851✔
744
                if (*ttp) {
355✔
745
                        Error("Invalid temporary label (not a number)", temp);
3✔
746
                        return;
3✔
747
                }
748
                if (NeedEQU() || NeedDEFL()) {
352✔
749
                        Error("Number labels are allowed as address labels only, not for DEFL/=/EQU", temp, SUPPRESS);
27✔
750
                        return;
27✔
751
                }
752
                val = atoi(tp);
325✔
753
                if (!TemporaryLabelTable.InsertRefresh(val)) {
325✔
754
                        Error("Temporary labels flow differs in last pass (missing/new temporary label since previous pass)");
116✔
755
                }
756
        } else {
757
                if (isMacroNext()) {
364,821✔
758
                        if (smcOffset) Error("Macro name can't use SMC-offset");
261✔
759
                        else SetLastParsedLabel(tp);        // store raw label into "last parsed" without adding module/etc
255✔
760
                        return;                                        // and don't add it to labels table at all
898✔
761
                }
762
                bool IsDEFL = NeedDEFL(), IsEQU = NeedEQU();
364,560✔
763
                if (IsDEFL || IsEQU) {
364,560✔
764
                        Relocation::isResultAffected = false;
344,357✔
765
                        if (!ParseExpressionNoSyntaxError(lp, val)) {
344,357✔
766
                                Error("Expression error", lp);
9✔
767
                                val = 0;
9✔
768
                        }
769
                        if (IsLabelNotFound && IsDEFL) Error("Forward reference", NULL, EARLY);
344,357✔
770
                        // check for explicit page defined by EQU
771
                        if (IsEQU && comma(lp)) {
344,357✔
772
                                if (!ParseExpressionNoSyntaxError(lp, equPageNum)) {
30✔
773
                                        Error("Expression error", lp);
6✔
774
                                        equPageNum = LABEL_PAGE_UNDEFINED;
6✔
775
                                }
776
                        }
777

778
                        // after EQU/DEFL expressions there must be <EOL>, anything else is syntax error
779
                        // this was added in v1.17.1 after realizing the line `label=$+1 and 7`
780
                        // does evaluate whole "$+1 and 7" as expression, not as instruction `and`
781
                        // (same problem exists with and/or/xor and if you have macro named after operator)
782
                        if (!SkipBlanks(lp)) {
344,396✔
783
                                Error("Unexpected", lp);
39✔
784
                                SkipToEol(lp);
39✔
785
                        }
786

787
                } else {
788
                        int gl = 0;
20,203✔
789
                        char* p = lp,* n;
20,203✔
790
                        SkipBlanks(p);
20,203✔
791
                        if (*p == '@') {
20,203✔
792
                                ++p; gl = 1;
24✔
793
                        }
794
                        if ((n = GetID(p)) && StructureTable.Emit(n, tp, p, gl)) {
20,203✔
795
                                if (smcOffset) Error("Structure instance can't use SMC-offset");
582✔
796
                                lp = p;
582✔
797
                                // this was instancing STRUCT, make it also define "main" label for future "local" ones
798
                                tp = ValidateLabel(tp, true);
582✔
799
                                if (tp) delete[] tp;
582✔
800
                                return;
582✔
801
                        }
802
                        val = CurAddress;
19,621✔
803
                }
804
                val += smcOffset;
363,978✔
805
                ttp = tp;
363,978✔
806
                bool isLocal;
807
                if (!(tp = ValidateLabel(isLocal, tp, true))) {
363,978✔
808
                        return;
48✔
809
                }
810
                // Copy label name to last parsed label variable
811
                if (!IsDEFL) SetLastParsedLabel(tp);
363,930✔
812
                unsigned traits = (isLocal ? LABEL_IS_LOCAL : 0) | (IsEQU ? LABEL_IS_EQU : 0) | (IsDEFL ? LABEL_IS_DEFL : 0) | (smcOffset ? LABEL_IS_SMC : 0);
363,930✔
813
                if (pass == LASTPASS) {
363,930✔
814
                        SLabelTableEntry* label = LabelTable.Find(tp, true);
121,347✔
815
                        if (nullptr == label && IsDEFL) {        // DEFL labels can be defined as late as needed (including pass3)
121,347✔
816
                                if (LabelTable.Insert(tp, val, traits)) label = LabelTable.Find(tp, true);
3✔
817
                        }
818
                        if (nullptr == label) {                // should have been already defined before last pass
121,347✔
819
                                Error("Label not found", tp);
7✔
820
                                delete[] tp;
7✔
821
                                return;
7✔
822
                        }
823
                        if (IsDEFL) {                //re-set DEFL value
121,340✔
824
                                LabelTable.Insert(tp, val, traits);
113,312✔
825
                        } else if (IsSldExportActive()) {
8,028✔
826
                                // SLD (Source Level Debugging) tracing-data logging
827
                                WriteToSldFile(IsEQU ? equPageNum : label->page, val, IsEQU ? 'D' : 'F', tp);        //version 0
174✔
828
                                WriteToSldFile(IsEQU ? equPageNum : label->page, val, 'L', ExportLabelToSld(ttp, label));        //version 1
174✔
829
                        }
830

831
                        if (val != label->value) {
121,340✔
832
                                // unless special label like DEFL, the values from Pass 2 are intact, just label->updatePass is touched
833
                                // so in case the value mismatch happens, the CLabelTable::Update is used to fix values for Pass 3 (with warning)
834
                                static constexpr const int WARNING_HELP_BUFFER_MAX = 128;
835
                                char buf[WARNING_HELP_BUFFER_MAX];
836
                                SPRINTF2(buf, WARNING_HELP_BUFFER_MAX, "previous value %u not equal %u", label->value, val);
23✔
837
                                Warning("Label has different value in pass 3", buf);
23✔
838
                                LabelTable.Update(tp, val);
23✔
839
                        } else {
840
                                label->updatePass = pass;        // just "touch" it here in third pass
121,317✔
841
                        }
842
                } else if (pass == 2 && !LabelTable.Insert(tp, val, traits, equPageNum)) {
242,583✔
843
                        // label already exist, and did NOT need update, ie. duplicate label (could not find other code path to this)
844
                        if (!LabelTable.Update(tp, val)) assert(false); // unreachable, update will always work after insert failed
7✔
845
                } else if (pass == 1 && !LabelTable.Insert(tp, val, traits, equPageNum)) {
242,576✔
846
                        Error("Duplicate label", tp, EARLY);
6✔
847
                }
848
                // sizeof() implementation, must search for inserted label one more time (to do it in every pass)
849
                SLabelTableEntry* label = LabelTable.Find(tp, true);
363,923✔
850
                assert(label);
363,923✔
851
                // soft main/local boundary for current nesting level
852
                LabelTable.SizeBoundary(isLocal ? CLabelTable::BOUNDARY_LOCAL : CLabelTable::BOUNDARY_MAIN);
363,923✔
853
                // add new label to size tracking if it has SIZEOF trait
854
                if (LABEL_HAS_SIZE & label->traits) LabelTable.TrackForSize(label);
363,923✔
855

856
// TODO v2.x: this is too complicated in current version: Unreal/Cspect already expect
857
// EQU/DEFL to be current page or "ROM" = not a big deal as they did change in v1.x course already.
858
// But also struct labels are set as EQU ones, so this has to split, and many other details.
859
// (will also need more than LABEL_PAGE_UNDEFINED value to deal with more states)
860
//                 if (IsEQU && comma(lp)) {        // Device extension: "<label> EQU <address>,<page number>"
861
//                         if (!DeviceID) {
862
//                                 Error("EQU can set page to label only in device mode", line);
863
//                                 SkipToEol(lp);
864
//                         } else if (!ParseExpression(lp, oval)) {        // try to read page number into "oval"
865
//                                 Error("Expression error", lp);
866
//                                 oval = -1;
867
//                         } else if (oval < 0 || Device->PagesCount <= oval) {
868
//                                 ErrorInt("Invalid page number", oval);
869
//                                 oval = -1;
870
//                         } else {
871
//                                 if (val < 0 || 0xFFFF < val) Warning("The EQU address is outside of 16bit range", line);
872
//                                 CLabelTableEntry* equLabel = LabelTable.Find(tp, true);        // must be already defined + found
873
//                                 equLabel->page = oval;                        // set it's page number
874
//                         }
875
//                 }
876

877
                delete[] tp;
363,923✔
878
        }
879
}
880

881
int ParseMacro() {
246,567✔
882
        int gl = 0, r = 0;
246,567✔
883
        char* p = lp, *n;
246,567✔
884
        SkipBlanks(p);
246,567✔
885
        if (*p == '@') {
246,567✔
886
                gl = 1; ++p;
45✔
887
        }
888
        if (!(n = GetID(p))) {
246,567✔
889
                return 0;
86✔
890
        }
891

892
        if (!gl) r = MacroTable.Emit(n, p);                // global '@' operator inhibits macros
246,481✔
893
        if (r == 2) return 1;        // successfully emitted
246,480✔
894
        if (r == 1) {                        // error reported
244,950✔
895
                lp = p;
42✔
896
                return 0;
42✔
897
        }
898

899
        // not a macro, see if it's structure
900
        if (StructureTable.Emit(n, nullptr, p, gl)) {
244,908✔
901
                lp = p;
174✔
902
                return 1;
174✔
903
        }
904

905
        return 0;
244,734✔
906
}
907

908
void ParseInstruction() {
244,862✔
909
        if ('@' == *lp) ++lp;                // skip single '@', if it was used to inhibit macro expansion
244,862✔
910
        if (ParseDirective()) return;
244,862✔
911
        Z80::GetOpCode();
120,342✔
912
}
913

914
static const byte win2dos[] = //taken from HorrorWord %)))
915
{
916
        0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
917
        0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,
918
        0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xF0, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE,
919
        0xDF, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF1, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0x20,
920
        0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
921
        0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,
922
        0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
923
        0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF
924
};
925

926
//#define DEBUG_COUT_PARSE_LINE
927

928
// returns 1 when already fully processed (part of DUP/etc)
929
int PrepareLine() {
679,105✔
930
        ListSilentOrExternalEmits();
679,105✔
931

932
        ++CompiledCurrentLine;
679,105✔
933
        if (!RepeatStack.empty()) {
679,105✔
934
                SRepeatStack& dup = RepeatStack.top();
450,684✔
935
                if (!dup.IsInWork) {
450,684✔
936
                        lp = line;
24,403✔
937
                        dup.Pointer->next = new CStringsList(lp);
24,403✔
938
                        dup.Pointer = dup.Pointer->next;
24,403✔
939
#ifdef DEBUG_COUT_PARSE_LINE
940
                        fprintf(stderr, ">%d %d %c%ld-%d [%s]\n", pass, CurSourcePos.line,
941
                                        (!RepeatStack.empty() && RepeatStack.top().IsInWork ? '!' : '.'),RepeatStack.size(),
942
                                        (!RepeatStack.empty() ? RepeatStack.top().Level : 0), line);
943
#endif
944
                        // check if there's some label at beginning of the line, skip it
945
                        if (islabchar(*lp)) {
24,403✔
946
                                // if directives are enabled at beginning of line, check if it is nested DUP/REPT/WHILE/EDUP...
947
                                if (Options::syx.IsPseudoOpBOF && ParseDirective_REPT()) return 1;
4,296✔
948
                                // skip label chars and trailing colon
949
                                while (islabchar(*lp)) ++lp;
22,800✔
950
                                if (':' == *lp) ++lp;
4,281✔
951
                        }
952
                        // catch any nested DUP/WHILE/REPT and EDUP directives
953
                        ParseDirective_REPT();
24,388✔
954
                        return 1;
24,388✔
955
                }
956
        }
957
#ifdef DEBUG_COUT_PARSE_LINE
958
        fprintf(stderr, "|%d %d %c%ld-%d [%s]\n", pass, CurSourcePos.line,
959
                        (!RepeatStack.empty() && RepeatStack.top().IsInWork ? '!' : '.'), RepeatStack.size(),
960
                        (!RepeatStack.empty() ? RepeatStack.top().Level : 0), line);
961
#endif
962
        lp = ReplaceDefine(line);
654,702✔
963

964
#ifdef DEBUG_COUT_PARSE_LINE
965
        fprintf(stderr,"rdOut [%s]->[%s] %d\n", line, lp, comlin);
966
#endif
967

968
        // update current address by memory wrapping, current page, etc... (before the label is defined)
969
        if (DeviceID)        Device->CheckPage(CDevice::CHECK_NO_EMIT);
654,702✔
970
        ListAddress = CurAddress;
654,702✔
971

972
        if (!ConvertEncoding) {
654,702✔
973
                byte* lp2 = (byte*) lp;
108✔
974
                while (*lp2) {
1,518✔
975
                        if (128 <= *lp2) {
1,410✔
976
                                *lp2 = win2dos[(*lp2) - 128];
33✔
977
                        }
978
                        ++lp2;
1,410✔
979
                }
980
        }
981
        return 0;
654,702✔
982
}
983

984
bool PrepareNonBlankMultiLine(char*& p) {
5,550✔
985
        // loop while the current line is blank-only (read further lines until EOF or non-blank char)
986
        while (SkipBlanks(p)) {
5,895✔
987
                // if inside macro system, but without any more macro-lines in buffer, act as if "EOF"
988
                // (to not leak into reading actual file while the macro is executing)
989
                if (listmacro && nullptr == lijstp) return false;
354✔
990
                // list the current (old) line
991
                ListFile();
345✔
992
                // read the next line
993
                if (!ReadLine()) return false;
345✔
994
                PrepareLine();
345✔
995
                p = lp;
345✔
996
        }
997
        return true;
5,541✔
998
}
999

1000
void ParseLine(bool parselabels) {
678,760✔
1001
        if (PrepareLine()) return;
678,760✔
1002

1003
        if (eolComment && IsSldExportActive()) SldTrackComments();
654,357✔
1004

1005
        if (!*lp) {
654,357✔
1006
                char *srcNonWhiteChar = line;
59,269✔
1007
                SkipBlanks(srcNonWhiteChar);
59,269✔
1008
                // check if only "end-line" comment remained, treat that one as "empty" line too
1009
                if (';' == srcNonWhiteChar[0] || ('/' == srcNonWhiteChar[0] && '/' == srcNonWhiteChar[1]))
59,269✔
1010
                        srcNonWhiteChar = lp;                        // force srcNonWhiteChar to point to 0
32,247✔
1011
                if (*srcNonWhiteChar || comlin) {        // non-empty source line turned into nothing
59,269✔
1012
                        ListFile(true);                                        // or empty source inside comment-block -> "skipped"
210✔
1013
                } else {
1014
                        ListFile();                                                // empty source line outside of block-comment -> "normal"
59,059✔
1015
                }
1016
                return;
59,269✔
1017
        }
1018

1019
        if (parselabels) ParseLabel();
595,088✔
1020
        if (!SkipBlanks()) ParseMacro();
595,088✔
1021
        if (!SkipBlanks()) ParseInstruction();
595,087✔
1022
        if (!SkipBlanks()) Error("Unexpected", lp);
595,081✔
1023
        ListFile();
595,081✔
1024
}
1025

1026
void ParseLineSafe(bool parselabels) {
457,357✔
1027
        char* tmp = NULL, * tmp2 = NULL;
457,357✔
1028
        char* rp = lp;
457,357✔
1029
        if (sline[0] > 0) {
457,357✔
1030
                tmp = STRDUP(sline);
434,707✔
1031
                if (tmp == NULL) ErrorOOM();
434,707✔
1032
        }
1033
        if (sline2[0] > 0) {
457,357✔
1034
                tmp2 = STRDUP(sline2);
90,579✔
1035
                if (tmp2 == NULL) ErrorOOM();
90,579✔
1036
        }
1037

1038
        ParseLine(parselabels);
457,357✔
1039

1040
        *sline = 0;
457,356✔
1041
        *sline2 = 0;
457,356✔
1042

1043
        if (tmp2 != NULL) {
457,356✔
1044
                STRCPY(sline2, LINEMAX2, tmp2);
90,579✔
1045
                free(tmp2);
90,579✔
1046
        }
1047
        if (tmp != NULL) {
457,356✔
1048
                STRCPY(sline, LINEMAX2, tmp);
434,706✔
1049
                free(tmp);
434,706✔
1050
        }
1051
        lp = rp;
457,356✔
1052
}
457,356✔
1053

1054
void ParseStructLabel(CStructure* st) {
1,221✔
1055
        char* tp, temp[LINEMAX];
1056
        if (PreviousIsLabel) {
1,221✔
1057
                free(PreviousIsLabel);
1,022✔
1058
                PreviousIsLabel = nullptr;
1,022✔
1059
        }
1060
        if (White()) {
1,221✔
1061
                return;
159✔
1062
        }
1063
        tp = temp;
1,068✔
1064
        if (*lp == '.') {
1,068✔
1065
                ++lp;
30✔
1066
        }
1067
        while (*lp && islabchar(*lp)) {
5,778✔
1068
                *tp = *lp; ++tp; ++lp;
4,710✔
1069
        }
1070
        *tp = 0;
1,068✔
1071
        if (*lp == ':') {
1,068✔
1072
                ++lp;
81✔
1073
        }
1074
        tp = temp; SkipBlanks();
1,068✔
1075
        if (isdigit((byte)*tp)) {
1,068✔
1076
                Error("[STRUCT] Number labels not allowed within structs"); return;
6✔
1077
        }
1078
        PreviousIsLabel = STRDUP(tp);
1,062✔
1079
        if (PreviousIsLabel == NULL) ErrorOOM();
1,062✔
1080
        st->AddLabel(tp);
1,062✔
1081
}
1082

1083
void ParseStructMember(CStructure* st) {
1,203✔
1084
        aint val, len;
1085
        bp = lp;
1,203✔
1086
        Relocation::isResultAffected = false;
1,203✔
1087
        Relocation::EType deltaType = Relocation::OFF;
1,203✔
1088
        switch (GetStructMemberId(lp)) {
1,203✔
1089
        case SMEMBBLOCK:
93✔
1090
                if (!ParseExpression(lp, len)) {
93✔
1091
                        len = 1;
6✔
1092
                        Error("[STRUCT] Expression expected");
6✔
1093
                }
1094
                if (comma(lp)) {
93✔
1095
                        if (!ParseExpression(lp, val)) {
54✔
1096
                                val = 0;
6✔
1097
                                Error("[STRUCT] Expression expected");
6✔
1098
                        }
1099
                        check8(val);
54✔
1100
                        val &= 255;
54✔
1101
                } else {
1102
                        val = -1;
39✔
1103
                }
1104
                st->AddMember(new CStructureEntry2(st->noffset, len, val, deltaType, SMEMBBLOCK));
93✔
1105
                break;
93✔
1106
        case SMEMBBYTE:
441✔
1107
                if (!ParseExpression(lp, val)) val = 0;
441✔
1108
                check8(val);
441✔
1109
                deltaType = Relocation::isResultAffected ? Relocation::deltaType : Relocation::OFF;
441✔
1110
                st->AddMember(new CStructureEntry2(st->noffset, 1, val, deltaType, SMEMBBYTE));
441✔
1111
                Relocation::resolveRelocationAffected(INT_MAX, Relocation::HIGH);        // clear flags + warn when can't be relocated
441✔
1112
                break;
441✔
1113
        case SMEMBWORD:
324✔
1114
                if (!ParseExpression(lp, val)) val = 0;
324✔
1115
                check16(val);
324✔
1116
                deltaType = Relocation::isResultAffected ? Relocation::deltaType : Relocation::OFF;
324✔
1117
                st->AddMember(new CStructureEntry2(st->noffset, 2, val, deltaType, SMEMBWORD));
324✔
1118
                Relocation::resolveRelocationAffected(INT_MAX);        // clear flags + warn when can't be relocated
324✔
1119
                break;
324✔
1120
        case SMEMBD24:
21✔
1121
                if (!ParseExpression(lp, val)) val = 0;
21✔
1122
                check24(val);
21✔
1123
                st->AddMember(new CStructureEntry2(st->noffset, 3, val, deltaType, SMEMBD24));
21✔
1124
                break;
21✔
1125
        case SMEMBDWORD:
33✔
1126
                if (!ParseExpression(lp, val)) val = 0;
33✔
1127
                st->AddMember(new CStructureEntry2(st->noffset, 4, val, deltaType, SMEMBDWORD));
33✔
1128
                break;
33✔
1129
        case SMEMBTEXT:
93✔
1130
                {
1131
                        if (!ParseExpression(lp, len) || len < 1 || CStructureEntry2::TEXT_MAX_SIZE < len) {
93✔
1132
                                Error("[STRUCT] Expression for length of text expected (1..8192)");
12✔
1133
                                SkipToEol(lp);
12✔
1134
                                break;
12✔
1135
                        }
1136
                        byte* textData = new byte[len]();        // zero-initialized for stable binary results
1,341✔
1137
                        if (nullptr == textData) ErrorOOM();
81✔
1138
                        if (comma(lp)) {                // if comma then init data array explicitly
81✔
1139
                                GetStructText(lp, len, textData);
63✔
1140
                        } else if (SkipBlanks(lp)) {
18✔
1141
                                // if empty without comma, init with the zeroed values
1142
                        } else {
1143
                                Error("[STRUCT] Comma expected", lp, SUPPRESS);        // syntax error
3✔
1144
                        }
1145
                        st->AddMember(new CStructureEntry2(st->noffset, len, textData));
81✔
1146
                }
1147
                break;
81✔
1148
        case SMEMBALIGN:
27✔
1149
        {
1150
                aint val, fill;
1151
                ParseAlignArguments(lp, val, fill);
27✔
1152
                if (-1 == val) val = 4;
27✔
1153
                if (st->maxAlignment < val) st->maxAlignment = val;        // update structure "max alignment"
27✔
1154
                aint bytesToAdvance = (~st->noffset + 1) & (val - 1);
27✔
1155
                if (bytesToAdvance < 1) break;                // already aligned, nothing to do
27✔
1156
                // create alignment block
1157
                st->AddMember(new CStructureEntry2(st->noffset, bytesToAdvance, fill, deltaType, SMEMBBLOCK));
21✔
1158
                break;
21✔
1159
        }
1160
        default:
171✔
1161
                char* pp = lp,* n;
171✔
1162
                int gl = 0;
171✔
1163
                CStructure* s;
1164
                SkipBlanks(pp);
171✔
1165
                if (*pp == '@') {
171✔
1166
                        ++pp;
6✔
1167
                        gl = 1;
6✔
1168
                }
1169
                if ((n = GetID(pp)) && (s = StructureTable.zoek(n, gl))) {
171✔
1170
                        if (st == s) {
96✔
1171
                                Error("[STRUCT] Can't include itself", NULL);
12✔
1172
                                SkipToEol(pp);
12✔
1173
                                lp = pp;
12✔
1174
                                break;
12✔
1175
                        }
1176
                        if (s->maxAlignment && ((~st->noffset + 1) & (s->maxAlignment - 1))) {
84✔
1177
                                // Inserted structure did use ALIGN in definition and it is misaligned here
1178
                                char warnTxt[LINEMAX];
1179
                                SPRINTF3(warnTxt, LINEMAX,
3✔
1180
                                                 "Struct %s did use ALIGN %d in definition, but here it is misaligned by %d bytes",
1181
                                                 s->naam, s->maxAlignment, ((~st->noffset + 1) & (s->maxAlignment - 1)));
1182
                                Warning(warnTxt);
3✔
1183
                        }
1184
                        lp = pp;
84✔
1185
                        st->CopyLabels(s);
84✔
1186
                        st->CopyMembers(s, lp);
84✔
1187
                }
1188
                break;
159✔
1189
        }
1190
        Relocation::checkAndWarn();
1,203✔
1191
}
1,203✔
1192

1193
void ParseStructLine(CStructure* st) {
1,275✔
1194
        ++CompiledCurrentLine;
1,275✔
1195
        lp = ReplaceDefine(line);
1,275✔
1196
        if (!*lp) return;
1,275✔
1197
        ParseStructLabel(st);
1,221✔
1198
        if (SkipBlanks()) return;
1,221✔
1199
        ParseStructMember(st);
1,203✔
1200
        if (SkipBlanks()) return;
1,203✔
1201
        if (*lp) Error("[STRUCT] Unexpected", lp);
99✔
1202
}
1203

1204
//eof parser.cpp
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc