/*!@file \brief lexical analyzer implementation for APC \details The lexer manages two FIFO stacks. One for maintaining tokens, the other for maintaining a list of files to be scanned. During execution, the lexer will return a token from its token queue if any are present. If not, the lexer will will pop an element from its file queue to 'scanner' to be tokenized. If the file queue is empty, the lexer will instead call 'parsedir' to traverse the directory tree and tokenize the results. If 'parsedir' does not generate any new tokens, we are done. \author Jordan Lavatai \date Aug 2016 ----------------------------------------------------------------------------*/ /* Standard */ #include #include #include /* Posix */ #include #include #include /* Local */ #include "parser.tab.h" #ifndef DE_STACKSIZE #define DE_STACKSIZE 1024 #endif #ifndef TK_STACKSIZE #define TK_STACKSIZE 1024 #endif /* Public */ int lexer_init(void); int lexer(void); void lexer_pushtok(int, YYSTYPE); extern //ragel int lexer_lex(const char*); struct dirent* lexer_direntpa[DE_STACKSIZE]; /* Private */ extern //scanner.c int scanner_init(void); extern //scanner.c int scanner(void); static inline int dredge_current_depth(void); extern //bison YYSTYPE yylval; static struct tok { union YYSTYPE val; //token val int tt; //token type } token_stack[TK_STACKSIZE]; static union tokp { int* tpt; //token pointer type struct tok* tok; union YYSTYPE* tvp; //token value pointer } tks, tkx; static struct dirent** dps; /* Directory Entity Array/Stack Simple array for keeping track of dirents yet to be processed by the scanner. If this list is empty and there are no tokens, the lexer is done. This array is populated by the scanner as an array, and popped locally by the lexer as a stack. */ #define DE_STACK (lexer_direntpa) #define DE_STACKP (dps) #define DE_LEN() (DE_STACKP - DE_STACK) #define DE_INIT() (DE_STACKP = DE_STACK) #define DE_POP() (*--DE_STACKP) /* Token Stack This is a FIFO stack whose pointers are a union of either a pointer to an integer, or a pointer to two integers (a struct tok). This way, integers may be added or removed from the stack either singularly (IPUSH/IPOP), or as a full token of two integers (PUSH/POP). An alignment error will occur if IPOP or IPUSH are used a non-even number of times in a sequence! */ #define TK_STACK (token_stack) #define TK_STACKP (tks.tok) #define TK_STACKPI (tks.tpt) #define TK_STACKPL (tks.tvp) #define TK_STACKX (tkx.tok) #define TK_STACKXI (tkx.tpt) #define TK_LEN() (TK_STACKP - TK_STACKX) #define TK_INIT() (TK_STACKP = TK_STACKX = TK_STACK) #define TK_POP() (*TK_STACKP++) #define TK_POPI() (*TK_STACKPI++); #define TK_POPL() (*TK_STACKPL++); #define TK_PUSH(T,L) (*TK_STACKX++ = (struct tok){L,T}) /* Initializer The initializer returns boolean true if an error occurs, which may be handled with standard errno. */ int lexer_init () { TK_INIT(); DE_INIT(); return scanner_init(); } /* Lexer If the token buffer is empty, 'lexer' will initialize the token buffer and call 'lexer_scandir'. If #SCANDIR_ERROR is returned, an error is printed before sending a null return to bison. If 0 tokens are generated, the error printing is skipped. In all other cases, 'yylval' is set, and the token's integer representation is returned. */ int lexer #define SCAN_ERROR -1 #define TK_EMPTY (TK_STACKP == TK_STACKX) () { if (TK_EMPTY) { TK_INIT(); if (scanner() == 0) { yylval.val = 0; return 0; } } yylval = TK_POPL(); return TK_POPI(); } /* Token Receiver This receiver takes a struct tok and pushes it to the FIFO stack. */ void lexer_pushtok #define S(S)#S //stringifier #define ERR_TK "Fatal: Generated over " S(TK_STACKSIZE) " tokens in one pass." ( int tok, YYSTYPE lval ) { if (TK_LEN() >= TK_STACKSIZE) { fprintf(stderr, ERR_TK); exit(EXIT_FAILURE); } TK_PUSH(tok, lval); } /* init_file: if (lsp != NULL) while ((c = *lsp++) == *csp) { switch (c) { case DELIM: delimeters_skipped++; default: csp++; //delayed to ensure csp is the start of scannable text break; } } last_string = string; scan_text: return scanner_tokenize(csp); */