1 /*
2 Z88-DK Z80ASM - Z80 Assembler
3 
4 Copyright (C) Gunther Strube, InterLogic 1993-99
5 Copyright (C) Paulo Custodio, 2011-2020
6 License: The Artistic License 2.0, http://www.perlfoundation.org/artistic_license_2_0
7 Repository: https://github.com/z88dk/z88dk
8 
9 Scanner. Scanning engine is built by ragel from scan_rules.rl.
10 */
11 
12 #include "alloc.h"
13 #include "errors.h"
14 #include "init.h"
15 #include "list.h"
16 #include "model.h"
17 #include "options.h"
18 #include "scan.h"
19 #include "str.h"
20 #include "utarray.h"
21 #include "die.h"
22 #include "zutils.h"
23 
24 #include <ctype.h>
25 
26 /*-----------------------------------------------------------------------------
27 * 	Globals
28 *----------------------------------------------------------------------------*/
29 Sym  sym;
30 bool EOL;
31 
32 /*-----------------------------------------------------------------------------
33 * 	Static - current scan context
34 *----------------------------------------------------------------------------*/
35 static Str	*input_buf;					/* current input buffer */
36 static List *input_stack;				/* stack of previous contexts */
37 
38 static bool	 at_bol;					/* true if at beginning of line */
39 
40 static int	 cs, act;					/* Ragel state variables */
41 static char	*p, *pe, *eof_, *ts, *te;	/* Ragel state variables */
42 
43 /* static DEFINE_STR(sym_string, STR_SIZE); */
44 
45 static bool	expect_opcode;				/* true to return opcodes as tokens,
46 										*  false to return as names */
47 
48 /* save scan status */
49 typedef struct scan_state_t
50 {
51 	Sym		 sym;
52 	char	*input_buf;
53 	bool	 at_bol;
54 	bool	 EOL;
55 	int		 cs, act;
56 	int		 p, pe, eof_, ts, te;
57 //	char	*sym_string;
58 	bool	 expect_opcode;
59 } ScanState;
60 
ut_scan_state_dtor(void * elt)61 static void ut_scan_state_dtor(void *elt)
62 {
63 	ScanState *state = elt;
64 	m_free(state->input_buf);
65 //	m_free(state->sym_string);
66 }
67 static UT_array *scan_state;
68 static UT_icd ut_scan_state_icd = { sizeof(ScanState), NULL, NULL, ut_scan_state_dtor };
69 
70 /*-----------------------------------------------------------------------------
71 * 	Init, save and restore current sym
72 *----------------------------------------------------------------------------*/
init_sym(void)73 static void init_sym(void)
74 {
75 	sym.tok = sym.tok_opcode = TK_END;
76 	sym.tstart = "";
77 	sym.tlen = 0;
78 #if 0
79 	sym.text = "";
80 	sym.string = NULL;
81 	sym.filename = NULL;
82 	sym.line_nr = 0;
83 #endif
84 	sym.number = 0;
85 }
86 
87 /*-----------------------------------------------------------------------------
88 *   Init functions
89 *----------------------------------------------------------------------------*/
DEFINE_init_module()90 DEFINE_init_module()
91 {
92 	input_buf = Str_new(STR_SIZE);
93 	p = Str_data(input_buf);
94 
95 	input_stack	 = OBJ_NEW(List);
96 	input_stack->free_data = m_free_compat;
97 
98 	init_sym();
99 	utarray_new(scan_state, &ut_scan_state_icd);
100 }
101 
DEFINE_dtor_module()102 DEFINE_dtor_module()
103 {
104 	Str_delete(input_buf);
105 	OBJ_DELETE(input_stack);
106 	utarray_free(scan_state);
107 }
108 
109 /*-----------------------------------------------------------------------------
110 * 	Save and restore current state
111 *----------------------------------------------------------------------------*/
save_scan_state(void)112 void save_scan_state(void)
113 {
114 	ScanState save;
115 
116 	init_module();
117 
118 	save.sym = sym;
119 	save.input_buf = m_strdup(Str_data(input_buf));
120 	save.at_bol = at_bol;
121 	save.EOL = EOL;
122 	save.cs = cs;
123 	save.act = act;
124 	save.p   = p   ? p   - Str_data(input_buf) : -1;
125 	save.pe  = pe  ? pe  - Str_data(input_buf) : -1;
126 	save.eof_ = eof_ ? eof_ - Str_data(input_buf) : -1;
127 	save.ts  = ts  ? ts  - Str_data(input_buf) : -1;
128 	save.te  = te  ? te  - Str_data(input_buf) : -1;
129 //	save.sym_string = m_strdup(sym_string->str);
130 	save.expect_opcode = expect_opcode;
131 
132 	utarray_push_back(scan_state, &save);
133 }
134 
restore_scan_state(void)135 void restore_scan_state(void)
136 {
137 	ScanState *save;
138 
139 	init_module();
140 
141 	save = (ScanState *)utarray_back(scan_state);
142 	sym = save->sym;
143 	Str_set(input_buf, save->input_buf);
144 	at_bol = save->at_bol;
145 	EOL = save->EOL;
146 	cs = save->cs;
147 	act = save->act;
148 	p   = save->p   >= 0 ? Str_data(input_buf) + save->p   : NULL;
149 	pe  = save->pe  >= 0 ? Str_data(input_buf) + save->pe  : NULL;
150 	eof_ = save->eof_ >= 0 ? Str_data(input_buf) + save->eof_ : NULL;
151 	ts  = save->ts  >= 0 ? Str_data(input_buf) + save->ts  : NULL;
152 	te  = save->te  >= 0 ? Str_data(input_buf) + save->te  : NULL;
153 //	Str_set(sym_string, save->sym_string);
154 	expect_opcode = save->expect_opcode;
155 
156 	utarray_pop_back(scan_state);
157 }
158 
drop_scan_state(void)159 void drop_scan_state(void)
160 {
161 	init_module();
162 
163 	utarray_pop_back(scan_state);
164 }
165 
scan_expect_opcode(void)166 void scan_expect_opcode(void)
167 {
168 	init_module();
169 
170 	expect_opcode = true;
171 
172 	/* convert current symbol */
173 	if (sym.tok_opcode)
174 		sym.tok = sym.tok_opcode;
175 }
176 
scan_expect_operands(void)177 void scan_expect_operands(void)
178 {
179 	init_module();
180 
181 	expect_opcode = false;
182 
183 	/* convert current symbol */
184 	if (sym.tok_opcode)
185 		sym.tok = TK_NAME;
186 }
187 
188 
189 /*-----------------------------------------------------------------------------
190 *	convert number to int, range warning if greater than INT_MAX
191 *----------------------------------------------------------------------------*/
scan_num(char * text,int length,int base)192 static long scan_num ( char *text, int length, int base )
193 {
194 	long value;
195 	int digit = 0;
196 	char c;
197 	int i;
198 
199 	value = 0;
200 	for ( i = 0 ; i < length ; i++ )
201 	{
202 		c = *text++;					/* read each digit */
203 		if ( isdigit(c) )
204 		{
205 			digit = c - '0';
206 		}
207 		else if ( isalpha(c) )
208 		{
209 			digit = toupper(c) - 'A' + 10;
210 		}
211 		else if ( base == 2 && (c == '-' || c == '#') )
212 		{
213 			digit = (c == '#') ? 1 : 0;
214 		}
215 		else
216 		{
217 			xassert(0); /* invalid digit - should not be reached */
218 		}
219 
220 		if (digit >= base)
221 		{
222 			xassert(0); /* digit out of range - should not be reached */
223 		}
224 
225 		value = value * base + digit;
226 	}
227 
228 	return value;
229 }
230 
231 /*-----------------------------------------------------------------------------
232 *   copy tok_string, start with p pointing at the start quote (' or "),
233 *	end with p pointing at the end quote, copy characters to tok_string
234 *	handling C escape sequences. Return false if string not terminated.
235 *----------------------------------------------------------------------------*/
get_sym_string(void)236 static bool get_sym_string( void )
237 {
238 	char quote;
239 
240 	/* mark token start */
241 	quote = *p++;
242 	xassert( quote == '"' || quote == '\'' );
243 	ts = p;
244 
245 	/* search for end quote or end of string */
246 	while (true)
247 	{
248 		if (*p == '\\' && p[1] != '\0')
249 			p++;						/* skip char after backslash, may be a quote */
250 		else if (*p == quote)
251 		{
252 			te = p;
253 			return true;
254 		}
255 		else if (*p == '\n' || *p == '\0')
256 		{
257 			te = ts;
258 			p--;						/* point to before separator */
259 			return false;
260 		}
261 
262 		/* advance to next */
263 		p++;
264 	}
265 }
266 
267 /*-----------------------------------------------------------------------------
268 *   skip up to and not including newline
269 *----------------------------------------------------------------------------*/
skip_to_newline(void)270 static void skip_to_newline( void )
271 {
272 	char *newline = strchr( p, '\n' );
273 	if ( newline != NULL && newline > p )
274 		p = newline - 1;
275 }
276 
277 /*-----------------------------------------------------------------------------
278 *   Skip line past the newline, set EOL
279 *----------------------------------------------------------------------------*/
Skipline(void)280 void Skipline( void )
281 {
282 	init_module();
283 
284 	if ( ! EOL )
285 	{
286 		char *newline = strchr( p, '\n' );
287 		if ( newline == NULL )
288 			p += strlen(p);
289 		else
290 			p = newline + 1;
291 
292 		EOL = true;
293 	}
294 }
295 
296 
297 /*-----------------------------------------------------------------------------
298 *   Import scanner generated by ragel
299 *----------------------------------------------------------------------------*/
300 #include "scan_rules.h"
301 
302 /*-----------------------------------------------------------------------------
303 *   Fill scan buffer if needed, return false on end of file
304 *----------------------------------------------------------------------------*/
fill_buffer(void)305 static bool fill_buffer( void )
306 {
307 	char *line;
308 
309 	while ( *p == '\0' )
310 	{
311 		/* get last buffer from stack, if any */
312 		line = List_pop( input_stack );
313 		if ( line != NULL )
314 		{
315 			set_scan_buf( line, false );	/* read from stack - assume not at BOL */
316 			m_free( line );
317 		}
318 		else
319 		{
320 			/* get next line from input source file */
321 			line = src_getline();
322 			if ( line == NULL )
323 				return false;
324 
325 			/* got new line */
326 			set_scan_buf( line, true );		/* read from file - at BOL */
327 		}
328 	}
329 
330 	return true;
331 }
332 
333 /*-----------------------------------------------------------------------------
334 *   Get the next token, fill the corresponding tok* variables
335 *----------------------------------------------------------------------------*/
GetSym(void)336 tokid_t GetSym( void )
337 {
338 	init_module();
339 
340 	init_sym();
341 
342 	/* keep returning TK_NEWLINE until EOL is cleared
343 	*  NOTE: HACK for inconsistent parser in handling newlines, should be removed */
344 	if ( EOL )
345 	{
346 		at_bol = true;
347 		sym.tstart = "\n"; sym.tlen = 1;
348 		return (sym.tok = TK_NEWLINE);			/* assign and return */
349 	}
350 
351 	/* loop filling buffer when needed */
352 	do
353 	{
354 		/* refill buffer if needed, check for end of file */
355 		if ( ! fill_buffer() )
356 		{
357 			sym.tok = TK_END;
358 			ts = te = p;
359 			break;
360 		}
361 
362 		/* run the state machine */
363 		sym.tok = _scan_get();
364 
365 	} while ( sym.tok == TK_END );
366 
367 	sym.tstart = ts; sym.tlen = te - ts;			/* remember token position */
368 
369 	at_bol = EOL = (sym.tok == TK_NEWLINE) ? true : false;
370 	return sym.tok;
371 }
372 
373 
374 /* get the next token, error if not the expected one */
GetSymExpect(tokid_t expected_tok)375 void GetSymExpect(tokid_t expected_tok)
376 {
377 	init_module();
378 
379 	GetSym();
380 	CurSymExpect(expected_tok);
381 }
382 
383 
384 /* check the current token, error if not the expected one */
CurSymExpect(tokid_t expected_tok)385 void CurSymExpect(tokid_t expected_tok)
386 {
387 	init_module();
388 
389 	if (sym.tok != expected_tok)
390 		error_syntax();
391 }
392 
393 
394 /*-----------------------------------------------------------------------------
395 *   Insert the given text at the current scan position
396 *----------------------------------------------------------------------------*/
SetTemporaryLine(const char * line)397 void SetTemporaryLine(const char *line )
398 {
399 	init_module();
400 
401 #if 0
402 	if (*p != '\0')
403 		List_push(&input_stack, m_strdup(p));		/* save current input */
404 #endif
405 	set_scan_buf( line, false );					/* assume not at BOL */
406 }
407 
408 /*-----------------------------------------------------------------------------
409 *   return static string with current token text
410 *   non-reentrant, string needs to be saved by caller
411 *----------------------------------------------------------------------------*/
sym_text(Sym * sym)412 char *sym_text(Sym *sym)
413 {
414 	static STR_DEFINE(text, STR_SIZE);
415 
416 	Str_set_bytes(text, sym->tstart, sym->tlen);
417 	return Str_data(text);
418 }
419