--- /dev/null
+/*!@file\r
+ \brief lexical analyzer implementation for APC\r
+ \details The lexer manages two FIFO stacks. One for maintaining tokens, the\r
+ other for maintaining a list of files to be scanned. During\r
+ execution, the lexer will return a token from its token queue if any\r
+ are present. If not, the lexer will will pop an element from its\r
+ file queue to 'scanner' to be tokenized. If the file queue is empty,\r
+ the lexer will instead call 'parsedir' to traverse the directory tree\r
+ and tokenize the results. If 'parsedir' does not generate any new\r
+ tokens, we are done.\r
+ \author Jordan Lavatai\r
+ \date Aug 2016\r
+ ----------------------------------------------------------------------------*/\r
+/* Standard */\r
+#include <stdio.h>\r
+#include <string.h>\r
+#include <errno.h>\r
+/* Posix */\r
+#include <unistd.h>\r
+#include <stdlib.h>\r
+#include <dirent.h>\r
+/* Local */\r
+#include "parser.tab.h"\r
+#ifndef DE_STACKSIZE\r
+#define DE_STACKSIZE 1024\r
+#endif\r
+#ifndef TK_STACKSIZE\r
+#define TK_STACKSIZE 1024\r
+#endif\r
+/* Public */\r
+int lexer_init(void);\r
+int lexer(void);\r
+int lexer_lexfile(const char*);\r
+void lexer_pushtok(int, YYSTYPE);\r
+extern //lexer_lex.rl\r
+int lexer_lex(const char*);\r
+struct dirent* lexer_direntpa[DE_STACKSIZE], **lexer_direntpp;\r
+/* Private */\r
+extern //scanner.c\r
+int scanner_init(void);\r
+extern //scanner.c\r
+int scanner(void);\r
+static inline\r
+int dredge_current_depth(void);\r
+extern //bison\r
+YYSTYPE yylval;\r
+static\r
+struct tok\r
+{ YYSTYPE lval; //token val\r
+ int tok_t; //token type\r
+} token_stack[TK_STACKSIZE];\r
+static\r
+union tokp\r
+{ int* tpt; //token pointer type\r
+ struct tok* tok;\r
+ YYSTYPE* tvp; //token value pointer\r
+} tks, tkx;\r
+\r
+/* Directory Entity Array/Stack\r
+ Simple array for keeping track of dirents yet to be processed by the scanner.\r
+ If this list is empty and there are no tokens, the lexer is done.\r
+ This array is populated by the scanner as an array, and popped locally by the\r
+ lexer as a stack.\r
+*/\r
+#define DE_STACK (lexer_direntpa)\r
+#define DE_STACKP (lexer_direntpp)\r
+#define DE_LEN() (DE_STACKP - DE_STACK)\r
+#define DE_INIT() (DE_STACKP = DE_STACK)\r
+#define DE_POP() (*--DE_STACKP)\r
+\r
+/* Token Stack\r
+ This is a FIFO stack whose pointers are a union of either a pointer to an\r
+ integer, or a pointer to two integers (a struct tok). This way, integers may\r
+ be added or removed from the stack either singularly (IPUSH/IPOP), or as a\r
+ full token of two integers (PUSH/POP).\r
+ An alignment error will occur if IPOP or IPUSH are used a non-even number of\r
+ times in a sequence!\r
+*/\r
+#define TK_STACK (token_stack)\r
+#define TK_STACKP (tks.tok)\r
+#define TK_STACKPI (tks.tpt)\r
+#define TK_STACKPL (tks.tvp)\r
+#define TK_STACKX (tkx.tok)\r
+#define TK_STACKXI (tkx.tpt)\r
+#define TK_LEN() (TK_STACKX - TK_STACKP)\r
+#define TK_INIT() (TK_STACKP = TK_STACKX = TK_STACK)\r
+#define TK_POP() (*TK_STACKP++)\r
+#define TK_POPI() (*TK_STACKPI++);\r
+#define TK_POPL() (*TK_STACKPL++);\r
+#define TK_PUSH(T,L) (*TK_STACKX++ = (struct tok){L,T})\r
+\r
+/* Initializer\r
+ The initializer returns boolean true if an error occurs, which may be handled with standard errno.\r
+*/\r
+int lexer_init\r
+()\r
+{ TK_INIT();\r
+ DE_INIT();\r
+ return scanner_init();\r
+}\r
+\r
+/* Lexer\r
+ If the token buffer is empty, 'lexer' will initialize the token buffer and\r
+ call 'lexer_scandir'. If SCAN_ERROR is returned, an error is printed\r
+ before sending a null return to bison. If 0 tokens are generated, the error\r
+ printing is skipped. In all other cases, 'yylval' is set, and the token's\r
+ integer representation is returned.\r
+*/\r
+int lexer\r
+#define $($)#$\r
+#define SCAN_ERROR -1\r
+#define TK_EMPTY (TK_STACKP == TK_STACKX)\r
+#define FAIL(...) \\r
+ do { \\r
+ fprintf(stderr,__VA_ARGS__); \\r
+ goto done; \\r
+ } while (0)\r
+()\r
+{start:\r
+ while (DE_LEN() > 0) //lex any directory entries in our stack\r
+ if (lexer_lexfile(DE_POP()->d_name) == 0)\r
+ FAIL("Lexer failed to tokenize [%s]\n",(*DE_STACKP)->d_name);\r
+ if (TK_EMPTY) //if there are no tokens,\r
+ { TK_INIT(); //initialize the token stack back to 0\r
+ switch (scanner())\r
+ { case SCAN_ERROR: //if an error occurred,\r
+ FAIL("Scanner error\n");\r
+ case 0: //if the the scanner finds no dirents,\r
+ goto done; //then we are done\r
+ default: //if we found some elements to scan,\r
+ goto start; //start over and lex them\r
+ }\r
+ }\r
+ yylval = TK_POPL();\r
+ return TK_POPI();\r
+ done:\r
+ yylval.val = 0;\r
+ return 0;\r
+}\r
+\r
+\r
+/* Token Receiver\r
+ This receiver takes a struct tok and pushes it to the FIFO stack.\r
+*/\r
+void lexer_pushtok\r
+#define $($)#$ //stringifier\r
+#define ERR_TK "Fatal: Generated over " $(TK_STACKSIZE) " tokens in one pass."\r
+( int tok, YYSTYPE lval )\r
+{ if (TK_LEN() >= TK_STACKSIZE)\r
+ { fprintf(stderr, ERR_TK);\r
+ exit(EXIT_FAILURE);\r
+ }\r
+ TK_PUSH(tok, lval);\r
+ printf("Pushed Token %i | %i\n", TK_STACK[TK_LEN() - 1].tok_t, TK_STACK[TK_LEN() - 1].lval.val);\r
+}\r
+\r
+/* Lexical analysis of a file\r
+ Strips a filename to its base name, then sends it to lexer_lex\r
+*/\r
+int lexer_lexfile\r
+#define MAX_FNAME 2048\r
+#define HIDDEN_WARNING "%s is hidden and will not be parsed!\n", filename\r
+( const char *filename\r
+)\r
+{ static char fname[MAX_FNAME];\r
+ char *last_period = NULL, *iter;\r
+\r
+ if (*filename == '.')\r
+ { fprintf (stderr, HIDDEN_WARNING);\r
+ return 0;\r
+ }\r
+ strncpy(fname,filename,MAX_FNAME);\r
+ last_period = NULL;\r
+ for (iter = fname; *iter; iter++)\r
+ if (*iter == '.')\r
+ last_period = iter;\r
+ if (last_period)\r
+ *last_period = '\0';\r
+ return lexer_lex(fname);\r
+}\r