diff --git a/Makefile b/Makefile index 166b9c6..6d899b9 100644 --- a/Makefile +++ b/Makefile @@ -28,30 +28,38 @@ rwildcard=$(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2) \ $(filter $(subst *,%,$2),$d)) java_srcs := $(call rwildcard, $(src), *.java) -c_srcs := $(call rwildcard, $(src), *.c) +all_c_srcs := $(call rwildcard, $(src), *.c) +c_re_srcs := $(call rwildcard, $(src), *.re.c) +c_rec_srcs := $(call rwildcard, $(src), *.re_c.c) +c_gperf_srcs := $(call rwildcard, $(src), *.gperf.c) +c_srcs := $(filter-out $(c_re_srcs) $(c_rec_srcs) $(c_gperf_srcs), $(all_c_srcs)) h_srcs := $(call rwildcard, $(src), *.h) -c_re_srcs := $(call rwildcard, $(src), *.c.re) -c_rec_srcs := $(call rwildcard, $(src), *.c.re_c) y_srcs := $(call rwildcard, $(src), *.y) -c_tests := $(call rwildcard, $(test), *.c) +all_c_tests := $(call rwildcard, $(test), *.c) +c_re_tests := $(call rwildcard, $(test), *.re.c) +c_rec_tests := $(call rwildcard, $(test), *.re_c.c) +c_tests := $(filter-out $(c_re_tests) $(c_rec_tests), $(all_c_tests)) h_tests := $(call rwildcard, $(test), *.h) icons := $(call rwildcard, $(media), *.ico) # combinations all_h := $(h_srcs) $(h_tests) -all_srcs := $(java_srcs) $(c_srcs) $(c_re_srcs) $(c_rec_srcs) $(y_srcs) -all_tests := $(c_tests) +all_srcs := $(java_srcs) $(all_c_srcs) $(y_srcs) +all_tests := $(all_c_tests) all_icons := $(icons) java_class := $(patsubst $(src)/%.java, $(build)/%.class, $(java_srcs)) c_objs := $(patsubst $(src)/%.c, $(build)/%.o, $(c_srcs)) # must not conflict, eg, foo.c.re and foo.c would go to the same thing -c_re_builds := $(patsubst $(src)/%.c.re, $(build)/%.c, $(c_re_srcs)) -c_rec_builds := $(patsubst $(src)/%.c.re_c, $(build)/%.c, $(c_rec_srcs)) +c_re_builds := $(patsubst $(src)/%.re.c, $(build)/%.c, $(c_re_srcs)) +c_re_test_builds := $(patsubst $(test)/%.re.c, $(build)/$(test)/%.c, $(c_re_tests)) +c_rec_builds := $(patsubst $(src)/%.re_c.c, $(build)/%.c, $(c_rec_srcs)) +c_rec_test_builds := $(patsubst $(test)/%.re_c.c, $(build)/%.c, $(c_rec_tests)) c_y_builds := $(patsubst $(src)/%.y, $(build)/%.c, $(y_srcs)) -# together .re/.re_c/.y +c_gperf_builds := $(patsubst $(src)/%.gperf.c, $(build)/%.c, $(c_gperf_srcs)) +# together .re/.re_c/.y/.gperf.c c_other_objs := $(patsubst $(build)/%.c, $(build)/%.o, $(c_re_builds) \ -$(c_rec_builds) $(c_y_builds)) +$(c_rec_builds) $(c_re_test_builds) $(c_rec_test_builds) $(c_y_builds) $(c_gperf_builds)) test_c_objs := $(patsubst $(test)/%.c, $(build)/$(test)/%.o, $(c_tests)) html_docs := $(patsubst $(src)/%.c, $(doc)/%.html, $(c_srcs)) @@ -62,9 +70,10 @@ cat := cat zip := zip bison := bison #lemon := lemon +gperf := gperf target := # -mwindows -optimize := -ffast-math -funroll-loops -Ofast # -O3 -g +optimize := -ffast-math warnbasic := -Wall -pedantic -ansi # -std=c99 # Some stuff is really new. warnclang := -Wextra \ @@ -78,12 +87,13 @@ warnclang := -Wextra \ -Wno-shift-op-parentheses \ -Wno-empty-body \ -Wno-padded \ --Wdisabled-macro-expansion +-Wno-switch-enum \ +-Wno-missing-noreturn warn := $(warnbasic) $(warnclang) CC := clang # gcc CF := $(target) $(optimize) $(warn) -OF := -Ofast # -O3 -framework OpenGL -framework GLUT or -lglut -lGLEW +OF := # -lm -framework OpenGL -framework GLUT or -lglut -lGLEW # Jakob Borg and Eldar Abusalimov # $(ARGS) is all the extra arguments; $(BRGS) is_all_the_extra_arguments @@ -97,6 +107,12 @@ ifeq (backup, $(firstword $(MAKECMDGOALS))) endif $(eval $(ARGS):;@:) endif +ifeq (release, $(firstword $(MAKECMDGOALS))) + CF += -funroll-loops -Ofast -D NDEBUG # -O3 + OF += -Ofast +else + CF += -g +endif ###### # compiles the programme by default @@ -133,14 +149,28 @@ $(test_c_objs): $(build)/$(test)/%.o: $(test)/%.c $(all_h) @$(mkdir) $(build)/$(test) $(CC) $(CF) -c -o $@ $< -$(c_re_builds): $(build)/%: $(src)/%.re - # *.re build rule +# -8 made my file 32767 lines or longer + +$(c_re_builds): $(build)/%.c: $(src)/%.re.c + # *.re.c build rule @$(mkdir) $(build) $(re2c) -W -T -o $@ $< -$(c_rec_builds): $(build)/%: $(src)/%.re_c - # *.re_c (conditions) build rule +$(c_re_test_builds): $(build)/$(test)/%.c: $(test)/%.re.c + # *.re.c tests rule @$(mkdir) $(build) + @$(mkdir) $(build)/$(test) + $(re2c) -W -T -o $@ $< + +$(c_rec_builds): $(build)/%.c: $(src)/%.re_c.c + # *.re_c.c (conditions) build rule + @$(mkdir) $(build) + $(re2c) -W -T -c -o $@ $< + +$(c_rec_test_builds): $(build)/$(test)/%.c: $(test)/%.re_c.c + # *.re_c.c (conditions) tests rule + @$(mkdir) $(build) + @$(mkdir) $(build)/$(test) $(re2c) -W -T -c -o $@ $< $(c_y_builds): $(build)/%.c: $(src)/%.y # $(lemon)/$(bin)/$(lem) @@ -148,6 +178,11 @@ $(c_y_builds): $(build)/%.c: $(src)/%.y # $(lemon)/$(bin)/$(lem) @$(mkdir) $(build) $(bison) -o $@ $< +$(c_gperf_builds): $(build)/%.c: $(src)/%.gperf.c + # *.gperf.c build rule + @$(mkdir) $(build) + $(gperf) $@ --output-file $< + $(html_docs): $(doc)/%.html: $(src)/%.c $(src)/%.h # docs rule @$(mkdir) $(doc) @@ -156,7 +191,7 @@ $(html_docs): $(doc)/%.html: $(src)/%.c $(src)/%.h ###### # phoney targets -.PHONY: setup clean backup icon install uninstall test docs +.PHONY: setup clean backup icon install uninstall test docs release clean: -rm -f $(c_objs) $(test_c_objs) $(c_other_objs) $(c_re_builds) \ @@ -185,7 +220,12 @@ setup: default icon # or zip $(BDIR)/$(INST)-Win32.zip -r $(BDIR)/$(INST) rm -R $(bin)/$(install) -install: default +# this needs work +release: clean default + strip $(bin)/$(project) + # define NDEBUG + +install: release @$(mkdir) -p $(DESTDIR)$(PREFIX)/bin cp $(bin)/$(project) $(DESTDIR)$(PREFIX)/bin/$(project) diff --git a/src/interpret.c.re b/src/interpret.c.re deleted file mode 100644 index 477a55c..0000000 --- a/src/interpret.c.re +++ /dev/null @@ -1,98 +0,0 @@ -/** @license 2022 Neil Edelman, distributed under the terms of the - [MIT License](https://opensource.org/licenses/MIT). - - Lexer for journal entries. - - @std C89/90 */ - -#include -#include -#include -#include -#include - -static int parse_uint(const char *s, const char *e, unsigned *u) { - uint32_t n = 0; - for ( ; s < e; ++s) { - unsigned digit = (unsigned)(*s - '0'); - assert(digit < 10); - if(n > (UINT_MAX - digit) / 10) return errno = ERANGE, 0; /* check */ - n = n * 10 + digit; - } - *u = n; - return 1; -} - -static int parse_double(const char *s0, const char *s1, double *d) { - char *finish; - assert(d && s0 && s0 < s1); - *d = strtod(s0, &finish); - return !errno && ((finish == s1) || (errno = EDOM, 0)); -} - -static int lex_line(const char *YYCURSOR, unsigned *const u) { - const char *YYMARKER, *o1, *o2, *o3, *o4; - double d; - /*!stags:re2c format = 'const char *@@;\n'; */ - /*!re2c - re2c:yyfill:enable = 0; - re2c:flags:tags = 1; - re2c:define:YYCTYPE = char; - - keyword = [a-z][a-z0-9_-]{0,64}; // uppercase is lame - octet = [0-9] | [1-9][0-9] | [1][0-9][0-9] | [2][0-4][0-9] | [2][5][0-5]; - dot = [.]; - end = [\x00]; - - eol = [\n]; - w = [ \t]*; - double = [0-9]* "." [0-9]+ | [0-9]+; - */ - errno = 0; /* For accuracy detecting errors. */ -text: - /*!re2c - "\\[" | "\\![" { goto text; } - "[" { goto custom; } - "![" { goto image; } - @o1 octet dot @o2 octet dot @o3 octet dot @o4 octet end { - unsigned u1, u2, u3, u4; - if(!parse_uint(o1, o2 - 1, &u1) - || !parse_uint(o2, o3 - 1, &u2) - || !parse_uint(o3, o4 - 1, &u3) - || !parse_uint(o4, YYCURSOR - 1, &u4)) return 0; - *u = u4 + (u3 << 8) + (u2 << 16) + (u1 << 24); - return 1; - } - * { return errno = EILSEQ, 0; } - */ -image: - /*!re2c - "osm" "](" @o1 double @o2 "," @o3 double @o4 ")" { - printf("Got a map.\n"); - if(!parse_double(o1, o2, &d)) return 0; - printf("Latitude %f.\n", d); - if(!parse_double(o3, o4, &d)) return 0; - printf("Longitude %f.\n", d); - goto text; - } - * { return errno = EILSEQ, 0; } - */ -custom: - /*!re2c - glider = "glider" ":" ; - flight = "flight" ":" ; - * { return errno = EILSEQ, 0; } - */ -} - - -int main(int argc, char **argv) { - unsigned u; - if(!lex_line("1.2.3.4", &u)) goto catch; - printf("0x%x\n", u); - if(!lex_line("1.2.3.49999999999999999", &u)) goto catch; - return EXIT_SUCCESS; -catch: - perror("interpret"); - return EXIT_FAILURE; -} diff --git a/src/interpret.re_c.c b/src/interpret.re_c.c new file mode 100644 index 0000000..c1479a5 --- /dev/null +++ b/src/interpret.re_c.c @@ -0,0 +1,119 @@ +/** @license 2022 Neil Edelman, distributed under the terms of the + [MIT License](https://opensource.org/licenses/MIT). + + Lexer for journal entries. + + @std C89/90 */ + +#include +#include +#include +#include +#include + +/* This defines `enum condition`. */ +/*!types:re2c*/ +enum symbol { END, TEXT, BANG, WHITE, MAP }; + +/** scanner reads a file and extracts semantic information. Valid to access + only while underlying pointers do not change. */ +struct scanner { + /* `re2c` variables; these point directly into `buffer`. */ + const char *marker, *ctx_marker, *from, *cursor; + /* Weird `c2re` stuff: these fields have to come after when >5? */ + const char *label, *buffer, *s0, *s1; + enum condition condition; + enum symbol symbol; + size_t line; + int ws_before; +}; + +/*!re2c +re2c:yyfill:enable = 0; +re2c:flags:tags = 1; +re2c:define:YYCTYPE = char; +re2c:define:YYCURSOR = s->cursor; +re2c:define:YYMARKER = s->marker; +re2c:define:YYCTXMARKER = s->ctx_marker; +re2c:define:YYCONDTYPE = 'condition'; +re2c:define:YYGETCONDITION = 's->condition'; +re2c:define:YYGETCONDITION:naked = 1; +re2c:define:YYSETCONDITION = 's->condition = @@;'; +re2c:define:YYSETCONDITION:naked = 1; + +// Eof is marked by null when preparing files for lexing. +// Mutually exclusive; only !, [, are not covered. +end = "\x00"; +newline = "\n" | "\r" "\n"?; +ws = [ \t\v\f]; +glyph = [^ \t\n\r\v\f![\x00]; +glyphs = glyph+; + +// inside the block +decimal = [1-9][0-9]*; +number = ([1-9][0-9]* | [0])? "." [0-9]+ | [1-9][0-9]* | [0]; +id = [a-zA-Z_][a-zA-Z_\-0-9]{0,63}; +*/ + +static int lex(struct scanner *const s) { + const char *s0, *s1; + /*!stags:re2c format = 'const char *@@;\n'; */ + s->ws_before = 0; +scan: + /*!re2c + end { return s->symbol = END, 1; } + // fixme: paragraphs. + newline { s->line++; s->ws_before = 1; goto scan; } + ws+ { s->ws_before = 1; goto scan; } + @s0 glyph+ @s1 { s->s0 = s0, s->s1 = s1; + return s->symbol = TEXT, 1; } + @s0 "!" @s1 { s->s0 = s0, s->s1 = s1; + return s->symbol = BANG, 1; } + "![" :=> image + "[" :=> command + * { return 0; } + ws* "osm" ws* "](geo:" @s0 number "," @s1 number ")" { + s->condition = yyctext; + s->s0 = s0, s->s1 = s1; + printf("Got a map.\n"); + return 1; + } + * { return 0; } + */ +} + +static int parse_uint(const char *s, const char *e, unsigned *u) { + uint32_t n = 0; + for ( ; s < e; ++s) { + unsigned digit = (unsigned)(*s - '0'); + assert(digit < 10); + if(n > (UINT_MAX - digit) / 10) return errno = ERANGE, 0; /* check */ + n = n * 10 + digit; + } + *u = n; + return 1; +} + +static int parse_double(const char *s0, const char *s1, double *d) { + char *finish; + assert(d && s0 && s0 < s1); + *d = strtod(s0, &finish); + return !errno && ((finish == s1) || (errno = EDOM, 0)); +} + +#include /* chdir (POSIX, not ANSI) */ +#include /* mode_t (umask) */ +#include /* umask */ + +int main(int argc, char **argv) { + int success = EXIT_FAILURE; + if(argc != 2) { fprintf(stderr, "Needs journal location.\n" + "(should contain //.txt)\n"); goto finally; } + if(chdir(argv[1]) == -1) goto catch; /* Go to journal. */ + + { success = EXIT_SUCCESS; goto finally; } +catch: + perror("interpret"); +finally: + return EXIT_FAILURE; +}