sapp.y
or Sappy is a macro preprocessor defined in one file, but IT DOES NOT WORK. I have not tested it. I stopped working on it. I am just posting it to show to someone!
Created
October 20, 2023 17:37
-
-
Save Chubek/3f1a2c13353b5484f307d97da1459e5e to your computer and use it in GitHub Desktop.
Sappy: A Macro Preprocessor Prototype
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
%{ | |
#define _POSIX_C_SOURCE 2 | |
#define _X_OPEN_SOURCE 500 | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <stdint.h> | |
#include <limits.h> | |
#include <string.h> | |
#include <regex.h> | |
#include <time.h> | |
#include <stdbool.h> | |
#include <sysexits.h> | |
#include <getopt.h> | |
#include <signal.h> | |
#include <readline/readline.h> | |
#include <readline/history.h> | |
#ifndef INNER_SYMTBL_MAX | |
#define INNER_SYMTBL_MAX 16 | |
#endif | |
#define LOCAL static | |
#define INlINE static inline | |
LOCAL uin8t_t *SYMTBL[UINT16_MAX + 1][INNER_SYMTBL_MAX]; | |
INLINE blank_symtbl(void) | |
{ memset(SYMTBL, 0, sizeof(SYMTBL); } | |
INLINE uint16_t djb2(uint8_t *id) | |
{ | |
uint16_t hash = 0; | |
uint8_t chr; | |
while ((chr = *id++)) | |
hash = (hash * 33) + chr; | |
return hash; | |
} | |
INLINE uint8_t *upsert(uint8_t *id, uint8_t *val) | |
{ | |
uint16_t hash = djb2(id); | |
uint8_t inhash = hash % (INNER_SYMTBL_MAX); | |
if (!val) | |
return SYMTBL[hash - 1][inhash - 1]; | |
else | |
SYMTBL[hash - 1][inhash - 1] = strndup(val, strlen(val)); | |
return NULL: | |
} | |
INLINE void remove(uint8_t *id) | |
{ | |
uint8_t **id = &upsert(id, NULL); | |
free(id); | |
} | |
INLINE void dump_symtbl(void) | |
{ | |
uint32_t l = UINT16_MAX + 1; | |
while (--l) | |
{ | |
uint16_t ll = INNER_SYMTBL_MAX; | |
while (--ll) | |
(SYMTBL[l][ll] ? free(SYMTBL[l]) : NULL); | |
} | |
} | |
LOCAL size_t STACKLEN = 4096; | |
LOCAL uint8_t *DEFN_STACK[STACKLEN]; | |
LOCAL size_t SP = 0; | |
INLINE void push_stack(uint8_t *defn) | |
{ DEFN_STACK[SP++] = strdup(defn); SP >= STACKLEN ? (SP = 0) : NULL; } | |
INLINE uint8_t* pop_stack(void) { return DEFN_STACK[--SP]; } | |
INLINE void dump_stack(void) { while (SP) free(pop_stack()); } | |
LOCAL uint8_t *OUTPATH; | |
LOCAL FILE *OUTPUT, PUTPUT; | |
LOCAL FILE *PIPE; | |
LOCAL FILE *DIVERT_STREAM[NUM_DIVERT]; | |
LOCAL FILE *NULL_STREAM; | |
LOCAL uint8_t *DIVERT_STRING[NUM_DIVERT]; | |
LOCAL size_t DIVERT_LENGTHS[NUM_DIVERT]; | |
LOCAL int LAST_EXIT_CODE; | |
LOCAL uint8_t *LAST_EXEC_RES; | |
LOCAL uint8_t **INVOKE_ARGS; | |
LOCAL size_t SSP = 0; | |
LOCAL size_t STATE_STACKLEN = 1024; | |
LOCAL struct LexState | |
{ | |
uint8_t *macro_prefix; | |
uint8_t *macro_suffix; | |
uint8_t *macro_leftbrack; | |
uint8_t *macro_rightbrack; | |
uint8_t *macro_separator; | |
uint8_t *macro_finisher; | |
uint8_t *macro_argnumprefix; | |
uint8_t *macro_argexpansion; | |
uint8_t *macro_delimexpansion; | |
uint8_t *macro_expansiondelim; | |
uint8_t *macro_name; | |
uint8_t *comment_start; | |
uint8_t *comment_end; | |
uint8_t *quote_start; | |
uint8_t *quote_end; | |
uint8_t *escape_mark; | |
uint8_t *define_mark; | |
uint8_t *undefine_mark; | |
uint8_t *push_mark; | |
uint8_t *pop_mark; | |
uint8_t *eval_mark; | |
uint8_t *exec_mark; | |
uint8_t *echo_mark; | |
uint8_t *mode_mark; | |
uint8_t *divert_mark; | |
uint8_t *aux_mark; | |
uint8_t *if_mark; | |
uint8_t *else_mark; | |
uint8_t *param_mark; | |
uint8_t *reflect_mark; | |
uint8_t *repeat_mark; | |
uint8_t *delim_begin; | |
uint8_t *delim_end; | |
uint8_t *parse_delim; | |
} | |
LEX_STATE, *STATE_STACK[STATE_STACKLEN]; | |
INLINE void push_state(void) | |
{ | |
STATE_STACK[SSP] = calloc(sizeof(struct LexState)); | |
memmove(&STATE_STACK[SSP++], &LEX_STATE, sizeof(struct LexState)); | |
} | |
INLINE void set_state(void) | |
{ LEX_STATE = *STATE_STACK[--SSP]; } | |
#define MACRO_PREFIX "%MPX" | |
#define MACRO_SUFFIX "%MSX" | |
#define MACRO_LEFTBRACK "%LBR" | |
#define MACRO_RIGHTBRACK "%RBR" | |
#define MACRO_SEPARATOR "%SEP" | |
#define MACRO_FINISHER "%FIN" | |
#define MACRO_ARGNUMPREFIX "%NARG" | |
#define MACRO_ARGEXPANSION "%XARG" | |
#define MACRO_DELIMEXPANSION "%DARG" | |
#define MACRO_EXPANSIONDELIM "%XDLIM" | |
#define MACRO_NAME "%MNAME" | |
#define COMMENT_START "%CMS" | |
#define COMMENT_END "%CME" | |
#define QUOTE_START "%QS" | |
#define QUOTE_END "%QE" | |
#define DEFINE_MARK "%DEF" | |
#define UNDEFINE_MARK "%UNDEF" | |
#define PUSH_MARK "%PUSH" | |
#define POP_MARK "%POP" | |
#define MODE_MARK "%MODE" | |
#define AUX_MARK "%RUN" | |
#define ESCAPE_MARK "%ESC" | |
#define ECHO_MARK "%ECHO" | |
#define EVAL_MARK "%EVAL" | |
#define EXEC_MARK "%EXEC" | |
#define DIVERT_MARK "%DIVR" | |
#define REPEAT_MARK "%REPT"1 | |
#define IF_MARK "%IF" | |
#define ELSE_MARK "%ELSE" | |
#define PARAM_MARK "%PARAM" | |
#define REFLECT_MARK "%REFL" | |
#define DELIM_BEGIN "%DBEG" | |
#define DELIM_END "%DEND" | |
#define PARSE_DELIM "%PDLIM" | |
#define MAX_TOKEN_LEN 32 | |
#define MAX_FMT_LEN 8 | |
LOCAL void config_state(char *token, uint8_t *set, bool f) | |
{ | |
LOCAL uint8_t **refarr = { | |
&LEX_STATE.macro_prefix, MACRO_PREFIX, | |
&LEX_STATE.macro_suffix, MACRO_SUFFIX, | |
&LEX_STATE.macro_leftbrack, MACRO_LEFTBRACK, | |
&LEX_STATE.macro_rightbrack, MACRO_RIGHTBRACK, | |
&LEX_STATE.macro_separator, MACRO_SEPARATOR, | |
&LEX_STATE.macro_finisher, MACRO_FINISHER, | |
&LEX_STATE.macro_argexpansion, MACRO_ARGEXPANSION, | |
&LEX_STATE.macro_expansiondelim, MACRO_EXPANSIONDELIM, | |
&LEX_STATE.macro_argnumprefix, MACRO_ARGNUMPREFIX, | |
&LEX_STATE.macro_delimexpansion, MACRO_DELIMEXPANSION, | |
&LEX_STATE.macro_name, MACRO_NAME, | |
&LEX_STATE.comment_start, COMMENT_START, | |
&LEX_STATE.comment_end, COMMENT_END, | |
&LEX_STATE.quote_start, QUOTE_START, | |
&LEX_STATE.quote_end, QUOTE_END, | |
&LEX_STATE.escape_mark, ESCAPE_MARK, | |
&LEX_STATE.echo_mark, ECHO_MARK, | |
&LEX_STATE.define_mark, DEFINE_MARK, | |
&LEX_STATE.undefine_mark, UNDEFINE_MARK, | |
&LEX_STATE.push_mark, PUSH_MARK, | |
&LEX_STATE.pop_mark, POP_MARK, | |
&LEX_STATE.mode_mark, MODE_MARK, | |
&LEX_STATE.aux_mark, AUX_MARK, | |
&LEX_STATE.eval_mark, EVAL_MARK, | |
&LEX_STATE.exec_mark, EXEC_MARK, | |
&LEX_STATE.reflect_mark, REFLECT_MARK, | |
&LEX_STATE.repeat_mark, REPEAT_MARK, | |
&LEX_STATE.divert_mark, DIVERT_MARK, | |
&LEX_STATE.if_mark, IF_MARK, | |
&LEX_STATE.param_mark, PARAM_MARK, | |
&LEX_STATE.else_mark, ELSE_MARK, | |
&LEX_STATE.delim_begin, DELIM_BEGIN, | |
&LEX_STATE.delim_end, DELIM_END, | |
&LEX_STATE.parse_delim, PARSE_DELIM, | |
NULL, NULL, | |
}; | |
uint8_t ***refptr = &refarr; | |
uint8_t **ref, *fmt; | |
for (ref = *refptr, fmt = *refptr++; | |
ref; | |
ref = *refptr++, fmt = *refptr++) | |
{ | |
if (!strncmp(fmt, token, MAX_FMT_LEN)) | |
{ | |
f ? free(*ref) : NULL; | |
*ref = set ? strndup(set, MAX_TOKEN_LEN) : NULL; | |
return; | |
} | |
} | |
} | |
INLINE void exit_run(int reason) | |
{ exit(reason); } | |
INLINE void exec_system(uint8_t *cmd) | |
{ LAST_EXIT_CODE = system(cmd); } | |
LOCAL void exec_pipe(uint8_t *cmd) | |
{ | |
PIPE = popen(cmd, "r"); | |
size_t len; | |
fseek(PIPE, SEEK_END); | |
len = ftell(PIPE); | |
rewind(PIPE); | |
LAST_EXEC_RES = calloc(len, 1); | |
fgets(&LAST_EXEC_RES[0], len, PIPE); | |
fputs(LAST_EXEC_RES, OUTPUT); | |
free(LAST_EXEC_RES); | |
pclose(PIPE); | |
} | |
INLINE void divert(int num) | |
{ | |
if (num < 0) { PUTPUT = OUTPUT; OUTPUT = NULL_STREAM; } | |
else { PUTPUT = OUTPUT; OUTPUT = DIVERT_STREAM[num]; } | |
} | |
INLINE void undivert(int num) | |
{ | |
OUTPUT = PUTPUT; | |
if (num > 0) | |
{ fputs(DIVERT_STRING[num], OUTPUT); } | |
} | |
INLINE void open_diverts(void) | |
{ for (int i = NUM_DIVERTS - 1; i; --i) | |
DIVERT_STREAM[i] = | |
open_memstream( | |
&DIVERT_STING[i], | |
&DIVERT_LENGTHS[i]); } | |
INLINE void free_diverts(void) | |
{ for (int i = NUM_DIVERTS - 1; i; --i) | |
{ free(DIVERT_STRING[i]); fclose(DIVERT_STREAM[i]); } | |
} | |
INLINE void add_invoke_arg(uint8_t *arg) | |
{ static int ninvoke = 0; INVOKE_ARGS[ninvoke++] = strdup(arg); } | |
INLINE void free_invoke_args(void) | |
{ uint8_t *ia; while ((ia = *INVOKE_ARGS++)) free(ia); } | |
INLINE void print_fmt(uint8_t *fmt) | |
{ uint8_t c; while ((c = *fmt++)) { | |
if (c == '%' && *fmt != '%') | |
{ | |
uint8_t fmt2[3] = {'%', c, 0}; | |
uint8_t *arg = *INVOKE_ARGS++; | |
fprintf(OUTPUT, &fmt2[0], (uintptr_t)arg); | |
} | |
} | |
INLINE void translit(uint8_t *text, uin8t_t *map, uint8_t *mapto) | |
{ | |
size_t i = 0; | |
uint8_t chr = 0; | |
size_t maplen = strlen(map); | |
size_t mtolen = strlen(mapto); | |
if (!maplen || !mtolen) | |
{ | |
fprintf(stderr, "Translit needs two arguments"); | |
exit(EXIT_FAILURE); | |
} | |
uint8_t trntable[256]; | |
while ((chr = *map++)) | |
trntable[chr] = *mapto++; | |
while ((chr = *text++)) | |
fprintf(OUTPUT, "%c", trntable[chr]); | |
} | |
INLINE void index(uint8_t *text, uint8_t *sub) | |
{ | |
fprintf(OUTPUT, "%lu", ((size_t)strstr(text, sub) - ((size_t)text))); | |
} | |
INLINE void include_file(uint8_t *path) | |
{ | |
FILE *s = fopen(path, "r"); | |
if (!s) | |
{ | |
ferr: | |
perror("fopen"); | |
exit(EX_IOERR); | |
} | |
fseek(s, -1, SEEK_END) < 0 ? goto ferr : NULL; | |
long len = ftell(s); | |
if (len < 0) | |
goto ferr; | |
rewind(s); | |
uint8_t *txt = calloc(len, 1); | |
if (!txt) | |
{ | |
fpritnf(stderr, "Memory error\n"); | |
exit(EXIT_FAILURE); | |
} | |
fread(&txt[0], len, 1, s); | |
fputs(&txt[0], s); | |
free(txt); | |
} | |
LOCAL uint16_t CURR_PATH_HASH = 0; | |
LOCAL FILE *CURR_FILE; | |
LOCAL uint8_t *CURR_LINE; | |
LOCAL size_t CURR_LEN; | |
INLINE void get_line(uint8_t *path, uint8_t *delim) | |
{ | |
uint16_t hash = djb2(path); | |
if (CURR_PATH_HASH == hash) | |
{ | |
getln: | |
!delim | |
? getline(&CURR_LINE, &CURR_LEN, CURR_FILE) | |
: getdelim(&CURR_LINE, &CURR_LEN, delim, CURR_FILE); | |
goto println; | |
} | |
else | |
{ | |
CURR_PATH_HASH = hash; | |
CURR_FILE = fopen(path, "r"); | |
if (!CURR_FILE) | |
{ | |
perror("fopen"); | |
exit(EX_IOERR); | |
} | |
goto getln; | |
} | |
println: | |
fputs(CURR_LINE, CURR_FILE); | |
free(CURR_LINE); | |
} | |
INLINE void close_file(void) | |
{ fclose(CURR_FILE); } | |
INLINE void print_date(uint8_t *fmt) | |
{ | |
struct tm tmp; | |
time_t time; | |
t = time(NULL); | |
tmp = localtime(&t); | |
if (!tmp) | |
{ | |
perror("localtime"); | |
exit(EX_TEMPFAIL); | |
} | |
char out[512]; | |
if (strftime(out, 512, fmt, tmp) == 0) | |
{ | |
perror("strftime"); | |
exit(EX_TEMPFAIL); | |
} | |
fputs(&out[0], OUTPUT); | |
} | |
LOCAL regex_t RE_CC; | |
LOCAL regmatch_t PMATCH[1]; | |
INLINE void yyexpreval(uint8_t*); | |
typedef void (*callback_t)(uint8_t*); | |
LOCAL void matches_act_if(uint8_t *pattern, | |
uint8_t *str, uint8_t *main, | |
uint8_t *instead, callback_t cback) | |
{ | |
if (regcomp(&RE_CC, pattern, REG_NOSUB) < 0) | |
{ | |
perror("regex"); | |
exit(EX_TEMPFAIL); | |
} | |
if (!regexec(&RE_CC, str, 1, PMATCH, 0)) | |
{ | |
cback(main); | |
} | |
else if (ins) | |
{ | |
cback(instead); | |
} | |
regfree(&RE_CC); | |
} | |
LOCAL void unmatches_act_if(uint8_t *pattern, | |
uint8_t *str, uint8_t *main, | |
uint8_t *instead, callback_t cback) | |
{ | |
if (regcomp(&RE_CC, pattern, REG_NOSUB) < 0) | |
{ | |
perror("regex"); | |
exit(EX_TEMPFAIL); | |
} | |
if (regexec(&RE_CC, str, 1, PMATCH, 0)) | |
{ | |
cback(main); | |
} | |
else if (ins) | |
{ | |
cback(instead); | |
} | |
regfree(&RE_CC); | |
} | |
LOCAL void defined_act_if(uint8_t *id, uint8_t *cmd, | |
uint8_t *instead, callback_t cback) | |
{ | |
if (upsert(id, NULL)) | |
cback(str) | |
else if (ins) | |
cback(instead); | |
} | |
LOCAL void undefined_act_if(uint8_t *id, uint8_t *cmd, | |
uint8_t *instead, callback_t cback) | |
{ | |
if (!upsert(id, NULL)) | |
cback(str) | |
else if (ins) | |
cback(instead); | |
} | |
LOCAL void equals_act_if(uint8_t *id, uint8_t *eq, uint8_t *ifeq, | |
uint8_t *instead, callback_t cback) | |
{ | |
uint8_t *sym = upsert(id); | |
if (!strncmp(eq, sym, strlen(eq))) | |
cback(ifeq); | |
else if (instead) | |
cback(instead); | |
} | |
LOCAL void unequals_act_if(uint8_t *id, uint8_t *eq, uint8_t *ifeq, | |
uint8_t *instead, callback_t cback) | |
{ | |
uint8_t *sym = upsert(id); | |
if (strncmp(eq, sym, strlen(eq))) | |
cback(ifeq); | |
else if (instead) | |
cback(instead); | |
} | |
LOCAL void print_wrapper(uint8_t *str); | |
{ fputs(str, OUTPUT); } | |
INLINE void matches_print_if(uint8_t *pattern, | |
uint8_t *str, uint8_t *main, uint8_t *instead) | |
{ matches_act_if(pattern, str, main, instead, print_wrapper); } | |
INLINE void matches_exec_if(uint8_t *pattern, | |
uint8_t *str, uint8_t *main, uint8_t *instead) | |
{ matches_act_if(pattern, str, main, instead, exec_pipe); } | |
INLINE void matches_eval_if(uint8_t *pattern, | |
uint8_t *str, uint8_t *main, uint8_t *instead) | |
{ matches_act_if(pattern, str, main, instead, yyexpreval); } | |
INLINE void unmatches_print_if(uint8_t *pattern, | |
uint8_t *str, uint8_t *main, uint8_t *instead) | |
{ unmatches_act_if(pattern, str, main, instead, print_wrapper); } | |
INLINE void unmatches_exec_if(uint8_t *pattern, | |
uint8_t *str, uint8_t *main, uint8_t *instead) | |
{ unmatches_act_if(pattern, str, main, instead, exec_pipe); } | |
INLINE void unmatches_eval_if(uint8_t *pattern, | |
uint8_t *str, uint8_t *main, uint8_t *instead) | |
{ unmatches_act_if(pattern, str, main, instead, yyexpreval); } | |
INLINE void defined_print_if(uint8_t *id, uint8_t *txt, | |
uint8_t *instead) | |
{ defined_act_if(id, txt, instead, print_wrapper); } | |
INLINE void defined_exec_if(uint8_t *id, uint8_t *cmd, | |
uint8_t *instead) | |
{ defined_act_if(id, cmd, instead, exec_pipe); } | |
INLINE void defined_eval_if(uint8_t *id, uint8_t *expr, | |
uint8_t *instead) | |
{ defined_act_if(id, expr, instead, yyexpreval); } | |
INLINE void undefined_print_if(uint8_t *id, uint8_t *txt, | |
uint8_t *instead) | |
{ undefined_act_if(id, cmd, instead, print_wrapper); } | |
INLINE void undefined_exec_if(uint8_t *id, uint8_t *cmd, | |
uint8_t *instead) | |
{ undefined_act_if(id, txt, instead, exec_pipe); } | |
INLINE void undefined_eval_if(uint8_t *id, uint8_t *expr, | |
uint8_t *instead) | |
{ undefined_act_if(id, expr, instead, yyexpreval); } | |
INLINE void equals_print_if(uint8_t *id, uint8_t *txt, | |
uint8_t *instead) | |
{ equals_act_if(id, cmd, instead, print_wrapper); } | |
INLINE void equals_exec_if(uint8_t *id, uint8_t *cmd, | |
uint8_t *instead) | |
{ equals_act_if(id, txt, instead, exec_pipe); } | |
INLINE void equals_eval_if(uint8_t *id, uint8_t *expr, | |
uint8_t *instead) | |
{ equals_act_if(id, expr, instead, yyexpreval); } | |
INLINE void unequals_print_if(uint8_t *id, uint8_t *txt, | |
uint8_t *instead) | |
{ unequals_act_if(id, cmd, instead, print_wrapper); } | |
INLINE void unequals_exec_if(uint8_t *id, uint8_t *cmd, | |
uint8_t *instead) | |
{ unequals_act_if(id, txt, instead, exec_pipe); } | |
INLINE void unequals_eval_if(uint8_t *id, uint8_t *expr, | |
uint8_t *instead) | |
{ unequals_act_if(id, expr, instead, yyexpreval); } | |
INLINE void to_stderr(uint8_t *str) | |
{ | |
uint8_t *prefix = getenv("SAPP_STDERR_PREFIX"); | |
fputs(prefix, stderr); | |
fputs(str, stderr); | |
} | |
LOCAL uint8_t *DELIM_EXEC_CMD; | |
INLINE void set_delim_exec(uint8_t *cmd) | |
{ if (DELIM_EXEC_CMD) free(DELIM_EXEC_CMD); DELIM_EXEC_CMD = strdup(cmd); } | |
INLINE void delim_exec(uint8_t *input) | |
{ | |
LOCAL uint8_t *tmpdir = getenv("SAPP_TMP_DIR"); | |
if (!tmpdir) | |
tmpdir = P_tmpdir; | |
strncat(tmpdir, "/XXXXXXX_SAPP.input", 32); | |
if (mkstemp(tmpdir) < 0) | |
{ | |
perror("mkstemp"); | |
exit(EX_IOERR); | |
} | |
FILE *backup = stdin; | |
FILE *restdin = freopen(tmpdir, "rw", stdin); | |
fputs(input, restdin); | |
pipe_exec(DELIM_EXEC_CMD); | |
fclose(restdin); | |
stdin = backup; | |
} | |
LOCAL regex_t RE_IDENT; | |
LOCAL regex_t RE_STRING; | |
INLINE void compile_ident_re(uint8_t *pattern) | |
{ | |
if (regcomp(&RE_IDENT, pattern, REG_NOSUB) < 0) | |
{ | |
perror("regcomp"); | |
exit(EX_TEMPFAIL); | |
} | |
} | |
INLINE bool match_ident(uint8_t *string) | |
{ | |
return !regexec(&RE_IDENT, string, 1, NULL, 0); | |
} | |
INLINE void free_ident_re(void) | |
{ regfree(&RE_IDENT); } | |
INLINE void compile_string_re(uint8_t *pattern) | |
{ | |
if (regcomp(&RE_STRING, pattern, REG_NOSUB) < 0) | |
{ | |
perror("regcomp"); | |
exit(EX_TEMPFAIL); | |
} | |
} | |
INLINE bool match_string(uint8_t *string) | |
{ | |
return !regexec(&RE_STRING, string, 1, NULL, 0); | |
} | |
INLINE void free_ident_re(void) | |
{ regfree(&RE_STRING); } | |
LOCAL struct Expansion | |
{ | |
uint8_t *id; | |
uint8_t *argv[ARG_MAX]; | |
uint8_t *argv_joined; | |
uint8_t *argv_delim_joined; | |
size_t argc; | |
} | |
CURR_EXPANSION; | |
INLINE void yymacroeval(uint8_t *macro); | |
INLINE void free_expn(void) | |
{ | |
if (!CURR_EXPANSION.id) return; | |
while (--CURR_EXPANSION.argc) | |
{ free(CURR_EXPANSION.argv[CURR_EXPANSION.argc]);} | |
free(CURR_EXPANSION.argv_joined); | |
free(CURR_EXPANSION.argv_delim_joined); | |
free(CURR_EXPANSION.id); | |
} | |
INLINE void blank_out_expn(void) | |
{ memset(&CURRENT_EXPANSION, 0, sizeof(struct Expansion)); } | |
INLINE void set_expn_ident(uint8_t *ident) | |
{ CURR_EXPANSION.id = strdup(ident); } | |
INLINE void add_expn_arg(uint8_t *arg) | |
{ CURR_EXPANSION.argv[CURR_EXPANSION.argc++] = strdup(arg); } | |
INLINE void eval_expn(void) | |
{ | |
uint8_t *macro = upsert(CURR_EXPANSION.id); | |
yymacroeval(macro); | |
} | |
INLINE void print_expn_id(void) | |
{ fputs(CURR_EXPANSION.id, OUTPUT); } | |
INLINE void join_argv(void) | |
{ | |
if (CURR_EXPANSION.argv_joined) return; | |
uint8_t *argvcpy = CURR_EXPANSION.argv, *arg; | |
while ((*arg = *argvcpy++)) | |
strncat(CURR_EXPANSION.argv_joined, arg, strlen(arg)); | |
} | |
INLINE void join_argv_delim(void) | |
{ | |
if (CURR_EXPANSION.argv_joined_delim) return; | |
uint8_t *argvcpy = CURR_EXPANSION.argv, *arg; | |
while ((*arg = *argvcpy++)) | |
{ | |
strncat(argv_joined, arg, strlen(arg)); | |
strncat( | |
CURR_EXPANSION.argv_joined_delim, | |
LEX_STATE.expansiondelim, | |
MAX_TOKEN_LEN); | |
} | |
} | |
INLINE void set_environ(uint8_t *key, uint8_t *val) | |
{ setenv(key, val, false); } | |
LOCAL yyreflect(uint8_t *code); | |
LOCAL yyordinaryeval(void); | |
LOCAL void exit_action(void) | |
{ | |
dump_symtbl(); close_file(); | |
free_expn(); free_ident_re(); | |
close_file(); dump_stack(); | |
free_invoke_args(); set_delim_exec(NULL); | |
} | |
void signal_end(int signum) | |
{ if (signum == SIGINT) exit_action(); } | |
#define YY_ENGAGE_EXPR 1 | |
#define YY_ENGAGE_MACRO 2 | |
#define YY_ENGAGE_ORD 3 | |
#define YY_ENGAGE_REFLECT 4 | |
#define YY_ENGAGE_PREFLECT 5 | |
LOCAL int yyengage = YY_ENGAGE_ORD; | |
LOCAL char **CMDL_ARGV; | |
LOCAL int CMDL_ARGC; | |
INLINE print_argv_idx(int idx) | |
{ if (idx < CMDL_ARGC) fputs(CMDL_ARGV[idx], OUTPUT); } | |
INLINE print_environ_name(uint8_t *name) | |
{ fputs(getenv(name), OUTPUT); } | |
LOCAL repl(void) | |
{ char *l = readline(NULL); yyreflect(l); free(l); } | |
LOCAL yyparamreflect(uint8_t *code, uint8_t *param) | |
LOCAL void reflect_foreach(uint8_t *code) | |
{ | |
for (uint8_t *iarg = *INVOKE_ARGS; iarg; iarg = *(++INVOKE_ARGS)) | |
{ | |
yyparamreflect(code, iarg); | |
} | |
} | |
extern char *optarg; | |
LOCAL FILE *INPUT_STREAM; | |
LOCAL uint8_t *INPUT_PATH; | |
LOCAL uint8_t *OUTPUT_PATH; | |
LOCAL uint8_t *YY_PARAM; | |
LOCAL void parse_cmdl_args(void) | |
{ | |
} | |
LOCAL void initialize(void) | |
{ | |
signal(SIGINT, signal_end); | |
atexit(exit_action); | |
} | |
%} | |
%union { | |
uint8_t *string; | |
int64_t integer; | |
double floatnum; | |
} | |
%token MACRO_FINISHER | |
%token MACRO_ARGNUMPREFIX | |
%token MACRO_ARGEXPANSION | |
%token MACRO_DELIMEXPANSION | |
%token MACRO_EXPANSIONDELIM | |
%token MACRO_NAME | |
%token COMMENT_START | |
%token COMMENT_END | |
%token QUOTE_START | |
%token QUOTE_END | |
%token ESCAPE_MARK | |
%token DEFINE_MARK | |
%token UNDEFINE_MARK | |
%token PUSH_MARK | |
%token POP_MARK | |
%token EVAL_MARK | |
%token EXEC_MARK | |
%token ECHO_MARK | |
%token MODE_MARK | |
%token DIVERT_MARK | |
%token AUX_MARK | |
%token IF_MARK | |
%token ELSE_MARK | |
%token PARAM_MARK | |
%token REFLECT_MARK | |
%token REPEAT_MARK | |
%token DELIM_BEGIN | |
%token DELIM_END | |
%token PARSE_DELIM | |
%token IDENT | |
%token STRING | |
%token YY_PARAM | |
%token DEC_INT | |
%token HEX_INT | |
%token OCT_INT | |
%token BIN_INT | |
%token FLOAT | |
%token SHIFT_LEFT | |
%token SHIFT_RIGHT | |
%token DIV_INT | |
%token INCR | |
%token DECR | |
%token POW | |
%token INTO | |
%token DUMP | |
%token DEFINED | |
%token UNDEFINED | |
%token MATCHES | |
%token EQUALS | |
%token UNEQUALS | |
%token SWITCH | |
%token PUSH | |
%token POP | |
%token INDEX | |
%token TRANSLIT | |
%token DATEFMT | |
%token DELIMCMD | |
%token SET | |
%token GETLINE | |
%token ENVIRON | |
%token DIVERT_PREFIX | |
%token DIVERT_NUM | |
%token DIVERT_NULL | |
%token LNDELIM | |
%token YYEND_EXPR YYEND_MACRO YYEND_REFLECT YYEND_PREFLECT YYEND_ORD | |
%left '+' '-' | |
%left '/' '*' DIV_INT POW | |
%left '%' | |
%right '^' | |
%right '&' | |
%right '|' | |
%% | |
%% | |
void yyreflect(uint8_t *code) | |
{ | |
INPUT_STREAM = fmemopen(code, strlen(code), "r"); | |
yyengage = YY_ENGAGE_REFLECT; | |
yyparse(); | |
} | |
void yyparamreflect(uint8_t *code, uint8_t *param) | |
{ | |
INPUT_STREAM = fmemopen(code, strlen(code), "r"); | |
YY_PARAM = param; | |
yyengage = YY_ENGAGE_PREFLECT; | |
yyparse(); | |
} | |
void yyexpreval(uint8_t *code) | |
{ | |
INPUT_STREAM = fmemopen(code, strlen(code), "r"); | |
yyengage = YY_ENGAGE_EXPR; | |
yyparse(); | |
} | |
void yymacroeval(uint8_t *code) | |
{ | |
INPUT_STREAM = fmemopen(code, strlen(code), "r"); | |
yyengage = YY_ENGAGE_MACRO; | |
yyparse(); | |
} | |
void yyordinaryeval(void) | |
{ | |
INPUT_STREAM = fmemopen(INPUT_PATH, "w"); | |
yyengage = YY_ENGAGE_ORD; | |
yyparse(); | |
} | |
/*!re2c | |
re2c:define:YYCTYPE = uint8_t; | |
re2c:yyfill:enable = 0; | |
*/ | |
int yylex(void) | |
{ | |
uint8_t token[MAX_LINE]; | |
size_t len; | |
if (getdelim(&token, &len, LEX_STATE.parse_delim, INPUT_STREAM) < 0) | |
{ | |
if (yyengage == YY_ENGAGE_EXPR) | |
return YYEND_EXPR; | |
else if (yyengage == YY_ENGAGE_MACRO) | |
return YYEND_MACRO; | |
else if (yyengage == YY_ENGAGE_PREFLECT) | |
return YYEND_PREFLECT; | |
else if (yyengage == YY_ENGAGE_REFLECT) | |
return YYEND_REFLECT; | |
else if (yyengage == YY_ENGAGE_ORD) | |
return YYEND_ORD; | |
} | |
uint8_t *YYCURSOR = token; | |
if (yyengage == YY_ENGAGE_EXPR) | |
{ | |
/*!re2c | |
[+-][1-9][0-9]* { return DEC_INT; } | |
[0xX][a-fA-F0-9]+ { return HEX_INT; } | |
[0oO][0-7]+ { return OCT_INT; } | |
[0bB][0-1]+ { return BIN_INT; } | |
[+-][0-9]*[.eE][0-9]+ { return FLOAT; } | |
"<<" { return SHIFT_LEFT; } | |
">>" { return SHIFT_RIGHT; } | |
"//" { return INT_DIV; } | |
"++" { return INCR; } | |
"--" { return DECR; } | |
"**" { return POW; } | |
*/ | |
} | |
else if (yyengage == YY_ENGAGE_PREFLECT) | |
{ | |
if (!strncmp(token, LEX_STATE.param_mark, MAX_TOKEN_LEN)) | |
return REFLECT_PARAM; | |
} | |
else if (yyengage == YY_ENGAGE_MACRO) | |
{ | |
if (!strncmp(token, LEX_STATE.macro_argnumprefix, MAX_TOKEN_LEN)) | |
return MACRO_ARGNUMPREFIX; | |
if (!strncmp(token, LEX_STATE.macro_argexpansion, MAX_TOKEN_LEN)) | |
return MACRO_ARGEXPANSION; | |
if (!strncmp(token, LEX_STATE.macro_delimexpansion, MAX_TOKLEN_LEN)) | |
return MACRO_DELIMEXPANSION; | |
if (!strncmp(token, LEX_STATE.macro_name, MAX_TOKEN_LEN)) | |
return MACRO_NAME; | |
} | |
if (!strncmp(token, LEX_STATE.macro_prefix, MAX_TOKEN_LEN)) | |
return MACRO_PREFIX; | |
if (!strncmp(token, LEX_STATE.macro_suffix, MAX_TOKEN_LEN)) | |
return MACRO_SUFFIX; | |
if (!strncmp(token, LEX_STATE.macro_leftbrack, MAX_TOKEN_LEN)) | |
return MACRO_LEFTBRACK; | |
if (!strncmp(token, LEX_STATE.macro_rightbrack, MAX_TOKEN_LEN)) | |
return MACRO_RIGHTBRACK; | |
if (!strncmp(token, LEX_STATE.macro_separator, MAX_TOKEN_LEN)) | |
return MACRO_SEPARATOR; | |
if (!strncmp(token, LEX_STATE.macro_finisher, MAX_TOKEN_LEN)) | |
return MACRO_FINISHER; | |
if (!strncmp(token, LEX_STATE.macro_name, MAX_TOKEN_LEN)) | |
return MACRO_NAME; | |
if (!strncmp(token, LEX_STATE.comment_start, MAX_TOKEN_LEN)) | |
return COMMENT_START; | |
if (!strncmp(token, LEX_STATE.comment_end, MAX_TOKEN_LEN)) | |
return COMMENT_END; | |
if (!strncmp(token, LEX_STATE.quote_start, MAX_TOKEN_LEN)) | |
return QUOTE_START; | |
if (!strncmp(token, LEX_STATE.quote_end, MAX_TOKEN_LEN)) | |
return QUOTE_END; | |
if (!strncmp(token, LEX_STATE.escape_mark, MAX_TOKEN_LEN)) | |
return ESCAPE_MARK; | |
if (!strncmp(token, LEX_STATE.define_mark, MAX_TOKEN_LEN)) | |
return DEFINE_MARK; | |
if (!strncmp(token, LEX_STATE.undefine_mark, MAX_TOKEN_LEN)) | |
return UNDEFINE_MARK; | |
if (!strncmp(token, LEX_STATE.push_mark, MAX_TOKEN_LEN)) | |
return PUSH_MARK; | |
if (!strncmp(token, LEX_STATE.pop_mark, MAX_TOKEN_LEN)) | |
return POP_MARK; | |
if (!strncmp(token, LEX_STATE.eval_mark, MAX_TOKEN_LEN)) | |
return EVAL_MARK; | |
if (!strncmp(token, LEX_STATE.exec_mark, MAX_TOKEN_LEN)) | |
return EXEC_MARK; | |
if (!strncmp(token, LEX_STATE.echo_mark, MAX_TOKEN_LEN)) | |
return ECHO_MARK; | |
if (!strncmp(token, LEX_STATE.mode_mark, MAX_TOKEN_LEN)) | |
return MODE_MARK; | |
if (!strncmp(token, LEX_STATE.divert_mark, MAX_TOKEN_LEN)) | |
return DIVERT_MARK; | |
if (!strncmp(token, LEX_STATE.aux_mark, MAX_TOKEN_LEN)) | |
return AUX_MARK; | |
if (!strncmp(token, LEX_STATE.if_mark, MAX_TOKEN_LEN)) | |
return IF_MARK; | |
if (!strncmp(token, LEX_STATE.else_mark, MAX_TOKEN_LEN)) | |
return ELSE_MARK; | |
if (!strncmp(token, LEX_STATE.reflect_mark, MAX_TOKEN_LEN)) | |
return REFLECT_MARK; | |
if (!strncmp(token, LEX_STATE.repeat_mark, MAX_TOKEN_LEN)) | |
return REPEAT_MARK; | |
if (!strncmp(token, LEX_STATE.delim_begin, MAX_TOKEN_LEN)) | |
return DELIM_BEGIN; | |
if (!strncmp(token, LEX_STATE.delim_end, MAX_TOKEN_LEN)) | |
return DELIM_END; | |
if (!strncmp(token, YY_PARAM, len)) | |
return YY_PARAM; | |
if (match_ident(token)) | |
return IDENT; | |
if (match_string(token)) | |
return STRING; | |
/*!re2c | |
"into" { return INTO; } | |
"dump" { return DUMP; } | |
"defined" { return DEFINED; } | |
"undefined" { return UNDIEFNED; } | |
"equals" { return EQUALS; } | |
"unequals" { return UNEQUALS; } | |
"matches" { return MATCHES; } | |
"unmatches" { return UNMAtCHES; } | |
"translit" { return TRANSLIT; } | |
"index" { return INDEX; } | |
"switch" { return SWITCH; } | |
"push" { return PUSH; } | |
"pop" { return POP; } | |
"set" { return SET; } | |
"datefmt" { return DATEFMT; } | |
"delimcmd" { return DELIMCMD; } | |
"environ" { return ENVIRON; } | |
"getline" { return GETLINE; } | |
"lndelim" { return LNDELIM; } | |
"DIVERT::" { return DIVERT_PREFIX; } | |
'[' [0-9] ']' { return DIVERT_NUM; } | |
"[NULL]" { return DIVERT_NULL; } | |
*/ | |
fputs(token, OUTPUT); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment