Browse code

Add source code of the assembler compiler

Pawel Jablonski authored on 14/12/2022 19:04:16
Showing 1 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,687 @@
1
+/*
2
+ * Author: Pawel Jablonski
3
+ * E-mail: pj@xirx.net
4
+ * WWW: xirx.net
5
+ * GIT: git.xirx.net
6
+ *
7
+ * License: You can use this code however you like
8
+ * but leave information about the original author.
9
+ * Code is free for non-commercial and commercial use.
10
+ */
11
+
12
+#include "parser.h"
13
+
14
+/**
15
+ * Constructor of the parser
16
+ *
17
+ * @param dataList Data list
18
+ */
19
+Parser::Parser(DataList &dataList) : dataList(dataList)
20
+{
21
+}
22
+
23
+/**
24
+ * Parse a file
25
+ *
26
+ * @param path Path to the file
27
+ *
28
+ * @return Status of parsing the file
29
+ */
30
+bool Parser::parseFile(const QString &path)
31
+{
32
+	QTextStream out(stdout);
33
+	QList<QString> fileList;
34
+	QList<QString> mapList;
35
+
36
+	fileList.append(QFileInfo(path).absoluteFilePath());
37
+
38
+	for(int i = 0; i < fileList.size(); i++)
39
+	{
40
+		QString filePath = fileList.at(i);
41
+		QFile file(filePath);
42
+		QStringList lineList;
43
+
44
+		if(!file.open(QIODevice::ReadOnly))
45
+		{
46
+			out << QString("ERROR: Cannot open file: \"%1\"\n").arg(filePath);
47
+			return(false);
48
+		}
49
+
50
+		while(!file.atEnd())
51
+		{
52
+			lineList.append(file.readLine().trimmed());
53
+		}
54
+
55
+		file.close();
56
+
57
+		QSharedPointer<Data> data(new Data(filePath));
58
+		QSharedPointer<CodeSection> codeSection(new CodeSection(""));
59
+
60
+		for(int j = 0; j < lineList.size(); j++)
61
+		{
62
+			// Trimm line
63
+			QString line = lineList.at(j);
64
+			int offset = line.indexOf(QRegularExpression("(?<!\\\\);|^;"));
65
+
66
+			if(offset >= 0)
67
+			{
68
+				line = line.left(offset).trimmed();
69
+			}
70
+
71
+			if(line.length() <= 0)
72
+			{
73
+				continue;
74
+			}
75
+
76
+			// Find label
77
+			offset = line.indexOf(QRegularExpression("\\s+"));
78
+			QString label;
79
+
80
+			if(offset > 0)
81
+			{
82
+				label = line.left(offset).trimmed();
83
+			}
84
+			else
85
+			{
86
+				label = line;
87
+			}
88
+
89
+			line = line.mid(label.length()).trimmed();
90
+
91
+			// Find external or global
92
+			bool isExternal = false;
93
+			bool isGlobal = false;
94
+
95
+			if(!label.compare("external", Qt::CaseInsensitive))
96
+			{
97
+				if(this->dataList.getMode() == DataList::Mode::Rom)
98
+				{
99
+					isExternal = true;
100
+				}
101
+				else
102
+				{
103
+					out << QString("ERROR: Special keyword \"external\" is not allowed in APP mode, found at line %1 in file: \"%2\"\n").arg(j + 1).arg(filePath);
104
+					return(false);
105
+				}
106
+			}
107
+
108
+			if(!label.compare("global", Qt::CaseInsensitive))
109
+			{
110
+				isGlobal = true;
111
+			}
112
+
113
+			if(isExternal || isGlobal)
114
+			{
115
+				offset = line.indexOf(QRegularExpression("\\s+"));
116
+
117
+				if(offset > 0)
118
+				{
119
+					label = line.left(offset).trimmed();
120
+				}
121
+				else
122
+				{
123
+					label = line;
124
+				}
125
+
126
+				line = line.mid(label.length()).trimmed();
127
+			}
128
+
129
+			// Find code section
130
+			if((label.left(1) == ".") && (label.right(1) == ":"))
131
+			{
132
+				if(label.mid(1, (label.length() - 2)).contains(QRegularExpression("^([0-9a-zA-Z_-]+)$")))
133
+				{
134
+					if(!data->addCodeSection(codeSection))
135
+					{
136
+						out << QString("ERROR: Reuse of section name: \"%1\", found at line %2 in file: \"%3\"\n").arg(codeSection->getName()).arg(j + 1).arg(filePath);
137
+						return(false);
138
+					}
139
+
140
+					codeSection = QSharedPointer<CodeSection>(new CodeSection(label.left(label.length() - 1)));
141
+
142
+					if(isExternal)
143
+					{
144
+						if(!this->dataList.addExternalCodeSection(codeSection))
145
+						{
146
+							out << QString("ERROR: Reuse of external section name: \"%1\", found at line %2 in file: \"%3\"\n").arg(codeSection->getName()).arg(j + 1).arg(filePath);
147
+							return(false);
148
+						}
149
+					}
150
+					else if(isGlobal)
151
+					{
152
+						if(!this->dataList.addGlobalCodeSection(codeSection))
153
+						{
154
+							out << QString("ERROR: Reuse of global section name: \"%1\", found at line %2 in file: \"%3\"\n").arg(codeSection->getName()).arg(j + 1).arg(filePath);
155
+							return(false);
156
+						}
157
+					}
158
+
159
+					if(line.length() == 0)
160
+					{
161
+						continue;
162
+					}
163
+
164
+					offset = line.indexOf(QRegularExpression("\\s+"));
165
+
166
+					if(offset > 0)
167
+					{
168
+						label = line.left(offset).trimmed();
169
+					}
170
+					else
171
+					{
172
+						label = line;
173
+					}
174
+
175
+					line = line.mid(label.length()).trimmed();
176
+				}
177
+				else
178
+				{
179
+					out << QString("ERROR: Syntax error at line %1 in file: \"%2\"\n").arg(j + 1).arg(filePath);
180
+					return(false);
181
+				}
182
+			}
183
+
184
+			// Parse include
185
+			if(!label.compare("include", Qt::CaseInsensitive))
186
+			{
187
+				QString tPath;
188
+
189
+				if(QFileInfo(line).isAbsolute())
190
+				{
191
+					tPath = line;
192
+				}
193
+				else
194
+				{
195
+					tPath = QFileInfo(QFileInfo(filePath).absolutePath().append("/").append(line)).absoluteFilePath();
196
+				}
197
+
198
+				bool exists = false;
199
+
200
+				for(int j = 0; j < this->dataList.size(); j++)
201
+				{
202
+					if(this->dataList.at(j)->getFileName() == tPath)
203
+					{
204
+						exists = true;
205
+						break;
206
+					}
207
+				}
208
+
209
+				if(!exists)
210
+				{
211
+					for(const QString &fileFor : fileList)
212
+					{
213
+						if(fileFor == tPath)
214
+						{
215
+							exists = true;
216
+							break;
217
+						}
218
+					}
219
+				}
220
+
221
+				if(!exists)
222
+				{
223
+					fileList.append(tPath);
224
+				}
225
+
226
+				continue;
227
+			}
228
+
229
+			// Parse map
230
+			if(!label.compare("map", Qt::CaseInsensitive))
231
+			{
232
+				if(this->dataList.getMode() != DataList::Mode::App)
233
+				{
234
+					out << QString("ERROR: Special keyword \"map\" is not allowed in ROM mode, found at line %1 in file: \"%2\"\n").arg(j + 1).arg(filePath);
235
+					return(false);
236
+				}
237
+
238
+				QString tPath;
239
+
240
+				if(QFileInfo(line).isAbsolute())
241
+				{
242
+					tPath = line;
243
+				}
244
+				else
245
+				{
246
+					tPath = QFileInfo(QFileInfo(filePath).absolutePath().append("/").append(line)).absoluteFilePath();
247
+				}
248
+
249
+				bool exists = false;
250
+
251
+				for(int k = 0; k < this->dataList.size(); k++)
252
+				{
253
+					if(this->dataList.at(k)->getFileName() == tPath)
254
+					{
255
+						exists = true;
256
+						break;
257
+					}
258
+				}
259
+
260
+				if(!exists)
261
+				{
262
+					for(const QString &mapFor : mapList)
263
+					{
264
+						if(mapFor == tPath)
265
+						{
266
+							exists = true;
267
+							break;
268
+						}
269
+					}
270
+				}
271
+
272
+				if(!exists)
273
+				{
274
+					mapList.append(tPath);
275
+				}
276
+
277
+				QFile file(tPath);
278
+				QStringList lineList;
279
+
280
+				if(!file.open(QIODevice::ReadOnly))
281
+				{
282
+					out << QString("ERROR: Cannot open file: \"%1\"\n").arg(tPath);
283
+					return(false);
284
+				}
285
+
286
+				while(!file.atEnd())
287
+				{
288
+					lineList.append(file.readLine().trimmed());
289
+				}
290
+
291
+				file.close();
292
+
293
+				for(int j = 0; j < lineList.size(); j++)
294
+				{
295
+					// Trimm line
296
+					QString line = lineList.at(j);
297
+					int offset = line.indexOf(QRegularExpression("(?<!\\\\);|^;"));
298
+
299
+					if(offset >= 0)
300
+					{
301
+						line = line.left(offset).trimmed();
302
+					}
303
+
304
+					if(line.length() <= 0)
305
+					{
306
+						continue;
307
+					}
308
+
309
+					QStringList lineItems = line.split(QRegularExpression("\\s+"));
310
+
311
+					if(!this->parseMap(j, tPath, lineItems))
312
+					{
313
+						return(false);
314
+					}
315
+				}
316
+
317
+				continue;
318
+			}
319
+
320
+			// Parse ram
321
+			if(!label.compare("var", Qt::CaseInsensitive))
322
+			{
323
+				QSharedPointer<RamItem> ramItem = this->parseRam(j, line);
324
+
325
+				if(!ramItem.isNull())
326
+				{
327
+					if(!data->addRamItem(ramItem))
328
+					{
329
+						out << QString("ERROR: Reuse of name: \"%1\", found at line %2 in file: \"%3\"\n").arg(ramItem->getName()).arg(j + 1).arg(filePath);
330
+						return(false);
331
+					}
332
+
333
+					if(isExternal)
334
+					{
335
+						if(!this->dataList.addExternalRamItem(ramItem))
336
+						{
337
+							out << QString("ERROR: Reuse of external name: \"%1\", found at line %2 in file: \"%3\"\n").arg(ramItem->getName()).arg(j + 1).arg(filePath);
338
+							return(false);
339
+						}
340
+					}
341
+					else if(isGlobal)
342
+					{
343
+						if(!this->dataList.addGlobalRamItem(ramItem))
344
+						{
345
+							out << QString("ERROR: Reuse of global name: \"%1\", found at line %2 in file: \"%3\"\n").arg(ramItem->getName()).arg(j + 1).arg(filePath);
346
+							return(false);
347
+						}
348
+					}
349
+				}
350
+				else
351
+				{
352
+					out << QString("ERROR: Syntax error at line %1 in file: \"%2\"\n").arg(j + 1).arg(filePath);
353
+					return(false);
354
+				}
355
+
356
+				continue;
357
+			}
358
+
359
+			// Parse flash
360
+			if(!label.compare("const", Qt::CaseInsensitive))
361
+			{
362
+				QSharedPointer<FlashItem> flashItem = this->parseFlash(j, line);
363
+
364
+				if(!flashItem.isNull())
365
+				{
366
+					if(!data->addFlashItem(flashItem))
367
+					{
368
+						out << QString("ERROR: Reuse of name: \"%1\", found at line %2 in file: \"%3\"\n").arg(flashItem->getName()).arg(j + 1).arg(filePath);
369
+						return(false);
370
+					}
371
+
372
+					if(isExternal)
373
+					{
374
+						if(!this->dataList.addExternalFlashItem(flashItem))
375
+						{
376
+							out << QString("ERROR: Reuse of external name: \"%1\", found at line %2 in file: \"%3\"\n").arg(flashItem->getName()).arg(j + 1).arg(filePath);
377
+							return(false);
378
+						}
379
+					}
380
+					else if(isGlobal)
381
+					{
382
+						if(!this->dataList.addGlobalFlashItem(flashItem))
383
+						{
384
+							out << QString("ERROR: Reuse of global name: \"%1\", found at line %2 in file: \"%3\"\n").arg(flashItem->getName()).arg(j + 1).arg(filePath);
385
+							return(false);
386
+						}
387
+					}
388
+				}
389
+				else
390
+				{
391
+					out << QString("ERROR: Syntax error at line %1 in file: \"%2\"\n").arg(j + 1).arg(filePath);
392
+					return(false);
393
+				}
394
+
395
+				continue;
396
+			}
397
+
398
+			// Parse code
399
+			QSharedPointer<CodeItem> codeItem = this->parseCode(j, label, line);
400
+
401
+			if(!codeItem.isNull())
402
+			{
403
+				codeSection->addCodeItem(codeItem);
404
+			}
405
+			else
406
+			{
407
+				out << QString("ERROR: Syntax error at line %1 in file: \"%2\"\n").arg(j + 1).arg(filePath);
408
+				return(false);
409
+			}
410
+		}
411
+
412
+		if(!data->addCodeSection(codeSection))
413
+		{
414
+			out << QString("ERROR: Reuse of section name: \"%1\", found in file: \"%2\"\n").arg(codeSection->getName(), filePath);
415
+			return(false);
416
+		}
417
+
418
+		this->dataList.append(data);
419
+	}
420
+
421
+	return(true);
422
+}
423
+
424
+/**
425
+ * Parse a map
426
+ *
427
+ * @param i Line number
428
+ * @param mapPath Path to the map file
429
+ * @param lineItems List of the line items
430
+ *
431
+ * @return Status of parsing the map
432
+ */
433
+bool Parser::parseMap(int i, const QString &mapPath, const QStringList &lineItems)
434
+{
435
+	QTextStream out(stdout);
436
+
437
+	QString name;
438
+	unsigned int address;
439
+	QString tAddress;
440
+
441
+	if(lineItems.size() == 3)
442
+	{
443
+		name = lineItems.at(0);
444
+		tAddress = lineItems.at(2);
445
+
446
+		if((name.left(1) == ".") && (lineItems.at(1) == "="))
447
+		{
448
+			if(name.mid(1).contains(QRegularExpression("^([0-9a-zA-Z_-]+)$")))
449
+			{
450
+				if(tAddress.contains(QRegularExpression("^(0x[0-9abcdefABCDEF]+)$")))
451
+				{
452
+					bool tOk;
453
+					address = tAddress.midRef(2).toUInt(&tOk, 16);
454
+
455
+					if(tOk)
456
+					{
457
+						if(address < Data::MEMORY_SIZE)
458
+						{
459
+							QSharedPointer<CodeSection> codeSection(new CodeSection(name));
460
+							codeSection->setAddress(address);
461
+
462
+							if(!this->dataList.addExternalCodeSection(codeSection))
463
+							{
464
+								out << QString("ERROR: Reuse of external section name: \"%1\", found at line %2 in file: \"%3\"\n").arg(name).arg(i + 1).arg(mapPath);
465
+								return(false);
466
+							}
467
+
468
+							return(true);
469
+						}
470
+					}
471
+				}
472
+			}
473
+		}
474
+	}
475
+	else if(lineItems.size() == 5)
476
+	{
477
+		name = lineItems.at(2);
478
+		tAddress = lineItems.at(4);
479
+
480
+		if((!lineItems.at(0).compare("external", Qt::CaseInsensitive)) && (lineItems.at(3) == "="))
481
+		{
482
+			if(name.contains(QRegularExpression("^([0-9a-zA-Z_-]+)$")))
483
+			{
484
+				if(tAddress.contains(QRegularExpression("^(0x[0-9abcdefABCDEF]+)$")))
485
+				{
486
+					bool tOk;
487
+					address = tAddress.midRef(2).toUInt(&tOk, 16);
488
+
489
+					if(tOk)
490
+					{
491
+						if(address < Data::MEMORY_SIZE)
492
+						{
493
+							if(!lineItems.at(1).compare("var", Qt::CaseInsensitive))
494
+							{
495
+								QSharedPointer<RamItem> ramItem(new RamItem(0, name, ""));
496
+								ramItem->setAddress(address);
497
+
498
+								if(!this->dataList.addExternalRamItem(ramItem))
499
+								{
500
+									out << QString("ERROR: Reuse of external section name: \"%1\", found at line %2 in file: \"%3\"\n").arg(name).arg(i + 1).arg(mapPath);
501
+									return(false);
502
+								}
503
+							}
504
+							else if(!lineItems.at(1).compare("const", Qt::CaseInsensitive))
505
+							{
506
+								QSharedPointer<FlashItem> flashItem(new FlashItem(0, name, QList<QString>()));
507
+								flashItem->setAddress(address);
508
+
509
+								if(!this->dataList.addExternalFlashItem(flashItem))
510
+								{
511
+									out << QString("ERROR: Reuse of external section name: \"%1\", found at line %2 in file: \"%3\"\n").arg(name).arg(i + 1).arg(mapPath);
512
+									return(false);
513
+								}
514
+							}
515
+
516
+							return(true);
517
+						}
518
+					}
519
+				}
520
+			}
521
+		}
522
+	}
523
+
524
+	out << QString("ERROR: Syntax error at line %1 in file: \"%2\"\n").arg(i + 1).arg(mapPath);
525
+
526
+	return(false);
527
+}
528
+
529
+/**
530
+ * Parse a variable line
531
+ *
532
+ * @param i Line number
533
+ * @param line Line raw data
534
+ *
535
+ * @return RAM Item object
536
+ */
537
+QSharedPointer<RamItem> Parser::parseRam(int i, QString &line)
538
+{
539
+	QSharedPointer<RamItem> ramItem(nullptr);
540
+	bool parseError = false;
541
+
542
+	QString name;
543
+	QString arraySize;
544
+
545
+	if(line.length() == 0)
546
+	{
547
+		parseError = true;
548
+	}
549
+
550
+	if(line.contains(QRegularExpression("^([a-zA-Z0-9_-]+)$")))
551
+	{
552
+		name = line.trimmed();
553
+	}
554
+	else if(line.contains(QRegularExpression(R"(^([a-zA-Z0-9_-]+)\[([0-9]+)\]$)")))
555
+	{
556
+		int offset = line.indexOf("[");
557
+
558
+		name = line.left(offset).trimmed();
559
+		arraySize = line.mid((offset + 1), (line.length() - offset - 2));
560
+	}
561
+	else
562
+	{
563
+		parseError = true;
564
+	}
565
+
566
+	if(!parseError)
567
+	{
568
+		ramItem.reset(new RamItem((i + 1), name, arraySize));
569
+	}
570
+
571
+	return(ramItem);
572
+}
573
+
574
+/**
575
+ * Parse a const line
576
+ *
577
+ * @param i Line number
578
+ * @param line Line raw data
579
+ *
580
+ * @return Flash Item object
581
+ */
582
+QSharedPointer<FlashItem> Parser::parseFlash(int i, QString &line)
583
+{
584
+	QSharedPointer<FlashItem> flashItem(nullptr);
585
+	bool parseError = false;
586
+
587
+	QString name;
588
+	QStringList valueList;
589
+
590
+	int offset = line.indexOf(QRegularExpression("\\s+"));
591
+
592
+	if(offset > 0)
593
+	{
594
+		name = line.left(offset).trimmed();
595
+		line = line.mid(name.length()).trimmed();
596
+
597
+		if(line.length() > 0)
598
+		{
599
+			valueList = line.split(QRegularExpression(R"((?<!\\),)"));
600
+		}
601
+
602
+		if(valueList.empty())
603
+		{
604
+			parseError = true;
605
+		}
606
+	}
607
+	else
608
+	{
609
+		parseError = true;
610
+	}
611
+
612
+	if(!parseError)
613
+	{
614
+		for(int i = 0; i < valueList.size(); i++)
615
+		{
616
+			QString value = valueList.at(i);
617
+			value = value.remove(QRegularExpression(R"((?<!\\)\\)")).trimmed();
618
+
619
+			valueList.replace(i, value);
620
+
621
+			if(valueList.at(i).length() == 0)
622
+			{
623
+				parseError = true;
624
+				break;
625
+			}
626
+		}
627
+
628
+		if(!parseError)
629
+		{
630
+			flashItem.reset(new FlashItem((i + 1), name, valueList));
631
+		}
632
+	}
633
+
634
+	return(flashItem);
635
+}
636
+
637
+/**
638
+ * Parse a code line
639
+ *
640
+ * @param i Line number
641
+ * @param label Name of the label
642
+ * @param line Line raw data
643
+ *
644
+ * @return Code Item object
645
+ */
646
+QSharedPointer<CodeItem> Parser::parseCode(int i, const QString &label, QString &line)
647
+{
648
+	QSharedPointer<CodeItem> codeItem(nullptr);
649
+	bool parseError = false;
650
+
651
+	QStringList paramList;
652
+
653
+	if(line.length() > 0)
654
+	{
655
+		paramList = line.split(QRegularExpression("(?<!\\\\),"));
656
+	}
657
+
658
+	for(int i = 0; i < paramList.length(); i++)
659
+	{
660
+		paramList.replace(i, paramList.at(i).trimmed());
661
+
662
+		if(paramList.at(i).isEmpty())
663
+		{
664
+			parseError = true;
665
+			break;
666
+		}
667
+	}
668
+
669
+	if(!parseError)
670
+	{
671
+		for(int i = 0; i < paramList.length(); i++)
672
+		{
673
+			if(paramList.at(i).at(0) != "'")
674
+			{
675
+				paramList.replace(i, paramList.at(i));
676
+			}
677
+			else
678
+			{
679
+				paramList.replace(i, QString(paramList.at(i)).remove(QRegularExpression(R"((?<!\\)\\)")).trimmed());
680
+			}
681
+		}
682
+
683
+		codeItem.reset(new CodeItem((i + 1), label, paramList));
684
+	}
685
+
686
+	return(codeItem);
687
+}