# Can we get meta5ix output in C? The one-pass nature of this # compiler prevents us from predeclaring things, but the remaining big # problem seems to be string syntax (backslashes, ''). # The Meta5ix string syntax is a real problem: no backslashing, but # some strings are in ''. Meta5ix is not quite powerful enough to # rewrite '"hi," he said' into "\"hi,\" he said". I thought I would # try to write a version that parsed C-style strings: # # :fnord <<'"' [:notin '"\\', :between "\\\\" :notin ""] '"'>> # # But the problem is that there’s no way to write a string containing # a doublequote as a doublequoted string in the Meta5ix string syntax. # So, although I can write the above, I have to make a different # version of this compiler *using* that syntax to be self-compiling. - program: preamble [comment] firstrule [rule, comment] epilogue - comment: "#" [:notin ""] - preamble: @{#include } @{#include } @{#include } {} @{static int ok = 1;} @{static int calls = 0;} @{static char outbuf[1024];} @{static char *outp = outbuf;} @{static char inbuf[16777216];} @{static char *inptr = inbuf;} @{static char *tok;} @{static int toklen;} {} @{static void literal(char *s, int n)} @{{} fnord {if ((ok = !memcmp(inptr, s, n))) inptr += n;} @{\}} {} @{static void die()} @{{} {fprintf(stderr, \"Parse failure at byte %d\\n\", (int)(inptr-inbuf));} {exit(1);} @{\}} {} @{static void writeline()} @{{} {fwrite(outbuf, 1, outp-outbuf, stdout);} {putchar('\\n');} {memset(outbuf, ' ', 8);} {outp = outbuf + 8;} @{\}} {} {/* hmm, maybe inline notin and between? */} @{static void notin(char *s)} @{{} {char c = *inptr;} {if ((ok = (c && c != '\\n' && !strchr(s, c)))) inptr++;} @{\}} {} @{static void between(char *s)} @{{} {char c = *inptr;} {if ((ok = (c && c != '\\n' && c >= s[0] && c <= s[1]))) inptr++;} @{\}} {} - fnord: {while (*inptr && strchr(\" \\n\\t\", *inptr)) inptr++;} - rule: "-" name @{int parse_$it()} @{{} function_prologue ":" terms {return 0;} @{\}} {} - function_prologue: {int me = calls++;} {(void)me; /* suppress unused warning */} - firstrule: "-" name @{void start_parsing()} @{{} {parse_$it();} @{\}} {} @{int parse_$it()} @{\{} function_prologue ":" terms {return 0;} @{\}} {} - epilogue: @{int main(int argc, char **argv)} @{{} {size_t n;} {size_t max = sizeof(inbuf) - 100; /* allow for literals */} {FILE *input = fopen(argv[1], \"r\");} {if (!input) {perror(argv[1]); return 1;\}} {} {n = fread(inbuf, 1, max, input);} {if (n == max) {} { fprintf(stderr, \"Input file %s too large\\n\", argv[1]);} { return 1;} {\}} {inbuf[n] = 0;} {if (ferror(input)) {} { perror(\"reading input file; maybe:\");} { return 1;} {\}} {} {start_parsing();} {return 0;} @{\}} - terms: term ["," {} {if (!ok) {} {ok = 1;} term {\}} ] - term: (factor, output) {if (ok) {} [factor {if (!ok) die();}, output] {\}} - factor: string {literal($it, strlen($it));} , "(" terms ")" , "[" {do {} terms {\} while (ok);} {ok = 1;} "]" , name {parse_$it();} , "<<" {tok = inptr;} terms ">>" {toklen = inptr - tok;} , ":fnord" fnord factor , ":notin" string {notin($it);} , ":between" string {between($it);} - output: (quasiquote, "@" {outp = outbuf;} (var, quasiquote)) {writeline();} - quasiquote: "{" [<> {strcpy(outp, \"$it\");} {outp += strlen(\"$it\");} , "$" var ] "}" - ch: :notin "$}\\" - var: "it" {memcpy(outp, tok, toklen);} {outp += toklen;} , name {outp += sprintf(outp, \"%s%d\", \"$it\", me);} - string: :fnord <> - quotes: "\"" - name: :fnord <> - letter: :between "az", :between "AZ", :between "__"