2 \brief lexical analyzer implementation for APC
3 \details The lexer manages two FIFO stacks. One for maintaining tokens, the
4 other for maintaining a list of files to be scanned. During
5 execution, the lexer will return a token from its token queue if any
6 are present. If not, the lexer will will pop an element from its
7 file queue to 'scanner' to be tokenized. If the file queue is empty,
8 the lexer will instead call 'parsedir' to traverse the directory tree
9 and tokenize the results. If 'parsedir' does not generate any new
11 \author Jordan Lavatai
13 ----------------------------------------------------------------------------*/
21 #include <limits.h> //realpath, NAME_MAX, PATH_MAX
24 #include "parser.tab.h"
26 #define DE_STACKSIZE 1024
29 #define TK_STACKSIZE 1024
34 int lexer_lexfile(const char*);
35 void lexer_pushtok(int, YYSTYPE
);
36 char const* lexer_get_current_filepath(void);
37 struct dirent
* lexer_direntpa
[DE_STACKSIZE
],** lexer_direntpp
,** lexer_direntpb
;
40 int lexer_lex(const char*);
42 int scanner_init(void);
46 int dredge_current_depth(void);
50 FILE* current_open_file
;
52 char const* current_filename
;
55 { YYSTYPE lval
; //token val
56 int tok_t
; //token type
57 } token_stack
[TK_STACKSIZE
], *tsp
, *tsx
;
59 /* Directory Entity Array/Stack
60 Simple array for keeping track of dirents yet to be processed by the scanner.
61 If this list is empty and there are no tokens, the lexer is done.
62 This array is populated by the scanner as an array, and popped locally by the
63 lexer as a stack, and is popped as a FIFO stack.
65 #define DE_STACK (lexer_direntpa)
66 #define DE_STACKP (lexer_direntpp)
67 #define DE_STACKB (lexer_direntpb)
68 #define DE_LEN() (DE_STACKP - DE_STACKB)
69 #define DE_INIT() (DE_STACKP = DE_STACKB = DE_STACK)
70 #define DE_POP() (*DE_STACKB++)
73 This is a FIFO stack whose pointers are a union of either a pointer to an
74 integer, or a pointer to two integers (a struct tok). This way, integers may
75 be added or removed from the stack either singularly (IPUSH/IPOP), or as a
76 full token of two integers (PUSH/POP).
77 An alignment error will occur if IPOP or IPUSH are used a non-even number of
80 #define TK_STACK (token_stack)
81 #define TK_STACKP (tsp)
82 #define TK_STACKX (tsx)
83 #define TK_LEN() (TK_STACKX - TK_STACKP)
84 #define TK_INIT() (TK_STACKP = TK_STACKX = TK_STACK)
85 #define TK_POP() (*TK_STACKP++)
86 #define TK_PUSH(T,L) (*TK_STACKX++ = (struct tok){L,T})
89 The initializer returns boolean true if an error occurs, which may be handled
96 current_open_file
= NULL
;
97 current_filename
= NULL
;
98 return scanner_init();
102 If the token buffer is empty, 'lexer' will initialize the token buffer and
103 call 'lexer_scandir'. If SCAN_ERROR is returned, an error is printed
104 before sending a null return to bison. If 0 tokens are generated, the error
105 printing is skipped. In all other cases, 'yylval' is set, and the token's
106 integer representation is returned.
110 #define SCAN_ERROR -1
111 #define TK_EMPTY (TK_STACKP == TK_STACKX)
114 fprintf(stderr,__VA_ARGS__); \
120 while (DE_LEN() > 0) //lex any directory entries in our stack
121 if (lexer_lexfile(DE_POP()->d_name
) == 0)
122 FAIL("Lexer failed to tokenize [%s]\n",(*DE_STACKB
)->d_name
);
123 if (TK_EMPTY
) //if there are no tokens,
124 { TK_INIT(); //initialize the token stack back to 0
126 { case SCAN_ERROR
: //if an error occurred,
127 FAIL("Scanner error\n");
128 case 0: //if the the scanner finds no dirents,
129 goto done
; //then we are done
130 default: //if we found some elements to scan,
131 goto start
; //start over and lex them
144 This receiver takes a struct tok and pushes it to the FIFO stack.
147 #define $($)#$ //stringifier
148 #define ERR_TK "Fatal: Generated over " $(TK_STACKSIZE) " tokens in one pass."
149 ( int tok
, YYSTYPE lval
)
150 { if (TK_LEN() >= TK_STACKSIZE
)
151 { fprintf(stderr
, ERR_TK
);
157 /* Lexical analysis of a file
158 Strips a filename to its base name, then sends it to lexer_lex
161 #define HIDDEN_WARNING "%s is hidden and will not be parsed!\n", filename
162 ( const char *filename
164 { static char fname
[NAME_MAX
];
165 char *last_period
= NULL
, *iter
;
167 if (*filename
== '.')
168 { fprintf (stderr
, HIDDEN_WARNING
);
171 /* Copy the filename and remove its suffix */
172 strncpy(fname
,filename
,NAME_MAX
);
174 for (iter
= fname
; *iter
; iter
++) //find the last '.' char
177 if (last_period
) //if we found one,
178 *last_period
= '\0'; //truncate the string there
179 /* Register the current_filename */
180 current_filename
= filename
;
182 return lexer_lex(fname
);
185 char const* lexer_get_current_filepath
187 { static char current_path
[PATH_MAX
];
188 static char const* last_filename
;
189 if ((!last_filename
|| last_filename
!= current_filename
) &&
190 (realpath(current_filename
, current_path
) != current_path
))
191 { perror("realpath: ");
194 return (const char*)current_path
;