-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathScanner.c
More file actions
492 lines (439 loc) · 14.5 KB
/
Scanner.c
File metadata and controls
492 lines (439 loc) · 14.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
/*
************************************************************
* COMPILERS COURSE - Algonquin College
* Code version: Summer, 2022
* Author: Svillen Ranev - Paulo Sousa
* Professors: Paulo Sousa
************************************************************
*/
/*
************************************************************
* File name: scanner.h
* Compiler: MS Visual Studio 2022
* Author: Paulo Sousa
* Course: CST 8152 – Compilers, Lab Section: [011, 012, 013]
* Assignment: A22, A32.
* Date: Jul 01 2022
* Professor: Paulo Sousa
* Purpose: This file contains all functionalities from Scanner.
* Function list: (...).
************************************************************
*/
/* TO_DO: Adjust the function header */
/* The #define _CRT_SECURE_NO_WARNINGS should be used in MS Visual Studio projects
* to suppress the warnings about using "unsafe" functions like fopen()
* and standard sting library functions defined in string.h.
* The define does not have any effect in Borland compiler projects.
*/
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h> /* standard input / output */
#include <ctype.h> /* conversion functions */
#include <stdlib.h> /* standard library functions and constants */
#include <string.h> /* string functions */
#include <limits.h> /* integer types constants */
#include <float.h> /* floating-point types constants */
/* #define NDEBUG to suppress assert() call */
#include <assert.h> /* assert() prototype */
/* project header files */
#ifndef COMPILERS_H_
#include "Compiler.h"
#endif
#ifndef BUFFER_H_
#include "Buffer.h"
#endif
#ifndef SCANNER_H_
#include "Scanner.h"
#endif
/*
----------------------------------------------------------------
TO_DO: Global vars definitions
----------------------------------------------------------------
*/
/* Global objects - variables */
/* This buffer is used as a repository for string literals. */
extern BufferPointer stringLiteralTable; /* String literal table */
monaco_int line; /* Current line number of the source code */
extern monaco_int errorNumber; /* Defined in platy_st.c - run-time error number */
extern monaco_int stateType[];
extern monaco_chr* keywordTable[];
extern PTR_ACCFUN finalStateTable[];
extern monaco_int transitionTable[][TABLE_COLUMNS];
/* Local(file) global objects - variables */
static BufferPointer lexemeBuffer; /* Pointer to temporary lexeme buffer */
static BufferPointer sourceBuffer; /* Pointer to input source buffer */
/*
************************************************************
* Intitializes scanner
* This function initializes the scanner using defensive programming.
***********************************************************
*/
/* TO_DO: Follow the standard and adjust datatypes */
monaco_int startScanner(BufferPointer psc_buf) {
/* in case the buffer has been read previously */
bufRecover(psc_buf);
bufClear(stringLiteralTable);
line = 1;
sourceBuffer = psc_buf;
return EXIT_SUCCESS; /*0*/
}
/*
************************************************************
* Process Token
* Main function of buffer, responsible to classify a char (or sequence
* of chars). In the first part, a specific sequence is detected (reading
* from buffer). In the second part, a pattern (defined by Regular Expression)
* is recognized and the appropriate function is called (related to final states
* in the Transition Diagram).
***********************************************************
*/
Token tokenizer(void) {
/* TO_DO: Follow the standard and adjust datatypes */
Token currentToken = { 0 }; /* token to return after pattern recognition. Set all structure members to 0 */
monaco_chr c; /* input symbol */
monaco_int state = 0; /* initial state of the FSM */
monaco_int lexStart; /* start offset of a lexeme in the input char buffer (array) */
monaco_int lexEnd; /* end offset of a lexeme in the input char buffer (array)*/
monaco_int lexLength; /* token length */
monaco_int i; /* counter */
monaco_chr newc; /* new char */
while (1) { /* endless loop broken by token returns it will generate a warning */
c = bufGetChar(sourceBuffer);
/* ------------------------------------------------------------------------
Part 1: Implementation of token driven scanner.
Every token is possessed by its own dedicated code
-----------------------------------------------------------------------
*/
/* TO_DO: All patterns that do not require accepting functions */
switch (c) {
/* Cases for spaces */
case ' ':
case '\t':
case '\f':
break;
case '\n':
line++;
break;
/* Cases for symbols */
case ';':
currentToken.code = EOS_T;
return currentToken;
case '(':
currentToken.code = LPR_T;
return currentToken;
case ')':
currentToken.code = RPR_T;
return currentToken;
case '{':
currentToken.code = LBR_T;
return currentToken;
case '}':
currentToken.code = RBR_T;
return currentToken;
/* Comments */
case '@':
newc = bufGetChar(sourceBuffer);
do {
c = bufGetChar(sourceBuffer);
if (c == CHARSEOF0 || c == CHARSEOF255) {
bufRetract(sourceBuffer);
//return currentToken;
} else if (c=='\n') {
line++;
}
} while (c != '@' && c!= CHARSEOF0 && c!= CHARSEOF255);
break;
/* Cases for END OF FILE */
case CHARSEOF0:
currentToken.code = SEOF_T;
currentToken.attribute.seofType = SEOF_0;
return currentToken;
case CHARSEOF255:
currentToken.code = SEOF_T;
currentToken.attribute.seofType = SEOF_255;
return currentToken;
/* ------------------------------------------------------------------------
Part 2: Implementation of Finite State Machine (DFA) or Transition Table driven Scanner
Note: Part 2 must follow Part 1 to catch the illegal symbols
-----------------------------------------------------------------------
*/
/* TO_DO: Adjust / check the logic for your language */
default: // general case
state = nextState(state, c);
lexStart = bufGetPosRead(sourceBuffer) - 1;
bufSetMark(sourceBuffer, lexStart);
int pos = 0;
while (stateType[state] == NOAS) {
c = bufGetChar(sourceBuffer);
state = nextState(state, c);
pos++;
}
if (stateType[state] == ASWR)
bufRetract(sourceBuffer);
lexEnd = bufGetPosRead(sourceBuffer);
lexLength = lexEnd - lexStart;
lexemeBuffer = bufCreate((short)lexLength + 2, 0, MODE_FIXED);
if (!lexemeBuffer) {
fprintf(stderr, "Scanner error: Can not create buffer\n");
exit(1);
}
bufRestore(sourceBuffer);
for (i = 0; i < lexLength; i++)
bufAddChar(lexemeBuffer, bufGetChar(sourceBuffer));
bufAddChar(lexemeBuffer, BUFFER_EOF);
currentToken = (*finalStateTable[state])(bufGetContent(lexemeBuffer, 0));
bufDestroy(lexemeBuffer);
return currentToken;
} // switch
} //while
} // tokenizer
/*
************************************************************
* Get Next State
The assert(int test) macro can be used to add run-time diagnostic to programs
and to "defend" from producing unexpected results.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(*) assert() is a macro that expands to an if statement;
if test evaluates to false (zero) , assert aborts the program
(by calling abort()) and sends the following message on stderr:
(*) Assertion failed: test, file filename, line linenum.
The filename and linenum listed in the message are the source file name
and line number where the assert macro appears.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
If you place the #define NDEBUG directive ("no debugging")
in the source code before the #include <assert.h> directive,
the effect is to comment out the assert statement.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
The other way to include diagnostics in a program is to use
conditional preprocessing as shown bellow. It allows the programmer
to send more details describing the run-time problem.
Once the program is tested thoroughly #define DEBUG is commented out
or #undef DEBUG is used - see the top of the file.
***********************************************************
*/
/* TO_DO: Just change the datatypes */
monaco_int nextState(monaco_int state, monaco_chr c) {
monaco_int col;
monaco_int next;
col = nextClass(c);
next = transitionTable[state][col];
if (DEBUG)
printf("Input symbol: %c Row: %d Column: %d Next: %d \n", c, state, col, next);
assert(next != IS);
if (DEBUG)
if (next == IS) {
printf("Scanner Error: Illegal state:\n");
printf("Input symbol: %c Row: %d Column: %d\n", c, state, col);
exit(1);
}
return next;
}
/*
************************************************************
* Get Next Token Class
* Create a function to return the column number in the transition table:
* Considering an input char c, you can identify the "class".
* For instance, a letter should return the column for letters, etc.
***********************************************************
*/
/* TO_DO: Use your column configuration */
/* Adjust the logic to return next column in TT */
/* [A-z](0), [0-9](1), _(2), &(3), "(4), SEOF(5), other(6) */
monaco_int nextClass(monaco_chr c) {
monaco_int val = -1;
switch (c) {
case CHRCOL2:
val = 2;
break;
case CHRCOL3:
val = 3;
break;
case CHRCOL4:
val = 4;
break;
case CHARSEOF0:
case CHARSEOF255:
val = 5;
break;
default:
if (isalpha(c))
val = 0;
else if (isdigit(c))
val = 1;
else
val = 6;
}
return val;
}
/*
************************************************************
* Acceptance State Function ID
* In this function, the pattern for IDs must be recognized.
* Since keywords obey the same pattern, is required to test if
* the current lexeme matches with KW from language.
* - Remember to respect the limit defined for lexemes (VID_LEN) and
* set the lexeme to the corresponding attribute (vidLexeme).
* Remember to end each token with the \0.
* - Suggestion: Use "strncpy" function.
***********************************************************
*/
/* TO_DO: Adjust the function for ID */
Token funcID(monaco_chr lexeme[]) {
Token currentToken = { 0 };
size_t length = strlen(lexeme);
monaco_chr lastch = lexeme[length - 1];
monaco_int isID = monaco_FALSE;
switch (lastch) {
case MNIDPREFIX:
currentToken.code = MNID_T;
isID = monaco_TRUE;
break;
default:
// Test Keyword
currentToken = funcKEY(lexeme);
break;
}
if (isID == monaco_TRUE) {
strncpy(currentToken.attribute.idLexeme, lexeme, VID_LEN);
currentToken.attribute.idLexeme[VID_LEN] = CHARSEOF0;
}
return currentToken;
}
/*
************************************************************
* Acceptance State Function SL
* Function responsible to identify SL (string literals).
* - The lexeme must be stored in the String Literal Table
* (stringLiteralTable). You need to include the literals in
* this structure, using offsets. Remember to include \0 to
* separate the lexemes. Remember also to incremente the line.
***********************************************************
*/
/* TO_DO: Adjust the function for SL */
Token funcSL(monaco_chr lexeme[]) {
Token currentToken = { 0 };
monaco_int i = 0, len = (monaco_int)strlen(lexeme);
currentToken.attribute.contentString = bufGetPosWrte(stringLiteralTable);
for (i = 1; i < len - 1; i++) {
if (lexeme[i] == '\n')
line++;
if (!bufAddChar(stringLiteralTable, lexeme[i])) {
currentToken.code = RTE_T;
strcpy(currentToken.attribute.errLexeme, "Run Time Error:");
errorNumber = RTE_CODE;
return currentToken;
}
}
if (!bufAddChar(stringLiteralTable, CHARSEOF0)) {
currentToken.code = RTE_T;
strcpy(currentToken.attribute.errLexeme, "Run Time Error:");
errorNumber = RTE_CODE;
return currentToken;
}
currentToken.code = STR_T;
return currentToken;
}
/*
************************************************************
* This function checks if one specific lexeme is a keyword.
* - Tip: Remember to use the keywordTable to check the keywords.
***********************************************************
*/
/* TO_DO: Adjust the function for Keywords */
Token funcKEY(monaco_chr lexeme[]) {
Token currentToken = { 0 };
monaco_int kwindex = -1, j = 0;
for (j = 0; j < KWT_SIZE; j++)
if (!strcmp(lexeme, &keywordTable[j][0]))
kwindex = j;
if (kwindex != -1) {
currentToken.code = KW_T;
currentToken.attribute.codeType = kwindex;
}
else {
currentToken = funcErr(lexeme);
}
return currentToken;
}
/*
************************************************************
* Acceptance State Function Error
* Function responsible to deal with ERR token.
* - This function uses the errLexeme, respecting the limit given
* by ERR_LEN. If necessary, use three dots (...) to use the
* limit defined. The error lexeme contains line terminators,
* so remember to increment line.
***********************************************************
*/
/* TO_DO: Adjust the function for Errors */
Token funcErr(monaco_chr lexeme[]) {
Token currentToken = { 0 };
monaco_int i = 0, len = (monaco_int)strlen(lexeme);
if (len > ERR_LEN) {
strncpy(currentToken.attribute.errLexeme, lexeme, ERR_LEN - 3);
currentToken.attribute.errLexeme[ERR_LEN - 3] = CHARSEOF0;
strcat(currentToken.attribute.errLexeme, "...");
}
else {
strcpy(currentToken.attribute.errLexeme, lexeme);
}
for (i = 0; i < len; i++)
if (lexeme[i] == '\n')
line++;
currentToken.code = ERR_T;
return currentToken;
}
/*
************************************************************
* The function prints the token returned by the scanner
***********************************************************
*/
monaco_nul printToken(Token t) {
extern monaco_chr* keywordTable[]; /* link to keyword table in */
switch (t.code) {
case RTE_T:
printf("RTE_T\t\t%s", t.attribute.errLexeme);
/* Call here run-time error handling component */
if (errorNumber) {
printf("%d", errorNumber);
exit(errorNumber);
}
printf("\n");
break;
case ERR_T:
break;
case SEOF_T:
printf("SEOF_T\t\t%d\t\n", t.attribute.seofType);
break;
case MNID_T:
printf("MNID_T\t\t%s\n", t.attribute.idLexeme);
break;
case STR_T:
printf("STR_T\t\t%d\t ", (short)t.attribute.codeType);
printf("%s\n", bufGetContent(stringLiteralTable,
(short)t.attribute.codeType));
break;
case LPR_T:
printf("LPR_T\n");
break;
case RPR_T:
printf("RPR_T\n");
break;
case LBR_T:
printf("LBR_T\n");
break;
case RBR_T:
printf("RBR_T\n");
break;
case KW_T:
printf("KW_T\t\t%s\n", keywordTable[t.attribute.codeType]);
break;
case EOS_T:
printf("EOS_T\n");
break;
default:
printf("Scanner error: invalid token code: %d\n", t.code);
}
}
/*
TO_DO: (If necessary): HERE YOU WRITE YOUR ADDITIONAL FUNCTIONS (IF ANY).
*/