#include #include #include #include "../lexer.h" #include "../write.h" #include "../file_utils.h" #define COLORS 16 /* lexer.h doesn't define a token type for whitespace (e.g. comments), but we need one so we can colourize comments properly. */ #define TOKEN_TYPE_WHITESPACE FUS_LEXER_TOKEN_TYPES typedef enum format { FORMAT_NONE, FORMAT_ANSI, FORMAT_HTML, FORMATS } format_t; /*********************** * GLOBAL CONSTS * ***********************/ const char *ansi_colors[COLORS] = { /* These are organized like QBASIC colours */ /* Dark */ "\e[0;30m", "\e[0;34m", "\e[0;32m", "\e[0;36m", "\e[0;31m", "\e[0;35m", "\e[0;33m", "\e[0;37m", /* Light */ "\e[0;90m", "\e[0;94m", "\e[0;92m", "\e[0;96m", "\e[0;91m", "\e[0;95m", "\e[0;93m", "\e[0;97m", }; const char *ansi_reset = "\e[0m"; const char *html_colors[COLORS] = { }; const int lexer_token_type_colors[FUS_LEXER_TOKEN_TYPES + 1] = { 12, // done (should never actually be displayed...) 6, // int 7, // sym 9, // op 2, // str 2, // blockstr 9, // open 9, // close 8, // whitespace (comments) }; /*********************** * GLOBAL VARS * ***********************/ bool use_vars = false; bool raw_tokens = false; format_t format = FORMAT_NONE; /*********************** * FUNCTIONS * ***********************/ static void print_help(){ fprintf(stderr, "Reads, parses, formats, and outputs lexer data.\n"); fprintf(stderr, "Options:\n"); fprintf(stderr, " -h | --help This message\n"); fprintf(stderr, " -v | --vars Use vars, macros, etc\n"); fprintf(stderr, " -r | --raw Output the raw tokens+whitespace from input\n"); fprintf(stderr, " (as opposed to auto-indenting, and using tokens\n"); fprintf(stderr, " generated by macros)\n"); fprintf(stderr, " --ansi ANSI colourized output\n"); fprintf(stderr, " --html HTML output\n"); } static void _print_tabs(FILE *file, int depth){ for(int i = 0; i < depth; i++)fputs(" ", file); } static void _print_newline(FILE *file, int depth){ fputc('\n', file); _print_tabs(file, depth); } static void _print_token_start(FILE *file, format_t format, fus_lexer_token_type_t token_type ){ int color = lexer_token_type_colors[token_type]; switch(format){ case FORMAT_ANSI: fputs(ansi_colors[color], file); break; case FORMAT_HTML: fprintf(file, "", color); break; default: break; } } static void _print_token_end(FILE *file, format_t format, fus_lexer_token_type_t token_type ){ int color = lexer_token_type_colors[token_type]; switch(format){ case FORMAT_ANSI: fputs(ansi_reset, file); break; case FORMAT_HTML: fputs("", file); break; default: break; } } static int mainloop_raw_tokens(fus_lexer_t *lexer, FILE *file){ int err; int depth = 0; while(!fus_lexer_done(lexer)){ const char *whitespace = &lexer->text[lexer->whitespace_pos]; int whitespace_len = lexer->whitespace_len; /* lexer->token_start is the position after any macros processed during the last call to fus_lexer_next */ int token_start = use_vars? lexer->token_start: lexer->whitespace_pos + whitespace_len; const char *token = &lexer->text[token_start]; int token_len = lexer->pos - token_start; if(use_vars && lexer->is_macro_token){ /* If lexer->token was generated by a macro (e.g. $GET_STR X), then use it instead of a substring of lexer->text (e.g. if the text was "$GET_STR X", we want to use the value of the variable, not the text "$GET_STR X" or "X" or whatever). */ token = lexer->token; token_len = lexer->token_len; } if(whitespace_len > 0){ _print_token_start(file, format, TOKEN_TYPE_WHITESPACE); fprintf(file, "%.*s", whitespace_len, whitespace); _print_token_end(file, format, TOKEN_TYPE_WHITESPACE); } if(token_len > 0){ _print_token_start(file, format, lexer->token_type); fprintf(file, "%.*s", token_len, token); _print_token_end(file, format, lexer->token_type); } err = fus_lexer_next(lexer); if(err)return err; } return 0; } static int mainloop(fus_lexer_t *lexer, FILE *file){ int err; int depth = 0; while(!fus_lexer_done(lexer)){ int print_depth = depth; const char *token = NULL; int token_len = 0; switch(lexer->token_type){ case FUS_LEXER_TOKEN_INT: case FUS_LEXER_TOKEN_SYM: case FUS_LEXER_TOKEN_OP: token = lexer->token; token_len = lexer->token_len; break; case FUS_LEXER_TOKEN_STR: token = lexer->token + 1; token_len = lexer->token_len - 2; break; case FUS_LEXER_TOKEN_BLOCKSTR: token = lexer->token + 2; token_len = lexer->token_len - 2; break; case FUS_LEXER_TOKEN_OPEN: token = ":"; token_len = 1; depth++; break; case FUS_LEXER_TOKEN_CLOSE: depth--; print_depth--; break; default: fprintf(stderr, "Unrecognized token type: %i\n", lexer->token_type); return 2; } if(token){ _print_tabs(file, print_depth); _print_token_start(file, format, lexer->token_type); if( lexer->token_type == FUS_LEXER_TOKEN_STR || lexer->token_type == FUS_LEXER_TOKEN_BLOCKSTR ){ fus_nwrite_str(file, token, token_len); }else{ fprintf(file, "%.*s", token_len, token); } _print_token_end(file, format, lexer->token_type); fputc('\n', file); } err = fus_lexer_next(lexer); if(err)return err; } return 0; } int main(int n_args, char **args){ int err; for(int i = 1; i < n_args; i++){ const char *arg = args[i]; if(!strcmp(arg, "-h") || !strcmp(arg, "--help")){ print_help(); return 0; }else if(!strcmp(arg, "-v") || !strcmp(arg, "--vars")){ use_vars = true; }else if(!strcmp(arg, "-r") || !strcmp(arg, "--raw")){ raw_tokens = true; }else if(!strcmp(arg, "--ansi")){ format = FORMAT_ANSI; }else if(!strcmp(arg, "--html")){ format = FORMAT_HTML; }else{ fprintf(stderr, "Unrecognized option: %s\n", arg); print_help(); return 2; } } char *buffer = read_stream(stdin, ""); if(!buffer)return 1; fus_lexer_t lexer; if(use_vars){ err = fus_lexer_init_with_vars(&lexer, buffer, "", NULL); if(err)return err; }else{ err = fus_lexer_init(&lexer, buffer, ""); if(err)return err; } if(raw_tokens){ err = mainloop_raw_tokens(&lexer, stdout); if(err)return err; }else{ err = mainloop(&lexer, stdout); if(err)return err; } fus_lexer_cleanup(&lexer); return 0; }