Added error messages displaying using source file name and line number
[zilutils.git] / zilasm / parser.cpp
1 /*
2  * parser.c -- part of ZilUtils/ZilAsm
3  *
4  * Copyright (C) 2016, 2019, 2020 Jason Self <j@jxself.org>
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Affero General Public License as
8  * published by the Free Software Foundation, either version 3 of the
9  * License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Affero General Public License for more details.
15  *
16  * You should have received a copy of the GNU Affero General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>
18  *
19  * SPDX-License-Identifier: AGPL-3.0-or-later
20  */
21 #include <stdlib.h>
22 #include <stdio.h>              /* fopen, fgets */
23 #include <string.h>             /* strlen */
24 #include <ctype.h>
25 #include <string>
26 #include <stack>
27 using namespace std;
28
29
30 #include "header.h"
31 #include "parser.h"
32 #include "directives.h"
33
34 extern "C"
35 {
36 #include "opcodes.h"
37 }
38 #include "labels.h"
39 #include "string_table.h"
40
41
42 #define iscomment(c)   ((c) == ';')
43 #define isbindigit(c)  ((c) == '0' || (c) == '1')
44
45 /* !!! TODO !!! */
46 //#define fatal_error(errmsg) printf(errmsg)
47 #define PC NULL
48
49 unsigned g_number_of_instructions = 0;
50
51 unsigned g_currentLineNumber = 0;
52 //string g_source_directory;
53 bool g_haveErrors = false;
54
55 stack<Parsing_Context> g_parsing_contexts;
56
57
58 ZMemblock (*g_codes[MAX_NUMBER_OF_INSTRUCTIONS]);
59
60 string
61 build_error_message(const char *message)
62 {
63         char buff[300];
64         sprintf(buff, "error at %s%s line %d: %s", g_parsing_contexts.top().current_directory.c_str(),
65                 g_parsing_contexts.top().current_file_name.c_str(), g_currentLineNumber, message);
66         return string(buff);
67 }
68
69
70 void
71 fatal_error(const char *errmsg)
72 {
73         printf( "%s\n", errmsg);
74         g_haveErrors = true;
75 }
76
77
78
79 void
80 checksep (const char *p)
81 {
82   if (!*p || iscomment (*p) || isspace (*p))
83     return;
84   fatal_error (build_error_message ("wrong chars").c_str());
85 }
86
87
88 const char *
89 pass_spaces (const char *p)
90 {
91   while (p && isspace (*p))
92     p++;
93   return (p && *p) ? p : NULL;
94 }
95
96
97 const char *
98 pass_alnums (const char *p)
99 {
100   while (p && isalnum (*p))
101     p++;
102   return (p && *p) ? p : NULL;
103 }
104
105
106 int tryparse_instruction (const char *a);
107
108 int
109 tryparse_directive (const char *p)
110 {
111   if (*p != '.')
112     return 0;
113   const char *a = p + 1;
114   const char *b = pass_alnums (a);
115   checksep (b);
116   Directive_handler f = directive_lookup (a, b - a);
117   if (!f)
118     return 0;
119   return (b-a) + (*f) (b);
120 }
121
122
123 int
124 tryparse_assignment (const char *a, const char *b, const char *c)
125 {
126   return 0;
127 }
128
129
130 int
131 tryparse_label (const char *a, const char *b, const char *c)
132 {
133   if (*(c + 1) != ':')
134     {
135       symtable_add2 (Local_labels, a, b - a, PC);
136     }
137   else if (*(c + 2) != ':')
138     {
139       symtable_add2 (Global_labels, a, b - a, PC);
140     }
141   else
142     {
143       fatal_error (build_error_message("wrong label type").c_str());
144     }
145
146   while (*c++ == ':');
147   if (*c && ((c = pass_spaces (c)) != NULL) && *c)
148     return tryparse_instruction (c);
149   return 1;
150 }
151
152
153 int
154 tryparse_name (const char *a)
155 {
156   const char *b = pass_alnums (a);
157   const char *c = pass_spaces (b);
158
159   if (!c)
160     return 0;
161   if (*c == '=')
162     return tryparse_assignment (a, b, c + 1);
163   if (*c == ':')
164     return tryparse_label (a, b, c);
165   return 0;
166 }
167
168
169 int
170 tryparse_instruction (const char *a)
171 {
172         bool display_error = false;
173         int len = 0;
174         const char *b = pass_alnums(a);
175         if (b != a)
176         {
177                 len = b ? b - a : strlen(a);
178                 ZOpcode *op = (ZOpcode *)symtable_lookup2(Opcodes, a, len);
179                 if (op)
180                 {
181                         ZOpcode_flags flags = op->flags;
182                         ZMemblock *mem_additional = NULL;
183
184                         switch (op->opcode)
185                         {
186                         case Opcode_CRLF:
187                         case Opcode_PRINT:
188                         case Opcode_QUIT:
189                                 break;
190
191                         case Opcode_PRINTI:
192                         case Opcode_PRINTR:
193                         {
194                                 char *p = (char *)a + len;
195                                 p = (char *)pass_spaces(p);
196                                 if (*p == '\"')
197                                 {
198                                         p++;
199                                         string str;
200                                         while (*p != '\"')
201                                         {
202                                                 str += *p;
203                                                 ++p;
204                                         }
205                                         len = p - a;
206                                         mem_additional =
207                                                 String_table::encrypt_string(str.c_str(), NULL);
208                                 }
209                                 break;
210                         }
211                         default:
212                         {
213                                 display_error = true;
214                         }
215                         }
216
217
218                         if (display_error == false)
219                         {
220
221                                 int instruction_size = 1;
222                                 if (mem_additional)
223                                         instruction_size += mem_additional->used_size;
224
225                                 g_codes[g_number_of_instructions] = zmem_init(instruction_size);
226                                 zmem_putbyte(g_codes[g_number_of_instructions], op->opcode);
227
228                                 if (mem_additional)
229                                 {
230                                         for (int i = 0; i < mem_additional->used_size; ++i)
231                                                 zmem_putbyte(g_codes[g_number_of_instructions],
232                                                         mem_additional->contents[i]);
233                                         zmem_destroy(mem_additional);
234                                 }
235
236                                 ++g_number_of_instructions;
237                                 return len;
238                         }
239                 }
240                 else
241                 {
242                         //display_error = true;
243                         string message = "wrong line \"" + string(a) + string("\"");
244                         fatal_error(build_error_message(message.c_str()).c_str());
245                 }
246         }
247
248         if (display_error)
249         {
250                 char buff[300];
251                 if (len == 0)
252                         len = strlen(a);
253                 char *instrcution_name = (char *)malloc(len + 1);
254                 memcpy(instrcution_name, a, len);
255                 instrcution_name[len] = 0;
256                 string message = "instruction " + string(instrcution_name) + string(" is not supported yet");
257                 fatal_error(build_error_message(message.c_str()).c_str());
258                 free(instrcution_name);
259         }
260
261         return strlen(a);
262 }
263
264
265 /*
266  *  Line can be one from: Comment, Global label, Local label, Directive, Name=Value, Instruction
267  */
268 int
269 parse_line (const char *p)
270 {
271   for (; *p; p++)
272     {
273       char c = *p;
274       int n;
275       if (isspace (c))
276         continue;
277       if (iscomment (c))
278         return 0;
279       if (n = tryparse_directive (p))
280         return n;
281       if (n = tryparse_name (p))
282         return n;               // ..label or assignment
283       if (n = tryparse_instruction (p))
284         return n;
285       //fatal_error ("wrong line");
286     }
287   return 0;
288 }
289
290 #define MAX_LINESIZE 1024
291
292 int
293 parse_file (/*const char *filename*/)
294 {
295         if (g_parsing_contexts.size() > 0)
296         {
297                 string filename = g_parsing_contexts.top().current_directory + g_parsing_contexts.top().current_file_name;
298
299                 FILE *fp = fopen(filename.c_str(), "r");
300                 if (fp)
301                 {
302
303                         g_currentLineNumber = 0;
304
305                         //const int MAX_LINESIZE = 1024;
306                         char line[MAX_LINESIZE];
307                         int newline_missing = 0;
308
309                         while (g_stopParsing == 0 && fgets(line, MAX_LINESIZE, fp))
310                         {
311                                 ++g_currentLineNumber;
312
313                                 if (newline_missing)
314                                         fatal_error(build_error_message("line too long").c_str());
315
316                                 int n = strlen(line);
317                                 if (!n)
318                                         continue;
319
320                                 parse_line(line);
321
322                                 newline_missing = (line[n - 1] != '\n');
323                         }
324
325                         fclose(fp);
326                 }
327                 else
328                 {
329                         string message = string("can't open file ") + filename;
330                         fatal_error(message.c_str());
331                 }
332
333                 g_parsing_contexts.pop();
334         }
335         return 0;
336 }
337
338 /*
339
340 line_passed() {
341     skip_spaces();
342     return (current_token == LINE_END || current_token == LINE_COMMENT);
343 }
344
345 if (line_passed()) continue;
346 if (current_token == DIRECTIVE) {
347     if (!try_next_token(NAME))
348         fatal_error("directive contains incorrect chars")
349     handler = get_directive_handler(current_token);
350     if (!handler)
351         fatal error("unknown directive");
352     (*handler)(remaining_line);
353     if (line_passed()) continue;
354     fatal_error("unexpected line tail");
355 } else if (current_token == NAME) {
356     skip_spaces();
357     if (current_token == ASSIGNMENT)
358 }
359     
360
361 */
362
363
364 void
365 init_parser ()
366 {
367   g_number_of_instructions = 0;
368 }
369
370
371
372
373
374 void
375 relase_parser ()
376 {
377   for (int i = 0; i < g_number_of_instructions; ++i)
378     {
379       zmem_destroy (g_codes[i]);
380     }
381   g_number_of_instructions = 0;
382 }