diff --git a/Makefile b/Makefile index 28fbec6b1..51822b242 100644 --- a/Makefile +++ b/Makefile @@ -139,6 +139,7 @@ include dsp/tty/tty.mk # ├──online include libc/dns/dns.mk # │ include libc/crypto/crypto.mk # │ include net/http/http.mk #─┘ +include third_party/lemon/lemon.mk include third_party/linenoise/linenoise.mk include third_party/editline/editline.mk include third_party/duktape/duktape.mk @@ -157,6 +158,8 @@ include tool/build/lib/buildlib.mk include tool/build/emucrt/emucrt.mk include tool/build/emubin/emubin.mk include tool/build/build.mk +include tool/calc/calc.mk +include tool/tags/tags.mk include tool/decode/lib/decodelib.mk include tool/decode/decode.mk include tool/hash/hash.mk diff --git a/ape/ape.S b/ape/ape.S index dc0fe792d..e38531002 100644 --- a/ape/ape.S +++ b/ape/ape.S @@ -1051,7 +1051,6 @@ realmodeloader: mov %sp,%bp call rlinit call sinit4 - mov $VIDYA_MODE,%di call vinit mov %es,XLM(VIDEO_POSITION_FAR_POINTER) mov %ax,XLM(VIDEO_POSITION_FAR_POINTER)+2 @@ -1291,32 +1290,16 @@ sputc: push %ax ret .endfn sputc,globl -/ Asks BIOS to initialize MDA/CGA display w/o cursor/blinking. +/ Asks BIOS to initialize Monochrome Display Adapter. / -/ @param dil video mode / @return es:ax start of video page / @mode real -vinit: push %bp - mov %sp,%bp - push %bx - bbmov VIDYA_ADDR_CGA>>4,%ax,%ah,%al +vinit: push $7 + pop %ax + int $VIDYA_SERVICE + bbmov VIDYA_ADDR_MDA>>4,%ax,%ah,%al mov %ax,%es - mov %di,%ax - cmp $VIDYA_MODE_CGA,%al - je 1f - xor %ch,%ch # aka movesdi VIDYA_ADDR_MDA -1: xor %di,%di - bbmov VIDYA_SET_MODE,%ax,%ah,%al - int $VIDYA_SERVICE - bbmov VIDYA_SET_CURSOR,,%ah,%al - bbmov VIDYA_SET_CURSOR_NONE,,%ch,%cl # since we don't support it - int $VIDYA_SERVICE - bbmov VIDYA_SET_BLINKING,,%ah,%al # ← cargo culting (ega?) - bbmov VIDYA_SET_BLINKING_NONE,,%bh,%bl - int $VIDYA_SERVICE xor %ax,%ax - pop %bx - pop %bp ret .endfn vinit,globl diff --git a/build/config.mk b/build/config.mk index 7801beb63..18e77b0c3 100644 --- a/build/config.mk +++ b/build/config.mk @@ -18,7 +18,7 @@ CONFIG_CCFLAGS += \ -Og TARGET_ARCH ?= \ - -march=k8-sse3 + -msse3 RAGELFLAGS ?= -G2 diff --git a/build/definitions.mk b/build/definitions.mk index 845c6f816..b56478fff 100644 --- a/build/definitions.mk +++ b/build/definitions.mk @@ -155,8 +155,7 @@ MATHEMATICAL = \ DEFAULT_CPPFLAGS = \ -DIMAGE_BASE_VIRTUAL=$(IMAGE_BASE_VIRTUAL) \ -nostdinc \ - -iquote . \ - -include libc/integral/normalize.inc + -iquote . DEFAULT_CFLAGS = \ -std=gnu2x @@ -217,7 +216,8 @@ cpp.flags = \ $(DEFAULT_CPPFLAGS) \ $(CONFIG_CPPFLAGS) \ $(CPPFLAGS) \ - $(OVERRIDE_CPPFLAGS) + $(OVERRIDE_CPPFLAGS) \ + -include libc/integral/normalize.inc copt.flags = \ $(TARGET_ARCH) \ @@ -317,18 +317,20 @@ OBJECTIFY.c99.c = $(CC) $(OBJECTIFY.c.flags) -std=c99 -Wextra -Werror -pedantic- OBJECTIFY.c11.c = $(CC) $(OBJECTIFY.c.flags) -std=c11 -Wextra -Werror -pedantic-errors -c OBJECTIFY.c2x.c = $(CC) $(OBJECTIFY.c.flags) -std=c2x -Wextra -Werror -pedantic-errors -c -# No-Clobber ABI (clobbers nothing, except rax and flags) -# -# This ABI is intended for core library functions that're frequently -# called by just about everything, e.g. memcpy, malloc, etc. By offering -# this guarantee, callers can optionally call these functions via asm(), -# which reduces register allocator pressure at call sites. -# -# This makes unrelated caller code faster, but the NCABI functions -# themselves a tiny bit slower. That's OK, since modern NexGen-32e CPUs -# seem to have one fifth of their execution engines devoted to pushing -# and popping, probably so legacy IA-32 code keeps going fast; so we use -# it to our advantage. +OBJECTIFY.real.c = \ + $(GCC) \ + $(OBJECTIFY.c.flags) \ + -wrapper build/realify.sh \ + -ffixed-r8 \ + -ffixed-r9 \ + -ffixed-r10 \ + -ffixed-r11 \ + -ffixed-r12 \ + -ffixed-r13 \ + -ffixed-r14 \ + -ffixed-r15 \ + -c + OBJECTIFY.ncabi.c = \ $(GCC) \ $(OBJECTIFY.c.flags) \ @@ -350,9 +352,6 @@ OBJECTIFY.ncabi.c = \ -c \ -xc -# Initializer ABI -# -# Doesn't clobber RDI and RSI. OBJECTIFY.initabi.c = \ $(GCC) \ $(OBJECTIFY.c.flags) \ diff --git a/build/realify.sed b/build/realify.sed new file mode 100644 index 000000000..b6e6ccfbc --- /dev/null +++ b/build/realify.sed @@ -0,0 +1,177 @@ +#-*-mode:sed;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐ +#───vi: et ft=sed ts=8 tw=8 fenc=utf-8 :vi─────────────────┘ +# +# SYNOPSIS +# +# sed -i -f build/realify.sed foo.s +# +# OVERVIEW +# +# This converts ints and longs to shorts while preserving System V ABI +# x86_64 compatibility. This works better than gcc -m16 because we can +# avoid the ASZ and OSZ prefixes in most cases while also avoiding the +# legacy 32-bit calling conventions. + +# remove comments +s/[ \t][ \t]*#.*// + +# preserve hardcoded stack offsets +s/leave\(q\|\)/leavew ; add $6,%sp/ +s/call\(q\|\)\t/sub $6,%sp ; callw / +s/ret\(q\|\)/retw\t$6/ +s/pushq\t\(.*\)/sub $6,%sp ; push \1/ +s/popq\t\(.*\)/pop \1 ; add $6,%sp/ + +# can be used instead if +# 1. functions have 6 args or fewer +# 2. long double parameters forceinline +#s/leave\(q\|\)/leavew/ +#s/call\(q\|\)/callw/ +#s/ret\(q\|\)/retw/ +#s/popq\t%rbp/pop\t%bp/ +#s/pushq\t%rbp/push\t%bp/ +#s/pushq\t\(.*\)/sub $6,%sp ; push \1/ +#s/popq\t\(.*\)/pop \1 ; add $6,%sp/ + +# 32-bitify +s/rax/eax/g +s/rbx/ebx/g +s/rcx/ecx/g +s/rdx/edx/g +s/rbp/ebp/g +s/rdi/edi/g +s/rsi/esi/g +s/rsp/esp/g + +# unextension +s/movswl/mov/ +s/movzwl/mov/ +s/movslq/mov/ +s/movzlq/mov/ + +# unsuffix +s/^\(\t\(fild\|fist\|fistp\|fiadd\|fisub\|fisubr\|fimul\|fidiv\|fidivr\|ficom\)\)q\t/\1\t/ +s/^\(\t\(mov\|add\|adc\|cmp\|test\|lea\|sbb\|mul\|imul\|div\|idiv\|in\|out\|xor\|sub\|and\|or\|rol\|ror\|rcl\|rcr\|shl\|shr\|sal\|sar\|inc\|dec\|not\|neg\)\)l\t/\1w\t/ +s/^\(\t[a-z]*\)q\t/\1w\t/ + +# remove fluff +s/mov\t%eax,%eax// +s/mov\t%ebx,%ebx// +s/mov\t%ecx,%ecx// +s/mov\t%edx,%edx// +s/mov\t%ebp,%ebp// +s/mov\t%edi,%edi// +s/mov\t%esi,%esi// +s/mov\t%esp,%esp// + +# make pic absolute +s/(%rip)// + +# legal real mode modrm +s/(%ebx)/(%bx)/ +s/(%edi)/(%di)/ +s/(%esi)/(%si)/ +s/(%ebp)/(%bp)/ +s/(%ebx,%esi\(,1\|\))/(%bx,%si)/ +s/(%ebx,%edi\(,1\|\))/(%bx,%di)/ +s/(%ebp,%esi\(,1\|\))/(%bp,%si)/ +s/(%ebp,%edi\(,1\|\))/(%bp,%di)/ + +# we need the asz prefix +s/(%eax,%eax/(%EAX,%EAX/ +s/(%eax,%ebp/(%EAX,%EBP/ +s/(%eax,%ebx/(%EAX,%EBX/ +s/(%eax,%ecx/(%EAX,%ECX/ +s/(%eax,%edi/(%EAX,%EDI/ +s/(%eax,%edx/(%EAX,%EDX/ +s/(%eax,%esi/(%EAX,%ESI/ +s/(%eax,%esp/(%EAX,%ESP/ +s/(%ebp,%eax/(%EBP,%EAX/ +s/(%ebp,%ebp/(%EBP,%EBP/ +s/(%ebp,%ebx/(%EBP,%EBX/ +s/(%ebp,%ecx/(%EBP,%ECX/ +s/(%ebp,%edi/(%EBP,%EDI/ +s/(%ebp,%edx/(%EBP,%EDX/ +s/(%ebp,%esi/(%EBP,%ESI/ +s/(%ebp,%esp/(%EBP,%ESP/ +s/(%ebx,%eax/(%EBX,%EAX/ +s/(%ebx,%ebp/(%EBX,%EBP/ +s/(%ebx,%ebx/(%EBX,%EBX/ +s/(%ebx,%ecx/(%EBX,%ECX/ +s/(%ebx,%edi/(%EBX,%EDI/ +s/(%ebx,%edx/(%EBX,%EDX/ +s/(%ebx,%esi/(%EBX,%ESI/ +s/(%ebx,%esp/(%EBX,%ESP/ +s/(%ecx,%eax/(%ECX,%EAX/ +s/(%ecx,%ebp/(%ECX,%EBP/ +s/(%ecx,%ebx/(%ECX,%EBX/ +s/(%ecx,%ecx/(%ECX,%ECX/ +s/(%ecx,%edi/(%ECX,%EDI/ +s/(%ecx,%edx/(%ECX,%EDX/ +s/(%ecx,%esi/(%ECX,%ESI/ +s/(%ecx,%esp/(%ECX,%ESP/ +s/(%edi,%eax/(%EDI,%EAX/ +s/(%edi,%ebp/(%EDI,%EBP/ +s/(%edi,%ebx/(%EDI,%EBX/ +s/(%edi,%ecx/(%EDI,%ECX/ +s/(%edi,%edi/(%EDI,%EDI/ +s/(%edi,%edx/(%EDI,%EDX/ +s/(%edi,%esi/(%EDI,%ESI/ +s/(%edi,%esp/(%EDI,%ESP/ +s/(%edx,%eax/(%EDX,%EAX/ +s/(%edx,%ebp/(%EDX,%EBP/ +s/(%edx,%ebx/(%EDX,%EBX/ +s/(%edx,%ecx/(%EDX,%ECX/ +s/(%edx,%edi/(%EDX,%EDI/ +s/(%edx,%edx/(%EDX,%EDX/ +s/(%edx,%esi/(%EDX,%ESI/ +s/(%edx,%esp/(%EDX,%ESP/ +s/(%esi,%eax/(%ESI,%EAX/ +s/(%esi,%ebp/(%ESI,%EBP/ +s/(%esi,%ebx/(%ESI,%EBX/ +s/(%esi,%ecx/(%ESI,%ECX/ +s/(%esi,%edi/(%ESI,%EDI/ +s/(%esi,%edx/(%ESI,%EDX/ +s/(%esi,%esi/(%ESI,%ESI/ +s/(%esi,%esp/(%ESI,%ESP/ +s/(%esp,%eax/(%ESP,%EAX/ +s/(%esp,%ebp/(%ESP,%EBP/ +s/(%esp,%ebx/(%ESP,%EBX/ +s/(%esp,%ecx/(%ESP,%ECX/ +s/(%esp,%edi/(%ESP,%EDI/ +s/(%esp,%edx/(%ESP,%EDX/ +s/(%esp,%esi/(%ESP,%ESI/ +s/(%esp,%esp/(%ESP,%ESP/ +s/(,%eax/(,%EAX/ +s/(,%ebx/(,%EBX/ +s/(,%ecx/(,%ECX/ +s/(,%edx/(,%EDX/ +s/(,%esi/(,%ESI/ +s/(,%edi/(,%EDI/ +s/(,%ebp/(,%EBP/ +s/(,%esp/(,%ESP/ +s/(%eax)/(%EAX)/ +s/(%ecx)/(%ECX)/ +s/(%edx)/(%EDX)/ +s/(%esp)/(%ESP)/ + +# 16bitify +s/eax/ax/g +s/ebx/bx/g +s/ecx/cx/g +s/edx/dx/g +s/ebp/bp/g +s/edi/di/g +s/esi/si/g +s/esp/sp/g + +# sigh :\ +# impossible to avoid rex byte access with naive substitution +# best workaround is avoid uint8_t* and try using uint16_t* more +#s/dil/bl/g +#s/sil/bh/g +#s/spl/bl/g +#s/bpl/bh/g + +# nope +s/cltq// diff --git a/build/realify.sh b/build/realify.sh new file mode 100755 index 000000000..95ef08859 --- /dev/null +++ b/build/realify.sh @@ -0,0 +1,23 @@ +#!/bin/sh +# +# SYNOPSIS +# +# gcc -g0 -Os -wrapper build/realify.sh -ffixed-r{8,9,1{0,1,2,4,5}} +# +# OVERVIEW +# +# Reconfigures x86_64 compiler to emit 16-bit PC boot code. + +if [ "${1##*/}" = as ]; then + for x; do + if [ "${x##*.}" = s ]; then + { + printf "\t.code16gcc" + sed -f build/realify.sed "$x" + } >"$x".tmp + mv -f "$x".tmp "$x" + fi + done +fi + +exec "$@" diff --git a/build/rules.mk b/build/rules.mk index 07badffb6..b3e15a40c 100644 --- a/build/rules.mk +++ b/build/rules.mk @@ -72,6 +72,7 @@ o/$(MODE)/%.c11.o: %.c11.c; @ACTION=OBJECTIFY.c11 build/compile $(OBJECTIFY.c11. o/$(MODE)/%.c2x.o: %.c2x.c; @ACTION=OBJECTIFY.c2x build/compile $(OBJECTIFY.c2x.c) $(OUTPUT_OPTION) $< o/$(MODE)/%.initabi.o: %.initabi.c; @ACTION=OBJECTIFY.init build/compile $(OBJECTIFY.initabi.c) $(OUTPUT_OPTION) $< o/$(MODE)/%.ncabi.o: %.ncabi.c; @ACTION=OBJECTIFY.nc build/compile $(OBJECTIFY.ncabi.c) $(OUTPUT_OPTION) $< +o/tiny/%.real.o: %.real.c; @ACTION=OBJECTIFY.real build/compile $(OBJECTIFY.real.c) $(OUTPUT_OPTION) $< o/$(MODE)/%.runs: o/$(MODE)/%; @ACTION=CHECK.runs TARGET=$< build/runcom $< $(TESTARGS) && touch $@ o/$(MODE)/%.pkg:; @build/package $(OUTPUT_OPTION) $(addprefix -d,$(filter %.pkg,$^)) $(filter %.o,$^) o/$(MODE)/%.zip.o: %; @build/zipobj $(OUTPUT_OPTION) $< diff --git a/examples/kilo.c b/examples/kilo.c index f74a094d2..43ab0ad52 100644 --- a/examples/kilo.c +++ b/examples/kilo.c @@ -243,21 +243,19 @@ fatal: int editorReadKey(int64_t fd) { int nread; char c, seq[3]; - while ((nread = read(fd, &c, 1)) == 0) { - } - if (nread == -1) exit(1); + if ((nread = read(fd, &c, 1)) == -1) exit(1); while (1) { switch (c) { case CTRL('J'): /* newline */ return CTRL('M'); - case CTRL('['): /* escape sequence */ + case CTRL('V'): + return PAGE_DOWN; + case '\e': /* escape sequence */ /* If this is just an ESC, we'll timeout here. */ if (read(fd, seq, 1) == 0) return CTRL('['); - if (read(fd, seq + 1, 1) == 0) return CTRL('['); - - /* ESC [ sequences. */ if (seq[0] == '[') { + if (read(fd, seq + 1, 1) == 0) return CTRL('['); if (seq[1] >= '0' && seq[1] <= '9') { /* Extended escape, read additional byte. */ if (read(fd, seq + 2, 1) == 0) return CTRL('['); @@ -296,10 +294,11 @@ int editorReadKey(int64_t fd) { return END_KEY; } } - } - - /* ESC O sequences. */ - else if (seq[0] == 'O') { + } else if (seq[0] == 'v') { + return PAGE_UP; + } else if (seq[0] == 'O') { + if (read(fd, seq + 1, 1) == 0) return CTRL('['); + /* ESC O sequences. */ switch (seq[1]) { case 'H': return HOME_KEY; @@ -586,8 +585,9 @@ void editorUpdateRow(erow *row) { /* Create a version of the row we can directly print on the screen, * respecting tabs, substituting non printable characters with '?'. */ free(row->render); - for (j = 0; j < row->size; j++) + for (j = 0; j < row->size; j++) { if (row->chars[j] == CTRL('I')) tabs++; + } row->render = malloc(row->size + tabs * 8 + nonprint * 9 + 1); idx = 0; @@ -1200,9 +1200,10 @@ void editorMoveCursor(int key) { void editorProcessKeypress(int64_t fd) { /* When the file is modified, requires Ctrl-q to be pressed N times * before actually quitting. */ + int c, times; static int quit_times; - int c = editorReadKey(fd); + c = editorReadKey(fd); switch (c) { case CTRL('M'): /* Enter */ editorInsertNewline(); @@ -1271,6 +1272,13 @@ void editorProcessKeypress(int64_t fd) { break; } + case CTRL('L'): + times = E.screenrows / 2; + while (times--) editorMoveCursor(c == PAGE_UP ? ARROW_UP : ARROW_DOWN); + times = E.screenrows / 2; + while (times--) editorMoveCursor(c == PAGE_UP ? ARROW_DOWN : ARROW_UP); + break; + case PAGE_UP: case PAGE_DOWN: if (c == PAGE_UP && E.cy != 0) { @@ -1278,10 +1286,10 @@ void editorProcessKeypress(int64_t fd) { } else if (c == PAGE_DOWN && E.cy != E.screenrows - 1) { E.cy = E.screenrows - 1; } - { - int times = E.screenrows; - while (times--) editorMoveCursor(c == PAGE_UP ? ARROW_UP : ARROW_DOWN); - } + times = E.screenrows; + while (times--) editorMoveCursor(c == PAGE_UP ? ARROW_UP : ARROW_DOWN); + times = E.screenrows / 2; + while (times--) editorMoveCursor(c == PAGE_UP ? ARROW_DOWN : ARROW_UP); break; case HOME_KEY: @@ -1316,9 +1324,6 @@ void editorProcessKeypress(int64_t fd) { editorMoveCursor(c); break; - case CTRL('L'): /* ctrl+l, clear screen */ - /* Just refresht the line as side effect. */ - break; case CTRL('['): /* Nothing to do for ESC in this mode. */ break; diff --git a/examples/lstime.c b/examples/lstime.c new file mode 100644 index 000000000..9d4ac753c --- /dev/null +++ b/examples/lstime.c @@ -0,0 +1,95 @@ +#if 0 +/*─────────────────────────────────────────────────────────────────╗ +│ To the extent possible under law, Justine Tunney has waived │ +│ all copyright and related or neighboring rights to this file, │ +│ as it is written in the following disclaimers: │ +│ • http://unlicense.org/ │ +│ • http://creativecommons.org/publicdomain/zero/1.0/ │ +╚─────────────────────────────────────────────────────────────────*/ +#endif +#include "libc/alg/alg.h" +#include "libc/alg/arraylist2.h" +#include "libc/bits/safemacros.h" +#include "libc/calls/calls.h" +#include "libc/calls/struct/dirent.h" +#include "libc/calls/struct/stat.h" +#include "libc/calls/struct/timespec.h" +#include "libc/log/check.h" +#include "libc/mem/mem.h" +#include "libc/runtime/gc.h" +#include "libc/stdio/stdio.h" +#include "libc/sysv/consts/dt.h" +#include "libc/x/x.h" + +struct stat st; +struct Files { + size_t i, n; + struct File { + struct timespec mt; + char *path; + } * p; +} g_files; + +int CompareFiles(struct File *a, struct File *b) { + if (a->mt.tv_sec > b->mt.tv_sec) return 1; + if (a->mt.tv_sec < b->mt.tv_sec) return -1; + if (a->mt.tv_nsec > b->mt.tv_nsec) return 1; + if (a->mt.tv_nsec < b->mt.tv_nsec) return -1; + return strcmp(a->path, b->path); +} + +void WalkPaths(const char *dirpath) { + DIR *d; + char *path; + struct File f; + struct dirent *e; + CHECK((d = opendir(dirpath))); + while ((e = readdir(d))) { + if (strcmp(e->d_name, ".") == 0) continue; + if (strcmp(e->d_name, "..") == 0) continue; + path = xjoinpaths(dirpath, e->d_name); + if (strcmp(e->d_name, "o") == 0) continue; + if (strcmp(e->d_name, ".git") == 0) continue; + if (e->d_type == DT_DIR) { + WalkPaths(gc(path)); + } else { + CHECK_NE(-1, lstat(path, &st), "%s", path); + f.mt = st.st_mtim; + f.path = path; + APPEND(&g_files.p, &g_files.i, &g_files.n, &f); + } + } + closedir(d); +} + +void SortPaths(void) { + qsort(g_files.p, g_files.i, sizeof(*g_files.p), (void *)CompareFiles); +} + +void PrintPaths(void) { + long i; + char *ts; + for (i = 0; i < g_files.i; ++i) { + ts = xiso8601(&g_files.p[i].mt); + printf("%s %s\n", ts, g_files.p[i].path); + free(ts); + } +} + +void FreePaths(void) { + long i; + for (i = 0; i < g_files.i; ++i) free(g_files.p[i].path); + g_files.i = 0; +} + +void LsTime(void) { + WalkPaths("."); + SortPaths(); + PrintPaths(); + FreePaths(); +} + +int main(int argc, char *argv[]) { + LsTime(); + return 0; +} diff --git a/libc/bits/bits.h b/libc/bits/bits.h index 4de81a9d6..31742e7d9 100644 --- a/libc/bits/bits.h +++ b/libc/bits/bits.h @@ -45,9 +45,9 @@ unsigned long hamming(unsigned long, unsigned long) pureconst; #define ROL(w, k) ((w) << (k) | CheckUnsigned(w) >> (sizeof(w) * 8 - (k))) #define SAR(w, k) (((w) & ~(~0u >> (k))) | ((w) >> ((k) & (sizeof(w) * 8 - 1)))) -#define bitreverse8(X) (kReverseBits[(X)&0xff]) -#define bitreverse16(X) \ - ((uint16_t)kReverseBits[(X)&0xff] << 010 | \ +#define bitreverse8(X) (kReverseBits[(uint8_t)(X)]) +#define bitreverse16(X) \ + ((uint16_t)kReverseBits[(uint8_t)(X)] << 010 | \ kReverseBits[((uint16_t)(X) >> 010) & 0xff]) #define READ16LE(S) \ diff --git a/libc/bits/roundup.c b/libc/bits/roundup.c index 7328d356c..dc8228452 100644 --- a/libc/bits/roundup.c +++ b/libc/bits/roundup.c @@ -19,4 +19,6 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/macros.h" -long(roundup)(long w, long k) { return ROUNDUP(w, k); } +long(roundup)(long w, long k) { + return ROUNDUP(w, k); +} diff --git a/libc/calls/calls.h b/libc/calls/calls.h index e843a0660..3680cc462 100644 --- a/libc/calls/calls.h +++ b/libc/calls/calls.h @@ -84,7 +84,6 @@ char *get_current_dir_name(void) nodiscard; char *getcwd(char *, size_t); char *realpath(const char *, char *); char *replaceuser(const char *) nodiscard; -char *slurp(const char *, size_t *) nodiscard; char *ttyname(int); char *commandv(const char *, char[hasatleast PATH_MAX]); int access(const char *, int) nothrow; diff --git a/libc/time/clock.c b/libc/calls/clock.c similarity index 100% rename from libc/time/clock.c rename to libc/calls/clock.c diff --git a/libc/time/clock_gettime.c b/libc/calls/clock_gettime.c similarity index 100% rename from libc/time/clock_gettime.c rename to libc/calls/clock_gettime.c diff --git a/libc/calls/close-nt.c b/libc/calls/close-nt.c index eca8cc2d6..f50f8e293 100644 --- a/libc/calls/close-nt.c +++ b/libc/calls/close-nt.c @@ -24,7 +24,7 @@ textwindows int close$nt(int fd) { bool32 ok; - if (isfdindex(fd)) { + if (isfdopen(fd)) { if (g_fds.p[fd].kind == kFdFile) { /* * Like Linux, closing a file on Windows doesn't guarantee it's diff --git a/libc/time/dtime.c b/libc/calls/dtime.c similarity index 100% rename from libc/time/dtime.c rename to libc/calls/dtime.c diff --git a/libc/calls/dup-nt.c b/libc/calls/dup-nt.c index 5d27e7eb0..c7fbbf6fd 100644 --- a/libc/calls/dup-nt.c +++ b/libc/calls/dup-nt.c @@ -34,13 +34,13 @@ textwindows int dup$nt(int oldfd, int newfd, int flags) { if (newfd == -1) { if ((newfd = createfd()) == -1) return -1; } else if (isfdindex(newfd)) { - close(newfd); - } else if (isfdlegal(newfd)) { + if (g_fds.p[newfd].kind != kFdEmpty) { + close(newfd); + } + } else { do { if (growfds() == -1) return -1; } while (newfd >= g_fds.n); - } else { - return ebadf(); } if (DuplicateHandle(GetCurrentProcess(), g_fds.p[oldfd].handle, GetCurrentProcess(), &g_fds.p[newfd].handle, 0, diff --git a/libc/calls/fcntl-nt.c b/libc/calls/fcntl-nt.c index 051d39a96..5eeaf72ed 100644 --- a/libc/calls/fcntl-nt.c +++ b/libc/calls/fcntl-nt.c @@ -24,7 +24,7 @@ #include "libc/sysv/errfuns.h" textwindows int fcntl$nt(int fd, int cmd, unsigned arg) { - if (!isfdindex(fd)) return ebadf(); + if (!isfdkind(fd, kFdFile)) return ebadf(); switch (cmd) { case F_GETFD: return GetHandleInformation(g_fds.p[fd].handle, &arg) ? (arg ^ FD_CLOEXEC) diff --git a/libc/calls/fprot2nt.c b/libc/calls/fprot2nt.c index 3c7d9fe6d..4eb0fe374 100644 --- a/libc/calls/fprot2nt.c +++ b/libc/calls/fprot2nt.c @@ -33,7 +33,10 @@ uint32_t fprot2nt(int prot, int flags) { return (HAS(prot, PROT_READ) ? kNtFileMapRead : 0) | (HAS(prot, PROT_WRITE) ? kNtFileMapWrite : 0) | (HAS(prot, PROT_EXEC) ? kNtFileMapExecute : 0) | - (HAS(flags, MAP_PRIVATE) ? kNtFileMapCopy : 0) | (HAS(flags, kNtSecLargePages) ? kNtFileMapLargePages : 0) | - (HAS(flags, kNtSecReserve) ? kNtFileMapReserve : 0); + (HAS(flags, kNtSecReserve) ? kNtFileMapReserve : 0) | + ((HAS(flags, MAP_PRIVATE) && HAS(prot, PROT_READ) && + HAS(prot, PROT_WRITE)) + ? kNtFileMapCopy + : 0); } diff --git a/libc/calls/getrlimit.c b/libc/calls/getrlimit.c index 1ddb0253a..258e5b2ae 100644 --- a/libc/calls/getrlimit.c +++ b/libc/calls/getrlimit.c @@ -31,7 +31,6 @@ * @see libc/sysv/consts.sh */ int getrlimit(int resource, struct rlimit *rlim) { - /* TODO(jart): Windows */ if (resource == -1) return einval(); return getrlimit$sysv(resource, rlim); } diff --git a/libc/time/gettimeofday-nt.c b/libc/calls/gettimeofday-nt.c similarity index 100% rename from libc/time/gettimeofday-nt.c rename to libc/calls/gettimeofday-nt.c diff --git a/libc/time/gettimeofday-sysv.S b/libc/calls/gettimeofday-sysv.S similarity index 100% rename from libc/time/gettimeofday-sysv.S rename to libc/calls/gettimeofday-sysv.S diff --git a/libc/time/gettimeofday.c b/libc/calls/gettimeofday.c similarity index 100% rename from libc/time/gettimeofday.c rename to libc/calls/gettimeofday.c diff --git a/libc/calls/growfds.c b/libc/calls/growfds.c index 5329907aa..57ae2d1b8 100644 --- a/libc/calls/growfds.c +++ b/libc/calls/growfds.c @@ -28,7 +28,7 @@ int growfds(void) { struct Fd *p; if (weaken(realloc)) { if ((p = weaken(realloc)(g_fds.p != g_fds.__init_p ? g_fds.p : NULL, - (n = ((i = g_fds.n) * 2)) * sizeof(*p)))) { + (n = (i = g_fds.n) << 1) * sizeof(*p)))) { do { p[i++].kind = kFdEmpty; } while (i < n); diff --git a/libc/calls/hefty/hefty.mk b/libc/calls/hefty/hefty.mk index cd294d02f..40e4d1c1b 100644 --- a/libc/calls/hefty/hefty.mk +++ b/libc/calls/hefty/hefty.mk @@ -46,8 +46,7 @@ LIBC_CALLS_HEFTY_A_DIRECTDEPS = \ LIBC_STR \ LIBC_STUBS \ LIBC_SYSV \ - LIBC_SYSV_CALLS \ - LIBC_TIME + LIBC_SYSV_CALLS LIBC_CALLS_HEFTY_A_DEPS := \ $(call uniq,$(foreach x,$(LIBC_CALLS_HEFTY_A_DIRECTDEPS),$($(x)))) diff --git a/libc/calls/internal.h b/libc/calls/internal.h index f12fe19a4..b2f67b0e7 100644 --- a/libc/calls/internal.h +++ b/libc/calls/internal.h @@ -1,22 +1,3 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│vi: set net ft=c ts=8 sts=2 sw=2 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ This program is free software; you can redistribute it and/or modify │ -│ it under the terms of the GNU General Public License as published by │ -│ the Free Software Foundation; version 2 of the License. │ -│ │ -│ This program is distributed in the hope that it will be useful, but │ -│ WITHOUT ANY WARRANTY; without even the implied warranty of │ -│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ -│ General Public License for more details. │ -│ │ -│ You should have received a copy of the GNU General Public License │ -│ along with this program; if not, write to the Free Software │ -│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ -│ 02110-1301 USA │ -╚─────────────────────────────────────────────────────────────────────────────*/ #ifndef COSMOPOLITAN_LIBC_CALLS_INTERNAL_H_ #define COSMOPOLITAN_LIBC_CALLS_INTERNAL_H_ #ifndef __STRICT_ANSI__ @@ -54,15 +35,8 @@ struct IoctlPtmGet { char workername[16]; }; -/** - * List of open handles on Windows NT. - * - * The array is indexed by the sysv-style file descriptor numbers that - * we assign. It's needed to make file descriptors only require 32bits - * and helps us abstract peculiarities like close() vs. closesocket(). - */ struct Fds { - size_t f; // length + size_t f; // arbitrary free slot start search index size_t n; // capacity struct Fd { int64_t handle; @@ -82,34 +56,27 @@ struct Fds { extern const struct Fd kEmptyFd; -hidden extern int g_sighandrvas[NSIG]; -hidden extern struct Fds g_fds; -hidden extern struct NtSystemInfo g_ntsysteminfo; -hidden extern struct NtStartupInfo g_ntstartupinfo; -hidden extern const struct NtSecurityAttributes kNtIsInheritable; - -forceinline bool isfdlegal(int fd) { - if (!IsTrustworthy()) { - return (0 <= fd && fd <= INT_MAX); - } else { - return fd; - } -} - -forceinline bool isfdindex(int fd) { - if (!IsTrustworthy()) { - return (0 <= fd && fd < g_fds.n); - } else { - return fd; - } -} +extern int g_sighandrvas[NSIG] hidden; +extern struct Fds g_fds hidden; +extern struct NtSystemInfo g_ntsysteminfo hidden; +extern struct NtStartupInfo g_ntstartupinfo hidden; +extern const struct NtSecurityAttributes kNtIsInheritable hidden; ssize_t createfd(void) hidden; int growfds(void) hidden; void removefd(int) hidden; enum FdKind fdkind(int) hidden nosideeffect; +bool isfdopen(int) hidden nosideeffect; bool isfdkind(int, enum FdKind) hidden nosideeffect; +forceinline bool isfdindex(int fd) { + if (!IsTrustworthy()) { + return (0 <= fd && fd < g_fds.n); + } else { + return true; + } +} + forceinline size_t clampio(size_t size) { if (!IsTrustworthy()) { return MIN(size, 0x7ffff000); diff --git a/libc/calls/ioctl-default.c b/libc/calls/ioctl-default.c index ecccd46c2..312f19fbb 100644 --- a/libc/calls/ioctl-default.c +++ b/libc/calls/ioctl-default.c @@ -29,8 +29,8 @@ int ioctl$default(int fd, uint64_t request, void *memory) { int64_t handle; if (!IsWindows()) { return ioctl$sysv(fd, request, memory); - } else if (isfdindex(fd)) { - if (isfdkind(fd, kFdSocket)) { + } else if (isfdopen(fd)) { + if (g_fds.p[fd].kind == kFdSocket) { handle = g_fds.p[fd].handle; if ((rc = weaken(__ioctlsocket$nt)(handle, request, memory)) != -1) { return rc; diff --git a/libc/calls/isfdkind.c b/libc/calls/isfdkind.c index fac1ed47e..6c21e0171 100644 --- a/libc/calls/isfdkind.c +++ b/libc/calls/isfdkind.c @@ -20,9 +20,5 @@ #include "libc/calls/internal.h" bool isfdkind(int fd, enum FdKind kind) { - if (isfdindex(fd)) { - return g_fds.p[fd].kind == kind; - } else { - return false; - } + return isfdindex(fd) && g_fds.p[fd].kind == kind; } diff --git a/libc/calls/isfdopen.c b/libc/calls/isfdopen.c new file mode 100644 index 000000000..d40470854 --- /dev/null +++ b/libc/calls/isfdopen.c @@ -0,0 +1,24 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/internal.h" + +bool isfdopen(int fd) { + return isfdindex(fd) && g_fds.p[fd].kind != kFdEmpty; +} diff --git a/libc/calls/ktmppath.S b/libc/calls/ktmppath.S index 04bf9cae0..4b8453b61 100644 --- a/libc/calls/ktmppath.S +++ b/libc/calls/ktmppath.S @@ -39,5 +39,4 @@ kTmpPath: ezlea GetTempPathA$flunk,ax call __getntsyspath .init.end 300,_init_kTmpPath - .source __FILE__ diff --git a/libc/time/nanosleep.c b/libc/calls/nanosleep.c similarity index 100% rename from libc/time/nanosleep.c rename to libc/calls/nanosleep.c diff --git a/libc/time/now.c b/libc/calls/now.c similarity index 100% rename from libc/time/now.c rename to libc/calls/now.c diff --git a/libc/calls/open-nt.c b/libc/calls/open-nt.c index 7107c8a17..bd7ab7131 100644 --- a/libc/calls/open-nt.c +++ b/libc/calls/open-nt.c @@ -22,6 +22,7 @@ #include "libc/calls/ntmagicpaths.h" #include "libc/nexgen32e/tinystrcmp.h" #include "libc/nt/createfile.h" +#include "libc/nt/enum/filesharemode.h" #include "libc/nt/enum/filetype.h" #include "libc/nt/enum/fsctl.h" #include "libc/nt/errors.h" @@ -38,7 +39,10 @@ static textwindows int64_t open$nt$impl(const char *file, uint32_t flags, char16_t file16[PATH_MAX]; if (mkntpath2(file, flags, file16) == -1) return -1; if ((handle = CreateFile( - file16, (flags & 0xf000000f), + file16, + (flags & 0xf000000f) | (/* this is needed if we mmap(rwx+cow) + nt is choosy about open() access */ + kNtGenericExecute | kNtFileGenericWrite), (flags & O_EXCL) ? kNtFileShareExclusive : kNtFileShareRead | kNtFileShareWrite | kNtFileShareDelete, diff --git a/libc/calls/prot2nt.greg.c b/libc/calls/prot2nt.greg.c index 9ff38b525..9b94ded8f 100644 --- a/libc/calls/prot2nt.greg.c +++ b/libc/calls/prot2nt.greg.c @@ -37,7 +37,7 @@ privileged uint32_t prot2nt(int prot, int flags) { : HAS(prot, PROT_READ | PROT_WRITE) ? (HAS(flags, MAP_SHARED) || HAS(flags, MAP_ANONYMOUS)) ? kNtPageReadwrite - : kNtPageWritecopy + : kNtPageReadwrite /* kNtPageWritecopy */ : HAS(prot, PROT_READ | PROT_EXEC) ? kNtPageExecuteRead : HAS(prot, PROT_EXEC) diff --git a/libc/calls/removefd.c b/libc/calls/removefd.c index 988024eba..4cdb450b3 100644 --- a/libc/calls/removefd.c +++ b/libc/calls/removefd.c @@ -17,12 +17,12 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/bits/safemacros.h" #include "libc/calls/internal.h" +#include "libc/macros.h" void removefd(int fd) { - if (isfdindex(fd)) { + if (isfdopen(fd)) { g_fds.p[fd].kind = kFdEmpty; - g_fds.f = min(g_fds.f, fd); + g_fds.f = MIN(g_fds.f, fd); } } diff --git a/libc/time/time.c b/libc/calls/time.c similarity index 100% rename from libc/time/time.c rename to libc/calls/time.c diff --git a/libc/time/utime.c b/libc/calls/utime.c similarity index 100% rename from libc/time/utime.c rename to libc/calls/utime.c diff --git a/libc/time/utimensat-nt.c b/libc/calls/utimensat-nt.c similarity index 97% rename from libc/time/utimensat-nt.c rename to libc/calls/utimensat-nt.c index 8a4f780a7..d0e449bd9 100644 --- a/libc/time/utimensat-nt.c +++ b/libc/calls/utimensat-nt.c @@ -50,9 +50,6 @@ textwindows int utimensat$nt(int dirfd, const char *path, } else { return einval(); } - } else if (isfdindex(dirfd)) { - fh = g_fds.p[dirfd].handle; - closeme = false; } else { return ebadf(); } diff --git a/libc/time/utimensat-sysv.c b/libc/calls/utimensat-sysv.c similarity index 100% rename from libc/time/utimensat-sysv.c rename to libc/calls/utimensat-sysv.c diff --git a/libc/time/utimensat-xnu.c b/libc/calls/utimensat-xnu.c similarity index 100% rename from libc/time/utimensat-xnu.c rename to libc/calls/utimensat-xnu.c diff --git a/libc/time/utimensat.c b/libc/calls/utimensat.c similarity index 100% rename from libc/time/utimensat.c rename to libc/calls/utimensat.c diff --git a/libc/time/utimes.c b/libc/calls/utimes.c similarity index 100% rename from libc/time/utimes.c rename to libc/calls/utimes.c diff --git a/libc/conv/conv.mk b/libc/conv/conv.mk index 1ee2d1b0f..69ca38f82 100644 --- a/libc/conv/conv.mk +++ b/libc/conv/conv.mk @@ -61,6 +61,11 @@ o/$(MODE)/libc/conv/filetimetotimeval.o: \ OVERRIDE_CFLAGS += \ -O3 +o/$(MODE)/libc/conv/itoa64radix10.greg.o \ +o/$(MODE)/libc/conv/itoa128radix10.greg.o: \ + OVERRIDE_CFLAGS += \ + -fwrapv + $(LIBC_CONV_A_OBJS): \ OVERRIDE_CFLAGS += \ $(NO_MAGIC) diff --git a/libc/conv/itoa.h b/libc/conv/itoa.h index 66de99125..10918dba4 100644 --- a/libc/conv/itoa.h +++ b/libc/conv/itoa.h @@ -16,17 +16,10 @@ COSMOPOLITAN_C_START_ - uint128toarray_radix10(0x31337, a) l: 93 (27ns) m: 141 (41ns) - int128toarray_radix10(0x31337, a) l: 96 (28ns) m: 173 (51ns) - SLOWEST + GENERAL - - - int64toarray(0x31337, a, 10) l: 218 (64ns) m: 262 (77ns) - - uint64toarray(0x31337, a, 10) l: 565 (166ns) m: 260 (76ns) - */ -size_t int64toarray_radix10(int64_t, char *); -size_t uint64toarray_radix10(uint64_t, char *); -size_t int64toarray(int64_t, char *, int); -size_t uint64toarray(uint64_t, char *, int); +size_t int64toarray_radix10(int64_t, char[hasatleast 21]); +size_t uint64toarray_radix10(uint64_t, char[hasatleast 21]); size_t uint64toarray_radix16(uint64_t, char[hasatleast 17]); size_t uint64toarray_fixed16(uint64_t, char[hasatleast 17], uint8_t); diff --git a/libc/conv/itoa128radix10.greg.c b/libc/conv/itoa128radix10.greg.c index 65b222623..a9d7e328c 100644 --- a/libc/conv/itoa128radix10.greg.c +++ b/libc/conv/itoa128radix10.greg.c @@ -48,15 +48,7 @@ noinline size_t uint128toarray_radix10(uint128_t i, char *a) { * @return bytes written w/o nul */ size_t int128toarray_radix10(int128_t i, char *a) { - if (i < 0) { - if (i != INT128_MIN) { - *a++ = '-'; - return 1 + uint128toarray_radix10(-i, a); - } else { - memcpy(a, "-170141183460469231731687303715884105728", 41); - return 40; - } - } else { - return uint128toarray_radix10(i, a); - } + if (i >= 0) return uint128toarray_radix10(i, a); + *a++ = '-'; + return 1 + uint128toarray_radix10(-i, a); } diff --git a/libc/conv/itoa64radix10.greg.c b/libc/conv/itoa64radix10.greg.c index d7d39bb7c..137761dc3 100644 --- a/libc/conv/itoa64radix10.greg.c +++ b/libc/conv/itoa64radix10.greg.c @@ -45,15 +45,7 @@ noinline size_t uint64toarray_radix10(uint64_t i, char *a) { * @return bytes written w/o nul */ size_t int64toarray_radix10(int64_t i, char *a) { - if (i < 0) { - if (i != INT64_MIN) { - *a++ = '-'; - return 1 + uint64toarray_radix10(-i, a); - } else { - memcpy(a, "-9223372036854775808", 21); - return 20; - } - } else { - return uint64toarray_radix10(i, a); - } + if (i >= 0) return uint64toarray_radix10(i, a); + *a++ = '-'; + return 1 + uint64toarray_radix10(-i, a); } diff --git a/libc/integral/normalize.inc b/libc/integral/normalize.inc index f6784ebb7..76b634d4c 100644 --- a/libc/integral/normalize.inc +++ b/libc/integral/normalize.inc @@ -66,7 +66,7 @@ #endif #define BIGPAGESIZE 0x200000 -#define STACKSIZE 0x20000 +#define STACKSIZE 0x80000 /* todo: zlib's fault? */ #define FRAMESIZE 0x10000 /* 8086 */ #define PAGESIZE 0x1000 /* i386+ */ #define BUFSIZ 0x1000 /* best stdio default */ diff --git a/libc/log/backtrace3.c b/libc/log/backtrace3.c index 1ba6c8cf4..5fc6f1e36 100644 --- a/libc/log/backtrace3.c +++ b/libc/log/backtrace3.c @@ -31,6 +31,16 @@ #include "libc/stdio/stdio.h" #include "libc/str/str.h" +/** + * Prints stack frames with symbols. + * + * PrintBacktraceUsingSymbols(stdout, NULL, getsymboltable()); + * + * @param f is output stream + * @param bp is rbp which can be NULL to detect automatically + * @param st is open symbol table for current executable + * @return -1 w/ errno if error happened + */ int PrintBacktraceUsingSymbols(FILE *f, const struct StackFrame *bp, struct SymbolTable *st) { size_t gi; @@ -41,6 +51,7 @@ int PrintBacktraceUsingSymbols(FILE *f, const struct StackFrame *bp, const struct Symbol *symbol; const struct StackFrame *frame; if (!st) return -1; + if (!bp) bp = __builtin_frame_address(0); garbage = weaken(g_garbage); gi = garbage ? garbage->i : 0; for (frame = bp; frame; frame = frame->next) { diff --git a/libc/log/cancolor.c b/libc/log/cancolor.c index f6bf61067..25618fc80 100644 --- a/libc/log/cancolor.c +++ b/libc/log/cancolor.c @@ -33,27 +33,6 @@ #include "libc/str/str.h" #include "libc/sysv/consts/fileno.h" -static struct CanColor { - bool once; - bool result; - struct OldNtConsole { - unsigned codepage; - unsigned mode; - int64_t handle; - } oldin, oldout; -} g_cancolor; - -static textwindows void restorecmdexe(void) { - if (g_cancolor.oldin.handle) { - SetConsoleCP(g_cancolor.oldin.codepage); - SetConsoleMode(g_cancolor.oldin.handle, g_cancolor.oldin.mode); - } - if (g_cancolor.oldout.handle) { - SetConsoleOutputCP(g_cancolor.oldout.codepage); - SetConsoleMode(g_cancolor.oldout.handle, g_cancolor.oldout.mode); - } -} - /** * Returns true if ANSI terminal colors are appropriate. * @@ -75,47 +54,20 @@ static textwindows void restorecmdexe(void) { * It's that easy fam. */ bool cancolor(void) { - int64_t handle; + static bool once; + static bool result; const char *term; - if (g_cancolor.once) return g_cancolor.result; - g_cancolor.once = true; - if (IsWindows()) { - if ((int)weakaddr("v_ntsubsystem") == kNtImageSubsystemWindowsCui && - NtGetVersion() >= kNtVersionWindows10) { - atexit(restorecmdexe); - if (GetFileType((handle = g_fds.p[STDIN_FILENO].handle)) == - kNtFileTypeChar) { - g_cancolor.result = true; - g_cancolor.oldin.handle = handle; - g_cancolor.oldin.codepage = GetConsoleCP(); - SetConsoleCP(kNtCpUtf8); - GetConsoleMode(handle, &g_cancolor.oldin.mode); - SetConsoleMode(handle, g_cancolor.oldin.mode | kNtEnableProcessedInput | - kNtEnableVirtualTerminalInput); - } - if (GetFileType((handle = g_fds.p[STDOUT_FILENO].handle)) == - kNtFileTypeChar || - GetFileType((handle = g_fds.p[STDERR_FILENO].handle)) == - kNtFileTypeChar) { - g_cancolor.result = true; - g_cancolor.oldout.handle = handle; - g_cancolor.oldout.codepage = GetConsoleOutputCP(); - SetConsoleOutputCP(kNtCpUtf8); - GetConsoleMode(handle, &g_cancolor.oldout.mode); - SetConsoleMode(handle, g_cancolor.oldout.mode | - kNtEnableProcessedOutput | - kNtEnableVirtualTerminalProcessing); + if (!once) { + if (!result) { + if ((term = getenv("TERM"))) { + /* anything but emacs basically */ + result = strcmp(term, "dumb") != 0; + } else { + /* TODO(jart): Why does Mac bash login shell exec nuke TERM? */ + result = IsXnu(); } } + once = true; } - if (!g_cancolor.result) { - if ((term = getenv("TERM"))) { - /* anything but emacs basically */ - g_cancolor.result = strcmp(term, "dumb") != 0; - } else { - /* TODO(jart): Why does Mac bash login shell exec nuke TERM? */ - g_cancolor.result = IsXnu(); - } - } - return g_cancolor.result; + return result; } diff --git a/libc/nexgen32e/cescapec.S b/libc/nexgen32e/cescapec.S index 869a55e3e..f3c1c3a42 100644 --- a/libc/nexgen32e/cescapec.S +++ b/libc/nexgen32e/cescapec.S @@ -40,7 +40,7 @@ cescapec: ret .LHT: mov $'t,%ah ret -.LLF: or $'n<<010|'\\<<020|'\n<<030,%eax +.LLF: mov $'n,%ah ret .LVT: mov $'v,%ah ret diff --git a/libc/nexgen32e/memcpy.S b/libc/nexgen32e/memcpy.S index e50c6e4da..b8db542fe 100644 --- a/libc/nexgen32e/memcpy.S +++ b/libc/nexgen32e/memcpy.S @@ -100,10 +100,15 @@ MemCpy: .leafprologue mov %ecx,(%rdi) mov %ebx,-4(%rdi,%rdx) jmp 1b -.L3: mov 2(%rsi),%cl - mov %cl,2(%rdi) -.L2: mov 1(%rsi),%cl - mov %cl,1(%rdi) +.L3: push %rbx + mov (%rsi),%cx + mov -2(%rsi,%rdx),%bx + mov %cx,(%rdi) + mov %bx,-2(%rdi,%rdx) + jmp 1b +.L2: mov (%rsi),%cx + mov %cx,(%rdi) + jmp .L0 .L1: mov (%rsi),%cl mov %cl,(%rdi) jmp .L0 diff --git a/libc/nexgen32e/vidya.h b/libc/nexgen32e/vidya.h index 082306df6..5de5b5838 100644 --- a/libc/nexgen32e/vidya.h +++ b/libc/nexgen32e/vidya.h @@ -48,20 +48,20 @@ * @mode long,legacy,real */ -#define VIDYA_ROWS 25 -#define VIDYA_COLUMNS 80 -#define VIDYA_SIZE (VIDYA_ROWS * VIDYA_COLUMNS * 2) -#define VIDYA_MODE_MDA 7 -#define VIDYA_MODE_CGA 3 -#define VIDYA_ADDR_MDA 0xb0000 -#define VIDYA_ADDR_CGA 0xb8000 -#define VIDYA_ATTR_NORMAL 0x07 /* cozy default for both mda and cga */ -#define VIDYA_REWIND ~0x7fff /* derived from mode addr min. lzcnt */ -#define VIDYA_SERVICE 0x10 -#define VIDYA_SET_MODE 0 -#define VIDYA_SET_CURSOR 0x0100 -#define VIDYA_SET_CURSOR_NONE 0x2000 -#define VIDYA_SET_BLINKING 0x1003 +#define VIDYA_ROWS 25 +#define VIDYA_COLUMNS 80 +#define VIDYA_SIZE (VIDYA_ROWS * VIDYA_COLUMNS * 2) +#define VIDYA_MODE_MDA 7 +#define VIDYA_MODE_CGA 3 +#define VIDYA_ADDR_MDA 0xb0000 +#define VIDYA_ADDR_CGA 0xb8000 +#define VIDYA_ATTR_NORMAL 0x07 /* cozy default for both mda and cga */ +#define VIDYA_REWIND ~0x7fff /* derived from mode addr min. lzcnt */ +#define VIDYA_SERVICE 0x10 +#define VIDYA_SET_MODE 0x0003 +#define VIDYA_SET_CURSOR 0x0100 +#define VIDYA_SET_CURSOR_NONE 0x2000 +#define VIDYA_SET_BLINKING 0x1003 #define VIDYA_SET_BLINKING_NONE 0x0000 #if !(__ASSEMBLER__ + __LINKER__ + 0) diff --git a/libc/nt/master.sh b/libc/nt/master.sh index 6443db5fe..8040d7235 100755 --- a/libc/nt/master.sh +++ b/libc/nt/master.sh @@ -5478,7 +5478,7 @@ imp 'RtlSetDaclSecurityDescriptor' RtlSetDaclSecurityDescriptor ntdll 138 imp 'RtlSetDynamicTimeZoneInformation' RtlSetDynamicTimeZoneInformation ntdll 1386 imp 'RtlSetEnvironmentStrings' RtlSetEnvironmentStrings ntdll 1387 imp 'RtlSetEnvironmentVar' RtlSetEnvironmentVar ntdll 1388 -imp 'RtlSetEnvironmentVariable' RtlSetEnvironmentVariable ntdll 1389 +imp 'RtlSetEnvironmentVariable' RtlSetEnvironmentVariable ntdll 1389 imp 'RtlSetExtendedFeaturesMask' RtlSetExtendedFeaturesMask ntdll 1390 imp 'RtlSetGroupSecurityDescriptor' RtlSetGroupSecurityDescriptor ntdll 1391 imp 'RtlSetHeapInformation' RtlSetHeapInformation ntdll 1392 diff --git a/libc/nt/thunk/console.inc b/libc/nt/thunk/console.inc index 72e4f36fc..e6cdf948f 100644 --- a/libc/nt/thunk/console.inc +++ b/libc/nt/thunk/console.inc @@ -1,11 +1,20 @@ #define GetConsoleMode(...) __imp_GetConsoleMode(__VA_ARGS__) -#define SetConsoleCP(...) __imp_SetConsoleCP(__VA_ARGS__) -#define SetConsoleCtrlHandler(...) __imp_SetConsoleCtrlHandler(__VA_ARGS__) -#define SetConsoleMode(...) __imp_SetConsoleMode(__VA_ARGS__) -#define SetConsoleOutputCP(...) __imp_SetConsoleOutputCP(__VA_ARGS__) - extern typeof(GetConsoleMode) *const __imp_GetConsoleMode __msabi; + +#define SetConsoleCP(...) __imp_SetConsoleCP(__VA_ARGS__) extern typeof(SetConsoleCP) *const __imp_SetConsoleCP __msabi; + +#define GetConsoleCP(...) __imp_GetConsoleCP(__VA_ARGS__) +extern typeof(GetConsoleCP) *const __imp_GetConsoleCP __msabi; + +#define SetConsoleCtrlHandler(...) __imp_SetConsoleCtrlHandler(__VA_ARGS__) extern typeof(SetConsoleCtrlHandler) *const __imp_SetConsoleCtrlHandler __msabi; + +#define SetConsoleMode(...) __imp_SetConsoleMode(__VA_ARGS__) extern typeof(SetConsoleMode) *const __imp_SetConsoleMode __msabi; + +#define SetConsoleOutputCP(...) __imp_SetConsoleOutputCP(__VA_ARGS__) extern typeof(SetConsoleOutputCP) *const __imp_SetConsoleOutputCP __msabi; + +#define GetConsoleOutputCP(...) __imp_GetConsoleOutputCP(__VA_ARGS__) +extern typeof(GetConsoleOutputCP) *const __imp_GetConsoleOutputCP __msabi; diff --git a/libc/nt/thunk/files.inc b/libc/nt/thunk/files.inc index b2ac7e491..ae316538f 100644 --- a/libc/nt/thunk/files.inc +++ b/libc/nt/thunk/files.inc @@ -1,5 +1,8 @@ -#define CopyFile(...) __imp_CopyFileW(__VA_ARGS__) -#define FlushFileBuffers(...) __imp_FlushFileBuffers(__VA_ARGS__) - +#define CopyFile(...) __imp_CopyFileW(__VA_ARGS__) extern typeof(CopyFile) *const __imp_CopyFileW __msabi; + +#define FlushFileBuffers(...) __imp_FlushFileBuffers(__VA_ARGS__) extern typeof(FlushFileBuffers) *const __imp_FlushFileBuffers __msabi; + +#define GetFileType(...) __imp_GetFileType(__VA_ARGS__) +extern typeof(GetFileType) *const __imp_GetFileType __msabi; diff --git a/libc/runtime/directmap.c b/libc/runtime/directmap.c index 18b034d53..de1f6850e 100644 --- a/libc/runtime/directmap.c +++ b/libc/runtime/directmap.c @@ -18,6 +18,7 @@ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/internal.h" +#include "libc/macros.h" #include "libc/nt/memory.h" #include "libc/nt/runtime.h" #include "libc/runtime/directmap.h" @@ -25,14 +26,21 @@ static textwindows struct DirectMap DirectMapNt(void *addr, size_t size, unsigned prot, unsigned flags, int fd, int64_t off) { + int64_t handle; struct DirectMap res; - if ((res.maphandle = CreateFileMappingNuma( - fd != -1 ? g_fds.p[fd].handle : kNtInvalidHandleValue, - &kNtIsInheritable, prot2nt(prot, flags), size >> 32, size, NULL, - kNtNumaNoPreferredNode))) { - if (!(res.addr = MapViewOfFileExNuma(res.maphandle, fprot2nt(prot, flags), - off >> 32, off, size, addr, - kNtNumaNoPreferredNode))) { + uint32_t protect, access; + if (fd != -1) { + handle = g_fds.p[fd].handle; + } else { + handle = kNtInvalidHandleValue; + } + protect = prot2nt(prot, flags); + access = fprot2nt(prot, flags); + if ((res.maphandle = + CreateFileMappingNuma(handle, &kNtIsInheritable, protect, size >> 32, + size, NULL, kNtNumaNoPreferredNode))) { + if (!(res.addr = MapViewOfFileExNuma(res.maphandle, access, off >> 32, off, + size, addr, kNtNumaNoPreferredNode))) { CloseHandle(res.maphandle); res.maphandle = kNtInvalidHandleValue; res.addr = (void *)(intptr_t)winerr(); diff --git a/libc/runtime/executive.S b/libc/runtime/executive.S index d19775d11..1b7792225 100644 --- a/libc/runtime/executive.S +++ b/libc/runtime/executive.S @@ -20,6 +20,8 @@ #include "libc/dce.h" #include "libc/macros.h" #include "libc/notice.inc" +#include "libc/sysv/consts/prot.h" +#include "libc/sysv/consts/map.h" #include "libc/dce.h" .text.startup .source __FILE__ @@ -40,12 +42,15 @@ _executive: mov %rdx,%r14 mov %rcx,%r15 call _spawn - mov %r12,%rdi - mov %r13,%rsi - mov %r14,%rdx - mov %r15,%rcx + call _getstack + mov %rax,%rdi .weak main - call main + mov $main,%esi + mov %r12,%rdx + mov %r13,%rcx + mov %r14,%r8 + mov %r15,%r9 + call _setstack mov %eax,%edi call exit 9: .endfn _executive,weak,hidden diff --git a/libc/runtime/ezmap.c b/libc/runtime/ezmap.c index c5331922b..d45d1d3a5 100644 --- a/libc/runtime/ezmap.c +++ b/libc/runtime/ezmap.c @@ -34,9 +34,9 @@ int mapfileread(const char *filename, struct MappedFile *mf) { mf->addr = MAP_FAILED; if ((mf->fd = open(filename, O_RDONLY)) != -1 && (mf->size = getfiledescriptorsize(mf->fd)) < INT_MAX && - (mf->addr = mf->size - ? mmap(NULL, mf->size, PROT_READ, MAP_SHARED, mf->fd, 0) - : NULL) != MAP_FAILED) { + (mf->addr = mf->size ? mmap(NULL, mf->size, PROT_READ, + MAP_PRIVATE | MAP_POPULATE, mf->fd, 0) + : NULL) != MAP_FAILED) { return 0; } else { unmapfile(mf); @@ -50,7 +50,7 @@ int mapfileread(const char *filename, struct MappedFile *mf) { int unmapfile(struct MappedFile *mf) { int rc; rc = 0; - if (mf->addr != MAP_FAILED) { + if (mf->addr && mf->addr != MAP_FAILED) { rc |= munmap(mf->addr, mf->size); mf->addr = MAP_FAILED; } diff --git a/libc/runtime/getstack.c b/libc/runtime/getstack.c new file mode 100644 index 000000000..15cc3ed7e --- /dev/null +++ b/libc/runtime/getstack.c @@ -0,0 +1,36 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/calls.h" +#include "libc/runtime/runtime.h" +#include "libc/sysv/consts/map.h" +#include "libc/sysv/consts/prot.h" + +/** + * Allocates deterministic stack for process. + * @see _executive() + */ +void *_getstack(void) { + char *p; + p = mmap((char *)0x700000000000 - STACKSIZE, STACKSIZE, + PROT_READ | PROT_WRITE, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, + 0); + if (p == MAP_FAILED) abort(); + return p + STACKSIZE; +} diff --git a/libc/runtime/memtrack.h b/libc/runtime/memtrack.h index d2c76c43d..64adb4536 100644 --- a/libc/runtime/memtrack.h +++ b/libc/runtime/memtrack.h @@ -12,8 +12,8 @@ struct MemoryIntervals { struct MemoryInterval { int x; int y; - } p[32]; - long h[32]; + } p[64]; + long h[64]; }; extern struct MemoryIntervals _mmi; diff --git a/libc/runtime/mmap.c b/libc/runtime/mmap.c index 0de46299f..bef9d55e1 100644 --- a/libc/runtime/mmap.c +++ b/libc/runtime/mmap.c @@ -44,6 +44,10 @@ struct MemoryIntervals _mmi; /** * Beseeches system for page-table entries. * + * char *p = mmap(NULL, 65536, PROT_READ | PROT_WRITE, + * MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + * munmap(p, 65536); + * * @param addr optionally requests a particular virtual base address, * which needs to be 64kb aligned if passed (for NT compatibility) * @param size must be >0 and needn't be a multiple of FRAMESIZE @@ -82,8 +86,6 @@ void *mmap(void *addr, size_t size, int prot, int flags, int fd, int64_t off) { } addr = (void *)(intptr_t)((int64_t)x << 16); } - assert((flags & MAP_FIXED) || - (!isheap(addr) && !isheap((char *)addr + size - 1))); dm = DirectMap(addr, size, prot, flags | MAP_FIXED, fd, off); if (dm.addr == MAP_FAILED || dm.addr != addr) { return MAP_FAILED; diff --git a/libc/runtime/msync-nt.c b/libc/runtime/msync-nt.c index db32f80ac..e646b85eb 100644 --- a/libc/runtime/msync-nt.c +++ b/libc/runtime/msync-nt.c @@ -25,16 +25,14 @@ textwindows int msync$nt(void *addr, size_t size, int flags) { int x, y, l, r, i; - if (!FlushViewOfFile(addr, size)) return winerr(); - x = (intptr_t)addr >> 16; - y = x + (ROUNDUP(size, 65536) >> 16) - 1; - l = FindMemoryInterval(&_mmi, x); - r = FindMemoryInterval(&_mmi, y); - if (l && x <= _mmi.p[l - 1].y) --l; - if (r && y <= _mmi.p[r - 1].y) --r; - if (l < _mmi.i) { - for (i = l; i <= r; --i) { - FlushFileBuffers(_mmi.h[i - 1]); + x = ROUNDDOWN((intptr_t)addr, FRAMESIZE) >> 16; + y = ROUNDDOWN((intptr_t)addr + size - 1, FRAMESIZE) >> 16; + for (i = FindMemoryInterval(&_mmi, x); i < _mmi.i; ++i) { + if ((x >= _mmi.p[i].x && x <= _mmi.p[i].y) || + (y >= _mmi.p[i].x && y <= _mmi.p[i].y)) { + FlushFileBuffers(_mmi.h[i]); + } else { + break; } } return 0; diff --git a/libc/runtime/runtime.h b/libc/runtime/runtime.h index da3919ee4..e0e0c3ac2 100644 --- a/libc/runtime/runtime.h +++ b/libc/runtime/runtime.h @@ -44,6 +44,7 @@ void exit(int) noreturn; void quick_exit(int) noreturn; void _exit(int) libcesque noreturn; void _Exit(int) libcesque noreturn; +long _setstack(void *, void *, ...); void abort(void) noreturn noinstrument; void panic(void) noreturn noinstrument privileged; void triplf(void) noreturn noinstrument privileged; diff --git a/libc/runtime/runtime.mk b/libc/runtime/runtime.mk index 0625f4b0d..bf08225ad 100644 --- a/libc/runtime/runtime.mk +++ b/libc/runtime/runtime.mk @@ -69,6 +69,8 @@ o/$(MODE)/libc/runtime/winmain.greg.o: \ DEFAULT_CPPFLAGS += \ -DSTACK_FRAME_UNLIMITED +o/$(MODE)/libc/runtime/main-gcc.asm: OVERRIDE_CFLAGS += -ffixed-r12 -ffixed-r13 -ffixed-r14 -ffixed-r15 + LIBC_RUNTIME_LIBS = $(foreach x,$(LIBC_RUNTIME_ARTIFACTS),$($(x))) LIBC_RUNTIME_SRCS = $(foreach x,$(LIBC_RUNTIME_ARTIFACTS),$($(x)_SRCS)) LIBC_RUNTIME_HDRS = $(foreach x,$(LIBC_RUNTIME_ARTIFACTS),$($(x)_HDRS)) diff --git a/libc/runtime/mergepages.S b/libc/runtime/setstack.S similarity index 78% rename from libc/runtime/mergepages.S rename to libc/runtime/setstack.S index 27deac848..f3d3f1341 100644 --- a/libc/runtime/mergepages.S +++ b/libc/runtime/setstack.S @@ -17,30 +17,30 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/dce.h" -#include "libc/sysv/consts/madv.h" -#include "libc/sysv/consts/nr.h" #include "libc/macros.h" -.text.startup -.source __FILE__ -/ Merges page table entries for pages with identical content. +/ Switches stack. / -/ This is a hint. It can help trim physical memory for things -/ like extremely sparse data. -/ -/ @param rdi is base address -/ @param rsi is byte length -mergepages: - .leafprologue - .profilable - mov $-PAGESIZE,%rax - and %rax,%rdi - and %rax,%rsi - mov __NR_madvise,%eax - mov MADV_MERGEABLE,%edx - test %edx,%edx - jz 1f - syscall -1: .leafepilogue - .endfn mergepages,globl +/ @param rdi is new rsp, passed as malloc(size) + size +/ @param rsi is function to call in new stack space +/ @param rdx,rcx,r8,r9 get passed as args to rsi +/ @return happens on original stack +_setstack: + push %rbp + mov %rsp,%rbp + push %rbx + mov %rsp,%rbx + mov %rdi,%rsp + push 16(%rbx) + push 8(%rbx) + mov %rsi,%rax + mov %rdx,%rdi + mov %rcx,%rsi + mov %r8,%rdx + mov %r9,%rcx + call *%rax + mov %rbx,%rsp + pop %rbx + pop %rbp + ret + .endfn _setstack,globl diff --git a/libc/runtime/winmain.greg.c b/libc/runtime/winmain.greg.c index 8165e93d4..7e6f6073a 100644 --- a/libc/runtime/winmain.greg.c +++ b/libc/runtime/winmain.greg.c @@ -19,15 +19,32 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/bits/bits.h" #include "libc/bits/pushpop.h" +#include "libc/bits/weaken.h" #include "libc/dce.h" #include "libc/nt/console.h" +#include "libc/nt/enum/consolemodeflags.h" +#include "libc/nt/enum/filetype.h" #include "libc/nt/enum/loadlibrarysearch.h" +#include "libc/nt/files.h" +#include "libc/nt/pedef.h" +#include "libc/nt/process.h" #include "libc/nt/runtime.h" +#include "libc/nt/struct/teb.h" #include "libc/runtime/getdosenviron.h" #include "libc/runtime/internal.h" #include "libc/runtime/missioncritical.h" +#include "libc/runtime/runtime.h" -static void LoadFasterAndPreventHijacking(void) { +static struct CmdExe { + bool result; + struct OldNtConsole { + unsigned codepage; + unsigned mode; + int64_t handle; + } oldin, oldout; +} g_cmdexe; + +static textwindows void MitigateDriveByDownloads(void) { unsigned wrote; if (!SetDefaultDllDirectories(kNtLoadLibrarySearchSearchSystem32)) { WriteFile(GetStdHandle(kNtStdErrorHandle), "nodll\n", 6, &wrote, NULL); @@ -35,6 +52,78 @@ static void LoadFasterAndPreventHijacking(void) { } } +static textwindows void RestoreCmdExe(void) { + if (g_cmdexe.oldin.handle) { + SetConsoleCP(g_cmdexe.oldin.codepage); + SetConsoleMode(g_cmdexe.oldin.handle, g_cmdexe.oldin.mode); + } + if (g_cmdexe.oldout.handle) { + SetConsoleOutputCP(g_cmdexe.oldout.codepage); + SetConsoleMode(g_cmdexe.oldout.handle, g_cmdexe.oldout.mode); + } +} + +static textwindows void SetTrueColor(void) { + SetEnvironmentVariable(u"TERM", u"xterm-truecolor"); +} + +static textwindows void NormalizeCmdExe(void) { + int64_t handle, hstdin, hstdout, hstderr; + if ((int)weakaddr("v_ntsubsystem") == kNtImageSubsystemWindowsCui && + NtGetVersion() >= kNtVersionWindows10) { + atexit(RestoreCmdExe); + hstdin = GetStdHandle(pushpop(kNtStdInputHandle)); + hstdout = GetStdHandle(pushpop(kNtStdOutputHandle)); + hstderr = GetStdHandle(pushpop(kNtStdErrorHandle)); + if (GetFileType((handle = hstdin)) == kNtFileTypeChar) { + SetTrueColor(); + g_cmdexe.oldin.handle = handle; + g_cmdexe.oldin.codepage = GetConsoleCP(); + SetConsoleCP(kNtCpUtf8); + GetConsoleMode(handle, &g_cmdexe.oldin.mode); + SetConsoleMode(handle, g_cmdexe.oldin.mode | kNtEnableProcessedInput | + kNtEnableVirtualTerminalInput); + } + if (GetFileType((handle = hstdout)) == kNtFileTypeChar || + GetFileType((handle = hstderr)) == kNtFileTypeChar) { + SetTrueColor(); + g_cmdexe.oldout.handle = handle; + g_cmdexe.oldout.codepage = GetConsoleOutputCP(); + SetConsoleOutputCP(kNtCpUtf8); + GetConsoleMode(handle, &g_cmdexe.oldout.mode); + SetConsoleMode(handle, g_cmdexe.oldout.mode | kNtEnableProcessedOutput | + kNtEnableVirtualTerminalProcessing); + } + } +} + +/** + * Main function on Windows NT. + * + * The Cosmopolitan Runtime provides the following services, which aim + * to bring Windows NT behavior closer in harmony with System Five: + * + * 1. We configure CMD.EXE for UTF-8 and enable ANSI colors on Win10. + * + * 2. Command line arguments are passed as a blob of UTF-16 text. We + * chop them up into an char *argv[] UTF-8 data structure, in + * accordance with the DOS conventions for argument quoting. + * + * 3. Environment variables are passed to us as a sorted UTF-16 double + * NUL terminated list. We translate this to char ** using UTF-8. + * + * 4. NT likes to choose a stack address that's beneath the program + * image. We want to be able to assume that stack addresses are + * located at higher addresses than heap and program memory. So the + * _executive() function will switch stacks appropriately. + * + * 5. Windows users are afraid of "drive-by downloads" where someone + * might accidentally an evil DLL to their Downloads folder which + * then overrides the behavior of a legitimate EXE being run from + * the downloads folder. Since we don't even use dynamic linking, + * we've cargo culted some API calls, that may harden against it. + * + */ textwindows int WinMain(void *hInstance, void *hPrevInstance, const char *lpCmdLine, int nCmdShow) { int i, count; @@ -42,7 +131,8 @@ textwindows int WinMain(void *hInstance, void *hPrevInstance, char *argarray[512], *envarray[512]; char argblock[ARG_MAX], envblock[ENV_MAX]; long auxarray[][2] = {{pushpop(0L), pushpop(0L)}}; - LoadFasterAndPreventHijacking(); + MitigateDriveByDownloads(); + NormalizeCmdExe(); *(/*unconst*/ int *)&hostos = WINDOWS; cmd16 = GetCommandLine(); env16 = GetEnvironmentStrings(); diff --git a/libc/stdio/favail.c b/libc/stdio/favail.c index 09f69d079..b8028a870 100644 --- a/libc/stdio/favail.c +++ b/libc/stdio/favail.c @@ -23,7 +23,5 @@ * Returns number of bytes available in stream buffer. */ unsigned favail(FILE *f) { - if (f->beg == f->end) return f->size; - if (f->end > f->beg) return f->end - f->beg; - return (f->size - f->beg) + f->end; + return ((f->end - f->beg - 1) & (f->size - 1)) + 1; } diff --git a/libc/stdio/fputs.c b/libc/stdio/fputs.c index e66fc0f88..124d206a1 100644 --- a/libc/stdio/fputs.c +++ b/libc/stdio/fputs.c @@ -17,8 +17,11 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/calls.h" #include "libc/errno.h" +#include "libc/macros.h" #include "libc/stdio/stdio.h" +#include "libc/str/str.h" /** * Writes string to stream. @@ -32,15 +35,14 @@ * @return strlen(s) or -1 w/ errno on error */ int fputs(const char *s, FILE *f) { - unsigned char *p = (unsigned char *)s; - int res = 0; - while (*p) { - if (fputc(*p++, f) == -1) { + int i, n, m; + n = strlen(s); + for (i = 0; i < n; ++i) { + if (fputc(s[i], f) == -1) { if (ferror(f) == EINTR) continue; if (feof(f)) errno = f->state = EPIPE; return -1; } - ++res; } - return ++res; + return n; } diff --git a/libc/stdio/stdio.mk b/libc/stdio/stdio.mk index b8c42622f..5517639d2 100644 --- a/libc/stdio/stdio.mk +++ b/libc/stdio/stdio.mk @@ -28,7 +28,6 @@ LIBC_STDIO_A_DIRECTDEPS = \ LIBC_ALG \ LIBC_BITS \ LIBC_CALLS \ - LIBC_CALLS_HEFTY \ LIBC_CONV \ LIBC_ESCAPE \ LIBC_FMT \ diff --git a/libc/str/chomp.c b/libc/str/chomp.c index 3b25df8de..0d27a21c6 100644 --- a/libc/str/chomp.c +++ b/libc/str/chomp.c @@ -26,6 +26,13 @@ * @see getline */ char *(chomp)(char *line) { - if (line) line[strcspn(line, "\r\n")] = '\0'; + size_t i; + for (i = strlen(line); i--;) { + if (line[i] == '\r' || line[i] == '\n') { + line[i] = '\0'; + } else { + break; + } + } return line; } diff --git a/libc/str/chomp16.c b/libc/str/chomp16.c index a12185d20..110dd824f 100644 --- a/libc/str/chomp16.c +++ b/libc/str/chomp16.c @@ -26,6 +26,6 @@ * @see getline */ char16_t *chomp16(char16_t *line) { - if (line) line[strcspn(line, u"\r\n")] = '\0'; + if (line) line[strcspn16(line, u"\r\n")] = '\0'; return line; } diff --git a/libc/str/str.h b/libc/str/str.h index 757cc4a4d..980512d5e 100644 --- a/libc/str/str.h +++ b/libc/str/str.h @@ -283,24 +283,6 @@ extern int (*const hook$wcsncmp)(const wchar_t *, const wchar_t *, size_t); #if __STDC_VERSION__ + 0 >= 201112 -#define strstr(haystack, needle) \ - _Generic(*(haystack), wchar_t \ - : wcsstr, char16_t \ - : strstr16, default \ - : strstr)(haystack, needle) - -#define strrchr(s, c) \ - _Generic(*(s), wchar_t \ - : wcsrchr, char16_t \ - : strrchr16, default \ - : strrchr)(s, c) - -#define strchrnul(s, c) \ - _Generic(*(s), wchar_t \ - : wcschrnul, char16_t \ - : strchrnul16, default \ - : strchrnul)(s, c) - #define strnlen(s, n) \ _Generic(*(s), wchar_t \ : wcsnlen, char16_t \ diff --git a/libc/str/strcspn.c b/libc/str/strcspn.c index d7f9bb210..89bc647ee 100644 --- a/libc/str/strcspn.c +++ b/libc/str/strcspn.c @@ -23,6 +23,9 @@ /** * Returns prefix length, consisting of chars not in reject. * a.k.a. Return index of first byte that's in charset. + * + * @param reject is nul-terminated character set + * @see strspn(), strtok_r() * @asyncsignalsafe */ size_t(strcspn)(const char *s, const char *reject) { diff --git a/libc/str/strstr.c b/libc/str/strstr.c index 2b523e1f7..ac49df170 100644 --- a/libc/str/strstr.c +++ b/libc/str/strstr.c @@ -35,13 +35,13 @@ char *(strstr)(const char *haystack, const char *needle) { if (needle[0]) { if (needle[1]) { - if (!((intptr_t)needle & 0xf) && X86_HAVE(SSE4_2) && !IsTiny()) { + if (!((intptr_t)needle & 0xf) && X86_HAVE(SSE4_2)) { return strstr$sse42(haystack, needle); } else { size_t needlelen; alignas(16) char needle2[64]; needlelen = strlen(needle); - if (needlelen < 64 && X86_HAVE(SSE4_2) && !IsTiny()) { + if (needlelen < 64 && X86_HAVE(SSE4_2)) { memcpy(needle2, needle, (needlelen + 1) * sizeof(char)); return strstr$sse42(haystack, needle2); } else { diff --git a/libc/str/wchomp.c b/libc/str/wchomp.c index 7348a4b1a..c33c073d4 100644 --- a/libc/str/wchomp.c +++ b/libc/str/wchomp.c @@ -26,6 +26,6 @@ * @see getline */ wchar_t *wchomp(wchar_t *line) { - if (line) line[strcspn(line, L"\r\n")] = '\0'; + if (line) line[wcscspn(line, L"\r\n")] = '\0'; return line; } diff --git a/libc/sysv/consts.sh b/libc/sysv/consts.sh index 45f209a28..c0183ac59 100755 --- a/libc/sysv/consts.sh +++ b/libc/sysv/consts.sh @@ -247,7 +247,7 @@ syscon mmap MAP_HUGE_MASK 63 0 0 0 0 syscon mmap MAP_HUGE_SHIFT 26 0 0 0 0 syscon mmap MAP_LOCKED 0x2000 0 0 0 0 syscon mmap MAP_NONBLOCK 0x010000 0 0 0 0 -syscon mmap MAP_POPULATE 0x8000 0 0 0 0 +syscon mmap MAP_POPULATE 0x8000 0 0 0 0 # can avoid madvise(MADV_WILLNEED) on private file mapping syscon mmap MAP_TYPE 15 0 0 0 0 # what is it syscon compat MAP_EXECUTABLE 0x1000 0 0 0 0 # ignored syscon compat MAP_DENYWRITE 0x0800 0 0 0 0 @@ -301,8 +301,8 @@ syscon mprot PROT_NONE 0 0 0 0 0 # unix consensus (nt needs special syscon mprot PROT_READ 1 1 1 1 1 # unix consensus syscon mprot PROT_WRITE 2 2 2 2 2 # unix consensus syscon mprot PROT_EXEC 4 4 4 4 4 # unix consensus -syscon mprot PROT_GROWSDOWN 0x01000000 0 0 0 0x01000000 # bsd consensus -syscon mprot PROT_GROWSUP 0x02000000 0 0 0 0 +syscon mprot PROT_GROWSDOWN 0x01000000 0 0 0 0 # intended for mprotect; see MAP_GROWSDOWN for mmap() (todo: what was 0x01000000 on nt) +syscon mprot PROT_GROWSUP 0x02000000 0 0 0 0 # intended for mprotect; see MAP_GROWSDOWN for mmap() syscon mremap MREMAP_MAYMOVE 1 1 1 1 1 # faked non-linux (b/c linux only) syscon mremap MREMAP_FIXED 2 2 2 2 2 # faked non-linux (b/c linux only) @@ -320,6 +320,28 @@ syscon access X_OK 1 1 1 1 0xa0000000 # unix consensus and kNtGener syscon access W_OK 2 2 2 2 0x40000000 # unix consensus and kNtGenericWrite syscon access R_OK 4 4 4 4 0x80000000 # unix consensus and kNtGenericRead +# flock() flags +# +# group name GNU/Systemd XNU's Not UNIX FreeBSD OpenBSD XENIX Commentary +syscon lock LOCK_SH 1 1 1 1 0 # shared [unix consensus] +syscon lock LOCK_EX 2 2 2 2 2 # exclusive [consensus!] +syscon lock LOCK_NB 4 4 4 4 1 # non-blocking [unix consensus] +syscon lock LOCK_UN 8 8 8 8 8 # unlock [unix consensus & faked NT] + +# waitpid() / wait4() options +# +# group name GNU/Systemd XNU's Not UNIX FreeBSD OpenBSD XENIX Commentary +syscon waitpid WNOHANG 1 1 1 1 0 # unix consensus +syscon waitpid WUNTRACED 2 2 2 2 0 # unix consensus +syscon waitpid WCONTINUED 8 0x10 4 8 0 + +# waitid() options +# +# group name GNU/Systemd XNU's Not UNIX FreeBSD OpenBSD XENIX Commentary +syscon waitid WEXITED 4 4 0x10 0 0 +syscon waitid WSTOPPED 2 8 2 0 0 +syscon waitid WNOWAIT 0x01000000 0x20 8 0 0 + # stat::st_mode constants # # group name GNU/Systemd XNU's Not UNIX FreeBSD OpenBSD XENIX Commentary @@ -1049,7 +1071,6 @@ syscon af AF_VSOCK 40 0 0 0 0 syscon af AF_KCM 41 0 0 0 0 syscon af AF_MAX 42 40 42 36 35 - syscon pf PF_UNIX 1 1 1 1 1 # consensus syscon pf PF_UNSPEC 0 0 0 0 0 # consensus syscon pf PF_APPLETALK 5 0x10 0x10 0x10 0x10 # bsd consensus @@ -1862,6 +1883,7 @@ syscon misc LC_MONETARY_MASK 0x10 0 4 8 0 syscon misc LC_MESSAGES_MASK 0x20 0 0x20 0x40 0 syscon misc LC_ALL_MASK 0x1fbf 0 63 126 0 +syscon lock LOCK_UNLOCK_CACHE 54 0 0 0 0 # wut syscon ptrace PTRACE_GETREGSET 0x4204 0 0 0 0 syscon ptrace PTRACE_GETSIGMASK 0x420a 0 0 0 0 @@ -1894,12 +1916,6 @@ syscon misc IP6F_MORE_FRAG 0x0100 0x0100 0x0100 0x0100 0x0100 # con syscon misc IP6F_OFF_MASK 0xf8ff 0xf8ff 0xf8ff 0xf8ff 0xf8ff # consensus syscon misc IP6F_RESERVED_MASK 0x0600 0x0600 0x0600 0x0600 0x0600 # consensus -syscon lock LOCK_SH 1 1 1 1 0 # unix consensus -syscon lock LOCK_EX 2 2 2 2 2 # consensus! -syscon lock LOCK_NB 4 4 4 4 1 # unix consensus -syscon lock LOCK_UN 8 8 8 8 8 # unix consensus & faked NT -syscon lock LOCK_UNLOCK_CACHE 54 0 0 0 0 - syscon misc NO_SENSE 0 0 0 0 0 # consensus syscon misc NO_ADDRESS 4 4 4 4 0x2afc # unix consensus syscon misc NO_DATA 4 4 4 4 0x2afc # unix consensus @@ -2025,9 +2041,9 @@ syscon misc RTLD_NOLOAD 4 0x10 0x2000 0 0 syscon misc RTLD_DI_LINKMAP 0 0 2 0 0 syscon misc RTLD_LOCAL 0 4 0 0 0 -syscon misc RUSAGE_SELF 0 0 0 0 0 # unix consensus & faked nt -syscon misc RUSAGE_CHILDREN -1 -1 -1 -1 99 # unix consensus & unavailable on nt -syscon misc RUSAGE_THREAD 1 99 1 1 1 # faked nt & unavailable on xnu +syscon rusage RUSAGE_SELF 0 0 0 0 0 # unix consensus & faked nt +syscon rusage RUSAGE_CHILDREN -1 -1 -1 -1 99 # unix consensus & unavailable on nt +syscon rusage RUSAGE_THREAD 1 99 1 1 1 # faked nt & unavailable on xnu syscon misc FSETLOCKING_QUERY 0 0 0 0 0 # consensus syscon misc FSETLOCKING_BYCALLER 2 0 0 0 0 @@ -2315,10 +2331,8 @@ syscon misc TUEXEC 0x40 0x40 0x40 0x40 0 # unix consensus syscon misc TUREAD 0x0100 0x0100 0x0100 0x0100 0 # unix consensus syscon misc TUWRITE 0x80 0x80 0x80 0x80 0 # unix consensus syscon misc TVERSLEN 2 2 2 2 0 # unix consensus -syscon misc WNOHANG 1 1 1 1 0 # unix consensus syscon misc WORD_BIT 0x20 0x20 0x20 0x20 0 # unix consensus syscon misc WRQ 2 2 2 2 0 # unix consensus -syscon misc WUNTRACED 2 2 2 2 0 # unix consensus syscon misc SIGEV_THREAD 2 3 2 0 0 syscon misc SIGEV_SIGNAL 0 1 1 0 0 syscon misc SIGEV_NONE 1 0 0 0 0 @@ -2757,10 +2771,6 @@ syscon misc TSS_DTOR_ITERATIONS 0 0 4 0 0 syscon misc TTY_NAME_MAX 0x20 0 0 260 0 syscon misc UIO_MAXIOV 0x0400 0 0 0x0400 0 syscon misc USER_PROCESS 7 7 4 0 0 -syscon misc WCONTINUED 8 0x10 4 8 0 -syscon misc WEXITED 4 4 0x10 0 0 -syscon misc WNOWAIT 0x01000000 0x20 8 0 0 -syscon misc WSTOPPED 2 8 2 0 0 syscon misc YESEXPR 0x050000 52 52 47 0 syscon misc YESSTR 0x050002 54 54 46 0 diff --git a/libc/sysv/consts/PROT_GROWSDOWN.s b/libc/sysv/consts/PROT_GROWSDOWN.s index 510b7ed80..97351e9b6 100644 --- a/libc/sysv/consts/PROT_GROWSDOWN.s +++ b/libc/sysv/consts/PROT_GROWSDOWN.s @@ -1,2 +1,2 @@ .include "libc/sysv/consts/syscon.inc" -.syscon mprot PROT_GROWSDOWN 0x01000000 0 0 0 0x01000000 +.syscon mprot PROT_GROWSDOWN 0x01000000 0 0 0 0 diff --git a/libc/sysv/consts/RUSAGE_CHILDREN.s b/libc/sysv/consts/RUSAGE_CHILDREN.s index d497cad0f..a25bc6701 100644 --- a/libc/sysv/consts/RUSAGE_CHILDREN.s +++ b/libc/sysv/consts/RUSAGE_CHILDREN.s @@ -1,2 +1,2 @@ .include "libc/sysv/consts/syscon.inc" -.syscon misc RUSAGE_CHILDREN -1 -1 -1 -1 99 +.syscon rusage RUSAGE_CHILDREN -1 -1 -1 -1 99 diff --git a/libc/sysv/consts/RUSAGE_SELF.s b/libc/sysv/consts/RUSAGE_SELF.s index 36a353cb5..b2ed1841a 100644 --- a/libc/sysv/consts/RUSAGE_SELF.s +++ b/libc/sysv/consts/RUSAGE_SELF.s @@ -1,2 +1,2 @@ .include "libc/sysv/consts/syscon.inc" -.syscon misc RUSAGE_SELF 0 0 0 0 0 +.syscon rusage RUSAGE_SELF 0 0 0 0 0 diff --git a/libc/sysv/consts/RUSAGE_THREAD.s b/libc/sysv/consts/RUSAGE_THREAD.s index 248fa9d04..2d007212c 100644 --- a/libc/sysv/consts/RUSAGE_THREAD.s +++ b/libc/sysv/consts/RUSAGE_THREAD.s @@ -1,2 +1,2 @@ .include "libc/sysv/consts/syscon.inc" -.syscon misc RUSAGE_THREAD 1 99 1 1 1 +.syscon rusage RUSAGE_THREAD 1 99 1 1 1 diff --git a/libc/sysv/consts/WCONTINUED.s b/libc/sysv/consts/WCONTINUED.s index b1023eb2d..c7abe517f 100644 --- a/libc/sysv/consts/WCONTINUED.s +++ b/libc/sysv/consts/WCONTINUED.s @@ -1,2 +1,2 @@ .include "libc/sysv/consts/syscon.inc" -.syscon misc WCONTINUED 8 0x10 4 8 0 +.syscon waitpid WCONTINUED 8 0x10 4 8 0 diff --git a/libc/sysv/consts/WEXITED.s b/libc/sysv/consts/WEXITED.s index 3e0360dc7..0e66ce7f8 100644 --- a/libc/sysv/consts/WEXITED.s +++ b/libc/sysv/consts/WEXITED.s @@ -1,2 +1,2 @@ .include "libc/sysv/consts/syscon.inc" -.syscon misc WEXITED 4 4 0x10 0 0 +.syscon waitid WEXITED 4 4 0x10 0 0 diff --git a/libc/sysv/consts/WNOHANG.s b/libc/sysv/consts/WNOHANG.s index 9be7b4289..8ac8b014b 100644 --- a/libc/sysv/consts/WNOHANG.s +++ b/libc/sysv/consts/WNOHANG.s @@ -1,2 +1,2 @@ .include "libc/sysv/consts/syscon.inc" -.syscon misc WNOHANG 1 1 1 1 0 +.syscon waitpid WNOHANG 1 1 1 1 0 diff --git a/libc/sysv/consts/WNOWAIT.s b/libc/sysv/consts/WNOWAIT.s index e3a4dddbe..a012a1e1d 100644 --- a/libc/sysv/consts/WNOWAIT.s +++ b/libc/sysv/consts/WNOWAIT.s @@ -1,2 +1,2 @@ .include "libc/sysv/consts/syscon.inc" -.syscon misc WNOWAIT 0x01000000 0x20 8 0 0 +.syscon waitid WNOWAIT 0x01000000 0x20 8 0 0 diff --git a/libc/sysv/consts/WSTOPPED.s b/libc/sysv/consts/WSTOPPED.s index b9a418348..6bcf8b870 100644 --- a/libc/sysv/consts/WSTOPPED.s +++ b/libc/sysv/consts/WSTOPPED.s @@ -1,2 +1,2 @@ .include "libc/sysv/consts/syscon.inc" -.syscon misc WSTOPPED 2 8 2 0 0 +.syscon waitid WSTOPPED 2 8 2 0 0 diff --git a/libc/sysv/consts/WUNTRACED.s b/libc/sysv/consts/WUNTRACED.s index 4f8480c6f..f1b19eadc 100644 --- a/libc/sysv/consts/WUNTRACED.s +++ b/libc/sysv/consts/WUNTRACED.s @@ -1,2 +1,2 @@ .include "libc/sysv/consts/syscon.inc" -.syscon misc WUNTRACED 2 2 2 2 0 +.syscon waitpid WUNTRACED 2 2 2 2 0 diff --git a/libc/sysv/consts/map.h b/libc/sysv/consts/map.h index 0c07414d3..5c3681e9d 100644 --- a/libc/sysv/consts/map.h +++ b/libc/sysv/consts/map.h @@ -27,10 +27,10 @@ hidden extern const long MAP_TYPE; COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ -#define MAP_FILE LITERALLY(0) -#define MAP_SHARED LITERALLY(1) -#define MAP_PRIVATE LITERALLY(2) -#define MAP_FIXED LITERALLY(16) +#define MAP_FILE 0 +#define MAP_SHARED 1 +#define MAP_PRIVATE 2 +#define MAP_FIXED 16 #define MAP_32BIT SYMBOLIC(MAP_32BIT) #define MAP_ANONYMOUS SYMBOLIC(MAP_ANONYMOUS) diff --git a/libc/sysv/consts/w.h b/libc/sysv/consts/w.h index d3b6a4005..7858e7629 100644 --- a/libc/sysv/consts/w.h +++ b/libc/sysv/consts/w.h @@ -2,14 +2,16 @@ #define COSMOPOLITAN_LIBC_SYSV_CONSTS_W_H_ #include "libc/runtime/symbolic.h" -#define WNOHANG SYMBOLIC(WNOHANG) -#define WUNTRACED SYMBOLIC(WUNTRACED) +#define WNOHANG SYMBOLIC(WNOHANG) +#define WUNTRACED SYMBOLIC(WUNTRACED) +#define WCONTINUED SYMBOLIC(WCONTINUED) #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ hidden extern const long WNOHANG; hidden extern const long WUNTRACED; +hidden extern const long WCONTINUED; COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/x/xiso8601.c b/libc/time/xiso8601.c similarity index 97% rename from libc/x/xiso8601.c rename to libc/time/xiso8601.c index 675d38fff..69f730b88 100644 --- a/libc/x/xiso8601.c +++ b/libc/time/xiso8601.c @@ -35,6 +35,7 @@ STATIC_YOINK("ntoa"); */ static char *xiso8601$impl(struct timespec *opt_ts, int sswidth) { + char *p; struct tm tm; struct timespec ts; int64_t sec, subsec; @@ -60,7 +61,8 @@ static char *xiso8601$impl(struct timespec *opt_ts, int sswidth) { localtime_r(&sec, &tm); strftime(timebuf, sizeof(timebuf), "%Y-%m-%dT%H:%M:%S", &tm); strftime(zonebuf, sizeof(zonebuf), "%z", &tm); - return (xasprintf)("%s.%0*ld%s", timebuf, sswidth, subsec, zonebuf); + (asprintf)(&p, "%s.%0*ld%s", timebuf, sswidth, subsec, zonebuf); + return p; } /** diff --git a/libc/tinymath/atan2.S b/libc/tinymath/atan2.S index 44e38d891..156c72b17 100644 --- a/libc/tinymath/atan2.S +++ b/libc/tinymath/atan2.S @@ -28,6 +28,6 @@ / @note the greatest of all libm functions tinymath_atan2: ezlea tinymath_atan2l,ax - jmp _f2ld2 + jmp _d2ld2 .endfn tinymath_atan2,globl .alias tinymath_atan2,atan2 diff --git a/libc/unicode/kcombiningchars.S b/libc/unicode/kcombiningchars.S index b05c5e58b..8e46defc7 100644 --- a/libc/unicode/kcombiningchars.S +++ b/libc/unicode/kcombiningchars.S @@ -43,31 +43,6 @@ kCombiningChars: add $1208,%rsi .init.end 400,_init_kCombiningChars -/ The data below is sparse, as evidenced by: -/ o/tool/viz/bing.com 2 * 1024 * 1024) { + fadvise(fd, 0, size, MADV_SEQUENTIAL); + } + for (i = 0; i < size; i += wrote) { + TryAgain: + if ((rc = pwrite(fd, p + i, size - i, i)) != -1) { + wrote = rc; + } else if (errno == EINTR) { + goto TryAgain; + } else { + res = -1; + break; + } + } + } else { + res = -1; + } + close(fd); + } else { + res = -1; + } + return res; +} diff --git a/libc/x/xcalloc.c b/libc/x/xcalloc.c index 99df2901d..e08424bb7 100644 --- a/libc/x/xcalloc.c +++ b/libc/x/xcalloc.c @@ -26,6 +26,6 @@ */ void *xcalloc(size_t count, size_t size) { void *res = calloc(count, size); - if (!res) die(); + if (!res) xdie(); return res; } diff --git a/libc/x/xdie.c b/libc/x/xdie.c new file mode 100644 index 000000000..6b56be035 --- /dev/null +++ b/libc/x/xdie.c @@ -0,0 +1,28 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/bits/weaken.h" +#include "libc/log/log.h" +#include "libc/runtime/runtime.h" +#include "libc/x/x.h" + +void xdie(void) { + if (weaken(die)) die(); + abort(); +} diff --git a/libc/x/xjoinpaths.c b/libc/x/xjoinpaths.c new file mode 100644 index 000000000..7c6ed87a5 --- /dev/null +++ b/libc/x/xjoinpaths.c @@ -0,0 +1,37 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/bits/safemacros.h" +#include "libc/str/str.h" +#include "libc/x/x.h" + +/** + * Joins paths. + */ +char *xjoinpaths(const char *path, const char *other) { + if (!*other) { + return xstrdup(path); + } else if (startswith(other, "/") || strcmp(path, ".") == 0) { + return xstrdup(other); + } else if (endswith(other, "/")) { + return xstrcat(path, other); + } else { + return xstrcat(path, '/', other); + } +} diff --git a/libc/x/xmalloc.c b/libc/x/xmalloc.c index 07672e07e..dfed292dd 100644 --- a/libc/x/xmalloc.c +++ b/libc/x/xmalloc.c @@ -26,6 +26,6 @@ */ void *xmalloc(size_t bytes) { void *res = malloc(bytes); - if (!res) die(); + if (!res) xdie(); return res; } diff --git a/libc/x/xmemalign.c b/libc/x/xmemalign.c index 0c3833c30..d2943bbd8 100644 --- a/libc/x/xmemalign.c +++ b/libc/x/xmemalign.c @@ -26,6 +26,6 @@ */ void *xmemalign(size_t alignment, size_t bytes) { void *res = memalign(alignment, bytes); - if (!res) die(); + if (!res) xdie(); return res; } diff --git a/libc/x/xrealloc.c b/libc/x/xrealloc.c index 49ef11afe..9bdfefaf5 100644 --- a/libc/x/xrealloc.c +++ b/libc/x/xrealloc.c @@ -29,6 +29,6 @@ */ void *xrealloc(void *p1, size_t newsize) { void *p2 = realloc(p1, newsize); - if (!p2) die(); + if (!p2) xdie(); return p2; } diff --git a/libc/calls/hefty/slurp.c b/libc/x/xslurp.c similarity index 61% rename from libc/calls/hefty/slurp.c rename to libc/x/xslurp.c index d8e7454f5..636f2316f 100644 --- a/libc/calls/hefty/slurp.c +++ b/libc/x/xslurp.c @@ -17,73 +17,56 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/alg/arraylist2.h" -#include "libc/bits/safemacros.h" #include "libc/calls/calls.h" #include "libc/calls/struct/stat.h" #include "libc/errno.h" #include "libc/mem/mem.h" -#include "libc/str/str.h" +#include "libc/runtime/runtime.h" #include "libc/sysv/consts/madv.h" #include "libc/sysv/consts/o.h" -static size_t getfilesize(int fd) { - struct stat st; - st.st_size = 0; - fstat(fd, &st); - return st.st_size; -} - -static size_t smudgefilesize(size_t size) { - return roundup(size, PAGESIZE) + PAGESIZE; -} - /** * Reads entire file into memory. * - * This function is fantastic for being lazy. It may waste space if the - * file is large, but it's implemented in a conscientious way that won't - * bomb the systemwide page cache. It means performance too, naturally. - * - * @return contents which must be free()'d or NULL w/ errno + * @return NUL-terminated malloc'd contents, or NULL w/ errno + * @note this is uninterruptible */ -char *slurp(const char *path, size_t *opt_out_readlength) { +char *xslurp(const char *path, size_t *opt_out_size) { int fd; ssize_t rc; + size_t i, got; char *res, *p; - size_t i, n, got, want; + struct stat st; res = NULL; - if ((fd = open(path, O_RDONLY)) == -1) goto Failure; - n = getfilesize(fd); - /* TODO(jart): Fix this, it's totally broken */ - if (!(res = valloc(smudgefilesize(n)))) goto Failure; - if (n > FRAMESIZE) fadvise(fd, 0, n, MADV_SEQUENTIAL); - i = 0; - for (;;) { - want = smudgefilesize(n - i); - TryAgain: - if ((rc = read(fd, &res[i], want)) == -1) { - if (errno == EINTR) goto TryAgain; - goto Failure; - } - got = (size_t)rc; - if (i + 1 >= n) { - if ((p = realloc(res, smudgefilesize((n += n >> 1))))) { - res = p; - } else { - goto Failure; + if ((fd = open(path, O_RDONLY)) != -1) { + if (fstat(fd, &st) != -1 && (res = valloc(st.st_size))) { + if (st.st_size > 2 * 1024 * 1024) { + fadvise(fd, 0, st.st_size, MADV_SEQUENTIAL); + } + for (i = 0; i < st.st_size; i += got) { + TryAgain: + if ((rc = pread(fd, res + i, st.st_size - i, i)) != -1) { + if (!(got = rc)) { + if (fstat(fd, &st) == -1) { + abort(); + } + } + } else if (errno == EINTR) { + goto TryAgain; + } else { + free(res); + res = NULL; + break; + } + } + if (res) { + if (opt_out_size) { + *opt_out_size = st.st_size; + } + res[i] = '\0'; } } - i += got; - if (got == 0) break; - if (got > want) abort(); + close(fd); } - if (opt_out_readlength) *opt_out_readlength = i; - res[i] = '\0'; - close(fd); return res; -Failure: - free(res); - close(fd); - return NULL; } diff --git a/libc/x/xstrdup.c b/libc/x/xstrdup.c index d65f05b10..0700078eb 100644 --- a/libc/x/xstrdup.c +++ b/libc/x/xstrdup.c @@ -27,6 +27,6 @@ */ char *xstrdup(const char *s) { void *res = strdup(s); - if (!res) die(); + if (!res) xdie(); return res; } diff --git a/libc/x/xstrmul.c b/libc/x/xstrmul.c index 651ccc55b..c0bc83852 100644 --- a/libc/x/xstrmul.c +++ b/libc/x/xstrmul.c @@ -21,6 +21,9 @@ #include "libc/str/str.h" #include "libc/x/x.h" +/** + * Multiplies string. + */ char *xstrmul(const char *s, size_t n) { char *p; size_t i, m, size; diff --git a/libc/x/xstrndup.c b/libc/x/xstrndup.c index 4927a6eef..3685025c7 100644 --- a/libc/x/xstrndup.c +++ b/libc/x/xstrndup.c @@ -31,6 +31,6 @@ */ char *xstrndup(const char *s, size_t n) { void *res = strndup(s, n); - if (!res) die(); + if (!res) xdie(); return res; } diff --git a/libc/x/xvalloc.c b/libc/x/xvalloc.c index d932a1003..ace7c6bf4 100644 --- a/libc/x/xvalloc.c +++ b/libc/x/xvalloc.c @@ -26,6 +26,6 @@ */ void *xvalloc(size_t size) { void *res = valloc(size); - if (!res) die(); + if (!res) xdie(); return res; } diff --git a/libc/x/xvasprintf.c b/libc/x/xvasprintf.c index 8fa594115..6308087d5 100644 --- a/libc/x/xvasprintf.c +++ b/libc/x/xvasprintf.c @@ -29,6 +29,6 @@ */ char *(xvasprintf)(const char *fmt, va_list va) { char *buf; - if ((vasprintf)(&buf, fmt, va) == -1) die(); + if ((vasprintf)(&buf, fmt, va) == -1) xdie(); return buf; } diff --git a/libc/zip.h b/libc/zip.h index 4b118e601..41f989823 100644 --- a/libc/zip.h +++ b/libc/zip.h @@ -81,41 +81,41 @@ #if !(__ASSEMBLER__ + __LINKER__ + 0) /* end of central directory record */ -#define ZIP_CDIR_MAGIC(P) read32le(P) -#define ZIP_CDIR_DISK(P) read16le((P) + 4) -#define ZIP_CDIR_STARTINGDISK(P) read16le((P) + 6) -#define ZIP_CDIR_RECORDSONDISK(P) read16le((P) + 8) -#define ZIP_CDIR_RECORDS(P) read16le((P) + 10) -#define ZIP_CDIR_SIZE(P) read32le((P) + 12) -#define ZIP_CDIR_OFFSET(P) read32le((P) + 16) -#define ZIP_CDIR_COMMENTSIZE(P) read16le((P) + 20) +#define ZIP_CDIR_MAGIC(P) READ32LE(P) +#define ZIP_CDIR_DISK(P) READ16LE((P) + 4) +#define ZIP_CDIR_STARTINGDISK(P) READ16LE((P) + 6) +#define ZIP_CDIR_RECORDSONDISK(P) READ16LE((P) + 8) +#define ZIP_CDIR_RECORDS(P) READ16LE((P) + 10) +#define ZIP_CDIR_SIZE(P) READ32LE((P) + 12) +#define ZIP_CDIR_OFFSET(P) READ32LE((P) + 16) +#define ZIP_CDIR_COMMENTSIZE(P) READ16LE((P) + 20) #define ZIP_CDIR_COMMENT(P) (&(P)[22]) #define ZIP_CDIR_HDRSIZE(P) (ZIP_CDIR_COMMENTSIZE(P) + kZipCdirHdrMinSize) /* central directory file header */ -#define ZIP_CFILE_MAGIC(P) read32le(P) +#define ZIP_CFILE_MAGIC(P) READ32LE(P) #define ZIP_CFILE_VERSIONMADE(P) ((P)[4]) #define ZIP_CFILE_FILEATTRCOMPAT(P) ((P)[5]) #define ZIP_CFILE_VERSIONNEED(P) ((P)[6]) #define ZIP_CFILE_OSNEED(P) ((P)[7]) -#define ZIP_CFILE_GENERALFLAG(P) read16le((P) + kZipCfileOffsetGeneralflag) +#define ZIP_CFILE_GENERALFLAG(P) READ16LE((P) + kZipCfileOffsetGeneralflag) #define ZIP_CFILE_COMPRESSIONMETHOD(P) \ - read16le((P) + kZipCfileOffsetCompressionmethod) + READ16LE((P) + kZipCfileOffsetCompressionmethod) #define ZIP_CFILE_LASTMODIFIEDTIME(P) \ - read16le((P) + kZipCfileOffsetLastmodifiedtime) /* @see DOS_TIME() */ + READ16LE((P) + kZipCfileOffsetLastmodifiedtime) /* @see DOS_TIME() */ #define ZIP_CFILE_LASTMODIFIEDDATE(P) \ - read16le((P) + kZipCfileOffsetLastmodifieddate) /* @see DOS_DATE() */ -#define ZIP_CFILE_CRC32(P) read32le((P) + kZipCfileOffsetCrc32) + READ16LE((P) + kZipCfileOffsetLastmodifieddate) /* @see DOS_DATE() */ +#define ZIP_CFILE_CRC32(P) READ32LE((P) + kZipCfileOffsetCrc32) #define ZIP_CFILE_COMPRESSEDSIZE(P) READ32LE(P + kZipCfileOffsetCompressedsize) -#define ZIP_CFILE_UNCOMPRESSEDSIZE(P) read32le((P) + 24) -#define ZIP_CFILE_NAMESIZE(P) read16le((P) + 28) -#define ZIP_CFILE_EXTRASIZE(P) read16le((P) + 30) -#define ZIP_CFILE_COMMENTSIZE(P) read16le((P) + 32) -#define ZIP_CFILE_DISK(P) read16le((P) + 34) -#define ZIP_CFILE_INTERNALATTRIBUTES(P) read16le((P) + 36) +#define ZIP_CFILE_UNCOMPRESSEDSIZE(P) READ32LE((P) + 24) +#define ZIP_CFILE_NAMESIZE(P) READ16LE((P) + 28) +#define ZIP_CFILE_EXTRASIZE(P) READ16LE((P) + 30) +#define ZIP_CFILE_COMMENTSIZE(P) READ16LE((P) + 32) +#define ZIP_CFILE_DISK(P) READ16LE((P) + 34) +#define ZIP_CFILE_INTERNALATTRIBUTES(P) READ16LE((P) + 36) #define ZIP_CFILE_EXTERNALATTRIBUTES(P) \ - read32le((P) + kZipCfileOffsetExternalattributes) -#define ZIP_CFILE_OFFSET(P) read32le((P) + kZipCfileOffsetOffset) + READ32LE((P) + kZipCfileOffsetExternalattributes) +#define ZIP_CFILE_OFFSET(P) READ32LE((P) + kZipCfileOffsetOffset) #define ZIP_CFILE_NAME(P) ((const char *)(&(P)[46])) /* not nul-terminated */ #define ZIP_CFILE_EXTRA(P) (&(P)[46 + ZIP_CFILE_NAMESIZE(P)]) #define ZIP_CFILE_COMMENT(P) \ @@ -125,22 +125,22 @@ kZipCfileHdrMinSize) /* central directory file header */ -#define ZIP_LFILE_MAGIC(P) read32le(P) +#define ZIP_LFILE_MAGIC(P) READ32LE(P) #define ZIP_LFILE_VERSIONNEED(P) ((P)[4]) #define ZIP_LFILE_OSNEED(P) ((P)[5]) -#define ZIP_LFILE_GENERALFLAG(P) read16le((P) + kZipLfileOffsetGeneralflag) +#define ZIP_LFILE_GENERALFLAG(P) READ16LE((P) + kZipLfileOffsetGeneralflag) #define ZIP_LFILE_COMPRESSIONMETHOD(P) \ - read16le((P) + kZipLfileOffsetCompressionmethod) + READ16LE((P) + kZipLfileOffsetCompressionmethod) #define ZIP_LFILE_LASTMODIFIEDTIME(P) \ - read16le((P) + kZipLfileOffsetLastmodifiedtime) /* @see DOS_TIME() */ + READ16LE((P) + kZipLfileOffsetLastmodifiedtime) /* @see DOS_TIME() */ #define ZIP_LFILE_LASTMODIFIEDDATE(P) \ - read16le((P) + kZipLfileOffsetLastmodifieddate) /* @see DOS_DATE() */ -#define ZIP_LFILE_CRC32(P) read32le((P) + kZipLfileOffsetCrc32) + READ16LE((P) + kZipLfileOffsetLastmodifieddate) /* @see DOS_DATE() */ +#define ZIP_LFILE_CRC32(P) READ32LE((P) + kZipLfileOffsetCrc32) #define ZIP_LFILE_COMPRESSEDSIZE(P) \ - read32le((P) + kZipLfileOffsetCompressedsize) -#define ZIP_LFILE_UNCOMPRESSEDSIZE(P) read32le((P) + 22) -#define ZIP_LFILE_NAMESIZE(P) read16le((P) + 26) -#define ZIP_LFILE_EXTRASIZE(P) read16le((P) + 28) + READ32LE((P) + kZipLfileOffsetCompressedsize) +#define ZIP_LFILE_UNCOMPRESSEDSIZE(P) READ32LE((P) + 22) +#define ZIP_LFILE_NAMESIZE(P) READ16LE((P) + 26) +#define ZIP_LFILE_EXTRASIZE(P) READ16LE((P) + 28) #define ZIP_LFILE_NAME(P) ((const char *)(&(P)[30])) #define ZIP_LFILE_EXTRA(P) (&(P)[30 + ZIP_LFILE_NAMESIZE(P)]) #define ZIP_LFILE_HDRSIZE(P) \ @@ -148,8 +148,8 @@ #define ZIP_LFILE_CONTENT(P) ((P) + ZIP_LFILE_HDRSIZE(P)) #define ZIP_LFILE_SIZE(P) (ZIP_LFILE_HDRSIZE(P) + ZIP_LFILE_COMPRESSEDSIZE(P)) -#define ZIP_EXTRA_HEADERID(P) read16le(P) -#define ZIP_EXTRA_CONTENTSIZE(P) read16le((P) + 2) +#define ZIP_EXTRA_HEADERID(P) READ16LE(P) +#define ZIP_EXTRA_CONTENTSIZE(P) READ16LE((P) + 2) #define ZIP_EXTRA_CONTENT(P) (&(P)[4]) #define ZIP_EXTRA_SIZE(P) (ZIP_EXTRA_CONTENTSIZE(P) + kZipExtraHdrSize) diff --git a/test/dsp/scale/magikarp_test.c b/test/dsp/scale/magikarp_test.c index 39ca33db4..29f3c7ef1 100644 --- a/test/dsp/scale/magikarp_test.c +++ b/test/dsp/scale/magikarp_test.c @@ -17,9 +17,14 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "dsp/core/q.h" #include "dsp/scale/cdecimate2xuint8x8.h" #include "dsp/scale/scale.h" +#include "libc/assert.h" #include "libc/fmt/bing.h" +#include "libc/log/check.h" +#include "libc/math.h" +#include "libc/mem/mem.h" #include "libc/nexgen32e/x86feature.h" #include "libc/str/str.h" #include "libc/testlib/ezbench.h" diff --git a/test/libc/calls/fallocate_test.c b/test/libc/calls/fallocate_test.c index a4b499785..05cd71ea3 100644 --- a/test/libc/calls/fallocate_test.c +++ b/test/libc/calls/fallocate_test.c @@ -18,13 +18,13 @@ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/bits/safemacros.h" +#include "libc/calls/calls.h" +#include "libc/calls/struct/stat.h" #include "libc/dce.h" #include "libc/errno.h" #include "libc/runtime/gc.h" #include "libc/runtime/runtime.h" #include "libc/stdio/stdio.h" -#include "libc/calls/struct/stat.h" -#include "libc/calls/calls.h" #include "libc/testlib/testlib.h" #include "libc/x/x.h" @@ -56,7 +56,7 @@ TEST(fallocate_020, test) { ASSERT_NE(-1, close(fd)); ASSERT_NE(-1, stat(path, &st)); ASSERT_EQ(31337, st.st_size); - ASSERT_BINEQ(u"helloworld", gc(slurp(path, NULL))); + ASSERT_BINEQ(u"helloworld", gc(xslurp(path, NULL))); unlink(path); } diff --git a/test/libc/calls/ftruncate_test.c b/test/libc/calls/ftruncate_test.c index 15783334d..e26517643 100644 --- a/test/libc/calls/ftruncate_test.c +++ b/test/libc/calls/ftruncate_test.c @@ -17,13 +17,13 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/calls.h" +#include "libc/calls/struct/stat.h" #include "libc/dce.h" #include "libc/errno.h" #include "libc/runtime/gc.h" #include "libc/runtime/runtime.h" #include "libc/stdio/stdio.h" -#include "libc/calls/struct/stat.h" -#include "libc/calls/calls.h" #include "libc/testlib/testlib.h" #include "libc/x/x.h" @@ -44,6 +44,6 @@ TEST(ftruncate, test) { ASSERT_NE(-1, close(fd)); ASSERT_NE(-1, stat(path, &st)); ASSERT_EQ(31337, st.st_size); - ASSERT_BINEQ(u"helloworld", gc(slurp(path, NULL))); + ASSERT_BINEQ(u"helloworld", gc(xslurp(path, 0))); unlink(path); } diff --git a/test/libc/conv/itoa64radix10_test.c b/test/libc/conv/itoa64radix10_test.c index 38c494e68..abfb167ff 100644 --- a/test/libc/conv/itoa64radix10_test.c +++ b/test/libc/conv/itoa64radix10_test.c @@ -26,6 +26,10 @@ TEST(int64toarray_radix10, test) { char buf[21]; EXPECT_EQ(1, int64toarray_radix10(0, buf)); EXPECT_STREQ("0", buf); + EXPECT_EQ(1, int64toarray_radix10(1, buf)); + EXPECT_STREQ("1", buf); + EXPECT_EQ(2, int64toarray_radix10(-1, buf)); + EXPECT_STREQ("-1", buf); EXPECT_EQ(19, int64toarray_radix10(INT64_MAX, buf)); EXPECT_STREQ("9223372036854775807", buf); EXPECT_EQ(20, int64toarray_radix10(INT64_MIN, buf)); diff --git a/test/libc/fmt/palandprintf_test.c b/test/libc/fmt/palandprintf_test.c index ad9a26e39..2c562a9ef 100644 --- a/test/libc/fmt/palandprintf_test.c +++ b/test/libc/fmt/palandprintf_test.c @@ -29,6 +29,7 @@ #include "libc/bits/safemacros.h" #include "libc/conv/itoa.h" #include "libc/errno.h" +#include "libc/escape/escape.h" #include "libc/fmt/fmt.h" #include "libc/limits.h" #include "libc/math.h" @@ -671,6 +672,12 @@ TEST(snprintf, twosBaneWithTypePromotion) { EXPECT_STREQ("-32768", Format("%hd", x)); } +TEST(snprintf, formatStringLiteral) { + EXPECT_EQ('\\' | 'n' << 8, cescapec('\n')); + EXPECT_EQ('\\' | '3' << 8 | '7' << 16 | '7' << 24, cescapec('\377')); + EXPECT_STREQ("\"hi\\n\"", Format("%`'s", "hi\n")); +} + BENCH(palandprintf, bench) { EZBENCH2("snprintf %x", donothing, Format("%x", VEIL("r", INT_MIN))); EZBENCH2("snprintf %d", donothing, Format("%d", VEIL("r", INT_MIN))); diff --git a/test/libc/math/atan2l_test.c b/test/libc/math/atan2l_test.c new file mode 100644 index 000000000..c368bc263 --- /dev/null +++ b/test/libc/math/atan2l_test.c @@ -0,0 +1,31 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/math/math.h" +#include "libc/runtime/gc.h" +#include "libc/testlib/testlib.h" +#include "libc/x/x.h" + +TEST(atan2l, test) { + volatile double a = -.9816175436063843; + volatile double b = -.1908585813741899; + EXPECT_STREQ("-2.95", gc(xasprintf("%.2f", atan2f(b, a)))); + EXPECT_STREQ("-2.95", gc(xasprintf("%.2f", atan2(b, a)))); + EXPECT_STREQ("-2.95", gc(xasprintf("%.2Lf", atan2l(b, a)))); +} diff --git a/test/libc/runtime/mmap_test.c b/test/libc/runtime/mmap_test.c index 8680bb67c..0d3c47978 100644 --- a/test/libc/runtime/mmap_test.c +++ b/test/libc/runtime/mmap_test.c @@ -21,9 +21,11 @@ #include "libc/bits/xchg.h" #include "libc/calls/calls.h" #include "libc/fmt/fmt.h" +#include "libc/log/log.h" #include "libc/mem/mem.h" #include "libc/runtime/gc.h" #include "libc/runtime/memtrack.h" +#include "libc/runtime/missioncritical.h" #include "libc/runtime/runtime.h" #include "libc/stdio/stdio.h" #include "libc/str/str.h" @@ -106,5 +108,6 @@ TEST(isheap, emptyMalloc) { } TEST(isheap, mallocOffset) { - ASSERT_TRUE(isheap((char *)gc(malloc(131072)) + 100000)); + char *p = gc(malloc(131072)); + ASSERT_TRUE(isheap(p + 100000)); } diff --git a/test/libc/stdio/favail_test.c b/test/libc/stdio/favail_test.c new file mode 100644 index 000000000..ce3a1bba7 --- /dev/null +++ b/test/libc/stdio/favail_test.c @@ -0,0 +1,45 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/assert.h" +#include "libc/bits/popcnt.h" +#include "libc/stdio/stdio.h" +#include "libc/testlib/testlib.h" + +unsigned naive(unsigned beg, unsigned end, unsigned size) { + assert(end < size); + assert(beg < size); + assert(popcnt(size) == 1); + if (beg == end) return size; + if (end > beg) return end - beg; + return (size - beg) + end; +} + +unsigned fancy(unsigned beg, unsigned end, unsigned size) { + return ((end - beg - 1) & (size - 1)) + 1; +} + +TEST(favail, test) { + unsigned i, j, n = 4; + for (i = 0; i < n; ++i) { + for (j = 0; j < n; ++j) { + ASSERT_EQ(naive(i, j, n), fancy(i, j, n), "%u %u %u", i, j, n); + } + } +} diff --git a/test/libc/stdio/test.mk b/test/libc/stdio/test.mk index 2089ff5d6..a6de40a33 100644 --- a/test/libc/stdio/test.mk +++ b/test/libc/stdio/test.mk @@ -24,6 +24,7 @@ TEST_LIBC_STDIO_CHECKS = \ $(TEST_LIBC_STDIO_SRCS_TEST:%.c=o/$(MODE)/%.com.runs) TEST_LIBC_STDIO_DIRECTDEPS = \ + LIBC_BITS \ LIBC_CALLS \ LIBC_CALLS_HEFTY \ LIBC_FMT \ diff --git a/test/libc/str/memcpy_test.c b/test/libc/str/memcpy_test.c index f2fc631d7..3118350ff 100644 --- a/test/libc/str/memcpy_test.c +++ b/test/libc/str/memcpy_test.c @@ -18,6 +18,7 @@ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/bits/bits.h" +#include "libc/mem/mem.h" #include "libc/str/str.h" #include "libc/testlib/testlib.h" @@ -95,3 +96,13 @@ TEST(stpcpy, test) { EXPECT_EQ((intptr_t)b + 10, (intptr_t)p); EXPECT_STREQ("hellothere", b); } + +TEST(memcpy, testBackwardsOverlap3) { + volatile char *c; + c = malloc(3); + memcpy(c, "\e[C", 3); + memcpy(c, c + 1, VEIL("r", 3) - 1); + EXPECT_EQ('[', c[0]); + EXPECT_EQ('C', c[1]); + free(c); +} diff --git a/test/libc/str/strrchr_test.c b/test/libc/str/strrchr_test.c index 614819f7c..880d72685 100644 --- a/test/libc/str/strrchr_test.c +++ b/test/libc/str/strrchr_test.c @@ -28,22 +28,26 @@ #undef S #undef T -#define T(NAME) NAME##16 -#define S(S) u##S -#define C(C) u##C -#define strchr(x, y) strchr16(x, y) +#define T(NAME) NAME##16 +#define S(S) u##S +#define C(C) u##C +#define strrchr(x, y) strrchr16(x, y) +#define strchr(x, y) strchr16(x, y) #include "test/libc/str/strrchr_test.inc" #undef strchr +#undef strrchr #undef C #undef S #undef T -#define T(NAME) NAME##32 -#define S(S) L##S -#define C(C) L##C -#define strchr(x, y) wcschr(x, y) +#define T(NAME) NAME##32 +#define S(S) L##S +#define C(C) L##C +#define strchr(x, y) wcschr(x, y) +#define strrchr(x, y) wcsrchr(x, y) #include "test/libc/str/strrchr_test.inc" #undef strchr +#undef strrchr #undef C #undef S #undef T diff --git a/test/libc/tinymath/atan2l_test.c b/test/libc/tinymath/atan2l_test.c new file mode 100644 index 000000000..39feda6a0 --- /dev/null +++ b/test/libc/tinymath/atan2l_test.c @@ -0,0 +1,31 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/math.h" +#include "libc/runtime/gc.h" +#include "libc/testlib/testlib.h" +#include "libc/x/x.h" + +TEST(atan2l, test) { + volatile double a = -.9816175436063843; + volatile double b = -.1908585813741899; + EXPECT_STREQ("-2.95", gc(xasprintf("%.2f", atan2f(b, a)))); + EXPECT_STREQ("-2.95", gc(xasprintf("%.2f", atan2(b, a)))); + EXPECT_STREQ("-2.95", gc(xasprintf("%.2Lf", atan2l(b, a)))); +} diff --git a/test/libc/x/test.mk b/test/libc/x/test.mk index 0d4e70af8..caf36385f 100644 --- a/test/libc/x/test.mk +++ b/test/libc/x/test.mk @@ -24,10 +24,12 @@ TEST_LIBC_X_CHECKS = \ $(TEST_LIBC_X_SRCS_TEST:%.c=o/$(MODE)/%.com.runs) TEST_LIBC_X_DIRECTDEPS = \ + LIBC_CALLS \ LIBC_FMT \ LIBC_MEM \ LIBC_STDIO \ LIBC_STR \ + LIBC_NEXGEN32E \ LIBC_RUNTIME \ LIBC_X \ LIBC_STUBS \ diff --git a/test/dsp/core/dgemm_test.c b/test/libc/x/xslurp_test.c similarity index 61% rename from test/dsp/core/dgemm_test.c rename to test/libc/x/xslurp_test.c index f396cb912..b007ce32b 100644 --- a/test/dsp/core/dgemm_test.c +++ b/test/libc/x/xslurp_test.c @@ -17,49 +17,30 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/bits/xmmintrin.h" -#include "libc/log/log.h" -#include "libc/macros.h" -#include "libc/math.h" +#include "libc/runtime/gc.h" #include "libc/str/str.h" -#include "libc/testlib/ezbench.h" +#include "libc/testlib/hyperion.h" #include "libc/testlib/testlib.h" -#include "third_party/blas/blas.h" -#include "tool/viz/lib/formatstringtable-testlib.h" +#include "libc/x/x.h" -TEST(dgemm, test) { - double alpha, beta; - long m, n, k, lda, ldb, ldc; - double A[3][3] = {{1 / 8.}, {6 / 8.}, {1 / 8.}}; - double B[1][3] = {{1 / 8., 6 / 8., 1 / 8.}}; - double C[3][3] = {0}; - m = 3; - n = 3; - k = 1; - lda = 3; - ldb = 3; - ldc = 3; - beta = 1; - alpha = 1; - dgemm_("T", "T", &m, &n, &k, &alpha, &A[0][0], &lda, &B[0][0], &ldb, &beta, - &C[0][0], &ldc); - EXPECT_DBLMATRIXEQ(6, rint, 3, 3, C, "\n\ -.015625 .09375 .015625\n\ - .09375 .5625 .09375\n\ -.015625 .09375 .015625"); +TEST(xslurp, testEmptyWithNulTerminatedStringBehavior) { + size_t got; + const char *data; + const char *path = gc(xjoinpaths(kTmpPath, "xslurp.txt")); + ASSERT_NE(-1, xbarf(path, "", -1)); + ASSERT_NE(NULL, (data = gc(xslurp(path, &got)))); + ASSERT_EQ(0, got); + ASSERT_STREQ("", data); + ASSERT_NE(-1, unlink(path)); } -void dgemmer(long m, long n, long k, void *A, long lda, void *B, long ldb, - void *C, long ldc) { - double alpha, beta; - beta = 1; - alpha = 1; - dgemm_("N", "N", &m, &n, &k, &alpha, A, &lda, B, &ldb, &beta, C, &ldc); -} - -BENCH(dgemm, bench) { - double(*A)[128][128] = tgc(tmalloc(128 * 128 * 8)); - double(*B)[128][128] = tgc(tmalloc(128 * 128 * 8)); - double(*C)[128][128] = tgc(tmalloc(128 * 128 * 8)); - EZBENCH2("dgemm_", donothing, dgemmer(128, 128, 128, A, 128, B, 128, C, 128)); +TEST(xslurp, testHyperion) { + size_t got; + const char *data; + const char *path = gc(xjoinpaths(kTmpPath, "xslurp.txt")); + ASSERT_NE(-1, xbarf(path, kHyperion, kHyperionSize)); + ASSERT_NE(NULL, (data = gc(xslurp(path, &got)))); + ASSERT_EQ(kHyperionSize, got); + ASSERT_EQ(0, memcmp(data, kHyperion, kHyperionSize)); + ASSERT_NE(-1, unlink(path)); } diff --git a/test/tool/build/lib/disinst_test.c b/test/tool/build/lib/disinst_test.c index e96fd182d..d52018c9d 100644 --- a/test/tool/build/lib/disinst_test.c +++ b/test/tool/build/lib/disinst_test.c @@ -24,13 +24,12 @@ char b1[64]; char b2[64]; struct Dis d[1]; -struct DisBuilder b = {d, d->xedd, 0}; TEST(DisInst, testInt3) { uint8_t op[] = {0xcc}; xed_decoded_inst_zero_set_mode(d->xedd, XED_MACHINE_MODE_LONG_64); ASSERT_EQ(0, xed_instruction_length_decode(d->xedd, op, sizeof(op))); - DisInst(b, b1, DisSpec(d->xedd, b2)); + DisInst(d, b1, DisSpec(d->xedd, b2)); EXPECT_STREQ("int3 ", b1); } @@ -38,7 +37,7 @@ TEST(DisInst, testImmMem_needsSuffix) { uint8_t op[] = {0x80, 0x3c, 0x07, 0x00}; xed_decoded_inst_zero_set_mode(d->xedd, XED_MACHINE_MODE_LONG_64); ASSERT_EQ(0, xed_instruction_length_decode(d->xedd, op, sizeof(op))); - DisInst(b, b1, DisSpec(d->xedd, b2)); + DisInst(d, b1, DisSpec(d->xedd, b2)); EXPECT_STREQ("cmpb $0,(%rdi,%rax)", b1); } @@ -46,7 +45,7 @@ TEST(DisInst, testImmReg_doesntNeedSuffix) { uint8_t op[] = {0xb8, 0x08, 0x70, 0x40, 0x00}; xed_decoded_inst_zero_set_mode(d->xedd, XED_MACHINE_MODE_LONG_64); ASSERT_EQ(0, xed_instruction_length_decode(d->xedd, op, sizeof(op))); - DisInst(b, b1, DisSpec(d->xedd, b2)); + DisInst(d, b1, DisSpec(d->xedd, b2)); EXPECT_STREQ("mov $0x407008,%eax", b1); } @@ -60,23 +59,23 @@ TEST(DisInst, testPuttingOnTheRiz) { }; xed_decoded_inst_zero_set_mode(d->xedd, XED_MACHINE_MODE_LONG_64); ASSERT_EQ(0, xed_instruction_length_decode(d->xedd, ops[0], sizeof(ops[0]))); - DisInst(b, b1, DisSpec(d->xedd, b2)); + DisInst(d, b1, DisSpec(d->xedd, b2)); EXPECT_STREQ("lea (%rsi),%esi", b1); xed_decoded_inst_zero_set_mode(d->xedd, XED_MACHINE_MODE_LONG_64); ASSERT_EQ(0, xed_instruction_length_decode(d->xedd, ops[1], sizeof(ops[1]))); - DisInst(b, b1, DisSpec(d->xedd, b2)); + DisInst(d, b1, DisSpec(d->xedd, b2)); EXPECT_STREQ("lea (%esi,%eiz,8),%esi", b1); xed_decoded_inst_zero_set_mode(d->xedd, XED_MACHINE_MODE_LONG_64); ASSERT_EQ(0, xed_instruction_length_decode(d->xedd, ops[2], sizeof(ops[2]))); - DisInst(b, b1, DisSpec(d->xedd, b2)); + DisInst(d, b1, DisSpec(d->xedd, b2)); EXPECT_STREQ("lea 0(%ebp,%eiz,8),%esi", b1); xed_decoded_inst_zero_set_mode(d->xedd, XED_MACHINE_MODE_LONG_64); ASSERT_EQ(0, xed_instruction_length_decode(d->xedd, ops[3], sizeof(ops[3]))); - DisInst(b, b1, DisSpec(d->xedd, b2)); + DisInst(d, b1, DisSpec(d->xedd, b2)); EXPECT_STREQ("lea 0x31337,%esi", b1); xed_decoded_inst_zero_set_mode(d->xedd, XED_MACHINE_MODE_LONG_64); ASSERT_EQ(0, xed_instruction_length_decode(d->xedd, ops[4], sizeof(ops[4]))); - DisInst(b, b1, DisSpec(d->xedd, b2)); + DisInst(d, b1, DisSpec(d->xedd, b2)); EXPECT_STREQ("lea 0(%rbp,%riz,8),%esi", b1); } @@ -84,7 +83,7 @@ TEST(DisInst, testSibIndexOnly) { uint8_t op[] = {76, 141, 4, 141, 0, 0, 0, 0}; xed_decoded_inst_zero_set_mode(d->xedd, XED_MACHINE_MODE_LONG_64); ASSERT_EQ(0, xed_instruction_length_decode(d->xedd, op, sizeof(op))); - DisInst(b, b1, DisSpec(d->xedd, b2)); + DisInst(d, b1, DisSpec(d->xedd, b2)); EXPECT_STREQ("lea 0(,%rcx,4),%r8", b1); } @@ -92,7 +91,7 @@ TEST(DisInst, testRealMode) { uint8_t op[] = {0x89, 0xe5}; xed_decoded_inst_zero_set_mode(d->xedd, XED_MACHINE_MODE_REAL); ASSERT_EQ(0, xed_instruction_length_decode(d->xedd, op, sizeof(op))); - DisInst(b, b1, DisSpec(d->xedd, b2)); + DisInst(d, b1, DisSpec(d->xedd, b2)); EXPECT_STREQ("mov %sp,%bp", b1); } @@ -100,7 +99,7 @@ TEST(DisInst, testNop) { uint8_t op[] = {0x66, 0x2e, 0x0f, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00}; xed_decoded_inst_zero_set_mode(d->xedd, XED_MACHINE_MODE_LONG_64); ASSERT_EQ(0, xed_instruction_length_decode(d->xedd, op, sizeof(op))); - DisInst(b, b1, DisSpec(d->xedd, b2)); + DisInst(d, b1, DisSpec(d->xedd, b2)); EXPECT_STREQ("nopw %cs:0(%rax,%rax)", b1); } @@ -110,7 +109,7 @@ TEST(DisInst, testPush) { ASSERT_EQ(0, xed_instruction_length_decode(d->xedd, op, sizeof(op))); EXPECT_EQ(4, ModrmSrm(d->xedd->op.rde)); EXPECT_EQ(1, Rexb(d->xedd->op.rde)); - DisInst(b, b1, DisSpec(d->xedd, b2)); + DisInst(d, b1, DisSpec(d->xedd, b2)); EXPECT_STREQ("pop %r12", b1); } @@ -118,11 +117,11 @@ TEST(DisInst, testMovb) { uint8_t op[] = {0x8a, 0x1e, 0x0c, 0x32}; xed_decoded_inst_zero_set_mode(d->xedd, XED_MACHINE_MODE_LONG_64); ASSERT_EQ(0, xed_instruction_length_decode(d->xedd, op, sizeof(op))); - DisInst(b, b1, DisSpec(d->xedd, b2)); + DisInst(d, b1, DisSpec(d->xedd, b2)); EXPECT_STREQ("mov (%rsi),%bl", b1); xed_decoded_inst_zero_set_mode(d->xedd, XED_MACHINE_MODE_REAL); ASSERT_EQ(0, xed_instruction_length_decode(d->xedd, op, sizeof(op))); - DisInst(b, b1, DisSpec(d->xedd, b2)); + DisInst(d, b1, DisSpec(d->xedd, b2)); EXPECT_STREQ("mov 0x320c,%bl", b1); } @@ -130,7 +129,7 @@ TEST(DisInst, testLes) { uint8_t op[] = {0xc4, 0x3e, 0x16, 0x32}; xed_decoded_inst_zero_set_mode(d->xedd, XED_MACHINE_MODE_REAL); ASSERT_EQ(0, xed_instruction_length_decode(d->xedd, op, sizeof(op))); - DisInst(b, b1, DisSpec(d->xedd, b2)); + DisInst(d, b1, DisSpec(d->xedd, b2)); EXPECT_STREQ("les 0x3216,%di", b1); } @@ -138,7 +137,7 @@ TEST(DisInst, testStosbLong) { uint8_t op[] = {0xAA}; xed_decoded_inst_zero_set_mode(d->xedd, XED_MACHINE_MODE_LONG_64); ASSERT_EQ(0, xed_instruction_length_decode(d->xedd, op, sizeof(op))); - DisInst(b, b1, DisSpec(d->xedd, b2)); + DisInst(d, b1, DisSpec(d->xedd, b2)); EXPECT_STREQ("stosb %al,(%rdi)", b1); } @@ -146,7 +145,7 @@ TEST(DisInst, testStosbReal) { uint8_t op[] = {0xAA}; xed_decoded_inst_zero_set_mode(d->xedd, XED_MACHINE_MODE_REAL); ASSERT_EQ(0, xed_instruction_length_decode(d->xedd, op, sizeof(op))); - DisInst(b, b1, DisSpec(d->xedd, b2)); + DisInst(d, b1, DisSpec(d->xedd, b2)); EXPECT_STREQ("stosb %al,(%di)", b1); } @@ -154,7 +153,7 @@ TEST(DisInst, testStosbLegacy) { uint8_t op[] = {0xAA}; xed_decoded_inst_zero_set_mode(d->xedd, XED_MACHINE_MODE_LEGACY_32); ASSERT_EQ(0, xed_instruction_length_decode(d->xedd, op, sizeof(op))); - DisInst(b, b1, DisSpec(d->xedd, b2)); + DisInst(d, b1, DisSpec(d->xedd, b2)); EXPECT_STREQ("stosb %al,(%edi)", b1); } @@ -162,7 +161,7 @@ TEST(DisInst, testStosbLongAsz) { uint8_t op[] = {0x67, 0xAA}; xed_decoded_inst_zero_set_mode(d->xedd, XED_MACHINE_MODE_LONG_64); ASSERT_EQ(0, xed_instruction_length_decode(d->xedd, op, sizeof(op))); - DisInst(b, b1, DisSpec(d->xedd, b2)); + DisInst(d, b1, DisSpec(d->xedd, b2)); EXPECT_STREQ("stosb %al,(%edi)", b1); } @@ -170,7 +169,7 @@ TEST(DisInst, testAddLong) { uint8_t op[] = {0x01, 0xff}; xed_decoded_inst_zero_set_mode(d->xedd, XED_MACHINE_MODE_LONG_64); ASSERT_EQ(0, xed_instruction_length_decode(d->xedd, op, sizeof(op))); - DisInst(b, b1, DisSpec(d->xedd, b2)); + DisInst(d, b1, DisSpec(d->xedd, b2)); EXPECT_STREQ("add %edi,%edi", b1); } @@ -178,7 +177,7 @@ TEST(DisInst, testAddLegacy) { uint8_t op[] = {0x01, 0xff}; xed_decoded_inst_zero_set_mode(d->xedd, XED_MACHINE_MODE_LEGACY_32); ASSERT_EQ(0, xed_instruction_length_decode(d->xedd, op, sizeof(op))); - DisInst(b, b1, DisSpec(d->xedd, b2)); + DisInst(d, b1, DisSpec(d->xedd, b2)); EXPECT_STREQ("add %edi,%edi", b1); } @@ -186,7 +185,7 @@ TEST(DisInst, testAddReal) { uint8_t op[] = {0x01, 0xff}; xed_decoded_inst_zero_set_mode(d->xedd, XED_MACHINE_MODE_REAL); ASSERT_EQ(0, xed_instruction_length_decode(d->xedd, op, sizeof(op))); - DisInst(b, b1, DisSpec(d->xedd, b2)); + DisInst(d, b1, DisSpec(d->xedd, b2)); EXPECT_STREQ("add %di,%di", b1); } @@ -194,7 +193,7 @@ TEST(DisInst, testAddLongOsz) { uint8_t op[] = {0x66, 0x01, 0xff}; xed_decoded_inst_zero_set_mode(d->xedd, XED_MACHINE_MODE_LONG_64); ASSERT_EQ(0, xed_instruction_length_decode(d->xedd, op, sizeof(op))); - DisInst(b, b1, DisSpec(d->xedd, b2)); + DisInst(d, b1, DisSpec(d->xedd, b2)); EXPECT_STREQ("add %di,%di", b1); } @@ -202,7 +201,7 @@ TEST(DisInst, testAddLegacyOsz) { uint8_t op[] = {0x66, 0x01, 0xff}; xed_decoded_inst_zero_set_mode(d->xedd, XED_MACHINE_MODE_LEGACY_32); ASSERT_EQ(0, xed_instruction_length_decode(d->xedd, op, sizeof(op))); - DisInst(b, b1, DisSpec(d->xedd, b2)); + DisInst(d, b1, DisSpec(d->xedd, b2)); EXPECT_STREQ("add %di,%di", b1); } @@ -210,7 +209,7 @@ TEST(DisInst, testAddRealOsz) { uint8_t op[] = {0x66, 0x01, 0xff}; xed_decoded_inst_zero_set_mode(d->xedd, XED_MACHINE_MODE_REAL); ASSERT_EQ(0, xed_instruction_length_decode(d->xedd, op, sizeof(op))); - DisInst(b, b1, DisSpec(d->xedd, b2)); + DisInst(d, b1, DisSpec(d->xedd, b2)); EXPECT_STREQ("add %edi,%edi", b1); } @@ -219,6 +218,54 @@ TEST(DisInst, testFxam) { xed_decoded_inst_zero_set_mode(d->xedd, XED_MACHINE_MODE_LONG_64); ASSERT_EQ(0, xed_instruction_length_decode(d->xedd, op, sizeof(op))); ASSERT_EQ(4, ModrmReg(d->xedd->op.rde)); - DisInst(b, b1, DisSpec(d->xedd, b2)); + DisInst(d, b1, DisSpec(d->xedd, b2)); EXPECT_STREQ("fxam ", b1); } + +TEST(DisInst, testOrImmCode16gcc) { + uint8_t op[] = {0x67, 0x81, 0x4c, 0x24, 0x0c, 0x00, 0x0c}; + xed_decoded_inst_zero_set_mode(d->xedd, XED_MACHINE_MODE_REAL); + ASSERT_EQ(0, xed_instruction_length_decode(d->xedd, op, sizeof(op))); + DisInst(d, b1, DisSpec(d->xedd, b2)); + EXPECT_STREQ("or $0xc00,12(%esp)", b1); +} + +TEST(DisInst, testPause) { + uint8_t op[] = {0xf3, 0x90}; + xed_decoded_inst_zero_set_mode(d->xedd, XED_MACHINE_MODE_LONG_64); + ASSERT_EQ(0, xed_instruction_length_decode(d->xedd, op, sizeof(op))); + DisInst(d, b1, DisSpec(d->xedd, b2)); + EXPECT_STREQ("pause ", b1); +} + +TEST(DisInst, testJmpEw) { + uint8_t op[] = {0xff, 0xe0}; + xed_decoded_inst_zero_set_mode(d->xedd, XED_MACHINE_MODE_REAL); + ASSERT_EQ(0, xed_instruction_length_decode(d->xedd, op, sizeof(op))); + DisInst(d, b1, DisSpec(d->xedd, b2)); + EXPECT_STREQ("jmp %ax", b1); +} + +TEST(DisInst, testJmpEv16) { + uint8_t op[] = {0x66, 0xff, 0xe0}; + xed_decoded_inst_zero_set_mode(d->xedd, XED_MACHINE_MODE_REAL); + ASSERT_EQ(0, xed_instruction_length_decode(d->xedd, op, sizeof(op))); + DisInst(d, b1, DisSpec(d->xedd, b2)); + EXPECT_STREQ("jmp %eax", b1); +} + +TEST(DisInst, testJmpEv32) { + uint8_t op[] = {0xff, 0xe0}; + xed_decoded_inst_zero_set_mode(d->xedd, XED_MACHINE_MODE_LEGACY_32); + ASSERT_EQ(0, xed_instruction_length_decode(d->xedd, op, sizeof(op))); + DisInst(d, b1, DisSpec(d->xedd, b2)); + EXPECT_STREQ("jmp %eax", b1); +} + +TEST(DisInst, testJmpEq) { + uint8_t op[] = {0x66, 0xff, 0xe0}; + xed_decoded_inst_zero_set_mode(d->xedd, XED_MACHINE_MODE_LONG_64); + ASSERT_EQ(0, xed_instruction_length_decode(d->xedd, op, sizeof(op))); + DisInst(d, b1, DisSpec(d->xedd, b2)); + EXPECT_STREQ("jmp %rax", b1); +} diff --git a/test/tool/build/lib/pml4t_test.c b/test/tool/build/lib/pml4t_test.c index 8191f88bc..8c7c0e2df 100644 --- a/test/tool/build/lib/pml4t_test.c +++ b/test/tool/build/lib/pml4t_test.c @@ -124,6 +124,13 @@ TEST(pml4t, testFindPml4t_holeTooSmall_skipsOver) { ASSERT_EQ(0x700006000, FindPml4t(cr3, 0x700000000, 0x10000)); } +TEST(pml4t, testFindPml4t_bigAllocation) { + ASSERT_EQ(0x00200000, FindPml4t(cr3, 0x00200000, 0x00400000)); + ASSERT_EQ(0x00201000, FindPml4t(cr3, 0x00201000, 0x00400000)); + ASSERT_EQ(0xff200000, FindPml4t(cr3, 0xff200000, 0x00400000)); + ASSERT_EQ(0xff201000, FindPml4t(cr3, 0xff201000, 0x00400000)); +} + TEST(pml4t, testFreePmlt) { ASSERT_NE(-1, RegisterPml4t(cr3, 0x000005000, 0x123000, 0x2000, NewPage)); ASSERT_NE(-1, RegisterPml4t(cr3, 0x000000000, 0x321000, 0x1000, NewPage)); diff --git a/test/tool/build/lib/wut_test.c b/test/tool/build/lib/wut_test.c new file mode 100644 index 000000000..181bd73c2 --- /dev/null +++ b/test/tool/build/lib/wut_test.c @@ -0,0 +1,32 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/mem/mem.h" +#include "libc/str/str.h" +#include "libc/testlib/testlib.h" + +TEST(memcpy, testBackwardsOverlap3) { + volatile char *c; + c = malloc(3); + memcpy(c, "\e[C", 3); + memcpy(c, c + 1, VEIL("r", 3) - 1); + EXPECT_EQ('[', c[0]); + EXPECT_EQ('C', c[1]); + free(c); +} diff --git a/third_party/blas/blas.mk b/third_party/blas/blas.mk index 7122c6fe7..7c3273d6e 100644 --- a/third_party/blas/blas.mk +++ b/third_party/blas/blas.mk @@ -34,10 +34,7 @@ THIRD_PARTY_BLAS_A_DEPS := \ $(THIRD_PARTY_BLAS_A_OBJS): \ OVERRIDE_CFLAGS += \ - -O3 #$(MATHEMATICAL) - -#$(THIRD_PARTY_BLAS_A_OBJS): \ - CC = $(CLANG) + $(MATHEMATICAL) $(THIRD_PARTY_BLAS_A): \ third_party/blas/ \ diff --git a/third_party/blas/dgemm_.S b/third_party/blas/dgemm_.S deleted file mode 100644 index d8b87c7f5..000000000 --- a/third_party/blas/dgemm_.S +++ /dev/null @@ -1,1102 +0,0 @@ -/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ -│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ This program is free software; you can redistribute it and/or modify │ -│ it under the terms of the GNU General Public License as published by │ -│ the Free Software Foundation; version 2 of the License. │ -│ │ -│ This program is distributed in the hope that it will be useful, but │ -│ WITHOUT ANY WARRANTY; without even the implied warranty of │ -│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ -│ General Public License for more details. │ -│ │ -│ You should have received a copy of the GNU General Public License │ -│ along with this program; if not, write to the Free Software │ -│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ -│ 02110-1301 USA │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/macros.h" - -/ Multiplies matrices. - .p2align 4 -dgemm_: push %r15 - mov %rdi,%r15 - push %r14 - mov %r8,%r14 - push %r13 - mov %rsi,%r13 - mov $.LC1,%esi - push %r12 - push %rbp - push %rbx - sub $248,%rsp - mov %rcx,8(%rsp) - mov 352(%rsp),%rcx - mov %rdx,(%rsp) - mov $1,%edx - mov 304(%rsp),%rbx - mov (%rcx),%ecx - mov 344(%rsp),%rbp - mov %r9,24(%rsp) - mov %ecx,16(%rsp) - mov 328(%rsp),%rcx - mov (%rcx),%ecx - mov %ecx,32(%rsp) - mov 312(%rsp),%rcx - mov (%rcx),%ecx - mov %ecx,40(%rsp) - mov $1,%ecx - call lsame_ - mov $1,%ecx - mov $1,%edx - mov %r13,%rdi - mov %eax,%r12d - mov $.LC1,%esi - call lsame_ - test %r12d,%r12d - mov 320(%rsp),%r11 - mov %eax,%r8d - je .L2 - mov (%rsp),%rax - mov (%rax),%r10d - mov %r10d,%r9d - test %r8d,%r8d - jne .L3 - movl $0,236(%rsp) - mov 8(%rsp),%rax - mov (%rax),%eax - mov %eax,48(%rsp) -.L103: mov $1,%ecx - mov $1,%edx - mov $.LC2,%esi - mov %r13,%rdi - mov %r11,320(%rsp) - mov %r8d,64(%rsp) - mov %r10d,56(%rsp) - call lsame_ - mov 56(%rsp),%r10d - mov 64(%rsp),%r8d - test %eax,%eax - mov 320(%rsp),%r11 - jne .L156 - mov $1,%ecx - mov $1,%edx - mov $.LC3,%esi - mov %r13,%rdi - mov %r11,320(%rsp) - mov %r8d,64(%rsp) - mov %r10d,56(%rsp) - call lsame_ - test %eax,%eax - jne .L155 - movl $2,236(%rsp) - jmp .L8 - .p2align 4,,10 - .p2align 3 -.L2: mov %r11,320(%rsp) - mov (%r14),%r10d - mov %eax,64(%rsp) - test %eax,%eax - je .L5 - mov $1,%ecx - mov $1,%edx - mov $.LC2,%esi - mov %r15,%rdi - mov %r10d,48(%rsp) - mov %r10d,56(%rsp) - movl $0,236(%rsp) - call lsame_ - mov 56(%rsp),%r10d - mov 64(%rsp),%r8d - test %eax,%eax - mov 320(%rsp),%r11 - je .L145 -.L156: mov (%rsp),%rax - mov (%rax),%r9d -.L6: test %r9d,%r9d - js .L160 -.L9: mov 8(%rsp),%rax - mov (%rax),%eax - test %eax,%eax - js .L161 - mov (%r14),%r13d - test %r13d,%r13d - js .L162 - mov 312(%rsp),%rdi - mov $1,%edx - test %r10d,%r10d - mov %r10d,%ecx - cmovle %edx,%ecx - cmp %ecx,(%rdi) - jge .L12 - movl $8,236(%rsp) - jmp .L8 -.L3: movl $0,236(%rsp) - mov (%r14),%eax - mov %eax,48(%rsp) - test %r9d,%r9d - jns .L9 -.L160: movl $3,236(%rsp) -.L8: mov $6,%edx - lea 236(%rsp),%rsi - mov $.LC5,%edi - call xerbla_ -.L1: add $248,%rsp - pop %rbx - pop %rbp - pop %r12 - pop %r13 - pop %r14 - pop %r15 - ret -.L161: movl $4,236(%rsp) - jmp .L8 -.L5: mov 8(%rsp),%rax - mov $1,%ecx - mov $1,%edx - mov %r15,%rdi - mov $.LC2,%esi - mov %r10d,56(%rsp) - movl $0,236(%rsp) - mov (%rax),%eax - mov %eax,48(%rsp) - call lsame_ - mov 56(%rsp),%r10d - mov 64(%rsp),%r8d - test %eax,%eax - mov 320(%rsp),%r11 - jne .L103 - mov $1,%ecx - mov $1,%edx - mov $.LC3,%esi - mov %r15,%rdi - mov %r11,320(%rsp) - mov %r8d,64(%rsp) - mov %r10d,56(%rsp) - call lsame_ - mov 56(%rsp),%r10d - mov 64(%rsp),%r8d - test %eax,%eax - mov 320(%rsp),%r11 - jne .L103 -.L104: movl $1,236(%rsp) - jmp .L8 -.L162: movl $5,236(%rsp) - jmp .L8 -.L12: mov 48(%rsp),%ecx - mov 328(%rsp),%rdi - test %ecx,%ecx - cmovle %edx,%ecx - cmp %ecx,(%rdi) - jl .L163 - mov 352(%rsp),%rcx - test %r9d,%r9d - cmovg %r9d,%edx - cmp %edx,(%rcx) - jge .L14 - movl $13,236(%rsp) - jmp .L8 -.L145: mov $1,%ecx - mov $1,%edx - mov $.LC3,%esi - mov %r15,%rdi - mov %r11,320(%rsp) - mov %r8d,64(%rsp) - mov %r10d,56(%rsp) - call lsame_ - test %eax,%eax - je .L104 -.L155: mov (%rsp),%rax - mov 56(%rsp),%r10d - mov 64(%rsp),%r8d - mov 320(%rsp),%r11 - mov (%rax),%r9d - jmp .L6 -.L163: movl $10,236(%rsp) - jmp .L8 - .p2align 4,,10 - .p2align 3 -.L14: test %r9d,%r9d - je .L1 - test %eax,%eax - je .L1 - movslq 16(%rsp),%rcx - mov $0,%edx - pxor %xmm3,%xmm3 - test %rcx,%rcx - cmovns %rcx,%rdx - mov 24(%rsp),%rcx - movsd (%rcx),%xmm2 - mov %rdx,48(%rsp) - ucomisd %xmm3,%xmm2 - jp .L119 - jne .L119 - mov 336(%rsp),%rbx - movsd (%rbx),%xmm1 - ucomisd .LC4(%rip),%xmm1 - jp .L100 - je .L1 -.L100: ucomisd %xmm3,%xmm1 - jnp .L164 -.L21: mov 48(%rsp),%rbx - mov %r9d,%r8d - mov %r9d,%r10d - mov %r9d,%r11d - shrl %r8d - mov %rbp,%rsi - add $1,%eax - and $-2,%r10d - lea 0(,%rbx,8),%r12 - sal $4,%r8 - or $1,%r11d - mov $-1,%rcx - mov $1,%edi - movddup %xmm1,%xmm2 - .p2align 4,,10 - .p2align 3 -.L28: cmp $1,%r9d - je .L106 - lea (%rsi,%r8),%r13 - mov %rsi,%rdx - .p2align 4,,10 - .p2align 3 -.L27: movupd (%rdx),%xmm0 - add $16,%rdx - mulpd %xmm2,%xmm0 - movups %xmm0,-16(%rdx) - cmp %rdx,%r13 - jne .L27 - movslq %r11d,%rdx - cmp %r9d,%r10d - je .L29 -.L25: add %rcx,%rdx - lea 0(%rbp,%rdx,8),%rdx - movsd (%rdx),%xmm0 - mulsd %xmm1,%xmm0 - movsd %xmm0,(%rdx) -.L29: add $1,%edi - add %rbx,%rcx - add %r12,%rsi - cmp %edi,%eax - jne .L28 - jmp .L1 -.L119: test %r13d,%r13d - jne .L20 - mov 336(%rsp),%rcx - movsd .LC4(%rip),%xmm0 - ucomisd (%rcx),%xmm0 - jnp .L165 -.L20: movslq 32(%rsp),%rcx - xor %edx,%edx - test %rcx,%rcx - cmovs %rdx,%rcx - mov %rcx,%rsi - mov %rcx,56(%rsp) - notq %rsi - mov %rsi,72(%rsp) - movslq 40(%rsp),%rsi - test %rsi,%rsi - cmovns %rsi,%rdx - mov %rdx,%rsi - mov %rdx,(%rsp) - mov %rdx,%rdi - notq %rsi - mov %rsi,80(%rsp) - test %r8d,%r8d - je .L30 - test %r12d,%r12d - jne .L166 - mov 48(%rsp),%rcx - add $1,%eax - lea -1(%r9),%edx - mov %r13d,%r8d - mov %eax,32(%rsp) - lea 8(%rbp,%rdx,8),%r10 - notq %rdx - shrl %r8d - sal $3,%rcx - mov $1,%r14d - mov %r13d,%ebp - mov %r13d,%r12d - mov %rcx,16(%rsp) - mov 56(%rsp),%rcx - mov %r11,%rdi - and $-2,%ebp - mov 336(%rsp),%rax - sal $4,%r8 - or $1,%r12d - lea 8(%rbx),%r15 - sal $3,%rcx - mov %r14d,8(%rsp) - mov (%rsp),%r14 - mov $-1,%rsi - movsd (%rax),%xmm5 - lea 0(,%rdx,8),%rax - mov %rcx,24(%rsp) - mov %rax,40(%rsp) - .p2align 4,,10 - .p2align 3 -.L64: mov 40(%rsp),%rax - mov $-1,%rcx - lea (%rax,%r10),%r9 - .p2align 4,,10 - .p2align 3 -.L63: test %r13d,%r13d - je .L111 - cmp $1,%r13d - je .L112 - lea (%r15,%rcx,8),%rdx - xor %eax,%eax - pxor %xmm4,%xmm4 - .p2align 4,,10 - .p2align 3 -.L58: movupd (%rdx,%rax),%xmm0 - movupd (%rdi,%rax),%xmm7 - add $16,%rax - mulpd %xmm7,%xmm0 - addpd %xmm0,%xmm4 - cmp %r8,%rax - jne .L58 - movapd %xmm4,%xmm1 - movslq %r12d,%rax - unpckhpd %xmm4,%xmm1 - addpd %xmm4,%xmm1 - cmp %ebp,%r13d - je .L55 -.L56: lea (%rcx,%rax),%rdx - add %rsi,%rax - movsd (%rbx,%rdx,8),%xmm0 - mulsd (%r11,%rax,8),%xmm0 - addsd %xmm0,%xmm1 -.L55: mulsd %xmm2,%xmm1 - ucomisd %xmm3,%xmm5 - jp .L60 - jne .L60 -.L157: movsd %xmm1,(%r9) - add $8,%r9 - add %r14,%rcx - cmp %r10,%r9 - jne .L63 - addl $1,8(%rsp) - add 16(%rsp),%r10 - mov 8(%rsp),%eax - add 56(%rsp),%rsi - add 24(%rsp),%rdi - cmp 32(%rsp),%eax - jne .L64 - jmp .L1 - .p2align 4,,10 - .p2align 3 -.L60: movsd (%r9),%xmm0 - mulsd %xmm5,%xmm0 - addsd %xmm0,%xmm1 - jmp .L157 - .p2align 4,,10 - .p2align 3 -.L111: movapd %xmm3,%xmm1 - jmp .L55 -.L112: mov $1,%eax - movapd %xmm3,%xmm1 - jmp .L56 -.L30: test %r12d,%r12d - jne .L167 - sub $1,%eax - lea -1(%r9),%esi - mov 48(%rsp),%rcx - movq $1,64(%rsp) - add $2,%rax - lea 8(%rbp,%rsi,8),%r14 - mov 56(%rsp),%rbp - mov %r11,16(%rsp) - mov %rax,88(%rsp) - mov 336(%rsp),%rax - sal $3,%rcx - mov %rcx,80(%rsp) - mov %rbp,%rcx - lea 0(,%rbp,8),%rdx - movsd (%rax),%xmm4 - mov (%rsp),%rax - sal $4,%rcx - sal $3,%rax - mov %rax,8(%rsp) - lea -1(%r13),%eax - mov %eax,24(%rsp) - mov %r13d,%eax - shrl %eax - sal $4,%rax - mov %rax,40(%rsp) - mov %r13d,%eax - and $-2,%eax - mov %eax,32(%rsp) - mov %r13d,%eax - or $1,%eax - mov %eax,56(%rsp) - mov %rsi,%rax - notq %rax - sal $3,%rax - mov %rax,96(%rsp) - .p2align 4,,10 - .p2align 3 -.L98: mov 96(%rsp),%rax - mov 72(%rsp),%rdi - mov %rbx,%r10 - mov $-1,%r9 - lea (%rax,%r14),%rsi - mov 64(%rsp),%rax - mov %rax,%r12 - add %rbp,%rax - add %rdi,%rax - add %rdi,%r12 - mov %rax,48(%rsp) - .p2align 4,,10 - .p2align 3 -.L97: test %r13d,%r13d - je .L117 - cmpl $2,24(%rsp) - jbe .L118 - mov 40(%rsp),%rax - mov 16(%rsp),%rdi - pxor %xmm1,%xmm1 - lea (%rax,%r10),%r8 - mov %r10,%rax - .p2align 4,,10 - .p2align 3 -.L92: movsd (%rdi),%xmm0 - movupd (%rax),%xmm6 - add $16,%rax - movhpd (%rdi,%rdx),%xmm0 - add %rcx,%rdi - mulpd %xmm6,%xmm0 - addpd %xmm0,%xmm1 - cmp %r8,%rax - jne .L92 - movapd %xmm1,%xmm0 - unpckhpd %xmm1,%xmm0 - addpd %xmm0,%xmm1 - cmp 32(%rsp),%r13d - je .L89 - mov 56(%rsp),%eax -.L90: movslq %eax,%r8 - mov %rbp,%rdi - imul %r8,%rdi - add %r9,%r8 - lea (%r12,%rdi),%r15 - movsd (%r11,%r15,8),%xmm0 - mulsd (%rbx,%r8,8),%xmm0 - lea 1(%rax),%r8d - addsd %xmm0,%xmm1 - cmp %r8d,%r13d - jl .L89 - add %rbp,%rdi - movslq %r8d,%r8 - add $2,%eax - lea (%r12,%rdi),%r15 - add %r9,%r8 - movsd (%r11,%r15,8),%xmm0 - mulsd (%rbx,%r8,8),%xmm0 - addsd %xmm0,%xmm1 - cmp %eax,%r13d - jl .L89 - cltq - add 48(%rsp),%rdi - add %r9,%rax - movsd (%r11,%rdi,8),%xmm0 - mulsd (%rbx,%rax,8),%xmm0 - addsd %xmm0,%xmm1 -.L89: mulsd %xmm2,%xmm1 - ucomisd %xmm3,%xmm4 - jp .L94 - jne .L94 -.L158: movsd %xmm1,(%rsi) - add $8,%rsi - add (%rsp),%r9 - add 8(%rsp),%r10 - cmp %r14,%rsi - jne .L97 - addq $1,64(%rsp) - add 80(%rsp),%r14 - addq $8,16(%rsp) - mov 64(%rsp),%rax - cmp 88(%rsp),%rax - jne .L98 - jmp .L1 - .p2align 4,,10 - .p2align 3 -.L94: movsd (%rsi),%xmm0 - mulsd %xmm4,%xmm0 - addsd %xmm0,%xmm1 - jmp .L158 - .p2align 4,,10 - .p2align 3 -.L117: movapd %xmm3,%xmm1 - jmp .L89 -.L118: movapd %xmm3,%xmm1 - mov $1,%eax - jmp .L90 -.L164: jne .L21 - mov 48(%rsp),%r12 - lea 1(%rax),%r14d - lea -1(%r9),%eax - mov $1,%ebx - lea 8(,%rax,8),%r13 - sal $3,%r12 - .p2align 4,,10 - .p2align 3 -.L24: mov %rbp,%rdi - mov %r13,%rdx - xor %esi,%esi - add $1,%ebx - call memset - add %r12,%rbp - cmp %ebx,%r14d - jne .L24 - jmp .L1 -.L167: sub $1,%eax - mov 72(%rsp),%rsi - mov %r9d,%r15d - mov %rbp,%r12 - add $2,%rax - mov 336(%rsp),%rcx - movq $1,24(%rsp) - shrl %r15d - mov %rax,64(%rsp) - mov %r9d,%eax - sal $4,%r15 - movsd .LC4(%rip),%xmm7 - and $-2,%eax - movsd (%rcx),%xmm5 - mov 48(%rsp),%rcx - mov %r11,320(%rsp) - mov %eax,8(%rsp) - mov %r9d,%eax - mov $-1,%r14 - or $1,%eax - sal $3,%rcx - movddup %xmm5,%xmm6 - mov %eax,16(%rsp) - lea -1(%r9),%eax - mov %rcx,40(%rsp) - mov 80(%rsp),%rcx - mov %eax,88(%rsp) - lea 8(,%rax,8),%rax - mov %rax,208(%rsp) - lea (%rdx,%rdx),%rax - add %rax,%rcx - mov %rax,96(%rsp) - mov 56(%rsp),%rax - mov %rcx,128(%rsp) - mov %rax,%rcx - sal $4,%rcx - mov %rcx,112(%rsp) - mov %rax,%rcx - lea (%rsi,%rax,2),%rax - lea (%r11,%rax,8),%rax - mov %rax,144(%rsp) - mov %rdx,%rax - sal $4,%rax - mov %rax,136(%rsp) - mov %rcx,%rax - negq %rax - sal $3,%rax - mov %rax,152(%rsp) - lea 0(,%rdx,8),%rax - mov %rax,168(%rsp) - mov %r9d,%eax - shr $2,%eax - sal $5,%rax - mov %rax,104(%rsp) - mov %r9d,%eax - and $-4,%eax - mov %eax,200(%rsp) - add $1,%eax - mov %eax,192(%rsp) - lea 0(,%rcx,8),%rax - mov %r13d,%ecx - mov %r9d,%r13d - mov %rax,216(%rsp) - .p2align 4,,10 - .p2align 3 -.L77: ucomisd %xmm3,%xmm5 - jnp .L168 -.L68: ucomisd %xmm7,%xmm5 - jp .L123 - jne .L123 -.L71: test %ecx,%ecx - je .L69 - cmp $2,%ecx - jle .L114 - mov 144(%rsp),%rax - mov 24(%rsp),%rdi - mov %r15,160(%rsp) - mov $-1,%r10 - mov 128(%rsp),%rsi - lea (%rax,%rdi,8),%r11 - mov 104(%rsp),%rdi - mov %rbx,%rax - mov %rsi,32(%rsp) - mov $2,%esi - mov %esi,%r15d -.L82: mov 152(%rsp),%rsi - cmpl $2,88(%rsp) - movsd (%r11,%rsi),%xmm10 - mulsd %xmm2,%xmm10 - jbe .L169 - movsd (%r11),%xmm11 - mov 168(%rsp),%rsi - movddup %xmm10,%xmm9 - xor %edx,%edx - mulsd %xmm2,%xmm11 - lea (%rax,%rsi),%r8 - mov %r12,%rsi - movddup %xmm11,%xmm8 - .p2align 4,,10 - .p2align 3 -.L88: movupd 16(%rax,%rdx),%xmm0 - movupd 16(%r8,%rdx),%xmm1 - add $32,%rsi - movupd -16(%rsi),%xmm4 - mulpd %xmm9,%xmm0 - mulpd %xmm8,%xmm1 - addpd %xmm4,%xmm0 - movupd -32(%rsi),%xmm4 - addpd %xmm1,%xmm0 - movupd (%rax,%rdx),%xmm1 - mulpd %xmm9,%xmm1 - movups %xmm0,-16(%rsi) - addpd %xmm4,%xmm1 - movupd (%r8,%rdx),%xmm4 - add $32,%rdx - mulpd %xmm8,%xmm4 - addpd %xmm4,%xmm1 - movups %xmm1,-32(%rsi) - cmp %rdi,%rdx - jne .L88 - cmp %r13d,200(%rsp) - je .L81 - mov 192(%rsp),%edx -.L79: movslq %edx,%rsi - lea (%r14,%rsi),%r8 - lea (%r10,%rsi),%r9 - add 32(%rsp),%rsi - movsd (%rbx,%rsi,8),%xmm1 - movsd (%rbx,%r9,8),%xmm0 - lea 0(%rbp,%r8,8),%r8 - lea 1(%rdx),%esi - mulsd %xmm11,%xmm1 - mulsd %xmm10,%xmm0 - addsd (%r8),%xmm0 - addsd %xmm1,%xmm0 - movsd %xmm0,(%r8) - cmp %esi,%r13d - jl .L81 - movslq %esi,%rsi - add $2,%edx - lea (%r10,%rsi),%r9 - lea (%r14,%rsi),%r8 - movsd (%rbx,%r9,8),%xmm0 - mov 32(%rsp),%r9 - lea 0(%rbp,%r8,8),%r8 - mulsd %xmm10,%xmm0 - add %r9,%rsi - movsd (%rbx,%rsi,8),%xmm1 - addsd (%r8),%xmm0 - mulsd %xmm11,%xmm1 - addsd %xmm1,%xmm0 - movsd %xmm0,(%r8) - cmp %edx,%r13d - jl .L81 - movslq %edx,%rdx - lea (%r14,%rdx),%rsi - lea (%r10,%rdx),%r8 - add %r9,%rdx - mulsd (%rbx,%rdx,8),%xmm11 - lea 0(%rbp,%rsi,8),%rsi - mulsd (%rbx,%r8,8),%xmm10 - addsd (%rsi),%xmm10 - addsd %xmm10,%xmm11 - movsd %xmm11,(%rsi) -.L81: mov 96(%rsp),%rdx - lea 1(%r15),%esi - add $2,%r15d - add %rdx,32(%rsp) - add 112(%rsp),%r11 - add 136(%rsp),%rax - add %rdx,%r10 - cmp %r15d,%ecx - jg .L82 - mov 160(%rsp),%r15 -.L78: mov 56(%rsp),%rdi - movslq %esi,%rdx - mov (%rsp),%r8 - lea 8(%rbx),%r10 - mov 72(%rsp),%rax - add 24(%rsp),%rax - imul %rdx,%rdi - mov 216(%rsp),%r9 - imul %r8,%rdx - add 80(%rsp),%rdx - add %rdi,%rax - mov 320(%rsp),%rdi - lea (%rdi,%rax,8),%rdi - .p2align 4,,10 - .p2align 3 -.L86: movsd (%rdi),%xmm0 - mulsd %xmm2,%xmm0 - cmp $1,%r13d - je .L116 - lea (%r10,%rdx,8),%r11 - movddup %xmm0,%xmm4 - xor %eax,%eax - .p2align 4,,10 - .p2align 3 -.L84: movupd (%r11,%rax),%xmm1 - movupd (%r12,%rax),%xmm13 - mulpd %xmm4,%xmm1 - addpd %xmm13,%xmm1 - movups %xmm1,(%r12,%rax) - add $16,%rax - cmp %r15,%rax - jne .L84 - cmp 8(%rsp),%r13d - je .L85 - movslq 16(%rsp),%rax -.L83: lea (%r14,%rax),%r11 - add %rdx,%rax - lea 0(%rbp,%r11,8),%r11 - mulsd (%rbx,%rax,8),%xmm0 - addsd (%r11),%xmm0 - movsd %xmm0,(%r11) -.L85: add $1,%esi - add %r9,%rdi - add %r8,%rdx - cmp %esi,%ecx - jge .L86 -.L69: addq $1,24(%rsp) - add 48(%rsp),%r14 - mov 24(%rsp),%rax - add 40(%rsp),%r12 - cmp 64(%rsp),%rax - jne .L77 - jmp .L1 -.L116: mov $1,%eax - jmp .L83 -.L168: jne .L68 - mov 208(%rsp),%rdx - xor %esi,%esi - mov %r12,%rdi - mov %ecx,204(%rsp) - movaps %xmm6,176(%rsp) - movsd %xmm5,160(%rsp) - movsd %xmm2,32(%rsp) - call memset - mov .LC4(%rip),%rax - movsd 32(%rsp),%xmm2 - pxor %xmm3,%xmm3 - movsd 160(%rsp),%xmm5 - mov 204(%rsp),%ecx - movapd 176(%rsp),%xmm6 - movq %rax,%xmm7 - jmp .L71 -.L123: cmp $1,%r13d - je .L113 - lea (%r15,%r12),%rdx - mov %r12,%rax - .p2align 4,,10 - .p2align 3 -.L75: movupd (%rax),%xmm0 - add $16,%rax - mulpd %xmm6,%xmm0 - movups %xmm0,-16(%rax) - cmp %rdx,%rax - jne .L75 - cmp 8(%rsp),%r13d - je .L71 - movslq 16(%rsp),%rax -.L73: add %r14,%rax - lea 0(%rbp,%rax,8),%rax - movsd (%rax),%xmm0 - mulsd %xmm5,%xmm0 - movsd %xmm0,(%rax) - jmp .L71 -.L166: add $1,%eax - mov %r9d,%r15d - sal $3,%rcx - mov %r9d,%r8d - mov %eax,32(%rsp) - mov %r9d,%eax - shrl %r15d - mov %rbp,%r12 - or $1,%eax - mov %rcx,40(%rsp) - sub $8,%r11 - mov %r13d,%ecx - mov %eax,8(%rsp) - lea -1(%r9),%eax - sal $4,%r15 - and $-2,%r8d - mov %eax,96(%rsp) - lea 8(,%rax,8),%rax - mov 336(%rsp),%rdx - mov $-1,%r14 - mov %rax,192(%rsp) - lea (%rdi,%rdi),%rax - movsd .LC4(%rip),%xmm7 - mov %r9d,%r13d - add %rax,%rsi - mov %rax,88(%rsp) - mov %rdi,%rax - movsd (%rdx),%xmm5 - mov 48(%rsp),%rdx - sal $4,%rdi - lea 0(,%rax,8),%rax - mov %rsi,72(%rsp) - mov %rax,152(%rsp) - mov %r9d,%eax - movddup %xmm5,%xmm6 - shr $2,%eax - sal $3,%rdx - mov %rdi,104(%rsp) - sal $5,%rax - mov %rdx,56(%rsp) - mov %rax,64(%rsp) - mov %r9d,%eax - and $-4,%eax - movl $1,16(%rsp) - mov %eax,144(%rsp) - add $1,%eax - mov %eax,160(%rsp) - .p2align 4,,10 - .p2align 3 -.L43: ucomisd %xmm3,%xmm5 - jnp .L170 -.L34: ucomisd %xmm7,%xmm5 - jp .L121 - jne .L121 -.L37: test %ecx,%ecx - je .L35 - cmp $2,%ecx - jle .L108 - mov 72(%rsp),%rdi - mov %r15,128(%rsp) - lea 16(%r11),%rsi - mov %rbx,%rax - mov $2,%edx - mov %r8d,112(%rsp) - mov $-1,%r10 - mov %rdi,24(%rsp) - mov 64(%rsp),%rdi - mov %edx,%r8d - mov %r11,136(%rsp) - mov %rsi,%r11 -.L48: movsd -8(%r11),%xmm10 - cmpl $2,96(%rsp) - mulsd %xmm2,%xmm10 - jbe .L171 - movsd (%r11),%xmm11 - mov 152(%rsp),%rsi - movddup %xmm10,%xmm9 - xor %edx,%edx - mulsd %xmm2,%xmm11 - lea (%rsi,%rax),%r9 - mov %r12,%rsi - movddup %xmm11,%xmm8 - .p2align 4,,10 - .p2align 3 -.L54: movupd 16(%rax,%rdx),%xmm0 - movupd 16(%r9,%rdx),%xmm1 - add $32,%rsi - movupd -16(%rsi),%xmm4 - mulpd %xmm9,%xmm0 - mulpd %xmm8,%xmm1 - addpd %xmm4,%xmm0 - movupd -32(%rsi),%xmm4 - addpd %xmm1,%xmm0 - movupd (%rax,%rdx),%xmm1 - mulpd %xmm9,%xmm1 - movups %xmm0,-16(%rsi) - addpd %xmm4,%xmm1 - movupd (%r9,%rdx),%xmm4 - add $32,%rdx - mulpd %xmm8,%xmm4 - addpd %xmm4,%xmm1 - movups %xmm1,-32(%rsi) - cmp %rdi,%rdx - jne .L54 - cmp %r13d,144(%rsp) - je .L47 - mov 160(%rsp),%edx -.L45: movslq %edx,%rsi - lea (%r14,%rsi),%r9 - lea (%r10,%rsi),%r15 - add 24(%rsp),%rsi - movsd (%rbx,%rsi,8),%xmm1 - movsd (%rbx,%r15,8),%xmm0 - lea 0(%rbp,%r9,8),%r9 - lea 1(%rdx),%esi - mulsd %xmm11,%xmm1 - mulsd %xmm10,%xmm0 - addsd (%r9),%xmm0 - addsd %xmm1,%xmm0 - movsd %xmm0,(%r9) - cmp %esi,%r13d - jl .L47 - movslq %esi,%rsi - add $2,%edx - lea (%r10,%rsi),%r15 - lea (%r14,%rsi),%r9 - movsd (%rbx,%r15,8),%xmm0 - mov 24(%rsp),%r15 - lea 0(%rbp,%r9,8),%r9 - mulsd %xmm10,%xmm0 - add %r15,%rsi - movsd (%rbx,%rsi,8),%xmm1 - addsd (%r9),%xmm0 - mulsd %xmm11,%xmm1 - addsd %xmm1,%xmm0 - movsd %xmm0,(%r9) - cmp %edx,%r13d - jl .L47 - movslq %edx,%rdx - lea (%r14,%rdx),%rsi - lea (%r10,%rdx),%r9 - add %r15,%rdx - mulsd (%rbx,%rdx,8),%xmm11 - lea 0(%rbp,%rsi,8),%rsi - mulsd (%rbx,%r9,8),%xmm10 - addsd (%rsi),%xmm10 - addsd %xmm10,%xmm11 - movsd %xmm11,(%rsi) -.L47: mov 88(%rsp),%rsi - lea 1(%r8),%edx - add $2,%r8d - add %rsi,24(%rsp) - add 104(%rsp),%rax - add $16,%r11 - add %rsi,%r10 - cmp %r8d,%ecx - jg .L48 - mov 128(%rsp),%r15 - mov 112(%rsp),%r8d - mov 136(%rsp),%r11 -.L44: mov (%rsp),%r9 - movslq %edx,%rdx - lea 8(%rbx),%rdi - mov %r9,%rsi - imul %rdx,%rsi - add 80(%rsp),%rsi - .p2align 4,,10 - .p2align 3 -.L52: movsd (%r11,%rdx,8),%xmm0 - mulsd %xmm2,%xmm0 - cmp $1,%r13d - je .L110 - lea (%rdi,%rsi,8),%r10 - movddup %xmm0,%xmm4 - xor %eax,%eax - .p2align 4,,10 - .p2align 3 -.L50: movupd (%r10,%rax),%xmm1 - movupd (%r12,%rax),%xmm12 - mulpd %xmm4,%xmm1 - addpd %xmm12,%xmm1 - movups %xmm1,(%r12,%rax) - add $16,%rax - cmp %rax,%r15 - jne .L50 - cmp %r8d,%r13d - je .L51 - movslq 8(%rsp),%rax -.L49: lea (%r14,%rax),%r10 - add %rsi,%rax - lea 0(%rbp,%r10,8),%r10 - mulsd (%rbx,%rax,8),%xmm0 - addsd (%r10),%xmm0 - movsd %xmm0,(%r10) -.L51: add $1,%rdx - add %r9,%rsi - cmp %edx,%ecx - jge .L52 -.L35: addl $1,16(%rsp) - add 48(%rsp),%r14 - mov 16(%rsp),%eax - add 56(%rsp),%r12 - add 40(%rsp),%r11 - cmp 32(%rsp),%eax - jne .L43 - jmp .L1 -.L110: mov $1,%eax - jmp .L49 -.L170: jne .L34 - mov 192(%rsp),%rdx - xor %esi,%esi - mov %r12,%rdi - mov %r11,168(%rsp) - mov %r8d,200(%rsp) - mov %ecx,136(%rsp) - movaps %xmm6,112(%rsp) - movsd %xmm5,128(%rsp) - movsd %xmm2,24(%rsp) - call memset - mov .LC4(%rip),%rax - pxor %xmm3,%xmm3 - movsd 24(%rsp),%xmm2 - movsd 128(%rsp),%xmm5 - movapd 112(%rsp),%xmm6 - mov 136(%rsp),%ecx - mov 200(%rsp),%r8d - movq %rax,%xmm7 - mov 168(%rsp),%r11 - jmp .L37 -.L121: cmp $1,%r13d - je .L107 - lea (%r15,%r12),%rdx - mov %r12,%rax - .p2align 4,,10 - .p2align 3 -.L41: movupd (%rax),%xmm0 - add $16,%rax - mulpd %xmm6,%xmm0 - movups %xmm0,-16(%rax) - cmp %rax,%rdx - jne .L41 - cmp %r8d,%r13d - je .L37 - movslq 8(%rsp),%rax -.L39: add %r14,%rax - lea 0(%rbp,%rax,8),%rax - movsd (%rax),%xmm0 - mulsd %xmm5,%xmm0 - movsd %xmm0,(%rax) - jmp .L37 -.L108: mov $1,%edx - jmp .L44 -.L114: mov $1,%esi - jmp .L78 -.L106: mov $1,%edx - jmp .L25 -.L171: movsd (%r11),%xmm11 - mov $1,%edx - mulsd %xmm2,%xmm11 - jmp .L45 -.L169: movsd (%r11),%xmm11 - mov $1,%edx - mulsd %xmm2,%xmm11 - jmp .L79 -.L107: mov $1,%eax - jmp .L39 -.L113: mov $1,%eax - jmp .L73 -.L165: je .L1 - jmp .L20 - .endfn dgemm_,globl - - .rodata.cst8 -.LC4: .double 1 - - .rodata.str1.1 -.LC1: .string "N" -.LC2: .string "C" -.LC3: .string "T" -.LC5: .string "DGEMM " diff --git a/third_party/dtoa/dtoa.c b/third_party/dtoa/dtoa.c index a3c338782..16a1470a8 100644 --- a/third_party/dtoa/dtoa.c +++ b/third_party/dtoa/dtoa.c @@ -6,6 +6,8 @@ #include "libc/str/str.h" #include "third_party/dtoa/dtoa.h" +#define Omit_Private_Memory + #define IEEE_8087 1 /* #define SET_INEXACT 1 */ diff --git a/third_party/duktape/duktape.mk b/third_party/duktape/duktape.mk index a43fac398..d0b5ad6ca 100644 --- a/third_party/duktape/duktape.mk +++ b/third_party/duktape/duktape.mk @@ -27,6 +27,7 @@ THIRD_PARTY_DUKTAPE_A_CHECKS = \ $(THIRD_PARTY_DUKTAPE_A).pkg THIRD_PARTY_DUKTAPE_A_DIRECTDEPS = \ + LIBC_CALLS \ LIBC_STUBS \ LIBC_FMT \ LIBC_TIME \ diff --git a/third_party/lemon/README b/third_party/lemon/README new file mode 100644 index 000000000..0179f0c4f --- /dev/null +++ b/third_party/lemon/README @@ -0,0 +1,1022 @@ + The Lemon Parser Generator + + Lemon is an LALR(1) parser generator for C. It does the same job as + "bison" and "yacc". But Lemon is not a bison or yacc clone. Lemon uses a + different grammar syntax which is designed to reduce the number of coding + errors. Lemon also uses a parsing engine that is faster than yacc and + bison and which is both reentrant and threadsafe. (Update: Since the + previous sentence was written, bison has also been updated so that it too + can generate a reentrant and threadsafe parser.) Lemon also implements + features that can be used to eliminate resource leaks, making it suitable + for use in long-running programs such as graphical user interfaces or + embedded controllers. + + This document is an introduction to the Lemon parser generator. + +1.0 Table of Contents + + * Introduction + * 1.0 Table of Contents + * 2.0 Security Notes + * 3.0 Theory of Operation + * 3.1 Command Line Options + * 3.2 The Parser Interface + * 3.2.1 Allocating The Parse Object On Stack + * 3.2.2 Interface Summary + * 3.3 Differences With YACC and BISON + * 3.4 Building The "lemon" Or "lemon.exe" Executable + * 4.0 Input File Syntax + * 4.1 Terminals and Nonterminals + * 4.2 Grammar Rules + * 4.3 Precedence Rules + * 4.4 Special Directives + * 5.0 Error Processing + * 6.0 History of Lemon + * 7.0 Copyright + +2.0 Security Note + + The language parser code created by Lemon is very robust and is + well-suited for use in internet-facing applications that need to safely + process maliciously crafted inputs. + + The "lemon.exe" command-line tool itself works great when given a valid + input grammar file and almost always gives helpful error messages for + malformed inputs. However, it is possible for a malicious user to craft a + grammar file that will cause lemon.exe to crash. We do not see this as a + problem, as lemon.exe is not intended to be used with hostile inputs. To + summarize: + + * Parser code generated by lemon → Robust and secure + * The "lemon.exe" command line tool itself → Not so much + +3.0 Theory of Operation + + Lemon is computer program that translates a context free grammar (CFG) for + a particular language into C code that implements a parser for that + language. The Lemon program has two inputs: + + * The grammar specification. + * A parser template file. + + Typically, only the grammar specification is supplied by the programmer. + Lemon comes with a default parser template ("lempar.c") that works fine + for most applications. But the user is free to substitute a different + parser template if desired. + + Depending on command-line options, Lemon will generate up to three output + files. + + * C code to implement a parser for the input grammar. + * A header file defining an integer ID for each terminal symbol (or + "token"). + * An information file that describes the states of the generated parser + automaton. + + By default, all three of these output files are generated. The header file + is suppressed if the "-m" command-line option is used and the report file + is omitted when "-q" is selected. + + The grammar specification file uses a ".y" suffix, by convention. In the + examples used in this document, we'll assume the name of the grammar file + is "gram.y". A typical use of Lemon would be the following command: + + lemon gram.y + + This command will generate three output files named "gram.c", "gram.h" and + "gram.out". The first is C code to implement the parser. The second is the + header file that defines numerical values for all terminal symbols, and + the last is the report that explains the states used by the parser + automaton. + + 3.1 Command Line Options + + The behavior of Lemon can be modified using command-line options. You can + obtain a list of the available command-line options together with a brief + explanation of what each does by typing + + lemon "-?" + + As of this writing, the following command-line options are supported: + + * -b Show only the basis for each parser state in the report file. + * -c Do not compress the generated action tables. The parser will be a + little larger and slower, but it will detect syntax errors sooner. + * -ddirectory Write all output files into directory. Normally, output + files are written into the directory that contains the input grammar + file. + * -Dname Define C preprocessor macro name. This macro is usable by + "%ifdef", "%ifndef", and "%if lines in the grammar file. + * -E Run the "%if" preprocessor step only and print the revised grammar + file. + * -g Do not generate a parser. Instead write the input grammar to + standard output with all comments, actions, and other extraneous text + removed. + * -l Omit "#line" directives in the generated parser C code. + * -m Cause the output C source code to be compatible with the + "makeheaders" program. + * -p Display all conflicts that are resolved by precedence rules. + * -q Suppress generation of the report file. + * -r Do not sort or renumber the parser states as part of optimization. + * -s Show parser statistics before exiting. + * -Tfile Use file as the template for the generated C-code parser + implementation. + * -x Print the Lemon version number. + + 3.2 The Parser Interface + + Lemon doesn't generate a complete, working program. It only generates a + few subroutines that implement a parser. This section describes the + interface to those subroutines. It is up to the programmer to call these + subroutines in an appropriate way in order to produce a complete system. + + Before a program begins using a Lemon-generated parser, the program must + first create the parser. A new parser is created as follows: + + void *pParser = ParseAlloc( malloc ); + + The ParseAlloc() routine allocates and initializes a new parser and + returns a pointer to it. The actual data structure used to represent a + parser is opaque — its internal structure is not visible or usable by the + calling routine. For this reason, the ParseAlloc() routine returns a + pointer to void rather than a pointer to some particular structure. The + sole argument to the ParseAlloc() routine is a pointer to the subroutine + used to allocate memory. Typically this means malloc(). + + After a program is finished using a parser, it can reclaim all memory + allocated by that parser by calling + + ParseFree(pParser, free); + + The first argument is the same pointer returned by ParseAlloc(). The + second argument is a pointer to the function used to release bulk memory + back to the system. + + After a parser has been allocated using ParseAlloc(), the programmer must + supply the parser with a sequence of tokens (terminal symbols) to be + parsed. This is accomplished by calling the following function once for + each token: + + Parse(pParser, hTokenID, sTokenData, pArg); + + The first argument to the Parse() routine is the pointer returned by + ParseAlloc(). The second argument is a small positive integer that tells + the parser the type of the next token in the data stream. There is one + token type for each terminal symbol in the grammar. The gram.h file + generated by Lemon contains #define statements that map symbolic terminal + symbol names into appropriate integer values. A value of 0 for the second + argument is a special flag to the parser to indicate that the end of input + has been reached. The third argument is the value of the given token. By + default, the type of the third argument is "void*", but the grammar will + usually redefine this type to be some kind of structure. Typically the + second argument will be a broad category of tokens such as "identifier" or + "number" and the third argument will be the name of the identifier or the + value of the number. + + The Parse() function may have either three or four arguments, depending on + the grammar. If the grammar specification file requests it (via the + %extra_argument directive), the Parse() function will have a fourth + parameter that can be of any type chosen by the programmer. The parser + doesn't do anything with this argument except to pass it through to action + routines. This is a convenient mechanism for passing state information + down to the action routines without having to use global variables. + + A typical use of a Lemon parser might look something like the following: + + 1 ParseTree *ParseFile(const char *zFilename){ + 2 Tokenizer *pTokenizer; + 3 void *pParser; + 4 Token sToken; + 5 int hTokenId; + 6 ParserState sState; + 7 + 8 pTokenizer = TokenizerCreate(zFilename); + 9 pParser = ParseAlloc( malloc ); + 10 InitParserState(&sState); + 11 while( GetNextToken(pTokenizer, &hTokenId, &sToken) ){ + 12 Parse(pParser, hTokenId, sToken, &sState); + 13 } + 14 Parse(pParser, 0, sToken, &sState); + 15 ParseFree(pParser, free ); + 16 TokenizerFree(pTokenizer); + 17 return sState.treeRoot; + 18 } + + This example shows a user-written routine that parses a file of text and + returns a pointer to the parse tree. (All error-handling code is omitted + from this example to keep it simple.) We assume the existence of some kind + of tokenizer which is created using TokenizerCreate() on line 8 and + deleted by TokenizerFree() on line 16. The GetNextToken() function on line + 11 retrieves the next token from the input file and puts its type in the + integer variable hTokenId. The sToken variable is assumed to be some kind + of structure that contains details about each token, such as its complete + text, what line it occurs on, etc. + + This example also assumes the existence of a structure of type ParserState + that holds state information about a particular parse. An instance of such + a structure is created on line 6 and initialized on line 10. A pointer to + this structure is passed into the Parse() routine as the optional 4th + argument. The action routine specified by the grammar for the parser can + use the ParserState structure to hold whatever information is useful and + appropriate. In the example, we note that the treeRoot field of the + ParserState structure is left pointing to the root of the parse tree. + + The core of this example as it relates to Lemon is as follows: + + ParseFile(){ + pParser = ParseAlloc( malloc ); + while( GetNextToken(pTokenizer,&hTokenId, &sToken) ){ + Parse(pParser, hTokenId, sToken); + } + Parse(pParser, 0, sToken); + ParseFree(pParser, free ); + } + + Basically, what a program has to do to use a Lemon-generated parser is + first create the parser, then send it lots of tokens obtained by + tokenizing an input source. When the end of input is reached, the Parse() + routine should be called one last time with a token type of 0. This step + is necessary to inform the parser that the end of input has been reached. + Finally, we reclaim memory used by the parser by calling ParseFree(). + + There is one other interface routine that should be mentioned before we + move on. The ParseTrace() function can be used to generate debugging + output from the parser. A prototype for this routine is as follows: + + ParseTrace(FILE *stream, char *zPrefix); + + After this routine is called, a short (one-line) message is written to the + designated output stream every time the parser changes states or calls an + action routine. Each such message is prefaced using the text given by + zPrefix. This debugging output can be turned off by calling ParseTrace() + again with a first argument of NULL (0). + + 3.2.1 Allocating The Parse Object On Stack + + If all calls to the Parse() interface are made from within %code + directives, then the parse object can be allocated from the stack rather + than from the heap. These are the steps: + * Declare a local variable of type "yyParser" + * Initialize the variable using ParseInit() + * Pass a pointer to the variable in calls ot Parse() + * Deallocate substructure in the parse variable using ParseFinalize(). + + The following code illustrates how this is done: + + ParseFile(){ + yyParser x; + ParseInit( &x ); + while( GetNextToken(pTokenizer,&hTokenId, &sToken) ){ + Parse(&x, hTokenId, sToken); + } + Parse(&x, 0, sToken); + ParseFinalize( &x ); + } + + 3.2.2 Interface Summary + + Here is a quick overview of the C-language interface to a Lemon-generated + parser: + + void *ParseAlloc( (void*(*malloc)(size_t) ); + void ParseFree(void *pParser, (void(*free)(void*) ); + void Parse(void *pParser, int tokenCode, ParseTOKENTYPE token, ...); + void ParseTrace(FILE *stream, char *zPrefix); + + Notes: + + * Use the %name directive to change the "Parse" prefix names of the + procedures in the interface. + * Use the %token_type directive to define the "ParseTOKENTYPE" type. + * Use the %extra_argument directive to specify the type and name of the + 4th parameter to the Parse() function. + + 3.3 Differences With YACC and BISON + + Programmers who have previously used the yacc or bison parser generator + will notice several important differences between yacc and/or bison and + Lemon. + + * In yacc and bison, the parser calls the tokenizer. In Lemon, the + tokenizer calls the parser. + * Lemon uses no global variables. Yacc and bison use global variables to + pass information between the tokenizer and parser. + * Lemon allows multiple parsers to be running simultaneously. Yacc and + bison do not. + + These differences may cause some initial confusion for programmers with + prior yacc and bison experience. But after years of experience using + Lemon, I firmly believe that the Lemon way of doing things is better. + + Updated as of 2016-02-16: The text above was written in the 1990s. We are + told that Bison has lately been enhanced to support the + tokenizer-calls-parser paradigm used by Lemon, eliminating the need for + global variables. + + 3.4 Building The "lemon" or "lemon.exe" Executable + + The "lemon" or "lemon.exe" program is built from a single file of C-code + named "lemon.c". The Lemon source code is generic C89 code that uses no + unusual or non-standard libraries. Any reasonable C compiler should + suffice to compile the lemon program. A command-line like the following + will usually work: + + cc -o lemon lemon.c + + On Windows machines with Visual C++ installed, bring up a "VS20NN x64 + Native Tools Command Prompt" window and enter: + + cl lemon.c + + Compiling Lemon really is that simple. Additional compiler options such as + "-O2" or "-g" or "-Wall" can be added if desired, but they are not + necessary. + +4.0 Input File Syntax + + The main purpose of the grammar specification file for Lemon is to define + the grammar for the parser. But the input file also specifies additional + information Lemon requires to do its job. Most of the work in using Lemon + is in writing an appropriate grammar file. + + The grammar file for Lemon is, for the most part, a free format. It does + not have sections or divisions like yacc or bison. Any declaration can + occur at any point in the file. Lemon ignores whitespace (except where it + is needed to separate tokens), and it honors the same commenting + conventions as C and C++. + + 4.1 Terminals and Nonterminals + + A terminal symbol (token) is any string of alphanumeric and/or underscore + characters that begins with an uppercase letter. A terminal can contain + lowercase letters after the first character, but the usual convention is + to make terminals all uppercase. A nonterminal, on the other hand, is any + string of alphanumeric and underscore characters than begins with a + lowercase letter. Again, the usual convention is to make nonterminals use + all lowercase letters. + + In Lemon, terminal and nonterminal symbols do not need to be declared or + identified in a separate section of the grammar file. Lemon is able to + generate a list of all terminals and nonterminals by examining the grammar + rules, and it can always distinguish a terminal from a nonterminal by + checking the case of the first character of the name. + + Yacc and bison allow terminal symbols to have either alphanumeric names or + to be individual characters included in single quotes, like this: ')' or + '$'. Lemon does not allow this alternative form for terminal symbols. With + Lemon, all symbols, terminals and nonterminals, must have alphanumeric + names. + + 4.2 Grammar Rules + + The main component of a Lemon grammar file is a sequence of grammar rules. + Each grammar rule consists of a nonterminal symbol followed by the special + symbol "::=" and then a list of terminals and/or nonterminals. The rule is + terminated by a period. The list of terminals and nonterminals on the + right-hand side of the rule can be empty. Rules can occur in any order, + except that the left-hand side of the first rule is assumed to be the + start symbol for the grammar (unless specified otherwise using the + %start_symbol directive described below.) A typical sequence of grammar + rules might look something like this: + + expr ::= expr PLUS expr. + expr ::= expr TIMES expr. + expr ::= LPAREN expr RPAREN. + expr ::= VALUE. + + There is one non-terminal in this example, "expr", and five terminal + symbols or tokens: "PLUS", "TIMES", "LPAREN", "RPAREN" and "VALUE". + + Like yacc and bison, Lemon allows the grammar to specify a block of C code + that will be executed whenever a grammar rule is reduced by the parser. In + Lemon, this action is specified by putting the C code (contained within + curly braces {...}) immediately after the period that closes the rule. For + example: + + expr ::= expr PLUS expr. { printf("Doing an addition...\n"); } + + In order to be useful, grammar actions must normally be linked to their + associated grammar rules. In yacc and bison, this is accomplished by + embedding a "$$" in the action to stand for the value of the left-hand + side of the rule and symbols "$1", "$2", and so forth to stand for the + value of the terminal or nonterminal at position 1, 2 and so forth on the + right-hand side of the rule. This idea is very powerful, but it is also + very error-prone. The single most common source of errors in a yacc or + bison grammar is to miscount the number of symbols on the right-hand side + of a grammar rule and say "$7" when you really mean "$8". + + Lemon avoids the need to count grammar symbols by assigning symbolic names + to each symbol in a grammar rule and then using those symbolic names in + the action. In yacc or bison, one would write this: + + expr -> expr PLUS expr { $$ = $1 + $3; }; + + But in Lemon, the same rule becomes the following: + + expr(A) ::= expr(B) PLUS expr(C). { A = B+C; } + + In the Lemon rule, any symbol in parentheses after a grammar rule symbol + becomes a place holder for that symbol in the grammar rule. This place + holder can then be used in the associated C action to stand for the value + of that symbol. + + The Lemon notation for linking a grammar rule with its reduce action is + superior to yacc/bison on several counts. First, as mentioned above, the + Lemon method avoids the need to count grammar symbols. Secondly, if a + terminal or nonterminal in a Lemon grammar rule includes a linking symbol + in parentheses but that linking symbol is not actually used in the reduce + action, then an error message is generated. For example, the rule + + expr(A) ::= expr(B) PLUS expr(C). { A = B; } + + will generate an error because the linking symbol "C" is used in the + grammar rule but not in the reduce action. + + The Lemon notation for linking grammar rules to reduce actions also + facilitates the use of destructors for reclaiming memory allocated by the + values of terminals and nonterminals on the right-hand side of a rule. + + 4.3 Precedence Rules + + Lemon resolves parsing ambiguities in exactly the same way as yacc and + bison. A shift-reduce conflict is resolved in favor of the shift, and a + reduce-reduce conflict is resolved by reducing whichever rule comes first + in the grammar file. + + Just like in yacc and bison, Lemon allows a measure of control over the + resolution of parsing conflicts using precedence rules. A precedence value + can be assigned to any terminal symbol using the %left, %right or + %nonassoc directives. Terminal symbols mentioned in earlier directives + have a lower precedence than terminal symbols mentioned in later + directives. For example: + + %left AND. + %left OR. + %nonassoc EQ NE GT GE LT LE. + %left PLUS MINUS. + %left TIMES DIVIDE MOD. + %right EXP NOT. + + In the preceding sequence of directives, the AND operator is defined to + have the lowest precedence. The OR operator is one precedence level + higher. And so forth. Hence, the grammar would attempt to group the + ambiguous expression + + a AND b OR c + + like this + + a AND (b OR c). + + The associativity (left, right or nonassoc) is used to determine the + grouping when the precedence is the same. AND is left-associative in our + example, so + + a AND b AND c + + is parsed like this + + (a AND b) AND c. + + The EXP operator is right-associative, though, so + + a EXP b EXP c + + is parsed like this + + a EXP (b EXP c). + + The nonassoc precedence is used for non-associative operators. So + + a EQ b EQ c + + is an error. + + The precedence of non-terminals is transferred to rules as follows: The + precedence of a grammar rule is equal to the precedence of the left-most + terminal symbol in the rule for which a precedence is defined. This is + normally what you want, but in those cases where you want the precedence + of a grammar rule to be something different, you can specify an + alternative precedence symbol by putting the symbol in square braces after + the period at the end of the rule and before any C-code. For example: + + expr = MINUS expr. [NOT] + + This rule has a precedence equal to that of the NOT symbol, not the MINUS + symbol as would have been the case by default. + + With the knowledge of how precedence is assigned to terminal symbols and + individual grammar rules, we can now explain precisely how parsing + conflicts are resolved in Lemon. Shift-reduce conflicts are resolved as + follows: + + * If either the token to be shifted or the rule to be reduced lacks + precedence information, then resolve in favor of the shift, but report + a parsing conflict. + * If the precedence of the token to be shifted is greater than the + precedence of the rule to reduce, then resolve in favor of the shift. + No parsing conflict is reported. + * If the precedence of the token to be shifted is less than the + precedence of the rule to reduce, then resolve in favor of the reduce + action. No parsing conflict is reported. + * If the precedences are the same and the shift token is + right-associative, then resolve in favor of the shift. No parsing + conflict is reported. + * If the precedences are the same and the shift token is + left-associative, then resolve in favor of the reduce. No parsing + conflict is reported. + * Otherwise, resolve the conflict by doing the shift, and report a + parsing conflict. + + Reduce-reduce conflicts are resolved this way: + + * If either reduce rule lacks precedence information, then resolve in + favor of the rule that appears first in the grammar, and report a + parsing conflict. + * If both rules have precedence and the precedence is different, then + resolve the dispute in favor of the rule with the highest precedence, + and do not report a conflict. + * Otherwise, resolve the conflict by reducing by the rule that appears + first in the grammar, and report a parsing conflict. + + 4.4 Special Directives + + The input grammar to Lemon consists of grammar rules and special + directives. We've described all the grammar rules, so now we'll talk about + the special directives. + + Directives in Lemon can occur in any order. You can put them before the + grammar rules, or after the grammar rules, or in the midst of the grammar + rules. It doesn't matter. The relative order of directives used to assign + precedence to terminals is important, but other than that, the order of + directives in Lemon is arbitrary. + + Lemon supports the following special directives: + + * %code + * %default_destructor + * %default_type + * %destructor + * %else + * %endif + * %extra_argument + * %fallback + * %if + * %ifdef + * %ifndef + * %include + * %left + * %name + * %nonassoc + * %parse_accept + * %parse_failure + * %right + * %stack_overflow + * %stack_size + * %start_symbol + * %syntax_error + * %token_class + * %token_destructor + * %token_prefix + * %token_type + * %type + * %wildcard + + Each of these directives will be described separately in the following + sections: + + 4.4.1 The %code directive + + The %code directive is used to specify additional C code that is added to + the end of the main output file. This is similar to the %include directive + except that %include is inserted at the beginning of the main output file. + + %code is typically used to include some action routines or perhaps a + tokenizer or even the "main()" function as part of the output file. + + There can be multiple %code directives. The arguments of all %code + directives are concatenated. + + 4.4.2 The %default_destructor directive + + The %default_destructor directive specifies a destructor to use for + non-terminals that do not have their own destructor specified by a + separate %destructor directive. See the documentation on the %destructor + directive below for additional information. + + In some grammars, many different non-terminal symbols have the same data + type and hence the same destructor. This directive is a convenient way to + specify the same destructor for all those non-terminals using a single + statement. + + 4.4.3 The %default_type directive + + The %default_type directive specifies the data type of non-terminal + symbols that do not have their own data type defined using a separate + %type directive. + + 4.4.4 The %destructor directive + + The %destructor directive is used to specify a destructor for a + non-terminal symbol. (See also the %token_destructor directive which is + used to specify a destructor for terminal symbols.) + + A non-terminal's destructor is called to dispose of the non-terminal's + value whenever the non-terminal is popped from the stack. This includes + all of the following circumstances: + + * When a rule reduces and the value of a non-terminal on the right-hand + side is not linked to C code. + * When the stack is popped during error processing. + * When the ParseFree() function runs. + + The destructor can do whatever it wants with the value of the + non-terminal, but its design is to deallocate memory or other resources + held by that non-terminal. + + Consider an example: + + %type nt {void*} + %destructor nt { free($$); } + nt(A) ::= ID NUM. { A = malloc( 100 ); } + + This example is a bit contrived, but it serves to illustrate how + destructors work. The example shows a non-terminal named "nt" that holds + values of type "void*". When the rule for an "nt" reduces, it sets the + value of the non-terminal to space obtained from malloc(). Later, when the + nt non-terminal is popped from the stack, the destructor will fire and + call free() on this malloced space, thus avoiding a memory leak. (Note + that the symbol "$$" in the destructor code is replaced by the value of + the non-terminal.) + + It is important to note that the value of a non-terminal is passed to the + destructor whenever the non-terminal is removed from the stack, unless the + non-terminal is used in a C-code action. If the non-terminal is used by + C-code, then it is assumed that the C-code will take care of destroying + it. More commonly, the value is used to build some larger structure, and + we don't want to destroy it, which is why the destructor is not called in + this circumstance. + + Destructors help avoid memory leaks by automatically freeing allocated + objects when they go out of scope. To do the same using yacc or bison is + much more difficult. + + 4.4.5 The %extra_argument directive + + The %extra_argument directive instructs Lemon to add a 4th parameter to + the parameter list of the Parse() function it generates. Lemon doesn't do + anything itself with this extra argument, but it does make the argument + available to C-code action routines, destructors, and so forth. For + example, if the grammar file contains: + + %extra_argument { MyStruct *pAbc } + + Then the Parse() function generated will have an 4th parameter of type + "MyStruct*" and all action routines will have access to a variable named + "pAbc" that is the value of the 4th parameter in the most recent call to + Parse(). + + The %extra_context directive works the same except that it is passed in on + the ParseAlloc() or ParseInit() routines instead of on Parse(). + + 4.4.6 The %extra_context directive + + The %extra_context directive instructs Lemon to add a 2nd parameter to the + parameter list of the ParseAlloc() and ParseInit() functions. Lemon + doesn't do anything itself with these extra argument, but it does store + the value make it available to C-code action routines, destructors, and so + forth. For example, if the grammar file contains: + + %extra_context { MyStruct *pAbc } + + Then the ParseAlloc() and ParseInit() functions will have an 2nd parameter + of type "MyStruct*" and all action routines will have access to a variable + named "pAbc" that is the value of that 2nd parameter. + + The %extra_argument directive works the same except that it is passed in + on the Parse() routine instead of on ParseAlloc()/ParseInit(). + + 4.4.7 The %fallback directive + + The %fallback directive specifies an alternative meaning for one or more + tokens. The alternative meaning is tried if the original token would have + generated a syntax error. + + The %fallback directive was added to support robust parsing of SQL syntax + in SQLite. The SQL language contains a large assortment of keywords, each + of which appears as a different token to the language parser. SQL contains + so many keywords that it can be difficult for programmers to keep up with + them all. Programmers will, therefore, sometimes mistakenly use an obscure + language keyword for an identifier. The %fallback directive provides a + mechanism to tell the parser: "If you are unable to parse this keyword, + try treating it as an identifier instead." + + The syntax of %fallback is as follows: + + %fallback ID TOKEN... . + + In words, the %fallback directive is followed by a list of token names + terminated by a period. The first token name is the fallback token — the + token to which all the other tokens fall back to. The second and + subsequent arguments are tokens which fall back to the token identified by + the first argument. + + 4.4.8 The %if directive and its friends + + The %if, %ifdef, %ifndef, %else, and %endif directives are similar to #if, + #ifdef, #ifndef, #else, and #endif in the C-preprocessor, just not as + general. Each of these directives must begin at the left margin. No + whitespace is allowed between the "%" and the directive name. + + Grammar text in between "%ifdef MACRO" and the next nested "%endif" is + ignored unless the "-DMACRO" command-line option is used. Grammar text + betwen "%ifndef MACRO" and the next nested "%endif" is included except + when the "-DMACRO" command-line option is used. + + The text in between "%if CONDITIONAL" and its corresponding %endif is + included only if CONDITIONAL is true. The CONDITION is one or more macro + names, optionally connected using the "||" and "&&" binary operators, the + "!" unary operator, and grouped using balanced parentheses. Each term is + true if the corresponding macro exists, and false if it does not exist. + + An optional "%else" directive can occur anywhere in between a %ifdef, + %ifndef, or %if directive and its corresponding %endif. + + Note that the argument to %ifdef and %ifndef is intended to be a single + preprocessor symbol name, not a general expression. Use the "%if" + directive for general expressions. + + 4.4.9 The %include directive + + The %include directive specifies C code that is included at the top of the + generated parser. You can include any text you want — the Lemon parser + generator copies it blindly. If you have multiple %include directives in + your grammar file, their values are concatenated so that all %include code + ultimately appears near the top of the generated parser, in the same order + as it appeared in the grammar. + + The %include directive is very handy for getting some extra #include + preprocessor statements at the beginning of the generated parser. For + example: + + %include {#include } + + This might be needed, for example, if some of the C actions in the grammar + call functions that are prototyped in unistd.h. + + Use the %code directive to add code to the end of the generated parser. + + 4.4.10 The %left directive + + The %left directive is used (along with the %right and %nonassoc + directives) to declare precedences of terminal symbols. Every terminal + symbol whose name appears after a %left directive but before the next + period (".") is given the same left-associative precedence value. + Subsequent %left directives have higher precedence. For example: + + %left AND. + %left OR. + %nonassoc EQ NE GT GE LT LE. + %left PLUS MINUS. + %left TIMES DIVIDE MOD. + %right EXP NOT. + + Note the period that terminates each %left, %right or %nonassoc directive. + + LALR(1) grammars can get into a situation where they require a large + amount of stack space if you make heavy use or right-associative + operators. For this reason, it is recommended that you use %left rather + than %right whenever possible. + + 4.4.11 The %name directive + + By default, the functions generated by Lemon all begin with the + five-character string "Parse". You can change this string to something + different using the %name directive. For instance: + + %name Abcde + + Putting this directive in the grammar file will cause Lemon to generate + functions named + + * AbcdeAlloc(), + * AbcdeFree(), + * AbcdeTrace(), and + * Abcde(). + The %name directive allows you to generate two or more different parsers + and link them all into the same executable. + + 4.4.12 The %nonassoc directive + + This directive is used to assign non-associative precedence to one or more + terminal symbols. See the section on precedence rules or on the %left + directive for additional information. + + 4.4.13 The %parse_accept directive + + The %parse_accept directive specifies a block of C code that is executed + whenever the parser accepts its input string. To "accept" an input string + means that the parser was able to process all tokens without error. + + For example: + + %parse_accept { + printf("parsing complete!\n"); + } + + 4.4.14 The %parse_failure directive + + The %parse_failure directive specifies a block of C code that is executed + whenever the parser fails complete. This code is not executed until the + parser has tried and failed to resolve an input error using is usual error + recovery strategy. The routine is only invoked when parsing is unable to + continue. + + %parse_failure { + fprintf(stderr,"Giving up. Parser is hopelessly lost...\n"); + } + + 4.4.15 The %right directive + + This directive is used to assign right-associative precedence to one or + more terminal symbols. See the section on precedence rules or on the %left + directive for additional information. + + 4.4.16 The %stack_overflow directive + + The %stack_overflow directive specifies a block of C code that is executed + if the parser's internal stack ever overflows. Typically this just prints + an error message. After a stack overflow, the parser will be unable to + continue and must be reset. + + %stack_overflow { + fprintf(stderr,"Giving up. Parser stack overflow\n"); + } + + You can help prevent parser stack overflows by avoiding the use of right + recursion and right-precedence operators in your grammar. Use left + recursion and and left-precedence operators instead to encourage rules to + reduce sooner and keep the stack size down. For example, do rules like + this: + + list ::= list element. // left-recursion. Good! + list ::= . + + Not like this: + + list ::= element list. // right-recursion. Bad! + list ::= . + + 4.4.17 The %stack_size directive + + If stack overflow is a problem and you can't resolve the trouble by using + left-recursion, then you might want to increase the size of the parser's + stack using this directive. Put an positive integer after the %stack_size + directive and Lemon will generate a parse with a stack of the requested + size. The default value is 100. + + %stack_size 2000 + + 4.4.18 The %start_symbol directive + + By default, the start symbol for the grammar that Lemon generates is the + first non-terminal that appears in the grammar file. But you can choose a + different start symbol using the %start_symbol directive. + + %start_symbol prog + + 4.4.19 The %syntax_error directive + + See Error Processing. + + 4.4.20 The %token_class directive + + Undocumented. Appears to be related to the MULTITERMINAL concept. + Implementation. + + 4.4.21 The %token_destructor directive + + The %destructor directive assigns a destructor to a non-terminal symbol. + (See the description of the %destructor directive above.) The + %token_destructor directive does the same thing for all terminal symbols. + + Unlike non-terminal symbols, which may each have a different data type for + their values, terminals all use the same data type (defined by the + %token_type directive) and so they use a common destructor. Other than + that, the token destructor works just like the non-terminal destructors. + + 4.4.22 The %token_prefix directive + + Lemon generates #defines that assign small integer constants to each + terminal symbol in the grammar. If desired, Lemon will add a prefix + specified by this directive to each of the #defines it generates. + + So if the default output of Lemon looked like this: + + #define AND 1 + #define MINUS 2 + #define OR 3 + #define PLUS 4 + + You can insert a statement into the grammar like this: + + %token_prefix TOKEN_ + + to cause Lemon to produce these symbols instead: + + #define TOKEN_AND 1 + #define TOKEN_MINUS 2 + #define TOKEN_OR 3 + #define TOKEN_PLUS 4 + + 4.4.23 The %token_type and %type directives + + These directives are used to specify the data types for values on the + parser's stack associated with terminal and non-terminal symbols. The + values of all terminal symbols must be of the same type. This turns out to + be the same data type as the 3rd parameter to the Parse() function + generated by Lemon. Typically, you will make the value of a terminal + symbol be a pointer to some kind of token structure. Like this: + + %token_type {Token*} + + If the data type of terminals is not specified, the default value is + "void*". + + Non-terminal symbols can each have their own data types. Typically the + data type of a non-terminal is a pointer to the root of a parse tree + structure that contains all information about that non-terminal. For + example: + + %type expr {Expr*} + + Each entry on the parser's stack is actually a union containing instances + of all data types for every non-terminal and terminal symbol. Lemon will + automatically use the correct element of this union depending on what the + corresponding non-terminal or terminal symbol is. But the grammar designer + should keep in mind that the size of the union will be the size of its + largest element. So if you have a single non-terminal whose data type + requires 1K of storage, then your 100 entry parser stack will require 100K + of heap space. If you are willing and able to pay that price, fine. You + just need to know. + + 4.4.24 The %wildcard directive + + The %wildcard directive is followed by a single token name and a period. + This directive specifies that the identified token should match any input + token. + + When the generated parser has the choice of matching an input against the + wildcard token and some other token, the other token is always used. The + wildcard token is only matched if there are no alternatives. + +5.0 Error Processing + + After extensive experimentation over several years, it has been discovered + that the error recovery strategy used by yacc is about as good as it gets. + And so that is what Lemon uses. + + When a Lemon-generated parser encounters a syntax error, it first invokes + the code specified by the %syntax_error directive, if any. It then enters + its error recovery strategy. The error recovery strategy is to begin + popping the parsers stack until it enters a state where it is permitted to + shift a special non-terminal symbol named "error". It then shifts this + non-terminal and continues parsing. The %syntax_error routine will not be + called again until at least three new tokens have been successfully + shifted. + + If the parser pops its stack until the stack is empty, and it still is + unable to shift the error symbol, then the %parse_failure routine is + invoked and the parser resets itself to its start state, ready to begin + parsing a new file. This is what will happen at the very first syntax + error, of course, if there are no instances of the "error" non-terminal in + your grammar. + +6.0 History of Lemon + + Lemon was originally written by Richard Hipp sometime in the late 1980s on + a Sun4 Workstation using K&R C. There was a companion LL(1) parser + generator program named "Lime", the source code to which as been lost. + + The lemon.c source file was originally many separate files that were + compiled together to generate the "lemon" executable. Sometime in the + 1990s, the individual source code files were combined together into the + current single large "lemon.c" source file. You can still see traces of + original filenames in the code. + + Since 2001, Lemon has been part of the SQLite project and the source code + to Lemon has been managed as a part of the SQLite source tree in the + following files: + + * tool/lemon.c + * tool/lempar.c + * doc/lemon.html + +7.0 Copyright + + All of the source code to Lemon, including the template parser file + "lempar.c" and this documentation file ("lemon.html") are in the public + domain. You can use the code for any purpose and without attribution. + + The code comes with no warranty. If it breaks, you get to keep both + pieces. diff --git a/third_party/lemon/lemon.c b/third_party/lemon/lemon.c new file mode 100644 index 000000000..fb948d670 --- /dev/null +++ b/third_party/lemon/lemon.c @@ -0,0 +1,5968 @@ +#if 0 +/*─────────────────────────────────────────────────────────────────╗ +│ To the extent possible under law, Justine Tunney has waived │ +│ all copyright and related or neighboring rights to this file, │ +│ as it is written in the following disclaimers: │ +│ • http://unlicense.org/ │ +│ • http://creativecommons.org/publicdomain/zero/1.0/ │ +╚─────────────────────────────────────────────────────────────────*/ +#endif +#include "libc/alg/alg.h" +#include "libc/assert.h" +#include "libc/calls/calls.h" +#include "libc/conv/conv.h" +#include "libc/fmt/fmt.h" +#include "libc/mem/mem.h" +#include "libc/runtime/gc.h" +#include "libc/runtime/runtime.h" +#include "libc/stdio/stdio.h" +#include "libc/str/str.h" +#include "libc/x/x.h" +#include "third_party/dtoa/dtoa.h" + +/* +** This file contains all sources (including headers) to the LEMON +** LALR(1) parser generator. The sources have been combined into a +** single file to make it easy to include LEMON in the source tree +** and Makefile of another program. +** +** The author of this program disclaims copyright. +*/ + +#define ISSPACE(X) isspace((unsigned char)(X)) +#define ISDIGIT(X) isdigit((unsigned char)(X)) +#define ISALNUM(X) isalnum((unsigned char)(X)) +#define ISALPHA(X) isalpha((unsigned char)(X)) +#define ISUPPER(X) isupper((unsigned char)(X)) +#define ISLOWER(X) islower((unsigned char)(X)) + +#define PRIVATE static +/* #define PRIVATE */ + +#ifdef TEST +#define MAXRHS 5 /* Set low to exercise exception code */ +#else +#define MAXRHS 1000 +#endif + +extern void memory_error(); +static int showPrecedenceConflict = 0; +static char *msort(char *, char **, int (*)(const char *, const char *)); + +/* +** Compilers are getting increasingly pedantic about type conversions +** as C evolves ever closer to Ada.... To work around the latest problems +** we have to define the following variant of strlen(). +*/ +#define lemonStrlen(X) ((int)strlen(X)) + +/* +** Compilers are starting to complain about the use of sprintf() and strcpy(), +** saying they are unsafe. So we define our own versions of those routines too. +** +** There are three routines here: lemon_sprintf(), lemon_vsprintf(), and +** lemon_addtext(). The first two are replacements for sprintf() and vsprintf(). +** The third is a helper routine for vsnprintf() that adds texts to the end of a +** buffer, making sure the buffer is always zero-terminated. +** +** The string formatter is a minimal subset of stdlib sprintf() supporting only +** a few simply conversions: +** +** %d +** %s +** %.*s +** +*/ +static void lemon_addtext( + char *zBuf, /* The buffer to which text is added */ + int *pnUsed, /* Slots of the buffer used so far */ + const char *zIn, /* Text to add */ + int nIn, /* Bytes of text to add. -1 to use strlen() */ + int iWidth /* Field width. Negative to left justify */ +) { + if (nIn < 0) + for (nIn = 0; zIn[nIn]; nIn++) { + } + while (iWidth > nIn) { + zBuf[(*pnUsed)++] = ' '; + iWidth--; + } + if (nIn == 0) return; + memcpy(&zBuf[*pnUsed], zIn, nIn); + *pnUsed += nIn; + while ((-iWidth) > nIn) { + zBuf[(*pnUsed)++] = ' '; + iWidth++; + } + zBuf[*pnUsed] = 0; +} +static int lemon_vsprintf(char *str, const char *zFormat, va_list ap) { + int i, j, k, c; + int nUsed = 0; + const char *z; + char zTemp[50]; + str[0] = 0; + for (i = j = 0; (c = zFormat[i]) != 0; i++) { + if (c == '%') { + int iWidth = 0; + lemon_addtext(str, &nUsed, &zFormat[j], i - j, 0); + c = zFormat[++i]; + if (ISDIGIT(c) || (c == '-' && ISDIGIT(zFormat[i + 1]))) { + if (c == '-') i++; + while (ISDIGIT(zFormat[i])) iWidth = iWidth * 10 + zFormat[i++] - '0'; + if (c == '-') iWidth = -iWidth; + c = zFormat[i]; + } + if (c == 'd') { + int v = va_arg(ap, int); + if (v < 0) { + lemon_addtext(str, &nUsed, "-", 1, iWidth); + v = -v; + } else if (v == 0) { + lemon_addtext(str, &nUsed, "0", 1, iWidth); + } + k = 0; + while (v > 0) { + k++; + zTemp[sizeof(zTemp) - k] = (v % 10) + '0'; + v /= 10; + } + lemon_addtext(str, &nUsed, &zTemp[sizeof(zTemp) - k], k, iWidth); + } else if (c == 's') { + z = va_arg(ap, const char *); + lemon_addtext(str, &nUsed, z, -1, iWidth); + } else if (c == '.' && memcmp(&zFormat[i], ".*s", 3) == 0) { + i += 2; + k = va_arg(ap, int); + z = va_arg(ap, const char *); + lemon_addtext(str, &nUsed, z, k, iWidth); + } else if (c == '%') { + lemon_addtext(str, &nUsed, "%", 1, 0); + } else { + fprintf(stderr, "illegal format\n"); + exit(1); + } + j = i + 1; + } + } + lemon_addtext(str, &nUsed, &zFormat[j], i - j, 0); + return nUsed; +} +static int lemon_sprintf(char *str, const char *format, ...) { + va_list ap; + int rc; + va_start(ap, format); + rc = lemon_vsprintf(str, format, ap); + va_end(ap); + return rc; +} +static void lemon_strcpy(char *dest, const char *src) { + while ((*(dest++) = *(src++)) != 0) { + } +} +static void lemon_strcat(char *dest, const char *src) { + while (*dest) dest++; + lemon_strcpy(dest, src); +} + +/* a few forward declarations... */ +struct rule; +struct lemon; +struct action; + +static struct action *Action_new(void); +static struct action *Action_sort(struct action *); + +/********** From the file "build.h" ************************************/ +void FindRulePrecedences(struct lemon *); +void FindFirstSets(struct lemon *); +void FindStates(struct lemon *); +void FindLinks(struct lemon *); +void FindFollowSets(struct lemon *); +void FindActions(struct lemon *); + +/********* From the file "configlist.h" *********************************/ +void Configlist_init(void); +struct config *Configlist_add(struct rule *, int); +struct config *Configlist_addbasis(struct rule *, int); +void Configlist_closure(struct lemon *); +void Configlist_sort(void); +void Configlist_sortbasis(void); +struct config *Configlist_return(void); +struct config *Configlist_basis(void); +void Configlist_eat(struct config *); +void Configlist_reset(void); + +/********* From the file "error.h" ***************************************/ +void ErrorMsg(const char *, int, const char *, ...); + +/****** From the file "option.h" ******************************************/ +enum option_type { + OPT_FLAG = 1, + OPT_INT, + OPT_DBL, + OPT_STR, + OPT_FFLAG, + OPT_FINT, + OPT_FDBL, + OPT_FSTR +}; +struct s_options { + enum option_type type; + const char *label; + char *arg; + const char *message; +}; +int OptInit(char **, struct s_options *, FILE *); +int OptNArgs(void); +char *OptArg(int); +void OptErr(int); +void OptPrint(void); + +/******** From the file "parse.h" *****************************************/ +void Parse(struct lemon *lemp); + +/********* From the file "plink.h" ***************************************/ +struct plink *Plink_new(void); +void Plink_add(struct plink **, struct config *); +void Plink_copy(struct plink **, struct plink *); +void Plink_delete(struct plink *); + +/********** From the file "report.h" *************************************/ +void Reprint(struct lemon *); +void ReportOutput(struct lemon *); +void ReportTable(struct lemon *, int, int); +void ReportHeader(struct lemon *); +void CompressTables(struct lemon *); +void ResortStates(struct lemon *); + +/********** From the file "set.h" ****************************************/ +void SetSize(int); /* All sets will be of size N */ +char *SetNew(void); /* A new set for element 0..N */ +void SetFree(char *); /* Deallocate a set */ +int SetAdd(char *, int); /* Add element to a set */ +int SetUnion(char *, char *); /* A <- A U B, thru element N */ +#define SetFind(X, Y) (X[Y]) /* True if Y is in set X */ + +/********** From the file "struct.h" *************************************/ +/* +** Principal data structures for the LEMON parser generator. +*/ + +typedef enum { LEMON_FALSE = 0, LEMON_TRUE } Boolean; + +/* Symbols (terminals and nonterminals) of the grammar are stored +** in the following: */ +enum symbol_type { TERMINAL, NONTERMINAL, MULTITERMINAL }; +enum e_assoc { LEFT, RIGHT, NONE, UNK }; +struct symbol { + const char *name; /* Name of the symbol */ + int index; /* Index number for this symbol */ + enum symbol_type type; /* Symbols are all either TERMINALS or NTs */ + struct rule *rule; /* Linked list of rules of this (if an NT) */ + struct symbol *fallback; /* fallback token in case this token doesn't parse */ + int prec; /* Precedence if defined (-1 otherwise) */ + enum e_assoc assoc; /* Associativity if precedence is defined */ + char *firstset; /* First-set for all rules of this symbol */ + Boolean lambda; /* True if NT and can generate an empty string */ + int useCnt; /* Number of times used */ + char *destructor; /* Code which executes whenever this symbol is + ** popped from the stack during error processing */ + int destLineno; /* Line number for start of destructor. Set to + ** -1 for duplicate destructors. */ + char *datatype; /* The data type of information held by this + ** object. Only used if type==NONTERMINAL */ + int dtnum; /* The data type number. In the parser, the value + ** stack is a union. The .yy%d element of this + ** union is the correct data type for this object */ + int bContent; /* True if this symbol ever carries content - if + ** it is ever more than just syntax */ + /* The following fields are used by MULTITERMINALs only */ + int nsubsym; /* Number of constituent symbols in the MULTI */ + struct symbol **subsym; /* Array of constituent symbols */ +}; + +/* Each production rule in the grammar is stored in the following +** structure. */ +struct rule { + struct symbol *lhs; /* Left-hand side of the rule */ + const char *lhsalias; /* Alias for the LHS (NULL if none) */ + int lhsStart; /* True if left-hand side is the start symbol */ + int ruleline; /* Line number for the rule */ + int nrhs; /* Number of RHS symbols */ + struct symbol **rhs; /* The RHS symbols */ + const char **rhsalias; /* An alias for each RHS symbol (NULL if none) */ + int line; /* Line number at which code begins */ + const char *code; /* The code executed when this rule is reduced */ + const char *codePrefix; /* Setup code before code[] above */ + const char *codeSuffix; /* Breakdown code after code[] above */ + struct symbol *precsym; /* Precedence symbol for this rule */ + int index; /* An index number for this rule */ + int iRule; /* Rule number as used in the generated tables */ + Boolean noCode; /* True if this rule has no associated C code */ + Boolean codeEmitted; /* True if the code has been emitted already */ + Boolean canReduce; /* True if this rule is ever reduced */ + Boolean doesReduce; /* Reduce actions occur after optimization */ + Boolean neverReduce; /* Reduce is theoretically possible, but prevented + ** by actions or other outside implementation */ + struct rule *nextlhs; /* Next rule with the same LHS */ + struct rule *next; /* Next rule in the global list */ +}; + +/* A configuration is a production rule of the grammar together with +** a mark (dot) showing how much of that rule has been processed so far. +** Configurations also contain a follow-set which is a list of terminal +** symbols which are allowed to immediately follow the end of the rule. +** Every configuration is recorded as an instance of the following: */ +enum cfgstatus { COMPLETE, INCOMPLETE }; +struct config { + struct rule *rp; /* The rule upon which the configuration is based */ + int dot; /* The parse point */ + char *fws; /* Follow-set for this configuration only */ + struct plink *fplp; /* Follow-set forward propagation links */ + struct plink *bplp; /* Follow-set backwards propagation links */ + struct state *stp; /* Pointer to state which contains this */ + enum cfgstatus status; /* used during followset and shift computations */ + struct config *next; /* Next configuration in the state */ + struct config *bp; /* The next basis configuration */ +}; + +enum e_action { + SHIFT, + ACCEPT, + REDUCE, + ERROR, + SSCONFLICT, /* A shift/shift conflict */ + SRCONFLICT, /* Was a reduce, but part of a conflict */ + RRCONFLICT, /* Was a reduce, but part of a conflict */ + SH_RESOLVED, /* Was a shift. Precedence resolved conflict */ + RD_RESOLVED, /* Was reduce. Precedence resolved conflict */ + NOT_USED, /* Deleted by compression */ + SHIFTREDUCE /* Shift first, then reduce */ +}; + +/* Every shift or reduce operation is stored as one of the following */ +struct action { + struct symbol *sp; /* The look-ahead symbol */ + enum e_action type; + union { + struct state *stp; /* The new state, if a shift */ + struct rule *rp; /* The rule, if a reduce */ + } x; + struct symbol *spOpt; /* SHIFTREDUCE optimization to this symbol */ + struct action *next; /* Next action for this state */ + struct action *collide; /* Next action with the same hash */ +}; + +/* Each state of the generated parser's finite state machine +** is encoded as an instance of the following structure. */ +struct state { + struct config *bp; /* The basis configurations for this state */ + struct config *cfp; /* All configurations in this set */ + int statenum; /* Sequential number for this state */ + struct action *ap; /* List of actions for this state */ + int nTknAct, nNtAct; /* Number of actions on terminals and nonterminals */ + int iTknOfst, iNtOfst; /* yy_action[] offset for terminals and nonterms */ + int iDfltReduce; /* Default action is to REDUCE by this rule */ + struct rule *pDfltReduce; /* The default REDUCE rule. */ + int autoReduce; /* True if this is an auto-reduce state */ +}; +#define NO_OFFSET (-2147483647) + +/* A followset propagation link indicates that the contents of one +** configuration followset should be propagated to another whenever +** the first changes. */ +struct plink { + struct config *cfp; /* The configuration to which linked */ + struct plink *next; /* The next propagate link */ +}; + +/* The state vector for the entire parser generator is recorded as +** follows. (LEMON uses no global variables and makes little use of +** static variables. Fields in the following structure can be thought +** of as begin global variables in the program.) */ +struct lemon { + struct state **sorted; /* Table of states sorted by state number */ + struct rule *rule; /* List of all rules */ + struct rule *startRule; /* First rule */ + int nstate; /* Number of states */ + int nxstate; /* nstate with tail degenerate states removed */ + int nrule; /* Number of rules */ + int nruleWithAction; /* Number of rules with actions */ + int nsymbol; /* Number of terminal and nonterminal symbols */ + int nterminal; /* Number of terminal symbols */ + int minShiftReduce; /* Minimum shift-reduce action value */ + int errAction; /* Error action value */ + int accAction; /* Accept action value */ + int noAction; /* No-op action value */ + int minReduce; /* Minimum reduce action */ + int maxAction; /* Maximum action value of any kind */ + struct symbol **symbols; /* Sorted array of pointers to symbols */ + int errorcnt; /* Number of errors */ + struct symbol *errsym; /* The error symbol */ + struct symbol *wildcard; /* Token that matches anything */ + char *name; /* Name of the generated parser */ + char *arg; /* Declaration of the 3th argument to parser */ + char *ctx; /* Declaration of 2nd argument to constructor */ + char *tokentype; /* Type of terminal symbols in the parser stack */ + char *vartype; /* The default type of non-terminal symbols */ + char *start; /* Name of the start symbol for the grammar */ + char *stacksize; /* Size of the parser stack */ + char *include; /* Code to put at the start of the C file */ + char *error; /* Code to execute when an error is seen */ + char *overflow; /* Code to execute on a stack overflow */ + char *failure; /* Code to execute on parser failure */ + char *accept; /* Code to execute when the parser excepts */ + char *extracode; /* Code appended to the generated file */ + char *tokendest; /* Code to execute to destroy token data */ + char *vardest; /* Code for the default non-terminal destructor */ + char *filename; /* Name of the input file */ + char *outname; /* Name of the current output file */ + char *tokenprefix; /* A prefix added to token names in the .h file */ + int nconflict; /* Number of parsing conflicts */ + int nactiontab; /* Number of entries in the yy_action[] table */ + int nlookaheadtab; /* Number of entries in yy_lookahead[] */ + int tablesize; /* Total table size of all tables in bytes */ + int basisflag; /* Print only basis configurations */ + int printPreprocessed; /* Show preprocessor output on stdout */ + int has_fallback; /* True if any %fallback is seen in the grammar */ + int nolinenosflag; /* True if #line statements should not be printed */ + char *argv0; /* Name of the program */ +}; + +#define MemoryCheck(X) \ + if ((X) == 0) { \ + memory_error(); \ + } + +/**************** From the file "table.h" *********************************/ +/* +** All code in this file has been automatically generated +** from a specification in the file +** "table.q" +** by the associative array code building program "aagen". +** Do not edit this file! Instead, edit the specification +** file, then rerun aagen. +*/ +/* +** Code for processing tables in the LEMON parser generator. +*/ +/* Routines for handling a strings */ + +const char *Strsafe(const char *); + +void Strsafe_init(void); +int Strsafe_insert(const char *); +const char *Strsafe_find(const char *); + +/* Routines for handling symbols of the grammar */ + +struct symbol *Symbol_new(const char *); +int Symbolcmpp(const void *, const void *); +void Symbol_init(void); +int Symbol_insert(struct symbol *, const char *); +struct symbol *Symbol_find(const char *); +struct symbol *Symbol_Nth(int); +int Symbol_count(void); +struct symbol **Symbol_arrayof(void); + +/* Routines to manage the state table */ + +int Configcmp(const char *, const char *); +struct state *State_new(void); +void State_init(void); +int State_insert(struct state *, struct config *); +struct state *State_find(struct config *); +struct state **State_arrayof(void); + +/* Routines used for efficiency in Configlist_add */ + +void Configtable_init(void); +int Configtable_insert(struct config *); +struct config *Configtable_find(struct config *); +void Configtable_clear(int (*)(struct config *)); + +/****************** From the file "action.c" *******************************/ +/* +** Routines processing parser actions in the LEMON parser generator. +*/ + +/* Allocate a new parser action */ +static struct action *Action_new(void) { + static struct action *actionfreelist = 0; + struct action *newaction; + + if (actionfreelist == 0) { + int i; + int amt = 100; + actionfreelist = (struct action *)calloc(amt, sizeof(struct action)); + if (actionfreelist == 0) { + fprintf(stderr, "Unable to allocate memory for a new parser action."); + exit(1); + } + for (i = 0; i < amt - 1; i++) + actionfreelist[i].next = &actionfreelist[i + 1]; + actionfreelist[amt - 1].next = 0; + } + newaction = actionfreelist; + actionfreelist = actionfreelist->next; + return newaction; +} + +/* Compare two actions for sorting purposes. Return negative, zero, or +** positive if the first action is less than, equal to, or greater than +** the first +*/ +static int actioncmp(struct action *ap1, struct action *ap2) { + int rc; + rc = ap1->sp->index - ap2->sp->index; + if (rc == 0) { + rc = (int)ap1->type - (int)ap2->type; + } + if (rc == 0 && (ap1->type == REDUCE || ap1->type == SHIFTREDUCE)) { + rc = ap1->x.rp->index - ap2->x.rp->index; + } + if (rc == 0) { + rc = (int)(ap2 - ap1); + } + return rc; +} + +/* Sort parser actions */ +static struct action *Action_sort(struct action *ap) { + ap = (struct action *)msort((char *)ap, (char **)&ap->next, + (int (*)(const char *, const char *))actioncmp); + return ap; +} + +void Action_add(struct action **app, enum e_action type, struct symbol *sp, + char *arg) { + struct action *newaction; + newaction = Action_new(); + newaction->next = *app; + *app = newaction; + newaction->type = type; + newaction->sp = sp; + newaction->spOpt = 0; + if (type == SHIFT) { + newaction->x.stp = (struct state *)arg; + } else { + newaction->x.rp = (struct rule *)arg; + } +} +/********************** New code to implement the "acttab" module ***********/ +/* +** This module implements routines use to construct the yy_action[] table. +*/ + +/* +** The state of the yy_action table under construction is an instance of +** the following structure. +** +** The yy_action table maps the pair (state_number, lookahead) into an +** action_number. The table is an array of integers pairs. The state_number +** determines an initial offset into the yy_action array. The lookahead +** value is then added to this initial offset to get an index X into the +** yy_action array. If the aAction[X].lookahead equals the value of the +** of the lookahead input, then the value of the action_number output is +** aAction[X].action. If the lookaheads do not match then the +** default action for the state_number is returned. +** +** All actions associated with a single state_number are first entered +** into aLookahead[] using multiple calls to acttab_action(). Then the +** actions for that single state_number are placed into the aAction[] +** array with a single call to acttab_insert(). The acttab_insert() call +** also resets the aLookahead[] array in preparation for the next +** state number. +*/ +struct lookahead_action { + int lookahead; /* Value of the lookahead token */ + int action; /* Action to take on the given lookahead */ +}; +typedef struct acttab acttab; +struct acttab { + int nAction; /* Number of used slots in aAction[] */ + int nActionAlloc; /* Slots allocated for aAction[] */ + struct lookahead_action + *aAction, /* The yy_action[] table under construction */ + *aLookahead; /* A single new transaction set */ + int mnLookahead; /* Minimum aLookahead[].lookahead */ + int mnAction; /* Action associated with mnLookahead */ + int mxLookahead; /* Maximum aLookahead[].lookahead */ + int nLookahead; /* Used slots in aLookahead[] */ + int nLookaheadAlloc; /* Slots allocated in aLookahead[] */ + int nterminal; /* Number of terminal symbols */ + int nsymbol; /* total number of symbols */ +}; + +/* Return the number of entries in the yy_action table */ +#define acttab_lookahead_size(X) ((X)->nAction) + +/* The value for the N-th entry in yy_action */ +#define acttab_yyaction(X, N) ((X)->aAction[N].action) + +/* The value for the N-th entry in yy_lookahead */ +#define acttab_yylookahead(X, N) ((X)->aAction[N].lookahead) + +/* Free all memory associated with the given acttab */ +void acttab_free(acttab *p) { + free(p->aAction); + free(p->aLookahead); + free(p); +} + +/* Allocate a new acttab structure */ +acttab *acttab_alloc(int nsymbol, int nterminal) { + acttab *p = (acttab *)calloc(1, sizeof(*p)); + if (p == 0) { + fprintf(stderr, "Unable to allocate memory for a new acttab."); + exit(1); + } + memset(p, 0, sizeof(*p)); + p->nsymbol = nsymbol; + p->nterminal = nterminal; + return p; +} + +/* Add a new action to the current transaction set. +** +** This routine is called once for each lookahead for a particular +** state. +*/ +void acttab_action(acttab *p, int lookahead, int action) { + if (p->nLookahead >= p->nLookaheadAlloc) { + p->nLookaheadAlloc += 25; + p->aLookahead = (struct lookahead_action *)realloc( + p->aLookahead, sizeof(p->aLookahead[0]) * p->nLookaheadAlloc); + if (p->aLookahead == 0) { + fprintf(stderr, "malloc failed\n"); + exit(1); + } + } + if (p->nLookahead == 0) { + p->mxLookahead = lookahead; + p->mnLookahead = lookahead; + p->mnAction = action; + } else { + if (p->mxLookahead < lookahead) p->mxLookahead = lookahead; + if (p->mnLookahead > lookahead) { + p->mnLookahead = lookahead; + p->mnAction = action; + } + } + p->aLookahead[p->nLookahead].lookahead = lookahead; + p->aLookahead[p->nLookahead].action = action; + p->nLookahead++; +} + +/* +** Add the transaction set built up with prior calls to acttab_action() +** into the current action table. Then reset the transaction set back +** to an empty set in preparation for a new round of acttab_action() calls. +** +** Return the offset into the action table of the new transaction. +** +** If the makeItSafe parameter is true, then the offset is chosen so that +** it is impossible to overread the yy_lookaside[] table regardless of +** the lookaside token. This is done for the terminal symbols, as they +** come from external inputs and can contain syntax errors. When makeItSafe +** is false, there is more flexibility in selecting offsets, resulting in +** a smaller table. For non-terminal symbols, which are never syntax errors, +** makeItSafe can be false. +*/ +int acttab_insert(acttab *p, int makeItSafe) { + int i, j, k, n, end; + assert(p->nLookahead > 0); + + /* Make sure we have enough space to hold the expanded action table + ** in the worst case. The worst case occurs if the transaction set + ** must be appended to the current action table + */ + n = p->nsymbol + 1; + if (p->nAction + n >= p->nActionAlloc) { + int oldAlloc = p->nActionAlloc; + p->nActionAlloc = p->nAction + n + p->nActionAlloc + 20; + p->aAction = (struct lookahead_action *)realloc( + p->aAction, sizeof(p->aAction[0]) * p->nActionAlloc); + if (p->aAction == 0) { + fprintf(stderr, "malloc failed\n"); + exit(1); + } + for (i = oldAlloc; i < p->nActionAlloc; i++) { + p->aAction[i].lookahead = -1; + p->aAction[i].action = -1; + } + } + + /* Scan the existing action table looking for an offset that is a + ** duplicate of the current transaction set. Fall out of the loop + ** if and when the duplicate is found. + ** + ** i is the index in p->aAction[] where p->mnLookahead is inserted. + */ + end = makeItSafe ? p->mnLookahead : 0; + for (i = p->nAction - 1; i >= end; i--) { + if (p->aAction[i].lookahead == p->mnLookahead) { + /* All lookaheads and actions in the aLookahead[] transaction + ** must match against the candidate aAction[i] entry. */ + if (p->aAction[i].action != p->mnAction) continue; + for (j = 0; j < p->nLookahead; j++) { + k = p->aLookahead[j].lookahead - p->mnLookahead + i; + if (k < 0 || k >= p->nAction) break; + if (p->aLookahead[j].lookahead != p->aAction[k].lookahead) break; + if (p->aLookahead[j].action != p->aAction[k].action) break; + } + if (j < p->nLookahead) continue; + + /* No possible lookahead value that is not in the aLookahead[] + ** transaction is allowed to match aAction[i] */ + n = 0; + for (j = 0; j < p->nAction; j++) { + if (p->aAction[j].lookahead < 0) continue; + if (p->aAction[j].lookahead == j + p->mnLookahead - i) n++; + } + if (n == p->nLookahead) { + break; /* An exact match is found at offset i */ + } + } + } + + /* If no existing offsets exactly match the current transaction, find an + ** an empty offset in the aAction[] table in which we can add the + ** aLookahead[] transaction. + */ + if (i < end) { + /* Look for holes in the aAction[] table that fit the current + ** aLookahead[] transaction. Leave i set to the offset of the hole. + ** If no holes are found, i is left at p->nAction, which means the + ** transaction will be appended. */ + i = makeItSafe ? p->mnLookahead : 0; + for (; i < p->nActionAlloc - p->mxLookahead; i++) { + if (p->aAction[i].lookahead < 0) { + for (j = 0; j < p->nLookahead; j++) { + k = p->aLookahead[j].lookahead - p->mnLookahead + i; + if (k < 0) break; + if (p->aAction[k].lookahead >= 0) break; + } + if (j < p->nLookahead) continue; + for (j = 0; j < p->nAction; j++) { + if (p->aAction[j].lookahead == j + p->mnLookahead - i) break; + } + if (j == p->nAction) { + break; /* Fits in empty slots */ + } + } + } + } + /* Insert transaction set at index i. */ +#if 0 + printf("Acttab:"); + for(j=0; jnLookahead; j++){ + printf(" %d", p->aLookahead[j].lookahead); + } + printf(" inserted at %d\n", i); +#endif + for (j = 0; j < p->nLookahead; j++) { + k = p->aLookahead[j].lookahead - p->mnLookahead + i; + p->aAction[k] = p->aLookahead[j]; + if (k >= p->nAction) p->nAction = k + 1; + } + if (makeItSafe && i + p->nterminal >= p->nAction) + p->nAction = i + p->nterminal + 1; + p->nLookahead = 0; + + /* Return the offset that is added to the lookahead in order to get the + ** index into yy_action of the action */ + return i - p->mnLookahead; +} + +/* +** Return the size of the action table without the trailing syntax error +** entries. +*/ +int acttab_action_size(acttab *p) { + int n = p->nAction; + while (n > 0 && p->aAction[n - 1].lookahead < 0) { + n--; + } + return n; +} + +/********************** From the file "build.c" *****************************/ +/* +** Routines to construction the finite state machine for the LEMON +** parser generator. +*/ + +/* Find a precedence symbol of every rule in the grammar. +** +** Those rules which have a precedence symbol coded in the input +** grammar using the "[symbol]" construct will already have the +** rp->precsym field filled. Other rules take as their precedence +** symbol the first RHS symbol with a defined precedence. If there +** are not RHS symbols with a defined precedence, the precedence +** symbol field is left blank. +*/ +void FindRulePrecedences(struct lemon *xp) { + struct rule *rp; + for (rp = xp->rule; rp; rp = rp->next) { + if (rp->precsym == 0) { + int i, j; + for (i = 0; i < rp->nrhs && rp->precsym == 0; i++) { + struct symbol *sp = rp->rhs[i]; + if (sp->type == MULTITERMINAL) { + for (j = 0; j < sp->nsubsym; j++) { + if (sp->subsym[j]->prec >= 0) { + rp->precsym = sp->subsym[j]; + break; + } + } + } else if (sp->prec >= 0) { + rp->precsym = rp->rhs[i]; + } + } + } + } + return; +} + +/* Find all nonterminals which will generate the empty string. +** Then go back and compute the first sets of every nonterminal. +** The first set is the set of all terminal symbols which can begin +** a string generated by that nonterminal. +*/ +void FindFirstSets(struct lemon *lemp) { + int i, j; + struct rule *rp; + int progress; + + for (i = 0; i < lemp->nsymbol; i++) { + lemp->symbols[i]->lambda = LEMON_FALSE; + } + for (i = lemp->nterminal; i < lemp->nsymbol; i++) { + lemp->symbols[i]->firstset = SetNew(); + } + + /* First compute all lambdas */ + do { + progress = 0; + for (rp = lemp->rule; rp; rp = rp->next) { + if (rp->lhs->lambda) continue; + for (i = 0; i < rp->nrhs; i++) { + struct symbol *sp = rp->rhs[i]; + assert(sp->type == NONTERMINAL || sp->lambda == LEMON_FALSE); + if (sp->lambda == LEMON_FALSE) break; + } + if (i == rp->nrhs) { + rp->lhs->lambda = LEMON_TRUE; + progress = 1; + } + } + } while (progress); + + /* Now compute all first sets */ + do { + struct symbol *s1, *s2; + progress = 0; + for (rp = lemp->rule; rp; rp = rp->next) { + s1 = rp->lhs; + for (i = 0; i < rp->nrhs; i++) { + s2 = rp->rhs[i]; + if (s2->type == TERMINAL) { + progress += SetAdd(s1->firstset, s2->index); + break; + } else if (s2->type == MULTITERMINAL) { + for (j = 0; j < s2->nsubsym; j++) { + progress += SetAdd(s1->firstset, s2->subsym[j]->index); + } + break; + } else if (s1 == s2) { + if (s1->lambda == LEMON_FALSE) break; + } else { + progress += SetUnion(s1->firstset, s2->firstset); + if (s2->lambda == LEMON_FALSE) break; + } + } + } + } while (progress); + return; +} + +/* Compute all LR(0) states for the grammar. Links +** are added to between some states so that the LR(1) follow sets +** can be computed later. +*/ +PRIVATE struct state *getstate(struct lemon *); /* forward reference */ +void FindStates(struct lemon *lemp) { + struct symbol *sp; + struct rule *rp; + + Configlist_init(); + + /* Find the start symbol */ + if (lemp->start) { + sp = Symbol_find(lemp->start); + if (sp == 0) { + ErrorMsg( + lemp->filename, 0, + "The specified start symbol \"%s\" is not " + "in a nonterminal of the grammar. \"%s\" will be used as the start " + "symbol instead.", + lemp->start, lemp->startRule->lhs->name); + lemp->errorcnt++; + sp = lemp->startRule->lhs; + } + } else { + sp = lemp->startRule->lhs; + } + + /* Make sure the start symbol doesn't occur on the right-hand side of + ** any rule. Report an error if it does. (YACC would generate a new + ** start symbol in this case.) */ + for (rp = lemp->rule; rp; rp = rp->next) { + int i; + for (i = 0; i < rp->nrhs; i++) { + if (rp->rhs[i] == sp) { /* FIX ME: Deal with multiterminals */ + ErrorMsg( + lemp->filename, 0, + "The start symbol \"%s\" occurs on the " + "right-hand side of a rule. This will result in a parser which " + "does not work properly.", + sp->name); + lemp->errorcnt++; + } + } + } + + /* The basis configuration set for the first state + ** is all rules which have the start symbol as their + ** left-hand side */ + for (rp = sp->rule; rp; rp = rp->nextlhs) { + struct config *newcfp; + rp->lhsStart = 1; + newcfp = Configlist_addbasis(rp, 0); + SetAdd(newcfp->fws, 0); + } + + /* Compute the first state. All other states will be + ** computed automatically during the computation of the first one. + ** The returned pointer to the first state is not used. */ + (void)getstate(lemp); + return; +} + +/* Return a pointer to a state which is described by the configuration +** list which has been built from calls to Configlist_add. +*/ +PRIVATE void buildshifts(struct lemon *, struct state *); /* Forwd ref */ +PRIVATE struct state *getstate(struct lemon *lemp) { + struct config *cfp, *bp; + struct state *stp; + + /* Extract the sorted basis of the new state. The basis was constructed + ** by prior calls to "Configlist_addbasis()". */ + Configlist_sortbasis(); + bp = Configlist_basis(); + + /* Get a state with the same basis */ + stp = State_find(bp); + if (stp) { + /* A state with the same basis already exists! Copy all the follow-set + ** propagation links from the state under construction into the + ** preexisting state, then return a pointer to the preexisting state */ + struct config *x, *y; + for (x = bp, y = stp->bp; x && y; x = x->bp, y = y->bp) { + Plink_copy(&y->bplp, x->bplp); + Plink_delete(x->fplp); + x->fplp = x->bplp = 0; + } + cfp = Configlist_return(); + Configlist_eat(cfp); + } else { + /* This really is a new state. Construct all the details */ + Configlist_closure(lemp); /* Compute the configuration closure */ + Configlist_sort(); /* Sort the configuration closure */ + cfp = Configlist_return(); /* Get a pointer to the config list */ + stp = State_new(); /* A new state structure */ + MemoryCheck(stp); + stp->bp = bp; /* Remember the configuration basis */ + stp->cfp = cfp; /* Remember the configuration closure */ + stp->statenum = lemp->nstate++; /* Every state gets a sequence number */ + stp->ap = 0; /* No actions, yet. */ + State_insert(stp, stp->bp); /* Add to the state table */ + buildshifts(lemp, stp); /* Recursively compute successor states */ + } + return stp; +} + +/* +** Return true if two symbols are the same. +*/ +int same_symbol(struct symbol *a, struct symbol *b) { + int i; + if (a == b) return 1; + if (a->type != MULTITERMINAL) return 0; + if (b->type != MULTITERMINAL) return 0; + if (a->nsubsym != b->nsubsym) return 0; + for (i = 0; i < a->nsubsym; i++) { + if (a->subsym[i] != b->subsym[i]) return 0; + } + return 1; +} + +/* Construct all successor states to the given state. A "successor" +** state is any state which can be reached by a shift action. +*/ +PRIVATE void buildshifts(struct lemon *lemp, struct state *stp) { + struct config *cfp; /* For looping thru the config closure of "stp" */ + struct config *bcfp; /* For the inner loop on config closure of "stp" */ + struct config *newcfg; /* */ + struct symbol *sp; /* Symbol following the dot in configuration "cfp" */ + struct symbol *bsp; /* Symbol following the dot in configuration "bcfp" */ + struct state *newstp; /* A pointer to a successor state */ + + /* Each configuration becomes complete after it contibutes to a successor + ** state. Initially, all configurations are incomplete */ + for (cfp = stp->cfp; cfp; cfp = cfp->next) cfp->status = INCOMPLETE; + + /* Loop through all configurations of the state "stp" */ + for (cfp = stp->cfp; cfp; cfp = cfp->next) { + if (cfp->status == COMPLETE) continue; /* Already used by inner loop */ + if (cfp->dot >= cfp->rp->nrhs) continue; /* Can't shift this config */ + Configlist_reset(); /* Reset the new config set */ + sp = cfp->rp->rhs[cfp->dot]; /* Symbol after the dot */ + + /* For every configuration in the state "stp" which has the symbol "sp" + ** following its dot, add the same configuration to the basis set under + ** construction but with the dot shifted one symbol to the right. */ + for (bcfp = cfp; bcfp; bcfp = bcfp->next) { + if (bcfp->status == COMPLETE) continue; /* Already used */ + if (bcfp->dot >= bcfp->rp->nrhs) continue; /* Can't shift this one */ + bsp = bcfp->rp->rhs[bcfp->dot]; /* Get symbol after dot */ + if (!same_symbol(bsp, sp)) continue; /* Must be same as for "cfp" */ + bcfp->status = COMPLETE; /* Mark this config as used */ + newcfg = Configlist_addbasis(bcfp->rp, bcfp->dot + 1); + Plink_add(&newcfg->bplp, bcfp); + } + + /* Get a pointer to the state described by the basis configuration set + ** constructed in the preceding loop */ + newstp = getstate(lemp); + + /* The state "newstp" is reached from the state "stp" by a shift action + ** on the symbol "sp" */ + if (sp->type == MULTITERMINAL) { + int i; + for (i = 0; i < sp->nsubsym; i++) { + Action_add(&stp->ap, SHIFT, sp->subsym[i], (char *)newstp); + } + } else { + Action_add(&stp->ap, SHIFT, sp, (char *)newstp); + } + } +} + +/* +** Construct the propagation links +*/ +void FindLinks(struct lemon *lemp) { + int i; + struct config *cfp, *other; + struct state *stp; + struct plink *plp; + + /* Housekeeping detail: + ** Add to every propagate link a pointer back to the state to + ** which the link is attached. */ + for (i = 0; i < lemp->nstate; i++) { + stp = lemp->sorted[i]; + for (cfp = stp->cfp; cfp; cfp = cfp->next) { + cfp->stp = stp; + } + } + + /* Convert all backlinks into forward links. Only the forward + ** links are used in the follow-set computation. */ + for (i = 0; i < lemp->nstate; i++) { + stp = lemp->sorted[i]; + for (cfp = stp->cfp; cfp; cfp = cfp->next) { + for (plp = cfp->bplp; plp; plp = plp->next) { + other = plp->cfp; + Plink_add(&other->fplp, cfp); + } + } + } +} + +/* Compute all followsets. +** +** A followset is the set of all symbols which can come immediately +** after a configuration. +*/ +void FindFollowSets(struct lemon *lemp) { + int i; + struct config *cfp; + struct plink *plp; + int progress; + int change; + + for (i = 0; i < lemp->nstate; i++) { + for (cfp = lemp->sorted[i]->cfp; cfp; cfp = cfp->next) { + cfp->status = INCOMPLETE; + } + } + + do { + progress = 0; + for (i = 0; i < lemp->nstate; i++) { + for (cfp = lemp->sorted[i]->cfp; cfp; cfp = cfp->next) { + if (cfp->status == COMPLETE) continue; + for (plp = cfp->fplp; plp; plp = plp->next) { + change = SetUnion(plp->cfp->fws, cfp->fws); + if (change) { + plp->cfp->status = INCOMPLETE; + progress = 1; + } + } + cfp->status = COMPLETE; + } + } + } while (progress); +} + +static int resolve_conflict(struct action *, struct action *); + +/* Compute the reduce actions, and resolve conflicts. + */ +void FindActions(struct lemon *lemp) { + int i, j; + struct config *cfp; + struct state *stp; + struct symbol *sp; + struct rule *rp; + + /* Add all of the reduce actions + ** A reduce action is added for each element of the followset of + ** a configuration which has its dot at the extreme right. + */ + for (i = 0; i < lemp->nstate; i++) { /* Loop over all states */ + stp = lemp->sorted[i]; + for (cfp = stp->cfp; cfp; + cfp = cfp->next) { /* Loop over all configurations */ + if (cfp->rp->nrhs == cfp->dot) { /* Is dot at extreme right? */ + for (j = 0; j < lemp->nterminal; j++) { + if (SetFind(cfp->fws, j)) { + /* Add a reduce action to the state "stp" which will reduce by the + ** rule "cfp->rp" if the lookahead symbol is "lemp->symbols[j]" */ + Action_add(&stp->ap, REDUCE, lemp->symbols[j], (char *)cfp->rp); + } + } + } + } + } + + /* Add the accepting token */ + if (lemp->start) { + sp = Symbol_find(lemp->start); + if (sp == 0) sp = lemp->startRule->lhs; + } else { + sp = lemp->startRule->lhs; + } + /* Add to the first state (which is always the starting state of the + ** finite state machine) an action to ACCEPT if the lookahead is the + ** start nonterminal. */ + Action_add(&lemp->sorted[0]->ap, ACCEPT, sp, 0); + + /* Resolve conflicts */ + for (i = 0; i < lemp->nstate; i++) { + struct action *ap, *nap; + stp = lemp->sorted[i]; + /* assert( stp->ap ); */ + stp->ap = Action_sort(stp->ap); + for (ap = stp->ap; ap && ap->next; ap = ap->next) { + for (nap = ap->next; nap && nap->sp == ap->sp; nap = nap->next) { + /* The two actions "ap" and "nap" have the same lookahead. + ** Figure out which one should be used */ + lemp->nconflict += resolve_conflict(ap, nap); + } + } + } + + /* Report an error for each rule that can never be reduced. */ + for (rp = lemp->rule; rp; rp = rp->next) rp->canReduce = LEMON_FALSE; + for (i = 0; i < lemp->nstate; i++) { + struct action *ap; + for (ap = lemp->sorted[i]->ap; ap; ap = ap->next) { + if (ap->type == REDUCE) ap->x.rp->canReduce = LEMON_TRUE; + } + } + for (rp = lemp->rule; rp; rp = rp->next) { + if (rp->canReduce) continue; + ErrorMsg(lemp->filename, rp->ruleline, "This rule can not be reduced.\n"); + lemp->errorcnt++; + } +} + +/* Resolve a conflict between the two given actions. If the +** conflict can't be resolved, return non-zero. +** +** NO LONGER TRUE: +** To resolve a conflict, first look to see if either action +** is on an error rule. In that case, take the action which +** is not associated with the error rule. If neither or both +** actions are associated with an error rule, then try to +** use precedence to resolve the conflict. +** +** If either action is a SHIFT, then it must be apx. This +** function won't work if apx->type==REDUCE and apy->type==SHIFT. +*/ +static int resolve_conflict(struct action *apx, struct action *apy) { + struct symbol *spx, *spy; + int errcnt = 0; + assert(apx->sp == apy->sp); /* Otherwise there would be no conflict */ + if (apx->type == SHIFT && apy->type == SHIFT) { + apy->type = SSCONFLICT; + errcnt++; + } + if (apx->type == SHIFT && apy->type == REDUCE) { + spx = apx->sp; + spy = apy->x.rp->precsym; + if (spy == 0 || spx->prec < 0 || spy->prec < 0) { + /* Not enough precedence information. */ + apy->type = SRCONFLICT; + errcnt++; + } else if (spx->prec > spy->prec) { /* higher precedence wins */ + apy->type = RD_RESOLVED; + } else if (spx->prec < spy->prec) { + apx->type = SH_RESOLVED; + } else if (spx->prec == spy->prec && + spx->assoc == RIGHT) { /* Use operator */ + apy->type = RD_RESOLVED; /* associativity */ + } else if (spx->prec == spy->prec && + spx->assoc == LEFT) { /* to break tie */ + apx->type = SH_RESOLVED; + } else { + assert(spx->prec == spy->prec && spx->assoc == NONE); + apx->type = ERROR; + } + } else if (apx->type == REDUCE && apy->type == REDUCE) { + spx = apx->x.rp->precsym; + spy = apy->x.rp->precsym; + if (spx == 0 || spy == 0 || spx->prec < 0 || spy->prec < 0 || + spx->prec == spy->prec) { + apy->type = RRCONFLICT; + errcnt++; + } else if (spx->prec > spy->prec) { + apy->type = RD_RESOLVED; + } else if (spx->prec < spy->prec) { + apx->type = RD_RESOLVED; + } + } else { + assert(apx->type == SH_RESOLVED || apx->type == RD_RESOLVED || + apx->type == SSCONFLICT || apx->type == SRCONFLICT || + apx->type == RRCONFLICT || apy->type == SH_RESOLVED || + apy->type == RD_RESOLVED || apy->type == SSCONFLICT || + apy->type == SRCONFLICT || apy->type == RRCONFLICT); + /* The REDUCE/SHIFT case cannot happen because SHIFTs come before + ** REDUCEs on the list. If we reach this point it must be because + ** the parser conflict had already been resolved. */ + } + return errcnt; +} +/********************* From the file "configlist.c" *************************/ +/* +** Routines to processing a configuration list and building a state +** in the LEMON parser generator. +*/ + +static struct config *freelist = 0; /* List of free configurations */ +static struct config *current = 0; /* Top of list of configurations */ +static struct config **currentend = 0; /* Last on list of configs */ +static struct config *basis = 0; /* Top of list of basis configs */ +static struct config **basisend = 0; /* End of list of basis configs */ + +/* Return a pointer to a new configuration */ +PRIVATE struct config *newconfig(void) { + struct config *newcfg; + if (freelist == 0) { + int i; + int amt = 3; + freelist = (struct config *)calloc(amt, sizeof(struct config)); + if (freelist == 0) { + fprintf(stderr, "Unable to allocate memory for a new configuration."); + exit(1); + } + for (i = 0; i < amt - 1; i++) freelist[i].next = &freelist[i + 1]; + freelist[amt - 1].next = 0; + } + newcfg = freelist; + freelist = freelist->next; + return newcfg; +} + +/* The configuration "old" is no longer used */ +PRIVATE void deleteconfig(struct config *old) { + old->next = freelist; + freelist = old; +} + +/* Initialized the configuration list builder */ +void Configlist_init(void) { + current = 0; + currentend = ¤t; + basis = 0; + basisend = &basis; + Configtable_init(); + return; +} + +/* Initialized the configuration list builder */ +void Configlist_reset(void) { + current = 0; + currentend = ¤t; + basis = 0; + basisend = &basis; + Configtable_clear(0); + return; +} + +/* Add another configuration to the configuration list */ +struct config *Configlist_add( + struct rule *rp, /* The rule */ + int dot /* Index into the RHS of the rule where the dot goes */ +) { + struct config *cfp, model; + + assert(currentend != 0); + model.rp = rp; + model.dot = dot; + cfp = Configtable_find(&model); + if (cfp == 0) { + cfp = newconfig(); + cfp->rp = rp; + cfp->dot = dot; + cfp->fws = SetNew(); + cfp->stp = 0; + cfp->fplp = cfp->bplp = 0; + cfp->next = 0; + cfp->bp = 0; + *currentend = cfp; + currentend = &cfp->next; + Configtable_insert(cfp); + } + return cfp; +} + +/* Add a basis configuration to the configuration list */ +struct config *Configlist_addbasis(struct rule *rp, int dot) { + struct config *cfp, model; + + assert(basisend != 0); + assert(currentend != 0); + model.rp = rp; + model.dot = dot; + cfp = Configtable_find(&model); + if (cfp == 0) { + cfp = newconfig(); + cfp->rp = rp; + cfp->dot = dot; + cfp->fws = SetNew(); + cfp->stp = 0; + cfp->fplp = cfp->bplp = 0; + cfp->next = 0; + cfp->bp = 0; + *currentend = cfp; + currentend = &cfp->next; + *basisend = cfp; + basisend = &cfp->bp; + Configtable_insert(cfp); + } + return cfp; +} + +/* Compute the closure of the configuration list */ +void Configlist_closure(struct lemon *lemp) { + struct config *cfp, *newcfp; + struct rule *rp, *newrp; + struct symbol *sp, *xsp; + int i, dot; + + assert(currentend != 0); + for (cfp = current; cfp; cfp = cfp->next) { + rp = cfp->rp; + dot = cfp->dot; + if (dot >= rp->nrhs) continue; + sp = rp->rhs[dot]; + if (sp->type == NONTERMINAL) { + if (sp->rule == 0 && sp != lemp->errsym) { + ErrorMsg(lemp->filename, rp->line, "Nonterminal \"%s\" has no rules.", + sp->name); + lemp->errorcnt++; + } + for (newrp = sp->rule; newrp; newrp = newrp->nextlhs) { + newcfp = Configlist_add(newrp, 0); + for (i = dot + 1; i < rp->nrhs; i++) { + xsp = rp->rhs[i]; + if (xsp->type == TERMINAL) { + SetAdd(newcfp->fws, xsp->index); + break; + } else if (xsp->type == MULTITERMINAL) { + int k; + for (k = 0; k < xsp->nsubsym; k++) { + SetAdd(newcfp->fws, xsp->subsym[k]->index); + } + break; + } else { + SetUnion(newcfp->fws, xsp->firstset); + if (xsp->lambda == LEMON_FALSE) break; + } + } + if (i == rp->nrhs) Plink_add(&cfp->fplp, newcfp); + } + } + } + return; +} + +/* Sort the configuration list */ +void Configlist_sort(void) { + current = (struct config *)msort((char *)current, (char **)&(current->next), + Configcmp); + currentend = 0; + return; +} + +/* Sort the basis configuration list */ +void Configlist_sortbasis(void) { + basis = (struct config *)msort((char *)current, (char **)&(current->bp), + Configcmp); + basisend = 0; + return; +} + +/* Return a pointer to the head of the configuration list and +** reset the list */ +struct config *Configlist_return(void) { + struct config *old; + old = current; + current = 0; + currentend = 0; + return old; +} + +/* Return a pointer to the head of the configuration list and +** reset the list */ +struct config *Configlist_basis(void) { + struct config *old; + old = basis; + basis = 0; + basisend = 0; + return old; +} + +/* Free all elements of the given configuration list */ +void Configlist_eat(struct config *cfp) { + struct config *nextcfp; + for (; cfp; cfp = nextcfp) { + nextcfp = cfp->next; + assert(cfp->fplp == 0); + assert(cfp->bplp == 0); + if (cfp->fws) SetFree(cfp->fws); + deleteconfig(cfp); + } + return; +} +/***************** From the file "error.c" *********************************/ +/* +** Code for printing error message. +*/ + +void ErrorMsg(const char *filename, int lineno, const char *format, ...) { + va_list ap; + fprintf(stderr, "%s:%d: ", filename, lineno); + va_start(ap, format); + vfprintf(stderr, format, ap); + va_end(ap); + fprintf(stderr, "\n"); +} +/**************** From the file "main.c" ************************************/ +/* +** Main program file for the LEMON parser generator. +*/ + +/* Report an out-of-memory condition and abort. This function +** is used mostly by the "MemoryCheck" macro in struct.h +*/ +void memory_error(void) { + fprintf(stderr, "Out of memory. Aborting...\n"); + exit(1); +} + +static int nDefine = 0; /* Number of -D options on the command line */ +static char **azDefine = 0; /* Name of the -D macros */ + +/* This routine is called with the argument to each -D command-line option. +** Add the macro defined to the azDefine array. +*/ +static void handle_D_option(char *z) { + char **paz; + nDefine++; + azDefine = (char **)realloc(azDefine, sizeof(azDefine[0]) * nDefine); + if (azDefine == 0) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + paz = &azDefine[nDefine - 1]; + *paz = (char *)malloc(lemonStrlen(z) + 1); + if (*paz == 0) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + lemon_strcpy(*paz, z); + for (z = *paz; *z && *z != '='; z++) { + } + *z = 0; +} + +/* Rember the name of the output directory + */ +static char *outputDir = NULL; +static void handle_d_option(char *z) { + outputDir = (char *)malloc(lemonStrlen(z) + 1); + if (outputDir == 0) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + lemon_strcpy(outputDir, z); +} + +static char *user_templatename = NULL; +static void handle_T_option(char *z) { + user_templatename = (char *)malloc(lemonStrlen(z) + 1); + if (user_templatename == 0) { + memory_error(); + } + lemon_strcpy(user_templatename, z); +} + +/* Merge together to lists of rules ordered by rule.iRule */ +static struct rule *Rule_merge(struct rule *pA, struct rule *pB) { + struct rule *pFirst = 0; + struct rule **ppPrev = &pFirst; + while (pA && pB) { + if (pA->iRule < pB->iRule) { + *ppPrev = pA; + ppPrev = &pA->next; + pA = pA->next; + } else { + *ppPrev = pB; + ppPrev = &pB->next; + pB = pB->next; + } + } + if (pA) { + *ppPrev = pA; + } else { + *ppPrev = pB; + } + return pFirst; +} + +/* +** Sort a list of rules in order of increasing iRule value +*/ +static struct rule *Rule_sort(struct rule *rp) { + unsigned int i; + struct rule *pNext; + struct rule *x[32]; + memset(x, 0, sizeof(x)); + while (rp) { + pNext = rp->next; + rp->next = 0; + for (i = 0; i < sizeof(x) / sizeof(x[0]) - 1 && x[i]; i++) { + rp = Rule_merge(x[i], rp); + x[i] = 0; + } + x[i] = rp; + rp = pNext; + } + rp = 0; + for (i = 0; i < sizeof(x) / sizeof(x[0]); i++) { + rp = Rule_merge(x[i], rp); + } + return rp; +} + +/* forward reference */ +static const char *minimum_size_type(int lwr, int upr, int *pnByte); + +/* Print a single line of the "Parser Stats" output + */ +static void stats_line(const char *zLabel, int iValue) { + int nLabel = lemonStrlen(zLabel); + printf(" %s%.*s %5d\n", zLabel, 35 - nLabel, + "................................", iValue); +} + +/* The main program. Parse the command line and do it... */ +int main(int argc, char **argv) { + static int version = 0; + static int rpflag = 0; + static int basisflag = 0; + static int compress = 0; + static int quiet = 0; + static int statistics = 0; + static int mhflag = 0; + static int nolinenosflag = 0; + static int noResort = 0; + static int sqlFlag = 0; + static int printPP = 0; + + static const struct s_options options[] = { + {OPT_FLAG, "b", (char *)&basisflag, "Print only the basis in report."}, + {OPT_FLAG, "c", (char *)&compress, "Don't compress the action table."}, + {OPT_FSTR, "d", (char *)&handle_d_option, + "Output directory. Default '.'"}, + {OPT_FSTR, "D", (char *)handle_D_option, "Define an %ifdef macro."}, + {OPT_FLAG, "E", (char *)&printPP, + "Print input file after preprocessing."}, + {OPT_FSTR, "f", 0, "Ignored. (Placeholder for -f compiler options.)"}, + {OPT_FLAG, "g", (char *)&rpflag, "Print grammar without actions."}, + {OPT_FSTR, "I", 0, "Ignored. (Placeholder for '-I' compiler options.)"}, + {OPT_FLAG, "m", (char *)&mhflag, "Output a makeheaders compatible file."}, + {OPT_FLAG, "l", (char *)&nolinenosflag, "Do not print #line statements."}, + {OPT_FSTR, "O", 0, "Ignored. (Placeholder for '-O' compiler options.)"}, + {OPT_FLAG, "p", (char *)&showPrecedenceConflict, + "Show conflicts resolved by precedence rules"}, + {OPT_FLAG, "q", (char *)&quiet, "(Quiet) Don't print the report file."}, + {OPT_FLAG, "r", (char *)&noResort, "Do not sort or renumber states"}, + {OPT_FLAG, "s", (char *)&statistics, + "Print parser stats to standard output."}, + {OPT_FLAG, "S", (char *)&sqlFlag, + "Generate the *.sql file describing the parser tables."}, + {OPT_FLAG, "x", (char *)&version, "Print the version number."}, + {OPT_FSTR, "T", (char *)handle_T_option, "Specify a template file."}, + {OPT_FSTR, "W", 0, "Ignored. (Placeholder for '-W' compiler options.)"}, + {OPT_FLAG, 0, 0, 0}}; + int i; + int exitcode; + struct lemon lem; + struct rule *rp; + + (void)argc; + OptInit(argv, options, stderr); + if (version) { + printf("Lemon version 1.0\n"); + exit(0); + } + if (OptNArgs() != 1) { + fprintf(stderr, "Exactly one filename argument is required.\n"); + exit(1); + } + memset(&lem, 0, sizeof(lem)); + lem.errorcnt = 0; + + /* Initialize the machine */ + Strsafe_init(); + Symbol_init(); + State_init(); + lem.argv0 = argv[0]; + lem.filename = OptArg(0); + lem.basisflag = basisflag; + lem.nolinenosflag = nolinenosflag; + lem.printPreprocessed = printPP; + Symbol_new("$"); + + /* Parse the input file */ + Parse(&lem); + if (lem.printPreprocessed || lem.errorcnt) exit(lem.errorcnt); + if (lem.nrule == 0) { + fprintf(stderr, "Empty grammar.\n"); + exit(1); + } + lem.errsym = Symbol_find("error"); + + /* Count and index the symbols of the grammar */ + Symbol_new("{default}"); + lem.nsymbol = Symbol_count(); + lem.symbols = Symbol_arrayof(); + for (i = 0; i < lem.nsymbol; i++) lem.symbols[i]->index = i; + qsort(lem.symbols, lem.nsymbol, sizeof(struct symbol *), Symbolcmpp); + for (i = 0; i < lem.nsymbol; i++) lem.symbols[i]->index = i; + while (lem.symbols[i - 1]->type == MULTITERMINAL) { + i--; + } + assert(strcmp(lem.symbols[i - 1]->name, "{default}") == 0); + lem.nsymbol = i - 1; + for (i = 1; ISUPPER(lem.symbols[i]->name[0]); i++) + ; + lem.nterminal = i; + + /* Assign sequential rule numbers. Start with 0. Put rules that have no + ** reduce action C-code associated with them last, so that the switch() + ** statement that selects reduction actions will have a smaller jump table. + */ + for (i = 0, rp = lem.rule; rp; rp = rp->next) { + rp->iRule = rp->code ? i++ : -1; + } + lem.nruleWithAction = i; + for (rp = lem.rule; rp; rp = rp->next) { + if (rp->iRule < 0) rp->iRule = i++; + } + lem.startRule = lem.rule; + lem.rule = Rule_sort(lem.rule); + + /* Generate a reprint of the grammar, if requested on the command line */ + if (rpflag) { + Reprint(&lem); + } else { + /* Initialize the size for all follow and first sets */ + SetSize(lem.nterminal + 1); + + /* Find the precedence for every production rule (that has one) */ + FindRulePrecedences(&lem); + + /* Compute the lambda-nonterminals and the first-sets for every + ** nonterminal */ + FindFirstSets(&lem); + + /* Compute all LR(0) states. Also record follow-set propagation + ** links so that the follow-set can be computed later */ + lem.nstate = 0; + FindStates(&lem); + lem.sorted = State_arrayof(); + + /* Tie up loose ends on the propagation links */ + FindLinks(&lem); + + /* Compute the follow set of every reducible configuration */ + FindFollowSets(&lem); + + /* Compute the action tables */ + FindActions(&lem); + + /* Compress the action tables */ + if (compress == 0) CompressTables(&lem); + + /* Reorder and renumber the states so that states with fewer choices + ** occur at the end. This is an optimization that helps make the + ** generated parser tables smaller. */ + if (noResort == 0) ResortStates(&lem); + + /* Generate a report of the parser generated. (the "y.output" file) */ + if (!quiet) ReportOutput(&lem); + + /* Generate the source code for the parser */ + ReportTable(&lem, mhflag, sqlFlag); + + /* Produce a header file for use by the scanner. (This step is + ** omitted if the "-m" option is used because makeheaders will + ** generate the file for us.) */ + if (!mhflag) ReportHeader(&lem); + } + if (statistics) { + printf("Parser statistics:\n"); + stats_line("terminal symbols", lem.nterminal); + stats_line("non-terminal symbols", lem.nsymbol - lem.nterminal); + stats_line("total symbols", lem.nsymbol); + stats_line("rules", lem.nrule); + stats_line("states", lem.nxstate); + stats_line("conflicts", lem.nconflict); + stats_line("action table entries", lem.nactiontab); + stats_line("lookahead table entries", lem.nlookaheadtab); + stats_line("total table size (bytes)", lem.tablesize); + } + if (lem.nconflict > 0) { + fprintf(stderr, "%d parsing conflicts.\n", lem.nconflict); + } + + /* return 0 on success, 1 on failure. */ + exitcode = ((lem.errorcnt > 0) || (lem.nconflict > 0)) ? 1 : 0; + exit(exitcode); + return (exitcode); +} + +/******************** From the file "msort.c" *******************************/ +/* +** A generic merge-sort program. +** +** USAGE: +** Let "ptr" be a pointer to some structure which is at the head of +** a null-terminated list. Then to sort the list call: +** +** ptr = msort(ptr,&(ptr->next),cmpfnc); +** +** In the above, "cmpfnc" is a pointer to a function which compares +** two instances of the structure and returns an integer, as in +** strcmp. The second argument is a pointer to the pointer to the +** second element of the linked list. This address is used to compute +** the offset to the "next" field within the structure. The offset to +** the "next" field must be constant for all structures in the list. +** +** The function returns a new pointer which is the head of the list +** after sorting. +** +** ALGORITHM: +** Merge-sort. +*/ + +/* +** Return a pointer to the next structure in the linked list. +*/ +#define NEXT(A) (*(char **)(((char *)A) + offset)) + +/* +** Inputs: +** a: A sorted, null-terminated linked list. (May be null). +** b: A sorted, null-terminated linked list. (May be null). +** cmp: A pointer to the comparison function. +** offset: Offset in the structure to the "next" field. +** +** Return Value: +** A pointer to the head of a sorted list containing the elements +** of both a and b. +** +** Side effects: +** The "next" pointers for elements in the lists a and b are +** changed. +*/ +static char *merge(char *a, char *b, int (*cmp)(const char *, const char *), + int offset) { + char *ptr, *head; + if (a == 0) { + head = b; + } else if (b == 0) { + head = a; + } else { + if ((*cmp)(a, b) <= 0) { + ptr = a; + a = NEXT(a); + } else { + ptr = b; + b = NEXT(b); + } + head = ptr; + while (a && b) { + if ((*cmp)(a, b) <= 0) { + NEXT(ptr) = a; + ptr = a; + a = NEXT(a); + } else { + NEXT(ptr) = b; + ptr = b; + b = NEXT(b); + } + } + if (a) + NEXT(ptr) = a; + else + NEXT(ptr) = b; + } + return head; +} + +/* +** Inputs: +** list: Pointer to a singly-linked list of structures. +** next: Pointer to pointer to the second element of the list. +** cmp: A comparison function. +** +** Return Value: +** A pointer to the head of a sorted list containing the elements +** orginally in list. +** +** Side effects: +** The "next" pointers for elements in list are changed. +*/ +#define LISTSIZE 30 +static char *msort(char *list, char **next, + int (*cmp)(const char *, const char *)) { + unsigned long offset; + char *ep; + char *set[LISTSIZE]; + int i; + offset = (unsigned long)((char *)next - (char *)list); + for (i = 0; i < LISTSIZE; i++) set[i] = 0; + while (list) { + ep = list; + list = NEXT(list); + NEXT(ep) = 0; + for (i = 0; i < LISTSIZE - 1 && set[i] != 0; i++) { + ep = merge(ep, set[i], cmp, offset); + set[i] = 0; + } + set[i] = ep; + } + ep = 0; + for (i = 0; i < LISTSIZE; i++) + if (set[i]) ep = merge(set[i], ep, cmp, offset); + return ep; +} + +/************************ From the file "option.c" **************************/ +static char **lemon_argv; +static struct s_options *op; +static FILE *errstream; + +#define ISOPT(X) ((X)[0] == '-' || (X)[0] == '+' || strchr((X), '=') != 0) + +/* +** Print the command line with a carrot pointing to the k-th character +** of the n-th field. +*/ +static void errline(int n, int k, FILE *err) { + int spcnt, i; + if (lemon_argv[0]) fprintf(err, "%s", lemon_argv[0]); + spcnt = lemonStrlen(lemon_argv[0]) + 1; + for (i = 1; i < n && lemon_argv[i]; i++) { + fprintf(err, " %s", lemon_argv[i]); + spcnt += lemonStrlen(lemon_argv[i]) + 1; + } + spcnt += k; + for (; lemon_argv[i]; i++) fprintf(err, " %s", lemon_argv[i]); + if (spcnt < 20) { + fprintf(err, "\n%*s^-- here\n", spcnt, ""); + } else { + fprintf(err, "\n%*shere --^\n", spcnt - 7, ""); + } +} + +/* +** Return the index of the N-th non-switch argument. Return -1 +** if N is out of range. +*/ +static int argindex(int n) { + int i; + int dashdash = 0; + if (lemon_argv != 0 && *lemon_argv != 0) { + for (i = 1; lemon_argv[i]; i++) { + if (dashdash || !ISOPT(lemon_argv[i])) { + if (n == 0) return i; + n--; + } + if (strcmp(lemon_argv[i], "--") == 0) dashdash = 1; + } + } + return -1; +} + +static const char emsg[] = "Command line syntax error: "; + +/* +** Process a flag command line argument. +*/ +static int handleflags(int i, FILE *err) { + int v; + int errcnt = 0; + int j; + for (j = 0; op[j].label; j++) { + if (strncmp(&lemon_argv[i][1], op[j].label, lemonStrlen(op[j].label)) == 0) + break; + } + v = lemon_argv[i][0] == '-' ? 1 : 0; + if (op[j].label == 0) { + if (err) { + fprintf(err, "%sundefined option.\n", emsg); + errline(i, 1, err); + } + errcnt++; + } else if (op[j].arg == 0) { + /* Ignore this option */ + } else if (op[j].type == OPT_FLAG) { + *((int *)op[j].arg) = v; + } else if (op[j].type == OPT_FFLAG) { + (*(void (*)(int))(op[j].arg))(v); + } else if (op[j].type == OPT_FSTR) { + (*(void (*)(char *))(op[j].arg))(&lemon_argv[i][2]); + } else { + if (err) { + fprintf(err, "%smissing argument on switch.\n", emsg); + errline(i, 1, err); + } + errcnt++; + } + return errcnt; +} + +/* +** Process a command line switch which has an argument. +*/ +static int handleswitch(int i, FILE *err) { + int lv = 0; + double dv = 0.0; + char *sv = 0, *end; + char *cp; + int j; + int errcnt = 0; + cp = strchr(lemon_argv[i], '='); + assert(cp != 0); + *cp = 0; + for (j = 0; op[j].label; j++) { + if (strcmp(lemon_argv[i], op[j].label) == 0) break; + } + *cp = '='; + if (op[j].label == 0) { + if (err) { + fprintf(err, "%sundefined option.\n", emsg); + errline(i, 0, err); + } + errcnt++; + } else { + cp++; + switch (op[j].type) { + case OPT_FLAG: + case OPT_FFLAG: + if (err) { + fprintf(err, "%soption requires an argument.\n", emsg); + errline(i, 0, err); + } + errcnt++; + break; + case OPT_DBL: + case OPT_FDBL: + dv = strtod(cp, &end); + if (*end) { + if (err) { + fprintf(err, "%sillegal character in floating-point argument.\n", + emsg); + errline(i, (int)((char *)end - (char *)lemon_argv[i]), err); + } + errcnt++; + } + break; + case OPT_INT: + case OPT_FINT: + lv = strtol(cp, &end, 0); + if (*end) { + if (err) { + fprintf(err, "%sillegal character in integer argument.\n", emsg); + errline(i, (int)((char *)end - (char *)lemon_argv[i]), err); + } + errcnt++; + } + break; + case OPT_STR: + case OPT_FSTR: + sv = cp; + break; + } + switch (op[j].type) { + case OPT_FLAG: + case OPT_FFLAG: + break; + case OPT_DBL: + *(double *)(op[j].arg) = dv; + break; + case OPT_FDBL: + (*(void (*)(double))(op[j].arg))(dv); + break; + case OPT_INT: + *(int *)(op[j].arg) = lv; + break; + case OPT_FINT: + (*(void (*)(int))(op[j].arg))((int)lv); + break; + case OPT_STR: + *(char **)(op[j].arg) = sv; + break; + case OPT_FSTR: + (*(void (*)(char *))(op[j].arg))(sv); + break; + } + } + return errcnt; +} + +int OptInit(char **a, struct s_options *o, FILE *err) { + int errcnt = 0; + lemon_argv = a; + op = o; + errstream = err; + if (lemon_argv && *lemon_argv && op) { + int i; + for (i = 1; lemon_argv[i]; i++) { + if (lemon_argv[i][0] == '+' || lemon_argv[i][0] == '-') { + errcnt += handleflags(i, err); + } else if (strchr(lemon_argv[i], '=')) { + errcnt += handleswitch(i, err); + } + } + } + if (errcnt > 0) { + fprintf(err, "Valid command line options for \"%s\" are:\n", *a); + OptPrint(); + exit(1); + } + return 0; +} + +int OptNArgs(void) { + int cnt = 0; + int dashdash = 0; + int i; + if (lemon_argv != 0 && lemon_argv[0] != 0) { + for (i = 1; lemon_argv[i]; i++) { + if (dashdash || !ISOPT(lemon_argv[i])) cnt++; + if (strcmp(lemon_argv[i], "--") == 0) dashdash = 1; + } + } + return cnt; +} + +char *OptArg(int n) { + int i; + i = argindex(n); + return i >= 0 ? lemon_argv[i] : 0; +} + +void OptErr(int n) { + int i; + i = argindex(n); + if (i >= 0) errline(i, 0, errstream); +} + +void OptPrint(void) { + int i; + int max, len; + max = 0; + for (i = 0; op[i].label; i++) { + len = lemonStrlen(op[i].label) + 1; + switch (op[i].type) { + case OPT_FLAG: + case OPT_FFLAG: + break; + case OPT_INT: + case OPT_FINT: + len += 9; /* length of "" */ + break; + case OPT_DBL: + case OPT_FDBL: + len += 6; /* length of "" */ + break; + case OPT_STR: + case OPT_FSTR: + len += 8; /* length of "" */ + break; + } + if (len > max) max = len; + } + for (i = 0; op[i].label; i++) { + switch (op[i].type) { + case OPT_FLAG: + case OPT_FFLAG: + fprintf(errstream, " -%-*s %s\n", max, op[i].label, op[i].message); + break; + case OPT_INT: + case OPT_FINT: + fprintf(errstream, " -%s%*s %s\n", op[i].label, + (int)(max - lemonStrlen(op[i].label) - 9), "", op[i].message); + break; + case OPT_DBL: + case OPT_FDBL: + fprintf(errstream, " -%s%*s %s\n", op[i].label, + (int)(max - lemonStrlen(op[i].label) - 6), "", op[i].message); + break; + case OPT_STR: + case OPT_FSTR: + fprintf(errstream, " -%s%*s %s\n", op[i].label, + (int)(max - lemonStrlen(op[i].label) - 8), "", op[i].message); + break; + } + } +} + +/*********************** From the file "parse.c" ****************************/ +/* +** Input file parser for the LEMON parser generator. +*/ + +/* The state of the parser */ +enum e_state { + INITIALIZE, + WAITING_FOR_DECL_OR_RULE, + WAITING_FOR_DECL_KEYWORD, + WAITING_FOR_DECL_ARG, + WAITING_FOR_PRECEDENCE_SYMBOL, + WAITING_FOR_ARROW, + IN_RHS, + LHS_ALIAS_1, + LHS_ALIAS_2, + LHS_ALIAS_3, + RHS_ALIAS_1, + RHS_ALIAS_2, + PRECEDENCE_MARK_1, + PRECEDENCE_MARK_2, + RESYNC_AFTER_RULE_ERROR, + RESYNC_AFTER_DECL_ERROR, + WAITING_FOR_DESTRUCTOR_SYMBOL, + WAITING_FOR_DATATYPE_SYMBOL, + WAITING_FOR_FALLBACK_ID, + WAITING_FOR_WILDCARD_ID, + WAITING_FOR_CLASS_ID, + WAITING_FOR_CLASS_TOKEN, + WAITING_FOR_TOKEN_NAME +}; + +struct pstate { + char *filename; /* Name of the input file */ + int tokenlineno; /* Linenumber at which current token starts */ + int errorcnt; /* Number of errors so far */ + char *tokenstart; /* Text of current token */ + struct lemon *gp; /* Global state vector */ + enum e_state state; /* The state of the parser */ + struct symbol *fallback; /* The fallback token */ + struct symbol *tkclass; /* Token class symbol */ + struct symbol *lhs; /* Left-hand side of current rule */ + const char *lhsalias; /* Alias for the LHS */ + int nrhs; /* Number of right-hand side symbols seen */ + struct symbol *rhs[MAXRHS]; /* RHS symbols */ + const char *alias[MAXRHS]; /* Aliases for each RHS symbol (or NULL) */ + struct rule *prevrule; /* Previous rule parsed */ + const char *declkeyword; /* Keyword of a declaration */ + char **declargslot; /* Where the declaration argument should be put */ + int insertLineMacro; /* Add #line before declaration insert */ + int *decllinenoslot; /* Where to write declaration line number */ + enum e_assoc declassoc; /* Assign this association to decl arguments */ + int preccounter; /* Assign this precedence to decl arguments */ + struct rule *firstrule; /* Pointer to first rule in the grammar */ + struct rule *lastrule; /* Pointer to the most recently parsed rule */ +}; + +/* Parse a single token */ +static void parseonetoken(struct pstate *psp) { + const char *x; + x = Strsafe(psp->tokenstart); /* Save the token permanently */ +#if 0 + printf("%s:%d: Token=[%s] state=%d\n",psp->filename,psp->tokenlineno, + x,psp->state); +#endif + switch (psp->state) { + case INITIALIZE: + psp->prevrule = 0; + psp->preccounter = 0; + psp->firstrule = psp->lastrule = 0; + psp->gp->nrule = 0; + /* fall through */ + case WAITING_FOR_DECL_OR_RULE: + if (x[0] == '%') { + psp->state = WAITING_FOR_DECL_KEYWORD; + } else if (ISLOWER(x[0])) { + psp->lhs = Symbol_new(x); + psp->nrhs = 0; + psp->lhsalias = 0; + psp->state = WAITING_FOR_ARROW; + } else if (x[0] == '{') { + if (psp->prevrule == 0) { + ErrorMsg(psp->filename, psp->tokenlineno, + "There is no prior rule upon which to attach the code " + "fragment which begins on this line."); + psp->errorcnt++; + } else if (psp->prevrule->code != 0) { + ErrorMsg(psp->filename, psp->tokenlineno, + "Code fragment beginning on this line is not the first " + "to follow the previous rule."); + psp->errorcnt++; + } else if (strcmp(x, "{NEVER-REDUCE") == 0) { + psp->prevrule->neverReduce = 1; + } else { + psp->prevrule->line = psp->tokenlineno; + psp->prevrule->code = &x[1]; + psp->prevrule->noCode = 0; + } + } else if (x[0] == '[') { + psp->state = PRECEDENCE_MARK_1; + } else { + ErrorMsg(psp->filename, psp->tokenlineno, + "Token \"%s\" should be either \"%%\" or a nonterminal name.", + x); + psp->errorcnt++; + } + break; + case PRECEDENCE_MARK_1: + if (!ISUPPER(x[0])) { + ErrorMsg(psp->filename, psp->tokenlineno, + "The precedence symbol must be a terminal."); + psp->errorcnt++; + } else if (psp->prevrule == 0) { + ErrorMsg(psp->filename, psp->tokenlineno, + "There is no prior rule to assign precedence \"[%s]\".", x); + psp->errorcnt++; + } else if (psp->prevrule->precsym != 0) { + ErrorMsg(psp->filename, psp->tokenlineno, + "Precedence mark on this line is not the first " + "to follow the previous rule."); + psp->errorcnt++; + } else { + psp->prevrule->precsym = Symbol_new(x); + } + psp->state = PRECEDENCE_MARK_2; + break; + case PRECEDENCE_MARK_2: + if (x[0] != ']') { + ErrorMsg(psp->filename, psp->tokenlineno, + "Missing \"]\" on precedence mark."); + psp->errorcnt++; + } + psp->state = WAITING_FOR_DECL_OR_RULE; + break; + case WAITING_FOR_ARROW: + if (x[0] == ':' && x[1] == ':' && x[2] == '=') { + psp->state = IN_RHS; + } else if (x[0] == '(') { + psp->state = LHS_ALIAS_1; + } else { + ErrorMsg(psp->filename, psp->tokenlineno, + "Expected to see a \":\" following the LHS symbol \"%s\".", + psp->lhs->name); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + } + break; + case LHS_ALIAS_1: + if (ISALPHA(x[0])) { + psp->lhsalias = x; + psp->state = LHS_ALIAS_2; + } else { + ErrorMsg(psp->filename, psp->tokenlineno, + "\"%s\" is not a valid alias for the LHS \"%s\"\n", x, + psp->lhs->name); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + } + break; + case LHS_ALIAS_2: + if (x[0] == ')') { + psp->state = LHS_ALIAS_3; + } else { + ErrorMsg(psp->filename, psp->tokenlineno, + "Missing \")\" following LHS alias name \"%s\".", + psp->lhsalias); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + } + break; + case LHS_ALIAS_3: + if (x[0] == ':' && x[1] == ':' && x[2] == '=') { + psp->state = IN_RHS; + } else { + ErrorMsg(psp->filename, psp->tokenlineno, + "Missing \"->\" following: \"%s(%s)\".", psp->lhs->name, + psp->lhsalias); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + } + break; + case IN_RHS: + if (x[0] == '.') { + struct rule *rp; + rp = (struct rule *)calloc(sizeof(struct rule) + + sizeof(struct symbol *) * psp->nrhs + + sizeof(char *) * psp->nrhs, + 1); + if (rp == 0) { + ErrorMsg(psp->filename, psp->tokenlineno, + "Can't allocate enough memory for this rule."); + psp->errorcnt++; + psp->prevrule = 0; + } else { + int i; + rp->ruleline = psp->tokenlineno; + rp->rhs = (struct symbol **)&rp[1]; + rp->rhsalias = (const char **)&(rp->rhs[psp->nrhs]); + for (i = 0; i < psp->nrhs; i++) { + rp->rhs[i] = psp->rhs[i]; + rp->rhsalias[i] = psp->alias[i]; + if (rp->rhsalias[i] != 0) { + rp->rhs[i]->bContent = 1; + } + } + rp->lhs = psp->lhs; + rp->lhsalias = psp->lhsalias; + rp->nrhs = psp->nrhs; + rp->code = 0; + rp->noCode = 1; + rp->precsym = 0; + rp->index = psp->gp->nrule++; + rp->nextlhs = rp->lhs->rule; + rp->lhs->rule = rp; + rp->next = 0; + if (psp->firstrule == 0) { + psp->firstrule = psp->lastrule = rp; + } else { + psp->lastrule->next = rp; + psp->lastrule = rp; + } + psp->prevrule = rp; + } + psp->state = WAITING_FOR_DECL_OR_RULE; + } else if (ISALPHA(x[0])) { + if (psp->nrhs >= MAXRHS) { + ErrorMsg(psp->filename, psp->tokenlineno, + "Too many symbols on RHS of rule beginning at \"%s\".", x); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + } else { + psp->rhs[psp->nrhs] = Symbol_new(x); + psp->alias[psp->nrhs] = 0; + psp->nrhs++; + } + } else if ((x[0] == '|' || x[0] == '/') && psp->nrhs > 0 && + ISUPPER(x[1])) { + struct symbol *msp = psp->rhs[psp->nrhs - 1]; + if (msp->type != MULTITERMINAL) { + struct symbol *origsp = msp; + msp = (struct symbol *)calloc(1, sizeof(*msp)); + memset(msp, 0, sizeof(*msp)); + msp->type = MULTITERMINAL; + msp->nsubsym = 1; + msp->subsym = (struct symbol **)calloc(1, sizeof(struct symbol *)); + msp->subsym[0] = origsp; + msp->name = origsp->name; + psp->rhs[psp->nrhs - 1] = msp; + } + msp->nsubsym++; + msp->subsym = (struct symbol **)realloc( + msp->subsym, sizeof(struct symbol *) * msp->nsubsym); + msp->subsym[msp->nsubsym - 1] = Symbol_new(&x[1]); + if (ISLOWER(x[1]) || ISLOWER(msp->subsym[0]->name[0])) { + ErrorMsg(psp->filename, psp->tokenlineno, + "Cannot form a compound containing a non-terminal"); + psp->errorcnt++; + } + } else if (x[0] == '(' && psp->nrhs > 0) { + psp->state = RHS_ALIAS_1; + } else { + ErrorMsg(psp->filename, psp->tokenlineno, + "Illegal character on RHS of rule: \"%s\".", x); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + } + break; + case RHS_ALIAS_1: + if (ISALPHA(x[0])) { + psp->alias[psp->nrhs - 1] = x; + psp->state = RHS_ALIAS_2; + } else { + ErrorMsg(psp->filename, psp->tokenlineno, + "\"%s\" is not a valid alias for the RHS symbol \"%s\"\n", x, + psp->rhs[psp->nrhs - 1]->name); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + } + break; + case RHS_ALIAS_2: + if (x[0] == ')') { + psp->state = IN_RHS; + } else { + ErrorMsg(psp->filename, psp->tokenlineno, + "Missing \")\" following LHS alias name \"%s\".", + psp->lhsalias); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + } + break; + case WAITING_FOR_DECL_KEYWORD: + if (ISALPHA(x[0])) { + psp->declkeyword = x; + psp->declargslot = 0; + psp->decllinenoslot = 0; + psp->insertLineMacro = 1; + psp->state = WAITING_FOR_DECL_ARG; + if (strcmp(x, "name") == 0) { + psp->declargslot = &(psp->gp->name); + psp->insertLineMacro = 0; + } else if (strcmp(x, "include") == 0) { + psp->declargslot = &(psp->gp->include); + } else if (strcmp(x, "code") == 0) { + psp->declargslot = &(psp->gp->extracode); + } else if (strcmp(x, "token_destructor") == 0) { + psp->declargslot = &psp->gp->tokendest; + } else if (strcmp(x, "default_destructor") == 0) { + psp->declargslot = &psp->gp->vardest; + } else if (strcmp(x, "token_prefix") == 0) { + psp->declargslot = &psp->gp->tokenprefix; + psp->insertLineMacro = 0; + } else if (strcmp(x, "syntax_error") == 0) { + psp->declargslot = &(psp->gp->error); + } else if (strcmp(x, "parse_accept") == 0) { + psp->declargslot = &(psp->gp->accept); + } else if (strcmp(x, "parse_failure") == 0) { + psp->declargslot = &(psp->gp->failure); + } else if (strcmp(x, "stack_overflow") == 0) { + psp->declargslot = &(psp->gp->overflow); + } else if (strcmp(x, "extra_argument") == 0) { + psp->declargslot = &(psp->gp->arg); + psp->insertLineMacro = 0; + } else if (strcmp(x, "extra_context") == 0) { + psp->declargslot = &(psp->gp->ctx); + psp->insertLineMacro = 0; + } else if (strcmp(x, "token_type") == 0) { + psp->declargslot = &(psp->gp->tokentype); + psp->insertLineMacro = 0; + } else if (strcmp(x, "default_type") == 0) { + psp->declargslot = &(psp->gp->vartype); + psp->insertLineMacro = 0; + } else if (strcmp(x, "stack_size") == 0) { + psp->declargslot = &(psp->gp->stacksize); + psp->insertLineMacro = 0; + } else if (strcmp(x, "start_symbol") == 0) { + psp->declargslot = &(psp->gp->start); + psp->insertLineMacro = 0; + } else if (strcmp(x, "left") == 0) { + psp->preccounter++; + psp->declassoc = LEFT; + psp->state = WAITING_FOR_PRECEDENCE_SYMBOL; + } else if (strcmp(x, "right") == 0) { + psp->preccounter++; + psp->declassoc = RIGHT; + psp->state = WAITING_FOR_PRECEDENCE_SYMBOL; + } else if (strcmp(x, "nonassoc") == 0) { + psp->preccounter++; + psp->declassoc = NONE; + psp->state = WAITING_FOR_PRECEDENCE_SYMBOL; + } else if (strcmp(x, "destructor") == 0) { + psp->state = WAITING_FOR_DESTRUCTOR_SYMBOL; + } else if (strcmp(x, "type") == 0) { + psp->state = WAITING_FOR_DATATYPE_SYMBOL; + } else if (strcmp(x, "fallback") == 0) { + psp->fallback = 0; + psp->state = WAITING_FOR_FALLBACK_ID; + } else if (strcmp(x, "token") == 0) { + psp->state = WAITING_FOR_TOKEN_NAME; + } else if (strcmp(x, "wildcard") == 0) { + psp->state = WAITING_FOR_WILDCARD_ID; + } else if (strcmp(x, "token_class") == 0) { + psp->state = WAITING_FOR_CLASS_ID; + } else { + ErrorMsg(psp->filename, psp->tokenlineno, + "Unknown declaration keyword: \"%%%s\".", x); + psp->errorcnt++; + psp->state = RESYNC_AFTER_DECL_ERROR; + } + } else { + ErrorMsg(psp->filename, psp->tokenlineno, + "Illegal declaration keyword: \"%s\".", x); + psp->errorcnt++; + psp->state = RESYNC_AFTER_DECL_ERROR; + } + break; + case WAITING_FOR_DESTRUCTOR_SYMBOL: + if (!ISALPHA(x[0])) { + ErrorMsg(psp->filename, psp->tokenlineno, + "Symbol name missing after %%destructor keyword"); + psp->errorcnt++; + psp->state = RESYNC_AFTER_DECL_ERROR; + } else { + struct symbol *sp = Symbol_new(x); + psp->declargslot = &sp->destructor; + psp->decllinenoslot = &sp->destLineno; + psp->insertLineMacro = 1; + psp->state = WAITING_FOR_DECL_ARG; + } + break; + case WAITING_FOR_DATATYPE_SYMBOL: + if (!ISALPHA(x[0])) { + ErrorMsg(psp->filename, psp->tokenlineno, + "Symbol name missing after %%type keyword"); + psp->errorcnt++; + psp->state = RESYNC_AFTER_DECL_ERROR; + } else { + struct symbol *sp = Symbol_find(x); + if ((sp) && (sp->datatype)) { + ErrorMsg(psp->filename, psp->tokenlineno, + "Symbol %%type \"%s\" already defined", x); + psp->errorcnt++; + psp->state = RESYNC_AFTER_DECL_ERROR; + } else { + if (!sp) { + sp = Symbol_new(x); + } + psp->declargslot = &sp->datatype; + psp->insertLineMacro = 0; + psp->state = WAITING_FOR_DECL_ARG; + } + } + break; + case WAITING_FOR_PRECEDENCE_SYMBOL: + if (x[0] == '.') { + psp->state = WAITING_FOR_DECL_OR_RULE; + } else if (ISUPPER(x[0])) { + struct symbol *sp; + sp = Symbol_new(x); + if (sp->prec >= 0) { + ErrorMsg(psp->filename, psp->tokenlineno, + "Symbol \"%s\" has already be given a precedence.", x); + psp->errorcnt++; + } else { + sp->prec = psp->preccounter; + sp->assoc = psp->declassoc; + } + } else { + ErrorMsg(psp->filename, psp->tokenlineno, + "Can't assign a precedence to \"%s\".", x); + psp->errorcnt++; + } + break; + case WAITING_FOR_DECL_ARG: + if (x[0] == '{' || x[0] == '\"' || ISALNUM(x[0])) { + const char *zOld, *zNew; + char *zBuf, *z; + int nOld, n, nLine = 0, nNew, nBack; + int addLineMacro; + char zLine[50]; + zNew = x; + if (zNew[0] == '"' || zNew[0] == '{') zNew++; + nNew = lemonStrlen(zNew); + if (*psp->declargslot) { + zOld = *psp->declargslot; + } else { + zOld = ""; + } + nOld = lemonStrlen(zOld); + n = nOld + nNew + 20; + addLineMacro = + !psp->gp->nolinenosflag && psp->insertLineMacro && + psp->tokenlineno > 1 && + (psp->decllinenoslot == 0 || psp->decllinenoslot[0] != 0); + if (addLineMacro) { + for (z = psp->filename, nBack = 0; *z; z++) { + if (*z == '\\') nBack++; + } + lemon_sprintf(zLine, "#line %d ", psp->tokenlineno); + nLine = lemonStrlen(zLine); + n += nLine + lemonStrlen(psp->filename) + nBack; + } + *psp->declargslot = (char *)realloc(*psp->declargslot, n); + zBuf = *psp->declargslot + nOld; + if (addLineMacro) { + if (nOld && zBuf[-1] != '\n') { + *(zBuf++) = '\n'; + } + memcpy(zBuf, zLine, nLine); + zBuf += nLine; + *(zBuf++) = '"'; + for (z = psp->filename; *z; z++) { + if (*z == '\\') { + *(zBuf++) = '\\'; + } + *(zBuf++) = *z; + } + *(zBuf++) = '"'; + *(zBuf++) = '\n'; + } + if (psp->decllinenoslot && psp->decllinenoslot[0] == 0) { + psp->decllinenoslot[0] = psp->tokenlineno; + } + memcpy(zBuf, zNew, nNew); + zBuf += nNew; + *zBuf = 0; + psp->state = WAITING_FOR_DECL_OR_RULE; + } else { + ErrorMsg(psp->filename, psp->tokenlineno, + "Illegal argument to %%%s: %s", psp->declkeyword, x); + psp->errorcnt++; + psp->state = RESYNC_AFTER_DECL_ERROR; + } + break; + case WAITING_FOR_FALLBACK_ID: + if (x[0] == '.') { + psp->state = WAITING_FOR_DECL_OR_RULE; + } else if (!ISUPPER(x[0])) { + ErrorMsg(psp->filename, psp->tokenlineno, + "%%fallback argument \"%s\" should be a token", x); + psp->errorcnt++; + } else { + struct symbol *sp = Symbol_new(x); + if (psp->fallback == 0) { + psp->fallback = sp; + } else if (sp->fallback) { + ErrorMsg(psp->filename, psp->tokenlineno, + "More than one fallback assigned to token %s", x); + psp->errorcnt++; + } else { + sp->fallback = psp->fallback; + psp->gp->has_fallback = 1; + } + } + break; + case WAITING_FOR_TOKEN_NAME: + /* Tokens do not have to be declared before use. But they can be + ** in order to control their assigned integer number. The number for + ** each token is assigned when it is first seen. So by including + ** + ** %token ONE TWO THREE + ** + ** early in the grammar file, that assigns small consecutive values + ** to each of the tokens ONE TWO and THREE. + */ + if (x[0] == '.') { + psp->state = WAITING_FOR_DECL_OR_RULE; + } else if (!ISUPPER(x[0])) { + ErrorMsg(psp->filename, psp->tokenlineno, + "%%token argument \"%s\" should be a token", x); + psp->errorcnt++; + } else { + (void)Symbol_new(x); + } + break; + case WAITING_FOR_WILDCARD_ID: + if (x[0] == '.') { + psp->state = WAITING_FOR_DECL_OR_RULE; + } else if (!ISUPPER(x[0])) { + ErrorMsg(psp->filename, psp->tokenlineno, + "%%wildcard argument \"%s\" should be a token", x); + psp->errorcnt++; + } else { + struct symbol *sp = Symbol_new(x); + if (psp->gp->wildcard == 0) { + psp->gp->wildcard = sp; + } else { + ErrorMsg(psp->filename, psp->tokenlineno, + "Extra wildcard to token: %s", x); + psp->errorcnt++; + } + } + break; + case WAITING_FOR_CLASS_ID: + if (!ISLOWER(x[0])) { + ErrorMsg(psp->filename, psp->tokenlineno, + "%%token_class must be followed by an identifier: %s", x); + psp->errorcnt++; + psp->state = RESYNC_AFTER_DECL_ERROR; + } else if (Symbol_find(x)) { + ErrorMsg(psp->filename, psp->tokenlineno, "Symbol \"%s\" already used", + x); + psp->errorcnt++; + psp->state = RESYNC_AFTER_DECL_ERROR; + } else { + psp->tkclass = Symbol_new(x); + psp->tkclass->type = MULTITERMINAL; + psp->state = WAITING_FOR_CLASS_TOKEN; + } + break; + case WAITING_FOR_CLASS_TOKEN: + if (x[0] == '.') { + psp->state = WAITING_FOR_DECL_OR_RULE; + } else if (ISUPPER(x[0]) || + ((x[0] == '|' || x[0] == '/') && ISUPPER(x[1]))) { + struct symbol *msp = psp->tkclass; + msp->nsubsym++; + msp->subsym = (struct symbol **)realloc( + msp->subsym, sizeof(struct symbol *) * msp->nsubsym); + if (!ISUPPER(x[0])) x++; + msp->subsym[msp->nsubsym - 1] = Symbol_new(x); + } else { + ErrorMsg(psp->filename, psp->tokenlineno, + "%%token_class argument \"%s\" should be a token", x); + psp->errorcnt++; + psp->state = RESYNC_AFTER_DECL_ERROR; + } + break; + case RESYNC_AFTER_RULE_ERROR: + /* if( x[0]=='.' ) psp->state = WAITING_FOR_DECL_OR_RULE; + ** break; */ + case RESYNC_AFTER_DECL_ERROR: + if (x[0] == '.') psp->state = WAITING_FOR_DECL_OR_RULE; + if (x[0] == '%') psp->state = WAITING_FOR_DECL_KEYWORD; + break; + } +} + +/* The text in the input is part of the argument to an %ifdef or %ifndef. +** Evaluate the text as a boolean expression. Return true or false. +*/ +static int eval_preprocessor_boolean(char *z, int lineno) { + int neg = 0; + int res = 0; + int okTerm = 1; + int i; + for (i = 0; z[i] != 0; i++) { + if (ISSPACE(z[i])) continue; + if (z[i] == '!') { + if (!okTerm) goto pp_syntax_error; + neg = !neg; + continue; + } + if (z[i] == '|' && z[i + 1] == '|') { + if (okTerm) goto pp_syntax_error; + if (res) return 1; + i++; + okTerm = 1; + continue; + } + if (z[i] == '&' && z[i + 1] == '&') { + if (okTerm) goto pp_syntax_error; + if (!res) return 0; + i++; + okTerm = 1; + continue; + } + if (z[i] == '(') { + int k; + int n = 1; + if (!okTerm) goto pp_syntax_error; + for (k = i + 1; z[k]; k++) { + if (z[k] == ')') { + n--; + if (n == 0) { + z[k] = 0; + res = eval_preprocessor_boolean(&z[i + 1], -1); + z[k] = ')'; + if (res < 0) { + i = i - res; + goto pp_syntax_error; + } + i = k; + break; + } + } else if (z[k] == '(') { + n++; + } else if (z[k] == 0) { + i = k; + goto pp_syntax_error; + } + } + if (neg) { + res = !res; + neg = 0; + } + okTerm = 0; + continue; + } + if (ISALPHA(z[i])) { + int j, k, n; + if (!okTerm) goto pp_syntax_error; + for (k = i + 1; ISALNUM(z[k]) || z[k] == '_'; k++) { + } + n = k - i; + res = 0; + for (j = 0; j < nDefine; j++) { + if (strncmp(azDefine[j], &z[i], n) == 0 && azDefine[j][n] == 0) { + res = 1; + break; + } + } + i = k - 1; + if (neg) { + res = !res; + neg = 0; + } + okTerm = 0; + continue; + } + goto pp_syntax_error; + } + return res; + +pp_syntax_error: + if (lineno > 0) { + fprintf(stderr, "%%if syntax error on line %d.\n", lineno); + fprintf(stderr, " %.*s <-- syntax error here\n", i + 1, z); + exit(1); + } else { + return -(i + 1); + } +} + +/* Run the preprocessor over the input file text. The global variables +** azDefine[0] through azDefine[nDefine-1] contains the names of all defined +** macros. This routine looks for "%ifdef" and "%ifndef" and "%endif" and +** comments them out. Text in between is also commented out as appropriate. +*/ +static void preprocess_input(char *z) { + int i, j, k; + int exclude = 0; + int start = 0; + int lineno = 1; + int start_lineno = 1; + for (i = 0; z[i]; i++) { + if (z[i] == '\n') lineno++; + if (z[i] != '%' || (i > 0 && z[i - 1] != '\n')) continue; + if (strncmp(&z[i], "%endif", 6) == 0 && ISSPACE(z[i + 6])) { + if (exclude) { + exclude--; + if (exclude == 0) { + for (j = start; j < i; j++) + if (z[j] != '\n') z[j] = ' '; + } + } + for (j = i; z[j] && z[j] != '\n'; j++) z[j] = ' '; + } else if (strncmp(&z[i], "%else", 5) == 0 && ISSPACE(z[i + 5])) { + if (exclude == 1) { + exclude = 0; + for (j = start; j < i; j++) + if (z[j] != '\n') z[j] = ' '; + } else if (exclude == 0) { + exclude = 1; + start = i; + start_lineno = lineno; + } + for (j = i; z[j] && z[j] != '\n'; j++) z[j] = ' '; + } else if (strncmp(&z[i], "%ifdef ", 7) == 0 || + strncmp(&z[i], "%if ", 4) == 0 || + strncmp(&z[i], "%ifndef ", 8) == 0) { + if (exclude) { + exclude++; + } else { + int isNot; + int iBool; + for (j = i; z[j] && !ISSPACE(z[j]); j++) { + } + iBool = j; + isNot = (j == i + 7); + while (z[j] && z[j] != '\n') { + j++; + } + k = z[j]; + z[j] = 0; + exclude = eval_preprocessor_boolean(&z[iBool], lineno); + z[j] = k; + if (!isNot) exclude = !exclude; + if (exclude) { + start = i; + start_lineno = lineno; + } + } + for (j = i; z[j] && z[j] != '\n'; j++) z[j] = ' '; + } + } + if (exclude) { + fprintf(stderr, "unterminated %%ifdef starting on line %d\n", start_lineno); + exit(1); + } +} + +/* In spite of its name, this function is really a scanner. It read +** in the entire input file (all at once) then tokenizes it. Each +** token is passed to the function "parseonetoken" which builds all +** the appropriate data structures in the global state vector "gp". +*/ +void Parse(struct lemon *gp) { + struct pstate *ps; + FILE *fp; + char *filebuf; + unsigned int filesize; + int lineno; + int c; + char *cp, *nextcp; + int startline = 0; + + ps = gc(xcalloc(1, sizeof(struct pstate))); + ps->gp = gp; + ps->filename = gp->filename; + ps->errorcnt = 0; + ps->state = INITIALIZE; + + /* Begin by reading the input file */ + fp = fopen(ps->filename, "rb"); + if (fp == 0) { + ErrorMsg(ps->filename, 0, "Can't open this file for reading."); + gp->errorcnt++; + return; + } + fseek(fp, 0, 2); + filesize = ftell(fp); + rewind(fp); + filebuf = (char *)malloc(filesize + 1); + if (filesize > 100000000 || filebuf == 0) { + ErrorMsg(ps->filename, 0, "Input file too large."); + free(filebuf); + gp->errorcnt++; + fclose(fp); + return; + } + if (fread(filebuf, 1, filesize, fp) != filesize) { + ErrorMsg(ps->filename, 0, "Can't read in all %d bytes of this file.", + filesize); + free(filebuf); + gp->errorcnt++; + fclose(fp); + return; + } + fclose(fp); + filebuf[filesize] = 0; + + /* Make an initial pass through the file to handle %ifdef and %ifndef */ + preprocess_input(filebuf); + if (gp->printPreprocessed) { + printf("%s\n", filebuf); + return; + } + + /* Now scan the text of the input file */ + lineno = 1; + for (cp = filebuf; (c = *cp) != 0;) { + if (c == '\n') lineno++; /* Keep track of the line number */ + if (ISSPACE(c)) { + cp++; + continue; + } /* Skip all white space */ + if (c == '/' && cp[1] == '/') { /* Skip C++ style comments */ + cp += 2; + while ((c = *cp) != 0 && c != '\n') cp++; + continue; + } + if (c == '/' && cp[1] == '*') { /* Skip C style comments */ + cp += 2; + while ((c = *cp) != 0 && (c != '/' || cp[-1] != '*')) { + if (c == '\n') lineno++; + cp++; + } + if (c) cp++; + continue; + } + ps->tokenstart = cp; /* Mark the beginning of the token */ + ps->tokenlineno = lineno; /* Linenumber on which token begins */ + if (c == '\"') { /* String literals */ + cp++; + while ((c = *cp) != 0 && c != '\"') { + if (c == '\n') lineno++; + cp++; + } + if (c == 0) { + ErrorMsg(ps->filename, startline, + "String starting on this line is not terminated before " + "the end of the file."); + ps->errorcnt++; + nextcp = cp; + } else { + nextcp = cp + 1; + } + } else if (c == '{') { /* A block of C code */ + int level; + cp++; + for (level = 1; (c = *cp) != 0 && (level > 1 || c != '}'); cp++) { + if (c == '\n') + lineno++; + else if (c == '{') + level++; + else if (c == '}') + level--; + else if (c == '/' && cp[1] == '*') { /* Skip comments */ + int prevc; + cp = &cp[2]; + prevc = 0; + while ((c = *cp) != 0 && (c != '/' || prevc != '*')) { + if (c == '\n') lineno++; + prevc = c; + cp++; + } + } else if (c == '/' && cp[1] == '/') { /* Skip C++ style comments too */ + cp = &cp[2]; + while ((c = *cp) != 0 && c != '\n') cp++; + if (c) lineno++; + } else if (c == '\'' || c == '\"') { /* String a character literals */ + int startchar, prevc; + startchar = c; + prevc = 0; + for (cp++; (c = *cp) != 0 && (c != startchar || prevc == '\\'); + cp++) { + if (c == '\n') lineno++; + if (prevc == '\\') + prevc = 0; + else + prevc = c; + } + } + } + if (c == 0) { + ErrorMsg(ps->filename, ps->tokenlineno, + "C code starting on this line is not terminated before " + "the end of the file."); + ps->errorcnt++; + nextcp = cp; + } else { + nextcp = cp + 1; + } + } else if (ISALNUM(c)) { /* Identifiers */ + while ((c = *cp) != 0 && (ISALNUM(c) || c == '_')) cp++; + nextcp = cp; + } else if (c == ':' && cp[1] == ':' && + cp[2] == '=') { /* The operator "::=" */ + cp += 3; + nextcp = cp; + } else if ((c == '/' || c == '|') && ISALPHA(cp[1])) { + cp += 2; + while ((c = *cp) != 0 && (ISALNUM(c) || c == '_')) cp++; + nextcp = cp; + } else { /* All other (one character) operators */ + cp++; + nextcp = cp; + } + c = *cp; + *cp = 0; /* Null terminate the token */ + parseonetoken(ps); /* Parse the token */ + *cp = (char)c; /* Restore the buffer */ + cp = nextcp; + } + free(filebuf); /* Release the buffer after parsing */ + gp->rule = ps->firstrule; + gp->errorcnt = ps->errorcnt; +} + +/*************************** From the file "plink.c" *********************/ +/* +** Routines processing configuration follow-set propagation links +** in the LEMON parser generator. +*/ +static struct plink *plink_freelist = 0; + +/* Allocate a new plink */ +struct plink *Plink_new(void) { + struct plink *newlink; + + if (plink_freelist == 0) { + int i; + int amt = 100; + plink_freelist = (struct plink *)calloc(amt, sizeof(struct plink)); + if (plink_freelist == 0) { + fprintf( + stderr, + "Unable to allocate memory for a new follow-set propagation link.\n"); + exit(1); + } + for (i = 0; i < amt - 1; i++) + plink_freelist[i].next = &plink_freelist[i + 1]; + plink_freelist[amt - 1].next = 0; + } + newlink = plink_freelist; + plink_freelist = plink_freelist->next; + return newlink; +} + +/* Add a plink to a plink list */ +void Plink_add(struct plink **plpp, struct config *cfp) { + struct plink *newlink; + newlink = Plink_new(); + newlink->next = *plpp; + *plpp = newlink; + newlink->cfp = cfp; +} + +/* Transfer every plink on the list "from" to the list "to" */ +void Plink_copy(struct plink **to, struct plink *from) { + struct plink *nextpl; + while (from) { + nextpl = from->next; + from->next = *to; + *to = from; + from = nextpl; + } +} + +/* Delete every plink on the list */ +void Plink_delete(struct plink *plp) { + struct plink *nextpl; + + while (plp) { + nextpl = plp->next; + plp->next = plink_freelist; + plink_freelist = plp; + plp = nextpl; + } +} + +/*********************** From the file "report.c" **************************/ +/* +** Procedures for generating reports and tables in the LEMON parser generator. +*/ + +/* Generate a filename with the given suffix. Space to hold the +** name comes from malloc() and must be freed by the calling +** function. +*/ +PRIVATE char *file_makename(struct lemon *lemp, const char *suffix) { + char *name; + char *cp; + char *filename = lemp->filename; + int sz; + + if (outputDir) { + cp = strrchr(filename, '/'); + if (cp) filename = cp + 1; + } + sz = lemonStrlen(filename); + sz += lemonStrlen(suffix); + if (outputDir) sz += lemonStrlen(outputDir) + 1; + sz += 5; + name = (char *)malloc(sz); + if (name == 0) { + fprintf(stderr, "Can't allocate space for a filename.\n"); + exit(1); + } + name[0] = 0; + if (outputDir) { + lemon_strcpy(name, outputDir); + lemon_strcat(name, "/"); + } + lemon_strcat(name, filename); + cp = strrchr(name, '.'); + if (cp) *cp = 0; + lemon_strcat(name, suffix); + return name; +} + +/* Open a file with a name based on the name of the input file, +** but with a different (specified) suffix, and return a pointer +** to the stream */ +PRIVATE FILE *file_open(struct lemon *lemp, const char *suffix, + const char *mode) { + FILE *fp; + + if (lemp->outname) free(lemp->outname); + lemp->outname = file_makename(lemp, suffix); + fp = fopen(lemp->outname, mode); + if (fp == 0 && *mode == 'w') { + fprintf(stderr, "Can't open file \"%s\".\n", lemp->outname); + lemp->errorcnt++; + return 0; + } + return fp; +} + +/* Print the text of a rule + */ +void rule_print(FILE *out, struct rule *rp) { + int i, j; + fprintf(out, "%s", rp->lhs->name); + /* if( rp->lhsalias ) fprintf(out,"(%s)",rp->lhsalias); */ + fprintf(out, " ::="); + for (i = 0; i < rp->nrhs; i++) { + struct symbol *sp = rp->rhs[i]; + if (sp->type == MULTITERMINAL) { + fprintf(out, " %s", sp->subsym[0]->name); + for (j = 1; j < sp->nsubsym; j++) { + fprintf(out, "|%s", sp->subsym[j]->name); + } + } else { + fprintf(out, " %s", sp->name); + } + /* if( rp->rhsalias[i] ) fprintf(out,"(%s)",rp->rhsalias[i]); */ + } +} + +/* Duplicate the input file without comments and without actions +** on rules */ +void Reprint(struct lemon *lemp) { + struct rule *rp; + struct symbol *sp; + int i, j, maxlen, len, ncolumns, skip; + printf("// Reprint of input file \"%s\".\n// Symbols:\n", lemp->filename); + maxlen = 10; + for (i = 0; i < lemp->nsymbol; i++) { + sp = lemp->symbols[i]; + len = lemonStrlen(sp->name); + if (len > maxlen) maxlen = len; + } + ncolumns = 76 / (maxlen + 5); + if (ncolumns < 1) ncolumns = 1; + skip = (lemp->nsymbol + ncolumns - 1) / ncolumns; + for (i = 0; i < skip; i++) { + printf("//"); + for (j = i; j < lemp->nsymbol; j += skip) { + sp = lemp->symbols[j]; + assert(sp->index == j); + printf(" %3d %-*.*s", j, maxlen, maxlen, sp->name); + } + printf("\n"); + } + for (rp = lemp->rule; rp; rp = rp->next) { + rule_print(stdout, rp); + printf("."); + if (rp->precsym) printf(" [%s]", rp->precsym->name); + /* if( rp->code ) printf("\n %s",rp->code); */ + printf("\n"); + } +} + +/* Print a single rule. + */ +void RulePrint(FILE *fp, struct rule *rp, int iCursor) { + struct symbol *sp; + int i, j; + fprintf(fp, "%s ::=", rp->lhs->name); + for (i = 0; i <= rp->nrhs; i++) { + if (i == iCursor) fprintf(fp, " *"); + if (i == rp->nrhs) break; + sp = rp->rhs[i]; + if (sp->type == MULTITERMINAL) { + fprintf(fp, " %s", sp->subsym[0]->name); + for (j = 1; j < sp->nsubsym; j++) { + fprintf(fp, "|%s", sp->subsym[j]->name); + } + } else { + fprintf(fp, " %s", sp->name); + } + } +} + +/* Print the rule for a configuration. + */ +void ConfigPrint(FILE *fp, struct config *cfp) { + RulePrint(fp, cfp->rp, cfp->dot); +} + +/* #define TEST */ +#if 0 +/* Print a set */ +PRIVATE void SetPrint(out,set,lemp) +FILE *out; +char *set; +struct lemon *lemp; +{ + int i; + char *spacer; + spacer = ""; + fprintf(out,"%12s[",""); + for(i=0; interminal; i++){ + if( SetFind(set,i) ){ + fprintf(out,"%s%s",spacer,lemp->symbols[i]->name); + spacer = " "; + } + } + fprintf(out,"]\n"); +} + +/* Print a plink chain */ +PRIVATE void PlinkPrint(out,plp,tag) +FILE *out; +struct plink *plp; +char *tag; +{ + while( plp ){ + fprintf(out,"%12s%s (state %2d) ","",tag,plp->cfp->stp->statenum); + ConfigPrint(out,plp->cfp); + fprintf(out,"\n"); + plp = plp->next; + } +} +#endif + +/* Print an action to the given file descriptor. Return FALSE if +** nothing was actually printed. +*/ +int PrintAction(struct action *ap, /* The action to print */ + FILE *fp, /* Print the action here */ + int indent /* Indent by this amount */ +) { + int result = 1; + switch (ap->type) { + case SHIFT: { + struct state *stp = ap->x.stp; + fprintf(fp, "%*s shift %-7d", indent, ap->sp->name, stp->statenum); + break; + } + case REDUCE: { + struct rule *rp = ap->x.rp; + fprintf(fp, "%*s reduce %-7d", indent, ap->sp->name, rp->iRule); + RulePrint(fp, rp, -1); + break; + } + case SHIFTREDUCE: { + struct rule *rp = ap->x.rp; + fprintf(fp, "%*s shift-reduce %-7d", indent, ap->sp->name, rp->iRule); + RulePrint(fp, rp, -1); + break; + } + case ACCEPT: + fprintf(fp, "%*s accept", indent, ap->sp->name); + break; + case ERROR: + fprintf(fp, "%*s error", indent, ap->sp->name); + break; + case SRCONFLICT: + case RRCONFLICT: + fprintf(fp, "%*s reduce %-7d ** Parsing conflict **", indent, + ap->sp->name, ap->x.rp->iRule); + break; + case SSCONFLICT: + fprintf(fp, "%*s shift %-7d ** Parsing conflict **", indent, + ap->sp->name, ap->x.stp->statenum); + break; + case SH_RESOLVED: + if (showPrecedenceConflict) { + fprintf(fp, "%*s shift %-7d -- dropped by precedence", indent, + ap->sp->name, ap->x.stp->statenum); + } else { + result = 0; + } + break; + case RD_RESOLVED: + if (showPrecedenceConflict) { + fprintf(fp, "%*s reduce %-7d -- dropped by precedence", indent, + ap->sp->name, ap->x.rp->iRule); + } else { + result = 0; + } + break; + case NOT_USED: + result = 0; + break; + } + if (result && ap->spOpt) { + fprintf(fp, " /* because %s==%s */", ap->sp->name, ap->spOpt->name); + } + return result; +} + +/* Generate the "*.out" log file */ +void ReportOutput(struct lemon *lemp) { + int i, n; + struct state *stp; + struct config *cfp; + struct action *ap; + struct rule *rp; + FILE *fp; + + fp = file_open(lemp, ".out", "wb"); + if (fp == 0) return; + for (i = 0; i < lemp->nxstate; i++) { + stp = lemp->sorted[i]; + fprintf(fp, "State %d:\n", stp->statenum); + if (lemp->basisflag) + cfp = stp->bp; + else + cfp = stp->cfp; + while (cfp) { + char buf[20]; + if (cfp->dot == cfp->rp->nrhs) { + lemon_sprintf(buf, "(%d)", cfp->rp->iRule); + fprintf(fp, " %5s ", buf); + } else { + fprintf(fp, " "); + } + ConfigPrint(fp, cfp); + fprintf(fp, "\n"); +#if 0 + SetPrint(fp,cfp->fws,lemp); + PlinkPrint(fp,cfp->fplp,"To "); + PlinkPrint(fp,cfp->bplp,"From"); +#endif + if (lemp->basisflag) + cfp = cfp->bp; + else + cfp = cfp->next; + } + fprintf(fp, "\n"); + for (ap = stp->ap; ap; ap = ap->next) { + if (PrintAction(ap, fp, 30)) fprintf(fp, "\n"); + } + fprintf(fp, "\n"); + } + fprintf(fp, "----------------------------------------------------\n"); + fprintf(fp, "Symbols:\n"); + fprintf(fp, "The first-set of non-terminals is shown after the name.\n\n"); + for (i = 0; i < lemp->nsymbol; i++) { + int j; + struct symbol *sp; + + sp = lemp->symbols[i]; + fprintf(fp, " %3d: %s", i, sp->name); + if (sp->type == NONTERMINAL) { + fprintf(fp, ":"); + if (sp->lambda) { + fprintf(fp, " "); + } + for (j = 0; j < lemp->nterminal; j++) { + if (sp->firstset && SetFind(sp->firstset, j)) { + fprintf(fp, " %s", lemp->symbols[j]->name); + } + } + } + if (sp->prec >= 0) fprintf(fp, " (precedence=%d)", sp->prec); + fprintf(fp, "\n"); + } + fprintf(fp, "----------------------------------------------------\n"); + fprintf(fp, "Syntax-only Symbols:\n"); + fprintf(fp, "The following symbols never carry semantic content.\n\n"); + for (i = n = 0; i < lemp->nsymbol; i++) { + int w; + struct symbol *sp = lemp->symbols[i]; + if (sp->bContent) continue; + w = (int)strlen(sp->name); + if (n > 0 && n + w > 75) { + fprintf(fp, "\n"); + n = 0; + } + if (n > 0) { + fprintf(fp, " "); + n++; + } + fprintf(fp, "%s", sp->name); + n += w; + } + if (n > 0) fprintf(fp, "\n"); + fprintf(fp, "----------------------------------------------------\n"); + fprintf(fp, "Rules:\n"); + for (rp = lemp->rule; rp; rp = rp->next) { + fprintf(fp, "%4d: ", rp->iRule); + rule_print(fp, rp); + fprintf(fp, "."); + if (rp->precsym) { + fprintf(fp, " [%s precedence=%d]", rp->precsym->name, rp->precsym->prec); + } + fprintf(fp, "\n"); + } + fclose(fp); + return; +} + +/* Search for the file "name" which is in the same directory as +** the exacutable */ +PRIVATE char *pathsearch(char *argv0, char *name, int modemask) { + const char *pathlist; + char *pathbufptr = 0; + char *pathbuf = 0; + char *path, *cp; + char c; + +#ifdef __WIN32__ + cp = strrchr(argv0, '\\'); +#else + cp = strrchr(argv0, '/'); +#endif + if (cp) { + c = *cp; + *cp = 0; + path = (char *)malloc(lemonStrlen(argv0) + lemonStrlen(name) + 2); + if (path) lemon_sprintf(path, "%s/%s", argv0, name); + *cp = c; + } else { + pathlist = getenv("PATH"); + if (pathlist == 0) pathlist = ".:/bin:/usr/bin"; + pathbuf = (char *)malloc(lemonStrlen(pathlist) + 1); + path = (char *)malloc(lemonStrlen(pathlist) + lemonStrlen(name) + 2); + if ((pathbuf != 0) && (path != 0)) { + pathbufptr = pathbuf; + lemon_strcpy(pathbuf, pathlist); + while (*pathbuf) { + cp = strchr(pathbuf, ':'); + if (cp == 0) cp = &pathbuf[lemonStrlen(pathbuf)]; + c = *cp; + *cp = 0; + lemon_sprintf(path, "%s/%s", pathbuf, name); + *cp = c; + if (c == 0) + pathbuf[0] = 0; + else + pathbuf = &cp[1]; + if (access(path, modemask) == 0) break; + } + } + free(pathbufptr); + } + return path; +} + +/* Given an action, compute the integer value for that action +** which is to be put in the action table of the generated machine. +** Return negative if no action should be generated. +*/ +PRIVATE int compute_action(struct lemon *lemp, struct action *ap) { + int act; + switch (ap->type) { + case SHIFT: + act = ap->x.stp->statenum; + break; + case SHIFTREDUCE: { + /* Since a SHIFT is inherient after a prior REDUCE, convert any + ** SHIFTREDUCE action with a nonterminal on the LHS into a simple + ** REDUCE action: */ + if (ap->sp->index >= lemp->nterminal) { + act = lemp->minReduce + ap->x.rp->iRule; + } else { + act = lemp->minShiftReduce + ap->x.rp->iRule; + } + break; + } + case REDUCE: + act = lemp->minReduce + ap->x.rp->iRule; + break; + case ERROR: + act = lemp->errAction; + break; + case ACCEPT: + act = lemp->accAction; + break; + default: + act = -1; + break; + } + return act; +} + +#define LINESIZE 1000 +/* The next cluster of routines are for reading the template file +** and writing the results to the generated parser */ +/* The first function transfers data from "in" to "out" until +** a line is seen which begins with "%%". The line number is +** tracked. +** +** if name!=0, then any word that begin with "Parse" is changed to +** begin with *name instead. +*/ +PRIVATE void tplt_xfer(char *name, FILE *in, FILE *out, int *lineno) { + int i, iStart; + char *line = gc(xmalloc(LINESIZE)); + while (fgets(line, LINESIZE, in) && (line[0] != '%' || line[1] != '%')) { + (*lineno)++; + iStart = 0; + if (name) { + for (i = 0; line[i]; i++) { + if (line[i] == 'P' && strncmp(&line[i], "Parse", 5) == 0 && + (i == 0 || !ISALPHA(line[i - 1]))) { + if (i > iStart) fprintf(out, "%.*s", i - iStart, &line[iStart]); + fprintf(out, "%s", name); + i += 4; + iStart = i + 1; + } + } + } + fprintf(out, "%s", &line[iStart]); + } +} + +/* Skip forward past the header of the template file to the first "%%" + */ +PRIVATE void tplt_skip_header(FILE *in, int *lineno) { + char *line = gc(xmalloc(LINESIZE)); + while (fgets(line, LINESIZE, in) && (line[0] != '%' || line[1] != '%')) { + (*lineno)++; + } +} + +/* The next function finds the template file and opens it, returning +** a pointer to the opened file. */ +PRIVATE FILE *tplt_open(struct lemon *lemp) { + static const char templatename[] = "third_party/lemon/lempar.c.txt"; + char *buf; + FILE *in; + char *tpltname; + char *toFree = 0; + char *cp; + + buf = gc(xmalloc(1000)); + + /* first, see if user specified a template filename on the command line. */ + if (user_templatename != 0) { + if (access(user_templatename, 004) == -1) { + fprintf(stderr, "Can't find the parser driver template file \"%s\".\n", + user_templatename); + lemp->errorcnt++; + return 0; + } + in = fopen(user_templatename, "rb"); + if (in == 0) { + fprintf(stderr, "Can't open the template file \"%s\".\n", + user_templatename); + lemp->errorcnt++; + return 0; + } + return in; + } + + cp = strrchr(lemp->filename, '.'); + if (cp) { + lemon_sprintf(buf, "%.*s.lt", (int)(cp - lemp->filename), lemp->filename); + } else { + lemon_sprintf(buf, "%s.lt", lemp->filename); + } + if (access(buf, 004) == 0) { + tpltname = buf; + } else if (access(templatename, 004) == 0) { + tpltname = templatename; + } else { + toFree = tpltname = pathsearch(lemp->argv0, templatename, 0); + } + if (tpltname == 0) { + fprintf(stderr, "Can't find the parser driver template file \"%s\".\n", + templatename); + lemp->errorcnt++; + return 0; + } + in = fopen(tpltname, "rb"); + if (in == 0) { + fprintf(stderr, "Can't open the template file \"%s\".\n", tpltname); + lemp->errorcnt++; + } + free(toFree); + return in; +} + +/* Print a #line directive line to the output file. */ +PRIVATE void tplt_linedir(FILE *out, int lineno, char *filename) { + fprintf(out, "#line %d \"", lineno); + while (*filename) { + if (*filename == '\\') putc('\\', out); + putc(*filename, out); + filename++; + } + fprintf(out, "\"\n"); +} + +/* Print a string to the file and keep the linenumber up to date */ +PRIVATE void tplt_print(FILE *out, struct lemon *lemp, char *str, int *lineno) { + if (str == 0) return; + while (*str) { + putc(*str, out); + if (*str == '\n') (*lineno)++; + str++; + } + if (str[-1] != '\n') { + putc('\n', out); + (*lineno)++; + } + if (!lemp->nolinenosflag) { + (*lineno)++; + tplt_linedir(out, *lineno, lemp->outname); + } + return; +} + +/* +** The following routine emits code for the destructor for the +** symbol sp +*/ +void emit_destructor_code(FILE *out, struct symbol *sp, struct lemon *lemp, + int *lineno) { + char *cp = 0; + + if (sp->type == TERMINAL) { + cp = lemp->tokendest; + if (cp == 0) return; + fprintf(out, "{\n"); + (*lineno)++; + } else if (sp->destructor) { + cp = sp->destructor; + fprintf(out, "{\n"); + (*lineno)++; + if (!lemp->nolinenosflag) { + (*lineno)++; + tplt_linedir(out, sp->destLineno, lemp->filename); + } + } else if (lemp->vardest) { + cp = lemp->vardest; + if (cp == 0) return; + fprintf(out, "{\n"); + (*lineno)++; + } else { + assert(0); /* Cannot happen */ + } + for (; *cp; cp++) { + if (*cp == '$' && cp[1] == '$') { + fprintf(out, "(yypminor->yy%d)", sp->dtnum); + cp++; + continue; + } + if (*cp == '\n') (*lineno)++; + fputc(*cp, out); + } + fprintf(out, "\n"); + (*lineno)++; + if (!lemp->nolinenosflag) { + (*lineno)++; + tplt_linedir(out, *lineno, lemp->outname); + } + fprintf(out, "}\n"); + (*lineno)++; + return; +} + +/* +** Return TRUE (non-zero) if the given symbol has a destructor. +*/ +int has_destructor(struct symbol *sp, struct lemon *lemp) { + int ret; + if (sp->type == TERMINAL) { + ret = lemp->tokendest != 0; + } else { + ret = lemp->vardest != 0 || sp->destructor != 0; + } + return ret; +} + +/* +** Append text to a dynamically allocated string. If zText is 0 then +** reset the string to be empty again. Always return the complete text +** of the string (which is overwritten with each call). +** +** n bytes of zText are stored. If n==0 then all of zText up to the first +** \000 terminator is stored. zText can contain up to two instances of +** %d. The values of p1 and p2 are written into the first and second +** %d. +** +** If n==-1, then the previous character is overwritten. +*/ +PRIVATE char *append_str(const char *zText, int n, int p1, int p2) { + static char empty[1] = {0}; + static char *z = 0; + static int alloced = 0; + static int used = 0; + int c; + char zInt[40]; + if (zText == 0) { + if (used == 0 && z != 0) z[0] = 0; + used = 0; + return z; + } + if (n <= 0) { + if (n < 0) { + used += n; + assert(used >= 0); + } + n = lemonStrlen(zText); + } + if ((int)(n + sizeof(zInt) * 2 + used) >= alloced) { + alloced = n + sizeof(zInt) * 2 + used + 200; + z = (char *)realloc(z, alloced); + } + if (z == 0) return empty; + while (n-- > 0) { + c = *(zText++); + if (c == '%' && n > 0 && zText[0] == 'd') { + lemon_sprintf(zInt, "%d", p1); + p1 = p2; + lemon_strcpy(&z[used], zInt); + used += lemonStrlen(&z[used]); + zText++; + n--; + } else { + z[used++] = (char)c; + } + } + z[used] = 0; + return z; +} + +/* +** Write and transform the rp->code string so that symbols are expanded. +** Populate the rp->codePrefix and rp->codeSuffix strings, as appropriate. +** +** Return 1 if the expanded code requires that "yylhsminor" local variable +** to be defined. +*/ +PRIVATE int translate_code(struct lemon *lemp, struct rule *rp) { + char *cp, *xp; + int i; + int rc = 0; /* True if yylhsminor is used */ + int dontUseRhs0 = 0; /* If true, use of left-most RHS label is illegal */ + const char *zSkip = 0; /* The zOvwrt comment within rp->code, or NULL */ + char lhsused = 0; /* True if the LHS element has been used */ + char lhsdirect; /* True if LHS writes directly into stack */ + char used[MAXRHS]; /* True for each RHS element which is used */ + char zLhs[50]; /* Convert the LHS symbol into this string */ + char zOvwrt[900]; /* Comment that to allow LHS to overwrite RHS */ + + for (i = 0; i < rp->nrhs; i++) used[i] = 0; + lhsused = 0; + + if (rp->code == 0) { + static const char newlinestr[2] = {'\n', '\0'}; + rp->code = newlinestr; + rp->line = rp->ruleline; + rp->noCode = 1; + } else { + rp->noCode = 0; + } + + if (rp->nrhs == 0) { + /* If there are no RHS symbols, then writing directly to the LHS is ok */ + lhsdirect = 1; + } else if (rp->rhsalias[0] == 0) { + /* The left-most RHS symbol has no value. LHS direct is ok. But + ** we have to call the distructor on the RHS symbol first. */ + lhsdirect = 1; + if (has_destructor(rp->rhs[0], lemp)) { + append_str(0, 0, 0, 0); + append_str(" yy_destructor(yypParser,%d,&yymsp[%d].minor);\n", 0, + rp->rhs[0]->index, 1 - rp->nrhs); + rp->codePrefix = Strsafe(append_str(0, 0, 0, 0)); + rp->noCode = 0; + } + } else if (rp->lhsalias == 0) { + /* There is no LHS value symbol. */ + lhsdirect = 1; + } else if (strcmp(rp->lhsalias, rp->rhsalias[0]) == 0) { + /* The LHS symbol and the left-most RHS symbol are the same, so + ** direct writing is allowed */ + lhsdirect = 1; + lhsused = 1; + used[0] = 1; + if (rp->lhs->dtnum != rp->rhs[0]->dtnum) { + ErrorMsg(lemp->filename, rp->ruleline, + "%s(%s) and %s(%s) share the same label but have " + "different datatypes.", + rp->lhs->name, rp->lhsalias, rp->rhs[0]->name, rp->rhsalias[0]); + lemp->errorcnt++; + } + } else { + lemon_sprintf(zOvwrt, "/*%s-overwrites-%s*/", rp->lhsalias, + rp->rhsalias[0]); + zSkip = strstr(rp->code, zOvwrt); + if (zSkip != 0) { + /* The code contains a special comment that indicates that it is safe + ** for the LHS label to overwrite left-most RHS label. */ + lhsdirect = 1; + } else { + lhsdirect = 0; + } + } + if (lhsdirect) { + sprintf(zLhs, "yymsp[%d].minor.yy%d", 1 - rp->nrhs, rp->lhs->dtnum); + } else { + rc = 1; + sprintf(zLhs, "yylhsminor.yy%d", rp->lhs->dtnum); + } + + append_str(0, 0, 0, 0); + + /* This const cast is wrong but harmless, if we're careful. */ + for (cp = (char *)rp->code; *cp; cp++) { + if (cp == zSkip) { + append_str(zOvwrt, 0, 0, 0); + cp += lemonStrlen(zOvwrt) - 1; + dontUseRhs0 = 1; + continue; + } + if (ISALPHA(*cp) && + (cp == rp->code || (!ISALNUM(cp[-1]) && cp[-1] != '_'))) { + char saved; + for (xp = &cp[1]; ISALNUM(*xp) || *xp == '_'; xp++) + ; + saved = *xp; + *xp = 0; + if (rp->lhsalias && strcmp(cp, rp->lhsalias) == 0) { + append_str(zLhs, 0, 0, 0); + cp = xp; + lhsused = 1; + } else { + for (i = 0; i < rp->nrhs; i++) { + if (rp->rhsalias[i] && strcmp(cp, rp->rhsalias[i]) == 0) { + if (i == 0 && dontUseRhs0) { + ErrorMsg(lemp->filename, rp->ruleline, + "Label %s used after '%s'.", rp->rhsalias[0], zOvwrt); + lemp->errorcnt++; + } else if (cp != rp->code && cp[-1] == '@') { + /* If the argument is of the form @X then substituted + ** the token number of X, not the value of X */ + append_str("yymsp[%d].major", -1, i - rp->nrhs + 1, 0); + } else { + struct symbol *sp = rp->rhs[i]; + int dtnum; + if (sp->type == MULTITERMINAL) { + dtnum = sp->subsym[0]->dtnum; + } else { + dtnum = sp->dtnum; + } + append_str("yymsp[%d].minor.yy%d", 0, i - rp->nrhs + 1, dtnum); + } + cp = xp; + used[i] = 1; + break; + } + } + } + *xp = saved; + } + append_str(cp, 1, 0, 0); + } /* End loop */ + + /* Main code generation completed */ + cp = append_str(0, 0, 0, 0); + if (cp && cp[0]) rp->code = Strsafe(cp); + append_str(0, 0, 0, 0); + + /* Check to make sure the LHS has been used */ + if (rp->lhsalias && !lhsused) { + ErrorMsg(lemp->filename, rp->ruleline, + "Label \"%s\" for \"%s(%s)\" is never used.", rp->lhsalias, + rp->lhs->name, rp->lhsalias); + lemp->errorcnt++; + } + + /* Generate destructor code for RHS minor values which are not referenced. + ** Generate error messages for unused labels and duplicate labels. + */ + for (i = 0; i < rp->nrhs; i++) { + if (rp->rhsalias[i]) { + if (i > 0) { + int j; + if (rp->lhsalias && strcmp(rp->lhsalias, rp->rhsalias[i]) == 0) { + ErrorMsg( + lemp->filename, rp->ruleline, + "%s(%s) has the same label as the LHS but is not the left-most " + "symbol on the RHS.", + rp->rhs[i]->name, rp->rhsalias[i]); + lemp->errorcnt++; + } + for (j = 0; j < i; j++) { + if (rp->rhsalias[j] && + strcmp(rp->rhsalias[j], rp->rhsalias[i]) == 0) { + ErrorMsg(lemp->filename, rp->ruleline, + "Label %s used for multiple symbols on the RHS of a rule.", + rp->rhsalias[i]); + lemp->errorcnt++; + break; + } + } + } + if (!used[i]) { + ErrorMsg(lemp->filename, rp->ruleline, + "Label %s for \"%s(%s)\" is never used.", rp->rhsalias[i], + rp->rhs[i]->name, rp->rhsalias[i]); + lemp->errorcnt++; + } + } else if (i > 0 && has_destructor(rp->rhs[i], lemp)) { + append_str(" yy_destructor(yypParser,%d,&yymsp[%d].minor);\n", 0, + rp->rhs[i]->index, i - rp->nrhs + 1); + } + } + + /* If unable to write LHS values directly into the stack, write the + ** saved LHS value now. */ + if (lhsdirect == 0) { + append_str(" yymsp[%d].minor.yy%d = ", 0, 1 - rp->nrhs, rp->lhs->dtnum); + append_str(zLhs, 0, 0, 0); + append_str(";\n", 0, 0, 0); + } + + /* Suffix code generation complete */ + cp = append_str(0, 0, 0, 0); + if (cp && cp[0]) { + rp->codeSuffix = Strsafe(cp); + rp->noCode = 0; + } + + return rc; +} + +/* +** Generate code which executes when the rule "rp" is reduced. Write +** the code to "out". Make sure lineno stays up-to-date. +*/ +PRIVATE void emit_code(FILE *out, struct rule *rp, struct lemon *lemp, + int *lineno) { + const char *cp; + + /* Setup code prior to the #line directive */ + if (rp->codePrefix && rp->codePrefix[0]) { + fprintf(out, "{%s", rp->codePrefix); + for (cp = rp->codePrefix; *cp; cp++) { + if (*cp == '\n') (*lineno)++; + } + } + + /* Generate code to do the reduce action */ + if (rp->code) { + if (!lemp->nolinenosflag) { + (*lineno)++; + tplt_linedir(out, rp->line, lemp->filename); + } + fprintf(out, "{%s", rp->code); + for (cp = rp->code; *cp; cp++) { + if (*cp == '\n') (*lineno)++; + } + fprintf(out, "}\n"); + (*lineno)++; + if (!lemp->nolinenosflag) { + (*lineno)++; + tplt_linedir(out, *lineno, lemp->outname); + } + } + + /* Generate breakdown code that occurs after the #line directive */ + if (rp->codeSuffix && rp->codeSuffix[0]) { + fprintf(out, "%s", rp->codeSuffix); + for (cp = rp->codeSuffix; *cp; cp++) { + if (*cp == '\n') (*lineno)++; + } + } + + if (rp->codePrefix) { + fprintf(out, "}\n"); + (*lineno)++; + } + + return; +} + +/* +** Print the definition of the union used for the parser's data stack. +** This union contains fields for every possible data type for tokens +** and nonterminals. In the process of computing and printing this +** union, also set the ".dtnum" field of every terminal and nonterminal +** symbol. +*/ +void print_stack_union( + FILE *out, /* The output stream */ + struct lemon *lemp, /* The main info structure for this parser */ + int *plineno, /* Pointer to the line number */ + int mhflag /* True if generating makeheaders output */ +) { + int lineno = *plineno; /* The line number of the output */ + char **types; /* A hash table of datatypes */ + int arraysize; /* Size of the "types" array */ + int maxdtlength; /* Maximum length of any ".datatype" field. */ + char *stddt; /* Standardized name for a datatype */ + int i, j; /* Loop counters */ + unsigned hash; /* For hashing the name of a type */ + const char *name; /* Name of the parser */ + + /* Allocate and initialize types[] and allocate stddt[] */ + arraysize = lemp->nsymbol * 2; + types = (char **)calloc(arraysize, sizeof(char *)); + if (types == 0) { + fprintf(stderr, "Out of memory.\n"); + exit(1); + } + for (i = 0; i < arraysize; i++) types[i] = 0; + maxdtlength = 0; + if (lemp->vartype) { + maxdtlength = lemonStrlen(lemp->vartype); + } + for (i = 0; i < lemp->nsymbol; i++) { + int len; + struct symbol *sp = lemp->symbols[i]; + if (sp->datatype == 0) continue; + len = lemonStrlen(sp->datatype); + if (len > maxdtlength) maxdtlength = len; + } + stddt = (char *)malloc(maxdtlength * 2 + 1); + if (stddt == 0) { + fprintf(stderr, "Out of memory.\n"); + exit(1); + } + + /* Build a hash table of datatypes. The ".dtnum" field of each symbol + ** is filled in with the hash index plus 1. A ".dtnum" value of 0 is + ** used for terminal symbols. If there is no %default_type defined then + ** 0 is also used as the .dtnum value for nonterminals which do not specify + ** a datatype using the %type directive. + */ + for (i = 0; i < lemp->nsymbol; i++) { + struct symbol *sp = lemp->symbols[i]; + char *cp; + if (sp == lemp->errsym) { + sp->dtnum = arraysize + 1; + continue; + } + if (sp->type != NONTERMINAL || (sp->datatype == 0 && lemp->vartype == 0)) { + sp->dtnum = 0; + continue; + } + cp = sp->datatype; + if (cp == 0) cp = lemp->vartype; + j = 0; + while (ISSPACE(*cp)) cp++; + while (*cp) stddt[j++] = *cp++; + while (j > 0 && ISSPACE(stddt[j - 1])) j--; + stddt[j] = 0; + if (lemp->tokentype && strcmp(stddt, lemp->tokentype) == 0) { + sp->dtnum = 0; + continue; + } + hash = 0; + for (j = 0; stddt[j]; j++) { + hash = hash * 53 + stddt[j]; + } + hash = (hash & 0x7fffffff) % arraysize; + while (types[hash]) { + if (strcmp(types[hash], stddt) == 0) { + sp->dtnum = hash + 1; + break; + } + hash++; + if (hash >= (unsigned)arraysize) hash = 0; + } + if (types[hash] == 0) { + sp->dtnum = hash + 1; + types[hash] = (char *)malloc(lemonStrlen(stddt) + 1); + if (types[hash] == 0) { + fprintf(stderr, "Out of memory.\n"); + exit(1); + } + lemon_strcpy(types[hash], stddt); + } + } + + /* Print out the definition of YYTOKENTYPE and YYMINORTYPE */ + name = lemp->name ? lemp->name : "Parse"; + lineno = *plineno; + if (mhflag) { + fprintf(out, "#if INTERFACE\n"); + lineno++; + } + fprintf(out, "#define %sTOKENTYPE %s\n", name, + lemp->tokentype ? lemp->tokentype : "void*"); + lineno++; + if (mhflag) { + fprintf(out, "#endif\n"); + lineno++; + } + fprintf(out, "typedef union {\n"); + lineno++; + fprintf(out, " int yyinit;\n"); + lineno++; + fprintf(out, " %sTOKENTYPE yy0;\n", name); + lineno++; + for (i = 0; i < arraysize; i++) { + if (types[i] == 0) continue; + fprintf(out, " %s yy%d;\n", types[i], i + 1); + lineno++; + free(types[i]); + } + if (lemp->errsym && lemp->errsym->useCnt) { + fprintf(out, " int yy%d;\n", lemp->errsym->dtnum); + lineno++; + } + free(stddt); + free(types); + fprintf(out, "} YYMINORTYPE;\n"); + lineno++; + *plineno = lineno; +} + +/* +** Return the name of a C datatype able to represent values between +** lwr and upr, inclusive. If pnByte!=NULL then also write the sizeof +** for that type (1, 2, or 4) into *pnByte. +*/ +static const char *minimum_size_type(int lwr, int upr, int *pnByte) { + const char *zType = "int"; + int nByte = 4; + if (lwr >= 0) { + if (upr <= 255) { + zType = "unsigned char"; + nByte = 1; + } else if (upr < 65535) { + zType = "unsigned short int"; + nByte = 2; + } else { + zType = "unsigned int"; + nByte = 4; + } + } else if (lwr >= -127 && upr <= 127) { + zType = "signed char"; + nByte = 1; + } else if (lwr >= -32767 && upr < 32767) { + zType = "short"; + nByte = 2; + } + if (pnByte) *pnByte = nByte; + return zType; +} + +/* +** Each state contains a set of token transaction and a set of +** nonterminal transactions. Each of these sets makes an instance +** of the following structure. An array of these structures is used +** to order the creation of entries in the yy_action[] table. +*/ +struct axset { + struct state *stp; /* A pointer to a state */ + int isTkn; /* True to use tokens. False for non-terminals */ + int nAction; /* Number of actions */ + int iOrder; /* Original order of action sets */ +}; + +/* +** Compare to axset structures for sorting purposes +*/ +static int axset_compare(const void *a, const void *b) { + struct axset *p1 = (struct axset *)a; + struct axset *p2 = (struct axset *)b; + int c; + c = p2->nAction - p1->nAction; + if (c == 0) { + c = p1->iOrder - p2->iOrder; + } + assert(c != 0 || p1 == p2); + return c; +} + +/* +** Write text on "out" that describes the rule "rp". +*/ +static void writeRuleText(FILE *out, struct rule *rp) { + int j; + fprintf(out, "%s ::=", rp->lhs->name); + for (j = 0; j < rp->nrhs; j++) { + struct symbol *sp = rp->rhs[j]; + if (sp->type != MULTITERMINAL) { + fprintf(out, " %s", sp->name); + } else { + int k; + fprintf(out, " %s", sp->subsym[0]->name); + for (k = 1; k < sp->nsubsym; k++) { + fprintf(out, "|%s", sp->subsym[k]->name); + } + } + } +} + +/* Generate C source code for the parser */ +void ReportTable(struct lemon *lemp, + int mhflag, /* Output in makeheaders format if true */ + int sqlFlag /* Generate the *.sql file too */ +) { + FILE *out, *in, *sql; + char *line = gc(xmalloc(LINESIZE)); + int lineno; + struct state *stp; + struct action *ap; + struct rule *rp; + struct acttab *pActtab; + int i, j, n, sz; + int nLookAhead; + int szActionType; /* sizeof(YYACTIONTYPE) */ + int szCodeType; /* sizeof(YYCODETYPE) */ + const char *name; + int mnTknOfst, mxTknOfst; + int mnNtOfst, mxNtOfst; + struct axset *ax; + char *prefix; + + lemp->minShiftReduce = lemp->nstate; + lemp->errAction = lemp->minShiftReduce + lemp->nrule; + lemp->accAction = lemp->errAction + 1; + lemp->noAction = lemp->accAction + 1; + lemp->minReduce = lemp->noAction + 1; + lemp->maxAction = lemp->minReduce + lemp->nrule; + + in = tplt_open(lemp); + if (in == 0) return; + out = file_open(lemp, ".c.inc", "wb"); + if (out == 0) { + fclose(in); + return; + } + if (sqlFlag == 0) { + sql = 0; + } else { + sql = file_open(lemp, ".sql", "wb"); + if (sql == 0) { + fclose(in); + fclose(out); + return; + } + fprintf(sql, "BEGIN;\n" + "CREATE TABLE symbol(\n" + " id INTEGER PRIMARY KEY,\n" + " name TEXT NOT NULL,\n" + " isTerminal BOOLEAN NOT NULL,\n" + " fallback INTEGER REFERENCES symbol" + " DEFERRABLE INITIALLY DEFERRED\n" + ");\n"); + for (i = 0; i < lemp->nsymbol; i++) { + fprintf(sql, + "INSERT INTO symbol(id,name,isTerminal,fallback)" + "VALUES(%d,'%s',%s", + i, lemp->symbols[i]->name, + i < lemp->nterminal ? "TRUE" : "FALSE"); + if (lemp->symbols[i]->fallback) { + fprintf(sql, ",%d);\n", lemp->symbols[i]->fallback->index); + } else { + fprintf(sql, ",NULL);\n"); + } + } + fprintf(sql, "CREATE TABLE rule(\n" + " ruleid INTEGER PRIMARY KEY,\n" + " lhs INTEGER REFERENCES symbol(id),\n" + " txt TEXT\n" + ");\n" + "CREATE TABLE rulerhs(\n" + " ruleid INTEGER REFERENCES rule(ruleid),\n" + " pos INTEGER,\n" + " sym INTEGER REFERENCES symbol(id)\n" + ");\n"); + for (i = 0, rp = lemp->rule; rp; rp = rp->next, i++) { + assert(i == rp->iRule); + fprintf(sql, "INSERT INTO rule(ruleid,lhs,txt)VALUES(%d,%d,'", rp->iRule, + rp->lhs->index); + writeRuleText(sql, rp); + fprintf(sql, "');\n"); + for (j = 0; j < rp->nrhs; j++) { + struct symbol *sp = rp->rhs[j]; + if (sp->type != MULTITERMINAL) { + fprintf(sql, "INSERT INTO rulerhs(ruleid,pos,sym)VALUES(%d,%d,%d);\n", + i, j, sp->index); + } else { + int k; + for (k = 0; k < sp->nsubsym; k++) { + fprintf(sql, + "INSERT INTO rulerhs(ruleid,pos,sym)VALUES(%d,%d,%d);\n", i, + j, sp->subsym[k]->index); + } + } + } + } + fprintf(sql, "COMMIT;\n"); + } + lineno = 1; + + fprintf( + out, + "/* This file is automatically generated by Lemon from input grammar\n" + "** source file \"%s\". */\n", + lemp->filename); + lineno += 2; + + /* The first %include directive begins with a C-language comment, + ** then skip over the header comment of the template file + */ + if (lemp->include == 0) lemp->include = ""; + for (i = 0; ISSPACE(lemp->include[i]); i++) { + if (lemp->include[i] == '\n') { + lemp->include += i + 1; + i = -1; + } + } + if (lemp->include[0] == '/') { + tplt_skip_header(in, &lineno); + } else { + tplt_xfer(lemp->name, in, out, &lineno); + } + + /* Generate the include code, if any */ + tplt_print(out, lemp, lemp->include, &lineno); + if (mhflag) { + char *incName = file_makename(lemp, ".h.inc"); + fprintf(out, "#include \"%s\"\n", incName); + lineno++; + free(incName); + } + tplt_xfer(lemp->name, in, out, &lineno); + + /* Generate #defines for all tokens */ + if (lemp->tokenprefix) + prefix = lemp->tokenprefix; + else + prefix = ""; + if (mhflag) { + fprintf(out, "#if INTERFACE\n"); + lineno++; + } else { + fprintf(out, "#ifndef %s%s\n", prefix, lemp->symbols[1]->name); + } + for (i = 1; i < lemp->nterminal; i++) { + fprintf(out, "#define %s%-30s %2d\n", prefix, lemp->symbols[i]->name, i); + lineno++; + } + fprintf(out, "#endif\n"); + lineno++; + tplt_xfer(lemp->name, in, out, &lineno); + + /* Generate the defines */ + fprintf(out, "#define YYCODETYPE %s\n", + minimum_size_type(0, lemp->nsymbol, &szCodeType)); + lineno++; + fprintf(out, "#define YYNOCODE %d\n", lemp->nsymbol); + lineno++; + fprintf(out, "#define YYACTIONTYPE %s\n", + minimum_size_type(0, lemp->maxAction, &szActionType)); + lineno++; + if (lemp->wildcard) { + fprintf(out, "#define YYWILDCARD %d\n", lemp->wildcard->index); + lineno++; + } + print_stack_union(out, lemp, &lineno, mhflag); + fprintf(out, "#ifndef YYSTACKDEPTH\n"); + lineno++; + if (lemp->stacksize) { + fprintf(out, "#define YYSTACKDEPTH %s\n", lemp->stacksize); + lineno++; + } else { + fprintf(out, "#define YYSTACKDEPTH 100\n"); + lineno++; + } + fprintf(out, "#endif\n"); + lineno++; + if (mhflag) { + fprintf(out, "#if INTERFACE\n"); + lineno++; + } + name = lemp->name ? lemp->name : "Parse"; + if (lemp->arg && lemp->arg[0]) { + i = lemonStrlen(lemp->arg); + while (i >= 1 && ISSPACE(lemp->arg[i - 1])) i--; + while (i >= 1 && (ISALNUM(lemp->arg[i - 1]) || lemp->arg[i - 1] == '_')) + i--; + fprintf(out, "#define %sARG_SDECL %s;\n", name, lemp->arg); + lineno++; + fprintf(out, "#define %sARG_PDECL ,%s\n", name, lemp->arg); + lineno++; + fprintf(out, "#define %sARG_PARAM ,%s\n", name, &lemp->arg[i]); + lineno++; + fprintf(out, "#define %sARG_FETCH %s=yypParser->%s;\n", name, lemp->arg, + &lemp->arg[i]); + lineno++; + fprintf(out, "#define %sARG_STORE yypParser->%s=%s;\n", name, &lemp->arg[i], + &lemp->arg[i]); + lineno++; + } else { + fprintf(out, "#define %sARG_SDECL\n", name); + lineno++; + fprintf(out, "#define %sARG_PDECL\n", name); + lineno++; + fprintf(out, "#define %sARG_PARAM\n", name); + lineno++; + fprintf(out, "#define %sARG_FETCH\n", name); + lineno++; + fprintf(out, "#define %sARG_STORE\n", name); + lineno++; + } + if (lemp->ctx && lemp->ctx[0]) { + i = lemonStrlen(lemp->ctx); + while (i >= 1 && ISSPACE(lemp->ctx[i - 1])) i--; + while (i >= 1 && (ISALNUM(lemp->ctx[i - 1]) || lemp->ctx[i - 1] == '_')) + i--; + fprintf(out, "#define %sCTX_SDECL %s;\n", name, lemp->ctx); + lineno++; + fprintf(out, "#define %sCTX_PDECL ,%s\n", name, lemp->ctx); + lineno++; + fprintf(out, "#define %sCTX_PARAM ,%s\n", name, &lemp->ctx[i]); + lineno++; + fprintf(out, "#define %sCTX_FETCH %s=yypParser->%s;\n", name, lemp->ctx, + &lemp->ctx[i]); + lineno++; + fprintf(out, "#define %sCTX_STORE yypParser->%s=%s;\n", name, &lemp->ctx[i], + &lemp->ctx[i]); + lineno++; + } else { + fprintf(out, "#define %sCTX_SDECL\n", name); + lineno++; + fprintf(out, "#define %sCTX_PDECL\n", name); + lineno++; + fprintf(out, "#define %sCTX_PARAM\n", name); + lineno++; + fprintf(out, "#define %sCTX_FETCH\n", name); + lineno++; + fprintf(out, "#define %sCTX_STORE\n", name); + lineno++; + } + if (mhflag) { + fprintf(out, "#endif\n"); + lineno++; + } + if (lemp->errsym && lemp->errsym->useCnt) { + fprintf(out, "#define YYERRORSYMBOL %d\n", lemp->errsym->index); + lineno++; + fprintf(out, "#define YYERRSYMDT yy%d\n", lemp->errsym->dtnum); + lineno++; + } + if (lemp->has_fallback) { + fprintf(out, "#define YYFALLBACK 1\n"); + lineno++; + } + + /* Compute the action table, but do not output it yet. The action + ** table must be computed before generating the YYNSTATE macro because + ** we need to know how many states can be eliminated. + */ + ax = (struct axset *)calloc(lemp->nxstate * 2, sizeof(ax[0])); + if (ax == 0) { + fprintf(stderr, "malloc failed\n"); + exit(1); + } + for (i = 0; i < lemp->nxstate; i++) { + stp = lemp->sorted[i]; + ax[i * 2].stp = stp; + ax[i * 2].isTkn = 1; + ax[i * 2].nAction = stp->nTknAct; + ax[i * 2 + 1].stp = stp; + ax[i * 2 + 1].isTkn = 0; + ax[i * 2 + 1].nAction = stp->nNtAct; + } + mxTknOfst = mnTknOfst = 0; + mxNtOfst = mnNtOfst = 0; + /* In an effort to minimize the action table size, use the heuristic + ** of placing the largest action sets first */ + for (i = 0; i < lemp->nxstate * 2; i++) ax[i].iOrder = i; + qsort(ax, lemp->nxstate * 2, sizeof(ax[0]), axset_compare); + pActtab = acttab_alloc(lemp->nsymbol, lemp->nterminal); + for (i = 0; i < lemp->nxstate * 2 && ax[i].nAction > 0; i++) { + stp = ax[i].stp; + if (ax[i].isTkn) { + for (ap = stp->ap; ap; ap = ap->next) { + int action; + if (ap->sp->index >= lemp->nterminal) continue; + action = compute_action(lemp, ap); + if (action < 0) continue; + acttab_action(pActtab, ap->sp->index, action); + } + stp->iTknOfst = acttab_insert(pActtab, 1); + if (stp->iTknOfst < mnTknOfst) mnTknOfst = stp->iTknOfst; + if (stp->iTknOfst > mxTknOfst) mxTknOfst = stp->iTknOfst; + } else { + for (ap = stp->ap; ap; ap = ap->next) { + int action; + if (ap->sp->index < lemp->nterminal) continue; + if (ap->sp->index == lemp->nsymbol) continue; + action = compute_action(lemp, ap); + if (action < 0) continue; + acttab_action(pActtab, ap->sp->index, action); + } + stp->iNtOfst = acttab_insert(pActtab, 0); + if (stp->iNtOfst < mnNtOfst) mnNtOfst = stp->iNtOfst; + if (stp->iNtOfst > mxNtOfst) mxNtOfst = stp->iNtOfst; + } +#if 0 /* Uncomment for a trace of how the yy_action[] table fills out */ + { int jj, nn; + for(jj=nn=0; jjnAction; jj++){ + if( pActtab->aAction[jj].action<0 ) nn++; + } + printf("%4d: State %3d %s n: %2d size: %5d freespace: %d\n", + i, stp->statenum, ax[i].isTkn ? "Token" : "Var ", + ax[i].nAction, pActtab->nAction, nn); + } +#endif + } + free(ax); + + /* Mark rules that are actually used for reduce actions after all + ** optimizations have been applied + */ + for (rp = lemp->rule; rp; rp = rp->next) rp->doesReduce = LEMON_FALSE; + for (i = 0; i < lemp->nxstate; i++) { + for (ap = lemp->sorted[i]->ap; ap; ap = ap->next) { + if (ap->type == REDUCE || ap->type == SHIFTREDUCE) { + ap->x.rp->doesReduce = 1; + } + } + } + + /* Finish rendering the constants now that the action table has + ** been computed */ + fprintf(out, "#define YYNSTATE %d\n", lemp->nxstate); + lineno++; + fprintf(out, "#define YYNRULE %d\n", lemp->nrule); + lineno++; + fprintf(out, "#define YYNRULE_WITH_ACTION %d\n", lemp->nruleWithAction); + lineno++; + fprintf(out, "#define YYNTOKEN %d\n", lemp->nterminal); + lineno++; + fprintf(out, "#define YY_MAX_SHIFT %d\n", lemp->nxstate - 1); + lineno++; + i = lemp->minShiftReduce; + fprintf(out, "#define YY_MIN_SHIFTREDUCE %d\n", i); + lineno++; + i += lemp->nrule; + fprintf(out, "#define YY_MAX_SHIFTREDUCE %d\n", i - 1); + lineno++; + fprintf(out, "#define YY_ERROR_ACTION %d\n", lemp->errAction); + lineno++; + fprintf(out, "#define YY_ACCEPT_ACTION %d\n", lemp->accAction); + lineno++; + fprintf(out, "#define YY_NO_ACTION %d\n", lemp->noAction); + lineno++; + fprintf(out, "#define YY_MIN_REDUCE %d\n", lemp->minReduce); + lineno++; + i = lemp->minReduce + lemp->nrule; + fprintf(out, "#define YY_MAX_REDUCE %d\n", i - 1); + lineno++; + tplt_xfer(lemp->name, in, out, &lineno); + + /* Now output the action table and its associates: + ** + ** yy_action[] A single table containing all actions. + ** yy_lookahead[] A table containing the lookahead for each entry in + ** yy_action. Used to detect hash collisions. + ** yy_shift_ofst[] For each state, the offset into yy_action for + ** shifting terminals. + ** yy_reduce_ofst[] For each state, the offset into yy_action for + ** shifting non-terminals after a reduce. + ** yy_default[] Default action for each state. + */ + + /* Output the yy_action table */ + lemp->nactiontab = n = acttab_action_size(pActtab); + lemp->tablesize += n * szActionType; + fprintf(out, "#define YY_ACTTAB_COUNT (%d)\n", n); + lineno++; + fprintf(out, "static const YYACTIONTYPE yy_action[] = {\n"); + lineno++; + for (i = j = 0; i < n; i++) { + int action = acttab_yyaction(pActtab, i); + if (action < 0) action = lemp->noAction; + if (j == 0) fprintf(out, " /* %5d */ ", i); + fprintf(out, " %4d,", action); + if (j == 9 || i == n - 1) { + fprintf(out, "\n"); + lineno++; + j = 0; + } else { + j++; + } + } + fprintf(out, "};\n"); + lineno++; + + /* Output the yy_lookahead table */ + lemp->nlookaheadtab = n = acttab_lookahead_size(pActtab); + lemp->tablesize += n * szCodeType; + fprintf(out, "static const YYCODETYPE yy_lookahead[] = {\n"); + lineno++; + for (i = j = 0; i < n; i++) { + int la = acttab_yylookahead(pActtab, i); + if (la < 0) la = lemp->nsymbol; + if (j == 0) fprintf(out, " /* %5d */ ", i); + fprintf(out, " %4d,", la); + if (j == 9) { + fprintf(out, "\n"); + lineno++; + j = 0; + } else { + j++; + } + } + /* Add extra entries to the end of the yy_lookahead[] table so that + ** yy_shift_ofst[]+iToken will always be a valid index into the array, + ** even for the largest possible value of yy_shift_ofst[] and iToken. */ + nLookAhead = lemp->nterminal + lemp->nactiontab; + while (i < nLookAhead) { + if (j == 0) fprintf(out, " /* %5d */ ", i); + fprintf(out, " %4d,", lemp->nterminal); + if (j == 9) { + fprintf(out, "\n"); + lineno++; + j = 0; + } else { + j++; + } + i++; + } + if (j > 0) { + fprintf(out, "\n"); + lineno++; + } + fprintf(out, "};\n"); + lineno++; + + /* Output the yy_shift_ofst[] table */ + n = lemp->nxstate; + while (n > 0 && lemp->sorted[n - 1]->iTknOfst == NO_OFFSET) n--; + fprintf(out, "#define YY_SHIFT_COUNT (%d)\n", n - 1); + lineno++; + fprintf(out, "#define YY_SHIFT_MIN (%d)\n", mnTknOfst); + lineno++; + fprintf(out, "#define YY_SHIFT_MAX (%d)\n", mxTknOfst); + lineno++; + fprintf( + out, "static const %s yy_shift_ofst[] = {\n", + minimum_size_type(mnTknOfst, lemp->nterminal + lemp->nactiontab, &sz)); + lineno++; + lemp->tablesize += n * sz; + for (i = j = 0; i < n; i++) { + int ofst; + stp = lemp->sorted[i]; + ofst = stp->iTknOfst; + if (ofst == NO_OFFSET) ofst = lemp->nactiontab; + if (j == 0) fprintf(out, " /* %5d */ ", i); + fprintf(out, " %4d,", ofst); + if (j == 9 || i == n - 1) { + fprintf(out, "\n"); + lineno++; + j = 0; + } else { + j++; + } + } + fprintf(out, "};\n"); + lineno++; + + /* Output the yy_reduce_ofst[] table */ + n = lemp->nxstate; + while (n > 0 && lemp->sorted[n - 1]->iNtOfst == NO_OFFSET) n--; + fprintf(out, "#define YY_REDUCE_COUNT (%d)\n", n - 1); + lineno++; + fprintf(out, "#define YY_REDUCE_MIN (%d)\n", mnNtOfst); + lineno++; + fprintf(out, "#define YY_REDUCE_MAX (%d)\n", mxNtOfst); + lineno++; + fprintf(out, "static const %s yy_reduce_ofst[] = {\n", + minimum_size_type(mnNtOfst - 1, mxNtOfst, &sz)); + lineno++; + lemp->tablesize += n * sz; + for (i = j = 0; i < n; i++) { + int ofst; + stp = lemp->sorted[i]; + ofst = stp->iNtOfst; + if (ofst == NO_OFFSET) ofst = mnNtOfst - 1; + if (j == 0) fprintf(out, " /* %5d */ ", i); + fprintf(out, " %4d,", ofst); + if (j == 9 || i == n - 1) { + fprintf(out, "\n"); + lineno++; + j = 0; + } else { + j++; + } + } + fprintf(out, "};\n"); + lineno++; + + /* Output the default action table */ + fprintf(out, "static const YYACTIONTYPE yy_default[] = {\n"); + lineno++; + n = lemp->nxstate; + lemp->tablesize += n * szActionType; + for (i = j = 0; i < n; i++) { + stp = lemp->sorted[i]; + if (j == 0) fprintf(out, " /* %5d */ ", i); + if (stp->iDfltReduce < 0) { + fprintf(out, " %4d,", lemp->errAction); + } else { + fprintf(out, " %4d,", stp->iDfltReduce + lemp->minReduce); + } + if (j == 9 || i == n - 1) { + fprintf(out, "\n"); + lineno++; + j = 0; + } else { + j++; + } + } + fprintf(out, "};\n"); + lineno++; + tplt_xfer(lemp->name, in, out, &lineno); + + /* Generate the table of fallback tokens. + */ + if (lemp->has_fallback) { + int mx = lemp->nterminal - 1; + /* 2019-08-28: Generate fallback entries for every token to avoid + ** having to do a range check on the index */ + /* while( mx>0 && lemp->symbols[mx]->fallback==0 ){ mx--; } */ + lemp->tablesize += (mx + 1) * szCodeType; + for (i = 0; i <= mx; i++) { + struct symbol *p = lemp->symbols[i]; + if (p->fallback == 0) { + fprintf(out, " 0, /* %10s => nothing */\n", p->name); + } else { + fprintf(out, " %3d, /* %10s => %s */\n", p->fallback->index, p->name, + p->fallback->name); + } + lineno++; + } + } + tplt_xfer(lemp->name, in, out, &lineno); + + /* Generate a table containing the symbolic name of every symbol + */ + for (i = 0; i < lemp->nsymbol; i++) { + lemon_sprintf(line, "\"%s\",", lemp->symbols[i]->name); + fprintf(out, " /* %4d */ \"%s\",\n", i, lemp->symbols[i]->name); + lineno++; + } + tplt_xfer(lemp->name, in, out, &lineno); + + /* Generate a table containing a text string that describes every + ** rule in the rule set of the grammar. This information is used + ** when tracing REDUCE actions. + */ + for (i = 0, rp = lemp->rule; rp; rp = rp->next, i++) { + assert(rp->iRule == i); + fprintf(out, " /* %3d */ \"", i); + writeRuleText(out, rp); + fprintf(out, "\",\n"); + lineno++; + } + tplt_xfer(lemp->name, in, out, &lineno); + + /* Generate code which executes every time a symbol is popped from + ** the stack while processing errors or while destroying the parser. + ** (In other words, generate the %destructor actions) + */ + if (lemp->tokendest) { + int once = 1; + for (i = 0; i < lemp->nsymbol; i++) { + struct symbol *sp = lemp->symbols[i]; + if (sp == 0 || sp->type != TERMINAL) continue; + if (once) { + fprintf(out, " /* TERMINAL Destructor */\n"); + lineno++; + once = 0; + } + fprintf(out, " case %d: /* %s */\n", sp->index, sp->name); + lineno++; + } + for (i = 0; i < lemp->nsymbol && lemp->symbols[i]->type != TERMINAL; i++) + ; + if (i < lemp->nsymbol) { + emit_destructor_code(out, lemp->symbols[i], lemp, &lineno); + fprintf(out, " break;\n"); + lineno++; + } + } + if (lemp->vardest) { + struct symbol *dflt_sp = 0; + int once = 1; + for (i = 0; i < lemp->nsymbol; i++) { + struct symbol *sp = lemp->symbols[i]; + if (sp == 0 || sp->type == TERMINAL || sp->index <= 0 || + sp->destructor != 0) + continue; + if (once) { + fprintf(out, " /* Default NON-TERMINAL Destructor */\n"); + lineno++; + once = 0; + } + fprintf(out, " case %d: /* %s */\n", sp->index, sp->name); + lineno++; + dflt_sp = sp; + } + if (dflt_sp != 0) { + emit_destructor_code(out, dflt_sp, lemp, &lineno); + } + fprintf(out, " break;\n"); + lineno++; + } + for (i = 0; i < lemp->nsymbol; i++) { + struct symbol *sp = lemp->symbols[i]; + if (sp == 0 || sp->type == TERMINAL || sp->destructor == 0) continue; + if (sp->destLineno < 0) continue; /* Already emitted */ + fprintf(out, " case %d: /* %s */\n", sp->index, sp->name); + lineno++; + + /* Combine duplicate destructors into a single case */ + for (j = i + 1; j < lemp->nsymbol; j++) { + struct symbol *sp2 = lemp->symbols[j]; + if (sp2 && sp2->type != TERMINAL && sp2->destructor && + sp2->dtnum == sp->dtnum && + strcmp(sp->destructor, sp2->destructor) == 0) { + fprintf(out, " case %d: /* %s */\n", sp2->index, sp2->name); + lineno++; + sp2->destLineno = -1; /* Avoid emitting this destructor again */ + } + } + + emit_destructor_code(out, lemp->symbols[i], lemp, &lineno); + fprintf(out, " break;\n"); + lineno++; + } + tplt_xfer(lemp->name, in, out, &lineno); + + /* Generate code which executes whenever the parser stack overflows */ + tplt_print(out, lemp, lemp->overflow, &lineno); + tplt_xfer(lemp->name, in, out, &lineno); + + /* Generate the tables of rule information. yyRuleInfoLhs[] and + ** yyRuleInfoNRhs[]. + ** + ** Note: This code depends on the fact that rules are number + ** sequentually beginning with 0. + */ + for (i = 0, rp = lemp->rule; rp; rp = rp->next, i++) { + fprintf(out, " %4d, /* (%d) ", rp->lhs->index, i); + rule_print(out, rp); + fprintf(out, " */\n"); + lineno++; + } + tplt_xfer(lemp->name, in, out, &lineno); + for (i = 0, rp = lemp->rule; rp; rp = rp->next, i++) { + fprintf(out, " %3d, /* (%d) ", -rp->nrhs, i); + rule_print(out, rp); + fprintf(out, " */\n"); + lineno++; + } + tplt_xfer(lemp->name, in, out, &lineno); + + /* Generate code which execution during each REDUCE action */ + i = 0; + for (rp = lemp->rule; rp; rp = rp->next) { + i += translate_code(lemp, rp); + } + if (i) { + fprintf(out, " YYMINORTYPE yylhsminor;\n"); + lineno++; + } + /* First output rules other than the default: rule */ + for (rp = lemp->rule; rp; rp = rp->next) { + struct rule *rp2; /* Other rules with the same action */ + if (rp->codeEmitted) continue; + if (rp->noCode) { + /* No C code actions, so this will be part of the "default:" rule */ + continue; + } + fprintf(out, " case %d: /* ", rp->iRule); + writeRuleText(out, rp); + fprintf(out, " */\n"); + lineno++; + for (rp2 = rp->next; rp2; rp2 = rp2->next) { + if (rp2->code == rp->code && rp2->codePrefix == rp->codePrefix && + rp2->codeSuffix == rp->codeSuffix) { + fprintf(out, " case %d: /* ", rp2->iRule); + writeRuleText(out, rp2); + fprintf(out, " */ yytestcase(yyruleno==%d);\n", rp2->iRule); + lineno++; + rp2->codeEmitted = 1; + } + } + emit_code(out, rp, lemp, &lineno); + fprintf(out, " break;\n"); + lineno++; + rp->codeEmitted = 1; + } + /* Finally, output the default: rule. We choose as the default: all + ** empty actions. */ + fprintf(out, " default:\n"); + lineno++; + for (rp = lemp->rule; rp; rp = rp->next) { + if (rp->codeEmitted) continue; + assert(rp->noCode); + fprintf(out, " /* (%d) ", rp->iRule); + writeRuleText(out, rp); + if (rp->neverReduce) { + fprintf(out, " (NEVER REDUCES) */ assert(yyruleno!=%d);\n", rp->iRule); + lineno++; + } else if (rp->doesReduce) { + fprintf(out, " */ yytestcase(yyruleno==%d);\n", rp->iRule); + lineno++; + } else { + fprintf(out, " (OPTIMIZED OUT) */ assert(yyruleno!=%d);\n", rp->iRule); + lineno++; + } + } + fprintf(out, " break;\n"); + lineno++; + tplt_xfer(lemp->name, in, out, &lineno); + + /* Generate code which executes if a parse fails */ + tplt_print(out, lemp, lemp->failure, &lineno); + tplt_xfer(lemp->name, in, out, &lineno); + + /* Generate code which executes when a syntax error occurs */ + tplt_print(out, lemp, lemp->error, &lineno); + tplt_xfer(lemp->name, in, out, &lineno); + + /* Generate code which executes when the parser accepts its input */ + tplt_print(out, lemp, lemp->accept, &lineno); + tplt_xfer(lemp->name, in, out, &lineno); + + /* Append any addition code the user desires */ + tplt_print(out, lemp, lemp->extracode, &lineno); + + acttab_free(pActtab); + fclose(in); + fclose(out); + if (sql) fclose(sql); + return; +} + +/* Generate a header file for the parser */ +void ReportHeader(struct lemon *lemp) { + FILE *out, *in; + const char *prefix; + char *line = gc(xmalloc(LINESIZE)); + char *pattern = gc(xmalloc(LINESIZE)); + int i; + + if (lemp->tokenprefix) + prefix = lemp->tokenprefix; + else + prefix = ""; + in = file_open(lemp, ".h.inc", "rb"); + if (in) { + int nextChar; + for (i = 1; i < lemp->nterminal && fgets(line, LINESIZE, in); i++) { + lemon_sprintf(pattern, "#define %s%-30s %3d\n", prefix, + lemp->symbols[i]->name, i); + if (strcmp(line, pattern)) break; + } + nextChar = fgetc(in); + fclose(in); + if (i == lemp->nterminal && nextChar == EOF) { + /* No change in the file. Don't rewrite it. */ + return; + } + } + out = file_open(lemp, ".h.inc", "wb"); + if (out) { + for (i = 1; i < lemp->nterminal; i++) { + fprintf(out, "#define %s%-30s %3d\n", prefix, lemp->symbols[i]->name, i); + } + fclose(out); + } + return; +} + +/* Reduce the size of the action tables, if possible, by making use +** of defaults. +** +** In this version, we take the most frequent REDUCE action and make +** it the default. Except, there is no default if the wildcard token +** is a possible look-ahead. +*/ +void CompressTables(struct lemon *lemp) { + struct state *stp; + struct action *ap, *ap2, *nextap; + struct rule *rp, *rp2, *rbest; + int nbest, n; + int i; + int usesWildcard; + + for (i = 0; i < lemp->nstate; i++) { + stp = lemp->sorted[i]; + nbest = 0; + rbest = 0; + usesWildcard = 0; + + for (ap = stp->ap; ap; ap = ap->next) { + if (ap->type == SHIFT && ap->sp == lemp->wildcard) { + usesWildcard = 1; + } + if (ap->type != REDUCE) continue; + rp = ap->x.rp; + if (rp->lhsStart) continue; + if (rp == rbest) continue; + n = 1; + for (ap2 = ap->next; ap2; ap2 = ap2->next) { + if (ap2->type != REDUCE) continue; + rp2 = ap2->x.rp; + if (rp2 == rbest) continue; + if (rp2 == rp) n++; + } + if (n > nbest) { + nbest = n; + rbest = rp; + } + } + + /* Do not make a default if the number of rules to default + ** is not at least 1 or if the wildcard token is a possible + ** lookahead. + */ + if (nbest < 1 || usesWildcard) continue; + + /* Combine matching REDUCE actions into a single default */ + for (ap = stp->ap; ap; ap = ap->next) { + if (ap->type == REDUCE && ap->x.rp == rbest) break; + } + assert(ap); + ap->sp = Symbol_new("{default}"); + for (ap = ap->next; ap; ap = ap->next) { + if (ap->type == REDUCE && ap->x.rp == rbest) ap->type = NOT_USED; + } + stp->ap = Action_sort(stp->ap); + + for (ap = stp->ap; ap; ap = ap->next) { + if (ap->type == SHIFT) break; + if (ap->type == REDUCE && ap->x.rp != rbest) break; + } + if (ap == 0) { + stp->autoReduce = 1; + stp->pDfltReduce = rbest; + } + } + + /* Make a second pass over all states and actions. Convert + ** every action that is a SHIFT to an autoReduce state into + ** a SHIFTREDUCE action. + */ + for (i = 0; i < lemp->nstate; i++) { + stp = lemp->sorted[i]; + for (ap = stp->ap; ap; ap = ap->next) { + struct state *pNextState; + if (ap->type != SHIFT) continue; + pNextState = ap->x.stp; + if (pNextState->autoReduce && pNextState->pDfltReduce != 0) { + ap->type = SHIFTREDUCE; + ap->x.rp = pNextState->pDfltReduce; + } + } + } + + /* If a SHIFTREDUCE action specifies a rule that has a single RHS term + ** (meaning that the SHIFTREDUCE will land back in the state where it + ** started) and if there is no C-code associated with the reduce action, + ** then we can go ahead and convert the action to be the same as the + ** action for the RHS of the rule. + */ + for (i = 0; i < lemp->nstate; i++) { + stp = lemp->sorted[i]; + for (ap = stp->ap; ap; ap = nextap) { + nextap = ap->next; + if (ap->type != SHIFTREDUCE) continue; + rp = ap->x.rp; + if (rp->noCode == 0) continue; + if (rp->nrhs != 1) continue; +#if 1 + /* Only apply this optimization to non-terminals. It would be OK to + ** apply it to terminal symbols too, but that makes the parser tables + ** larger. */ + if (ap->sp->index < lemp->nterminal) continue; +#endif + /* If we reach this point, it means the optimization can be applied */ + nextap = ap; + for (ap2 = stp->ap; ap2 && (ap2 == ap || ap2->sp != rp->lhs); + ap2 = ap2->next) { + } + assert(ap2 != 0); + ap->spOpt = ap2->sp; + ap->type = ap2->type; + ap->x = ap2->x; + } + } +} + +/* +** Compare two states for sorting purposes. The smaller state is the +** one with the most non-terminal actions. If they have the same number +** of non-terminal actions, then the smaller is the one with the most +** token actions. +*/ +static int stateResortCompare(const void *a, const void *b) { + const struct state *pA = *(const struct state **)a; + const struct state *pB = *(const struct state **)b; + int n; + + n = pB->nNtAct - pA->nNtAct; + if (n == 0) { + n = pB->nTknAct - pA->nTknAct; + if (n == 0) { + n = pB->statenum - pA->statenum; + } + } + assert(n != 0); + return n; +} + +/* +** Renumber and resort states so that states with fewer choices +** occur at the end. Except, keep state 0 as the first state. +*/ +void ResortStates(struct lemon *lemp) { + int i; + struct state *stp; + struct action *ap; + + for (i = 0; i < lemp->nstate; i++) { + stp = lemp->sorted[i]; + stp->nTknAct = stp->nNtAct = 0; + stp->iDfltReduce = -1; /* Init dflt action to "syntax error" */ + stp->iTknOfst = NO_OFFSET; + stp->iNtOfst = NO_OFFSET; + for (ap = stp->ap; ap; ap = ap->next) { + int iAction = compute_action(lemp, ap); + if (iAction >= 0) { + if (ap->sp->index < lemp->nterminal) { + stp->nTknAct++; + } else if (ap->sp->index < lemp->nsymbol) { + stp->nNtAct++; + } else { + assert(stp->autoReduce == 0 || stp->pDfltReduce == ap->x.rp); + stp->iDfltReduce = iAction; + } + } + } + } + qsort(&lemp->sorted[1], lemp->nstate - 1, sizeof(lemp->sorted[0]), + stateResortCompare); + for (i = 0; i < lemp->nstate; i++) { + lemp->sorted[i]->statenum = i; + } + lemp->nxstate = lemp->nstate; + while (lemp->nxstate > 1 && lemp->sorted[lemp->nxstate - 1]->autoReduce) { + lemp->nxstate--; + } +} + +/***************** From the file "set.c" ************************************/ +/* +** Set manipulation routines for the LEMON parser generator. +*/ + +static int size = 0; + +/* Set the set size */ +void SetSize(int n) { + size = n + 1; +} + +/* Allocate a new set */ +char *SetNew(void) { + char *s; + s = (char *)calloc(size, 1); + if (s == 0) { + memory_error(); + } + return s; +} + +/* Deallocate a set */ +void SetFree(char *s) { + free(s); +} + +/* Add a new element to the set. Return TRUE if the element was added +** and FALSE if it was already there. */ +int SetAdd(char *s, int e) { + int rv; + assert(e >= 0 && e < size); + rv = s[e]; + s[e] = 1; + return !rv; +} + +/* Add every element of s2 to s1. Return TRUE if s1 changes. */ +int SetUnion(char *s1, char *s2) { + int i, progress; + progress = 0; + for (i = 0; i < size; i++) { + if (s2[i] == 0) continue; + if (s1[i] == 0) { + progress = 1; + s1[i] = 1; + } + } + return progress; +} + +/********************** From the file "table.c" ****************************/ +/* +** All code in this file has been automatically generated +** from a specification in the file +** "table.q" +** by the associative array code building program "aagen". +** Do not edit this file! Instead, edit the specification +** file, then rerun aagen. +*/ +/* +** Code for processing tables in the LEMON parser generator. +*/ + +PRIVATE unsigned strhash(const char *x) { + unsigned h = 0; + while (*x) h = h * 13 + *(x++); + return h; +} + +/* Works like strdup, sort of. Save a string in malloced memory, but +** keep strings in a table so that the same string is not in more +** than one place. +*/ +const char *Strsafe(const char *y) { + const char *z; + char *cpy; + + if (y == 0) return 0; + z = Strsafe_find(y); + if (z == 0 && (cpy = (char *)malloc(lemonStrlen(y) + 1)) != 0) { + lemon_strcpy(cpy, y); + z = cpy; + Strsafe_insert(z); + } + MemoryCheck(z); + return z; +} + +/* There is one instance of the following structure for each +** associative array of type "x1". +*/ +struct s_x1 { + int size; /* The number of available slots. */ + /* Must be a power of 2 greater than or */ + /* equal to 1 */ + int count; /* Number of currently slots filled */ + struct s_x1node *tbl; /* The data stored here */ + struct s_x1node **ht; /* Hash table for lookups */ +}; + +/* There is one instance of this structure for every data element +** in an associative array of type "x1". +*/ +typedef struct s_x1node { + const char *data; /* The data */ + struct s_x1node *next; /* Next entry with the same hash */ + struct s_x1node **from; /* Previous link */ +} x1node; + +/* There is only one instance of the array, which is the following */ +static struct s_x1 *x1a; + +/* Allocate a new associative array */ +void Strsafe_init(void) { + if (x1a) return; + x1a = (struct s_x1 *)malloc(sizeof(struct s_x1)); + if (x1a) { + x1a->size = 1024; + x1a->count = 0; + x1a->tbl = (x1node *)calloc(1024, sizeof(x1node) + sizeof(x1node *)); + if (x1a->tbl == 0) { + free(x1a); + x1a = 0; + } else { + int i; + x1a->ht = (x1node **)&(x1a->tbl[1024]); + for (i = 0; i < 1024; i++) x1a->ht[i] = 0; + } + } +} +/* Insert a new record into the array. Return TRUE if successful. +** Prior data with the same key is NOT overwritten */ +int Strsafe_insert(const char *data) { + x1node *np; + unsigned h; + unsigned ph; + + if (x1a == 0) return 0; + ph = strhash(data); + h = ph & (x1a->size - 1); + np = x1a->ht[h]; + while (np) { + if (strcmp(np->data, data) == 0) { + /* An existing entry with the same key is found. */ + /* Fail because overwrite is not allows. */ + return 0; + } + np = np->next; + } + if (x1a->count >= x1a->size) { + /* Need to make the hash table bigger */ + int i, arrSize; + struct s_x1 array; + array.size = arrSize = x1a->size * 2; + array.count = x1a->count; + array.tbl = (x1node *)calloc(arrSize, sizeof(x1node) + sizeof(x1node *)); + if (array.tbl == 0) return 0; /* Fail due to malloc failure */ + array.ht = (x1node **)&(array.tbl[arrSize]); + for (i = 0; i < arrSize; i++) array.ht[i] = 0; + for (i = 0; i < x1a->count; i++) { + x1node *oldnp, *newnp; + oldnp = &(x1a->tbl[i]); + h = strhash(oldnp->data) & (arrSize - 1); + newnp = &(array.tbl[i]); + if (array.ht[h]) array.ht[h]->from = &(newnp->next); + newnp->next = array.ht[h]; + newnp->data = oldnp->data; + newnp->from = &(array.ht[h]); + array.ht[h] = newnp; + } + free(x1a->tbl); + *x1a = array; + } + /* Insert the new data */ + h = ph & (x1a->size - 1); + np = &(x1a->tbl[x1a->count++]); + np->data = data; + if (x1a->ht[h]) x1a->ht[h]->from = &(np->next); + np->next = x1a->ht[h]; + x1a->ht[h] = np; + np->from = &(x1a->ht[h]); + return 1; +} + +/* Return a pointer to data assigned to the given key. Return NULL +** if no such key. */ +const char *Strsafe_find(const char *key) { + unsigned h; + x1node *np; + + if (x1a == 0) return 0; + h = strhash(key) & (x1a->size - 1); + np = x1a->ht[h]; + while (np) { + if (strcmp(np->data, key) == 0) break; + np = np->next; + } + return np ? np->data : 0; +} + +/* Return a pointer to the (terminal or nonterminal) symbol "x". +** Create a new symbol if this is the first time "x" has been seen. +*/ +struct symbol *Symbol_new(const char *x) { + struct symbol *sp; + + sp = Symbol_find(x); + if (sp == 0) { + sp = (struct symbol *)calloc(1, sizeof(struct symbol)); + MemoryCheck(sp); + sp->name = Strsafe(x); + sp->type = ISUPPER(*x) ? TERMINAL : NONTERMINAL; + sp->rule = 0; + sp->fallback = 0; + sp->prec = -1; + sp->assoc = UNK; + sp->firstset = 0; + sp->lambda = LEMON_FALSE; + sp->destructor = 0; + sp->destLineno = 0; + sp->datatype = 0; + sp->useCnt = 0; + Symbol_insert(sp, sp->name); + } + sp->useCnt++; + return sp; +} + +/* Compare two symbols for sorting purposes. Return negative, +** zero, or positive if a is less then, equal to, or greater +** than b. +** +** Symbols that begin with upper case letters (terminals or tokens) +** must sort before symbols that begin with lower case letters +** (non-terminals). And MULTITERMINAL symbols (created using the +** %token_class directive) must sort at the very end. Other than +** that, the order does not matter. +** +** We find experimentally that leaving the symbols in their original +** order (the order they appeared in the grammar file) gives the +** smallest parser tables in SQLite. +*/ +int Symbolcmpp(const void *_a, const void *_b) { + const struct symbol *a = *(const struct symbol **)_a; + const struct symbol *b = *(const struct symbol **)_b; + int i1 = a->type == MULTITERMINAL ? 3 : a->name[0] > 'Z' ? 2 : 1; + int i2 = b->type == MULTITERMINAL ? 3 : b->name[0] > 'Z' ? 2 : 1; + return i1 == i2 ? a->index - b->index : i1 - i2; +} + +/* There is one instance of the following structure for each +** associative array of type "x2". +*/ +struct s_x2 { + int size; /* The number of available slots. */ + /* Must be a power of 2 greater than or */ + /* equal to 1 */ + int count; /* Number of currently slots filled */ + struct s_x2node *tbl; /* The data stored here */ + struct s_x2node **ht; /* Hash table for lookups */ +}; + +/* There is one instance of this structure for every data element +** in an associative array of type "x2". +*/ +typedef struct s_x2node { + struct symbol *data; /* The data */ + const char *key; /* The key */ + struct s_x2node *next; /* Next entry with the same hash */ + struct s_x2node **from; /* Previous link */ +} x2node; + +/* There is only one instance of the array, which is the following */ +static struct s_x2 *x2a; + +/* Allocate a new associative array */ +void Symbol_init(void) { + if (x2a) return; + x2a = (struct s_x2 *)malloc(sizeof(struct s_x2)); + if (x2a) { + x2a->size = 128; + x2a->count = 0; + x2a->tbl = (x2node *)calloc(128, sizeof(x2node) + sizeof(x2node *)); + if (x2a->tbl == 0) { + free(x2a); + x2a = 0; + } else { + int i; + x2a->ht = (x2node **)&(x2a->tbl[128]); + for (i = 0; i < 128; i++) x2a->ht[i] = 0; + } + } +} +/* Insert a new record into the array. Return TRUE if successful. +** Prior data with the same key is NOT overwritten */ +int Symbol_insert(struct symbol *data, const char *key) { + x2node *np; + unsigned h; + unsigned ph; + + if (x2a == 0) return 0; + ph = strhash(key); + h = ph & (x2a->size - 1); + np = x2a->ht[h]; + while (np) { + if (strcmp(np->key, key) == 0) { + /* An existing entry with the same key is found. */ + /* Fail because overwrite is not allows. */ + return 0; + } + np = np->next; + } + if (x2a->count >= x2a->size) { + /* Need to make the hash table bigger */ + int i, arrSize; + struct s_x2 array; + array.size = arrSize = x2a->size * 2; + array.count = x2a->count; + array.tbl = (x2node *)calloc(arrSize, sizeof(x2node) + sizeof(x2node *)); + if (array.tbl == 0) return 0; /* Fail due to malloc failure */ + array.ht = (x2node **)&(array.tbl[arrSize]); + for (i = 0; i < arrSize; i++) array.ht[i] = 0; + for (i = 0; i < x2a->count; i++) { + x2node *oldnp, *newnp; + oldnp = &(x2a->tbl[i]); + h = strhash(oldnp->key) & (arrSize - 1); + newnp = &(array.tbl[i]); + if (array.ht[h]) array.ht[h]->from = &(newnp->next); + newnp->next = array.ht[h]; + newnp->key = oldnp->key; + newnp->data = oldnp->data; + newnp->from = &(array.ht[h]); + array.ht[h] = newnp; + } + free(x2a->tbl); + *x2a = array; + } + /* Insert the new data */ + h = ph & (x2a->size - 1); + np = &(x2a->tbl[x2a->count++]); + np->key = key; + np->data = data; + if (x2a->ht[h]) x2a->ht[h]->from = &(np->next); + np->next = x2a->ht[h]; + x2a->ht[h] = np; + np->from = &(x2a->ht[h]); + return 1; +} + +/* Return a pointer to data assigned to the given key. Return NULL +** if no such key. */ +struct symbol *Symbol_find(const char *key) { + unsigned h; + x2node *np; + + if (x2a == 0) return 0; + h = strhash(key) & (x2a->size - 1); + np = x2a->ht[h]; + while (np) { + if (strcmp(np->key, key) == 0) break; + np = np->next; + } + return np ? np->data : 0; +} + +/* Return the n-th data. Return NULL if n is out of range. */ +struct symbol *Symbol_Nth(int n) { + struct symbol *data; + if (x2a && n > 0 && n <= x2a->count) { + data = x2a->tbl[n - 1].data; + } else { + data = 0; + } + return data; +} + +/* Return the size of the array */ +int Symbol_count() { + return x2a ? x2a->count : 0; +} + +/* Return an array of pointers to all data in the table. +** The array is obtained from malloc. Return NULL if memory allocation +** problems, or if the array is empty. */ +struct symbol **Symbol_arrayof() { + struct symbol **array; + int i, arrSize; + if (x2a == 0) return 0; + arrSize = x2a->count; + array = (struct symbol **)calloc(arrSize, sizeof(struct symbol *)); + if (array) { + for (i = 0; i < arrSize; i++) array[i] = x2a->tbl[i].data; + } + return array; +} + +/* Compare two configurations */ +int Configcmp(const char *_a, const char *_b) { + const struct config *a = (struct config *)_a; + const struct config *b = (struct config *)_b; + int x; + x = a->rp->index - b->rp->index; + if (x == 0) x = a->dot - b->dot; + return x; +} + +/* Compare two states */ +PRIVATE int statecmp(struct config *a, struct config *b) { + int rc; + for (rc = 0; rc == 0 && a && b; a = a->bp, b = b->bp) { + rc = a->rp->index - b->rp->index; + if (rc == 0) rc = a->dot - b->dot; + } + if (rc == 0) { + if (a) rc = 1; + if (b) rc = -1; + } + return rc; +} + +/* Hash a state */ +PRIVATE unsigned statehash(struct config *a) { + unsigned h = 0; + while (a) { + h = h * 571 + a->rp->index * 37 + a->dot; + a = a->bp; + } + return h; +} + +/* Allocate a new state structure */ +struct state *State_new() { + struct state *newstate; + newstate = (struct state *)calloc(1, sizeof(struct state)); + MemoryCheck(newstate); + return newstate; +} + +/* There is one instance of the following structure for each +** associative array of type "x3". +*/ +struct s_x3 { + int size; /* The number of available slots. */ + /* Must be a power of 2 greater than or */ + /* equal to 1 */ + int count; /* Number of currently slots filled */ + struct s_x3node *tbl; /* The data stored here */ + struct s_x3node **ht; /* Hash table for lookups */ +}; + +/* There is one instance of this structure for every data element +** in an associative array of type "x3". +*/ +typedef struct s_x3node { + struct state *data; /* The data */ + struct config *key; /* The key */ + struct s_x3node *next; /* Next entry with the same hash */ + struct s_x3node **from; /* Previous link */ +} x3node; + +/* There is only one instance of the array, which is the following */ +static struct s_x3 *x3a; + +/* Allocate a new associative array */ +void State_init(void) { + if (x3a) return; + x3a = (struct s_x3 *)malloc(sizeof(struct s_x3)); + if (x3a) { + x3a->size = 128; + x3a->count = 0; + x3a->tbl = (x3node *)calloc(128, sizeof(x3node) + sizeof(x3node *)); + if (x3a->tbl == 0) { + free(x3a); + x3a = 0; + } else { + int i; + x3a->ht = (x3node **)&(x3a->tbl[128]); + for (i = 0; i < 128; i++) x3a->ht[i] = 0; + } + } +} +/* Insert a new record into the array. Return TRUE if successful. +** Prior data with the same key is NOT overwritten */ +int State_insert(struct state *data, struct config *key) { + x3node *np; + unsigned h; + unsigned ph; + + if (x3a == 0) return 0; + ph = statehash(key); + h = ph & (x3a->size - 1); + np = x3a->ht[h]; + while (np) { + if (statecmp(np->key, key) == 0) { + /* An existing entry with the same key is found. */ + /* Fail because overwrite is not allows. */ + return 0; + } + np = np->next; + } + if (x3a->count >= x3a->size) { + /* Need to make the hash table bigger */ + int i, arrSize; + struct s_x3 array; + array.size = arrSize = x3a->size * 2; + array.count = x3a->count; + array.tbl = (x3node *)calloc(arrSize, sizeof(x3node) + sizeof(x3node *)); + if (array.tbl == 0) return 0; /* Fail due to malloc failure */ + array.ht = (x3node **)&(array.tbl[arrSize]); + for (i = 0; i < arrSize; i++) array.ht[i] = 0; + for (i = 0; i < x3a->count; i++) { + x3node *oldnp, *newnp; + oldnp = &(x3a->tbl[i]); + h = statehash(oldnp->key) & (arrSize - 1); + newnp = &(array.tbl[i]); + if (array.ht[h]) array.ht[h]->from = &(newnp->next); + newnp->next = array.ht[h]; + newnp->key = oldnp->key; + newnp->data = oldnp->data; + newnp->from = &(array.ht[h]); + array.ht[h] = newnp; + } + free(x3a->tbl); + *x3a = array; + } + /* Insert the new data */ + h = ph & (x3a->size - 1); + np = &(x3a->tbl[x3a->count++]); + np->key = key; + np->data = data; + if (x3a->ht[h]) x3a->ht[h]->from = &(np->next); + np->next = x3a->ht[h]; + x3a->ht[h] = np; + np->from = &(x3a->ht[h]); + return 1; +} + +/* Return a pointer to data assigned to the given key. Return NULL +** if no such key. */ +struct state *State_find(struct config *key) { + unsigned h; + x3node *np; + + if (x3a == 0) return 0; + h = statehash(key) & (x3a->size - 1); + np = x3a->ht[h]; + while (np) { + if (statecmp(np->key, key) == 0) break; + np = np->next; + } + return np ? np->data : 0; +} + +/* Return an array of pointers to all data in the table. +** The array is obtained from malloc. Return NULL if memory allocation +** problems, or if the array is empty. */ +struct state **State_arrayof(void) { + struct state **array; + int i, arrSize; + if (x3a == 0) return 0; + arrSize = x3a->count; + array = (struct state **)calloc(arrSize, sizeof(struct state *)); + if (array) { + for (i = 0; i < arrSize; i++) array[i] = x3a->tbl[i].data; + } + return array; +} + +/* Hash a configuration */ +PRIVATE unsigned confighash(struct config *a) { + unsigned h = 0; + h = h * 571 + a->rp->index * 37 + a->dot; + return h; +} + +/* There is one instance of the following structure for each +** associative array of type "x4". +*/ +struct s_x4 { + int size; /* The number of available slots. */ + /* Must be a power of 2 greater than or */ + /* equal to 1 */ + int count; /* Number of currently slots filled */ + struct s_x4node *tbl; /* The data stored here */ + struct s_x4node **ht; /* Hash table for lookups */ +}; + +/* There is one instance of this structure for every data element +** in an associative array of type "x4". +*/ +typedef struct s_x4node { + struct config *data; /* The data */ + struct s_x4node *next; /* Next entry with the same hash */ + struct s_x4node **from; /* Previous link */ +} x4node; + +/* There is only one instance of the array, which is the following */ +static struct s_x4 *x4a; + +/* Allocate a new associative array */ +void Configtable_init(void) { + if (x4a) return; + x4a = (struct s_x4 *)malloc(sizeof(struct s_x4)); + if (x4a) { + x4a->size = 64; + x4a->count = 0; + x4a->tbl = (x4node *)calloc(64, sizeof(x4node) + sizeof(x4node *)); + if (x4a->tbl == 0) { + free(x4a); + x4a = 0; + } else { + int i; + x4a->ht = (x4node **)&(x4a->tbl[64]); + for (i = 0; i < 64; i++) x4a->ht[i] = 0; + } + } +} +/* Insert a new record into the array. Return TRUE if successful. +** Prior data with the same key is NOT overwritten */ +int Configtable_insert(struct config *data) { + x4node *np; + unsigned h; + unsigned ph; + + if (x4a == 0) return 0; + ph = confighash(data); + h = ph & (x4a->size - 1); + np = x4a->ht[h]; + while (np) { + if (Configcmp((const char *)np->data, (const char *)data) == 0) { + /* An existing entry with the same key is found. */ + /* Fail because overwrite is not allows. */ + return 0; + } + np = np->next; + } + if (x4a->count >= x4a->size) { + /* Need to make the hash table bigger */ + int i, arrSize; + struct s_x4 array; + array.size = arrSize = x4a->size * 2; + array.count = x4a->count; + array.tbl = (x4node *)calloc(arrSize, sizeof(x4node) + sizeof(x4node *)); + if (array.tbl == 0) return 0; /* Fail due to malloc failure */ + array.ht = (x4node **)&(array.tbl[arrSize]); + for (i = 0; i < arrSize; i++) array.ht[i] = 0; + for (i = 0; i < x4a->count; i++) { + x4node *oldnp, *newnp; + oldnp = &(x4a->tbl[i]); + h = confighash(oldnp->data) & (arrSize - 1); + newnp = &(array.tbl[i]); + if (array.ht[h]) array.ht[h]->from = &(newnp->next); + newnp->next = array.ht[h]; + newnp->data = oldnp->data; + newnp->from = &(array.ht[h]); + array.ht[h] = newnp; + } + free(x4a->tbl); + *x4a = array; + } + /* Insert the new data */ + h = ph & (x4a->size - 1); + np = &(x4a->tbl[x4a->count++]); + np->data = data; + if (x4a->ht[h]) x4a->ht[h]->from = &(np->next); + np->next = x4a->ht[h]; + x4a->ht[h] = np; + np->from = &(x4a->ht[h]); + return 1; +} + +/* Return a pointer to data assigned to the given key. Return NULL +** if no such key. */ +struct config *Configtable_find(struct config *key) { + int h; + x4node *np; + + if (x4a == 0) return 0; + h = confighash(key) & (x4a->size - 1); + np = x4a->ht[h]; + while (np) { + if (Configcmp((const char *)np->data, (const char *)key) == 0) break; + np = np->next; + } + return np ? np->data : 0; +} + +/* Remove all data from the table. Pass each data to the function "f" +** as it is removed. ("f" may be null to avoid this step.) */ +void Configtable_clear(int (*f)(struct config *)) { + int i; + if (x4a == 0 || x4a->count == 0) return; + if (f) + for (i = 0; i < x4a->count; i++) (*f)(x4a->tbl[i].data); + for (i = 0; i < x4a->size; i++) x4a->ht[i] = 0; + x4a->count = 0; + return; +} diff --git a/third_party/lemon/lemon.mk b/third_party/lemon/lemon.mk new file mode 100644 index 000000000..304660c65 --- /dev/null +++ b/third_party/lemon/lemon.mk @@ -0,0 +1,55 @@ +#-*-mode:makefile-gmake;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐ +#───vi: set et ft=make ts=8 tw=8 fenc=utf-8 :vi───────────────────────┘ + +LEMON = $(MKDIR) $(@D) && ACTION="LEMON $@" build/do $(THIRD_PARTY_LEMON) + +THIRD_PARTY_LEMON = o/$(MODE)/third_party/lemon/lemon.com.dbg + +THIRD_PARTY_LEMON_OBJS = \ + o/$(MODE)/third_party/lemon/lemon.o \ + o/$(MODE)/third_party/lemon/lempar.c.txt.zip.o + +THIRD_PARTY_LEMON_COMS = \ + o/$(MODE)/third_party/lemon/lemon.com + +THIRD_PARTY_LEMON_LINK = \ + $(THIRD_PARTY_LEMON_DEPS) \ + o/$(MODE)/third_party/lemon/%.o \ + $(CRT) \ + $(APE) + +THIRD_PARTY_LEMON_DIRECTDEPS = \ + LIBC_ALG \ + LIBC_CALLS \ + LIBC_CONV \ + LIBC_FMT \ + LIBC_MEM \ + LIBC_NEXGEN32E \ + LIBC_RUNTIME \ + LIBC_STDIO \ + LIBC_STR \ + LIBC_STUBS \ + LIBC_UNICODE \ + LIBC_X \ + LIBC_ZIPOS \ + THIRD_PARTY_DTOA + +THIRD_PARTY_LEMON_DEPS := \ + $(call uniq,$(foreach x,$(THIRD_PARTY_LEMON_DIRECTDEPS),$($(x)))) + +o/$(MODE)/third_party/lemon/lemon.pkg: \ + $(THIRD_PARTY_LEMON_OBJS) \ + $(foreach x,$(THIRD_PARTY_LEMON_DIRECTDEPS),$($(x)_A).pkg) + +o/$(MODE)/third_party/lemon/lemon.com.dbg: \ + $(THIRD_PARTY_LEMON_DEPS) \ + o/$(MODE)/third_party/lemon/lemon.pkg \ + o/$(MODE)/third_party/lemon/lemon.o \ + o/$(MODE)/third_party/lemon/lempar.c.txt.zip.o \ + $(CRT) \ + $(APE) + -@$(APELINK) + +.PHONY: o/$(MODE)/third_party/lemon +o/$(MODE)/third_party/lemon: \ + o/$(MODE)/third_party/lemon/lemon.com diff --git a/third_party/lemon/lempar.c.txt b/third_party/lemon/lempar.c.txt new file mode 100644 index 000000000..ca46ddc9a --- /dev/null +++ b/third_party/lemon/lempar.c.txt @@ -0,0 +1,1083 @@ +#if 0 +/*─────────────────────────────────────────────────────────────────╗ +│ To the extent possible under law, Justine Tunney has waived │ +│ all copyright and related or neighboring rights to this file, │ +│ as it is written in the following disclaimers: │ +│ • http://unlicense.org/ │ +│ • http://creativecommons.org/publicdomain/zero/1.0/ │ +╚─────────────────────────────────────────────────────────────────*/ +#endif +#include "libc/stdio/stdio.h" +#include "libc/assert.h" + +/* +** 2000-05-29 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** Driver template for the LEMON parser generator. +** +** The "lemon" program processes an LALR(1) input grammar file, then uses +** this template to construct a parser. The "lemon" program inserts text +** at each "%%" line. Also, any "P-a-r-s-e" identifer prefix (without the +** interstitial "-" characters) contained in this template is changed into +** the value of the %name directive from the grammar. Otherwise, the content +** of this template is copied straight through into the generate parser +** source file. +** +** The following is the concatenation of all %include directives from the +** input grammar file: +*/ + +/************ Begin %include sections from the grammar ************************/ +%% +/**************** End of %include directives **********************************/ +/* These constants specify the various numeric values for terminal symbols. +***************** Begin token definitions *************************************/ +%% +/**************** End token definitions ***************************************/ + +/* The next sections is a series of control #defines. +** various aspects of the generated parser. +** YYCODETYPE is the data type used to store the integer codes +** that represent terminal and non-terminal symbols. +** "unsigned char" is used if there are fewer than +** 256 symbols. Larger types otherwise. +** YYNOCODE is a number of type YYCODETYPE that is not used for +** any terminal or nonterminal symbol. +** YYFALLBACK If defined, this indicates that one or more tokens +** (also known as: "terminal symbols") have fall-back +** values which should be used if the original symbol +** would not parse. This permits keywords to sometimes +** be used as identifiers, for example. +** YYACTIONTYPE is the data type used for "action codes" - numbers +** that indicate what to do in response to the next +** token. +** ParseTOKENTYPE is the data type used for minor type for terminal +** symbols. Background: A "minor type" is a semantic +** value associated with a terminal or non-terminal +** symbols. For example, for an "ID" terminal symbol, +** the minor type might be the name of the identifier. +** Each non-terminal can have a different minor type. +** Terminal symbols all have the same minor type, though. +** This macros defines the minor type for terminal +** symbols. +** YYMINORTYPE is the data type used for all minor types. +** This is typically a union of many types, one of +** which is ParseTOKENTYPE. The entry in the union +** for terminal symbols is called "yy0". +** YYSTACKDEPTH is the maximum depth of the parser's stack. If +** zero the stack is dynamically sized using realloc() +** ParseARG_SDECL A static variable declaration for the %extra_argument +** ParseARG_PDECL A parameter declaration for the %extra_argument +** ParseARG_PARAM Code to pass %extra_argument as a subroutine parameter +** ParseARG_STORE Code to store %extra_argument into yypParser +** ParseARG_FETCH Code to extract %extra_argument from yypParser +** ParseCTX_* As ParseARG_ except for %extra_context +** YYERRORSYMBOL is the code number of the error symbol. If not +** defined, then do no error processing. +** YYNSTATE the combined number of states. +** YYNRULE the number of rules in the grammar +** YYNTOKEN Number of terminal symbols +** YY_MAX_SHIFT Maximum value for shift actions +** YY_MIN_SHIFTREDUCE Minimum value for shift-reduce actions +** YY_MAX_SHIFTREDUCE Maximum value for shift-reduce actions +** YY_ERROR_ACTION The yy_action[] code for syntax error +** YY_ACCEPT_ACTION The yy_action[] code for accept +** YY_NO_ACTION The yy_action[] code for no-op +** YY_MIN_REDUCE Minimum value for reduce actions +** YY_MAX_REDUCE Maximum value for reduce actions +*/ +#ifndef INTERFACE +# define INTERFACE 1 +#endif +/************* Begin control #defines *****************************************/ +%% +/************* End control #defines *******************************************/ +#define YY_NLOOKAHEAD ((int)(sizeof(yy_lookahead)/sizeof(yy_lookahead[0]))) + +/* Define the yytestcase() macro to be a no-op if is not already defined +** otherwise. +** +** Applications can choose to define yytestcase() in the %include section +** to a macro that can assist in verifying code coverage. For production +** code the yytestcase() macro should be turned off. But it is useful +** for testing. +*/ +#ifndef yytestcase +# define yytestcase(X) +#endif + + +/* Next are the tables used to determine what action to take based on the +** current state and lookahead token. These tables are used to implement +** functions that take a state number and lookahead value and return an +** action integer. +** +** Suppose the action integer is N. Then the action is determined as +** follows +** +** 0 <= N <= YY_MAX_SHIFT Shift N. That is, push the lookahead +** token onto the stack and goto state N. +** +** N between YY_MIN_SHIFTREDUCE Shift to an arbitrary state then +** and YY_MAX_SHIFTREDUCE reduce by rule N-YY_MIN_SHIFTREDUCE. +** +** N == YY_ERROR_ACTION A syntax error has occurred. +** +** N == YY_ACCEPT_ACTION The parser accepts its input. +** +** N == YY_NO_ACTION No such action. Denotes unused +** slots in the yy_action[] table. +** +** N between YY_MIN_REDUCE Reduce by rule N-YY_MIN_REDUCE +** and YY_MAX_REDUCE +** +** The action table is constructed as a single large table named yy_action[]. +** Given state S and lookahead X, the action is computed as either: +** +** (A) N = yy_action[ yy_shift_ofst[S] + X ] +** (B) N = yy_default[S] +** +** The (A) formula is preferred. The B formula is used instead if +** yy_lookahead[yy_shift_ofst[S]+X] is not equal to X. +** +** The formulas above are for computing the action when the lookahead is +** a terminal symbol. If the lookahead is a non-terminal (as occurs after +** a reduce action) then the yy_reduce_ofst[] array is used in place of +** the yy_shift_ofst[] array. +** +** The following are the tables generated in this section: +** +** yy_action[] A single table containing all actions. +** yy_lookahead[] A table containing the lookahead for each entry in +** yy_action. Used to detect hash collisions. +** yy_shift_ofst[] For each state, the offset into yy_action for +** shifting terminals. +** yy_reduce_ofst[] For each state, the offset into yy_action for +** shifting non-terminals after a reduce. +** yy_default[] Default action for each state. +** +*********** Begin parsing tables **********************************************/ +%% +/********** End of lemon-generated parsing tables *****************************/ + +/* The next table maps tokens (terminal symbols) into fallback tokens. +** If a construct like the following: +** +** %fallback ID X Y Z. +** +** appears in the grammar, then ID becomes a fallback token for X, Y, +** and Z. Whenever one of the tokens X, Y, or Z is input to the parser +** but it does not parse, the type of the token is changed to ID and +** the parse is retried before an error is thrown. +** +** This feature can be used, for example, to cause some keywords in a language +** to revert to identifiers if they keyword does not apply in the context where +** it appears. +*/ +#ifdef YYFALLBACK +static const YYCODETYPE yyFallback[] = { +%% +}; +#endif /* YYFALLBACK */ + +/* The following structure represents a single element of the +** parser's stack. Information stored includes: +** +** + The state number for the parser at this level of the stack. +** +** + The value of the token stored at this level of the stack. +** (In other words, the "major" token.) +** +** + The semantic value stored at this level of the stack. This is +** the information used by the action routines in the grammar. +** It is sometimes called the "minor" token. +** +** After the "shift" half of a SHIFTREDUCE action, the stateno field +** actually contains the reduce action for the second half of the +** SHIFTREDUCE. +*/ +struct yyStackEntry { + YYACTIONTYPE stateno; /* The state-number, or reduce action in SHIFTREDUCE */ + YYCODETYPE major; /* The major token value. This is the code + ** number for the token at this stack level */ + YYMINORTYPE minor; /* The user-supplied minor token value. This + ** is the value of the token */ +}; +typedef struct yyStackEntry yyStackEntry; + +/* The state of the parser is completely contained in an instance of +** the following structure */ +struct yyParser { + yyStackEntry *yytos; /* Pointer to top element of the stack */ +#ifdef YYTRACKMAXSTACKDEPTH + int yyhwm; /* High-water mark of the stack */ +#endif +#ifndef YYNOERRORRECOVERY + int yyerrcnt; /* Shifts left before out of the error */ +#endif + ParseARG_SDECL /* A place to hold %extra_argument */ + ParseCTX_SDECL /* A place to hold %extra_context */ +#if YYSTACKDEPTH<=0 + int yystksz; /* Current side of the stack */ + yyStackEntry *yystack; /* The parser's stack */ + yyStackEntry yystk0; /* First stack entry */ +#else + yyStackEntry yystack[YYSTACKDEPTH]; /* The parser's stack */ + yyStackEntry *yystackEnd; /* Last entry in the stack */ +#endif +}; +typedef struct yyParser yyParser; + +#ifndef NDEBUG +static FILE *yyTraceFILE; +static char *yyTracePrompt; +#endif /* NDEBUG */ + +#ifndef NDEBUG +/* +** Turn parser tracing on by giving a stream to which to write the trace +** and a prompt to preface each trace message. Tracing is turned off +** by making either argument NULL +** +** Inputs: +**
    +**
  • A FILE* to which trace output should be written. +** If NULL, then tracing is turned off. +**
  • A prefix string written at the beginning of every +** line of trace output. If NULL, then tracing is +** turned off. +**
+** +** Outputs: +** None. +*/ +void ParseTrace(FILE *TraceFILE, char *zTracePrompt){ + yyTraceFILE = TraceFILE; + yyTracePrompt = zTracePrompt; + if( yyTraceFILE==0 ) yyTracePrompt = 0; + else if( yyTracePrompt==0 ) yyTraceFILE = 0; +} +#endif /* NDEBUG */ + +#if defined(YYCOVERAGE) || !defined(NDEBUG) +/* For tracing shifts, the names of all terminals and nonterminals +** are required. The following table supplies these names */ +static const char *const yyTokenName[] = { +%% +}; +#endif /* defined(YYCOVERAGE) || !defined(NDEBUG) */ + +#ifndef NDEBUG +/* For tracing reduce actions, the names of all rules are required. +*/ +static const char *const yyRuleName[] = { +%% +}; +#endif /* NDEBUG */ + +#if YYSTACKDEPTH<=0 +/* +** Try to increase the size of the parser stack. Return the number +** of errors. Return 0 on success. +*/ +static int yyGrowStack(yyParser *p){ + int newSize; + int idx; + yyStackEntry *pNew; + newSize = p->yystksz*2 + 100; + idx = p->yytos ? (int)(p->yytos - p->yystack) : 0; + if( p->yystack==&p->yystk0 ){ + pNew = malloc(newSize*sizeof(pNew[0])); + if( pNew ) pNew[0] = p->yystk0; + }else{ + pNew = realloc(p->yystack, newSize*sizeof(pNew[0])); + } + if( pNew ){ + p->yystack = pNew; + p->yytos = &p->yystack[idx]; +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sStack grows from %d to %d entries.\n", + yyTracePrompt, p->yystksz, newSize); + } +#endif + p->yystksz = newSize; + } + return pNew==0; +} +#endif + +/* Datatype of the argument to the memory allocated passed as the +** second argument to ParseAlloc() below. This can be changed by +** putting an appropriate #define in the %include section of the input +** grammar. +*/ +#ifndef YYMALLOCARGTYPE +# define YYMALLOCARGTYPE size_t +#endif + +/* Initialize a new parser that has already been allocated. +*/ +static void ParseInit(void *yypRawParser ParseCTX_PDECL){ + yyParser *yypParser = (yyParser*)yypRawParser; + ParseCTX_STORE +#ifdef YYTRACKMAXSTACKDEPTH + yypParser->yyhwm = 0; +#endif +#if YYSTACKDEPTH<=0 + yypParser->yytos = NULL; + yypParser->yystack = NULL; + yypParser->yystksz = 0; + if( yyGrowStack(yypParser) ){ + yypParser->yystack = &yypParser->yystk0; + yypParser->yystksz = 1; + } +#endif +#ifndef YYNOERRORRECOVERY + yypParser->yyerrcnt = -1; +#endif + yypParser->yytos = yypParser->yystack; + yypParser->yystack[0].stateno = 0; + yypParser->yystack[0].major = 0; +#if YYSTACKDEPTH>0 + yypParser->yystackEnd = &yypParser->yystack[YYSTACKDEPTH-1]; +#endif +} + +#ifndef Parse_ENGINEALWAYSONSTACK +/* +** This function allocates a new parser. +** The only argument is a pointer to a function which works like +** malloc. +** +** Inputs: +** A pointer to the function used to allocate memory. +** +** Outputs: +** A pointer to a parser. This pointer is used in subsequent calls +** to Parse and ParseFree. +*/ +static void *ParseAlloc(void *(*mallocProc)(YYMALLOCARGTYPE) ParseCTX_PDECL){ + yyParser *yypParser; + yypParser = (yyParser*)(*mallocProc)( (YYMALLOCARGTYPE)sizeof(yyParser) ); + if( yypParser ){ + ParseCTX_STORE + ParseInit(yypParser ParseCTX_PARAM); + } + return (void*)yypParser; +} +#endif /* Parse_ENGINEALWAYSONSTACK */ + + +/* The following function deletes the "minor type" or semantic value +** associated with a symbol. The symbol can be either a terminal +** or nonterminal. "yymajor" is the symbol code, and "yypminor" is +** a pointer to the value to be deleted. The code used to do the +** deletions is derived from the %destructor and/or %token_destructor +** directives of the input grammar. +*/ +static void yy_destructor( + yyParser *yypParser, /* The parser */ + YYCODETYPE yymajor, /* Type code for object to destroy */ + YYMINORTYPE *yypminor /* The object to be destroyed */ +){ + ParseARG_FETCH + ParseCTX_FETCH + switch( yymajor ){ + /* Here is inserted the actions which take place when a + ** terminal or non-terminal is destroyed. This can happen + ** when the symbol is popped from the stack during a + ** reduce or during error processing or when a parser is + ** being destroyed before it is finished parsing. + ** + ** Note: during a reduce, the only symbols destroyed are those + ** which appear on the RHS of the rule, but which are *not* used + ** inside the C code. + */ +/********* Begin destructor definitions ***************************************/ +%% +/********* End destructor definitions *****************************************/ + default: break; /* If no destructor action specified: do nothing */ + } +} + +/* +** Pop the parser's stack once. +** +** If there is a destructor routine associated with the token which +** is popped from the stack, then call it. +*/ +static void yy_pop_parser_stack(yyParser *pParser){ + yyStackEntry *yytos; + assert( pParser->yytos!=0 ); + assert( pParser->yytos > pParser->yystack ); + yytos = pParser->yytos--; +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sPopping %s\n", + yyTracePrompt, + yyTokenName[yytos->major]); + } +#endif + yy_destructor(pParser, yytos->major, &yytos->minor); +} + +/* +** Clear all secondary memory allocations from the parser +*/ +static void ParseFinalize(void *p){ + yyParser *pParser = (yyParser*)p; + while( pParser->yytos>pParser->yystack ) yy_pop_parser_stack(pParser); +#if YYSTACKDEPTH<=0 + if( pParser->yystack!=&pParser->yystk0 ) free(pParser->yystack); +#endif +} + +#ifndef Parse_ENGINEALWAYSONSTACK +/* +** Deallocate and destroy a parser. Destructors are called for +** all stack elements before shutting the parser down. +** +** If the YYPARSEFREENEVERNULL macro exists (for example because it +** is defined in a %include section of the input grammar) then it is +** assumed that the input pointer is never NULL. +*/ +static void ParseFree( + void *p, /* The parser to be deleted */ + void (*freeProc)(void*) /* Function used to reclaim memory */ +){ +#ifndef YYPARSEFREENEVERNULL + if( p==0 ) return; +#endif + ParseFinalize(p); + (*freeProc)(p); +} +#endif /* Parse_ENGINEALWAYSONSTACK */ + +/* +** Return the peak depth of the stack for a parser. +*/ +#ifdef YYTRACKMAXSTACKDEPTH +int ParseStackPeak(void *p){ + yyParser *pParser = (yyParser*)p; + return pParser->yyhwm; +} +#endif + +/* This array of booleans keeps track of the parser statement +** coverage. The element yycoverage[X][Y] is set when the parser +** is in state X and has a lookahead token Y. In a well-tested +** systems, every element of this matrix should end up being set. +*/ +#if defined(YYCOVERAGE) +static unsigned char yycoverage[YYNSTATE][YYNTOKEN]; +#endif + +/* +** Write into out a description of every state/lookahead combination that +** +** (1) has not been used by the parser, and +** (2) is not a syntax error. +** +** Return the number of missed state/lookahead combinations. +*/ +#if defined(YYCOVERAGE) +static int ParseCoverage(FILE *out){ + int stateno, iLookAhead, i; + int nMissed = 0; + for(stateno=0; statenoYY_MAX_SHIFT ) return stateno; + assert( stateno <= YY_SHIFT_COUNT ); +#if defined(YYCOVERAGE) + yycoverage[stateno][iLookAhead] = 1; +#endif + do{ + i = yy_shift_ofst[stateno]; + assert( i>=0 ); + assert( i<=YY_ACTTAB_COUNT ); + assert( i+YYNTOKEN<=(int)YY_NLOOKAHEAD ); + assert( iLookAhead!=YYNOCODE ); + assert( iLookAhead < YYNTOKEN ); + i += iLookAhead; + assert( i<(int)YY_NLOOKAHEAD ); + if( yy_lookahead[i]!=iLookAhead ){ +#ifdef YYFALLBACK + YYCODETYPE iFallback; /* Fallback token */ + assert( iLookAhead %s\n", + yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[iFallback]); + } +#endif + assert( yyFallback[iFallback]==0 ); /* Fallback loop must terminate */ + iLookAhead = iFallback; + continue; + } +#endif +#ifdef YYWILDCARD + { + int j = i - iLookAhead + YYWILDCARD; + assert( j<(int)(sizeof(yy_lookahead)/sizeof(yy_lookahead[0])) ); + if( yy_lookahead[j]==YYWILDCARD && iLookAhead>0 ){ +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE, "%sWILDCARD %s => %s\n", + yyTracePrompt, yyTokenName[iLookAhead], + yyTokenName[YYWILDCARD]); + } +#endif /* NDEBUG */ + return yy_action[j]; + } + } +#endif /* YYWILDCARD */ + return yy_default[stateno]; + }else{ + assert( i>=0 && i<(int)(sizeof(yy_action)/sizeof(yy_action[0])) ); + return yy_action[i]; + } + }while(1); +} + +/* +** Find the appropriate action for a parser given the non-terminal +** look-ahead token iLookAhead. +*/ +static YYACTIONTYPE yy_find_reduce_action( + YYACTIONTYPE stateno, /* Current state number */ + YYCODETYPE iLookAhead /* The look-ahead token */ +){ + int i; +#ifdef YYERRORSYMBOL + if( stateno>YY_REDUCE_COUNT ){ + return yy_default[stateno]; + } +#else + assert( stateno<=YY_REDUCE_COUNT ); +#endif + i = yy_reduce_ofst[stateno]; + assert( iLookAhead!=YYNOCODE ); + i += iLookAhead; +#ifdef YYERRORSYMBOL + if( i<0 || i>=YY_ACTTAB_COUNT || yy_lookahead[i]!=iLookAhead ){ + return yy_default[stateno]; + } +#else + assert( i>=0 && iyytos>yypParser->yystack ) yy_pop_parser_stack(yypParser); + /* Here code is inserted which will execute if the parser + ** stack every overflows */ +/******** Begin %stack_overflow code ******************************************/ +%% +/******** End %stack_overflow code ********************************************/ + ParseARG_STORE /* Suppress warning about unused %extra_argument var */ + ParseCTX_STORE +} + +/* +** Print tracing information for a SHIFT action +*/ +#ifndef NDEBUG +static void yyTraceShift(yyParser *yypParser, int yyNewState, const char *zTag){ + if( yyTraceFILE ){ + if( yyNewStateyytos->major], + yyNewState); + }else{ + fprintf(yyTraceFILE,"%s%s '%s', pending reduce %d\n", + yyTracePrompt, zTag, yyTokenName[yypParser->yytos->major], + yyNewState - YY_MIN_REDUCE); + } + } +} +#else +# define yyTraceShift(X,Y,Z) +#endif + +/* +** Perform a shift action. +*/ +static void yy_shift( + yyParser *yypParser, /* The parser to be shifted */ + YYACTIONTYPE yyNewState, /* The new state to shift in */ + YYCODETYPE yyMajor, /* The major token to shift in */ + ParseTOKENTYPE yyMinor /* The minor token to shift in */ +){ + yyStackEntry *yytos; + yypParser->yytos++; +#ifdef YYTRACKMAXSTACKDEPTH + if( (int)(yypParser->yytos - yypParser->yystack)>yypParser->yyhwm ){ + yypParser->yyhwm++; + assert( yypParser->yyhwm == (int)(yypParser->yytos - yypParser->yystack) ); + } +#endif +#if YYSTACKDEPTH>0 + if( yypParser->yytos>yypParser->yystackEnd ){ + yypParser->yytos--; + yyStackOverflow(yypParser); + return; + } +#else + if( yypParser->yytos>=&yypParser->yystack[yypParser->yystksz] ){ + if( yyGrowStack(yypParser) ){ + yypParser->yytos--; + yyStackOverflow(yypParser); + return; + } + } +#endif + if( yyNewState > YY_MAX_SHIFT ){ + yyNewState += YY_MIN_REDUCE - YY_MIN_SHIFTREDUCE; + } + yytos = yypParser->yytos; + yytos->stateno = yyNewState; + yytos->major = yyMajor; + yytos->minor.yy0 = yyMinor; + yyTraceShift(yypParser, yyNewState, "Shift"); +} + +/* For rule J, yyRuleInfoLhs[J] contains the symbol on the left-hand side +** of that rule */ +static const YYCODETYPE yyRuleInfoLhs[] = { +%% +}; + +/* For rule J, yyRuleInfoNRhs[J] contains the negative of the number +** of symbols on the right-hand side of that rule. */ +static const signed char yyRuleInfoNRhs[] = { +%% +}; + +static void yy_accept(yyParser*); /* Forward Declaration */ + +/* +** Perform a reduce action and the shift that must immediately +** follow the reduce. +** +** The yyLookahead and yyLookaheadToken parameters provide reduce actions +** access to the lookahead token (if any). The yyLookahead will be YYNOCODE +** if the lookahead token has already been consumed. As this procedure is +** only called from one place, optimizing compilers will in-line it, which +** means that the extra parameters have no performance impact. +*/ +static YYACTIONTYPE yy_reduce( + yyParser *yypParser, /* The parser */ + unsigned int yyruleno, /* Number of the rule by which to reduce */ + int yyLookahead, /* Lookahead token, or YYNOCODE if none */ + ParseTOKENTYPE yyLookaheadToken /* Value of the lookahead token */ + ParseCTX_PDECL /* %extra_context */ +){ + int yygoto; /* The next state */ + YYACTIONTYPE yyact; /* The next action */ + yyStackEntry *yymsp; /* The top of the parser's stack */ + int yysize; /* Amount to pop the stack */ + ParseARG_FETCH + (void)yyLookahead; + (void)yyLookaheadToken; + yymsp = yypParser->yytos; +#ifndef NDEBUG + assert( yyruleno<(int)(sizeof(yyRuleName)/sizeof(yyRuleName[0])) ); + if( yyTraceFILE ){ + yysize = yyRuleInfoNRhs[yyruleno]; + if( yysize ){ + fprintf(yyTraceFILE, "%sReduce %d [%s]%s, pop back to state %d.\n", + yyTracePrompt, + yyruleno, yyRuleName[yyruleno], + yyrulenoyytos - yypParser->yystack)>yypParser->yyhwm ){ + yypParser->yyhwm++; + assert( yypParser->yyhwm == (int)(yypParser->yytos - yypParser->yystack)); + } +#endif +#if YYSTACKDEPTH>0 + if( yypParser->yytos>=yypParser->yystackEnd ){ + yyStackOverflow(yypParser); + /* The call to yyStackOverflow() above pops the stack until it is + ** empty, causing the main parser loop to exit. So the return value + ** is never used and does not matter. */ + return 0; + } +#else + if( yypParser->yytos>=&yypParser->yystack[yypParser->yystksz-1] ){ + if( yyGrowStack(yypParser) ){ + yyStackOverflow(yypParser); + /* The call to yyStackOverflow() above pops the stack until it is + ** empty, causing the main parser loop to exit. So the return value + ** is never used and does not matter. */ + return 0; + } + yymsp = yypParser->yytos; + } +#endif + } + + switch( yyruleno ){ + /* Beginning here are the reduction cases. A typical example + ** follows: + ** case 0: + ** #line + ** { ... } // User supplied code + ** #line + ** break; + */ +/********** Begin reduce actions **********************************************/ +%% +/********** End reduce actions ************************************************/ + }; + assert( yyrulenoYY_MAX_SHIFT && yyact<=YY_MAX_SHIFTREDUCE) ); + + /* It is not possible for a REDUCE to be followed by an error */ + assert( yyact!=YY_ERROR_ACTION ); + + yymsp += yysize+1; + yypParser->yytos = yymsp; + yymsp->stateno = (YYACTIONTYPE)yyact; + yymsp->major = (YYCODETYPE)yygoto; + yyTraceShift(yypParser, yyact, "... then shift"); + return yyact; +} + +/* +** The following code executes when the parse fails +*/ +#ifndef YYNOERRORRECOVERY +static void yy_parse_failed( + yyParser *yypParser /* The parser */ +){ + ParseARG_FETCH + ParseCTX_FETCH +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sFail!\n",yyTracePrompt); + } +#endif + while( yypParser->yytos>yypParser->yystack ) yy_pop_parser_stack(yypParser); + /* Here code is inserted which will be executed whenever the + ** parser fails */ +/************ Begin %parse_failure code ***************************************/ +%% +/************ End %parse_failure code *****************************************/ + ParseARG_STORE /* Suppress warning about unused %extra_argument variable */ + ParseCTX_STORE +} +#endif /* YYNOERRORRECOVERY */ + +/* +** The following code executes when a syntax error first occurs. +*/ +static void yy_syntax_error( + yyParser *yypParser, /* The parser */ + int yymajor, /* The major type of the error token */ + ParseTOKENTYPE yyminor /* The minor type of the error token */ +){ + ParseARG_FETCH + ParseCTX_FETCH +#define TOKEN yyminor +/************ Begin %syntax_error code ****************************************/ +%% +/************ End %syntax_error code ******************************************/ + ParseARG_STORE /* Suppress warning about unused %extra_argument variable */ + ParseCTX_STORE +} + +/* +** The following is executed when the parser accepts +*/ +static void yy_accept( + yyParser *yypParser /* The parser */ +){ + ParseARG_FETCH + ParseCTX_FETCH +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sAccept!\n",yyTracePrompt); + } +#endif +#ifndef YYNOERRORRECOVERY + yypParser->yyerrcnt = -1; +#endif + assert( yypParser->yytos==yypParser->yystack ); + /* Here code is inserted which will be executed whenever the + ** parser accepts */ +/*********** Begin %parse_accept code *****************************************/ +%% +/*********** End %parse_accept code *******************************************/ + ParseARG_STORE /* Suppress warning about unused %extra_argument variable */ + ParseCTX_STORE +} + +/* The main parser program. +** The first argument is a pointer to a structure obtained from +** "ParseAlloc" which describes the current state of the parser. +** The second argument is the major token number. The third is +** the minor token. The fourth optional argument is whatever the +** user wants (and specified in the grammar) and is available for +** use by the action routines. +** +** Inputs: +**
    +**
  • A pointer to the parser (an opaque structure.) +**
  • The major token number. +**
  • The minor token number. +**
  • An option argument of a grammar-specified type. +**
+** +** Outputs: +** None. +*/ +static void Parse( + void *yyp, /* The parser */ + int yymajor, /* The major token code number */ + ParseTOKENTYPE yyminor /* The value for the token */ + ParseARG_PDECL /* Optional %extra_argument parameter */ +){ + YYMINORTYPE yyminorunion; + YYACTIONTYPE yyact; /* The parser action. */ +#if !defined(YYERRORSYMBOL) && !defined(YYNOERRORRECOVERY) + int yyendofinput; /* True if we are at the end of input */ +#endif +#ifdef YYERRORSYMBOL + int yyerrorhit = 0; /* True if yymajor has invoked an error */ +#endif + yyParser *yypParser = (yyParser*)yyp; /* The parser */ + ParseCTX_FETCH + ParseARG_STORE + + assert( yypParser->yytos!=0 ); +#if !defined(YYERRORSYMBOL) && !defined(YYNOERRORRECOVERY) + yyendofinput = (yymajor==0); +#endif + + yyact = yypParser->yytos->stateno; +#ifndef NDEBUG + if( yyTraceFILE ){ + if( yyact < YY_MIN_REDUCE ){ + fprintf(yyTraceFILE,"%sInput '%s' in state %d\n", + yyTracePrompt,yyTokenName[yymajor],yyact); + }else{ + fprintf(yyTraceFILE,"%sInput '%s' with pending reduce %d\n", + yyTracePrompt,yyTokenName[yymajor],yyact-YY_MIN_REDUCE); + } + } +#endif + + do{ + assert( yyact==yypParser->yytos->stateno ); + yyact = yy_find_shift_action((YYCODETYPE)yymajor,yyact); + if( yyact >= YY_MIN_REDUCE ){ + yyact = yy_reduce(yypParser,yyact-YY_MIN_REDUCE,yymajor, + yyminor ParseCTX_PARAM); + }else if( yyact <= YY_MAX_SHIFTREDUCE ){ + yy_shift(yypParser,yyact,(YYCODETYPE)yymajor,yyminor); +#ifndef YYNOERRORRECOVERY + yypParser->yyerrcnt--; +#endif + break; + }else if( yyact==YY_ACCEPT_ACTION ){ + yypParser->yytos--; + yy_accept(yypParser); + return; + }else{ + assert( yyact == YY_ERROR_ACTION ); + yyminorunion.yy0 = yyminor; +#ifdef YYERRORSYMBOL + int yymx; +#endif +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sSyntax Error!\n",yyTracePrompt); + } +#endif +#ifdef YYERRORSYMBOL + /* A syntax error has occurred. + ** The response to an error depends upon whether or not the + ** grammar defines an error token "ERROR". + ** + ** This is what we do if the grammar does define ERROR: + ** + ** * Call the %syntax_error function. + ** + ** * Begin popping the stack until we enter a state where + ** it is legal to shift the error symbol, then shift + ** the error symbol. + ** + ** * Set the error count to three. + ** + ** * Begin accepting and shifting new tokens. No new error + ** processing will occur until three tokens have been + ** shifted successfully. + ** + */ + if( yypParser->yyerrcnt<0 ){ + yy_syntax_error(yypParser,yymajor,yyminor); + } + yymx = yypParser->yytos->major; + if( yymx==YYERRORSYMBOL || yyerrorhit ){ +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sDiscard input token %s\n", + yyTracePrompt,yyTokenName[yymajor]); + } +#endif + yy_destructor(yypParser, (YYCODETYPE)yymajor, &yyminorunion); + yymajor = YYNOCODE; + }else{ + while( yypParser->yytos >= yypParser->yystack + && (yyact = yy_find_reduce_action( + yypParser->yytos->stateno, + YYERRORSYMBOL)) > YY_MAX_SHIFTREDUCE + ){ + yy_pop_parser_stack(yypParser); + } + if( yypParser->yytos < yypParser->yystack || yymajor==0 ){ + yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion); + yy_parse_failed(yypParser); +#ifndef YYNOERRORRECOVERY + yypParser->yyerrcnt = -1; +#endif + yymajor = YYNOCODE; + }else if( yymx!=YYERRORSYMBOL ){ + yy_shift(yypParser,yyact,YYERRORSYMBOL,yyminor); + } + } + yypParser->yyerrcnt = 3; + yyerrorhit = 1; + if( yymajor==YYNOCODE ) break; + yyact = yypParser->yytos->stateno; +#elif defined(YYNOERRORRECOVERY) + /* If the YYNOERRORRECOVERY macro is defined, then do not attempt to + ** do any kind of error recovery. Instead, simply invoke the syntax + ** error routine and continue going as if nothing had happened. + ** + ** Applications can set this macro (for example inside %include) if + ** they intend to abandon the parse upon the first syntax error seen. + */ + yy_syntax_error(yypParser,yymajor, yyminor); + yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion); + break; +#else /* YYERRORSYMBOL is not defined */ + /* This is what we do if the grammar does not define ERROR: + ** + ** * Report an error message, and throw away the input token. + ** + ** * If the input token is $, then fail the parse. + ** + ** As before, subsequent error messages are suppressed until + ** three input tokens have been successfully shifted. + */ + if( yypParser->yyerrcnt<=0 ){ + yy_syntax_error(yypParser,yymajor, yyminor); + } + yypParser->yyerrcnt = 3; + yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion); + if( yyendofinput ){ + yy_parse_failed(yypParser); +#ifndef YYNOERRORRECOVERY + yypParser->yyerrcnt = -1; +#endif + } + break; +#endif + } + }while( yypParser->yytos>yypParser->yystack ); +#ifndef NDEBUG + if( yyTraceFILE ){ + yyStackEntry *i; + char cDiv = '['; + fprintf(yyTraceFILE,"%sReturn. Stack=",yyTracePrompt); + for(i=&yypParser->yystack[1]; i<=yypParser->yytos; i++){ + fprintf(yyTraceFILE,"%c%s", cDiv, yyTokenName[i->major]); + cDiv = ' '; + } + fprintf(yyTraceFILE,"]\n"); + } +#endif + return; +} + +/* +** Return the fallback token corresponding to canonical token iToken, or +** 0 if iToken has no fallback. +*/ +static int ParseFallback(int iToken){ +#ifdef YYFALLBACK + assert( iToken<(int)(sizeof(yyFallback)/sizeof(yyFallback[0])) ); + return yyFallback[iToken]; +#else + (void)iToken; + return 0; +#endif +} diff --git a/third_party/third_party.mk b/third_party/third_party.mk index 5eb772c5f..2b4b381fd 100644 --- a/third_party/third_party.mk +++ b/third_party/third_party.mk @@ -14,6 +14,7 @@ o/$(MODE)/third_party: \ o/$(MODE)/third_party/editline \ o/$(MODE)/third_party/f2c \ o/$(MODE)/third_party/getopt \ + o/$(MODE)/third_party/lemon \ o/$(MODE)/third_party/lex \ o/$(MODE)/third_party/linenoise \ o/$(MODE)/third_party/lz4cli \ diff --git a/tool/build/build.mk b/tool/build/build.mk index 2a5a8edd2..f3910ed02 100644 --- a/tool/build/build.mk +++ b/tool/build/build.mk @@ -17,12 +17,6 @@ TOOL_BUILD_OBJS = \ TOOL_BUILD_COMS = \ $(TOOL_BUILD_SRCS:%.c=o/$(MODE)/%.com) -TOOL_BUILD_LINK = \ - $(TOOL_BUILD_DEPS) \ - o/$(MODE)/tool/build/%.o \ - $(CRT) \ - $(APE) - TOOL_BUILD_CHECKS = \ $(TOOL_BUILD).pkg \ $(TOOL_BUILD_HDRS:%=o/$(MODE)/%.ok) \ @@ -86,24 +80,10 @@ o/$(MODE)/tool/build/%.com.dbg: \ $(APE) -@$(APELINK) -o/$(MODE)/tool/build/mkdeps.o: tool/build/mkdeps.c - -@ACTION=OBJECTIFY.c build/compile $(OBJECTIFY.c) $(OUTPUT_OPTION) $< - o/$(MODE)/tool/build/emulator.o: \ OVERRIDE_COPTS += \ -fno-sanitize=pointer-overflow -o/$(MODE)/tool/build/transpile16.o: \ - OVERRIDE_CFLAGS += \ - -ffixed-r8 \ - -ffixed-r9 \ - -ffixed-r10 \ - -ffixed-r11 \ - -ffixed-r12 \ - -ffixed-r13 \ - -ffixed-r14 \ - -ffixed-r15 - .PHONY: o/$(MODE)/tool/build o/$(MODE)/tool/build: \ o/$(MODE)/tool/build/emucrt \ diff --git a/tool/build/emubin/emubin.mk b/tool/build/emubin/emubin.mk index 6fef236d1..f1baaeb42 100644 --- a/tool/build/emubin/emubin.mk +++ b/tool/build/emubin/emubin.mk @@ -49,14 +49,15 @@ o/$(MODE)/tool/build/emubin/%.bin.dbg: \ $(TOOL_BUILD_EMUBIN_A).pkg @$(ELFLINK) -e emucrt -z max-page-size=0x10 -o/$(MODE)/tool/build/emubin/real/spiral.o: tool/build/emubin/real/spiral.c - @$(MKDIR) $(@D) - /opt/cross/bin/i486-linux-musl-gcc -nostdlib -nostdinc -m16 -Os -g -c -o $@ $< +o/tiny/tool/build/emubin/spiral.bin.dbg: \ + $(TOOL_BUILD_EMUBIN_DEPS) \ + o/tiny/tool/build/emubin/spiral.real.o + @$(ELFLINK) -z max-page-size=0x10 -T tool/build/emucrt/real.lds -o/$(MODE)/tool/build/emubin/real/spiral.bin: o/$(MODE)/tool/build/emubin/real/spiral.o - /opt/cross/bin/i486-linux-musl-ld -static -nostdlib --oformat=binary -T tool/build/emubin/real/spiral.lds -o $@ $^ - -o/$(MODE)/tool/build/emubin/hug.o: OVERRIDE_CFLAGS += -ffast-math +o/tiny/tool/build/emubin/mdatest.bin.dbg: \ + $(TOOL_BUILD_EMUBIN_DEPS) \ + o/tiny/tool/build/emubin/mdatest.real.o + @$(ELFLINK) -z max-page-size=0x10 -T tool/build/emucrt/real.lds .PHONY: o/$(MODE)/tool/build/emubin o/$(MODE)/tool/build/emubin: \ diff --git a/tool/build/emubin/mdatest.real.c b/tool/build/emubin/mdatest.real.c new file mode 100644 index 000000000..86f53a057 --- /dev/null +++ b/tool/build/emubin/mdatest.real.c @@ -0,0 +1,44 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "tool/build/emubin/poke.h" +#include "tool/build/emubin/real.h" + +/* +m=tiny; make -j12 MODE=$m o/$m/tool/build/{tinyemu,emulator}.com \ +o/$m/tool/build/emubin/mdatest.bin && o/$m/tool/build/emulator.com \ +-rt o/$m/tool/build/emubin/mdatest.bin +*/ + +static void MdaTest(uint16_t p[25][80]) { + int i, y, x; + for (i = 0; i < 256; ++i) { + y = i / 16; + x = i % 16 * 3; + POKE(p[y][x + 0], i << 8 | "0123456789abcdef"[(i & 0xf0) >> 4]); + POKE(p[y][x + 1], i << 8 | "0123456789abcdef"[(i & 0x0f) >> 0]); + } +} + +int main() { + SetVideoMode(7); + SetEs(0xb0000 >> 4); + MdaTest((void *)0); + for (;;) asm("pause"); +} diff --git a/tool/build/emubin/mips.c b/tool/build/emubin/mips.c index 5abb5cb03..fc3f8f921 100644 --- a/tool/build/emubin/mips.c +++ b/tool/build/emubin/mips.c @@ -19,7 +19,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "tool/build/emubin/metalsha256.h" -#define DISINGENUOUS /* 100 million NOPs is still 100 MIPS lool */ +//#define DISINGENUOUS /* 100 million NOPs is still 100 MIPS lool */ static void Print(uint8_t c) { asm volatile("out\t%0,$0xE9" : /* no outputs */ : "a"(c) : "memory"); diff --git a/tool/build/emubin/poke.h b/tool/build/emubin/poke.h new file mode 100644 index 000000000..27966d190 --- /dev/null +++ b/tool/build/emubin/poke.h @@ -0,0 +1,11 @@ +#ifndef COSMOPOLITAN_TOOL_BUILD_EMUBIN_POKE_H_ +#define COSMOPOLITAN_TOOL_BUILD_EMUBIN_POKE_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +#define POKE(MEM, VAL) \ + asm volatile("mov\t%1,%%es:%0" : "=m"(MEM) : "Qi"((typeof(MEM))(VAL))) + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_TOOL_BUILD_EMUBIN_POKE_H_ */ diff --git a/tool/build/emubin/real.h b/tool/build/emubin/real.h new file mode 100644 index 000000000..85a7d2313 --- /dev/null +++ b/tool/build/emubin/real.h @@ -0,0 +1,26 @@ +#ifndef COSMOPOLITAN_TOOL_BUILD_EMUBIN_REAL_H_ +#define COSMOPOLITAN_TOOL_BUILD_EMUBIN_REAL_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +asm(".pushsection .start,\"ax\",@progbits\n\t" + ".globl\t_start\n" + "_start:\n\t" + "jmp\t1f\n1:\t" + "call\tmain\n\t" + "nop\n\t" + ".popsection"); + +forceinline void SetEs(int base) { + asm volatile("mov%z0\t%0,%%es" : /* no outputs */ : "r"(base)); +} + +forceinline void SetVideoMode(int mode) { + asm volatile("int\t$0x10" + : /* no outputs */ + : "a"(mode)); +} + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_TOOL_BUILD_EMUBIN_REAL_H_ */ diff --git a/tool/build/emubin/real/spiral.c b/tool/build/emubin/spiral.real.c similarity index 77% rename from tool/build/emubin/real/spiral.c rename to tool/build/emubin/spiral.real.c index 718b42a0f..e44e716dc 100644 --- a/tool/build/emubin/real/spiral.c +++ b/tool/build/emubin/spiral.real.c @@ -17,6 +17,8 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "tool/build/emubin/poke.h" +#include "tool/build/emubin/real.h" #define signbit(x) __builtin_signbit(x) @@ -38,45 +40,49 @@ static const unsigned char kBlocks[] = { [0b0001] = 0xdf, // ▀ }; -static void *memset(void *di, int al, unsigned long cx) { - asm("rep stosb %%al,(%0)" +forceinline void *memset(void *di, int al, unsigned long cx) { + asm("rep stosb" : "=D"(di), "=c"(cx), "=m"(*(char(*)[cx])di) : "0"(di), "1"(cx), "a"(al)); return di; } -static long double pi(void) { +forceinline long double pi(void) { long double x; asm("fldpi" : "=t"(x)); return x; } -static void sincosl(long double x, long double *sin, long double *cos) { +forceinline long double tau(void) { + return pi() * 2; +} + +forceinline void sincosl(long double x, long double *sin, long double *cos) { asm("fsincos" : "=t"(*sin), "=u"(*cos) : "0"(x)); } -static long double atan2l(long double x, long double y) { +forceinline long double atan2l(long double x, long double y) { asm("fpatan" : "+t"(x) : "u"(y) : "st(1)"); return x; } -static long lrintl(long double x) { +forceinline long lrintl(long double x) { long i; asm("fistp%z0\t%0" : "=m"(i) : "t"(x) : "st"); return i; } -static long double truncl(long double x) { +forceinline long double truncl(long double x) { asm("frndint" : "+t"(x)); return x; } -static long double fabsl(long double x) { +forceinline long double fabsl(long double x) { asm("fabs" : "+t"(x)); return x; } -static long lroundl(long double x) { +forceinline long lroundl(long double x) { int s = signbit(x); x = truncl(fabsl(x) + .5); if (s) x = -x; @@ -93,36 +99,40 @@ static void SetFpuControlWord(unsigned short cw) { asm volatile("fldcw\t%0" : /* no outputs */ : "m"(cw)); } +static void InitializeFpu(void) { + asm("fninit"); +} + static void SetTruncationRounding(void) { SetFpuControlWord(GetFpuControlWord() | 0x0c00); } -static void spiral(unsigned char p[25][80][2], unsigned char B[25][80]) { +static void spiral(unsigned char p[25][80][2], unsigned char B[25][80], int g) { int i, x, y; long double a, b, u, v, h; for (a = b = i = 0; i < 1000; ++i) { sincosl(a, &u, &v); - h = atan2l(u, v); + h = atan2l(u, v) - .333L * g; x = lroundl(80 + u * b); y = lroundl(25 + v * b * (1. / ((266 / 64.) * (900 / 1600.)))); B[y >> 1][x >> 1] |= 1 << ((y & 1) << 1 | (x & 1)); - p[y >> 1][x >> 1][0] = kBlocks[B[y >> 1][x >> 1]]; - p[y >> 1][x >> 1][1] = (lrintl((h + pi() * 2) * (8 / (pi() * 2))) & 7) + 8; + POKE(p[y >> 1][x >> 1][0], kBlocks[B[y >> 1][x >> 1]]); + POKE(p[y >> 1][x >> 1][1], (lrintl((h + tau()) * (8 / tau())) & 7) + 8); a += .05; b += .05; } } int main() { - asm(".pushsection .start,\"ax\",@progbits\n\t" - ".globl\t_start\n" - "_start:\n\t" - "callw\tmain\n\t" - ".popsection"); + int g; + InitializeFpu(); SetTruncationRounding(); - for (;;) { - memset((void *)0x40000, 0, 25 * 80); - memset((void *)0xb8000, 0, 25 * 80 * 2); - spiral((void *)0xb8000, (void *)0x40000); + SetVideoMode(3); + for (g = 0;; ++g) { + SetEs(0); + memset((void *)(0x7c00 + 512), 0, 25 * 80); + SetEs(0xb8000 >> 4); + memset((void *)0, 0, 25 * 80 * 2); + spiral((void *)0, (void *)(0x7c00 + 512), g); } } diff --git a/tool/build/emubin/real/spiral.lds b/tool/build/emucrt/real.lds similarity index 98% rename from tool/build/emubin/real/spiral.lds rename to tool/build/emucrt/real.lds index e76971708..5d354cb92 100644 --- a/tool/build/emubin/real/spiral.lds +++ b/tool/build/emucrt/real.lds @@ -27,6 +27,8 @@ SECTIONS { *(.text .text.*) *(.rodata .rodata.*) *(.data .data.*) + . = 0x1fe; + SHORT(0xaa55); *(.bss .bss.*) *(COMMON) } diff --git a/tool/build/emulator.c b/tool/build/emulator.c index ebff3dc92..62c392047 100644 --- a/tool/build/emulator.c +++ b/tool/build/emulator.c @@ -64,13 +64,16 @@ #include "tool/build/lib/breakpoint.h" #include "tool/build/lib/case.h" #include "tool/build/lib/cga.h" +#include "tool/build/lib/demangle.h" #include "tool/build/lib/dis.h" #include "tool/build/lib/endian.h" #include "tool/build/lib/fds.h" #include "tool/build/lib/flags.h" #include "tool/build/lib/fpu.h" +#include "tool/build/lib/high.h" #include "tool/build/lib/loader.h" #include "tool/build/lib/machine.h" +#include "tool/build/lib/mda.h" #include "tool/build/lib/memory.h" #include "tool/build/lib/modrm.h" #include "tool/build/lib/panel.h" @@ -176,8 +179,10 @@ static const char kSegmentNames[16][4] = { static int tyn; static int txn; +static int tick; +static int speed; +static int vidya; static int ttyfd; -static bool vidya; static bool react; static bool ssehex; static int exitcode; @@ -200,7 +205,6 @@ static struct Panels pan; static int64_t readstart; static int64_t writestart; static int64_t stackstart; -static struct DisHigh high; static struct Machine m[2]; static struct MachinePty *pty; static char logpath[PATH_MAX]; @@ -521,6 +525,9 @@ static void SetupDraw(void) { c2y[1] -= tyn - c2y[2] - 26; c2y[2] = tyn - 26; } + if (tyn - c2y[2] < 26) { + c2y[2] = tyn - 26; + } a = (tyn - (cpuy + ssey) - 3) / 4; c3y[0] = cpuy; @@ -642,15 +649,11 @@ static void SetupDraw(void) { xcalloc(pan.p[i].bottom - pan.p[i].top, sizeof(struct Buffer)); } - if (pty->yn != pan.display.bottom - pan.display.top || - pty->xn != pan.display.right - pan.display.left) { - LOGF("MachinePtyResize"); - MachinePtyResize(pty, pan.display.bottom - pan.display.top, - pan.display.right - pan.display.left); - } + MachinePtyResize(pty, pan.display.bottom - pan.display.top, + pan.display.right - pan.display.left); } -static long GetDisIndex(int64_t addr) { +static long GetDisIndex(void) { long i; if ((i = DisFind(dis, GetIp())) == -1) { Dis(dis, m, GetIp(), pan.disassembly.bottom - pan.disassembly.top * 2); @@ -692,12 +695,21 @@ static void DrawTerminal(struct Panel *p) { } void DrawDisplay(struct Panel *p) { - if (vidya) { - DrawHr(&pan.displayhr, "COLOR GRAPHICS ADAPTER"); - DrawCga(p, VirtualSend(m, gc(xmalloc(25 * 80 * 2)), 0xb8000, 25 * 80 * 2)); - } else { - DrawHr(&pan.displayhr, "TELETYPEWRITER"); - DrawTerminal(p); + switch (vidya) { + case 7: + DrawHr(&pan.displayhr, "MONOCHROME DISPLAY ADAPTER"); + DrawMda(p, + VirtualSend(m, gc(xmalloc(25 * 80 * 2)), 0xb0000, 25 * 80 * 2)); + break; + case 3: + DrawHr(&pan.displayhr, "COLOR GRAPHICS ADAPTER"); + DrawCga(p, + VirtualSend(m, gc(xmalloc(25 * 80 * 2)), 0xb8000, 25 * 80 * 2)); + break; + default: + DrawHr(&pan.displayhr, "TELETYPEWRITER"); + DrawTerminal(p); + break; } } @@ -947,15 +959,17 @@ static void DrawMaps(struct Panel *p) { static void DrawBreakpoints(struct Panel *p) { int64_t addr; - char buf[128]; const char *name; + char *s, buf[256]; long i, line, sym; for (line = 0, i = breakpoints.i; i--;) { if (breakpoints.p[i].disable) continue; addr = breakpoints.p[i].addr; sym = DisFindSym(dis, addr); name = sym != -1 ? dis->syms.stab + dis->syms.p[sym].name : "UNKNOWN"; - snprintf(buf, sizeof(buf), "%p %s", addr, name); + s = buf; + s += sprintf(s, "%p ", addr); + CHECK_LT(Demangle(s, name), buf + ARRAYLEN(buf)); AppendPanel(p, line, buf); if (sym != -1 && addr != dis->syms.p[sym].addr) { snprintf(buf, sizeof(buf), "+%#lx", addr - dis->syms.p[sym].addr); @@ -982,8 +996,8 @@ static void DrawTrace(struct Panel *p) { int i, n; long sym; uint8_t *r; - char line[128]; const char *name; + char *s, line[256]; int64_t sp, bp, rp; rp = m->ip; bp = Read64(m->bp); @@ -992,7 +1006,9 @@ static void DrawTrace(struct Panel *p) { rp += Read64(m->cs); sym = DisFindSym(dis, rp); name = sym != -1 ? dis->syms.stab + dis->syms.p[sym].name : "UNKNOWN"; - snprintf(line, sizeof(line), "%p %p %s", Read64(m->ss) + bp, rp, name); + s = line; + s += sprintf(s, "%p %p ", Read64(m->ss) + bp, rp); + s = Demangle(s, name); AppendPanel(p, i, line); if (sym != -1 && rp != dis->syms.p[sym].addr) { snprintf(line, sizeof(line), "+%#lx", rp - dis->syms.p[sym].addr); @@ -1015,19 +1031,8 @@ static void DrawTrace(struct Panel *p) { break; } sp = bp; - switch (m->mode & 3) { - case XED_MODE_LONG: - bp = Read64(r + 0); - rp = Read64(r + 8); - break; - case XED_MODE_REAL: - case XED_MODE_LEGACY: - bp = Read32(r + 0); - rp = Read32(r + 4); - break; - default: - unreachable; - } + bp = Read64(r + 0); + rp = Read64(r + 8); } } @@ -1098,12 +1103,53 @@ static void Redraw(void) { } } +static void ReactiveDraw(void) { + if (tuimode) { + m->ip -= m->xedd->length; + SetupDraw(); + Redraw(); + m->ip += m->xedd->length; + tick = speed; + } +} + static int OnPtyFdClose(int fd) { return 0; } +static bool HasPendingInput(int fd) { + struct pollfd fds[1]; + fds[0].fd = fd; + fds[0].events = POLLIN; + fds[0].revents = 0; + poll(fds, ARRAYLEN(fds), 0); + return fds[0].revents & (POLLIN | POLLERR); +} + +static ssize_t ConsumePtyInput(int fd) { + char *buf; + ssize_t rc; + buf = malloc(PAGESIZE); + pty->conf |= kMachinePtyBlinkcursor; + ReactiveDraw(); + rc = read(fd, buf, PAGESIZE); + pty->conf &= ~kMachinePtyBlinkcursor; + ReactiveDraw(); + if (rc > 0) MachinePtyWriteInput(pty, buf, rc); + free(buf); + return rc; +} + static ssize_t OnPtyFdRead(int fd, void *data, size_t size) { - return read(fd, data, size); + ssize_t rc; + if (!size) return 0; + if (HasPendingInput(fd)) { + if ((rc = ConsumePtyInput(fd)) <= 0) return rc; + } + while (!(rc = MachinePtyRead(pty, data, size))) { + if ((rc = ConsumePtyInput(fd)) <= 0) return rc; + } + return rc; } static ssize_t OnPtyFdWrite(int fd, const void *data, size_t size) { @@ -1115,12 +1161,46 @@ static ssize_t OnPtyFdWrite(int fd, const void *data, size_t size) { } } +static int OnPtyFdTiocgwinsz(int fd, struct winsize *ws) { + ws->ws_row = pty->yn; + ws->ws_col = pty->xn; + return 0; +} + +static int OnPtyFdTcgets(int fd, struct termios *c) { + memset(c, 0, sizeof(*c)); + if (!(pty->conf & kMachinePtyNocanon)) c->c_iflag |= ICANON; + if (!(pty->conf & kMachinePtyNoecho)) c->c_iflag |= ECHO; + if (!(pty->conf & kMachinePtyNoopost)) c->c_oflag |= OPOST; + return 0; +} + +static int OnPtyFdTcsets(int fd, uint64_t request, struct termios *c) { + if (c->c_iflag & ICANON) { + pty->conf &= ~kMachinePtyNocanon; + } else { + pty->conf |= kMachinePtyNocanon; + } + if (c->c_iflag & ECHO) { + pty->conf &= ~kMachinePtyNoecho; + } else { + pty->conf |= kMachinePtyNoecho; + } + if (c->c_oflag & OPOST) { + pty->conf &= ~kMachinePtyNoopost; + } else { + pty->conf |= kMachinePtyNoopost; + } + return 0; +} + static int OnPtyFdIoctl(int fd, uint64_t request, void *memory) { if (request == TIOCGWINSZ) { - struct winsize *ws = memory; - ws->ws_row = pan.display.bottom - pan.display.top; - ws->ws_col = pan.display.right - pan.display.left; - return 0; + return OnPtyFdTiocgwinsz(fd, memory); + } else if (request == TCGETS) { + return OnPtyFdTcgets(fd, memory); + } else if (request == TCSETS || request == TCSETSW || request == TCSETSF) { + return OnPtyFdTcsets(fd, request, memory); } else { return einval(); } @@ -1278,9 +1358,59 @@ static void OnDiskService(void) { } static void OnVidyaServiceSetMode(void) { + vidya = m->ax[0]; } -static void OnVidyaServiceSetCursor(void) { +static void OnVidyaServiceGetMode(void) { + m->ax[0] = vidya; + m->ax[1] = 80; /* columns */ + m->bx[1] = 0; /* page */ +} + +static void OnVidyaServiceSetCursorPosition(void) { + pty->y = m->dx[1]; + pty->x = m->dx[0]; +} + +static void OnVidyaServiceGetCursorPosition(void) { + m->dx[1] = pty->y; + m->dx[0] = pty->x; + m->cx[1] = 5; /* cursor ▂ scan lines 5..7 of 0..7 */ + m->cx[0] = 7 | !!(pty->conf & kMachinePtyNocursor) << 5; +} + +static void OnVidyaServiceWriteCharacter(void) { + char buf[12]; + int i, n, y, x; + y = pty->y; + x = pty->x; + n = FormatCga(m->bx[0], buf); + n += tpencode(buf + n, 6, kCp437[m->ax[0]], false); + i = Read16(m->cx); + while (i--) MachinePtyWrite(pty, buf, n); + pty->y = y; + pty->x = x; +} + +static char16_t VidyaServiceXlatTeletype(uint8_t c) { + switch (c) { + case '\a': + case '\b': + case '\r': + case '\n': + case 0177: + return c; + default: + return kCp437[c]; + } +} + +static void OnVidyaServiceTeletypeOutput(void) { + int n; + char buf[12]; + n = FormatCga(m->bx[0], buf); + n += tpencode(buf + n, 6, VidyaServiceXlatTeletype(m->ax[0]), false); + MachinePtyWrite(pty, buf, n); } static void OnVidyaService(void) { @@ -1289,7 +1419,47 @@ static void OnVidyaService(void) { OnVidyaServiceSetMode(); break; case 0x02: - OnVidyaServiceSetCursor(); + OnVidyaServiceSetCursorPosition(); + break; + case 0x03: + OnVidyaServiceGetCursorPosition(); + break; + case 0x09: + OnVidyaServiceWriteCharacter(); + break; + case 0x0E: + OnVidyaServiceTeletypeOutput(); + break; + case 0x0F: + OnVidyaServiceGetMode(); + break; + default: + break; + } +} + +static void OnKeyboardServiceReadKeyPress(void) { + uint8_t b; + ReactiveDraw(); + read(0, &b, 1); + switch (b) { + case 0177: + b = '\b'; + break; + case CTRL('C'): + raise(SIGINT); + break; + default: + break; + } + m->ax[0] = b; + m->ax[1] = 0; +} + +static void OnKeyboardService(void) { + switch (m->ax[1]) { + case 0x00: + OnKeyboardServiceReadKeyPress(); break; default: break; @@ -1356,6 +1526,9 @@ static bool OnHalt(int interrupt) { case 0x15: OnInt15h(); return true; + case 0x16: + OnKeyboardService(); + return true; case kMachineSegmentationFault: OnSegmentationFault(); return false; @@ -1394,11 +1567,19 @@ static void OnPageDown(void) { } static void OnUpArrow(void) { - opstart--; + if (action & CONTINUE) { + speed = MIN(0x40000000, MAX(1, speed) << 1); + } else { + --opstart; + } } static void OnDownArrow(void) { - opstart++; + if (action & CONTINUE) { + speed >>= 1; + } else { + ++opstart; + } } static void OnHome(void) { @@ -1422,15 +1603,6 @@ static void OnFeed(void) { } static void OnUp(void) { - uint8_t b[8]; - int64_t sp, bp, ret; - bp = Read64(m->bp); - sp = Read64(m->sp); - if (bp >= sp) { - VirtualSend(m, b, bp + 8, sizeof(b)); - ret = Read64(b); - ScrollOp(&pan.disassembly, GetDisIndex(ret)); - } } static void OnDown(void) { @@ -1460,7 +1632,7 @@ static void OnFinish(void) { } static void OnContinue(void) { - action |= CONTINUE; + action ^= CONTINUE; action &= ~STEP; action &= ~NEXT; action &= ~FINISH; @@ -1495,12 +1667,7 @@ static void OnSseHex(void) { } static bool HasPendingKeyboard(void) { - struct pollfd fds[1]; - fds[0].fd = ttyfd; - fds[0].events = POLLIN; - fds[0].revents = 0; - poll(fds, ARRAYLEN(fds), 0); - return fds[0].revents & (POLLIN | POLLERR); + return HasPendingInput(ttyfd); } static bool IsExecuting(void) { @@ -1700,10 +1867,11 @@ static void Tui(void) { long op; ssize_t bp; int interrupt; + bool interactive; LOGF("TUI"); TuiSetup(); SetupDraw(); - ScrollOp(&pan.disassembly, GetDisIndex(m->ip)); + ScrollOp(&pan.disassembly, GetDisIndex()); if (!(interrupt = setjmp(m->onhalt))) { for (;;) { if (!(action & FAILURE)) { @@ -1719,13 +1887,18 @@ static void Tui(void) { GetTtySize(); action &= ~WINCHED; } - SetupDraw(); - Redraw(); + interactive = ++tick > speed; + if (!(action & CONTINUE) || interactive) { + tick = 0; + GetDisIndex(); + SetupDraw(); + Redraw(); + } if (action & FAILURE) { LOGF("TUI FAILURE"); PrintMessageBox(ttyfd, systemfailure, tyn, txn); ReadKeyboard(); - } else if (!IsExecuting() || HasPendingKeyboard()) { + } else if (!IsExecuting() || (interactive && HasPendingKeyboard())) { ReadKeyboard(); } if (action & INT) { @@ -1755,7 +1928,7 @@ static void Tui(void) { break; } if (IsExecuting()) { - op = GetDisIndex(m->ip); + op = GetDisIndex(); ScrollOp(&pan.disassembly, op); VERBOSEF("%s", DisGetLine(dis, m, op)); memcpy(&m[1], &m[0], sizeof(m[0])); @@ -1789,7 +1962,7 @@ static void Tui(void) { CheckFramePointer(); ops++; if (!(action & CONTINUE)) { - ScrollOp(&pan.disassembly, GetDisIndex(m->ip)); + ScrollOp(&pan.disassembly, GetDisIndex()); if ((action & FINISH) && IsRet()) action &= ~FINISH; if (((action & NEXT) && IsRet()) || (action & FINISH)) { action &= ~NEXT; @@ -1807,7 +1980,7 @@ static void Tui(void) { if (OnHalt(interrupt)) { goto KeepGoing; } - ScrollOp(&pan.disassembly, GetDisIndex(m->ip)); + ScrollOp(&pan.disassembly, GetDisIndex()); } TuiCleanup(); } @@ -1825,7 +1998,6 @@ static void GetOpts(int argc, char *argv[]) { break; case 'r': m->mode = XED_MACHINE_MODE_REAL; - vidya = true; break; case 's': printstats = true; @@ -1834,7 +2006,7 @@ static void GetOpts(int argc, char *argv[]) { HandleBreakpointFlag(optarg); break; case 'H': - g_dis_high = NULL; + memset(&g_high, 0, sizeof(g_high)); break; case 'v': ++g_loglevel; @@ -1905,14 +2077,14 @@ int main(int argc, char *argv[]) { m->mode = XED_MACHINE_MODE_LONG_64; ssewidth = 2; /* 16-bit is best bit */ if (!NoDebug()) showcrashreports(); - // OnlyRunOnFirstCpu(); + /* OnlyRunOnFirstCpu(); */ if ((colorize = cancolor())) { - high.keyword = 155; - high.reg = 215; - high.literal = 182; - high.label = 221; - high.comment = 112; - g_dis_high = &high; + g_high.keyword = 155; + g_high.reg = 215; + g_high.literal = 182; + g_high.label = 221; + g_high.comment = 112; + g_high.quote = 215; } GetOpts(argc, argv); if (optind == argc) PrintUsage(EX_USAGE, stderr); diff --git a/tool/build/lib/address.c b/tool/build/lib/address.c index 76945abaa..c2f626ab4 100644 --- a/tool/build/lib/address.c +++ b/tool/build/lib/address.c @@ -47,11 +47,10 @@ uint8_t *GetSegment(struct Machine *m, uint32_t rde, int s) { uint64_t AddSegment(struct Machine *m, uint32_t rde, uint64_t i, uint8_t s[8]) { if (!Sego(rde)) { - i += Read64(s); + return i + Read64(s); } else { - i += Read64(GetSegment(m, rde, Sego(rde) - 1)); + return i + Read64(GetSegment(m, rde, Sego(rde) - 1)); } - return i; } uint64_t DataSegment(struct Machine *m, uint32_t rde, uint64_t i) { diff --git a/tool/build/lib/breg.c b/tool/build/lib/breg.c new file mode 100644 index 000000000..5dd54ccd2 --- /dev/null +++ b/tool/build/lib/breg.c @@ -0,0 +1,37 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "tool/build/lib/modrm.h" + +/** + * Byte register offsets. + * + * for (i = 0; i < 2; ++i) { // rex + * for (j = 0; j < 2; ++j) { // rexb, or rexr + * for (k = 0; k < 8; ++k) { // reg, rm, or srm + * kByteReg[i << 4 | j << 3 | k] = + * i ? (j << 3 | k) * 8 : (k & 0b11) * 8 + ((k & 0b100) >> 2); + * } + * } + * } + */ +const uint8_t kByteReg[32] = {0x00, 0x08, 0x10, 0x18, 0x01, 0x09, 0x11, 0x19, + 0x00, 0x08, 0x10, 0x18, 0x01, 0x09, 0x11, 0x19, + 0x00, 0x08, 0x10, 0x18, 0x20, 0x28, 0x30, 0x38, + 0x40, 0x48, 0x50, 0x58, 0x60, 0x68, 0x70, 0x78}; diff --git a/tool/build/lib/buffer.c b/tool/build/lib/buffer.c index 048c63b21..444e6c4bc 100644 --- a/tool/build/lib/buffer.c +++ b/tool/build/lib/buffer.c @@ -17,9 +17,9 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "dsp/tty/tty.h" #include "libc/alg/arraylist2.h" #include "libc/calls/calls.h" +#include "libc/errno.h" #include "libc/fmt/fmt.h" #include "libc/mem/mem.h" #include "libc/str/tpencode.h" @@ -58,5 +58,22 @@ void AppendFmt(struct Buffer *b, const char *fmt, ...) { * Writes buffer until completion, interrupt, or error occurs. */ ssize_t WriteBuffer(struct Buffer *b, int fd) { - return ttywrite(fd, b->p, b->i); + char *p; + ssize_t rc; + size_t wrote, n; + p = b->p; + n = b->i; + do { + TryAgain: + if ((rc = write(fd, p, n)) != -1) { + wrote = rc; + p += wrote; + n -= wrote; + } else if (errno == EINTR) { + goto TryAgain; + } else { + return -1; + } + } while (n); + return 0; } diff --git a/tool/build/lib/buildlib.mk b/tool/build/lib/buildlib.mk index 976935d59..c14966a89 100644 --- a/tool/build/lib/buildlib.mk +++ b/tool/build/lib/buildlib.mk @@ -25,7 +25,6 @@ TOOL_BUILD_LIB_A_OBJS = \ $(TOOL_BUILD_LIB_A_SRCS_C:%.c=o/$(MODE)/%.o) TOOL_BUILD_LIB_A_DIRECTDEPS = \ - DSP_TTY \ LIBC_ALG \ LIBC_BITS \ LIBC_CONV \ diff --git a/tool/build/lib/cga.c b/tool/build/lib/cga.c index 53b30b705..1ce8ad9c9 100644 --- a/tool/build/lib/cga.c +++ b/tool/build/lib/cga.c @@ -17,24 +17,37 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/conv/itoa.h" #include "libc/macros.h" #include "libc/str/str.h" #include "tool/build/lib/buffer.h" #include "tool/build/lib/cga.h" -static const uint8_t kCgaToAnsi[] = {30, 34, 32, 36, 31, 35, 33, 37, - 90, 94, 92, 96, 91, 95, 93, 97}; +/* blk blu grn cyn red mag yel wht */ +static const uint8_t kCgaToAnsi[16] = {30, 34, 32, 36, 31, 35, 33, 37, + 90, 94, 92, 96, 91, 95, 93, 97}; + +size_t FormatCga(uint8_t bgfg, char buf[hasatleast 11]) { + char *p = buf; + *p++ = '\e'; + *p++ = '['; + p += uint64toarray_radix10(kCgaToAnsi[(bgfg & 0xF0) >> 4] + 10, p); + *p++ = ';'; + p += uint64toarray_radix10(kCgaToAnsi[bgfg & 0x0F], p); + *p++ = 'm'; + *p = '\0'; + return p - buf; +} void DrawCga(struct Panel *p, uint8_t v[25][80][2]) { + char buf[11]; unsigned y, x, n, a; n = MIN(25, p->bottom - p->top); for (y = 0; y < n; ++y) { a = -1; for (x = 0; x < 80; ++x) { if (v[y][x][1] != a) { - a = v[y][x][1]; - AppendFmt(&p->lines[y], "\e[%d;%dm", kCgaToAnsi[a & 0x0F], - kCgaToAnsi[(a & 0xF0) >> 4] + 10); + AppendData(&p->lines[y], buf, FormatCga((a = v[y][x][1]), buf)); } AppendWide(&p->lines[y], kCp437[v[y][x][0]]); } diff --git a/tool/build/lib/cga.h b/tool/build/lib/cga.h index 51ec429a0..d45c76cae 100644 --- a/tool/build/lib/cga.h +++ b/tool/build/lib/cga.h @@ -5,6 +5,7 @@ COSMOPOLITAN_C_START_ void DrawCga(struct Panel *, uint8_t[25][80][2]); +size_t FormatCga(uint8_t, char[hasatleast 11]); COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/tool/build/lib/cpuid.c b/tool/build/lib/cpuid.c index b1eadfe99..074fda28c 100644 --- a/tool/build/lib/cpuid.c +++ b/tool/build/lib/cpuid.c @@ -35,32 +35,32 @@ void OpCpuid(struct Machine *m, uint32_t rde) { dx = 'o' | 's' << 8 | 'm' << 16 | 'o' << 24; break; case 1: - cx |= 1 << 0; /* sse3 */ - cx |= 0 << 1; /* pclmulqdq */ - cx |= 1 << 9; /* ssse3 */ - cx |= 1 << 23; /* popcnt */ - cx |= 0 << 30; /* rdrnd */ - cx |= 0 << 25; /* aes */ - dx |= 1 << 0; /* fpu */ - dx |= 1 << 4; /* tsc */ - dx |= 1 << 6; /* pae */ - dx |= 1 << 8; /* cmpxchg8b */ - dx |= 1 << 15; /* cmov */ - dx |= 1 << 19; /* clflush */ - dx |= 1 << 23; /* mmx */ - dx |= 1 << 25; /* sse */ - dx |= 1 << 26; /* sse2 */ + cx |= 1 << 0; // sse3 + cx |= 0 << 1; // pclmulqdq + cx |= 1 << 9; // ssse3 + cx |= 1 << 23; // popcnt + cx |= 0 << 30; // rdrnd + cx |= 0 << 25; // aes + dx |= 1 << 0; // fpu + dx |= 1 << 4; // tsc + dx |= 1 << 6; // pae + dx |= 1 << 8; // cmpxchg8b + dx |= 1 << 15; // cmov + dx |= 1 << 19; // clflush + dx |= 1 << 23; // mmx + dx |= 1 << 25; // sse + dx |= 1 << 26; // sse2 break; case 7: - bx |= 1 << 9; /* erms */ + bx |= 1 << 9; // erms break; case 0x80000001: - cx |= 1 << 0; /* lahf/sahf */ - dx |= 1 << 11; /* syscall */ - dx |= 1 << 29; /* long mode */ + cx |= 1 << 0; // lahf + dx |= 1 << 11; // syscall + dx |= 1 << 29; // long break; case 0x80000007: - dx |= 1 << 8; /* invtsc */ + dx |= 1 << 8; // invtsc break; default: break; diff --git a/tool/build/lib/demangle.c b/tool/build/lib/demangle.c new file mode 100644 index 000000000..e9274883c --- /dev/null +++ b/tool/build/lib/demangle.c @@ -0,0 +1,94 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/calls.h" +#include "libc/calls/hefty/spawn.h" +#include "libc/macros.h" +#include "libc/runtime/runtime.h" +#include "libc/str/str.h" +#include "tool/build/lib/demangle.h" + +struct CxxFilt { + int pid; + int fds[3]; +} g_cxxfilt; + +void CloseCxxFilt(void) { + close(g_cxxfilt.fds[0]); + close(g_cxxfilt.fds[1]); + g_cxxfilt.pid = -1; +} + +void SpawnCxxFilt(void) { + int pid; + char path[PATH_MAX]; + if (commandv("c++filt", path)) { + g_cxxfilt.fds[0] = -1; + g_cxxfilt.fds[1] = -1; + g_cxxfilt.fds[2] = 2; + if ((pid = spawnve(0, g_cxxfilt.fds, path, (char *const[]){"c++filt", NULL}, + environ)) != -1) { + atexit(CloseCxxFilt); + } + } else { + pid = -1; + } + g_cxxfilt.pid = pid; +} + +char *DemangleCxxFilt(char *p, const char *symbol) { + int n; + char buf[512]; + bool iscomplicated; + if (!g_cxxfilt.pid) SpawnCxxFilt(); + if (g_cxxfilt.pid == -1) return NULL; + if ((n = strlen(symbol)) >= ARRAYLEN(buf)) return NULL; + memcpy(buf, symbol, n); + buf[n] = '\n'; + write(g_cxxfilt.fds[0], buf, n + 1); + n = read(g_cxxfilt.fds[1], buf, ARRAYLEN(buf)); + if (n > 1 && buf[n - 1] == '\n') { + if (buf[n - 2] == '\r') --n; + --n; + iscomplicated = memchr(buf, ' ', n) || memchr(buf, '(', n); + if (iscomplicated) *p++ = '"'; + p = mempcpy(p, buf, n); + if (iscomplicated) *p++ = '"'; + *p = '\0'; + return p; + } else { + CloseCxxFilt(); + return NULL; + } +} + +/** + * Decrypts C++ symbol. + * + * Decoding these takes roughly the same lines of code as an entire + * x86_64 disassembler. That's just for the GNU encoding scheme. So + * what we'll do, is just offload this work to the c++filt program. + */ +char *Demangle(char *p, const char *symbol) { + char *r; + if (startswith(symbol, "_Z")) { + if ((r = DemangleCxxFilt(p, symbol))) return r; + } + return stpcpy(p, symbol); +} diff --git a/tool/build/lib/demangle.h b/tool/build/lib/demangle.h new file mode 100644 index 000000000..209cd6c40 --- /dev/null +++ b/tool/build/lib/demangle.h @@ -0,0 +1,10 @@ +#ifndef COSMOPOLITAN_TOOL_BUILD_LIB_DEMANGLE_H_ +#define COSMOPOLITAN_TOOL_BUILD_LIB_DEMANGLE_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +char *Demangle(char *, const char *); + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_TOOL_BUILD_LIB_DEMANGLE_H_ */ diff --git a/tool/build/lib/dis.c b/tool/build/lib/dis.c index fd292bc53..1e7405be9 100644 --- a/tool/build/lib/dis.c +++ b/tool/build/lib/dis.c @@ -33,7 +33,9 @@ #include "libc/str/tpencode.h" #include "third_party/xed/x86.h" #include "tool/build/lib/case.h" +#include "tool/build/lib/demangle.h" #include "tool/build/lib/dis.h" +#include "tool/build/lib/high.h" #include "tool/build/lib/memory.h" #include "tool/build/lib/modrm.h" @@ -73,93 +75,99 @@ static char *DisOctets(char *p, const uint8_t *d, size_t n) { } static char *DisByte(char *p, const uint8_t *d, size_t n) { - if (g_dis_high) p = DisHigh(p, g_dis_high->keyword); + p = HighStart(p, g_high.keyword); p = DisColumn(stpcpy(p, ".byte"), p, NAMELEN); - if (g_dis_high) p = DisHigh(p, -1); + p = HighEnd(p); p = DisOctets(p, d, n); return p; } -static char *DisError(struct DisBuilder b, char *p) { - p = DisColumn(DisByte(p, b.xedd->bytes, MIN(15, b.xedd->length)), p, CODELEN); - if (g_dis_high) p = DisHigh(p, g_dis_high->comment); +static char *DisError(struct Dis *d, char *p) { + p = DisColumn(DisByte(p, d->xedd->bytes, MIN(15, d->xedd->length)), p, + CODELEN); + p = HighStart(p, g_high.comment); *p++ = '#'; *p++ = ' '; - p = stpcpy(p, indexdoublenulstring(kXedErrorNames, b.xedd->op.error)); - if (g_dis_high) p = DisHigh(p, -1); + p = stpcpy(p, indexdoublenulstring(kXedErrorNames, d->xedd->op.error)); + p = HighEnd(p); *p = '\0'; return p; } -static char *DisAddr(struct DisBuilder b, char *p) { - if (INT_MIN <= b.addr && b.addr <= INT_MAX) { - return p + uint64toarray_fixed16(b.addr, p, 32); +static char *DisAddr(struct Dis *d, char *p) { + if (INT32_MIN <= d->addr && d->addr <= INT32_MAX) { + return p + uint64toarray_fixed16(d->addr, p, 32); } else { - return p + uint64toarray_fixed16(b.addr, p, 48); + return p + uint64toarray_fixed16(d->addr, p, 48); } } -static char *DisRaw(struct DisBuilder b, char *p) { +static char *DisRaw(struct Dis *d, char *p) { long i; - for (i = 0; i < PFIXLEN - MIN(PFIXLEN, b.xedd->op.PIVOTOP); ++i) { + for (i = 0; i < PFIXLEN - MIN(PFIXLEN, d->xedd->op.PIVOTOP); ++i) { *p++ = ' '; *p++ = ' '; } - for (i = 0; i < MIN(15, b.xedd->length); ++i) { - if (i == b.xedd->op.PIVOTOP) *p++ = ' '; - *p++ = "0123456789abcdef"[(b.xedd->bytes[i] & 0xf0) >> 4]; - *p++ = "0123456789abcdef"[b.xedd->bytes[i] & 0x0f]; + for (i = 0; i < MIN(15, d->xedd->length); ++i) { + if (i == d->xedd->op.PIVOTOP) *p++ = ' '; + *p++ = "0123456789abcdef"[(d->xedd->bytes[i] & 0xf0) >> 4]; + *p++ = "0123456789abcdef"[d->xedd->bytes[i] & 0x0f]; } *p = '\0'; return p; } -static char *DisCode(struct DisBuilder b, char *p) { +static char *DisCode(struct Dis *d, char *p) { char optspecbuf[128]; - if (!b.xedd->op.error) { - return DisInst(b, p, DisSpec(b.xedd, optspecbuf)); + if (!d->xedd->op.error) { + return DisInst(d, p, DisSpec(d->xedd, optspecbuf)); } else { - return DisError(b, p); + return DisError(d, p); } } -static char *DisLineCode(struct DisBuilder b, char *p) { - p = DisColumn(DisAddr(b, p), p, ADDRLEN); - p = DisColumn(DisRaw(b, p), p, PFIXLEN * 2 + 1 + BYTELEN * 2); - p = DisCode(b, p); +static char *DisLineCode(struct Dis *d, char *p) { + p = DisColumn(DisAddr(d, p), p, ADDRLEN); + p = DisColumn(DisRaw(d, p), p, PFIXLEN * 2 + 1 + BYTELEN * 2); + p = DisCode(d, p); return p; } -static char *DisLineData(struct DisBuilder b, char *p, const uint8_t *d, - size_t n) { +static char *DisLineData(struct Dis *d, char *p, const uint8_t *b, size_t n) { size_t i; - p = DisColumn(DisAddr(b, p), p, ADDRLEN); - p = DisColumn(DisByte(p, d, n), p, 64); - if (g_dis_high) p = DisHigh(p, g_dis_high->comment); + p = DisColumn(DisAddr(d, p), p, ADDRLEN); + p = DisColumn(DisByte(p, b, n), p, 64); + p = HighStart(p, g_high.comment); *p++ = '#'; *p++ = ' '; - for (i = 0; i < n; ++i) p += tpencode(p, 8, bing(d[i], 0), false); - if (g_dis_high) p = DisHigh(p, -1); + for (i = 0; i < n; ++i) p += tpencode(p, 8, bing(b[i], 0), false); + p = HighEnd(p); *p = '\0'; return p; } -static char *DisLabel(struct DisBuilder b, char *p, const char *name) { - p = DisColumn(DisAddr(b, p), p, ADDRLEN); - if (g_dis_high) p = DisHigh(p, g_dis_high->label); - p = stpcpy(p, name); - if (g_dis_high) p = DisHigh(p, -1); +static char *DisLabel(struct Dis *d, char *p, const char *name) { + p = DisColumn(DisAddr(d, p), p, ADDRLEN); + p = HighStart(p, g_high.label); + p = Demangle(p, name); + p = HighEnd(p); *p++ = ':'; *p = '\0'; return p; } long DisFind(struct Dis *d, int64_t addr) { - long i; - for (i = 0; i < d->ops.i; ++i) { - if (addr >= d->ops.p[i].addr && - addr < d->ops.p[i].addr + d->ops.p[i].size) { - return i; + int l, r, m, i; + l = 0; + r = d->ops.i - 1; + while (l <= r) { + m = (l + r) >> 1; + if (d->ops.p[m].addr < addr) { + l = m + 1; + } else if (d->ops.p[m].addr > addr) { + r = m - 1; + } else { + return m; } } return -1; @@ -181,8 +189,8 @@ static long DisOne(struct Dis *d, struct Machine *m, int64_t addr) { op.addr = addr; op.size = 0; op.active = true; - DisLabel((struct DisBuilder){d, d->xedd, addr}, d->buf, - d->syms.stab + d->syms.p[symbol].name); + d->addr = addr; + DisLabel(d, d->buf, d->syms.stab + d->syms.p[symbol].name); if (!(op.s = strdup(d->buf))) return -1; APPEND(&d->ops.p, &d->ops.i, &d->ops.n, &op); } @@ -238,7 +246,8 @@ const char *DisGetLine(struct Dis *d, struct Machine *m, size_t i) { xed_instruction_length_decode( d->xedd, AccessRam(m, d->ops.p[i].addr, d->ops.p[i].size, r, b, true), d->ops.p[i].size); - p = DisLineCode((struct DisBuilder){d, d->xedd, d->ops.p[i].addr}, d->buf); + d->addr = d->ops.p[i].addr; + p = DisLineCode(d, d->buf); CHECK_LT(p - d->buf, sizeof(d->buf)); return d->buf; } diff --git a/tool/build/lib/dis.h b/tool/build/lib/dis.h index 08dd90682..65f05e7ab 100644 --- a/tool/build/lib/dis.h +++ b/tool/build/lib/dis.h @@ -45,25 +45,10 @@ struct Dis { } * p; } edges; struct XedDecodedInst xedd[1]; + uint64_t addr; char buf[512]; }; -struct DisBuilder { - struct Dis *dis; - struct XedDecodedInst *xedd; - int64_t addr; -}; - -struct DisHigh { - uint8_t keyword; - uint8_t reg; - uint8_t literal; - uint8_t label; - uint8_t comment; -}; - -extern struct DisHigh *g_dis_high; - long Dis(struct Dis *, struct Machine *, int64_t, int); long DisFind(struct Dis *, int64_t); void DisFree(struct Dis *); @@ -74,10 +59,9 @@ long DisFindSym(struct Dis *, int64_t); long DisFindSymByName(struct Dis *, const char *); bool DisIsText(struct Dis *, int64_t); bool DisIsProg(struct Dis *, int64_t); +char *DisInst(struct Dis *, char *, const char *); +char *DisArg(struct Dis *, char *, const char *); const char *DisSpec(struct XedDecodedInst *, char *); -char *DisInst(struct DisBuilder, char *, const char *); -char *DisArg(struct DisBuilder, char *, const char *); -char *DisHigh(char *, int); const char *DisGetLine(struct Dis *, struct Machine *, size_t); COSMOPOLITAN_C_END_ diff --git a/tool/build/lib/disarg.c b/tool/build/lib/disarg.c index a901398f7..0ba591148 100644 --- a/tool/build/lib/disarg.c +++ b/tool/build/lib/disarg.c @@ -24,23 +24,27 @@ #include "libc/log/check.h" #include "libc/macros.h" #include "libc/str/str.h" +#include "tool/build/lib/demangle.h" #include "tool/build/lib/dis.h" #include "tool/build/lib/endian.h" +#include "tool/build/lib/high.h" #include "tool/build/lib/modrm.h" -static const char kScale[4][4] = {"", ",2", ",4", ",8"}; -static const char kSegName[8][3] = {"es", "cs", "ss", "ds", "fs", "gs"}; -static const char kPuttingOnTheRiz[2][4] = {"eiz", "riz"}; -static const char kPuttingOnTheRip[2][4] = {"eip", "rip"}; +static const char kRiz[2][4] = {"eiz", "riz"}; +static const char kRip[2][4] = {"eip", "rip"}; +static const char kSka[4][4] = {"", ",2", ",4", ",8"}; +static const char kSeg[8][3] = {"es", "cs", "ss", "ds", "fs", "gs"}; +static const char kCtl[8][4] = {"cr0", "wut", "cr2", "cr3", + "cr4", "wut", "wut", "wut"}; -static const char kRegisterName8[2][2][8][5] = { +static const char kBreg[2][2][8][5] = { {{"al", "cl", "dl", "bl", "ah", "ch", "dh", "bh"}, {"al", "cl", "dl", "bl", "spl", "bpl", "sil", "dil"}}, {{"wut", "wut", "wut", "wut", "wut", "wut", "wut", "wut"}, {"r8b", "r9b", "r10b", "r11b", "r12b", "r13b", "r14b", "r15b"}}, }; -static const char kRegisterName[2][2][2][8][5] = { +static const char kGreg[2][2][2][8][5] = { {{{"eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi"}, {"r8d", "r9d", "r10d", "r11d", "r12d", "r13d", "r14d", "r15d"}}, {{"rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi"}, @@ -51,43 +55,39 @@ static const char kRegisterName[2][2][2][8][5] = { {"r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"}}}, }; -static const char kControlName[8][4] = { - "cr0", "wut", "cr2", "cr3", "cr4", "wut", "wut", "wut", -}; - -static int64_t RipRelative(struct DisBuilder b, int64_t d) { - return b.addr + b.xedd->length + d; +static int64_t RipRelative(struct Dis *d, int64_t i) { + return d->addr + d->xedd->length + i; } -static const char *GetAddrReg(struct DisBuilder b, uint32_t rde, uint8_t x, +static const char *GetAddrReg(struct Dis *d, uint32_t rde, uint8_t x, uint8_t r) { - return kRegisterName[Eamode(rde) == XED_MODE_REAL] - [Eamode(rde) == XED_MODE_LONG][x & 1][r & 7]; + return kGreg[Eamode(rde) == XED_MODE_REAL][Eamode(rde) == XED_MODE_LONG] + [x & 1][r & 7]; } static char *DisRegister(char *p, const char *s) { - if (g_dis_high) p = DisHigh(p, g_dis_high->reg); + p = HighStart(p, g_high.reg); *p++ = '%'; p = stpcpy(p, s); - if (g_dis_high) p = DisHigh(p, -1); + p = HighEnd(p); return p; } static char *DisComment(char *p, const char *s) { - if (g_dis_high) p = DisHigh(p, g_dis_high->comment); + p = HighStart(p, g_high.comment); p = stpcpy(p, s); - if (g_dis_high) p = DisHigh(p, -1); + p = HighEnd(p); return p; } -static char *DisRegisterByte(struct DisBuilder b, uint32_t rde, char *p, bool g, +static char *DisRegisterByte(struct Dis *d, uint32_t rde, char *p, bool g, int r) { - return DisRegister(p, kRegisterName8[g][Rex(rde)][r]); + return DisRegister(p, kBreg[g][Rex(rde)][r]); } -static char *DisRegisterWord(struct DisBuilder b, uint32_t rde, char *p, bool g, +static char *DisRegisterWord(struct Dis *d, uint32_t rde, char *p, bool g, int r) { - return DisRegister(p, kRegisterName[Osz(rde)][Rexw(rde)][g][r]); + return DisRegister(p, kGreg[Osz(rde)][Rexw(rde)][g][r]); } static char *DisInt(char *p, int64_t x) { @@ -106,14 +106,14 @@ static char *DisInt(char *p, int64_t x) { return p; } -static char *DisSym(struct DisBuilder b, char *p, int64_t addr) { +static char *DisSym(struct Dis *d, char *p, int64_t addr) { long sym; int64_t addend; const char *name; - if ((sym = DisFindSym(b.dis, addr)) != -1 && b.dis->syms.p[sym].name) { - addend = addr - b.dis->syms.p[sym].addr; - name = b.dis->syms.stab + b.dis->syms.p[sym].name; - p = stpcpy(p, name); + if ((sym = DisFindSym(d, addr)) != -1 && d->syms.p[sym].name) { + addend = addr - d->syms.p[sym].addr; + name = d->syms.stab + d->syms.p[sym].name; + p = Demangle(p, name); if (addend) { *p++ = '+'; p = DisInt(p, addend); @@ -124,79 +124,69 @@ static char *DisSym(struct DisBuilder b, char *p, int64_t addr) { } } -static char *DisSymLiteral(struct DisBuilder b, char *p, uint64_t x) { +static char *DisSymLiteral(struct Dis *d, char *p, uint64_t x) { *p++ = '$'; - if (g_dis_high) p = DisHigh(p, g_dis_high->literal); - p = DisSym(b, p, x); - if (g_dis_high) p = DisHigh(p, -1); + p = HighStart(p, g_high.literal); + p = DisSym(d, p, x); + p = HighEnd(p); return p; } -static char *DisXmm(struct DisBuilder b, uint32_t rde, char *p, const char *s, - int reg) { - if (g_dis_high) p = DisHigh(p, g_dis_high->reg); - *p++ = '%'; - p = stpcpy(p, s); - p += uint64toarray_radix10(Rexr(rde) << 3 | reg, p); - if (g_dis_high) p = DisHigh(p, -1); - return p; +static char *DisGvqp(struct Dis *d, uint32_t rde, char *p) { + return DisRegisterWord(d, rde, p, Rexr(rde), ModrmReg(rde)); } -static char *DisGvqp(struct DisBuilder b, uint32_t rde, char *p) { - return DisRegisterWord(b, rde, p, Rexr(rde), ModrmReg(rde)); +static char *DisGdqp(struct Dis *d, uint32_t rde, char *p) { + return DisRegister(p, kGreg[0][Rexw(rde)][Rexr(rde)][ModrmReg(rde)]); } -static char *DisGdqp(struct DisBuilder b, uint32_t rde, char *p) { - return DisRegister(p, kRegisterName[0][Rexw(rde)][Rexr(rde)][ModrmReg(rde)]); +static char *DisGb(struct Dis *d, uint32_t rde, char *p) { + return DisRegisterByte(d, rde, p, Rexr(rde), ModrmReg(rde)); } -static char *DisGb(struct DisBuilder b, uint32_t rde, char *p) { - return DisRegisterByte(b, rde, p, Rexr(rde), ModrmReg(rde)); -} - -static char *DisSego(struct DisBuilder b, uint32_t rde, char *p) { +static char *DisSego(struct Dis *d, uint32_t rde, char *p) { int seg; - seg = Sego(rde) ? Sego(rde) : b.xedd->op.hint; + seg = Sego(rde) ? Sego(rde) : d->xedd->op.hint; if (seg) { - p = DisRegister(p, kSegName[seg - 1]); + p = DisRegister(p, kSeg[seg - 1]); *p++ = ':'; } return p; } -static char *DisM(struct DisBuilder b, uint32_t rde, char *p) { +static char *DisM(struct Dis *d, uint32_t rde, char *p) { int64_t disp; const char *base, *index, *scale; - p = DisSego(b, rde, p); + p = DisSego(d, rde, p); base = index = scale = NULL; if (ModrmMod(rde) == 0b01 || ModrmMod(rde) == 0b10 || IsRipRelative(rde) || (Eamode(rde) == XED_MODE_REAL && ModrmRm(rde) == 6 && !ModrmMod(rde)) || (ModrmMod(rde) == 0b00 && ModrmRm(rde) == 0b100 && - SibBase(b.xedd) == 0b101)) { - disp = b.xedd->op.disp; - if (IsRipRelative(rde)) disp = RipRelative(b, disp); - p = DisSym(b, p, disp); + SibBase(d->xedd) == 0b101)) { + disp = d->xedd->op.disp; + if (IsRipRelative(rde)) disp = RipRelative(d, disp); + p = DisSym(d, p, disp); } if (Eamode(rde) != XED_MODE_REAL) { if (!SibExists(rde)) { - DCHECK(!b.xedd->op.has_sib); + DCHECK(!d->xedd->op.has_sib); if (IsRipRelative(rde)) { if (Mode(rde) == XED_MODE_LONG) { - base = kPuttingOnTheRip[Eamode(rde) == XED_MODE_LONG]; + base = kRip[Eamode(rde) == XED_MODE_LONG]; } } else { - base = GetAddrReg(b, rde, Rexb(rde), ModrmRm(rde)); + base = GetAddrReg(d, rde, Rexb(rde), ModrmRm(rde)); } - } else if (!SibIsAbsolute(b.xedd, rde)) { - if (SibHasBase(b.xedd, rde)) { - base = GetAddrReg(b, rde, Rexb(rde), SibBase(b.xedd)); + } else if (!SibIsAbsolute(d->xedd, rde)) { + if (SibHasBase(d->xedd, rde)) { + base = GetAddrReg(d, rde, Rexb(rde), SibBase(d->xedd)); } - if (SibHasIndex(b.xedd)) { - index = GetAddrReg(b, rde, Rexx(b.xedd), SibIndex(b.xedd)); - } else if (b.xedd->op.scale) { - index = kPuttingOnTheRiz[Eamode(rde) == XED_MODE_LONG]; + if (SibHasIndex(d->xedd)) { + index = GetAddrReg(d, rde, Rexx(d->xedd), SibIndex(d->xedd)); + } else if (d->xedd->op.scale) { + index = kRiz[Eamode(rde) == XED_MODE_LONG]; } - scale = kScale[b.xedd->op.scale]; + scale = kSka[d->xedd->op.scale]; } } else { switch (ModrmRm(rde)) { @@ -250,206 +240,235 @@ static char *DisM(struct DisBuilder b, uint32_t rde, char *p) { return p; } -static char *DisEb(struct DisBuilder b, uint32_t rde, char *p) { +static char *DisRegMem(struct Dis *d, uint32_t rde, char *p, + char *f(struct Dis *, uint32_t, char *)) { if (IsModrmRegister(rde)) { - return DisRegisterByte(b, rde, p, Rexb(rde), ModrmRm(rde)); + return f(d, rde, p); } else { - return DisM(b, rde, p); + return DisM(d, rde, p); } } -static char *DisEvqp(struct DisBuilder b, uint32_t rde, char *p) { +static noinline char *DisE(struct Dis *d, uint32_t rde, char *p, + char *f(struct Dis *, uint32_t, char *, bool, int)) { if (IsModrmRegister(rde)) { - return DisRegisterWord(b, rde, p, Rexb(rde), ModrmRm(rde)); + return f(d, rde, p, Rexb(rde), ModrmRm(rde)); } else { - return DisM(b, rde, p); + return DisM(d, rde, p); } } -static char *DisEdqp(struct DisBuilder b, uint32_t rde, char *p) { - if (IsModrmRegister(rde)) { - return DisRegister(p, kRegisterName[0][Rexw(rde)][Rexb(rde)][ModrmRm(rde)]); - } else { - return DisM(b, rde, p); - } +static char *DisEb(struct Dis *d, uint32_t rde, char *p) { + return DisE(d, rde, p, DisRegisterByte); } -static char *DisEv(struct DisBuilder b, uint32_t rde, char *p) { - const char *s; - if (IsModrmRegister(rde)) { - return DisRegister(p, kRegisterName[Osz(rde)][0][Rexb(rde)][ModrmRm(rde)]); - } else { - return DisM(b, rde, p); - } +static char *DisEvqp(struct Dis *d, uint32_t rde, char *p) { + return DisE(d, rde, p, DisRegisterWord); } -static char *DisGvq(struct DisBuilder b, uint32_t rde, char *p, int r) { +static char *DisEdqpReg(struct Dis *d, uint32_t rde, char *p) { + return DisRegister(p, kGreg[0][Rexw(rde)][Rexb(rde)][ModrmRm(rde)]); +} + +static char *DisEdqp(struct Dis *d, uint32_t rde, char *p) { + return DisRegMem(d, rde, p, DisEdqpReg); +} + +static char *DisEvReg(struct Dis *d, uint32_t rde, char *p) { + return DisRegister(p, kGreg[Osz(rde)][0][Rexb(rde)][ModrmRm(rde)]); +} + +static char *DisEv(struct Dis *d, uint32_t rde, char *p) { + return DisRegMem(d, rde, p, DisEvReg); +} + +static char *DisGvq(struct Dis *d, uint32_t rde, char *p, int r) { const char *s; if (Mode(rde) == XED_MODE_LONG) { - s = kRegisterName[Osz(rde)][!Osz(rde)][Rexb(rde)][r]; + s = kGreg[Osz(rde)][!Osz(rde)][Rexb(rde)][r]; } else { - s = kRegisterName[Osz(rde)][0][Rexb(rde)][r]; + s = kGreg[Osz(rde)][0][Rexb(rde)][r]; } return DisRegister(p, s); } -static char *DisZvq(struct DisBuilder b, uint32_t rde, char *p) { - return DisGvq(b, rde, p, ModrmSrm(rde)); +static char *DisZvq(struct Dis *d, uint32_t rde, char *p) { + return DisGvq(d, rde, p, ModrmSrm(rde)); } -static char *DisEvq(struct DisBuilder b, uint32_t rde, char *p) { - if (IsModrmRegister(rde)) { - return DisGvq(b, rde, p, ModrmRm(rde)); +static char *DisEvqReg(struct Dis *d, uint32_t rde, char *p) { + return DisGvq(d, rde, p, ModrmRm(rde)); +} + +static char *DisEvq(struct Dis *d, uint32_t rde, char *p) { + return DisRegMem(d, rde, p, DisEvqReg); +} + +static char *DisEdReg(struct Dis *d, uint32_t rde, char *p) { + return DisRegister(p, kGreg[0][0][Rexb(rde)][ModrmRm(rde)]); +} + +static char *DisEd(struct Dis *d, uint32_t rde, char *p) { + return DisRegMem(d, rde, p, DisEdReg); +} + +static char *DisEqReg(struct Dis *d, uint32_t rde, char *p) { + const char *r; + if (Mode(rde) == XED_MODE_LONG) { + r = kGreg[0][1][Rexb(rde)][ModrmRm(rde)]; } else { - return DisM(b, rde, p); + r = kGreg[Osz(rde)][0][Rexb(rde)][ModrmRm(rde)]; } + return DisRegister(p, r); } -static char *DisEd(struct DisBuilder b, uint32_t rde, char *p) { - if (IsModrmRegister(rde)) { - return DisRegister(p, kRegisterName[0][0][Rexb(rde)][ModrmRm(rde)]); - } else { - return DisM(b, rde, p); - } +static char *DisEq(struct Dis *d, uint32_t rde, char *p) { + return DisRegMem(d, rde, p, DisEqReg); } -static char *DisEq(struct DisBuilder b, uint32_t rde, char *p) { - if (IsModrmRegister(rde)) { - return DisRegister(p, kRegisterName[0][1][Rexb(rde)][ModrmRm(rde)]); - } else { - return DisM(b, rde, p); - } +static char *DisZvqp(struct Dis *d, uint32_t rde, char *p) { + return DisRegisterWord(d, rde, p, Rexb(rde), ModrmSrm(rde)); } -static char *DisZvqp(struct DisBuilder b, uint32_t rde, char *p) { - return DisRegisterWord(b, rde, p, Rexb(rde), ModrmSrm(rde)); +static char *DisZb(struct Dis *d, uint32_t rde, char *p) { + return DisRegisterByte(d, rde, p, Rexb(rde), ModrmSrm(rde)); } -static char *DisZb(struct DisBuilder b, uint32_t rde, char *p) { - return DisRegisterByte(b, rde, p, Rexb(rde), ModrmSrm(rde)); +static char *DisEax(struct Dis *d, uint32_t rde, char *p) { + return DisRegister(p, kGreg[Osz(rde)][0][0][0]); } -static char *DisEax(struct DisBuilder b, uint32_t rde, char *p) { - return DisRegister(p, kRegisterName[Osz(rde)][0][0][0]); +static char *DisRax(struct Dis *d, uint32_t rde, char *p) { + return DisRegister(p, kGreg[Osz(rde)][Rexw(rde)][0][0]); } -static char *DisRax(struct DisBuilder b, uint32_t rde, char *p) { - return DisRegister(p, kRegisterName[Osz(rde)][Rexw(rde)][0][0]); +static char *DisRdx(struct Dis *d, uint32_t rde, char *p) { + return DisRegister(p, kGreg[Osz(rde)][Rexw(rde)][0][2]); } -static char *DisRdx(struct DisBuilder b, uint32_t rde, char *p) { - return DisRegister(p, kRegisterName[Osz(rde)][Rexw(rde)][0][2]); +static char *DisCd(struct Dis *d, uint32_t rde, char *p) { + return DisRegister(p, kCtl[ModrmReg(rde)]); } -static char *DisCd(struct DisBuilder b, uint32_t rde, char *p) { - return DisRegister(p, kControlName[ModrmReg(rde)]); +static char *DisHd(struct Dis *d, uint32_t rde, char *p) { + return DisRegister(p, kGreg[0][Mode(rde) == XED_MODE_LONG][0][ModrmRm(rde)]); } -static char *DisHd(struct DisBuilder b, uint32_t rde, char *p) { - return DisRegister( - p, kRegisterName[0][Mode(rde) == XED_MODE_LONG][0][ModrmRm(rde)]); +static char *DisImm(struct Dis *d, uint32_t rde, char *p) { + return DisSymLiteral(d, p, d->xedd->op.uimm0); } -static char *DisImm(struct DisBuilder b, uint32_t rde, char *p) { - return DisSymLiteral(b, p, b.xedd->op.uimm0); +static char *DisRvds(struct Dis *d, uint32_t rde, char *p) { + return DisSymLiteral(d, p, d->xedd->op.disp); } -static char *DisRvds(struct DisBuilder b, uint32_t rde, char *p) { - return DisSymLiteral(b, p, b.xedd->op.disp); -} - -static char *DisKvds(struct DisBuilder b, uint32_t rde, char *p) { +static char *DisKpvds(struct Dis *d, uint32_t rde, char *p, uint64_t x) { *p++ = '$'; - if (g_dis_high) p = DisHigh(p, g_dis_high->literal); - p = DisInt(p, b.xedd->op.uimm0); - if (g_dis_high) p = DisHigh(p, -1); + p = HighStart(p, g_high.literal); + p = DisInt(p, x); + p = HighEnd(p); return p; } -static char *DisOne(struct DisBuilder b, uint32_t rde, char *p) { +static char *DisKvds(struct Dis *d, uint32_t rde, char *p) { + return DisKpvds(d, rde, p, d->xedd->op.uimm0); +} + +static char *DisPvds(struct Dis *d, uint32_t rde, char *p) { + return DisKpvds(d, rde, p, + d->xedd->op.disp & (Osz(rde) ? 0xffff : 0xffffffff)); +} + +static char *DisOne(struct Dis *d, uint32_t rde, char *p) { *p++ = '$'; - if (g_dis_high) p = DisHigh(p, g_dis_high->literal); + p = HighStart(p, g_high.literal); p = stpcpy(p, "1"); - if (g_dis_high) p = DisHigh(p, -1); + p = HighEnd(p); return p; } -static char *DisJbs(struct DisBuilder b, uint32_t rde, char *p) { - if (b.xedd->op.disp > 0) *p++ = '+'; - p += int64toarray_radix10(b.xedd->op.disp, p); +static char *DisJbs(struct Dis *d, uint32_t rde, char *p) { + if (d->xedd->op.disp > 0) *p++ = '+'; + p += int64toarray_radix10(d->xedd->op.disp, p); return p; } -static char *DisJb(struct DisBuilder b, uint32_t rde, char *p) { - if (b.xedd->op.disp > 0) *p++ = '+'; - p += uint64toarray_radix10(b.xedd->op.disp & 0xff, p); +static char *DisJb(struct Dis *d, uint32_t rde, char *p) { + if (d->xedd->op.disp > 0) *p++ = '+'; + p += uint64toarray_radix10(d->xedd->op.disp & 0xff, p); return p; } -static char *DisJvds(struct DisBuilder b, uint32_t rde, char *p) { - return DisSym(b, p, RipRelative(b, b.xedd->op.disp)); +static char *DisJvds(struct Dis *d, uint32_t rde, char *p) { + return DisSym(d, p, RipRelative(d, d->xedd->op.disp)); } -static char *DisAbs(struct DisBuilder b, uint32_t rde, char *p) { - return DisSym(b, p, b.xedd->op.disp); +static char *DisAbs(struct Dis *d, uint32_t rde, char *p) { + return DisSym(d, p, d->xedd->op.disp); } -static char *DisSw(struct DisBuilder b, uint32_t rde, char *p) { - if (kSegName[ModrmReg(rde)][0]) p = DisRegister(p, kSegName[ModrmReg(rde)]); +static char *DisSw(struct Dis *d, uint32_t rde, char *p) { + if (kSeg[ModrmReg(rde)][0]) p = DisRegister(p, kSeg[ModrmReg(rde)]); return p; } -static char *DisSpecialAddr(struct DisBuilder b, uint32_t rde, char *p, int r) { +static char *DisSpecialAddr(struct Dis *d, uint32_t rde, char *p, int r) { *p++ = '('; - p = DisRegister(p, GetAddrReg(b, rde, 0, r)); + p = DisRegister(p, GetAddrReg(d, rde, 0, r)); *p++ = ')'; *p = '\0'; return p; } -static char *DisY(struct DisBuilder b, uint32_t rde, char *p) { - return DisSpecialAddr(b, rde, p, 7); // es:di +static char *DisY(struct Dis *d, uint32_t rde, char *p) { + return DisSpecialAddr(d, rde, p, 7); // es:di } -static char *DisX(struct DisBuilder b, uint32_t rde, char *p) { - DisSego(b, rde, p); - return DisSpecialAddr(b, rde, p, 6); // ds:si +static char *DisX(struct Dis *d, uint32_t rde, char *p) { + DisSego(d, rde, p); + return DisSpecialAddr(d, rde, p, 6); // ds:si } -static char *DisBBb(struct DisBuilder b, uint32_t rde, char *p) { - DisSego(b, rde, p); - return DisSpecialAddr(b, rde, p, 3); // ds:bx +static char *DisBBb(struct Dis *d, uint32_t rde, char *p) { + DisSego(d, rde, p); + return DisSpecialAddr(d, rde, p, 3); // ds:bx } -static char *DisNq(struct DisBuilder b, uint32_t rde, char *p) { - return DisXmm(b, rde, p, "mm", ModrmRm(rde)); +static char *DisXmm(struct Dis *d, uint32_t rde, char *p, const char *s, + int reg) { + p = HighStart(p, g_high.reg); + *p++ = '%'; + p = stpcpy(p, s); + p += uint64toarray_radix10(reg, p); + p = HighEnd(p); + return p; } -static char *DisUq(struct DisBuilder b, uint32_t rde, char *p) { - return DisXmm(b, rde, p, "mm", ModrmRm(rde)); +static char *DisNq(struct Dis *d, uint32_t rde, char *p) { + return DisXmm(d, rde, p, "mm", ModrmRm(rde)); } -static char *DisPq(struct DisBuilder b, uint32_t rde, char *p) { - return DisXmm(b, rde, p, "mm", ModrmReg(rde)); +static char *DisPq(struct Dis *d, uint32_t rde, char *p) { + return DisXmm(d, rde, p, "mm", ModrmReg(rde)); } -static char *DisUdq(struct DisBuilder b, uint32_t rde, char *p) { - return DisXmm(b, rde, p, "xmm", ModrmRm(rde)); +static char *DisUq(struct Dis *d, uint32_t rde, char *p) { + return DisXmm(d, rde, p, "xmm", RexbRm(rde)); } -static char *DisVdq(struct DisBuilder b, uint32_t rde, char *p) { - return DisXmm(b, rde, p, "xmm", ModrmReg(rde)); +static char *DisUdq(struct Dis *d, uint32_t rde, char *p) { + return DisXmm(d, rde, p, "xmm", RexbRm(rde)); } -static char *DisQq(struct DisBuilder b, uint32_t rde, char *p) { - if (IsModrmRegister(rde)) { - return DisNq(b, rde, p); - } else { - return DisM(b, rde, p); - } +static char *DisVdq(struct Dis *d, uint32_t rde, char *p) { + return DisXmm(d, rde, p, "xmm", RexrReg(rde)); } -static char *DisEst(struct DisBuilder b, uint32_t rde, char *p) { +static char *DisQq(struct Dis *d, uint32_t rde, char *p) { + return DisRegMem(d, rde, p, DisNq); +} + +static char *DisEst(struct Dis *d, uint32_t rde, char *p) { p = DisRegister(p, "st"); if (ModrmRm(rde) != 0) { *p++ = '('; @@ -460,29 +479,21 @@ static char *DisEst(struct DisBuilder b, uint32_t rde, char *p) { return p; } -static char *DisEst1(struct DisBuilder b, uint32_t rde, char *p) { +static char *DisEst1(struct Dis *d, uint32_t rde, char *p) { if (ModrmRm(rde) != 1) { - p = DisEst(b, rde, p); + p = DisEst(d, rde, p); } else { *p = '\0'; } return p; } -static char *DisEssr(struct DisBuilder b, uint32_t rde, char *p) { - if (IsModrmRegister(rde)) { - return DisEst(b, rde, p); - } else { - return DisM(b, rde, p); - } +static char *DisEssr(struct Dis *d, uint32_t rde, char *p) { + return DisRegMem(d, rde, p, DisEst); } -static char *DisWps(struct DisBuilder b, uint32_t rde, char *p) { - if (IsModrmRegister(rde)) { - return DisUdq(b, rde, p); - } else { - return DisM(b, rde, p); - } +static char *DisWps(struct Dis *d, uint32_t rde, char *p) { + return DisRegMem(d, rde, p, DisUdq); } #define DisEdr DisM @@ -537,7 +548,7 @@ static char *DisWps(struct DisBuilder b, uint32_t rde, char *p) { static const struct DisArg { char s[8]; - char *(*f)(struct DisBuilder, uint32_t, char *); + char *(*f)(struct Dis *, uint32_t, char *); } kDisArgs[] = /* */ { {"$1", DisOne}, // {"%Cd", DisCd}, // @@ -610,6 +621,7 @@ static const struct DisArg { {"Mwi", DisMwi}, // {"Ob", DisOb}, // {"Ovqp", DisOvqp}, // + {"Pvds", DisPvds}, // {"Qpi", DisQpi}, // {"Qq", DisQq}, // {"Rvds", DisRvds}, // @@ -635,7 +647,7 @@ static int CompareString8(const char a[8], const char b[8]) { return x > y ? 1 : x < y ? -1 : 0; } -char *DisArg(struct DisBuilder b, char *p, const char *s) { +char *DisArg(struct Dis *d, char *p, const char *s) { char key[8]; int c, m, l, r; l = 0; @@ -649,7 +661,7 @@ char *DisArg(struct DisBuilder b, char *p, const char *s) { } else if (c > 0) { r = m - 1; } else { - return kDisArgs[m].f(b, b.xedd->op.rde, p); + return kDisArgs[m].f(d, d->xedd->op.rde, p); } } if (*s == '%') { diff --git a/tool/build/lib/diself.c b/tool/build/lib/diself.c index c9ae55e9a..afb51bd13 100644 --- a/tool/build/lib/diself.c +++ b/tool/build/lib/diself.c @@ -115,31 +115,33 @@ bool DisIsText(struct Dis *d, int64_t addr) { long DisFindSym(struct Dis *d, int64_t addr) { size_t i, l, r, m, n; - if (DisIsProg(d, addr)) { - l = 0; - r = d->syms.i; - while (l < r) { - m = (l + r) >> 1; - if (d->syms.p[m].addr < addr) { - l = m + 1; - } else { - r = m; + if (d->syms.p) { + if (DisIsProg(d, addr)) { + l = 0; + r = d->syms.i; + while (l < r) { + m = (l + r) >> 1; + if (d->syms.p[m].addr < addr) { + l = m + 1; + } else { + r = m; + } } - } - if (d->syms.p[l].addr == addr) { - return l; - } - l = MAX(0, (long)l - 10); - for (n = 0, i = l; i < d->syms.i && n < 20; ++i, ++n) { - if (addr >= d->syms.p[i].addr && - addr < d->syms.p[i].addr + d->syms.p[i].size) { - return i; + if (d->syms.p[l].addr == addr) { + return l; } - } - for (n = 0, i = l; i < d->syms.i && n < 20; ++i, ++n) { - if (addr >= d->syms.p[i].addr && - (i + 1 == d->syms.i || addr < d->syms.p[i + 1].addr)) { - return i; + l = MAX(0, (long)l - 10); + for (n = 0, i = l; i < d->syms.i && n < 20; ++i, ++n) { + if (addr >= d->syms.p[i].addr && + addr < d->syms.p[i].addr + d->syms.p[i].size) { + return i; + } + } + for (n = 0, i = l; i < d->syms.i && n < 20; ++i, ++n) { + if (addr >= d->syms.p[i].addr && + (i + 1 == d->syms.i || addr < d->syms.p[i + 1].addr)) { + return i; + } } } } diff --git a/tool/build/lib/disinst.c b/tool/build/lib/disinst.c index 1e4d5825d..b52e96785 100644 --- a/tool/build/lib/disinst.c +++ b/tool/build/lib/disinst.c @@ -21,6 +21,7 @@ #include "libc/nexgen32e/tinystrcmp.h" #include "libc/str/str.h" #include "tool/build/lib/dis.h" +#include "tool/build/lib/high.h" #include "tool/build/lib/modrm.h" static const char kJcxz[3][6] = {"jcxz", "jecxz", "jrcxz"}; @@ -28,43 +29,45 @@ static const char kAluOp[8][4] = {"add", "or", "adc", "sbb", "and", "sub", "xor", "cmp"}; static const char kBitOp[8][4] = {"rol", "ror", "rcl", "rcr", "shl", "shr", "sal", "sar"}; +static const char kCc[16][3] = {"o", "no", "b", "ae", "e", "ne", "be", "a", + "s", "ns", "p", "np", "l", "ge", "le", "g"}; static bool IsProbablyByteOp(struct XedDecodedInst *x) { return !(x->op.opcode & 1); } -static int IsRepOpcode(struct DisBuilder b) { - switch (b.xedd->op.opcode & ~1) { - case 0x6C: /* INS */ +static int IsRepOpcode(struct Dis *d) { + switch (d->xedd->op.opcode & ~1) { + case 0x6C: // INS return 1; - case 0x6E: /* OUTS */ + case 0x6E: // OUTS return 1; - case 0xA4: /* MOVS */ + case 0xA4: // MOVS return 1; - case 0xAA: /* STOS */ + case 0xAA: // STOS return 1; - case 0xAC: /* LODS */ + case 0xAC: // LODS return 1; - case 0xA6: /* CMPS */ + case 0xA6: // CMPS return 2; - case 0xAE: /* SCAS */ + case 0xAE: // SCAS return 2; default: return 0; } } -static char *DisRepPrefix(struct DisBuilder b, char *p) { +static char *DisRepPrefix(struct Dis *d, char *p) { const char *s; - if (Rep(b.xedd->op.rde) && b.xedd->op.map == XED_ILD_MAP0) { - switch (IsRepOpcode(b)) { + if (Rep(d->xedd->op.rde) && d->xedd->op.map == XED_ILD_MAP0) { + switch (IsRepOpcode(d)) { case 0: break; case 1: p = stpcpy(p, "rep "); break; case 2: - p = stpcpy(p, Rep(b.xedd->op.rde) == 2 ? "repnz " : "repz "); + p = stpcpy(p, Rep(d->xedd->op.rde) == 2 ? "repnz " : "repz "); break; default: break; @@ -73,8 +76,8 @@ static char *DisRepPrefix(struct DisBuilder b, char *p) { return p; } -static char *DisBranchTaken(struct DisBuilder b, char *p) { - switch (b.xedd->op.hint) { +static char *DisBranchTaken(struct Dis *d, char *p) { + switch (d->xedd->op.hint) { case XED_HINT_NTAKEN: return stpcpy(p, ",pn"); case XED_HINT_TAKEN: @@ -84,24 +87,26 @@ static char *DisBranchTaken(struct DisBuilder b, char *p) { } } -static char *DisName(struct DisBuilder b, char *bp, const char *name, +static char *DisName(struct Dis *d, char *bp, const char *name, bool ambiguous) { char *p, *np; uint32_t rde; bool notbyte, notlong, wantsuffix, wantsuffixsd; p = bp; - rde = b.xedd->op.rde; - if (b.xedd->op.lock) p = stpcpy(p, "lock "); - p = DisRepPrefix(b, p); + rde = d->xedd->op.rde; + if (d->xedd->op.lock) p = stpcpy(p, "lock "); + p = DisRepPrefix(d, p); if (tinystrcmp(name, "BIT") == 0) { p = stpcpy(p, kBitOp[ModrmReg(rde)]); + } else if (tinystrcmp(name, "nop") == 0 && d->xedd->op.rep) { + p = stpcpy(p, "pause"); } else if (tinystrcmp(name, "CALL") == 0) { p = stpcpy(p, "call"); } else if (tinystrcmp(name, "JMP") == 0) { p = stpcpy(p, "jmp"); } else if (tinystrcmp(name, "jcxz") == 0) { p = stpcpy(p, kJcxz[Eamode(rde)]); - p = DisBranchTaken(b, p); + p = DisBranchTaken(d, p); } else if (tinystrcmp(name, "loop") == 0 || tinystrcmp(name, "loope") == 0 || tinystrcmp(name, "loopne") == 0) { p = stpcpy(p, name); @@ -109,7 +114,7 @@ static char *DisName(struct DisBuilder b, char *bp, const char *name, *p++ = "wl"[Eamode(rde)]; *p = '\0'; } - p = DisBranchTaken(b, p); + p = DisBranchTaken(d, p); } else if (tinystrcmp(name, "cwtl") == 0) { if (Osz(rde)) name = "cbtw"; if (Rexw(rde)) name = "cltq"; @@ -127,10 +132,15 @@ static char *DisName(struct DisBuilder b, char *bp, const char *name, *p++ = *np; } if (tinystrcmp(name, "ALU") == 0) { + p = stpcpy(p, kAluOp[(d->xedd->op.opcode & 070) >> 3]); + } else if (tinystrcmp(name, "ALU2") == 0) { p = stpcpy(p, kAluOp[ModrmReg(rde)]); } else if (tinystrcmp(np, "WLQ") == 0) { notbyte = true; wantsuffix = true; + } else if (tinystrcmp(np, "CC") == 0) { + p = stpcpy(p, kCc[d->xedd->op.opcode & 15]); + p = DisBranchTaken(d, p); } else if (tinystrcmp(np, "WQ") == 0) { notbyte = true; notlong = Eamode(rde) != XED_MODE_REAL; @@ -143,8 +153,6 @@ static char *DisName(struct DisBuilder b, char *bp, const char *name, wantsuffixsd = true; } else if (tinystrcmp(np, "ABS") == 0) { if (Rexw(rde)) p = stpcpy(p, "abs"); - } else if (tinystrcmp(np, "BT") == 0) { - p = DisBranchTaken(b, p); } if (wantsuffixsd) { if (Osz(rde)) { @@ -155,12 +163,12 @@ static char *DisName(struct DisBuilder b, char *bp, const char *name, } else if (wantsuffix || (ambiguous && !startswith(name, "f") && !startswith(name, "set"))) { if (Osz(rde)) { - if (Eamode(rde) != XED_MODE_REAL) { + if (Mode(rde) != XED_MODE_REAL) { *p++ = 'w'; } } else if (Rexw(rde)) { *p++ = 'q'; - } else if (ambiguous && !notbyte && IsProbablyByteOp(b.xedd)) { + } else if (ambiguous && !notbyte && IsProbablyByteOp(d->xedd)) { *p++ = 'b'; } else if (!notlong) { *p++ = 'l'; @@ -177,28 +185,28 @@ static char *DisName(struct DisBuilder b, char *bp, const char *name, * Disassembles instruction based on string spec. * @see DisSpec() */ -char *DisInst(struct DisBuilder b, char *p, const char *spec) { +char *DisInst(struct Dis *d, char *p, const char *spec) { long i, n; - char sbuf[256]; - char args[4][128]; + char sbuf[300]; + char args[4][300]; char *s, *name, *state; bool hasarg, hasmodrm, hasregister, hasmemory; - CHECK_EQ(0, (int)b.xedd->op.error); + CHECK_EQ(0, (int)d->xedd->op.error); DCHECK_LT(strlen(spec), 128); hasarg = false; - hasmodrm = b.xedd->op.has_modrm; - hasmemory = hasmodrm && !IsModrmRegister(b.xedd->op.rde); - hasregister = hasmodrm && IsModrmRegister(b.xedd->op.rde); + hasmodrm = d->xedd->op.has_modrm; + hasmemory = hasmodrm && !IsModrmRegister(d->xedd->op.rde); + hasregister = hasmodrm && IsModrmRegister(d->xedd->op.rde); name = strtok_r(strcpy(sbuf, spec), " ", &state); for (n = 0; (s = strtok_r(NULL, " ", &state)); ++n) { hasarg = true; hasregister |= *s == '%'; hasmemory |= *s == 'O'; - CHECK_LT(DisArg(b, args[n], s) - args[n], sizeof(args[n])); + CHECK_LT(DisArg(d, args[n], s) - args[n], sizeof(args[n])); } - if (g_dis_high) p = DisHigh(p, g_dis_high->keyword); - p = DisName(b, p, name, hasarg && !hasregister && hasmemory); - if (g_dis_high) p = DisHigh(p, -1); + p = HighStart(p, g_high.keyword); + p = DisName(d, p, name, hasarg && !hasregister && hasmemory); + p = HighEnd(p); for (i = 0; i < n; ++i) { if (i && args[n - i][0]) { *p++ = ','; diff --git a/tool/build/lib/disspec.c b/tool/build/lib/disspec.c index 9e363c75f..85eb957ec 100644 --- a/tool/build/lib/disspec.c +++ b/tool/build/lib/disspec.c @@ -36,12 +36,6 @@ static const char kFpuName[][8][8] = { {"fneni", "fndisi", "fnclex", "fninit", "fnsetpm"}, }; -char *DisOpFpu0(struct XedDecodedInst *x, int group) { - const char *s; - s = kFpuName[group][ModrmRm(x->op.rde)]; - return *s ? s : UNKNOWN; -} - char *DisOpFpu1(struct XedDecodedInst *x, char *p, const char *extra) { stpcpy(stpcpy(p, kFpuName[0][ModrmReg(x->op.rde)]), extra); return p; @@ -88,68 +82,88 @@ char *DisOpVpsWpsVssWssVpdWpdVsdWsd(struct XedDecodedInst *x, char *p, return p; } +const char *DisSpecFpu0(struct XedDecodedInst *x, int group) { + const char *s; + s = kFpuName[group][ModrmRm(x->op.rde)]; + return *s ? s : UNKNOWN; +} + +const char *DisSpecRegMem(struct XedDecodedInst *x, const char *a, + const char *b) { + if (IsModrmRegister(x->op.rde)) { + return a; + } else { + return b; + } +} + +const char *DisSpecRegMemFpu0(struct XedDecodedInst *x, int group, + const char *b) { + return DisSpecRegMem(x, DisSpecFpu0(x, group), b); +} + const char *DisSpecMap0(struct XedDecodedInst *x, char *p) { switch (x->op.opcode & 0xff) { - RCASE(0x00, "add Eb %Gb"); - RCASE(0x01, "add Evqp %Gvqp"); - RCASE(0x02, "add %Gb Eb"); - RCASE(0x03, "add %Gvqp Evqp"); - RCASE(0x04, "add %al Ib"); - RCASE(0x05, "add %rAX Ivds"); + RCASE(0x00, "ALU Eb %Gb"); + RCASE(0x01, "ALU Evqp %Gvqp"); + RCASE(0x02, "ALU %Gb Eb"); + RCASE(0x03, "ALU %Gvqp Evqp"); + RCASE(0x04, "ALU %al Ib"); + RCASE(0x05, "ALU %rAX Ivds"); RCASE(0x06, "push %es"); RCASE(0x07, "pop %es"); - RCASE(0x08, "or Eb %Gb"); - RCASE(0x09, "or Evqp %Gvqp"); - RCASE(0x0a, "or %Gb Eb"); - RCASE(0x0b, "or %Gvqp Evqp"); - RCASE(0x0c, "or %al Ib"); - RCASE(0x0d, "or %rAX Ivds"); + RCASE(0x08, "ALU Eb %Gb"); + RCASE(0x09, "ALU Evqp %Gvqp"); + RCASE(0x0a, "ALU %Gb Eb"); + RCASE(0x0b, "ALU %Gvqp Evqp"); + RCASE(0x0c, "ALU %al Ib"); + RCASE(0x0d, "ALU %rAX Ivds"); RCASE(0x0e, "push %cs"); RCASE(0x0f, "pop %cs"); - RCASE(0x10, "adc Eb %Gb"); - RCASE(0x11, "adc Evqp %Gvqp"); - RCASE(0x12, "adc %Gb Eb"); - RCASE(0x13, "adc %Gvqp Evqp"); - RCASE(0x14, "adc %al Ib"); - RCASE(0x15, "adc %rAX Ivds"); + RCASE(0x10, "ALU Eb %Gb"); + RCASE(0x11, "ALU Evqp %Gvqp"); + RCASE(0x12, "ALU %Gb Eb"); + RCASE(0x13, "ALU %Gvqp Evqp"); + RCASE(0x14, "ALU %al Ib"); + RCASE(0x15, "ALU %rAX Ivds"); RCASE(0x16, "push %ss"); RCASE(0x17, "pop %ss"); - RCASE(0x18, "sbb Eb %Gb"); - RCASE(0x19, "sbb Evqp %Gvqp"); - RCASE(0x1a, "sbb %Gb Eb"); - RCASE(0x1b, "sbb %Gvqp Evqp"); - RCASE(0x1c, "sbb %al Ib"); - RCASE(0x1d, "sbb %rAX Ivds"); + RCASE(0x18, "ALU Eb %Gb"); + RCASE(0x19, "ALU Evqp %Gvqp"); + RCASE(0x1a, "ALU %Gb Eb"); + RCASE(0x1b, "ALU %Gvqp Evqp"); + RCASE(0x1c, "ALU %al Ib"); + RCASE(0x1d, "ALU %rAX Ivds"); RCASE(0x1e, "push %ds"); RCASE(0x1f, "pop %ds"); - RCASE(0x20, "and Eb %Gb"); - RCASE(0x21, "and Evqp %Gvqp"); - RCASE(0x22, "and %Gb Eb"); - RCASE(0x23, "and %Gvqp Evqp"); - RCASE(0x24, "and %al Ib"); - RCASE(0x25, "and %rAX Ivds"); + RCASE(0x20, "ALU Eb %Gb"); + RCASE(0x21, "ALU Evqp %Gvqp"); + RCASE(0x22, "ALU %Gb Eb"); + RCASE(0x23, "ALU %Gvqp Evqp"); + RCASE(0x24, "ALU %al Ib"); + RCASE(0x25, "ALU %rAX Ivds"); RCASE(0x26, "push %es"); RCASE(0x27, "pop %es"); - RCASE(0x28, "sub Eb %Gb"); - RCASE(0x29, "sub Evqp %Gvqp"); + RCASE(0x28, "ALU Eb %Gb"); + RCASE(0x29, "ALU Evqp %Gvqp"); + RCASE(0x2a, "ALU %Gb Eb"); + RCASE(0x2b, "ALU %Gvqp Evqp"); + RCASE(0x2c, "ALU %al Ib"); + RCASE(0x2d, "ALU %rAX Ivds"); RCASE(0x2F, "das"); - RCASE(0x2a, "sub %Gb Eb"); - RCASE(0x2b, "sub %Gvqp Evqp"); - RCASE(0x2c, "sub %al Ib"); - RCASE(0x2d, "sub %rAX Ivds"); - RCASE(0x30, "xor Eb %Gb"); - RCASE(0x31, "xor Evqp %Gvqp"); - RCASE(0x32, "xor %Gb Eb"); - RCASE(0x33, "xor %Gvqp Evqp"); - RCASE(0x34, "xor %al Ib"); - RCASE(0x35, "xor %rAX Ivds"); + RCASE(0x30, "ALU Eb %Gb"); + RCASE(0x31, "ALU Evqp %Gvqp"); + RCASE(0x32, "ALU %Gb Eb"); + RCASE(0x33, "ALU %Gvqp Evqp"); + RCASE(0x34, "ALU %al Ib"); + RCASE(0x35, "ALU %rAX Ivds"); RCASE(0x37, "aaa"); - RCASE(0x38, "cmp Eb %Gb"); - RCASE(0x39, "cmp Evqp %Gvqp"); - RCASE(0x3A, "cmp %Gb Eb"); - RCASE(0x3B, "cmp %Gvqp Evqp"); - RCASE(0x3C, "cmp %al Ib"); - RCASE(0x3D, "cmp %rAX Ivds"); + RCASE(0x38, "ALU Eb %Gb"); + RCASE(0x39, "ALU Evqp %Gvqp"); + RCASE(0x3A, "ALU %Gb Eb"); + RCASE(0x3B, "ALU %Gvqp Evqp"); + RCASE(0x3C, "ALU %al Ib"); + RCASE(0x3D, "ALU %rAX Ivds"); RCASE(0x3F, "aas"); RCASE(0x40 ... 0x47, "inc %Zv"); RCASE(0x48 ... 0x4f, "dec %Zv"); @@ -167,26 +181,11 @@ const char *DisSpecMap0(struct XedDecodedInst *x, char *p) { RCASE(0x6D, "insWL Yv %dx"); RCASE(0x6E, "outsb %dx Xb"); RCASE(0x6F, "outsWL %dx Xv"); - RCASE(0x70, "joBT Jbs"); - RCASE(0x71, "jnoBT Jbs"); - RCASE(0x72, "jbBT Jbs"); - RCASE(0x73, "jaeBT Jbs"); - RCASE(0x74, "jeBT Jbs"); - RCASE(0x75, "jneBT Jbs"); - RCASE(0x76, "jbeBT Jbs"); - RCASE(0x77, "jaBT Jbs"); - RCASE(0x78, "jsBT Jbs"); - RCASE(0x79, "jnsBT Jbs"); - RCASE(0x7a, "jpBT Jbs"); - RCASE(0x7b, "jnpBT Jbs"); - RCASE(0x7c, "jlBT Jbs"); - RCASE(0x7d, "jgeBT Jbs"); - RCASE(0x7e, "jleBT Jbs"); - RCASE(0x7f, "jgBT Jbs"); - RCASE(0x80, "ALU Eb Ib"); - RCASE(0x81, "ALU Evqp Ivds"); - RCASE(0x82, "ALU Eb Ib"); - RCASE(0x83, "ALU Evqp Ibs"); + RCASE(0x70 ... 0x7f, "jCC Jbs"); + RCASE(0x80, "ALU2 Eb Ib"); + RCASE(0x81, "ALU2 Evqp Ivds"); + RCASE(0x82, "ALU2 Eb Ib"); + RCASE(0x83, "ALU2 Evqp Ibs"); RCASE(0x84, "test Eb %Gb"); RCASE(0x85, "test %Gvqp Evqp"); RCASE(0x86, "xchg %Gb Eb"); @@ -202,6 +201,7 @@ const char *DisSpecMap0(struct XedDecodedInst *x, char *p) { RCASE(0x91 ... 0x97, "xchg %Zvqp %rAX"); RCASE(0x98, "cwtl"); RCASE(0x99, "cltd"); + RCASE(0x9A, "lcall Pvds Kvds"); RCASE(0x9B, "fwait"); RCASE(0x9C, "pushfWQ"); RCASE(0x9D, "popfWQ"); @@ -234,6 +234,8 @@ const char *DisSpecMap0(struct XedDecodedInst *x, char *p) { RCASE(0xC6, "mov Eb Ib"); RCASE(0xC7, "mov Evqp Ivds"); RCASE(0xC9, "leave"); + RCASE(0xCA, "lret Iw"); + RCASE(0xCB, "lret"); RCASE(0xCC, "int3"); RCASE(0xCD, "int Ib"); RCASE(0xD0, "BIT Eb $1"); @@ -280,163 +282,36 @@ const char *DisSpecMap0(struct XedDecodedInst *x, char *p) { switch (ModrmReg(x->op.rde)) { RCASE(1, "fxch EST1"); RCASE(3, "fstps Msr %st"); - case 0: - if (IsModrmRegister(x->op.rde)) { - return "fld EST"; - } else { - return "flds Msr"; - } - break; - case 2: - if (IsModrmRegister(x->op.rde)) { - return "fnop"; - } else { - return "fsts Msr %st"; - } - break; - case 4: - if (IsModrmRegister(x->op.rde)) { - return DisOpFpu0(x, 1); - } else { - return "fldenv Me"; - } - break; - case 5: - if (IsModrmRegister(x->op.rde)) { - return DisOpFpu0(x, 2); - } else { - return "fldcw Mw"; - } - break; - case 6: - if (IsModrmRegister(x->op.rde)) { - return DisOpFpu0(x, 3); - } else { - return "fnstenv M"; - } - break; - case 7: - if (IsModrmRegister(x->op.rde)) { - return DisOpFpu0(x, 4); - } else { - return "fnstcw Mw"; - } - break; + RCASE(0, DisSpecRegMem(x, "fld EST", "flds Msr")); + RCASE(2, DisSpecRegMem(x, "fnop", "fsts Msr %st")); + RCASE(4, DisSpecRegMemFpu0(x, 1, "fldenv Me")); + RCASE(5, DisSpecRegMemFpu0(x, 2, "fldcw Mw")); + RCASE(6, DisSpecRegMemFpu0(x, 3, "fnstenv M")); + RCASE(7, DisSpecRegMemFpu0(x, 4, "fnstcw Mw")); } break; case 0xDA: switch (ModrmReg(x->op.rde)) { - case 0: - if (!IsModrmRegister(x->op.rde)) { - return "fiaddl Mdi"; - } else { - return "fcmovb %st EST"; - } - break; - case 1: - if (!IsModrmRegister(x->op.rde)) { - return "fimull Mdi"; - } else { - return "fcmove %st EST"; - } - break; - case 2: - if (!IsModrmRegister(x->op.rde)) { - return "ficoml Mdi"; - } else { - return "fcmovbe %st EST"; - } - break; - case 3: - if (!IsModrmRegister(x->op.rde)) { - return "ficompl Mdi"; - } else { - return "fcmovu %st EST"; - } - break; - case 4: - if (!IsModrmRegister(x->op.rde)) { - return "fisubl Mdi"; - } else { - return "fisubr Mdi"; - } - break; - case 5: - if (!IsModrmRegister(x->op.rde)) { - return "fisubrl Mdi"; - } else { - return "fucompp"; - } - break; - case 6: - if (!IsModrmRegister(x->op.rde)) { - return "fidivl Mdi"; - } else { - return UNKNOWN; - } - break; - case 7: - if (!IsModrmRegister(x->op.rde)) { - return "fidivrl Mdi"; - } else { - return UNKNOWN; - } - break; + RCASE(0, DisSpecRegMem(x, "fcmovb %st EST", "fiaddl Mdi")); + RCASE(1, DisSpecRegMem(x, "fcmove %st EST", "fimull Mdi")); + RCASE(2, DisSpecRegMem(x, "fcmovbe %st EST", "ficoml Mdi")); + RCASE(3, DisSpecRegMem(x, "fcmovu %st EST", "ficompl Mdi")); + RCASE(4, DisSpecRegMem(x, "fisubr Mdi", "fisubl Mdi")); + RCASE(5, DisSpecRegMem(x, "fucompp", "fisubrl Mdi")); + RCASE(6, DisSpecRegMem(x, "fidivl Mdi", "UNKNOWN")); + RCASE(7, DisSpecRegMem(x, "fidivrl Mdi", "UNKNOWN")); } break; case 0xDB: switch (ModrmReg(x->op.rde)) { - case 0: - if (!IsModrmRegister(x->op.rde)) { - return "fildl Mdi"; - } else { - return "fcmovnb %st EST"; - } - break; - case 1: - if (!IsModrmRegister(x->op.rde)) { - return "fisttpl Mdi"; - } else { - return "fcmovne %st EST"; - } - break; - case 2: - if (!IsModrmRegister(x->op.rde)) { - return "fistl Mdi"; - } else { - return "fcmovnbe %st EST"; - } - break; - case 3: - if (!IsModrmRegister(x->op.rde)) { - return "fistpl Mdi"; - } else { - return "fcmovnu %st EST"; - } - break; - case 4: - return DisOpFpu0(x, 5); - case 5: - if (!IsModrmRegister(x->op.rde)) { - return "fldt Mer"; - } else { - return "fucomi %st EST"; - } - break; - case 6: - if (IsModrmRegister(x->op.rde)) { - return "fcomi %st EST"; - } else { - return UNKNOWN; - } - break; - case 7: - if (!IsModrmRegister(x->op.rde)) { - return "fstpt Mer"; - } else { - return UNKNOWN; - } - break; + RCASE(0, DisSpecRegMem(x, "fcmovnb %st EST", "fildl Mdi")); + RCASE(1, DisSpecRegMem(x, "fcmovne %st EST", "fisttpl Mdi")); + RCASE(2, DisSpecRegMem(x, "fcmovnbe %st EST", "fistl Mdi")); + RCASE(3, DisSpecRegMem(x, "fcmovnu %st EST", "fistpl Mdi")); + RCASE(4, DisSpecFpu0(x, 5)); + RCASE(5, DisSpecRegMem(x, "fucomi %st EST", "fldt Mer")); + RCASE(6, DisSpecRegMem(x, "fcomi %st EST", UNKNOWN)); + RCASE(7, DisSpecRegMem(x, UNKNOWN, "fstpt Mer")); } break; case 0xD8: @@ -590,22 +465,7 @@ const char *DisSpecMap1(struct XedDecodedInst *x, char *p) { RCASE(0x2E, Osz(x->op.rde) ? "ucomisd %Vsd Wsd" : "ucomiss %Vss Wss"); RCASE(0x2F, Osz(x->op.rde) ? "comisd %Vsd Wsd" : "comiss %Vss Wss"); RCASE(0x31, "rdtsc"); - RCASE(0x40, "cmovo %Gvqp Evqp"); - RCASE(0x41, "cmovno %Gvqp Evqp"); - RCASE(0x42, "cmovb %Gvqp Evqp"); - RCASE(0x43, "cmovae %Gvqp Evqp"); - RCASE(0x44, "cmove %Gvqp Evqp"); - RCASE(0x45, "cmovne %Gvqp Evqp"); - RCASE(0x46, "cmovbe %Gvqp Evqp"); - RCASE(0x47, "cmova %Gvqp Evqp"); - RCASE(0x48, "cmovs %Gvqp Evqp"); - RCASE(0x49, "cmovns %Gvqp Evqp"); - RCASE(0x4a, "cmovp %Gvqp Evqp"); - RCASE(0x4b, "cmovnp %Gvqp Evqp"); - RCASE(0x4c, "cmovl %Gvqp Evqp"); - RCASE(0x4d, "cmovge %Gvqp Evqp"); - RCASE(0x4e, "cmovle %Gvqp Evqp"); - RCASE(0x4f, "cmovg %Gvqp Evqp"); + RCASE(0x40 ... 0x4f, "cmovCC %Gvqp Evqp"); RCASE(0x52, DisOpVpsWpsVssWss(x, p, "rsqrt")); RCASE(0x53, DisOpVpsWpsVssWss(x, p, "rcp")); RCASE(0x54, DisOpVpdWpdVpsWps(x, p, "and")); @@ -635,38 +495,8 @@ const char *DisSpecMap1(struct XedDecodedInst *x, char *p) { RCASE(0x74, DisOpPqQqVdqWdq(x, p, "pcmpeqb")); RCASE(0x75, DisOpPqQqVdqWdq(x, p, "pcmpeqw")); RCASE(0x76, DisOpPqQqVdqWdq(x, p, "pcmpeqd")); - RCASE(0x80, "jo Jvds"); - RCASE(0x81, "jno Jvds"); - RCASE(0x82, "jb Jvds"); - RCASE(0x83, "jae Jvds"); - RCASE(0x84, "je Jvds"); - RCASE(0x85, "jne Jvds"); - RCASE(0x86, "jbe Jvds"); - RCASE(0x87, "ja Jvds"); - RCASE(0x88, "js Jvds"); - RCASE(0x89, "jns Jvds"); - RCASE(0x8a, "jp Jvds"); - RCASE(0x8b, "jnp Jvds"); - RCASE(0x8c, "jl Jvds"); - RCASE(0x8d, "jge Jvds"); - RCASE(0x8e, "jle Jvds"); - RCASE(0x8f, "jg Jvds"); - RCASE(0x90, "seto Eb"); - RCASE(0x91, "setno Eb"); - RCASE(0x92, "setb Eb"); - RCASE(0x93, "setnb Eb"); - RCASE(0x94, "sete Eb"); - RCASE(0x95, "setne Eb"); - RCASE(0x96, "setbe Eb"); - RCASE(0x97, "seta Eb"); - RCASE(0x98, "sets Eb"); - RCASE(0x99, "setns Eb"); - RCASE(0x9A, "setp Eb"); - RCASE(0x9B, "setnp Eb"); - RCASE(0x9C, "setl Eb"); - RCASE(0x9D, "setge Eb"); - RCASE(0x9E, "setle Eb"); - RCASE(0x9F, "setg Eb"); + RCASE(0x80 ... 0x8f, "jCC Jvds"); + RCASE(0x90 ... 0x9f, "setCC Jvds"); RCASE(0xA0, "push %fs"); RCASE(0xA1, "pop %fs"); RCASE(0xA2, "cpuid"); diff --git a/tool/build/lib/flags.c b/tool/build/lib/flags.c new file mode 100644 index 000000000..9bd899c3c --- /dev/null +++ b/tool/build/lib/flags.c @@ -0,0 +1,55 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "tool/build/lib/flags.h" + +bool GetParity(uint8_t b) { + b ^= b >> 4; + b ^= b >> 2; + b ^= b >> 1; + return ~b & 1; +} + +void ImportFlags(struct Machine *m, uint64_t flags) { + uint64_t old, mask = 0; + mask |= 1u << FLAGS_CF; + mask |= 1u << FLAGS_PF; + mask |= 1u << FLAGS_AF; + mask |= 1u << FLAGS_ZF; + mask |= 1u << FLAGS_SF; + mask |= 1u << FLAGS_TF; + mask |= 1u << FLAGS_IF; + mask |= 1u << FLAGS_DF; + mask |= 1u << FLAGS_OF; + mask |= 1u << FLAGS_NT; + mask |= 1u << FLAGS_AC; + mask |= 1u << FLAGS_ID; + m->flags = (flags & mask) | (m->flags & ~mask); + m->flags = SetFlag(m->flags, FLAGS_RF, false); + m->flags = SetLazyParityByte(m->flags, !((m->flags >> FLAGS_PF) & 1)); +} + +uint64_t ExportFlags(uint64_t flags) { + flags = SetFlag(flags, FLAGS_IOPL, 3); + flags = SetFlag(flags, FLAGS_F1, true); + flags = SetFlag(flags, FLAGS_F0, false); + flags = flags & ~(1ull << FLAGS_PF); + flags |= GetLazyParityBool(flags) << FLAGS_PF; + return flags; +} diff --git a/tool/build/lib/flags.h b/tool/build/lib/flags.h index 9ebd47a0d..763a00456 100644 --- a/tool/build/lib/flags.h +++ b/tool/build/lib/flags.h @@ -1,5 +1,6 @@ #ifndef COSMOPOLITAN_TOOL_BUILD_LIB_FLAGS_H_ #define COSMOPOLITAN_TOOL_BUILD_LIB_FLAGS_H_ +#include "tool/build/lib/machine.h" #define FLAGS_CF 0 #define FLAGS_VF 1 @@ -23,18 +24,12 @@ #define FLAGS_VIP 20 #define FLAGS_ID 21 +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + #define GetLazyParityBool(f) GetParity((f) >> 24) #define SetLazyParityByte(f, x) (((f) & ~0xFF000000u) | ((x)&0xFFu) << 24) -#define GetParity(WORD) \ - ({ \ - unsigned Byte = (WORD); \ - Byte ^= Byte >> 4; \ - Byte ^= Byte >> 2; \ - Byte ^= Byte >> 1; \ - ~Byte & 1; \ - }) - #define GetFlag(FLAGS, BIT) \ ({ \ autotype(FLAGS) Flags = (FLAGS); \ @@ -66,13 +61,10 @@ Flags; \ }) -forceinline uint64_t ExportFlags(uint64_t flags) { - flags = SetFlag(flags, FLAGS_IOPL, 3); - flags = SetFlag(flags, FLAGS_F1, true); - flags = SetFlag(flags, FLAGS_F0, false); - flags = flags & ~(1ull << FLAGS_PF); - flags |= GetLazyParityBool(flags) << FLAGS_PF; - return flags; -} +bool GetParity(uint8_t); +uint64_t ExportFlags(uint64_t); +void ImportFlags(struct Machine *, uint64_t); +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* COSMOPOLITAN_TOOL_BUILD_LIB_FLAGS_H_ */ diff --git a/tool/build/lib/fpu.c b/tool/build/lib/fpu.c index 9fd70ae06..3410f1d24 100644 --- a/tool/build/lib/fpu.c +++ b/tool/build/lib/fpu.c @@ -298,8 +298,7 @@ static long double FpuRound(struct Machine *m, long double x) { } static void FpuCompare(struct Machine *m, long double y) { - long double x; - x = St0(m); + long double x = St0(m); m->fpu.c1 = false; if (!isunordered(x, y)) { m->fpu.c0 = x < y; @@ -452,7 +451,7 @@ static void OpFsincos(struct Machine *m) { static void OpFpatan(struct Machine *m) { FpuClearRoundup(m); - FpuSetStPop(m, 1, atan2l(St0(m), St1(m))); + FpuSetStPop(m, 1, atan2l(St1(m), St0(m))); } static void OpFcom(struct Machine *m) { @@ -704,8 +703,7 @@ static void OpFincstp(struct Machine *m) { } static void OpFxtract(struct Machine *m) { - long double x; - x = St0(m); + long double x = St0(m); FpuSetSt0(m, logbl(x)); FpuPush(m, significandl(x)); } @@ -766,30 +764,44 @@ static void OpFldl(struct Machine *m) { FpuPush(m, FpuGetMemoryDouble(m)); } +static long double Fld1(void) { + return 1; +} + +static long double Fldl2t(void) { + return 0xd.49a784bcd1b8afep-2L; /* log₂10 */ +} + +static long double Fldl2e(void) { + return 0xb.8aa3b295c17f0bcp-3L; /* log₂𝑒 */ +} + +static long double Fldpi(void) { + return 0x1.921fb54442d1846ap+1L; /* π */ +} + +static long double Fldlg2(void) { + return 0x9.a209a84fbcff799p-5L; /* log₁₀2 */ +} + +static long double Fldln2(void) { + return 0xb.17217f7d1cf79acp-4L; /* logₑ2 */ +} + +static long double Fldz(void) { + return 0; +} + static void OpFldConstant(struct Machine *m) { long double x; switch (ModrmRm(m->xedd->op.rde)) { - case 0: - x = fld1(); - break; - case 1: - x = fldl2t(); - break; - case 2: - x = fldl2e(); - break; - case 3: - x = fldpi(); - break; - case 4: - x = fldlg2(); - break; - case 5: - x = fldln2(); - break; - case 6: - x = fldz(); - break; + CASE(0, x = Fld1()); + CASE(1, x = Fldl2t()); + CASE(2, x = Fldl2e()); + CASE(3, x = Fldpi()); + CASE(4, x = Fldlg2()); + CASE(5, x = Fldln2()); + CASE(6, x = Fldz()); default: OpUd(m, m->xedd->op.rde); } diff --git a/tool/build/lib/dishigh.c b/tool/build/lib/high.c similarity index 87% rename from tool/build/lib/dishigh.c rename to tool/build/lib/high.c index 7e73304e9..a505f127c 100644 --- a/tool/build/lib/dishigh.c +++ b/tool/build/lib/high.c @@ -19,16 +19,24 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/conv/itoa.h" #include "libc/str/str.h" -#include "tool/build/lib/dis.h" +#include "tool/build/lib/high.h" -struct DisHigh *g_dis_high; +struct High g_high; -char *DisHigh(char *p, int h) { - if (h != -1) { +char *HighStart(char *p, int h) { + if (h) { p = stpcpy(p, "\e[38;5;"); p += uint64toarray_radix10(h, p); - } else { - p = stpcpy(p, "\e[39"); + p = stpcpy(p, "m"); + g_high.active = true; } - return stpcpy(p, "m"); + return p; +} + +char *HighEnd(char *p) { + if (g_high.active) { + p = stpcpy(p, "\e[39m"); + g_high.active = false; + } + return p; } diff --git a/tool/build/lib/high.h b/tool/build/lib/high.h new file mode 100644 index 000000000..4d3f5730b --- /dev/null +++ b/tool/build/lib/high.h @@ -0,0 +1,23 @@ +#ifndef COSMOPOLITAN_TOOL_BUILD_LIB_HIGH_H_ +#define COSMOPOLITAN_TOOL_BUILD_LIB_HIGH_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +struct High { + bool active; + uint8_t keyword; + uint8_t reg; + uint8_t literal; + uint8_t label; + uint8_t comment; + uint8_t quote; +}; + +extern struct High g_high; + +char *HighStart(char *, int); +char *HighEnd(char *); + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_TOOL_BUILD_LIB_HIGH_H_ */ diff --git a/tool/build/lib/instruction.c b/tool/build/lib/instruction.c index 688895c0a..d1076cd45 100644 --- a/tool/build/lib/instruction.c +++ b/tool/build/lib/instruction.c @@ -52,18 +52,33 @@ static void DecodeInstruction(struct Machine *m, uint8_t *p, unsigned n) { } } +static noinline void LoadInstructionSlow(struct Machine *m, uint64_t ip) { + unsigned i; + uint8_t *addr; + uint8_t copy[15], *toil; + i = 0x1000 - (ip & 0xfff); + addr = ResolveAddress(m, ip); + if ((toil = FindReal(m, ip + i))) { + memcpy(copy, addr, i); + memcpy(copy + i, toil, 15 - i); + DecodeInstruction(m, copy, 15); + } else { + DecodeInstruction(m, addr, i); + } +} + void LoadInstruction(struct Machine *m) { uint64_t ip; - unsigned i, key; - uint8_t *addr, *toil, copy[15]; + unsigned key; + uint8_t *addr; ip = Read64(m->cs) + MaskAddress(m->mode & 3, m->ip); key = ip & (ARRAYLEN(m->icache) - 1); m->xedd = (struct XedDecodedInst *)m->icache[key]; - if ((i = 0x1000 - (ip & 0xfff)) >= 15) { - if (ROUNDDOWN(ip, 0x1000) == m->codevirt && ip) { + if ((ip & 0xfff) < 0x1000 - 15) { + if (ip - (ip & 0xfff) == m->codevirt && ip) { addr = m->codereal + (ip & 0xfff); } else { - m->codevirt = ROUNDDOWN(ip, 0x1000); + m->codevirt = ip - (ip & 0xfff); m->codereal = ResolveAddress(m, m->codevirt); addr = m->codereal + (ip & 0xfff); } @@ -71,13 +86,6 @@ void LoadInstruction(struct Machine *m) { DecodeInstruction(m, addr, 15); } } else { - addr = ResolveAddress(m, ip); - if ((toil = FindReal(m, ip + i))) { - memcpy(copy, addr, i); - memcpy(copy + i, toil, 15 - i); - DecodeInstruction(m, copy, 15); - } else { - DecodeInstruction(m, addr, i); - } + LoadInstructionSlow(m, ip); } } diff --git a/tool/build/lib/loader.c b/tool/build/lib/loader.c index a39e132b7..035ded928 100644 --- a/tool/build/lib/loader.c +++ b/tool/build/lib/loader.c @@ -52,6 +52,7 @@ static void LoadElfLoadSegment(struct Machine *m, void *code, size_t codesize, fstart = felf + ROUNDDOWN(phdr->p_offset, align); fend = felf + ROUNDUP(phdr->p_offset + phdr->p_filesz, align); bsssize = vend - vbss; + m->brk = MAX(m->brk, vend); CHECK_GE(vend, vstart); CHECK_GE(fend, fstart); CHECK_LE(felf, fstart); @@ -120,7 +121,7 @@ void LoadProgram(struct Machine *m, const char *prog, char **args, char **vars, DCHECK_NOTNULL(prog); elf->prog = prog; if ((fd = open(prog, O_RDONLY)) == -1 || - (fstat(fd, &st) == -1 || !st.st_size) || !S_ISREG(st.st_mode)) { + (fstat(fd, &st) == -1 || !st.st_size) /* || !S_ISREG(st.st_mode) */) { fputs(prog, stderr); fputs(": not found\n", stderr); exit(1); diff --git a/tool/build/lib/machine.c b/tool/build/lib/machine.c index a4fe0ffe9..be86f9191 100644 --- a/tool/build/lib/machine.c +++ b/tool/build/lib/machine.c @@ -50,6 +50,12 @@ #include "tool/build/lib/throw.h" #include "tool/build/lib/time.h" +#define OpLfence OpNoop +#define OpMfence OpNoop +#define OpSfence OpNoop +#define OpClflush OpNoop +#define OpHintNopEv OpNoop + typedef void (*nexgen32e_f)(struct Machine *, uint32_t); static uint64_t ReadMemory(uint32_t rde, uint8_t p[8]) { @@ -130,38 +136,7 @@ static bool IsGreater(struct Machine *m) { (GetFlag(m->flags, FLAGS_SF) == GetFlag(m->flags, FLAGS_OF)); } -static void ImportFlags(struct Machine *m, uint64_t flags) { - uint64_t old, mask = 0; - mask |= 1u << FLAGS_CF; - mask |= 1u << FLAGS_PF; - mask |= 1u << FLAGS_AF; - mask |= 1u << FLAGS_ZF; - mask |= 1u << FLAGS_SF; - mask |= 1u << FLAGS_TF; - mask |= 1u << FLAGS_IF; - mask |= 1u << FLAGS_DF; - mask |= 1u << FLAGS_OF; - mask |= 1u << FLAGS_NT; - mask |= 1u << FLAGS_AC; - mask |= 1u << FLAGS_ID; - m->flags = (flags & mask) | (m->flags & ~mask); - m->flags = SetFlag(m->flags, FLAGS_RF, false); - m->flags = SetLazyParityByte(m->flags, !((m->flags >> FLAGS_PF) & 1)); -} - -static void OpLfence(struct Machine *m, uint32_t rde) { -} - -static void OpMfence(struct Machine *m, uint32_t rde) { -} - -static void OpSfence(struct Machine *m, uint32_t rde) { -} - -static void OpClflush(struct Machine *m, uint32_t rde) { -} - -static void OpWutNopEv(struct Machine *m, uint32_t rde) { +static void OpNoop(struct Machine *m, uint32_t rde) { } static void OpCmc(struct Machine *m, uint32_t rde) { @@ -216,27 +191,27 @@ static void OpLeaGvqpM(struct Machine *m, uint32_t rde) { WriteRegister(rde, RegRexrReg(m, rde), ComputeAddress(m, rde)); } -static void OpPushSeg(struct Machine *m, uint32_t rde) { +static relegated void OpPushSeg(struct Machine *m, uint32_t rde) { uint8_t seg = (m->xedd->op.opcode & 070) >> 3; Push(m, rde, Read64(GetSegment(m, rde, seg)) >> 4); } -static void OpPopSeg(struct Machine *m, uint32_t rde) { +static relegated void OpPopSeg(struct Machine *m, uint32_t rde) { uint8_t seg = (m->xedd->op.opcode & 070) >> 3; Write64(GetSegment(m, rde, seg), Pop(m, rde, 0) << 4); } -static void OpMovEvqpSw(struct Machine *m, uint32_t rde) { +static relegated void OpMovEvqpSw(struct Machine *m, uint32_t rde) { WriteRegisterOrMemory(rde, GetModrmRegisterWordPointerWriteOszRexw(m, rde), Read64(GetSegment(m, rde, ModrmReg(rde))) >> 4); } -static void OpMovSwEvqp(struct Machine *m, uint32_t rde) { +static relegated void OpMovSwEvqp(struct Machine *m, uint32_t rde) { Write64(GetSegment(m, rde, ModrmReg(rde)), ReadMemory(rde, GetModrmRegisterWordPointerReadOszRexw(m, rde)) << 4); } -static void OpJmpf(struct Machine *m, uint32_t rde) { +static relegated void OpJmpf(struct Machine *m, uint32_t rde) { Write64(m->cs, m->xedd->op.uimm0 << 4); m->ip = m->xedd->op.disp; } @@ -474,7 +449,8 @@ static void OpBit(struct Machine *m, uint32_t rde) { SetWriteAddr(m, v, 1 << w); } } - y = 1ull << bit; + y = 1; + y <<= bit; x = ReadMemory(rde, p); m->flags = SetFlag(m->flags, FLAGS_CF, !!(y & x)); switch (op) { @@ -495,7 +471,7 @@ static void OpBit(struct Machine *m, uint32_t rde) { WriteRegisterOrMemory(rde, p, z); } -static void OpConvert1(struct Machine *m, uint32_t rde) { +static void OpSax(struct Machine *m, uint32_t rde) { if (Rexw(rde)) { Write64(m->ax, (int32_t)Read32(m->ax)); } else if (!Osz(rde)) { @@ -505,7 +481,7 @@ static void OpConvert1(struct Machine *m, uint32_t rde) { } } -static void OpConvert2(struct Machine *m, uint32_t rde) { +static void OpConvert(struct Machine *m, uint32_t rde) { if (Rexw(rde)) { Write64(m->dx, Read64(m->ax) & 0x8000000000000000 ? 0xffffffffffffffff : 0); } else if (!Osz(rde)) { @@ -530,7 +506,7 @@ static void OpBswapZvqp(struct Machine *m, uint32_t rde) { ((x & 0xff000000) >> 030 | (x & 0x000000ff) << 030 | (x & 0x00ff0000) >> 010 | (x & 0x0000ff00) << 010)); } else { - Write16(RegRexbSrm(m, rde), ((x & 0x00ff) << 010 | (x & 0xff00) << 010)); + Write16(RegRexbSrm(m, rde), (x & 0x00ff) << 010 | (x & 0xff00) << 010); } } @@ -625,18 +601,41 @@ static void OpMovswGvqpEw(struct Machine *m, uint32_t rde) { } static void OpMovsxdGdqpEd(struct Machine *m, uint32_t rde) { - uint64_t x; - uint8_t *p; - x = (int32_t)Read32(GetModrmRegisterWordPointerRead4(m, rde)); - if (!Rexw(rde)) x &= 0xffffffff; /* wut */ - Write64(RegRexrReg(m, rde), x); + Write64(RegRexrReg(m, rde), + (int32_t)Read32(GetModrmRegisterWordPointerRead4(m, rde))); } -static void OpAlub(struct Machine *m, uint32_t rde) { - uint8_t *a; - a = GetModrmRegisterBytePointerWrite(m, rde); - Write8(a, kAlu[(m->xedd->op.opcode & 070) >> 3][0]( - Read8(a), Read8(ByteRexrReg(m, rde)), &m->flags)); +static void Alub(struct Machine *m, uint32_t rde, aluop_f op) { + uint8_t *a = GetModrmRegisterBytePointerWrite(m, rde); + Write8(a, op(Read8(a), Read8(ByteRexrReg(m, rde)), &m->flags)); +} + +static void OpAlubAdd(struct Machine *m, uint32_t rde) { + Alub(m, rde, Add8); +} + +static void OpAlubOr(struct Machine *m, uint32_t rde) { + Alub(m, rde, Or8); +} + +static void OpAlubAdc(struct Machine *m, uint32_t rde) { + Alub(m, rde, Adc8); +} + +static void OpAlubSbb(struct Machine *m, uint32_t rde) { + Alub(m, rde, Sbb8); +} + +static void OpAlubAnd(struct Machine *m, uint32_t rde) { + Alub(m, rde, And8); +} + +static void OpAlubSub(struct Machine *m, uint32_t rde) { + Alub(m, rde, Sub8); +} + +static void OpAlubXor(struct Machine *m, uint32_t rde) { + Alub(m, rde, Xor8); } static void AlubRo(struct Machine *m, uint32_t rde, aluop_f op) { @@ -652,11 +651,38 @@ static void OpAlubTest(struct Machine *m, uint32_t rde) { AlubRo(m, rde, And8); } -static void OpAlubFlip(struct Machine *m, uint32_t rde) { +static void AlubFlip(struct Machine *m, uint32_t rde, aluop_f op) { Write8(ByteRexrReg(m, rde), - kAlu[(m->xedd->op.opcode & 070) >> 3][0]( - Read8(ByteRexrReg(m, rde)), - Read8(GetModrmRegisterBytePointerRead(m, rde)), &m->flags)); + op(Read8(ByteRexrReg(m, rde)), + Read8(GetModrmRegisterBytePointerRead(m, rde)), &m->flags)); +} + +static void OpAlubFlipAdd(struct Machine *m, uint32_t rde) { + AlubFlip(m, rde, Add8); +} + +static void OpAlubFlipOr(struct Machine *m, uint32_t rde) { + AlubFlip(m, rde, Or8); +} + +static void OpAlubFlipAdc(struct Machine *m, uint32_t rde) { + AlubFlip(m, rde, Adc8); +} + +static void OpAlubFlipSbb(struct Machine *m, uint32_t rde) { + AlubFlip(m, rde, Sbb8); +} + +static void OpAlubFlipAnd(struct Machine *m, uint32_t rde) { + AlubFlip(m, rde, And8); +} + +static void OpAlubFlipSub(struct Machine *m, uint32_t rde) { + AlubFlip(m, rde, Sub8); +} + +static void OpAlubFlipXor(struct Machine *m, uint32_t rde) { + AlubFlip(m, rde, Xor8); } static void AlubFlipRo(struct Machine *m, uint32_t rde, aluop_f op) { @@ -765,9 +791,36 @@ static void OpAluwiReg(struct Machine *m, uint32_t rde) { } } -static void OpAluAlIb(struct Machine *m, uint32_t rde) { - Write8(m->ax, kAlu[(m->xedd->op.opcode & 070) >> 3][0]( - Read8(m->ax), m->xedd->op.uimm0, &m->flags)); +static void AluAlIb(struct Machine *m, aluop_f op) { + Write8(m->ax, op(Read8(m->ax), m->xedd->op.uimm0, &m->flags)); +} + +static void OpAluAlIbAdd(struct Machine *m, uint32_t rde) { + AluAlIb(m, Add8); +} + +static void OpAluAlIbOr(struct Machine *m, uint32_t rde) { + AluAlIb(m, Or8); +} + +static void OpAluAlIbAdc(struct Machine *m, uint32_t rde) { + AluAlIb(m, Adc8); +} + +static void OpAluAlIbSbb(struct Machine *m, uint32_t rde) { + AluAlIb(m, Sbb8); +} + +static void OpAluAlIbAnd(struct Machine *m, uint32_t rde) { + AluAlIb(m, And8); +} + +static void OpAluAlIbSub(struct Machine *m, uint32_t rde) { + AluAlIb(m, Sub8); +} + +static void OpAluAlIbXor(struct Machine *m, uint32_t rde) { + AluAlIb(m, Xor8); } static void OpAluRaxIvds(struct Machine *m, uint32_t rde) { @@ -831,18 +884,19 @@ static void OpBsubiImm(struct Machine *m, uint32_t rde) { Bsubi(m, rde, m->xedd->op.uimm0); } -static void LoadFarPointer(struct Machine *m, uint32_t rde, uint8_t seg[8]) { +static relegated void LoadFarPointer(struct Machine *m, uint32_t rde, + uint8_t seg[8]) { uint32_t fp; fp = Read32(ComputeReserveAddressRead4(m, rde)); Write64(seg, (fp & 0x0000ffff) << 4); Write16(RegRexrReg(m, rde), fp >> 16); } -static void OpLes(struct Machine *m, uint32_t rde) { +static relegated void OpLes(struct Machine *m, uint32_t rde) { LoadFarPointer(m, rde, m->es); } -static void OpLds(struct Machine *m, uint32_t rde) { +static relegated void OpLds(struct Machine *m, uint32_t rde) { LoadFarPointer(m, rde, m->ds); } @@ -853,14 +907,6 @@ static void OpPushImm(struct Machine *m, uint32_t rde) { Push(m, rde, m->xedd->op.uimm0); } -static void OpRet0(struct Machine *m, uint32_t rde) { - OpRet(m, rde, 0); -} - -static void OpRetImm(struct Machine *m, uint32_t rde) { - OpRet(m, rde, m->xedd->op.uimm0); -} - static void Interrupt(struct Machine *m, uint32_t rde, int i) { HaltMachine(m, i); } @@ -1213,17 +1259,18 @@ static void OpNegEb(struct Machine *m, uint32_t rde) { AluEb(m, rde, Neg8); } +static const nexgen32e_f kOp0f6[] = { + OpAlubiTest, + OpAlubiTest, + OpNotEb, + OpNegEb, + OpMulAxAlEbUnsigned, + OpMulAxAlEbSigned, + OpDivAlAhAxEbUnsigned, + OpDivAlAhAxEbSigned, +}; + static void Op0f6(struct Machine *m, uint32_t rde) { - static const nexgen32e_f kOp0f6[] = { - OpAlubiTest, - OpAlubiTest, - OpNotEb, - OpNegEb, - OpMulAxAlEbUnsigned, - OpMulAxAlEbSigned, - OpDivAlAhAxEbUnsigned, - OpDivAlAhAxEbSigned, - }; kOp0f6[ModrmReg(rde)](m, rde); } @@ -1239,17 +1286,18 @@ static void OpNegEvqp(struct Machine *m, uint32_t rde) { AluEvqp(m, rde, kAlu[ALU_NEG]); } +static const nexgen32e_f kOp0f7[] = { + OpTestEvqpIvds, + OpTestEvqpIvds, + OpNotEvqp, + OpNegEvqp, + OpMulRdxRaxEvqpUnsigned, + OpMulRdxRaxEvqpSigned, + OpDivRdxRaxEvqpUnsigned, + OpDivRdxRaxEvqpSigned, +}; + static void Op0f7(struct Machine *m, uint32_t rde) { - static const nexgen32e_f kOp0f7[] = { - OpTestEvqpIvds, - OpTestEvqpIvds, - OpNotEvqp, - OpNegEvqp, - OpMulRdxRaxEvqpUnsigned, - OpMulRdxRaxEvqpSigned, - OpDivRdxRaxEvqpUnsigned, - OpDivRdxRaxEvqpSigned, - }; kOp0f7[ModrmReg(rde)](m, rde); } @@ -1274,9 +1322,10 @@ static void OpDecEvqp(struct Machine *m, uint32_t rde) { AluEvqp(m, rde, kAlu[ALU_DEC]); } +static const nexgen32e_f kOp0ff[] = {OpIncEvqp, OpDecEvqp, OpCallEq, OpUd, + OpJmpEq, OpUd, OpPushEvq, OpUd}; + static void Op0ff(struct Machine *m, uint32_t rde) { - static const nexgen32e_f kOp0ff[] = {OpIncEvqp, OpDecEvqp, OpCallEq, OpUd, - OpJmpEq, OpUd, OpPushEvq, OpUd}; kOp0ff[ModrmReg(rde)](m, rde); } @@ -1295,57 +1344,6 @@ static void Op101(struct Machine *m, uint32_t rde) { OpUd(m, rde); } -static void Op171(struct Machine *m, uint32_t rde) { - switch (ModrmReg(rde)) { - case 2: - OpSseUdqIb(m, rde, kOpSseUdqIbPsrlw); - break; - case 4: - OpSseUdqIb(m, rde, kOpSseUdqIbPsraw); - break; - case 6: - OpSseUdqIb(m, rde, kOpSseUdqIbPsllw); - break; - default: - OpUd(m, rde); - } -} - -static void Op172(struct Machine *m, uint32_t rde) { - switch (ModrmReg(rde)) { - case 2: - OpSseUdqIb(m, rde, kOpSseUdqIbPsrld); - break; - case 4: - OpSseUdqIb(m, rde, kOpSseUdqIbPsrad); - break; - case 6: - OpSseUdqIb(m, rde, kOpSseUdqIbPslld); - break; - default: - OpUd(m, rde); - } -} - -static void Op173(struct Machine *m, uint32_t rde) { - switch (ModrmReg(rde)) { - case 2: - OpSseUdqIb(m, rde, kOpSseUdqIbPsrlq); - break; - case 3: - OpSseUdqIb(m, rde, kOpSseUdqIbPsrldq); - break; - case 6: - OpSseUdqIb(m, rde, kOpSseUdqIbPsllq); - break; - case 7: - OpSseUdqIb(m, rde, kOpSseUdqIbPslldq); - break; - default: - OpUd(m, rde); - } -} - static void OpDoubleShift(struct Machine *m, uint32_t rde) { uint8_t *p; uint64_t x; @@ -1386,6 +1384,8 @@ static void OpSalc(struct Machine *m, uint32_t rde) { static void OpNopEv(struct Machine *m, uint32_t rde) { if (ModrmMod(rde) == 0b01 && ModrmReg(rde) == 0 && ModrmRm(rde) == 0b101) { OpBofram(m, rde); + } else { + OpNoop(m, rde); } } @@ -1394,63 +1394,65 @@ static void OpNop(struct Machine *m, uint32_t rde) { OpXchgZvqp(m, rde); } else if (Rep(rde) == 3) { OpPause(m, rde); + } else { + OpNoop(m, rde); } } static const nexgen32e_f kNexgen32e[] = { - [0x000] = OpAlub, + [0x000] = OpAlubAdd, [0x001] = OpAluw, - [0x002] = OpAlubFlip, + [0x002] = OpAlubFlipAdd, [0x003] = OpAluwFlip, - [0x004] = OpAluAlIb, + [0x004] = OpAluAlIbAdd, [0x005] = OpAluRaxIvds, [0x006] = OpPushSeg, [0x007] = OpPopSeg, - [0x008] = OpAlub, + [0x008] = OpAlubOr, [0x009] = OpAluw, - [0x00A] = OpAlubFlip, + [0x00A] = OpAlubFlipOr, [0x00B] = OpAluwFlip, - [0x00C] = OpAluAlIb, + [0x00C] = OpAluAlIbOr, [0x00D] = OpAluRaxIvds, [0x00E] = OpPushSeg, [0x00F] = OpPopSeg, - [0x010] = OpAlub, + [0x010] = OpAlubAdc, [0x011] = OpAluw, - [0x012] = OpAlubFlip, + [0x012] = OpAlubFlipAdc, [0x013] = OpAluwFlip, - [0x014] = OpAluAlIb, + [0x014] = OpAluAlIbAdc, [0x015] = OpAluRaxIvds, [0x016] = OpPushSeg, [0x017] = OpPopSeg, - [0x018] = OpAlub, + [0x018] = OpAlubSbb, [0x019] = OpAluw, - [0x01A] = OpAlubFlip, + [0x01A] = OpAlubFlipSbb, [0x01B] = OpAluwFlip, - [0x01C] = OpAluAlIb, + [0x01C] = OpAluAlIbSbb, [0x01D] = OpAluRaxIvds, [0x01E] = OpPushSeg, [0x01F] = OpPopSeg, - [0x020] = OpAlub, + [0x020] = OpAlubAnd, [0x021] = OpAluw, - [0x022] = OpAlubFlip, + [0x022] = OpAlubFlipAnd, [0x023] = OpAluwFlip, - [0x024] = OpAluAlIb, + [0x024] = OpAluAlIbAnd, [0x025] = OpAluRaxIvds, [0x026] = OpPushSeg, [0x027] = OpPopSeg, - [0x028] = OpAlub, + [0x028] = OpAlubSub, [0x029] = OpAluw, - [0x02A] = OpAlubFlip, + [0x02A] = OpAlubFlipSub, [0x02B] = OpAluwFlip, - [0x02C] = OpAluAlIb, + [0x02C] = OpAluAlIbSub, [0x02D] = OpAluRaxIvds, [0x02E] = OpUd, [0x02F] = OpDas, - [0x030] = OpAlub, + [0x030] = OpAlubXor, [0x031] = OpAluw, - [0x032] = OpAlubFlip, + [0x032] = OpAlubFlipXor, [0x033] = OpAluwFlip, - [0x034] = OpAluAlIb, + [0x034] = OpAluAlIbXor, [0x035] = OpAluRaxIvds, [0x036] = OpUd, [0x037] = OpAaa, @@ -1550,9 +1552,9 @@ static const nexgen32e_f kNexgen32e[] = { [0x095] = OpXchgZvqp, [0x096] = OpXchgZvqp, [0x097] = OpXchgZvqp, - [0x098] = OpConvert1, - [0x099] = OpConvert2, - [0x09A] = OpUd, + [0x098] = OpSax, + [0x099] = OpConvert, + [0x09A] = OpCallf, [0x09B] = OpFwait, [0x09C] = OpPushf, [0x09D] = OpPopf, @@ -1592,16 +1594,16 @@ static const nexgen32e_f kNexgen32e[] = { [0x0BF] = OpMovZvqpIvqp, [0x0C0] = OpBsubiImm, [0x0C1] = OpBsuwiImm, - [0x0C2] = OpRetImm, - [0x0C3] = OpRet0, + [0x0C2] = OpRet, + [0x0C3] = OpRet, [0x0C4] = OpLes, [0x0C5] = OpLds, [0x0C6] = OpMovEbIb, [0x0C7] = OpMovEvqpIvds, [0x0C8] = OpUd, [0x0C9] = OpLeave, - [0x0CA] = OpUd, - [0x0CB] = OpUd, + [0x0CA] = OpRetf, + [0x0CB] = OpRetf, [0x0CC] = OpInterrupt3, [0x0CD] = OpInterruptImm, [0x0CE] = OpUd, @@ -1667,7 +1669,7 @@ static const nexgen32e_f kNexgen32e[] = { [0x10A] = OpUd, [0x10B] = OpUd, [0x10C] = OpUd, - [0x10D] = OpWutNopEv, + [0x10D] = OpHintNopEv, [0x10E] = OpUd, [0x10F] = OpUd, [0x110] = OpMov0f10, @@ -1678,12 +1680,12 @@ static const nexgen32e_f kNexgen32e[] = { [0x115] = OpUnpckhpsd, [0x116] = OpMov0f16, [0x117] = OpMov0f17, - [0x118] = OpWutNopEv, - [0x119] = OpWutNopEv, - [0x11A] = OpWutNopEv, - [0x11B] = OpWutNopEv, - [0x11C] = OpWutNopEv, - [0x11D] = OpWutNopEv, + [0x118] = OpHintNopEv, + [0x119] = OpHintNopEv, + [0x11A] = OpHintNopEv, + [0x11B] = OpHintNopEv, + [0x11C] = OpHintNopEv, + [0x11D] = OpHintNopEv, [0x11E] = OpUd, [0x11F] = OpNopEv, [0x120] = OpUd, diff --git a/tool/build/lib/machine.h b/tool/build/lib/machine.h index 565a64548..130d7e213 100644 --- a/tool/build/lib/machine.h +++ b/tool/build/lib/machine.h @@ -137,12 +137,13 @@ struct Machine { uint32_t i; void *p[6]; } freelist; + int64_t brk; int64_t bofram[2]; jmp_buf onhalt; int64_t faultaddr; uint8_t stash[4096]; uint8_t xmmtype[2][8]; - uint8_t icache[2048][40] aligned(8); + uint8_t icache[4096 / 4][40] aligned(16); struct MachineFds fds; } aligned(64); diff --git a/tool/build/lib/mda.c b/tool/build/lib/mda.c index 84d530c32..91cf6ff53 100644 --- a/tool/build/lib/mda.c +++ b/tool/build/lib/mda.c @@ -22,12 +22,45 @@ #include "tool/build/lib/buffer.h" #include "tool/build/lib/mda.h" +#define kBlink 1 +#define kVisible 2 +#define kUnderline 4 +#define kBold 8 +#define kReverse 16 + +/** + * Decodes Monochrome Display Adapter attributes. + * @see https://www.seasip.info/VintagePC/mda.html + */ +static uint8_t DecodeMdaAttributes(int8_t a) { + uint8_t r = 0; + if (a & 0x77) { + if ((a & 0x77) == 0x70) r |= kReverse; + if ((a & 0x07) == 0x01) r |= kUnderline; + if (a & 0x08) r |= kBold; + if (a < 0) r |= kBlink; + r |= kVisible; + } + return r; +} + void DrawMda(struct Panel *p, uint8_t v[25][80][2]) { - unsigned y, x, n; + unsigned y, x, n, a, b; n = MIN(25, p->bottom - p->top); for (y = 0; y < n; ++y) { + a = -1; for (x = 0; x < 80; ++x) { - if (v[y][x][1]) { + b = DecodeMdaAttributes(v[y][x][1]); + if (a != b) { + a = b; + AppendStr(&p->lines[y], "\e[0"); + if (a & kBold) AppendStr(&p->lines[y], ";1"); + if (a & kUnderline) AppendStr(&p->lines[y], ";4"); + if (a & kBlink) AppendStr(&p->lines[y], ";5"); + if (a & kReverse) AppendStr(&p->lines[y], ";7"); + AppendChar(&p->lines[y], 'm'); + } + if (a) { AppendWide(&p->lines[y], kCp437[v[y][x][0]]); } else { AppendChar(&p->lines[y], ' '); diff --git a/tool/build/lib/modrm.c b/tool/build/lib/modrm.c index 18a2e394b..d20f2fe97 100644 --- a/tool/build/lib/modrm.c +++ b/tool/build/lib/modrm.c @@ -26,29 +26,54 @@ #include "tool/build/lib/modrm.h" #include "tool/build/lib/throw.h" -/** - * Byte register offsets. - * - * for (i = 0; i < 2; ++i) { // rex - * for (j = 0; j < 2; ++j) { // rexb, or rexr - * for (k = 0; k < 8; ++k) { // reg, rm, or srm - * kByteReg[i << 4 | j << 3 | k] = - * i ? (j << 3 | k) * 8 : (k & 0b11) * 8 + ((k & 0b100) >> 2); - * } - * } - * } - */ -const uint8_t kByteReg[32] = {0x00, 0x08, 0x10, 0x18, 0x01, 0x09, 0x11, 0x19, - 0x00, 0x08, 0x10, 0x18, 0x01, 0x09, 0x11, 0x19, - 0x00, 0x08, 0x10, 0x18, 0x20, 0x28, 0x30, 0x38, - 0x40, 0x48, 0x50, 0x58, 0x60, 0x68, 0x70, 0x78}; +static relegated noinline int64_t ComputeAddressReal(const struct Machine *m, + uint32_t rde, uint8_t *s, + uint64_t i) { + switch (ModrmRm(rde)) { + case 0: + i += Read16(m->bx); + i += Read16(m->si); + break; + case 1: + i += Read16(m->bx); + i += Read16(m->di); + break; + case 2: + s = m->ss; + i += Read16(m->bp); + i += Read16(m->si); + break; + case 3: + s = m->ss; + i += Read16(m->bp); + i += Read16(m->di); + break; + case 4: + i += Read16(m->si); + break; + case 5: + i += Read16(m->di); + break; + case 6: + if (ModrmMod(rde)) { + s = m->ss; + i += Read16(m->bp); + } + break; + case 7: + i += Read16(m->bx); + break; + default: + unreachable; + } + i &= 0xffff; + return AddSegment(m, rde, i, s); +} int64_t ComputeAddress(const struct Machine *m, uint32_t rde) { - uint64_t i; - uint8_t *s; + uint8_t *s = m->ds; + uint64_t i = m->xedd->op.disp; DCHECK(!IsModrmRegister(rde)); - s = m->ds; - i = m->xedd->op.disp; if (Eamode(rde) != XED_MODE_REAL) { if (!SibExists(rde)) { if (IsRipRelative(rde)) { @@ -75,47 +100,10 @@ int64_t ComputeAddress(const struct Machine *m, uint32_t rde) { if (Eamode(rde) == XED_MODE_LEGACY) { i &= 0xffffffff; } + return AddSegment(m, rde, i, s); } else { - switch (ModrmRm(rde)) { - case 0: - i += Read16(m->bx); - i += Read16(m->si); - break; - case 1: - i += Read16(m->bx); - i += Read16(m->di); - break; - case 2: - s = m->ss; - i += Read16(m->bp); - i += Read16(m->si); - break; - case 3: - s = m->ss; - i += Read16(m->bp); - i += Read16(m->di); - break; - case 4: - i += Read16(m->si); - break; - case 5: - i += Read16(m->di); - break; - case 6: - if (ModrmMod(rde)) { - s = m->ss; - i += Read16(m->bp); - } - break; - case 7: - i += Read16(m->bx); - break; - default: - unreachable; - } - i &= 0xffff; + return ComputeAddressReal(m, rde, s, i); } - return AddSegment(m, rde, i, s); } void *ComputeReserveAddressRead(struct Machine *m, uint32_t rde, size_t n) { diff --git a/tool/build/lib/modrm.h b/tool/build/lib/modrm.h index 3c327334a..afa2cf74b 100644 --- a/tool/build/lib/modrm.h +++ b/tool/build/lib/modrm.h @@ -2,8 +2,6 @@ #define COSMOPOLITAN_TOOL_BUILD_LIB_MODRM_H_ #include "tool/build/lib/abp.h" #include "tool/build/lib/machine.h" -#if !(__ASSEMBLER__ + __LINKER__ + 0) -COSMOPOLITAN_C_START_ #define Rex(x) ((x & 000000000020) >> 004) #define Osz(x) ((x & 000000000040) >> 005) @@ -21,12 +19,16 @@ COSMOPOLITAN_C_START_ #define ModrmReg(x) ((x & 000000000007) >> 000) #define ModrmSrm(x) ((x & 000000070000) >> 014) #define ModrmMod(x) ((x & 000060000000) >> 026) +#define Modrm(x) (ModrmMod(x) << 6 | ModrmReg(x) << 3 | ModrmRm(x)) +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +#define RexbBase(m, x) (Rexb(x) << 3 | m->xedd->op.base) #define AddrByteReg(m, k) ((uint8_t *)m->reg + kByteReg[k]) #define ByteRexrReg(m, x) AddrByteReg(m, (x & 00000000037) >> 0) #define ByteRexbRm(m, x) AddrByteReg(m, (x & 00000007600) >> 7) #define ByteRexbSrm(m, x) AddrByteReg(m, (x & 00000370000) >> 12) -#define RexbBase(m, x) (Rexb(x) << 3 | m->xedd->op.base) #define RegSrm(m, x) Abp8(m->reg[(x & 00000070000) >> 12]) #define RegRexbRm(m, x) Abp8(m->reg[RexbRm(x)]) #define RegRexbSrm(m, x) Abp8(m->reg[(x & 00000170000) >> 12]) diff --git a/tool/build/lib/panel.c b/tool/build/lib/panel.c index 874271736..c144e8008 100644 --- a/tool/build/lib/panel.c +++ b/tool/build/lib/panel.c @@ -36,12 +36,13 @@ * @param p is panel list in logically sorted order * @param tyn is terminal height in cells * @param txn is terminal width in cells - * @return bytes emitted, or -1 w/ errno + * @return -1 w/ errno if an error happened * @see nblack's notcurses project too! */ ssize_t PrintPanels(int fd, long pn, struct Panel p[pn], long tyn, long txn) { wint_t wc; ssize_t rc; + size_t wrote; struct Buffer b, *l; int x, y, i, j, width; enum { kUtf8, kAnsi, kAnsiCsi } state; diff --git a/tool/build/lib/pml4t.c b/tool/build/lib/pml4t.c index f7a48c4a4..6f8800a82 100644 --- a/tool/build/lib/pml4t.c +++ b/tool/build/lib/pml4t.c @@ -53,10 +53,11 @@ static uint64_t *GetPageTable(pml4t_t p, long i, void *NewPhysicalPage(void)) { } static void PtFinder(uint64_t *a, uint64_t *b, uint64_t n, pml4t_t pd, int k) { + unsigned i; uint64_t e, c; - unsigned start; - for (start = (*b >> k) & 511; *b - *a < n && ((*b >> k) & 511) >= start;) { - e = pd[(*b >> k) & 511]; + while (*b - *a < n) { + i = (*b >> k) & 511; + e = pd[i]; c = ROUNDUP(*b + 1, 1 << k); if (!IsValidPage(e)) { *b = c; @@ -65,6 +66,9 @@ static void PtFinder(uint64_t *a, uint64_t *b, uint64_t n, pml4t_t pd, int k) { } else { *a = *b = c; } + if (((*b >> k) & 511) < i) { + break; + } } } diff --git a/tool/build/lib/pty.c b/tool/build/lib/pty.c index 74ab21ef2..c1bdc58a1 100644 --- a/tool/build/lib/pty.c +++ b/tool/build/lib/pty.c @@ -17,9 +17,11 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/alg/arraylist2.h" #include "libc/bits/bits.h" #include "libc/bits/safemacros.h" #include "libc/conv/conv.h" +#include "libc/conv/itoa.h" #include "libc/log/check.h" #include "libc/macros.h" #include "libc/mem/mem.h" @@ -32,13 +34,18 @@ #include "tool/build/lib/pty.h" struct MachinePty *MachinePtyNew(void) { - return xcalloc(1, sizeof(struct MachinePty)); + struct MachinePty *pty; + pty = xcalloc(1, sizeof(struct MachinePty)); + MachinePtyResize(pty, 25, 80); + return pty; } void MachinePtyResize(struct MachinePty *pty, int yn, int xn) { unsigned y, ym, xm, y0; uint32_t *wcs, *fgs, *bgs, *prs; - if (yn <= 0 || xn <= 0) return; + if (xn < 80) xn = 80; + if (yn < 25) yn = 25; + if (xn == pty->xn && yn == pty->yn) return; wcs = xcalloc(yn * xn, 4); fgs = xcalloc(yn * xn, 4); bgs = xcalloc(yn * xn, 4); @@ -92,7 +99,9 @@ static void MachinePtyNewline(struct MachinePty *pty) { pty->x = 0; if (++pty->y == pty->yn) { --pty->y; - MachinePtyScroll(pty); + if (!(pty->conf & kMachinePtyNoopost)) { + MachinePtyScroll(pty); + } } } @@ -335,6 +344,38 @@ static void MachinePtyShowCursor(struct MachinePty *pty) { pty->conf &= ~kMachinePtyNocursor; } +static void MachinePtyReportCursorPosition(struct MachinePty *pty) { + char *p; + char buf[2 + 10 + 1 + 10 + 1]; + p = buf; + *p++ = '\e'; + *p++ = '['; + p += uint64toarray_radix10((pty->y + 1) & 0xffffffff, p); + *p++ = ';'; + p += uint64toarray_radix10((pty->x + 1) & 0xffffffff, p); + *p++ = 'R'; + CONCAT(&pty->input.p, &pty->input.i, &pty->input.n, buf, p - buf); +} + +static void MachinePtyCsiN(struct MachinePty *pty) { + switch (atoi(pty->esc.s)) { + case 6: + MachinePtyReportCursorPosition(pty); + break; + default: + break; + } +} + +static void MachinePtyCsiScrollUp(struct MachinePty *pty) { + int n; + n = atoi(pty->esc.s); + n = MAX(1, n); + while (n--) { + MachinePtyScroll(pty); + } +} + static void MachinePtyCsi(struct MachinePty *pty) { switch (pty->esc.s[pty->esc.i - 1]) { case 'A': @@ -362,6 +403,12 @@ static void MachinePtyCsi(struct MachinePty *pty) { case 'm': MachinePtySelectGraphicsRendition(pty); break; + case 'n': + MachinePtyCsiN(pty); + break; + case 'S': + MachinePtyCsiScrollUp(pty); + break; case 'l': if (strcmp(pty->esc.s, "?25l") == 0) { MachinePtyHideCursor(pty); @@ -389,7 +436,6 @@ static void MachinePtyEscAppend(struct MachinePty *pty, char c) { ssize_t MachinePtyWrite(struct MachinePty *pty, const void *data, size_t n) { int i; const uint8_t *p; - if (!pty->yn || !pty->xn) return 0; for (p = data, i = 0; i < n; ++i) { switch (pty->state) { case kMachinePtyAscii: @@ -408,6 +454,14 @@ ssize_t MachinePtyWrite(struct MachinePty *pty, const void *data, size_t n) { case '\n': MachinePtyNewline(pty); break; + case 0177: + case '\b': + pty->x = MAX(0, pty->x - 1); + break; + case '\f': + break; + case '\a': + break; default: SetMachinePtyCell(pty, p[i]); break; @@ -483,47 +537,87 @@ ssize_t MachinePtyWrite(struct MachinePty *pty, const void *data, size_t n) { return n; } +ssize_t MachinePtyWriteInput(struct MachinePty *pty, const void *data, + size_t n) { + const char *p = data; + CONCAT(&pty->input.p, &pty->input.i, &pty->input.n, data, n); + if (!(pty->conf & kMachinePtyNoecho)) { + MachinePtyWrite(pty, data, n); + } + return n; +} + +ssize_t MachinePtyRead(struct MachinePty *pty, void *buf, size_t size) { + char *p; + size_t n; + n = MIN(size, pty->input.i); + if (!(pty->conf & kMachinePtyNocanon)) { + if ((p = memchr(pty->input.p, '\n', n))) { + n = MIN(n, pty->input.p - p + 1); + } else { + n = 0; + } + } + memcpy(buf, pty->input.p, n); + memcpy(pty->input.p, pty->input.p + n, pty->input.i - n); + pty->input.i -= n; + return n; +} + void MachinePtyAppendLine(struct MachinePty *pty, struct Buffer *buf, unsigned y) { + bool atcursor; uint32_t x, i, fg, bg, pr, wc, w; if (y >= pty->yn) return; for (fg = bg = pr = x = 0; x < pty->xn; x += w) { i = y * pty->xn + x; wc = pty->wcs[i]; w = MAX(0, wcwidth(wc)); - if (w) { - if (pty->prs[i] != pr || pty->fgs[i] != fg || pty->bgs[i] != bg) { - fg = pty->fgs[i]; - bg = pty->bgs[i]; - pr = pty->prs[i]; - AppendStr(buf, "\e[0"); - if (pr & kMachinePtyBold) AppendStr(buf, ";1"); - if (pr & kMachinePtyFaint) AppendStr(buf, ";2"); - if (pr & kMachinePtyFlip) AppendStr(buf, ";7"); - if (pr & kMachinePtyFg) { - if (pr & kMachinePtyTrue) { - AppendFmt(buf, ";38;2;%d;%d;%d", (fg & 0x0000ff) >> 000, - (fg & 0x00ff00) >> 010, (fg & 0xff0000) >> 020); - } else { - AppendFmt(buf, ";38;5;%d", fg); - } - } - if (pr & kMachinePtyBg) { - if (pr & kMachinePtyTrue) { - AppendFmt(buf, ";48;2;%d;%d;%d", (bg & 0x0000ff) >> 000, - (bg & 0x00ff00) >> 010, (bg & 0xff0000) >> 020); - } else { - AppendFmt(buf, ";48;5;%d", bg); - } - } - AppendStr(buf, "m"); + atcursor = y == pty->y && x == pty->x; + if ((w && atcursor) || + (pty->prs[i] != pr || pty->fgs[i] != fg || pty->bgs[i] != bg)) { + fg = pty->fgs[i]; + bg = pty->bgs[i]; + pr = pty->prs[i]; + AppendStr(buf, "\e[0"); + if (w && atcursor) { + pr ^= kMachinePtyFlip; } + if (pr & kMachinePtyBold) AppendStr(buf, ";1"); + if (pr & kMachinePtyFaint) AppendStr(buf, ";2"); + if (pr & kMachinePtyBlink) AppendStr(buf, ";5"); + if (pr & kMachinePtyFlip) AppendStr(buf, ";7"); + if (pr & kMachinePtyFg) { + if (pr & kMachinePtyTrue) { + AppendFmt(buf, ";38;2;%d;%d;%d", (fg & 0x0000ff) >> 000, + (fg & 0x00ff00) >> 010, (fg & 0xff0000) >> 020); + } else { + AppendFmt(buf, ";38;5;%d", fg); + } + } + if (pr & kMachinePtyBg) { + if (pr & kMachinePtyTrue) { + AppendFmt(buf, ";48;2;%d;%d;%d", (bg & 0x0000ff) >> 000, + (bg & 0x00ff00) >> 010, (bg & 0xff0000) >> 020); + } else { + AppendFmt(buf, ";48;5;%d", bg); + } + } + AppendStr(buf, "m"); + } + if (w) { AppendWide(buf, wc); } else { w = 1; - if (y == pty->y && x == pty->x) { + if (atcursor) { if (!(pty->conf & kMachinePtyNocursor)) { - AppendStr(buf, "\e[5m▂\e[25m"); + if (pty->conf & kMachinePtyBlinkcursor) { + AppendStr(buf, "\e[5m"); + } + AppendWide(buf, u'▂'); + if (pty->conf & kMachinePtyBlinkcursor) { + AppendStr(buf, "\e[25m"); + } } } else { AppendChar(buf, ' '); diff --git a/tool/build/lib/pty.h b/tool/build/lib/pty.h index fd72299d4..e5a9ec093 100644 --- a/tool/build/lib/pty.h +++ b/tool/build/lib/pty.h @@ -8,17 +8,22 @@ #define kMachinePtyBold 0x08 #define kMachinePtyFaint 0x10 #define kMachinePtyFlip 0x20 +#define kMachinePtyBlink 0x40 -#define kMachinePtyNocursor 0x01 +#define kMachinePtyNocursor 0x01 +#define kMachinePtyBlinkcursor 0x02 +#define kMachinePtyNocanon 0x04 +#define kMachinePtyNoecho 0x08 +#define kMachinePtyNoopost 0x10 #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ struct MachinePty { - uint32_t yn; - uint32_t xn; - uint32_t y; - uint32_t x; + int y; + int x; + int yn; + int xn; uint32_t pr; uint32_t fg; uint32_t bg; @@ -38,12 +43,18 @@ struct MachinePty { unsigned i; char s[64]; } esc; + struct MachinePtyInput { + size_t i, n; + char *p; + } input; }; void MachinePtyFree(struct MachinePty *); struct MachinePty *MachinePtyNew(void) nodiscard; void MachinePtyResize(struct MachinePty *, int, int); +ssize_t MachinePtyRead(struct MachinePty *, void *, size_t); ssize_t MachinePtyWrite(struct MachinePty *, const void *, size_t); +ssize_t MachinePtyWriteInput(struct MachinePty *, const void *, size_t); void MachinePtyAppendLine(struct MachinePty *, struct Buffer *, unsigned); COSMOPOLITAN_C_END_ diff --git a/tool/build/lib/sse.c b/tool/build/lib/sse.c index 8ce233e58..eb5f6da53 100644 --- a/tool/build/lib/sse.c +++ b/tool/build/lib/sse.c @@ -100,19 +100,7 @@ #include "tool/build/lib/memory.h" #include "tool/build/lib/modrm.h" #include "tool/build/lib/sse.h" - -union MachineVector { - float f32[4]; - double f64[2]; - int8_t i8[16]; - int16_t i16[8]; - int32_t i32[4]; - int64_t i64[2]; - uint8_t u8[16]; - uint16_t u16[8]; - uint32_t u32[4]; - uint64_t u64[2]; -}; +#include "tool/build/lib/throw.h" static void SsePsubb(void *b, const void *a) { psubb(b, b, a); @@ -711,29 +699,81 @@ void OpSsePmulld(struct Machine *m, uint32_t rde) { OpSse(m, rde, SsePmulld); } -void OpSseUdqIb(struct Machine *m, uint32_t rde, enum OpSseUdqIbKernel kernel) { +static void SseUdqIb(struct Machine *m, uint32_t rde, int kernel) { + void *y; uint8_t i; - union MachineVector x; + uint8_t x[16]; i = m->xedd->op.uimm0; - memcpy(&x, XmmRexbRm(m, rde), 16); + y = XmmRexbRm(m, rde); switch (kernel) { - CASE(kOpSseUdqIbPsrlw, (psrlw)(x.u16, x.u16, i)); - CASE(kOpSseUdqIbPsraw, (psraw)(x.i16, x.i16, i)); - CASE(kOpSseUdqIbPsllw, (psllw)(x.u16, x.u16, i)); - CASE(kOpSseUdqIbPsrld, (psrld)(x.u32, x.u32, i)); - CASE(kOpSseUdqIbPsrad, (psrad)(x.i32, x.i32, i)); - CASE(kOpSseUdqIbPslld, (pslld)(x.u32, x.u32, i)); - CASE(kOpSseUdqIbPsrlq, (psrlq)(x.u64, x.u64, i)); - CASE(kOpSseUdqIbPsrldq, (psrldq)(x.u8, x.u8, i)); - CASE(kOpSseUdqIbPsllq, (psllq)(x.u64, x.u64, i)); - CASE(kOpSseUdqIbPslldq, (pslldq)(x.u8, x.u8, i)); + CASE(0, (psrlw)((void *)x, y, i)); + CASE(1, (psraw)((void *)x, y, i)); + CASE(2, (psllw)((void *)x, y, i)); + CASE(3, (psrld)((void *)x, y, i)); + CASE(4, (psrad)((void *)x, y, i)); + CASE(5, (pslld)((void *)x, y, i)); + CASE(6, (psrlq)((void *)x, y, i)); + CASE(7, (psrldq)((void *)x, y, i)); + CASE(8, (psllq)((void *)x, y, i)); + CASE(9, (pslldq)((void *)x, y, i)); default: unreachable; } if (Osz(rde)) { - memcpy(XmmRexbRm(m, rde), &x, 16); + memcpy(XmmRexbRm(m, rde), x, 16); } else { - memcpy(XmmRexbRm(m, rde), &x, 8); + memcpy(XmmRexbRm(m, rde), x, 8); + } +} + +void Op171(struct Machine *m, uint32_t rde) { + switch (ModrmReg(rde)) { + case 2: + SseUdqIb(m, rde, 0); + break; + case 4: + SseUdqIb(m, rde, 1); + break; + case 6: + SseUdqIb(m, rde, 2); + break; + default: + OpUd(m, rde); + } +} + +void Op172(struct Machine *m, uint32_t rde) { + switch (ModrmReg(rde)) { + case 2: + SseUdqIb(m, rde, 3); + break; + case 4: + SseUdqIb(m, rde, 4); + break; + case 6: + SseUdqIb(m, rde, 5); + break; + default: + OpUd(m, rde); + } +} + +void Op173(struct Machine *m, uint32_t rde) { + switch (ModrmReg(rde)) { + case 2: + SseUdqIb(m, rde, 6); + break; + case 3: + SseUdqIb(m, rde, 7); + break; + case 6: + SseUdqIb(m, rde, 8); + break; + case 7: + SseUdqIb(m, rde, 9); + break; + default: + OpUd(m, rde); } } diff --git a/tool/build/lib/sse.h b/tool/build/lib/sse.h index 2fd84dcfc..8361cdccc 100644 --- a/tool/build/lib/sse.h +++ b/tool/build/lib/sse.h @@ -4,96 +4,83 @@ #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ -typedef void (*sse_f)(void *, void *, void *); - -enum OpSseUdqIbKernel { - kOpSseUdqIbPsrlw, - kOpSseUdqIbPsraw, - kOpSseUdqIbPsllw, - kOpSseUdqIbPsrld, - kOpSseUdqIbPsrad, - kOpSseUdqIbPslld, - kOpSseUdqIbPsrlq, - kOpSseUdqIbPsrldq, - kOpSseUdqIbPsllq, - kOpSseUdqIbPslldq, -}; - -void OpSseUdqIb(struct Machine *, uint32_t, enum OpSseUdqIbKernel); -void OpSsePalignr(struct Machine *, uint32_t); -void OpSsePunpcklbw(struct Machine *, uint32_t); -void OpSsePunpcklwd(struct Machine *, uint32_t); -void OpSsePunpckldq(struct Machine *, uint32_t); -void OpSsePacksswb(struct Machine *, uint32_t); -void OpSsePcmpgtb(struct Machine *, uint32_t); -void OpSsePcmpgtw(struct Machine *, uint32_t); -void OpSsePcmpgtd(struct Machine *, uint32_t); -void OpSsePackuswb(struct Machine *, uint32_t); -void OpSsePunpckhbw(struct Machine *, uint32_t); -void OpSsePunpckhwd(struct Machine *, uint32_t); -void OpSsePunpckhdq(struct Machine *, uint32_t); +void Op171(struct Machine *, uint32_t); +void Op172(struct Machine *, uint32_t); +void Op173(struct Machine *, uint32_t); +void OpSsePabsb(struct Machine *, uint32_t); +void OpSsePabsd(struct Machine *, uint32_t); +void OpSsePabsw(struct Machine *, uint32_t); void OpSsePackssdw(struct Machine *, uint32_t); -void OpSsePunpcklqdq(struct Machine *, uint32_t); -void OpSsePunpckhqdq(struct Machine *, uint32_t); -void OpSsePcmpeqb(struct Machine *, uint32_t); -void OpSsePcmpeqw(struct Machine *, uint32_t); -void OpSsePcmpeqd(struct Machine *, uint32_t); -void OpSsePsrlwv(struct Machine *, uint32_t); -void OpSsePsrldv(struct Machine *, uint32_t); -void OpSsePsrlqv(struct Machine *, uint32_t); +void OpSsePacksswb(struct Machine *, uint32_t); +void OpSsePackuswb(struct Machine *, uint32_t); +void OpSsePaddb(struct Machine *, uint32_t); +void OpSsePaddd(struct Machine *, uint32_t); void OpSsePaddq(struct Machine *, uint32_t); -void OpSsePmullw(struct Machine *, uint32_t); -void OpSsePsubusb(struct Machine *, uint32_t); -void OpSsePsubusw(struct Machine *, uint32_t); -void OpSsePminub(struct Machine *, uint32_t); -void OpSsePand(struct Machine *, uint32_t); -void OpSsePaddusb(struct Machine *, uint32_t); -void OpSsePaddusw(struct Machine *, uint32_t); -void OpSsePmaxub(struct Machine *, uint32_t); -void OpSsePandn(struct Machine *, uint32_t); -void OpSsePavgb(struct Machine *, uint32_t); -void OpSsePsrawv(struct Machine *, uint32_t); -void OpSsePsradv(struct Machine *, uint32_t); -void OpSsePavgw(struct Machine *, uint32_t); -void OpSsePmulhuw(struct Machine *, uint32_t); -void OpSsePmulhw(struct Machine *, uint32_t); -void OpSsePsubsb(struct Machine *, uint32_t); -void OpSsePsubsw(struct Machine *, uint32_t); -void OpSsePminsw(struct Machine *, uint32_t); -void OpSsePor(struct Machine *, uint32_t); void OpSsePaddsb(struct Machine *, uint32_t); void OpSsePaddsw(struct Machine *, uint32_t); -void OpSsePmaxsw(struct Machine *, uint32_t); -void OpSsePxor(struct Machine *, uint32_t); -void OpSsePsllwv(struct Machine *, uint32_t); -void OpSsePslldv(struct Machine *, uint32_t); -void OpSsePsllqv(struct Machine *, uint32_t); -void OpSsePmuludq(struct Machine *, uint32_t); -void OpSsePmaddwd(struct Machine *, uint32_t); -void OpSsePsadbw(struct Machine *, uint32_t); -void OpSsePsubb(struct Machine *, uint32_t); -void OpSsePsubw(struct Machine *, uint32_t); -void OpSsePsubd(struct Machine *, uint32_t); -void OpSsePsubq(struct Machine *, uint32_t); -void OpSsePaddb(struct Machine *, uint32_t); +void OpSsePaddusb(struct Machine *, uint32_t); +void OpSsePaddusw(struct Machine *, uint32_t); void OpSsePaddw(struct Machine *, uint32_t); -void OpSsePaddd(struct Machine *, uint32_t); -void OpSsePshufb(struct Machine *, uint32_t); -void OpSsePhaddw(struct Machine *, uint32_t); +void OpSsePalignr(struct Machine *, uint32_t); +void OpSsePand(struct Machine *, uint32_t); +void OpSsePandn(struct Machine *, uint32_t); +void OpSsePavgb(struct Machine *, uint32_t); +void OpSsePavgw(struct Machine *, uint32_t); +void OpSsePcmpeqb(struct Machine *, uint32_t); +void OpSsePcmpeqd(struct Machine *, uint32_t); +void OpSsePcmpeqw(struct Machine *, uint32_t); +void OpSsePcmpgtb(struct Machine *, uint32_t); +void OpSsePcmpgtd(struct Machine *, uint32_t); +void OpSsePcmpgtw(struct Machine *, uint32_t); void OpSsePhaddd(struct Machine *, uint32_t); void OpSsePhaddsw(struct Machine *, uint32_t); -void OpSsePmaddubsw(struct Machine *, uint32_t); -void OpSsePhsubw(struct Machine *, uint32_t); +void OpSsePhaddw(struct Machine *, uint32_t); void OpSsePhsubd(struct Machine *, uint32_t); void OpSsePhsubsw(struct Machine *, uint32_t); -void OpSsePsignb(struct Machine *, uint32_t); -void OpSsePsignw(struct Machine *, uint32_t); -void OpSsePsignd(struct Machine *, uint32_t); +void OpSsePhsubw(struct Machine *, uint32_t); +void OpSsePmaddubsw(struct Machine *, uint32_t); +void OpSsePmaddwd(struct Machine *, uint32_t); +void OpSsePmaxsw(struct Machine *, uint32_t); +void OpSsePmaxub(struct Machine *, uint32_t); +void OpSsePminsw(struct Machine *, uint32_t); +void OpSsePminub(struct Machine *, uint32_t); void OpSsePmulhrsw(struct Machine *, uint32_t); -void OpSsePabsb(struct Machine *, uint32_t); -void OpSsePabsw(struct Machine *, uint32_t); -void OpSsePabsd(struct Machine *, uint32_t); +void OpSsePmulhuw(struct Machine *, uint32_t); +void OpSsePmulhw(struct Machine *, uint32_t); void OpSsePmulld(struct Machine *, uint32_t); +void OpSsePmullw(struct Machine *, uint32_t); +void OpSsePmuludq(struct Machine *, uint32_t); +void OpSsePor(struct Machine *, uint32_t); +void OpSsePsadbw(struct Machine *, uint32_t); +void OpSsePshufb(struct Machine *, uint32_t); +void OpSsePsignb(struct Machine *, uint32_t); +void OpSsePsignd(struct Machine *, uint32_t); +void OpSsePsignw(struct Machine *, uint32_t); +void OpSsePslldv(struct Machine *, uint32_t); +void OpSsePsllqv(struct Machine *, uint32_t); +void OpSsePsllwv(struct Machine *, uint32_t); +void OpSsePsradv(struct Machine *, uint32_t); +void OpSsePsrawv(struct Machine *, uint32_t); +void OpSsePsrldv(struct Machine *, uint32_t); +void OpSsePsrlqv(struct Machine *, uint32_t); +void OpSsePsrlwv(struct Machine *, uint32_t); +void OpSsePsubb(struct Machine *, uint32_t); +void OpSsePsubd(struct Machine *, uint32_t); +void OpSsePsubq(struct Machine *, uint32_t); +void OpSsePsubsb(struct Machine *, uint32_t); +void OpSsePsubsw(struct Machine *, uint32_t); +void OpSsePsubusb(struct Machine *, uint32_t); +void OpSsePsubusw(struct Machine *, uint32_t); +void OpSsePsubw(struct Machine *, uint32_t); +void OpSsePunpckhbw(struct Machine *, uint32_t); +void OpSsePunpckhdq(struct Machine *, uint32_t); +void OpSsePunpckhqdq(struct Machine *, uint32_t); +void OpSsePunpckhwd(struct Machine *, uint32_t); +void OpSsePunpcklbw(struct Machine *, uint32_t); +void OpSsePunpckldq(struct Machine *, uint32_t); +void OpSsePunpcklqdq(struct Machine *, uint32_t); +void OpSsePunpcklwd(struct Machine *, uint32_t); +void OpSsePxor(struct Machine *, uint32_t); COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/tool/build/lib/stack.c b/tool/build/lib/stack.c index 9fc484784..db1a8e276 100644 --- a/tool/build/lib/stack.c +++ b/tool/build/lib/stack.c @@ -56,12 +56,10 @@ static uint64_t ReadStackWord(uint8_t *p, uint32_t osz) { } } -void Push(struct Machine *m, uint32_t rde, uint64_t x) { +static void PushN(struct Machine *m, uint32_t rde, uint64_t x, unsigned osz) { uint64_t v; void *p[2]; uint8_t b[8]; - unsigned osz; - osz = kStackOsz[m->xedd->op.osz][Mode(rde)]; switch (Eamode(rde)) { case XED_MODE_REAL: v = (Read32(m->sp) - osz) & 0xffff; @@ -84,18 +82,21 @@ void Push(struct Machine *m, uint32_t rde, uint64_t x) { EndStore(m, v, osz, p, b); } +void Push(struct Machine *m, uint32_t rde, uint64_t x) { + PushN(m, rde, x, kStackOsz[m->xedd->op.osz][Mode(rde)]); +} + void OpPushZvq(struct Machine *m, uint32_t rde) { unsigned osz; osz = kStackOsz[m->xedd->op.osz][Mode(rde)]; - Push(m, rde, ReadStackWord(RegRexbSrm(m, rde), osz)); + PushN(m, rde, ReadStackWord(RegRexbSrm(m, rde), osz), osz); } -uint64_t Pop(struct Machine *m, uint32_t rde, uint16_t extra) { +static uint64_t PopN(struct Machine *m, uint32_t rde, uint16_t extra, + unsigned osz) { uint64_t v; void *p[2]; uint8_t b[8]; - unsigned osz; - osz = kStackOsz[m->xedd->op.osz][Mode(rde)]; switch (Eamode(rde)) { case XED_MODE_LONG: v = Read64(m->sp); @@ -117,10 +118,16 @@ uint64_t Pop(struct Machine *m, uint32_t rde, uint16_t extra) { return ReadStackWord(AccessRam(m, v, osz, p, b, true), osz); } +uint64_t Pop(struct Machine *m, uint32_t rde, uint16_t extra) { + return PopN(m, rde, extra, kStackOsz[m->xedd->op.osz][Mode(rde)]); +} + void OpPopZvq(struct Machine *m, uint32_t rde) { uint64_t x; - x = Pop(m, rde, 0); - switch (kStackOsz[m->xedd->op.osz][Mode(rde)]) { + unsigned osz; + osz = kStackOsz[m->xedd->op.osz][Mode(rde)]; + x = PopN(m, rde, 0, osz); + switch (osz) { case 8: case 4: Write64(RegRexbSrm(m, rde), x); @@ -175,8 +182,8 @@ void OpLeave(struct Machine *m, uint32_t rde) { } } -void OpRet(struct Machine *m, uint32_t rde, uint16_t n) { - m->ip = Pop(m, rde, n); +void OpRet(struct Machine *m, uint32_t rde) { + m->ip = Pop(m, rde, m->xedd->op.uimm0); } void OpBofram(struct Machine *m, uint32_t rde) { @@ -203,7 +210,7 @@ void OpPopEvq(struct Machine *m, uint32_t rde) { Pop(m, rde, 0)); } -static void Pushaw(struct Machine *m, uint32_t rde) { +static relegated void Pushaw(struct Machine *m, uint32_t rde) { uint16_t v; uint8_t b[8][2]; memcpy(b[0], m->di, 2); @@ -218,7 +225,7 @@ static void Pushaw(struct Machine *m, uint32_t rde) { VirtualRecv(m, Read64(m->ss) + v, b, sizeof(b)); } -static void Pushad(struct Machine *m, uint32_t rde) { +static relegated void Pushad(struct Machine *m, uint32_t rde) { uint32_t v; uint8_t b[8][4]; memcpy(b[0], m->di, 4); @@ -233,7 +240,35 @@ static void Pushad(struct Machine *m, uint32_t rde) { VirtualRecv(m, Read64(m->ss) + v, b, sizeof(b)); } -void OpPusha(struct Machine *m, uint32_t rde) { +static relegated void Popaw(struct Machine *m, uint32_t rde) { + uint8_t b[8][2]; + VirtualSend(m, b, Read64(m->ss) + Read16(m->sp), sizeof(b)); + Write16(m->sp, (Read32(m->sp) + sizeof(b)) & 0xffff); + memcpy(m->di, b[0], 2); + memcpy(m->si, b[1], 2); + memcpy(m->bp, b[2], 2); + memcpy(m->sp, b[3], 2); + memcpy(m->bx, b[4], 2); + memcpy(m->dx, b[5], 2); + memcpy(m->cx, b[6], 2); + memcpy(m->ax, b[7], 2); +} + +static relegated void Popad(struct Machine *m, uint32_t rde) { + uint8_t b[8][4]; + VirtualSend(m, b, Read64(m->ss) + Read32(m->sp), sizeof(b)); + Write64(m->sp, (Read32(m->sp) + sizeof(b)) & 0xffffffff); + memcpy(m->di, b[0], 4); + memcpy(m->si, b[1], 4); + memcpy(m->bp, b[2], 4); + memcpy(m->sp, b[3], 4); + memcpy(m->bx, b[4], 4); + memcpy(m->dx, b[5], 4); + memcpy(m->cx, b[6], 4); + memcpy(m->ax, b[7], 4); +} + +relegated void OpPusha(struct Machine *m, uint32_t rde) { switch (Eamode(rde)) { case XED_MODE_REAL: Pushaw(m, rde); @@ -248,35 +283,7 @@ void OpPusha(struct Machine *m, uint32_t rde) { } } -static void Popaw(struct Machine *m, uint32_t rde) { - uint8_t b[8][2]; - VirtualSend(m, b, Read64(m->ss) + Read16(m->sp), sizeof(b)); - Write16(m->sp, (Read32(m->sp) + sizeof(b)) & 0xffff); - memcpy(m->di, b[0], 2); - memcpy(m->si, b[1], 2); - memcpy(m->bp, b[2], 2); - memcpy(m->sp, b[3], 2); - memcpy(m->bx, b[4], 2); - memcpy(m->dx, b[5], 2); - memcpy(m->cx, b[6], 2); - memcpy(m->ax, b[7], 2); -} - -static void Popad(struct Machine *m, uint32_t rde) { - uint8_t b[8][4]; - VirtualSend(m, b, Read64(m->ss) + Read32(m->sp), sizeof(b)); - Write64(m->sp, (Read32(m->sp) + sizeof(b)) & 0xffffffff); - memcpy(m->di, b[0], 4); - memcpy(m->si, b[1], 4); - memcpy(m->bp, b[2], 4); - memcpy(m->sp, b[3], 4); - memcpy(m->bx, b[4], 4); - memcpy(m->dx, b[5], 4); - memcpy(m->cx, b[6], 4); - memcpy(m->ax, b[7], 4); -} - -void OpPopa(struct Machine *m, uint32_t rde) { +relegated void OpPopa(struct Machine *m, uint32_t rde) { switch (Eamode(rde)) { case XED_MODE_REAL: Popaw(m, rde); @@ -290,3 +297,15 @@ void OpPopa(struct Machine *m, uint32_t rde) { unreachable; } } + +relegated void OpCallf(struct Machine *m, uint32_t rde) { + Push(m, rde, Read64(m->cs) >> 4); + Push(m, rde, m->ip); + Write64(m->cs, m->xedd->op.uimm0 << 4); + m->ip = m->xedd->op.disp & (Osz(rde) ? 0xffff : 0xffffffff); +} + +relegated void OpRetf(struct Machine *m, uint32_t rde) { + m->ip = Pop(m, rde, 0); + Write64(m->cs, Pop(m, rde, m->xedd->op.uimm0) << 4); +} diff --git a/tool/build/lib/stack.h b/tool/build/lib/stack.h index 00c734e75..b78d09071 100644 --- a/tool/build/lib/stack.h +++ b/tool/build/lib/stack.h @@ -7,7 +7,8 @@ COSMOPOLITAN_C_START_ void Push(struct Machine *, uint32_t, uint64_t); uint64_t Pop(struct Machine *, uint32_t, uint16_t); void OpCallJvds(struct Machine *, uint32_t); -void OpRet(struct Machine *, uint32_t, uint16_t); +void OpRet(struct Machine *, uint32_t); +void OpRetf(struct Machine *, uint32_t); void OpLeave(struct Machine *, uint32_t); void OpCallEq(struct Machine *, uint32_t); void OpBofram(struct Machine *, uint32_t); @@ -20,6 +21,7 @@ void PushVq(struct Machine *, uint32_t); void OpJmpEq(struct Machine *, uint32_t); void OpPusha(struct Machine *, uint32_t); void OpPopa(struct Machine *, uint32_t); +void OpCallf(struct Machine *, uint32_t); COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/tool/build/lib/syscall.c b/tool/build/lib/syscall.c index 07f3243e9..3cc81b7dc 100644 --- a/tool/build/lib/syscall.c +++ b/tool/build/lib/syscall.c @@ -21,9 +21,11 @@ #include "libc/calls/internal.h" #include "libc/calls/ioctl.h" #include "libc/calls/struct/iovec.h" +#include "libc/calls/struct/rusage.h" #include "libc/calls/struct/sigaction-linux.h" #include "libc/calls/struct/sigaction.h" #include "libc/calls/struct/stat.h" +#include "libc/calls/struct/termios.h" #include "libc/calls/struct/timespec.h" #include "libc/calls/struct/timeval.h" #include "libc/calls/struct/winsize.h" @@ -41,10 +43,14 @@ #include "libc/sysv/consts/f.h" #include "libc/sysv/consts/fd.h" #include "libc/sysv/consts/ipproto.h" +#include "libc/sysv/consts/lock.h" +#include "libc/sysv/consts/madv.h" #include "libc/sysv/consts/map.h" #include "libc/sysv/consts/msync.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/consts/ok.h" +#include "libc/sysv/consts/prot.h" +#include "libc/sysv/consts/rusage.h" #include "libc/sysv/consts/sa.h" #include "libc/sysv/consts/sig.h" #include "libc/sysv/consts/so.h" @@ -52,6 +58,7 @@ #include "libc/sysv/consts/sol.h" #include "libc/sysv/consts/tcp.h" #include "libc/sysv/consts/termios.h" +#include "libc/sysv/consts/w.h" #include "libc/sysv/errfuns.h" #include "libc/time/struct/timezone.h" #include "libc/time/time.h" @@ -67,6 +74,14 @@ #define AT_FDCWD_LINUX -100 #define TIOCGWINSZ_LINUX 0x5413 +#define TCGETS_LINUX 0x5401 +#define TCSETS_LINUX 0x5402 +#define TCSETSW_LINUX 0x5403 +#define TCSETSF_LINUX 0x5404 +#define ISIG_LINUX 0b0000000000000001 +#define ICANON_LINUX 0b0000000000000010 +#define ECHO_LINUX 0b0000000000001000 +#define OPOST_LINUX 0b0000000000000001 #define POINTER(x) ((void *)(intptr_t)(x)) #define UNPOINTER(x) ((int64_t)(intptr_t)(x)) @@ -322,8 +337,8 @@ static unsigned XlatOpenFlags(unsigned flags) { return res; } -static int XlatFcntlCmd(int cmd) { - switch (cmd) { +static int XlatFcntlCmd(int x) { + switch (x) { XLAT(1, F_GETFD); XLAT(2, F_SETFD); XLAT(3, F_GETFL); @@ -333,8 +348,8 @@ static int XlatFcntlCmd(int cmd) { } } -static int XlatFcntlArg(int arg) { - switch (arg) { +static int XlatFcntlArg(int x) { + switch (x) { XLAT(0, 0); XLAT(1, FD_CLOEXEC); default: @@ -342,6 +357,61 @@ static int XlatFcntlArg(int arg) { } } +static int XlatAdvice(int x) { + switch (x) { + XLAT(0, MADV_NORMAL); + XLAT(1, MADV_RANDOM); + XLAT(2, MADV_SEQUENTIAL); + XLAT(3, MADV_WILLNEED); + XLAT(4, MADV_DONTNEED); + XLAT(8, MADV_FREE); + XLAT(12, MADV_MERGEABLE); + default: + return einval(); + } +} + +static int XlatLock(int x) { + unsigned res = 0; + if (x & 1) res |= LOCK_SH; + if (x & 2) res |= LOCK_EX; + if (x & 4) res |= LOCK_NB; + if (x & 8) res |= LOCK_UN; + return res; +} + +static int XlatWait(int x) { + unsigned res = 0; + if (x & 1) res |= WNOHANG; + if (x & 2) res |= WUNTRACED; + if (x & 8) res |= WCONTINUED; + return res; +} + +static int XlatRusage(int x) { + switch (x) { + XLAT(0, RUSAGE_SELF); + XLAT(-1, RUSAGE_CHILDREN); + XLAT(1, RUSAGE_THREAD); + default: + return einval(); + } +} + +static void *VirtualSendRead(struct Machine *m, void *dst, int64_t addr, + uint64_t n) { + VirtualSend(m, dst, addr, n); + SetReadAddr(m, addr, n); + return dst; +} + +static void *VirtualRecvWrite(struct Machine *m, int64_t addr, void *dst, + uint64_t n) { + VirtualRecv(m, addr, dst, n); + SetWriteAddr(m, addr, n); + return dst; +} + static struct sigaction *CoerceSigactionToCosmo( struct sigaction *dst, const struct sigaction$linux *src) { if (!src) return NULL; @@ -364,6 +434,25 @@ static struct sigaction$linux *CoerceSigactionToLinux( return dst; } +static int OpArchPrctl(struct Machine *m, int code, int64_t addr) { + switch (code) { + case ARCH_SET_GS: + Write64(m->gs, addr); + return 0; + case ARCH_SET_FS: + Write64(m->fs, addr); + return 0; + case ARCH_GET_GS: + VirtualRecvWrite(m, addr, m->gs, 8); + return 0; + case ARCH_GET_FS: + VirtualRecvWrite(m, addr, m->fs, 8); + return 0; + default: + return einval(); + } +} + static int OpMprotect(struct Machine *m, int64_t addr, uint64_t len, int prot) { return 0; } @@ -373,6 +462,25 @@ static int OpMadvise(struct Machine *m, int64_t addr, size_t length, return enosys(); } +static int64_t OpBrk(struct Machine *m, int64_t addr) { + void *real; + if (addr && addr != m->brk) { + if (addr < m->brk) { + addr = ROUNDUP(addr, FRAMESIZE); + FreePml4t(m->cr3, addr, m->brk - addr, free, munmap); + m->brk = addr; + } else { + addr = ROUNDUP(addr, FRAMESIZE); + if ((real = mmap(NULL, addr - m->brk, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)) != MAP_FAILED) { + CHECK_NE(-1, RegisterMemory(m, m->brk, real, addr - m->brk)); + m->brk = addr; + } + } + } + return m->brk; +} + static int64_t OpMmap(struct Machine *m, int64_t virt, size_t size, int prot, int flags, int fd, int64_t off) { void *real; @@ -417,8 +525,7 @@ static struct iovec *GetDirectIov(struct Machine *m, int64_t addr, int *len) { struct iovec *iov; if (!__builtin_mul_overflow(sizeof(*iov), *len, &n) && n <= 0x7ffff000) { if ((iov = malloc(n))) { - VirtualSend(m, iov, addr, n); - SetReadAddr(m, addr, n); + VirtualSendRead(m, iov, addr, n); for (i = 0; i < *len; ++i) { size = iov[i].iov_len; if ((iov[i].iov_base = GetDirectBuf( @@ -544,16 +651,14 @@ static int OpAccept4(struct Machine *m, int fd, int64_t addraddr, uint8_t b[4]; uint32_t addrsize; if ((fd = XlatFd(m, fd)) == -1) return -1; - VirtualSend(m, b, addrsizeaddr, 4); - SetReadAddr(m, addrsizeaddr, 4); + VirtualSendRead(m, b, addrsizeaddr, 4); addrsize = Read32(b); if (!(addr = malloc(addrsize))) return -1; if ((i = rc = MachineFdAdd(&m->fds)) != -1) { if ((rc = accept4(fd, addr, &addrsize, XlatSocketFlags(flags))) != -1) { Write32(b, addrsize); VirtualRecv(m, addrsizeaddr, b, 4); - VirtualRecv(m, addraddr, addr, addrsize); - SetWriteAddr(m, addraddr, addrsize); + VirtualRecvWrite(m, addraddr, addr, addrsize); m->fds.p[i].cb = &kMachineFdCbHost; m->fds.p[i].fd = rc; rc = i; @@ -572,8 +677,7 @@ static int OpConnectBind(struct Machine *m, int fd, intptr_t addraddr, void *addr; if ((fd = XlatFd(m, fd)) == -1) return -1; if (!(addr = malloc(addrsize))) return -1; - VirtualSend(m, addr, addraddr, addrsize); - SetReadAddr(m, addraddr, addrsize); + VirtualSendRead(m, addr, addraddr, addrsize); rc = impl(fd, addr, addrsize); free(addr); return rc; @@ -597,8 +701,7 @@ static int OpSetsockopt(struct Machine *m, int fd, int level, int optname, if ((optname = XlatSocketOptname(optname)) == -1) return -1; if ((fd = XlatFd(m, fd)) == -1) return -1; if (!(optval = malloc(optvalsize))) return -1; - VirtualSend(m, optval, optvaladdr, optvalsize); - SetReadAddr(m, optvaladdr, optvalsize); + VirtualSendRead(m, optval, optvaladdr, optvalsize); rc = setsockopt(fd, level, optname, optval, optvalsize); free(optval); return rc; @@ -626,17 +729,63 @@ static ssize_t OpWrite(struct Machine *m, int fd, int64_t addr, size_t size) { return rc; } -static int OpIoctl(struct Machine *m, int fd, uint64_t request, - int64_t memaddr) { +static int IoctlTiocgwinsz(struct Machine *m, int fd, int64_t addr, + int (*fn)(int, uint64_t, void *)) { int rc; struct winsize ws; + if ((rc = fn(fd, TIOCGWINSZ, &ws)) != -1) { + VirtualRecvWrite(m, addr, &ws, sizeof(ws)); + } + return rc; +} + +static int IoctlTcgets(struct Machine *m, int fd, int64_t addr, + int (*fn)(int, uint64_t, void *)) { + int rc; + struct termios tio, tio2; + if ((rc = fn(fd, TCGETS, &tio)) != -1) { + memcpy(&tio2, &tio, sizeof(tio)); + tio2.c_iflag = 0; + if (tio.c_iflag & ISIG) tio2.c_iflag |= ISIG_LINUX; + if (tio.c_iflag & ICANON) tio2.c_iflag |= ICANON_LINUX; + if (tio.c_iflag & ECHO) tio2.c_iflag |= ECHO_LINUX; + tio2.c_oflag = 0; + if (tio.c_oflag & OPOST) tio2.c_oflag |= OPOST_LINUX; + VirtualRecvWrite(m, addr, &tio2, sizeof(tio2)); + } + return rc; +} + +static int IoctlTcsets(struct Machine *m, int fd, int64_t request, int64_t addr, + int (*fn)(int, uint64_t, void *)) { + struct termios tio, tio2; + VirtualSendRead(m, &tio, addr, sizeof(tio)); + memcpy(&tio2, &tio, sizeof(tio)); + tio2.c_iflag = 0; + if (tio.c_iflag & ISIG_LINUX) tio2.c_iflag |= ISIG; + if (tio.c_iflag & ICANON_LINUX) tio2.c_iflag |= ICANON; + if (tio.c_iflag & ECHO_LINUX) tio2.c_iflag |= ECHO; + tio2.c_oflag = 0; + if (tio.c_oflag & OPOST_LINUX) tio2.c_oflag |= OPOST; + return fn(fd, request, &tio2); +} + +static int OpIoctl(struct Machine *m, int fd, uint64_t request, int64_t addr) { + int (*fn)(int, uint64_t, void *); if (!(0 <= fd && fd < m->fds.i) || !m->fds.p[fd].cb) return ebadf(); + fn = m->fds.p[fd].cb->ioctl; + fd = m->fds.p[fd].fd; switch (request) { case TIOCGWINSZ_LINUX: - rc = (m->fds.p[fd].cb->ioctl)(m->fds.p[fd].fd, TIOCGWINSZ, &ws); - VirtualRecv(m, memaddr, &ws, sizeof(ws)); - SetWriteAddr(m, memaddr, sizeof(ws)); - return rc; + return IoctlTiocgwinsz(m, fd, addr, fn); + case TCGETS_LINUX: + return IoctlTcgets(m, fd, addr, fn); + case TCSETS_LINUX: + return IoctlTcsets(m, fd, TCSETS, addr, fn); + case TCSETSW_LINUX: + return IoctlTcsets(m, fd, TCSETSW, addr, fn); + case TCSETSF_LINUX: + return IoctlTcsets(m, fd, TCSETSF, addr, fn); default: return einval(); } @@ -763,6 +912,19 @@ static int OpFcntl(struct Machine *m, int fd, int cmd, int arg) { return fcntl(fd, cmd, arg); } +static int OpFadvise(struct Machine *m, int fd, uint64_t offset, uint64_t len, + int advice) { + if ((fd = XlatFd(m, fd)) == -1) return -1; + if ((advice = XlatAdvice(advice)) == -1) return -1; + return fadvise(fd, offset, len, advice); +} + +static int OpFlock(struct Machine *m, int fd, int lock) { + if ((fd = XlatFd(m, fd)) == -1) return -1; + if ((lock = XlatLock(lock)) == -1) return -1; + return flock(fd, lock); +} + static int OpChdir(struct Machine *m, int64_t path) { return chdir(LoadStr(m, path)); } @@ -813,6 +975,37 @@ static int OpExecve(struct Machine *m, int64_t programaddr, int64_t argvaddr, return enosys(); } +static int OpWait4(struct Machine *m, int pid, int64_t opt_out_wstatus_addr, + int options, int64_t opt_out_rusage_addr) { + int rc; + int32_t wstatus; + struct rusage rusage; + if ((options = XlatWait(options)) == -1) return -1; + if ((rc = wait4(pid, &wstatus, options, &rusage)) != -1) { + if (opt_out_wstatus_addr) { + VirtualRecvWrite(m, opt_out_wstatus_addr, &wstatus, sizeof(wstatus)); + } + if (opt_out_rusage_addr) { + VirtualRecvWrite(m, opt_out_rusage_addr, &rusage, sizeof(rusage)); + } + } + return rc; +} + +static int OpGetrusage(struct Machine *m, int resource, int64_t rusageaddr) { + int rc; + struct rusage rusage; + if ((resource = XlatRusage(resource)) == -1) return -1; + if ((rc = getrusage(resource, &rusage)) != -1) { + VirtualRecvWrite(m, rusageaddr, &rusage, sizeof(rusage)); + } + return rc; +} + +static int OpGetrlimit(struct Machine *m, int resource, int64_t rlimitaddr) { + return enosys(); +} + static int64_t OpGetcwd(struct Machine *m, int64_t bufaddr, size_t size) { size_t n; char *buf; @@ -821,8 +1014,7 @@ static int64_t OpGetcwd(struct Machine *m, int64_t bufaddr, size_t size) { if (!(buf = malloc(size))) return enomem(); if ((getcwd)(buf, size)) { n = strlen(buf); - VirtualRecv(m, bufaddr, buf, n); - SetWriteAddr(m, bufaddr, n); + VirtualRecvWrite(m, bufaddr, buf, n); res = bufaddr; } else { res = -1; @@ -904,8 +1096,7 @@ static int OpPoll(struct Machine *m, int64_t fdsaddr, uint64_t nfds, struct pollfd *fds; if (!__builtin_mul_overflow(sizeof(*fds), nfds, &n) && n <= 0x7ffff000) { if ((fds = malloc(n))) { - VirtualSend(m, fds, fdsaddr, n); - SetReadAddr(m, fdsaddr, n); + VirtualSendRead(m, fds, fdsaddr, n); rc = poll(fds, nfds, timeout_ms); free(fds); return rc; @@ -924,17 +1115,53 @@ static int OpSigprocmask(struct Machine *m, int how, int64_t setaddr, if (setaddr) { set = &ss; memset(set, 0, sizeof(ss)); - VirtualSend(m, set, setaddr, 8); + VirtualSendRead(m, set, setaddr, 8); } else { set = NULL; } if ((rc = sigprocmask(XlatSig(how), set, &oldset)) != -1) { - if (setaddr) VirtualRecv(m, setaddr, set, 8); - if (oldsetaddr) VirtualRecv(m, oldsetaddr, &oldset, 8); + if (setaddr) VirtualRecvWrite(m, setaddr, set, 8); + if (oldsetaddr) VirtualRecvWrite(m, oldsetaddr, &oldset, 8); } return rc; } +static int OpGetPid(struct Machine *m) { + return getpid(); +} + +static int OpGetPpid(struct Machine *m) { + return getppid(); +} + +static int OpKill(struct Machine *m, int pid, int sig) { + return kill(pid, sig); +} + +static int OpGetUid(struct Machine *m) { + return getuid(); +} + +static int OpGetGid(struct Machine *m) { + return getgid(); +} + +static int OpGetTid(struct Machine *m) { + return gettid(); +} + +static int OpSchedYield(struct Machine *m) { + return sched_yield(); +} + +static int OpAlarm(struct Machine *m, unsigned seconds) { + return alarm(seconds); +} + +static int OpPause(struct Machine *m) { + return pause(); +} + static int DoOpen(struct Machine *m, int64_t path, int flags, int mode) { return OpOpenat(m, AT_FDCWD_LINUX, path, flags, mode); } @@ -983,6 +1210,7 @@ void OpSyscall(struct Machine *m, uint32_t rde) { SYSCALL(0x01A, OpMsync(m, di, si, dx)); SYSCALL(0x00A, OpMprotect(m, di, si, dx)); SYSCALL(0x00B, OpMunmap(m, di, si)); + SYSCALL(0x00C, OpBrk(m, di)); SYSCALL(0x00D, OpSigaction(m, di, si, dx)); SYSCALL(0x00E, OpSigprocmask(m, di, si, dx)); SYSCALL(0x010, OpIoctl(m, di, si, dx)); @@ -993,16 +1221,16 @@ void OpSyscall(struct Machine *m, uint32_t rde) { SYSCALL(0x015, DoAccess(m, di, si)); SYSCALL(0x016, OpPipe(m, di)); SYSCALL(0x017, select(di, P(si), P(dx), P(r0), P(r8))); - SYSCALL(0x018, sched_yield()); + SYSCALL(0x018, OpSchedYield(m)); SYSCALL(0x01C, OpMadvise(m, di, si, dx)); SYSCALL(0x020, OpDup(m, di)); SYSCALL(0x021, OpDup2(m, di, si)); - SYSCALL(0x022, pause()); + SYSCALL(0x022, OpPause(m)); SYSCALL(0x023, OpNanosleep(m, di, si)); SYSCALL(0x024, getitimer(di, PNN(si))); - SYSCALL(0x025, alarm(di)); + SYSCALL(0x025, OpAlarm(m, di)); SYSCALL(0x026, setitimer(di, PNN(si), P(dx))); - SYSCALL(0x027, getpid()); + SYSCALL(0x027, OpGetPid(m)); SYSCALL(0x028, sendfile(di, si, P(dx), r0)); SYSCALL(0x029, OpSocket(m, di, si, dx)); SYSCALL(0x02A, OpConnect(m, di, si, dx)); @@ -1018,11 +1246,11 @@ void OpSyscall(struct Machine *m, uint32_t rde) { SYSCALL(0x037, getsockopt(di, si, dx, PNN(r0), PNN(r8))); SYSCALL(0x039, OpFork(m)); SYSCALL(0x03B, OpExecve(m, di, si, dx)); - SYSCALL(0x03D, wait4(di, P(si), dx, P(r0))); - SYSCALL(0x03E, kill(di, si)); + SYSCALL(0x03D, OpWait4(m, di, si, dx, r0)); + SYSCALL(0x03E, OpKill(m, di, si)); SYSCALL(0x03F, uname(P(di))); SYSCALL(0x048, OpFcntl(m, di, si, dx)); - SYSCALL(0x049, flock(di, si)); + SYSCALL(0x049, OpFlock(m, di, si)); SYSCALL(0x04A, OpFsync(m, di)); SYSCALL(0x04B, OpFdatasync(m, di)); SYSCALL(0x04C, OpTruncate(m, di, si)); @@ -1039,13 +1267,13 @@ void OpSyscall(struct Machine *m, uint32_t rde) { SYSCALL(0x05A, OpChmod(m, di, si)); SYSCALL(0x05B, OpFchmod(m, di, si)); SYSCALL(0x060, OpGettimeofday(m, di, si)); - SYSCALL(0x061, getrlimit(di, P(si))); - SYSCALL(0x062, getrusage(di, P(si))); + SYSCALL(0x061, OpGetrlimit(m, di, si)); + SYSCALL(0x062, OpGetrusage(m, di, si)); SYSCALL(0x063, sysinfo(PNN(di))); SYSCALL(0x064, times(PNN(di))); - SYSCALL(0x066, getuid()); - SYSCALL(0x068, getgid()); - SYSCALL(0x06E, getppid()); + SYSCALL(0x066, OpGetUid(m)); + SYSCALL(0x068, OpGetGid(m)); + SYSCALL(0x06E, OpGetPpid(m)); SYSCALL(0x075, setresuid(di, si, dx)); SYSCALL(0x077, setresgid(di, si, dx)); SYSCALL(0x082, OpSigsuspend(m, di)); @@ -1055,10 +1283,10 @@ void OpSyscall(struct Machine *m, uint32_t rde) { SYSCALL(0x0A0, setrlimit(di, P(si))); SYSCALL(0x084, utime(PNN(di), PNN(si))); SYSCALL(0x0EB, utimes(P(di), P(si))); - SYSCALL(0x09E, arch_prctl(di, si)); - SYSCALL(0x0BA, gettid()); + SYSCALL(0x09E, OpArchPrctl(m, di, si)); + SYSCALL(0x0BA, OpGetTid(m)); SYSCALL(0x0CB, sched_setaffinity(di, si, P(dx))); - SYSCALL(0x0DD, fadvise(di, si, dx, r0)); + SYSCALL(0x0DD, OpFadvise(m, di, si, dx, r0)); SYSCALL(0x0E4, OpClockGettime(m, di, si)); SYSCALL(0x101, OpOpenat(m, di, si, dx, r0)); SYSCALL(0x102, mkdirat(XlatAfd(m, di), P(si), dx)); @@ -1075,7 +1303,7 @@ void OpSyscall(struct Machine *m, uint32_t rde) { SYSCALL(0x177, vmsplice(di, P(si), dx, r0)); CASE(0xE7, HaltMachine(m, di | 0x100)); default: - DEBUGF("missing syscall %03x", ax); + /* LOGF("missing syscall 0x%03x", ax); */ ax = enosys(); break; } diff --git a/tool/build/lib/throw.c b/tool/build/lib/throw.c index 9741e367c..53414fcc9 100644 --- a/tool/build/lib/throw.c +++ b/tool/build/lib/throw.c @@ -49,7 +49,6 @@ void ThrowProtectionFault(struct Machine *m) { } void OpUd(struct Machine *m, uint32_t rde) { - DebugBreak(); m->ip -= m->xedd->length; HaltMachine(m, kMachineUndefinedInstruction); } diff --git a/tool/build/lib/time.c b/tool/build/lib/time.c index bdd724780..87efbcdc6 100644 --- a/tool/build/lib/time.c +++ b/tool/build/lib/time.c @@ -33,13 +33,9 @@ void OpPause(struct Machine *m, uint32_t rde) { */ void OpRdtsc(struct Machine *m, uint32_t rde) { uint64_t c; -#ifdef __x86_64__ - c = rdtsc(); -#else struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); c = ts.tv_sec * 1000000000 + ts.tv_nsec; -#endif Write64(m->ax, (c >> 000) & 0xffffffff); Write64(m->dx, (c >> 040) & 0xffffffff); } @@ -50,5 +46,5 @@ void OpRdtscp(struct Machine *m, uint32_t rde) { core = 0; node = 0; tscaux = (node & 0xfff) << 12 | (core & 0xfff); - Write64(m->ax, tscaux & 0xffffffff); + Write64(m->cx, tscaux & 0xffffffff); } diff --git a/tool/build/lisp.c b/tool/build/lisp.c new file mode 100644 index 000000000..469864887 --- /dev/null +++ b/tool/build/lisp.c @@ -0,0 +1,145 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/runtime/runtime.h" +#include "libc/stdio/stdio.h" + +#define T short + +#define CONS 0 +#define ATOM 1 +#define INTEGER 2 + +#define NIL OBJECT(CONS, 0) +#define TYPE(x) ((x)&3) +#define VALUE(x) ((x) >> 2) +#define OBJECT(t, v) ((v) << 2 | (t)) + +static T Mi, Si; +static T M[2048]; +static char S[2048]; + +static noreturn void Die(void) { + abort(); +} + +static int Parse(const char *s) { + int x, j; + for (;;) { + switch (*s & 0xff) { + case ' ': + case '\t': + case '\r': + case '\n': + case '\v': + ++s; + break; + case 'A' ... 'Z': + x = Si; + do { + S[Si++] = *s++; + } while ('A' <= *s && *s <= 'Z'); + S[Si++] = 0; + M[++Mi] = OBJECT(ATOM, x); + return Mi; + case '0' ... '9': + x = 0; + do { + x *= 10; + x += *s++ - '0'; + } while ('0' <= *s && *s <= '9'); + M[++Mi] = OBJECT(INTEGER, x); + return Mi; + default: + Die(); + } + } +} + +static int PrintChar(int c) { + return putc(c, stdout); +} + +static void PrintString(const char *s) { + while (*s) PrintChar(*s++); +} + +static void PrintInteger(int x) { + int q, r; + q = x / 10; + r = x % 10; + if (q) PrintInteger(q); + PrintChar('0' + r); +} + +static void PrintObject(int i) { + int j, x; + switch (TYPE(M[i])) { + case CONS: + if ((i = VALUE(M[i]))) { + PrintChar('('); + PrintObject(i); + for (;;) { + if (TYPE(M[i + 1]) == CONS) { + if (!(i = VALUE(M[i + 1]))) break; + PrintChar(' '); + PrintObject(i); + } else { + PrintString(" . "); + PrintObject(i + 1); + break; + } + } + PrintChar(')'); + } else { + PrintString("NIL"); + } + break; + case ATOM: + for (j = VALUE(M[i]); S[j]; ++j) { + PrintChar(S[j]); + } + break; + case INTEGER: + PrintInteger(VALUE(M[i])); + break; + default: + unreachable; + } +} + +static void Print(int i) { + PrintObject(i); + PrintChar('\n'); +} + +int main(int argc, char *argv[]) { + int i; + + M[1] = OBJECT(CONS, 2); + M[2] = OBJECT(INTEGER, 123); + M[3] = OBJECT(CONS, 4); + M[4] = OBJECT(INTEGER, 456); + M[5] = OBJECT(CONS, 6); + M[6] = OBJECT(INTEGER, 789); + M[7] = NIL; + Print(1); + + return 0; +} diff --git a/tool/build/mkdeps.c b/tool/build/mkdeps.c index 6754cfbad..9bb62b913 100644 --- a/tool/build/mkdeps.c +++ b/tool/build/mkdeps.c @@ -25,6 +25,7 @@ #include "libc/bits/bits.h" #include "libc/bits/safemacros.h" #include "libc/calls/calls.h" +#include "libc/calls/struct/stat.h" #include "libc/errno.h" #include "libc/fmt/fmt.h" #include "libc/log/check.h" @@ -39,28 +40,42 @@ #include "libc/str/str.h" #include "libc/sysv/consts/madv.h" #include "libc/sysv/consts/map.h" +#include "libc/sysv/consts/o.h" #include "libc/sysv/consts/prot.h" #include "libc/x/x.h" #include "third_party/getopt/getopt.h" +#define MAX_READ FRAMESIZE + /** * @fileoverview Make dependency generator. * - * This program generates Makefile code saying which sources include - * which headers, thus improving build invalidation. + * This generates Makefile code for source -> header dependencies. * - * The same thing can be accomplished using GCC's -M flag. This tool is - * much faster. It's designed to process over 9,000 sources to generate - * 50k+ lines of make code in ~80ms using one core and a meg of ram. + * Includes look like this: + * + * - #include "root/of/repository/foo.h" + * - .include "root/of/repository/foo.inc" + * + * They do not look like this: + * + * - #include "foo.h" + * - # include "foo.h" + * - #include "foo.h" + * + * Only the first 64kb of each source file is considered. */ -static const char kSourceExts[][5] = {".s", ".S", ".c", ".cc", ".cpp"}; -static alignas(16) const char kIncludePrefix[] = "include \""; +alignas(16) const char kIncludePrefix[] = "include \""; -static const char *const kModelessPackages[] = { - "libc/nt/", - "libc/stubs/", - "libc/sysv/", +const char kSourceExts[][5] = {".s", ".S", ".c", ".cc", ".cpp"}; + +const char *const kIgnorePrefixes[] = { +#if 0 + "libc/sysv/consts/", "libc/sysv/calls/", "libc/nt/kernel32/", + "libc/nt/KernelBase/", "libc/nt/advapi32/", "libc/nt/gdi32/", + "libc/nt/ntdll/", "libc/nt/user32/", "libc/nt/shell32/", +#endif }; struct Strings { @@ -69,14 +84,14 @@ struct Strings { }; struct Source { - uint32_t hash; /* 0 means empty w/ triangle probe */ - uint32_t name; /* strings.p[name] w/ interning */ - uint32_t id; /* rehashing changes indexes */ + unsigned hash; /* 0 means empty w/ triangle probe */ + unsigned name; /* strings.p[name] w/ interning */ + unsigned id; /* rehashing changes indexes */ }; struct Edge { - int32_t from; /* sources.p[from.id] */ - int32_t to; /* sources.p[to.id] */ + unsigned from; /* sources.p[from.id] */ + unsigned to; /* sources.p[to.id] */ }; struct Sources { @@ -89,27 +104,42 @@ struct Edges { struct Edge *p; }; -int g_sourceid; -struct Strings strings; -struct Sources sources; -struct Edges edges; -const char *buildroot; -int *visited; char *out; FILE *fout; +int *visited; +unsigned counter; +struct Edges edges; +struct Strings strings; +struct Sources sources; +const char *buildroot; -static int CompareSourcesById(struct Source *a, struct Source *b) { +int CompareSourcesById(struct Source *a, struct Source *b) { return a->id > b->id ? 1 : a->id < b->id ? -1 : 0; } -static int CompareEdgesByFrom(struct Edge *a, struct Edge *b) { +int CompareEdgesByFrom(struct Edge *a, struct Edge *b) { return a->from > b->from ? 1 : a->from < b->from ? -1 : 0; } -static uint32_t Hash(const void *s, size_t l) { +unsigned Hash(const void *s, size_t l) { return max(1, crc32c(0, s, l)); } +unsigned FindFirstFromEdge(unsigned id) { + unsigned m, l, r; + l = 0; + r = edges.i; + while (l < r) { + m = (l + r) >> 1; + if (edges.p[m].from < id) { + l = m + 1; + } else { + r = m; + } + } + return l; +} + void Crunch(void) { size_t i, j; for (i = 0, j = 0; j < sources.n; ++j) { @@ -140,9 +170,9 @@ void Rehash(void) { free(old.p); } -uint32_t GetSourceId(const char *name, size_t len) { +unsigned GetSourceId(const char *name, size_t len) { size_t i, step; - uint32_t hash; + unsigned hash; i = 0; hash = Hash(name, len); if (sources.n) { @@ -167,55 +197,73 @@ uint32_t GetSourceId(const char *name, size_t len) { sources.p[i].hash = hash; sources.p[i].name = CONCAT(&strings.p, &strings.i, &strings.n, name, len); strings.p[strings.i++] = '\0'; - return (sources.p[i].id = g_sourceid++); + return (sources.p[i].id = counter++); +} + +bool ShouldSkipSource(const char *src) { + unsigned j; + for (j = 0; j < ARRAYLEN(kIgnorePrefixes); ++j) { + if (startswith(src, kIgnorePrefixes[j])) { + return true; + } + } + return false; +} + +noreturn void OnMissingFile(const char *list, const char *src) { + DCHECK_EQ(ENOENT, errno, "%s", src); + /* + * This code helps GNU Make automatically fix itself when we + * delete a source file. It removes o/.../srcs.txt or + * o/.../hdrs.txt and exits nonzero. Since we use hyphen + * notation on mkdeps related rules, the build will + * automatically restart itself. + */ + fprintf(stderr, "%s %s...\n", "Refreshing", list); + unlink(list); + exit(1); } void LoadRelationships(int argc, char *argv[]) { - struct MappedFile mf; - const char *p, *pe; - size_t i, linecap = 0; - char *line = NULL; + int fd; + ssize_t rc; + bool skipme; FILE *finpaths; + struct Edge edge; + char *line, *buf; + unsigned srcid, dependency; + size_t i, linecap, inclen, size; + const char *p, *pe, *src, *path, *pathend; + line = NULL; + linecap = 0; + inclen = strlen(kIncludePrefix); + buf = gc(xmemalign(PAGESIZE, PAGESIZE + MAX_READ + 16)); + buf += PAGESIZE; + buf[-1] = '\n'; for (i = optind; i < argc; ++i) { CHECK_NOTNULL((finpaths = fopen(argv[i], "r"))); while (getline(&line, &linecap, finpaths) != -1) { - if (mapfileread(chomp(line), &mf) == -1) { - CHECK_EQ(ENOENT, errno, "%s", line); - /* - * This code helps GNU Make automatically fix itself when we - * delete a source file. It removes o/.../srcs.txt or - * o/.../hdrs.txt and exits nonzero. Since we use hyphen - * notation on mkdeps related rules, the build will - * automatically restart itself. - */ - fprintf(stderr, "%s %s...\n", "Refreshing", argv[i]); - unlink(argv[i]); - exit(1); - } - if (mf.size) { - if (mf.size > PAGESIZE) { - madvise(mf.addr, mf.size, MADV_WILLNEED | MADV_SEQUENTIAL); - } - uint32_t sauce = GetSourceId(line, strlen(line)); - size_t inclen = strlen(kIncludePrefix); - p = mf.addr; - pe = p + mf.size; - while ((p = strstr(p, kIncludePrefix))) { - const char *path = p + inclen; - const char *pathend = memchr(path, '"', pe - path); - if (pathend && (intptr_t)p > (intptr_t)mf.addr && - (p[-1] == '#' || p[-1] == '.') && - (p - 1 == mf.addr || p[-2] == '\n')) { - uint32_t dependency = GetSourceId(path, pathend - path); - struct Edge edge; - edge.from = sauce; - edge.to = dependency; - append(&edges, &edge); - } - p = path; + src = chomp(line); + if (ShouldSkipSource(src)) continue; + srcid = GetSourceId(src, strlen(src)); + if ((fd = open(src, O_RDONLY)) == -1) OnMissingFile(argv[i], src); + CHECK_NE(-1, (rc = read(fd, buf, MAX_READ))); + close(fd); + size = rc; + memset(buf + size, 0, 16); + for (p = buf, pe = p + size; p < pe; ++p) { + p = strstr(p, kIncludePrefix); + if (!p) break; + path = p + inclen; + pathend = memchr(path, '"', pe - path); + if (pathend && (p[-1] == '#' || p[-1] == '.') && p[-2] == '\n') { + dependency = GetSourceId(path, pathend - path); + edge.from = srcid; + edge.to = dependency; + append(&edges, &edge); + p = pathend; } } - CHECK_NE(-1, unmapfile(&mf)); } CHECK_NE(-1, fclose(finpaths)); } @@ -258,60 +306,100 @@ const char *StripExt(const char *s) { } bool IsObjectSource(const char *name) { - for (size_t i = 0; i < ARRAYLEN(kSourceExts); ++i) { + int i; + for (i = 0; i < ARRAYLEN(kSourceExts); ++i) { if (endswith(name, kSourceExts[i])) return true; } return false; } -void Dive(uint32_t sauce) { - for (uint32_t i = bisectcarleft((const int32_t(*)[2])edges.p, edges.i, sauce); - edges.p[i].from == sauce; ++i) { - int32_t dep = edges.p[i].to; - if (bts(visited, dep)) continue; +void Dive(unsigned id) { + int i; + for (i = FindFirstFromEdge(id); i < edges.i && edges.p[i].from == id; ++i) { + if (bts(visited, edges.p[i].to)) continue; fputs(" \\\n\t", fout); - fputs(&strings.p[sources.p[dep].name], fout); - Dive(dep); + fputs(&strings.p[sources.p[edges.p[i].to].name], fout); + Dive(edges.p[i].to); } } -bool IsModeless(const char *path) { - size_t i; - for (i = 0; i < ARRAYLEN(kModelessPackages); ++i) { - if (startswith(path, kModelessPackages[i])) return true; +size_t GetFileSizeOrZero(const char *path) { + struct stat st; + st.st_size = 0; + stat(path, &st); + return st.st_size; +} + +bool FilesHaveSameContent(const char *path1, const char *path2) { + bool r; + int c1, c2; + size_t s1, s2; + FILE *f1, *f2; + s1 = GetFileSizeOrZero(path1); + s2 = GetFileSizeOrZero(path2); + if (s1 == s2) { + r = true; + if (s1) { + CHECK_NOTNULL((f1 = fopen(path1, "r"))); + CHECK_NOTNULL((f2 = fopen(path2, "r"))); + for (;;) { + c1 = getc(f1); + c2 = getc(f2); + if (c1 != c2) { + r = false; + break; + } + if (c1 == -1) { + break; + } + } + CHECK_NE(-1, fclose(f2)); + CHECK_NE(-1, fclose(f1)); + } + } else { + r = false; } - return false; + return r; } int main(int argc, char *argv[]) { + char *tp; + bool needprefix; + size_t i, bitmaplen; + const char *path, *prefix; + showcrashreports(); out = "/dev/stdout"; GetOpts(argc, argv); - char *tmp = - !fileexists(out) || isregularfile(out) ? xasprintf("%s.tmp", out) : NULL; - CHECK_NOTNULL((fout = fopen(tmp ? tmp : out, "w"))); + tp = !fileexists(out) || isregularfile(out) ? xasprintf("%s.tmp", out) : NULL; + CHECK_NOTNULL((fout = fopen(tp ? tp : out, "w"))); LoadRelationships(argc, argv); Crunch(); - size_t bitmaplen = roundup((sources.i + 8) / 8, 4); + bitmaplen = roundup((sources.i + 8) / 8, 4); visited = malloc(bitmaplen); - for (size_t i = 0; i < sources.i; ++i) { - const char *path = &strings.p[sources.p[i].name]; + for (i = 0; i < sources.i; ++i) { + path = &strings.p[sources.p[i].name]; if (!IsObjectSource(path)) continue; - bool needprefix = !startswith(path, "o/"); - const char *prefix = !needprefix ? "" : IsModeless(path) ? "o/" : buildroot; + needprefix = !startswith(path, "o/"); + prefix = !needprefix ? "" : buildroot; fprintf(fout, "\n%s%s.o: \\\n\t%s", prefix, StripExt(path), path); memset(visited, 0, bitmaplen); bts(visited, i); Dive(i); fprintf(fout, "\n"); } - if (fclose(fout) == -1) perror(out), exit(1); - if (tmp) { - if (rename(tmp, out) == -1) perror(out), exit(1); + CHECK_NE(-1, fclose(fout)); + if (tp) { + /* prevent gnu make from restarting unless necessary */ + if (!FilesHaveSameContent(tp, out)) { + CHECK_NE(-1, rename(tp, out)); + } else { + CHECK_NE(-1, unlink(tp)); + } } free_s(&strings.p); free_s(&sources.p); free_s(&edges.p); free_s(&visited); - free_s(&tmp); + free_s(&tp); return 0; } diff --git a/tool/build/package.c b/tool/build/package.c index ba8851db3..dfa7a1611 100644 --- a/tool/build/package.c +++ b/tool/build/package.c @@ -352,7 +352,8 @@ void OpenObject(struct Package *pkg, struct Object *obj, int mode, int prot, CHECK_NE(MAP_FAILED, (obj->elf = mmap(NULL, (obj->size = st.st_size), prot, flags, fd, 0))); CHECK_NE(-1, close(fd)); - CHECK(iself64binary(obj->elf, obj->size)); + CHECK(iself64binary(obj->elf, obj->size), "path=%`'s", + &pkg->strings.p[obj->path]); CHECK_NOTNULL((obj->strs = getelfstringtable(obj->elf, obj->size))); CHECK_NOTNULL( (obj->syms = getelfsymboltable(obj->elf, obj->size, &obj->symcount))); diff --git a/tool/build/tinyemu.c b/tool/build/tinyemu.c index c8511352e..93839ed68 100644 --- a/tool/build/tinyemu.c +++ b/tool/build/tinyemu.c @@ -36,7 +36,6 @@ int main(int argc, char *argv[]) { int rc; struct Elf elf; const char *codepath; - showcrashreports(); codepath = argv[1]; if (argc < 2) { fputs("Usage: ", stderr); diff --git a/tool/calc/calc.c b/tool/calc/calc.c new file mode 100644 index 000000000..0a6c17559 --- /dev/null +++ b/tool/calc/calc.c @@ -0,0 +1,900 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/bits/bits.h" +#include "libc/bits/safemacros.h" +#include "libc/calls/calls.h" +#include "libc/conv/conv.h" +#include "libc/errno.h" +#include "libc/log/log.h" +#include "libc/macros.h" +#include "libc/mem/mem.h" +#include "libc/nexgen32e/bsf.h" +#include "libc/nexgen32e/bsr.h" +#include "libc/str/str.h" +#include "libc/sysv/consts/o.h" +#include "libc/time/time.h" +#include "libc/x/x.h" +#include "o/tool/calc/calc.c.inc" +#include "o/tool/calc/calc.h.inc" +#include "tool/calc/calc.h" + +/** + * make -j8 o//tool/calc + * rlwrap -A -H ~/.calc -f tool/calc/calc.lst -e\( o//tool/calc/calc.com + * @see https://github.com/hanslub42/rlwrap + */ + +static jmp_buf jb; +static int g_line; +static int g_column; +static const char *g_file; +static yyParser g_parser[1]; + +noreturn static void Error(const char *msg) { + fprintf(stderr, "%s:%d:%d: %s\n", g_file, g_line, g_column, msg); + longjmp(jb, 1); +} + +noreturn static void SyntaxError(void) { + Error("SYNTAX ERROR"); +} + +noreturn static void LexError(void) { + Error("LEX ERROR"); +} + +noreturn static void MissingArgumentError(void) { + Error("MISSING ARGUMENT"); +} + +noreturn static void MissingFunctionError(void) { + Error("MISSING FUNCTION"); +} + +noreturn static void SyscallError(const char *name) { + fprintf(stderr, "ERROR: %s[%s]: %d\n", name, g_file, errno); + exit(1); +} + +static void NumbersFree(struct Numbers *n) { + if (n) { + NumbersFree(n->n); + free(n); + } +} + +static struct Numbers *NumbersAppend(struct Numbers *n, long double x) { + struct Numbers *a; + a = malloc(sizeof(struct Numbers)); + a->n = n; + a->x = x; + return a; +} + +static long double ParseNumber(struct Token t) { + char *ep; + ep = t.s + t.n; + if (t.s[0] == '0') { + return strtoumax(t.s, &ep, 0); + } else { + return strtod(t.s, &ep); + } +} + +static long double FnAtan2(struct Numbers *a) { + if (!a || !a->n) MissingArgumentError(); + return atan2l(a->n->x, a->x); +} + +static long double FnLdexp(struct Numbers *a) { + if (!a || !a->n) MissingArgumentError(); + return ldexpl(a->n->x, a->x); +} + +static long double FnCopysign(struct Numbers *a) { + if (!a || !a->n) MissingArgumentError(); + return copysignl(a->n->x, a->x); +} + +static long double FnFmax(struct Numbers *a) { + if (!a || !a->n) MissingArgumentError(); + return fmaxl(a->n->x, a->x); +} + +static long double FnFmin(struct Numbers *a) { + if (!a || !a->n) MissingArgumentError(); + return fminl(a->n->x, a->x); +} + +static long double FnFmod(struct Numbers *a) { + if (!a || !a->n) MissingArgumentError(); + return fmodl(a->n->x, a->x); +} + +static long double FnHypot(struct Numbers *a) { + if (!a || !a->n) MissingArgumentError(); + return hypotl(a->n->x, a->x); +} + +static long double FnPowi(struct Numbers *a) { + if (!a || !a->n) MissingArgumentError(); + return powil(a->n->x, a->x); +} + +static long double FnPow(struct Numbers *a) { + if (!a || !a->n) MissingArgumentError(); + return powl(a->n->x, a->x); +} + +static long double FnScalb(struct Numbers *a) { + if (!a || !a->n) MissingArgumentError(); + return scalbl(a->n->x, a->x); +} + +static long double FnIsgreater(struct Numbers *a) { + if (!a || !a->n) MissingArgumentError(); + return isgreater(a->n->x, a->x); +} + +static long double FnRemainder(struct Numbers *a) { + if (!a || !a->n) MissingArgumentError(); + return remainderl(a->n->x, a->x); +} + +static long double FnIsgreaterequal(struct Numbers *a) { + if (!a || !a->n) MissingArgumentError(); + return isgreaterequal(a->n->x, a->x); +} + +static long double FnIsless(struct Numbers *a) { + if (!a || !a->n) MissingArgumentError(); + return isless(a->n->x, a->x); +} + +static long double FnIslessequal(struct Numbers *a) { + if (!a || !a->n) MissingArgumentError(); + return islessequal(a->n->x, a->x); +} + +static long double FnIslessgreater(struct Numbers *a) { + if (!a || !a->n) MissingArgumentError(); + return islessgreater(a->n->x, a->x); +} + +static long double FnIsunordered(struct Numbers *a) { + if (!a || !a->n) MissingArgumentError(); + return isunordered(a->n->x, a->x); +} + +static long double FnRounddown(struct Numbers *a) { + if (!a || !a->n) MissingArgumentError(); + return ROUNDDOWN((int128_t)a->n->x, (int128_t)a->x); +} + +static long double FnRoundup(struct Numbers *a) { + if (!a || !a->n) MissingArgumentError(); + return ROUNDUP((int128_t)a->n->x, (int128_t)a->x); +} + +static long double FnAcos(struct Numbers *a) { + if (!a) MissingArgumentError(); + return acosl(a->x); +} + +static long double FnAsin(struct Numbers *a) { + if (!a) MissingArgumentError(); + return asinl(a->x); +} + +static long double FnAtan(struct Numbers *a) { + if (!a) MissingArgumentError(); + return atanl(a->x); +} + +static long double FnCbrt(struct Numbers *a) { + if (!a) MissingArgumentError(); + return cbrtl(a->x); +} + +static long double FnCeil(struct Numbers *a) { + if (!a) MissingArgumentError(); + return ceill(a->x); +} + +static long double FnCos(struct Numbers *a) { + if (!a) MissingArgumentError(); + return cosl(a->x); +} + +static long double FnExp10(struct Numbers *a) { + if (!a) MissingArgumentError(); + return exp10l(a->x); +} + +static long double FnExp2(struct Numbers *a) { + if (!a) MissingArgumentError(); + return exp2l(a->x); +} + +static long double FnExp(struct Numbers *a) { + if (!a) MissingArgumentError(); + return expl(a->x); +} + +static long double FnExpm1(struct Numbers *a) { + if (!a) MissingArgumentError(); + return expm1l(a->x); +} + +static long double FnFabs(struct Numbers *a) { + if (!a) MissingArgumentError(); + return fabsl(a->x); +} + +static long double FnFloor(struct Numbers *a) { + if (!a) MissingArgumentError(); + return floorl(a->x); +} + +static long double FnIlogb(struct Numbers *a) { + if (!a) MissingArgumentError(); + return ilogbl(a->x); +} + +static long double FnLog10(struct Numbers *a) { + if (!a) MissingArgumentError(); + return log10l(a->x); +} + +static long double FnLog1p(struct Numbers *a) { + if (!a) MissingArgumentError(); + return log1pl(a->x); +} + +static long double FnLog2(struct Numbers *a) { + if (!a) MissingArgumentError(); + return log2l(a->x); +} + +static long double FnLogb(struct Numbers *a) { + if (!a) MissingArgumentError(); + return logbl(a->x); +} + +static long double FnLog(struct Numbers *a) { + if (!a) MissingArgumentError(); + return logl(a->x); +} + +static long double FnLrint(struct Numbers *a) { + if (!a) MissingArgumentError(); + return lrintl(a->x); +} + +static long double FnLround(struct Numbers *a) { + if (!a) MissingArgumentError(); + return lroundl(a->x); +} + +static long double FnNearbyint(struct Numbers *a) { + if (!a) MissingArgumentError(); + return nearbyintl(a->x); +} + +static long double FnRint(struct Numbers *a) { + if (!a) MissingArgumentError(); + return rintl(a->x); +} + +static long double FnRound(struct Numbers *a) { + if (!a) MissingArgumentError(); + return roundl(a->x); +} + +static long double FnSignificand(struct Numbers *a) { + if (!a) MissingArgumentError(); + return significandl(a->x); +} + +static long double FnSin(struct Numbers *a) { + if (!a) MissingArgumentError(); + return sinl(a->x); +} + +static long double FnSqrt(struct Numbers *a) { + if (!a) MissingArgumentError(); + return sqrtl(a->x); +} + +static long double FnTan(struct Numbers *a) { + if (!a) MissingArgumentError(); + return tanl(a->x); +} + +static long double FnTrunc(struct Numbers *a) { + if (!a) MissingArgumentError(); + return truncl(a->x); +} + +static long double FnIsinf(struct Numbers *a) { + if (!a) MissingArgumentError(); + return isinf(a->x); +} + +static long double FnIsnan(struct Numbers *a) { + if (!a) MissingArgumentError(); + return isnan(a->x); +} + +static long double FnIsfinite(struct Numbers *a) { + if (!a) MissingArgumentError(); + return isfinite(a->x); +} + +static long double FnIsnormal(struct Numbers *a) { + if (!a) MissingArgumentError(); + return isnormal(a->x); +} + +static long double FnSignbit(struct Numbers *a) { + if (!a) MissingArgumentError(); + return signbit(a->x); +} + +static long double FnFpclassify(struct Numbers *a) { + if (!a) MissingArgumentError(); + return fpclassify(a->x); +} + +static long double FnBsr(struct Numbers *a) { + if (!a) MissingArgumentError(); + return bsr(a->x); +} + +static long double FnBsfl(struct Numbers *a) { + if (!a) MissingArgumentError(); + return bsfl(a->x); +} + +static long double FnFfs(struct Numbers *a) { + if (!a) MissingArgumentError(); + return ffs(a->x); +} + +static long double FnFfsl(struct Numbers *a) { + if (!a) MissingArgumentError(); + return ffsl(a->x); +} + +static long double FnGray(struct Numbers *a) { + if (!a) MissingArgumentError(); + return gray(a->x); +} + +static long double FnUngray(struct Numbers *a) { + if (!a) MissingArgumentError(); + return ungray(a->x); +} + +static long double FnRounddown2pow(struct Numbers *a) { + if (!a) MissingArgumentError(); + return rounddown2pow(a->x); +} + +static long double FnRoundup2pow(struct Numbers *a) { + if (!a) MissingArgumentError(); + return roundup2pow(a->x); +} + +static long double FnRoundup2log(struct Numbers *a) { + if (!a) MissingArgumentError(); + return roundup2log(a->x); +} + +static long double FnBitreverse8(struct Numbers *a) { + if (!a) MissingArgumentError(); + return bitreverse8(a->x); +} + +static long double FnBitreverse16(struct Numbers *a) { + if (!a) MissingArgumentError(); + return bitreverse16(a->x); +} + +static long double FnBitreverse32(struct Numbers *a) { + if (!a) MissingArgumentError(); + return bitreverse32(a->x); +} + +static long double FnBitreverse64(struct Numbers *a) { + if (!a) MissingArgumentError(); + return bitreverse64(a->x); +} + +static int8_t sarb(int8_t x, uint8_t y) { + return x >> (y & 7); +} + +static int16_t sarw(int16_t x, uint8_t y) { + return x >> (y & 15); +} + +static int32_t sarl(int32_t x, uint8_t y) { + return x >> (y & 31); +} + +static int64_t sarq(int64_t x, uint8_t y) { + return x >> (y & 63); +} + +static long double FnSarb(struct Numbers *a) { + if (!a || !a->n) MissingArgumentError(); + return sarb(a->n->x, a->x); +} + +static long double FnSarw(struct Numbers *a) { + if (!a || !a->n) MissingArgumentError(); + return sarw(a->n->x, a->x); +} + +static long double FnSarl(struct Numbers *a) { + if (!a || !a->n) MissingArgumentError(); + return sarl(a->n->x, a->x); +} + +static long double FnSarq(struct Numbers *a) { + if (!a || !a->n) MissingArgumentError(); + return sarq(a->n->x, a->x); +} + +static long double FnSar(struct Numbers *a) { + if (!a || !a->n) MissingArgumentError(); + return sarq(a->n->x, a->x); +} + +static uint8_t rorb(uint8_t x, uint8_t y) { + return x >> (y & 7) | x << (8 - (y & 7)); +} + +static uint16_t rorw(uint16_t x, uint8_t y) { + return x >> (y & 15) | x << (16 - (y & 15)); +} + +static uint32_t rorl(uint32_t x, uint8_t y) { + return x >> (y & 31) | x << (32 - (y & 31)); +} + +static uint64_t rorq(uint64_t x, uint8_t y) { + return x >> (y & 63) | x << (64 - (y & 63)); +} + +static long double FnRorb(struct Numbers *a) { + if (!a || !a->n) MissingArgumentError(); + return rorb(a->n->x, a->x); +} + +static long double FnRorw(struct Numbers *a) { + if (!a || !a->n) MissingArgumentError(); + return rorw(a->n->x, a->x); +} + +static long double FnRorl(struct Numbers *a) { + if (!a || !a->n) MissingArgumentError(); + return rorl(a->n->x, a->x); +} + +static long double FnRorq(struct Numbers *a) { + if (!a || !a->n) MissingArgumentError(); + return rorq(a->n->x, a->x); +} + +static long double FnRor(struct Numbers *a) { + if (!a || !a->n) MissingArgumentError(); + return rorq(a->n->x, a->x); +} + +static uint8_t rolb(uint8_t x, uint8_t y) { + return x << (y & 7) | x >> (8 - (y & 7)); +} + +static uint16_t rolw(uint16_t x, uint8_t y) { + return x << (y & 15) | x >> (16 - (y & 15)); +} + +static uint32_t roll(uint32_t x, uint8_t y) { + return x << (y & 31) | x >> (32 - (y & 31)); +} + +static uint64_t rolq(uint64_t x, uint8_t y) { + return x << (y & 63) | x >> (64 - (y & 63)); +} + +static long double FnRolb(struct Numbers *a) { + if (!a || !a->n) MissingArgumentError(); + return rolb(a->n->x, a->x); +} + +static long double FnRolw(struct Numbers *a) { + if (!a || !a->n) MissingArgumentError(); + return rolw(a->n->x, a->x); +} + +static long double FnRoll(struct Numbers *a) { + if (!a || !a->n) MissingArgumentError(); + return roll(a->n->x, a->x); +} + +static long double FnRolq(struct Numbers *a) { + if (!a || !a->n) MissingArgumentError(); + return rolq(a->n->x, a->x); +} + +static long double FnRol(struct Numbers *a) { + if (!a || !a->n) MissingArgumentError(); + return rolq(a->n->x, a->x); +} + +static long double FnTime(struct Numbers *a) { + return nowl(); +} + +static long double FnBin(struct Numbers *a) { + if (!a) MissingArgumentError(); + printf("0b%jb\n", (uint128_t)a->x); + return 0; +} + +static long double FnOct(struct Numbers *a) { + if (!a) MissingArgumentError(); + printf("0%jo\n", (uint128_t)a->x); + return 0; +} + +static long double FnHex(struct Numbers *a) { + if (!a) MissingArgumentError(); + printf("0x%jx\n", (uint128_t)a->x); + return 0; +} + +static void PrintNumber(long double x) { + char b[32]; + g_fmt(b, x); + fputs(b, stdout); +} + +static void Print(struct Numbers *a) { + if (a) { + Print(a->n); + if (a->n) fputc(' ', stdout); + PrintNumber(a->x); + } +} + +static long double FnPrint(struct Numbers *a) { + Print(a); + fputc('\n', stdout); + return 0; +} + +static const struct Fn { + const char *s; + long double (*f)(struct Numbers *); +} kFunctions[] = { + {"abs", FnFabs}, + {"acos", FnAcos}, + {"asin", FnAsin}, + {"atan", FnAtan}, + {"atan2", FnAtan2}, + {"bin", FnBin}, + {"bitreverse16", FnBitreverse16}, + {"bitreverse32", FnBitreverse32}, + {"bitreverse64", FnBitreverse64}, + {"bitreverse8", FnBitreverse8}, + {"bsfl", FnBsfl}, + {"bsfl", FnBsfl}, + {"bsr", FnBsr}, + {"bsr", FnBsr}, + {"cbrt", FnCbrt}, + {"ceil", FnCeil}, + {"copysign", FnCopysign}, + {"cos", FnCos}, + {"exp", FnExp}, + {"exp10", FnExp10}, + {"exp2", FnExp2}, + {"expm1", FnExpm1}, + {"fabs", FnFabs}, + {"ffs", FnFfs}, + {"ffsl", FnFfsl}, + {"floor", FnFloor}, + {"fmax", FnFmax}, + {"fmin", FnFmin}, + {"fmod", FnFmod}, + {"fpclassify", FnFpclassify}, + {"gray", FnGray}, + {"hex", FnHex}, + {"hypot", FnHypot}, + {"ilogb", FnIlogb}, + {"isfinite", FnIsfinite}, + {"isgreater", FnIsgreater}, + {"isgreaterequal", FnIsgreaterequal}, + {"isinf", FnIsinf}, + {"isless", FnIsless}, + {"islessequal", FnIslessequal}, + {"islessgreater", FnIslessgreater}, + {"isnan", FnIsnan}, + {"isnormal", FnIsnormal}, + {"isunordered", FnIsunordered}, + {"ldexp", FnLdexp}, + {"ldexp", FnLdexp}, + {"log", FnLog}, + {"log10", FnLog10}, + {"log1p", FnLog1p}, + {"log2", FnLog2}, + {"logb", FnLogb}, + {"lrint", FnLrint}, + {"lround", FnLround}, + {"max", FnFmax}, + {"min", FnFmin}, + {"nearbyint", FnNearbyint}, + {"oct", FnOct}, + {"pow", FnPow}, + {"powi", FnPowi}, + {"print", FnPrint}, + {"remainder", FnRemainder}, + {"rint", FnRint}, + {"rol", FnRol}, + {"rolb", FnRolb}, + {"roll", FnRoll}, + {"rolq", FnRolq}, + {"rolw", FnRolw}, + {"ror", FnRor}, + {"rorb", FnRorb}, + {"rorl", FnRorl}, + {"rorq", FnRorq}, + {"rorw", FnRorw}, + {"round", FnRound}, + {"rounddown", FnRounddown}, + {"rounddown2pow", FnRounddown2pow}, + {"roundup", FnRoundup}, + {"roundup2log", FnRoundup2log}, + {"roundup2pow", FnRoundup2pow}, + {"sar", FnSar}, + {"sarb", FnSarb}, + {"sarl", FnSarl}, + {"sarq", FnSarq}, + {"sarw", FnSarw}, + {"scalb", FnScalb}, + {"signbit", FnSignbit}, + {"signbit", FnSignbit}, + {"significand", FnSignificand}, + {"sin", FnSin}, + {"sqrt", FnSqrt}, + {"tan", FnTan}, + {"time", FnTime}, + {"trunc", FnTrunc}, + {"ungray", FnUngray}, +}; + +static long double CallFunction(struct Token fn, struct Numbers *args) { + int l, r, m, p; + l = 0; + r = ARRAYLEN(kFunctions) - 1; + while (l <= r) { + m = (l + r) >> 1; + p = strncmp(kFunctions[m].s, fn.s, fn.n); + if (p < 0) { + l = m + 1; + } else if (p > 0) { + r = m - 1; + } else { + return kFunctions[m].f(args); + } + } + MissingFunctionError(); +} + +static void Tokenize(const char *s, size_t size) { + size_t n; + char *se; + for (se = s + size; s < se; s += n, ++g_column) { + n = 1; + switch (*s & 0xff) { + case ' ': + case '\t': + case '\v': + case '\r': + break; + case '\n': + ++g_line; + g_column = 0; + break; + case 'A' ... 'Z': + case 'a' ... 'z': + n = strspn(s, "0123456789" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz"); + Parse(g_parser, SYMBOL, (struct Token){s, n}); + break; + case '0': + n = strspn(s, "xXbB0123456789abcdefABCDEF"); + Parse(g_parser, NUMBER, (struct Token){s, n}); + n += strspn(s + n, "LUlu"); + break; + case '1' ... '9': + n = strspn(s, "0123456789."); + if (s[n] == 'e' || s[n] == 'E') { + ++n; + if (s[n] == '+' || s[n] == '-') ++n; + n += strspn(s + n, "0123456789"); + } + Parse(g_parser, NUMBER, (struct Token){s, n}); + n += strspn(s + n, "LUlu"); + break; + case '(': + Parse(g_parser, LP, (struct Token){0, 0}); + break; + case ')': + Parse(g_parser, RP, (struct Token){0, 0}); + break; + case ',': + Parse(g_parser, COMMA, (struct Token){0, 0}); + break; + case '^': + Parse(g_parser, XOR, (struct Token){0, 0}); + break; + case '%': + Parse(g_parser, REM, (struct Token){0, 0}); + break; + case '+': + Parse(g_parser, PLUS, (struct Token){0, 0}); + break; + case '-': + Parse(g_parser, MINUS, (struct Token){0, 0}); + break; + case '~': + Parse(g_parser, NOT, (struct Token){0, 0}); + break; + case '/': + if (s[1] == '/') { + Parse(g_parser, DDIV, (struct Token){0, 0}); + ++n; + } else { + Parse(g_parser, DIV, (struct Token){0, 0}); + } + break; + case '*': + if (s[1] == '*') { + Parse(g_parser, EXP, (struct Token){0, 0}); + ++n; + } else { + Parse(g_parser, MUL, (struct Token){0, 0}); + } + break; + case '|': + if (s[1] == '|') { + Parse(g_parser, LOR, (struct Token){0, 0}); + ++n; + } else { + Parse(g_parser, OR, (struct Token){0, 0}); + } + break; + case '&': + if (s[1] == '&') { + Parse(g_parser, LAND, (struct Token){0, 0}); + ++n; + } else { + Parse(g_parser, AND, (struct Token){0, 0}); + } + break; + case '!': + if (s[1] == '=') { + Parse(g_parser, NE, (struct Token){0, 0}); + ++n; + } else { + Parse(g_parser, LNOT, (struct Token){0, 0}); + } + break; + case '=': + if (s[1] == '=') { + Parse(g_parser, EQ, (struct Token){0, 0}); + ++n; + } else { + LexError(); + } + break; + case '>': + if (s[1] == '=') { + Parse(g_parser, GE, (struct Token){0, 0}); + ++n; + } else if (s[1] == '>') { + Parse(g_parser, SHR, (struct Token){0, 0}); + ++n; + } else { + Parse(g_parser, GT, (struct Token){0, 0}); + } + break; + case '<': + if (s[1] == '=') { + Parse(g_parser, LE, (struct Token){0, 0}); + ++n; + } else if (s[1] == '<') { + Parse(g_parser, SHL, (struct Token){0, 0}); + ++n; + } else { + Parse(g_parser, LT, (struct Token){0, 0}); + } + break; + default: + LexError(); + } + } +} + +int main(int argc, char *argv[]) { + int i; + int ec; + int fd; + size_t n; + char *buf; + ssize_t rc; + size_t bufcap; + if (!(ec = setjmp(jb))) { + if (argc > 1) { + ParseInit(g_parser); + bufcap = BIGPAGESIZE; + buf = malloc(bufcap); + for (i = 1; i < argc; ++i) { + g_file = argv[i]; + g_line = 0; + g_column = 0; + n = 0; /* wut */ + if ((fd = open(g_file, O_RDONLY)) == -1) SyscallError("open"); + for (;;) { + if ((rc = read(fd, buf, bufcap)) == -1) SyscallError("read"); + if (!(n = rc)) break; + Tokenize(buf, n); + } + close(fd); + Parse(g_parser, 0, (struct Token){0, 0}); + } + ParseFinalize(g_parser); + } else { + g_file = "/dev/stdin"; + g_line = 0; + g_column = 0; + buf = NULL; + bufcap = 0; + while (getline(&buf, &bufcap, stdin) != -1) { + if ((n = strlen(buf))) { + ParseInit(g_parser); + if (!setjmp(jb)) { + Tokenize("print(", 6); + Tokenize(buf, n); + Tokenize(")", 1); + Parse(g_parser, 0, (struct Token){0, 0}); + } + ParseFinalize(g_parser); + } + } + } + } + free(buf); + return ec; +} diff --git a/tool/calc/calc.h b/tool/calc/calc.h new file mode 100644 index 000000000..b50896a1e --- /dev/null +++ b/tool/calc/calc.h @@ -0,0 +1,24 @@ +#ifndef COSMOPOLITAN_TOOL_CALC_CALC_H_ +#define COSMOPOLITAN_TOOL_CALC_CALC_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +struct Token { + const char *s; + size_t n; +}; + +struct Numbers { + struct Numbers *n; + long double x; +}; + +static void SyntaxError(void) noreturn; +static long double ParseNumber(struct Token); +static void NumbersFree(struct Numbers *); +static struct Numbers *NumbersAppend(struct Numbers *, long double); +static long double CallFunction(struct Token, struct Numbers *); + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_TOOL_CALC_CALC_H_ */ diff --git a/tool/calc/calc.lst b/tool/calc/calc.lst new file mode 100644 index 000000000..1b7fccd7a --- /dev/null +++ b/tool/calc/calc.lst @@ -0,0 +1,93 @@ +abs +acos +asin +atan +atan2 +bin +bitreverse16 +bitreverse32 +bitreverse64 +bitreverse8 +bsfl +bsfl +bsr +bsr +cbrt +ceil +copysign +cos +exp +exp10 +exp2 +expm1 +fabs +ffs +ffsl +floor +fmax +fmin +fmod +fpclassify +gray +hex +hypot +ilogb +isfinite +isgreater +isgreaterequal +isinf +isless +islessequal +islessgreater +isnan +isnormal +isunordered +ldexp +ldexp +log +log10 +log1p +log2 +logb +lrint +lround +max +min +nearbyint +oct +pow +powi +print +remainder +rint +rol +rolb +roll +rolq +rolw +ror +rorb +rorl +rorq +rorw +round +rounddown +rounddown2pow +roundup +roundup2log +roundup2pow +sar +sarb +sarl +sarq +sarw +scalb +signbit +signbit +significand +sin +sqrt +tan +time +trunc +ungray diff --git a/tool/calc/calc.mk b/tool/calc/calc.mk new file mode 100644 index 000000000..2b5c75e87 --- /dev/null +++ b/tool/calc/calc.mk @@ -0,0 +1,75 @@ +#-*-mode:makefile-gmake;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐ +#───vi: set et ft=make ts=8 tw=8 fenc=utf-8 :vi───────────────────────┘ + +PKGS += TOOL_CALC + +TOOL_CALC = $(TOOL_LIB_A_DEPS) $(TOOL_LIB_A) +TOOL_CALC_A = o/$(MODE)/tool/calc/calc.a +TOOL_CALC_FILES := $(wildcard tool/calc/*) +TOOL_CALC_COMS = $(TOOL_CALC_OBJS:%.o=%.com) +TOOL_CALC_SRCS = $(filter %.c,$(TOOL_CALC_FILES)) +TOOL_CALC_HDRS = $(filter %.h,$(TOOL_CALC_FILES)) + +TOOL_CALC_OBJS = \ + $(TOOL_CALC_SRCS:%=o/$(MODE)/%.zip.o) \ + $(TOOL_CALC_SRCS:%.c=o/$(MODE)/%.o) + +TOOL_CALC_COMS = \ + $(TOOL_CALC_SRCS:%.c=o/$(MODE)/%.com) + +TOOL_CALC_BINS = \ + $(TOOL_CALC_COMS) \ + $(TOOL_CALC_COMS:%=%.dbg) + +TOOL_CALC_CHECKS = \ + $(TOOL_CALC_HDRS:%=o/$(MODE)/%.ok) + +TOOL_CALC_DIRECTDEPS = \ + LIBC_BITS \ + LIBC_CALLS \ + LIBC_CONV \ + LIBC_LOG \ + LIBC_FMT \ + LIBC_MEM \ + LIBC_NEXGEN32E \ + LIBC_RUNTIME \ + LIBC_STDIO \ + LIBC_STUBS \ + LIBC_STR \ + LIBC_SYSV \ + LIBC_TINYMATH \ + LIBC_X \ + THIRD_PARTY_COMPILER_RT \ + THIRD_PARTY_DTOA + +TOOL_CALC_DEPS := \ + $(call uniq,$(foreach x,$(TOOL_CALC_DIRECTDEPS),$($(x)))) + +$(TOOL_CALC_A): \ + tool/calc/ \ + $(TOOL_CALC_A).pkg \ + $(TOOL_CALC_OBJS) + +$(TOOL_CALC_A).pkg: \ + $(TOOL_CALC_OBJS) \ + $(foreach x,$(TOOL_CALC_DIRECTDEPS),$($(x)_A).pkg) + +o/tool/calc/calc.h.inc: o/tool/calc/calc.c.inc +o/tool/calc/calc.c.inc: \ + tool/calc/calc.y \ + $(THIRD_PARTY_LEMON) + @$(LEMON) -l -d$(@D) $< + +o/$(MODE)/tool/calc/%.com.dbg: \ + $(TOOL_CALC_DEPS) \ + $(TOOL_CALC_A) \ + o/$(MODE)/tool/calc/%.o \ + $(TOOL_CALC_A).pkg \ + $(CRT) \ + $(APE) + @$(APELINK) + +.PHONY: o/$(MODE)/tool/calc +o/$(MODE)/tool/calc: \ + $(TOOL_CALC_BINS) \ + $(TOOL_CALC_CHECKS) diff --git a/tool/calc/calc.y b/tool/calc/calc.y new file mode 100644 index 000000000..9c594a57b --- /dev/null +++ b/tool/calc/calc.y @@ -0,0 +1,81 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +%include { +#include "libc/stdio/stdio.h" +#include "tool/calc/calc.h" +#include "libc/calls/calls.h" +#include "libc/str/str.h" +#include "third_party/dtoa/dtoa.h" +#include "libc/x/x.h" +#include "libc/runtime/gc.h" +#include "libc/math.h" +} + +%token_type {struct Token} +%type number {long double} +%syntax_error { SyntaxError(); } + +%left LOR. +%left LAND. +%left OR. +%left XOR. +%left AND. +%left EQ NE. +%left LT LE GT GE. +%left SHL SHR. +%left PLUS MINUS. +%left MUL DIV DDIV REM. +%right NOT LNOT. +%right EXP. + +program ::= number. +number(A) ::= NUMBER(B). { A = ParseNumber(B); } +number(A) ::= LP number(B) RP. { A = B; } +number(A) ::= LNOT number(B). { A = !B; } +number(A) ::= NOT number(B). { A = ~(long)B; } +number(A) ::= PLUS number(B). { A = +B; } [NOT] +number(A) ::= MINUS number(B). { A = -B; } [NOT] +number(A) ::= SYMBOL(F) LP numbers(N) RP. { A = CallFunction(F, N); } +number(A) ::= number(B) EQ number(C). { A = B == C; } +number(A) ::= number(B) NE number(C). { A = B != C; } +number(A) ::= number(B) LT number(C). { A = B < C; } +number(A) ::= number(B) LE number(C). { A = B <= C; } +number(A) ::= number(B) GT number(C). { A = B > C; } +number(A) ::= number(B) GE number(C). { A = B >= C; } +number(A) ::= number(B) LOR number(C). { A = B || C; } +number(A) ::= number(B) LAND number(C). { A = B && C; } +number(A) ::= number(B) PLUS number(C). { A = B + C; } +number(A) ::= number(B) MINUS number(C). { A = B - C; } +number(A) ::= number(B) MUL number(C). { A = B * C; } +number(A) ::= number(B) DIV number(C). { A = B / C; } +number(A) ::= number(B) REM number(C). { A = remainderl(B, C); } +number(A) ::= number(B) EXP number(C). { A = powl(B, C); } +number(A) ::= number(B) DDIV number(C). { A = truncl(B / C); } +number(A) ::= number(B) OR number(C). { A = (long)B | (long)C; } +number(A) ::= number(B) XOR number(C). { A = (long)B ^ (long)C; } +number(A) ::= number(B) AND number(C). { A = (long)B & (long)C; } +number(A) ::= number(B) SHL number(C). { A = (long)B << (long)C; } +number(A) ::= number(B) SHR number(C). { A = (long)B >> (long)C; } + +%type numbers {struct Numbers *} +%destructor numbers { NumbersFree($$); } +numbers(A) ::= . { A = 0; } +numbers(A) ::= number(B). { A = NumbersAppend(0, B); } +numbers(A) ::= numbers(A) COMMA number(B). { A = NumbersAppend(A, B); } diff --git a/tool/decode/decode.mk b/tool/decode/decode.mk index 02a67d4f8..2b178df1c 100644 --- a/tool/decode/decode.mk +++ b/tool/decode/decode.mk @@ -30,6 +30,7 @@ TOOL_DECODE_DIRECTDEPS = \ LIBC_MEM \ LIBC_NEXGEN32E \ LIBC_RAND \ + LIBC_TIME \ LIBC_RUNTIME \ LIBC_RUNTIME \ LIBC_STDIO \ diff --git a/tool/emacs/cosmo-c-keywords.el b/tool/emacs/cosmo-c-keywords.el index 423fc9c0b..4186d9e96 100644 --- a/tool/emacs/cosmo-c-keywords.el +++ b/tool/emacs/cosmo-c-keywords.el @@ -128,7 +128,6 @@ "__msabi" "microarchitecture" "targetclones" - "winstruct" "testonly" "forcealignargpointer" "textexit" @@ -136,8 +135,6 @@ "noinline" "noclone" "donothing" - "byanymeansnecessary" - "threadlocal" "printfesque" "flattenout" "mallocesque" diff --git a/tool/tags/keywords.c b/tool/tags/keywords.c new file mode 100644 index 000000000..1406b62a8 --- /dev/null +++ b/tool/tags/keywords.c @@ -0,0 +1,33 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "tool/tags/keywords.inc" +#include "tool/tags/tags.h" + +/** + * Returns small number for HTTP header, or -1 if not found. + */ +int GetKeyword(const char *str, size_t len) { + const struct KeywordSlot *slot; + if ((slot = LookupKeyword(str, len))) { + return slot->code; + } else { + return -1; + } +} diff --git a/tool/tags/keywords.gperf b/tool/tags/keywords.gperf new file mode 100644 index 000000000..22da37fb1 --- /dev/null +++ b/tool/tags/keywords.gperf @@ -0,0 +1,161 @@ +%{ +#include "libc/str/str.h" +#include "tool/tags/tags.h" +#include "o/tool/tags/tags.h.inc" +%} +%compare-strncmp +%language=ANSI-C +%readonly-tables +%struct-type +%define lookup-function-name LookupKeyword +struct KeywordSlot { char *name; int code; }; +%% +auto, TK_AUTO +break, TK_BREAK +case, TK_CASE +char, TK_CHAR +const, TK_CONST +continue, TK_CONTINUE +default, TK_DEFAULT +do, TK_DO +double, TK_DOUBLE +else, TK_ELSE +enum, TK_ENUM +extern, TK_EXTERN +float, TK_FLOAT +for, TK_FOR +goto, TK_GOTO +if, TK_IF +inline, TK_INLINE +int, TK_INT +long, TK_LONG +register, TK_REGISTER +restrict, TK_RESTRICT +return, TK_RETURN +short, TK_SHORT +signed, TK_SIGNED +sizeof, TK_SIZEOF +static, TK_STATIC +struct, TK_STRUCT +switch, TK_SWITCH +typedef, TK_TYPEDEF +union, TK_UNION +unsigned, TK_UNSIGNED +void, TK_VOID +volatile, TK_VOLATILE +while, TK_WHILE +_Alignas, TK_ALIGNAS +_Alignof, TK_ALIGNOF +_Atomic, TK_ATOMIC +_Bool, TK_BOOL +_Complex, TK_COMPLEX +_Generic, TK_GENERIC +_Imaginary, TK_IMAGINARY +_Noreturn, TK_NORETURN +_Static_assert, TK_STATIC_ASSERT +_Thread_local, TK_THREAD_LOCAL +elif, TK_ELIF +endif, TK_ENDIF +ifdef, TK_IFDEF +ifndef, TK_IFNDEF +define, TK_DEFINE +undef, TK_UNDEF +include, TK_INCLUDE +line, TK_LINE +error, TK_ERROR +pragma, TK_PRAGMA +asm, TK_ASM +__attribute__, TK_ATTRIBUTE +__restrict__, TK_RESTRICT +__typeof__, TK_TYPEOF +__typeof, TK_TYPEOF +__inline, TK_INLINE +__const__, TK_CONST +__label__, TK_LABEL +__noinline__, TK_LABEL +__force_align_arg_pointer__, TK_FORCE_ALIGN_ARG_POINTER +__always_inline__, TK_ALWAYS_INLINE +__gnu_inline__, TK_GNU_INLINE +__alignof__, TK_ALIGNOF +__asm__, TK_ASM +__auto_type, TK_AUTO_TYPE +__byte__, TK_BYTE +__complex__, TK_COMPLEX +__imag__, TK_IMAG +__may_alias__, TK_MAY_ALIAS +__noreturn__, TK_NORETURN +__packed__, TK_PACKED +__pointer__, TK_POINTER +__printf__, TK_PRINTF +__real__, TK_REAL +__scanf__, TK_SCANF +__strfmon__, TK_STRFMON +__strftime__, TK_STRFTIME +__strong__, TK_STRONG +__target__, TK_TARGET +__transparent_union__, TK_TRANSPARENT_UNION +__volatile__, TK_VOLATILE +__word__, TK_WORD +__alias__, TK_ALIAS +__aligned__, TK_ALIGNED +__alloc_align__, TK_ALLOC_ALIGN +__alloc_size__, TK_ALLOC_SIZE +__artificial__, TK_ARTIFICIAL +__assume_aligned__, TK_ASSUME_ALIGNED +__cold__, TK_COLD +__constructor__, TK_CONSTRUCTOR +__destructor__, TK_DESTRUCTOR +__copy__, TK_COPY +__deprecated__, TK_DEPRECATED +__error__, TK_ERROR +__warning__, TK_WARNING +__externally_visible__, TK_EXTERNALLY_VISIBLE +__flatten__, TK_FLATTEN +__format__, TK_FORMAT +__gnu_format__, TK_GNU_FORMAT +__gnu_printf__, TK_GNU_PRINTF +__gnu_scanf__, TK_GNU_SCANF +__format_arg__, TK_FORMAT_ARG +__hot__, TK_HOT +__ifunc__, TK_IFUNC +__interrupt__, TK_INTERRUPT +__interrupt_handler__, TK_INTERRUPT_HANDLER +__no_caller_saved_registers__, TK_NO_CALLER_SAVED_REGISTERS +__leaf__, TK_LEAF +__malloc__, TK_MALLOC +__no_icf__, TK_NO_ICF +__no_instrument_function__, TK_NO_INSTRUMENT_FUNCTION +__no_profile_instrument_function__, TK_NO_PROFILE_INSTRUMENT_FUNCTION +__no_reorder__, TK_NO_REORDER +__no_sanitize__, TK_NO_SANITIZE +__no_sanitize_address__, TK_NO_SANITIZE_ADDRESS +__no_address_safety_analysis__, TK_NO_ADDRESS_SAFETY_ANALYSIS +__no_sanitize_thread__, TK_NO_SANITIZE_THREAD +__no_sanitize_undefined__, TK_NO_SANITIZE_UNDEFINED +__no_split_stack__, TK_NO_SPLIT_STACK +__no_stack_limit__, TK_NO_STACK_LIMIT +__noclone__, TK_NOCLONE +__noipa__, TK_NOIPA +__nonnull__, TK_NONNULL +__noplt__, TK_NOPLT +__nothrow__, TK_NOTHROW +__optimize__, TK_OPTIMIZE +__pure__, TK_PURE +__patchable_function_entry__, TK_PATCHABLE_FUNCTION_ENTRY +__returns_nonnull__, TK_RETURNS_NONNULL +__returns_twice__, TK_RETURNS_TWICE +__section__, TK_SECTION +__sentinel__, TK_SENTINEL +__simd__, TK_SIMD +__target_clones__, TK_TARGET_CLONES +__unused__, TK_UNUSED +__used__, TK_USED +__visibility__, TK_VISIBILITY +__warn_unused_result__, TK_WARN_UNUSED_RESULT +__params_nonnull__, TK_PARAMS_NONNULL +__weak__, TK_WEAK +__vector_size__, TK_VECTOR_SIZE +__ms_abi__, TK_MS_ABI +__mode__, TK_MODE +__optnone__, TK_OPTNONE +__nodebug__, TK_NODEBUG diff --git a/tool/tags/keywords.inc b/tool/tags/keywords.inc new file mode 100644 index 000000000..a6e470fb2 --- /dev/null +++ b/tool/tags/keywords.inc @@ -0,0 +1,516 @@ +/* ANSI-C code produced by gperf version 3.1 */ +/* Command-line: gperf keywords.gperf */ +/* Computed positions: -k'1-3,5,8' */ + +#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \ + && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \ + && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \ + && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \ + && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \ + && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \ + && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \ + && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \ + && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \ + && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \ + && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \ + && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \ + && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \ + && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \ + && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \ + && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \ + && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \ + && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \ + && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \ + && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \ + && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \ + && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \ + && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126)) +/* The character set is not based on ISO-646. */ +#error "gperf generated tables don't work with this execution character set. Please report a bug to ." +#endif + +#line 1 "keywords.gperf" + +#include "libc/str/str.h" +#include "tool/tags/tags.h" +#include "o/tool/tags/tags.h.inc" +#line 11 "keywords.gperf" +struct KeywordSlot { char *name; int code; }; + +#define TOTAL_KEYWORDS 149 +#define MIN_WORD_LENGTH 2 +#define MAX_WORD_LENGTH 34 +#define MIN_HASH_VALUE 15 +#define MAX_HASH_VALUE 485 +/* maximum key range = 471, duplicates = 0 */ + +#ifdef __GNUC__ +__inline +#else +#ifdef __cplusplus +inline +#endif +#endif +static unsigned int +hash (register const char *str, register size_t len) +{ + static const unsigned short asso_values[] = + { + 486, 486, 486, 486, 486, 486, 486, 486, 486, 486, + 486, 486, 486, 486, 486, 486, 486, 486, 486, 486, + 486, 486, 486, 486, 486, 486, 486, 486, 486, 486, + 486, 486, 486, 486, 486, 486, 486, 486, 486, 486, + 486, 486, 486, 486, 486, 486, 486, 486, 486, 486, + 486, 486, 486, 486, 486, 486, 486, 486, 486, 486, + 486, 486, 486, 486, 486, 0, 5, 5, 486, 486, + 486, 5, 486, 0, 486, 486, 486, 486, 0, 486, + 486, 486, 486, 5, 0, 486, 486, 486, 486, 486, + 486, 486, 486, 486, 486, 45, 0, 40, 120, 160, + 80, 60, 0, 130, 0, 55, 15, 486, 175, 20, + 10, 0, 5, 70, 125, 10, 40, 0, 40, 145, + 20, 20, 0, 486, 486, 486, 486, 486, 486, 486, + 486, 486, 486, 486, 486, 486, 486, 486, 486, 486, + 486, 486, 486, 486, 486, 486, 486, 486, 486, 486, + 486, 486, 486, 486, 486, 486, 486, 486, 486, 486, + 486, 486, 486, 486, 486, 486, 486, 486, 486, 486, + 486, 486, 486, 486, 486, 486, 486, 486, 486, 486, + 486, 486, 486, 486, 486, 486, 486, 486, 486, 486, + 486, 486, 486, 486, 486, 486, 486, 486, 486, 486, + 486, 486, 486, 486, 486, 486, 486, 486, 486, 486, + 486, 486, 486, 486, 486, 486, 486, 486, 486, 486, + 486, 486, 486, 486, 486, 486, 486, 486, 486, 486, + 486, 486, 486, 486, 486, 486, 486, 486, 486, 486, + 486, 486, 486, 486, 486, 486, 486, 486, 486, 486, + 486, 486, 486, 486, 486, 486, 486 + }; + register unsigned int hval = len; + + switch (hval) + { + default: + hval += asso_values[(unsigned char)str[7]+1]; + /*FALLTHROUGH*/ + case 7: + case 6: + case 5: + hval += asso_values[(unsigned char)str[4]+1]; + /*FALLTHROUGH*/ + case 4: + case 3: + hval += asso_values[(unsigned char)str[2]]; + /*FALLTHROUGH*/ + case 2: + hval += asso_values[(unsigned char)str[1]]; + /*FALLTHROUGH*/ + case 1: + hval += asso_values[(unsigned char)str[0]]; + break; + } + return hval; +} + +const struct KeywordSlot * +LookupKeyword (register const char *str, register size_t len) +{ + static const struct KeywordSlot wordlist[] = + { + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, +#line 35 "keywords.gperf" + {"short", TK_SHORT}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, +#line 28 "keywords.gperf" + {"if", TK_IF}, +#line 56 "keywords.gperf" + {"_Thread_local", TK_THREAD_LOCAL}, + {""}, {""}, {""}, {""}, {""}, +#line 54 "keywords.gperf" + {"_Noreturn", TK_NORETURN}, + {""}, {""}, {""}, {""}, {""}, +#line 42 "keywords.gperf" + {"union", TK_UNION}, +#line 60 "keywords.gperf" + {"ifndef", TK_IFNDEF}, + {""}, +#line 67 "keywords.gperf" + {"asm", TK_ASM}, +#line 23 "keywords.gperf" + {"enum", TK_ENUM}, +#line 50 "keywords.gperf" + {"_Bool", TK_BOOL}, +#line 37 "keywords.gperf" + {"sizeof", TK_SIZEOF}, + {""}, {""}, {""}, {""}, {""}, +#line 20 "keywords.gperf" + {"do", TK_DO}, + {""}, +#line 13 "keywords.gperf" + {"auto", TK_AUTO}, + {""}, {""}, {""}, +#line 43 "keywords.gperf" + {"unsigned", TK_UNSIGNED}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 119 "keywords.gperf" + {"__hot__", TK_HOT}, +#line 152 "keywords.gperf" + {"__used__", TK_USED}, +#line 44 "keywords.gperf" + {"void", TK_VOID}, + {""}, +#line 160 "keywords.gperf" + {"__optnone__", TK_OPTNONE}, +#line 49 "keywords.gperf" + {"_Atomic", TK_ATOMIC}, + {""}, +#line 55 "keywords.gperf" + {"_Static_assert", TK_STATIC_ASSERT}, + {""}, +#line 21 "keywords.gperf" + {"double", TK_DOUBLE}, + {""}, +#line 30 "keywords.gperf" + {"int", TK_INT}, + {""}, +#line 114 "keywords.gperf" + {"__format__", TK_FORMAT}, +#line 38 "keywords.gperf" + {"static", TK_STATIC}, +#line 148 "keywords.gperf" + {"__sentinel__", TK_SENTINEL}, +#line 143 "keywords.gperf" + {"__pure__", TK_PURE}, +#line 118 "keywords.gperf" + {"__format_arg__", TK_FORMAT_ARG}, +#line 130 "keywords.gperf" + {"__no_sanitize__", TK_NO_SANITIZE}, +#line 141 "keywords.gperf" + {"__nothrow__", TK_NOTHROW}, +#line 142 "keywords.gperf" + {"__optimize__", TK_OPTIMIZE}, +#line 149 "keywords.gperf" + {"__simd__", TK_SIMD}, +#line 129 "keywords.gperf" + {"__no_reorder__", TK_NO_REORDER}, +#line 94 "keywords.gperf" + {"__strong__", TK_STRONG}, +#line 88 "keywords.gperf" + {"__pointer__", TK_POINTER}, +#line 133 "keywords.gperf" + {"__no_sanitize_thread__", TK_NO_SANITIZE_THREAD}, +#line 131 "keywords.gperf" + {"__no_sanitize_address__", TK_NO_SANITIZE_ADDRESS}, +#line 138 "keywords.gperf" + {"__noipa__", TK_NOIPA}, +#line 134 "keywords.gperf" + {"__no_sanitize_undefined__", TK_NO_SANITIZE_UNDEFINED}, +#line 92 "keywords.gperf" + {"__strfmon__", TK_STRFMON}, +#line 76 "keywords.gperf" + {"__force_align_arg_pointer__", TK_FORCE_ALIGN_ARG_POINTER}, +#line 26 "keywords.gperf" + {"for", TK_FOR}, + {""}, {""}, +#line 139 "keywords.gperf" + {"__nonnull__", TK_NONNULL}, +#line 41 "keywords.gperf" + {"typedef", TK_TYPEDEF}, + {""}, {""}, +#line 158 "keywords.gperf" + {"__ms_abi__", TK_MS_ABI}, +#line 24 "keywords.gperf" + {"extern", TK_EXTERN}, +#line 93 "keywords.gperf" + {"__strftime__", TK_STRFTIME}, +#line 135 "keywords.gperf" + {"__no_split_stack__", TK_NO_SPLIT_STACK}, +#line 128 "keywords.gperf" + {"__no_profile_instrument_function__", TK_NO_PROFILE_INSTRUMENT_FUNCTION}, + {""}, +#line 81 "keywords.gperf" + {"__auto_type", TK_AUTO_TYPE}, +#line 75 "keywords.gperf" + {"__noinline__", TK_LABEL}, +#line 85 "keywords.gperf" + {"__may_alias__", TK_MAY_ALIAS}, + {""}, {""}, +#line 61 "keywords.gperf" + {"define", TK_DEFINE}, +#line 80 "keywords.gperf" + {"__asm__", TK_ASM}, +#line 51 "keywords.gperf" + {"_Complex", TK_COMPLEX}, +#line 123 "keywords.gperf" + {"__no_caller_saved_registers__", TK_NO_CALLER_SAVED_REGISTERS}, +#line 95 "keywords.gperf" + {"__target__", TK_TARGET}, + {""}, {""}, {""}, +#line 99 "keywords.gperf" + {"__alias__", TK_ALIAS}, + {""}, +#line 100 "keywords.gperf" + {"__aligned__", TK_ALIGNED}, +#line 150 "keywords.gperf" + {"__target_clones__", TK_TARGET_CLONES}, + {""}, +#line 103 "keywords.gperf" + {"__artificial__", TK_ARTIFICIAL}, + {""}, +#line 79 "keywords.gperf" + {"__alignof__", TK_ALIGNOF}, +#line 86 "keywords.gperf" + {"__noreturn__", TK_NORETURN}, +#line 155 "keywords.gperf" + {"__params_nonnull__", TK_PARAMS_NONNULL}, +#line 102 "keywords.gperf" + {"__alloc_size__", TK_ALLOC_SIZE}, +#line 101 "keywords.gperf" + {"__alloc_align__", TK_ALLOC_ALIGN}, +#line 127 "keywords.gperf" + {"__no_instrument_function__", TK_NO_INSTRUMENT_FUNCTION}, + {""}, +#line 121 "keywords.gperf" + {"__interrupt__", TK_INTERRUPT}, +#line 110 "keywords.gperf" + {"__error__", TK_ERROR}, + {""}, {""}, +#line 112 "keywords.gperf" + {"__externally_visible__", TK_EXTERNALLY_VISIBLE}, +#line 72 "keywords.gperf" + {"__inline", TK_INLINE}, +#line 27 "keywords.gperf" + {"goto", TK_GOTO}, +#line 17 "keywords.gperf" + {"const", TK_CONST}, +#line 122 "keywords.gperf" + {"__interrupt_handler__", TK_INTERRUPT_HANDLER}, +#line 97 "keywords.gperf" + {"__volatile__", TK_VOLATILE}, +#line 159 "keywords.gperf" + {"__mode__", TK_MODE}, +#line 140 "keywords.gperf" + {"__noplt__", TK_NOPLT}, +#line 25 "keywords.gperf" + {"float", TK_FLOAT}, + {""}, {""}, {""}, {""}, {""}, +#line 66 "keywords.gperf" + {"pragma", TK_PRAGMA}, +#line 19 "keywords.gperf" + {"default", TK_DEFAULT}, +#line 104 "keywords.gperf" + {"__assume_aligned__", TK_ASSUME_ALIGNED}, +#line 31 "keywords.gperf" + {"long", TK_LONG}, +#line 132 "keywords.gperf" + {"__no_address_safety_analysis__", TK_NO_ADDRESS_SAFETY_ANALYSIS}, +#line 137 "keywords.gperf" + {"__noclone__", TK_NOCLONE}, + {""}, +#line 18 "keywords.gperf" + {"continue", TK_CONTINUE}, +#line 120 "keywords.gperf" + {"__ifunc__", TK_IFUNC}, +#line 53 "keywords.gperf" + {"_Imaginary", TK_IMAGINARY}, +#line 147 "keywords.gperf" + {"__section__", TK_SECTION}, + {""}, +#line 52 "keywords.gperf" + {"_Generic", TK_GENERIC}, +#line 153 "keywords.gperf" + {"__visibility__", TK_VISIBILITY}, +#line 151 "keywords.gperf" + {"__unused__", TK_UNUSED}, +#line 36 "keywords.gperf" + {"signed", TK_SIGNED}, + {""}, {""}, +#line 16 "keywords.gperf" + {"char", TK_CHAR}, +#line 46 "keywords.gperf" + {"while", TK_WHILE}, + {""}, +#line 77 "keywords.gperf" + {"__always_inline__", TK_ALWAYS_INLINE}, + {""}, {""}, {""}, +#line 161 "keywords.gperf" + {"__nodebug__", TK_NODEBUG}, + {""}, +#line 33 "keywords.gperf" + {"restrict", TK_RESTRICT}, +#line 15 "keywords.gperf" + {"case", TK_CASE}, + {""}, {""}, {""}, +#line 82 "keywords.gperf" + {"__byte__", TK_BYTE}, + {""}, +#line 125 "keywords.gperf" + {"__malloc__", TK_MALLOC}, +#line 113 "keywords.gperf" + {"__flatten__", TK_FLATTEN}, + {""}, +#line 45 "keywords.gperf" + {"volatile", TK_VOLATILE}, + {""}, +#line 62 "keywords.gperf" + {"undef", TK_UNDEF}, + {""}, {""}, {""}, +#line 91 "keywords.gperf" + {"__scanf__", TK_SCANF}, + {""}, {""}, +#line 146 "keywords.gperf" + {"__returns_twice__", TK_RETURNS_TWICE}, + {""}, +#line 145 "keywords.gperf" + {"__returns_nonnull__", TK_RETURNS_NONNULL}, +#line 157 "keywords.gperf" + {"__vector_size__", TK_VECTOR_SIZE}, + {""}, {""}, +#line 136 "keywords.gperf" + {"__no_stack_limit__", TK_NO_STACK_LIMIT}, + {""}, +#line 126 "keywords.gperf" + {"__no_icf__", TK_NO_ICF}, +#line 34 "keywords.gperf" + {"return", TK_RETURN}, + {""}, +#line 144 "keywords.gperf" + {"__patchable_function_entry__", TK_PATCHABLE_FUNCTION_ENTRY}, +#line 64 "keywords.gperf" + {"line", TK_LINE}, +#line 87 "keywords.gperf" + {"__packed__", TK_PACKED}, +#line 29 "keywords.gperf" + {"inline", TK_INLINE}, + {""}, {""}, +#line 22 "keywords.gperf" + {"else", TK_ELSE}, +#line 89 "keywords.gperf" + {"__printf__", TK_PRINTF}, + {""}, {""}, +#line 98 "keywords.gperf" + {"__word__", TK_WORD}, + {""}, {""}, +#line 111 "keywords.gperf" + {"__warning__", TK_WARNING}, + {""}, {""}, +#line 73 "keywords.gperf" + {"__const__", TK_CONST}, + {""}, +#line 39 "keywords.gperf" + {"struct", TK_STRUCT}, + {""}, {""}, +#line 107 "keywords.gperf" + {"__destructor__", TK_DESTRUCTOR}, + {""}, {""}, {""}, +#line 47 "keywords.gperf" + {"_Alignas", TK_ALIGNAS}, + {""}, +#line 59 "keywords.gperf" + {"ifdef", TK_IFDEF}, +#line 83 "keywords.gperf" + {"__complex__", TK_COMPLEX}, +#line 63 "keywords.gperf" + {"include", TK_INCLUDE}, +#line 84 "keywords.gperf" + {"__imag__", TK_IMAG}, +#line 78 "keywords.gperf" + {"__gnu_inline__", TK_GNU_INLINE}, +#line 106 "keywords.gperf" + {"__constructor__", TK_CONSTRUCTOR}, + {""}, {""}, +#line 105 "keywords.gperf" + {"__cold__", TK_COLD}, +#line 115 "keywords.gperf" + {"__gnu_format__", TK_GNU_FORMAT}, + {""}, {""}, +#line 69 "keywords.gperf" + {"__restrict__", TK_RESTRICT}, + {""}, +#line 116 "keywords.gperf" + {"__gnu_printf__", TK_GNU_PRINTF}, +#line 58 "keywords.gperf" + {"endif", TK_ENDIF}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 57 "keywords.gperf" + {"elif", TK_ELIF}, + {""}, +#line 40 "keywords.gperf" + {"switch", TK_SWITCH}, + {""}, {""}, {""}, {""}, {""}, {""}, +#line 68 "keywords.gperf" + {"__attribute__", TK_ATTRIBUTE}, + {""}, {""}, {""}, +#line 154 "keywords.gperf" + {"__warn_unused_result__", TK_WARN_UNUSED_RESULT}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 65 "keywords.gperf" + {"error", TK_ERROR}, + {""}, {""}, +#line 108 "keywords.gperf" + {"__copy__", TK_COPY}, + {""}, {""}, {""}, {""}, {""}, +#line 109 "keywords.gperf" + {"__deprecated__", TK_DEPRECATED}, + {""}, {""}, {""}, +#line 71 "keywords.gperf" + {"__typeof", TK_TYPEOF}, + {""}, +#line 70 "keywords.gperf" + {"__typeof__", TK_TYPEOF}, +#line 96 "keywords.gperf" + {"__transparent_union__", TK_TRANSPARENT_UNION}, + {""}, +#line 90 "keywords.gperf" + {"__real__", TK_REAL}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 117 "keywords.gperf" + {"__gnu_scanf__", TK_GNU_SCANF}, + {""}, {""}, {""}, {""}, +#line 48 "keywords.gperf" + {"_Alignof", TK_ALIGNOF}, + {""}, {""}, {""}, {""}, +#line 156 "keywords.gperf" + {"__weak__", TK_WEAK}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, +#line 32 "keywords.gperf" + {"register", TK_REGISTER}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, +#line 124 "keywords.gperf" + {"__leaf__", TK_LEAF}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, +#line 74 "keywords.gperf" + {"__label__", TK_LABEL}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {""}, {""}, {""}, {""}, {""}, +#line 14 "keywords.gperf" + {"break", TK_BREAK} + }; + + if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) + { + register unsigned int key = hash (str, len); + + if (key <= MAX_HASH_VALUE) + { + register const char *s = wordlist[key].name; + + if (*str == *s && !strncmp (str + 1, s + 1, len - 1) && s[len] == '\0') + return &wordlist[key]; + } + } + return 0; +} diff --git a/tool/tags/tags.c b/tool/tags/tags.c new file mode 100644 index 000000000..f4ff10dfc --- /dev/null +++ b/tool/tags/tags.c @@ -0,0 +1,343 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/bits/bits.h" +#include "libc/bits/safemacros.h" +#include "libc/calls/calls.h" +#include "libc/conv/conv.h" +#include "libc/errno.h" +#include "libc/log/log.h" +#include "libc/macros.h" +#include "libc/mem/mem.h" +#include "libc/nexgen32e/bsf.h" +#include "libc/nexgen32e/bsr.h" +#include "libc/str/str.h" +#include "libc/sysv/consts/o.h" +#include "libc/time/time.h" +#include "libc/x/x.h" +#include "o/tool/tags/tags.c.inc" +#include "o/tool/tags/tags.h.inc" +#include "tool/tags/tags.h" + +static jmp_buf jb; +static int g_line; +static int g_column; +static const char *g_file; +static yyParser g_parser[1]; + +noreturn static void Error(const char *msg) { + fprintf(stderr, "%s:%d:%d: %s\n", g_file, g_line, g_column, msg); + longjmp(jb, 1); +} + +noreturn static void SyntaxError(void) { + Error("SYNTAX ERROR"); +} + +noreturn static void LexError(void) { + Error("LEX ERROR"); +} + +noreturn static void MissingArgumentError(void) { + Error("MISSING ARGUMENT"); +} + +noreturn static void MissingFunctionError(void) { + Error("MISSING FUNCTION"); +} + +noreturn static void SyscallError(const char *name) { + fprintf(stderr, "ERROR: %s[%s]: %d\n", name, g_file, errno); + exit(1); +} + +static void ExprsFree(struct Exprs *n) { + if (n) { + ExprsFree(n->n); + free(n); + } +} + +static struct Exprs *ExprsAppend(struct Exprs *n, long double x) { + struct Exprs *a; + a = malloc(sizeof(struct Exprs)); + a->n = n; + a->x = x; + return a; +} + +static long double ParseExpr(struct Token t) { + char *ep; + ep = t.s + t.n; + if (t.s[0] == '0') { + return strtoumax(t.s, &ep, 0); + } else { + return strtod(t.s, &ep); + } +} + +static long double CallFunction(struct Token fn, struct Exprs *args) { + return 0; +} + +static void Tokenize(const char *s, size_t size) { + int kw; + size_t n; + char *se; + for (se = s + size; s < se; s += n, ++g_column) { + n = 1; + switch (*s & 0xff) { + case ' ': + case '\t': + case '\v': + case '\r': + case 0x0C: + break; + case '\n': + ++g_line; + g_column = 0; + break; + case 'A' ... 'Z': + case 'a' ... 'z': + n = strspn(s, "$" + "0123456789" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz"); + if ((kw = GetKeyword(s, n)) != -1) { + Parse(g_parser, kw, (struct Token){s, n}); + } else { + Parse(g_parser, TK_SYMBOL, (struct Token){s, n}); + } + break; + case '0': + n = strspn(s, "xXbB0123456789abcdefABCDEF"); + Parse(g_parser, TK_I_CONSTANT, (struct Token){s, n}); + n += strspn(s + n, "LUlu"); + break; + case '1' ... '9': + n = strspn(s, "0123456789."); + if (s[n] == 'e' || s[n] == 'E') { + ++n; + if (s[n] == '+' || s[n] == '-') ++n; + n += strspn(s + n, "0123456789"); + } + Parse(g_parser, memchr(s, '.', n) ? TK_F_CONSTANT : TK_I_CONSTANT, + (struct Token){s, n}); + n += strspn(s + n, "LUlu"); + break; + case ';': + Parse(g_parser, TK_SEMI, (struct Token){0, 0}); + break; + case '(': + Parse(g_parser, TK_LP, (struct Token){0, 0}); + break; + case ')': + Parse(g_parser, TK_RP, (struct Token){0, 0}); + break; + case '[': + Parse(g_parser, TK_LSB, (struct Token){0, 0}); + break; + case ']': + Parse(g_parser, TK_RSB, (struct Token){0, 0}); + break; + case '{': + Parse(g_parser, TK_LCB, (struct Token){0, 0}); + break; + case '}': + Parse(g_parser, TK_RCB, (struct Token){0, 0}); + break; + case '?': + Parse(g_parser, TK_QUESTION, (struct Token){0, 0}); + break; + case ':': + Parse(g_parser, TK_COLON, (struct Token){0, 0}); + break; + case ',': + Parse(g_parser, TK_COMMA, (struct Token){0, 0}); + break; + case '^': + if (s[1] == '=') { + Parse(g_parser, TK_XOR_ASSIGN, (struct Token){0, 0}); + ++n; + } else { + Parse(g_parser, TK_XOR, (struct Token){0, 0}); + } + break; + case '%': + if (s[1] == '=') { + Parse(g_parser, TK_REM_ASSIGN, (struct Token){0, 0}); + ++n; + } else { + Parse(g_parser, TK_REM, (struct Token){0, 0}); + } + break; + case '.': + Parse(g_parser, TK_DOT, (struct Token){0, 0}); + break; + case '+': + if (s[1] == '=') { + Parse(g_parser, TK_ADD_ASSIGN, (struct Token){0, 0}); + ++n; + } else if (s[1] == '+') { + Parse(g_parser, TK_INC, (struct Token){0, 0}); + ++n; + } else { + Parse(g_parser, TK_ADD, (struct Token){0, 0}); + } + break; + case '-': + if (s[1] == '=') { + Parse(g_parser, TK_SUB_ASSIGN, (struct Token){0, 0}); + ++n; + } else if (s[1] == '-') { + Parse(g_parser, TK_DEC, (struct Token){0, 0}); + ++n; + } else if (s[1] == '>') { + Parse(g_parser, TK_ARROW, (struct Token){0, 0}); + ++n; + } else { + Parse(g_parser, TK_SUB, (struct Token){0, 0}); + } + break; + case '~': + Parse(g_parser, TK_TILDE, (struct Token){0, 0}); + break; + case '/': + if (s[1] == '=') { + Parse(g_parser, TK_DIV_ASSIGN, (struct Token){0, 0}); + ++n; + } else { + Parse(g_parser, TK_DIV, (struct Token){0, 0}); + } + break; + case '*': + if (s[1] == '=') { + Parse(g_parser, TK_MUL_ASSIGN, (struct Token){0, 0}); + ++n; + } else { + Parse(g_parser, TK_STAR, (struct Token){0, 0}); + } + break; + case '|': + if (s[1] == '|') { + Parse(g_parser, TK_OR_LOGICAL, (struct Token){0, 0}); + ++n; + } else if (s[1] == '=') { + Parse(g_parser, TK_OR_ASSIGN, (struct Token){0, 0}); + ++n; + } else { + Parse(g_parser, TK_OR, (struct Token){0, 0}); + } + break; + case '&': + if (s[1] == '&') { + Parse(g_parser, TK_AND_LOGICAL, (struct Token){0, 0}); + ++n; + } else if (s[1] == '=') { + Parse(g_parser, TK_AND_ASSIGN, (struct Token){0, 0}); + ++n; + } else { + Parse(g_parser, TK_AND, (struct Token){0, 0}); + } + break; + case '!': + if (s[1] == '=') { + Parse(g_parser, TK_NOTEQUAL, (struct Token){0, 0}); + ++n; + } else { + Parse(g_parser, TK_EXCLAIM, (struct Token){0, 0}); + } + break; + case '=': + if (s[1] == '=') { + Parse(g_parser, TK_EQUAL, (struct Token){0, 0}); + ++n; + } else { + Parse(g_parser, TK_EQ, (struct Token){0, 0}); + } + break; + case '>': + if (s[1] == '=') { + Parse(g_parser, TK_GE, (struct Token){0, 0}); + ++n; + } else if (s[1] == '>') { + if (s[2] == '=') { + Parse(g_parser, TK_SHR_ASSIGN, (struct Token){0, 0}); + ++n; + } else { + Parse(g_parser, TK_SHR, (struct Token){0, 0}); + } + ++n; + } else { + Parse(g_parser, TK_GT, (struct Token){0, 0}); + } + break; + case '<': + if (s[1] == '=') { + Parse(g_parser, TK_LE, (struct Token){0, 0}); + ++n; + } else if (s[1] == '<') { + if (s[2] == '=') { + Parse(g_parser, TK_SHL_ASSIGN, (struct Token){0, 0}); + ++n; + } else { + Parse(g_parser, TK_SHL, (struct Token){0, 0}); + } + ++n; + } else { + Parse(g_parser, TK_LT, (struct Token){0, 0}); + } + break; + default: + LexError(); + } + } +} + +int main(int argc, char *argv[]) { + int i; + int ec; + int fd; + size_t n; + char *buf; + ssize_t rc; + size_t bufcap; + bufcap = BIGPAGESIZE; + buf = malloc(bufcap); + if (!(ec = setjmp(jb))) { + for (i = 1; i < argc; ++i) { + g_file = argv[i]; + g_line = 0; + g_column = 0; + n = 0; /* wut */ + if ((fd = open(g_file, O_RDONLY)) == -1) SyscallError("open"); + ParseInit(g_parser); + for (;;) { + if ((rc = read(fd, buf, bufcap)) == -1) SyscallError("read"); + if (!(n = rc)) break; + Tokenize(buf, n); + } + close(fd); + Parse(g_parser, 0, (struct Token){0, 0}); + ParseFinalize(g_parser); + } + } + free(buf); + return ec; +} diff --git a/tool/tags/tags.h b/tool/tags/tags.h new file mode 100644 index 000000000..bc5f7ccaf --- /dev/null +++ b/tool/tags/tags.h @@ -0,0 +1,25 @@ +#ifndef COSMOPOLITAN_TOOL_TAGS_TAGS_H_ +#define COSMOPOLITAN_TOOL_TAGS_TAGS_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +struct Token { + const char *s; + size_t n; +}; + +struct Exprs { + struct Exprs *n; + long double x; +}; + +static void SyntaxError(void) noreturn; +static long double ParseExpr(struct Token); +static void ExprsFree(struct Exprs *); +static struct Exprs *ExprsAppend(struct Exprs *, long double); +static long double CallFunction(struct Token, struct Exprs *); +int GetKeyword(const char *, size_t); + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_TOOL_TAGS_TAGS_H_ */ diff --git a/tool/tags/tags.mk b/tool/tags/tags.mk new file mode 100644 index 000000000..3f544c407 --- /dev/null +++ b/tool/tags/tags.mk @@ -0,0 +1,75 @@ +#-*-mode:makefile-gmake;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐ +#───vi: set et ft=make ts=8 tw=8 fenc=utf-8 :vi───────────────────────┘ + +PKGS += TOOL_TAGS + +TOOL_TAGS = $(TOOL_LIB_A_DEPS) $(TOOL_LIB_A) +TOOL_TAGS_A = o/$(MODE)/tool/tags/tags.a +TOOL_TAGS_FILES := $(wildcard tool/tags/*) +TOOL_TAGS_COMS = $(TOOL_TAGS_OBJS:%.o=%.com) +TOOL_TAGS_SRCS = $(filter %.c,$(TOOL_TAGS_FILES)) +TOOL_TAGS_HDRS = $(filter %.h,$(TOOL_TAGS_FILES)) + +TOOL_TAGS_OBJS = \ + $(TOOL_TAGS_SRCS:%=o/$(MODE)/%.zip.o) \ + $(TOOL_TAGS_SRCS:%.c=o/$(MODE)/%.o) + +TOOL_TAGS_COMS = \ + $(TOOL_TAGS_SRCS:%.c=o/$(MODE)/%.com) + +TOOL_TAGS_BINS = \ + $(TOOL_TAGS_COMS) \ + $(TOOL_TAGS_COMS:%=%.dbg) + +TOOL_TAGS_CHECKS = \ + $(TOOL_TAGS_HDRS:%=o/$(MODE)/%.ok) + +TOOL_TAGS_DIRECTDEPS = \ + LIBC_BITS \ + LIBC_CALLS \ + LIBC_CONV \ + LIBC_LOG \ + LIBC_FMT \ + LIBC_MEM \ + LIBC_NEXGEN32E \ + LIBC_RUNTIME \ + LIBC_STDIO \ + LIBC_STUBS \ + LIBC_STR \ + LIBC_SYSV \ + LIBC_TINYMATH \ + LIBC_X \ + THIRD_PARTY_COMPILER_RT \ + THIRD_PARTY_DTOA + +TOOL_TAGS_DEPS := \ + $(call uniq,$(foreach x,$(TOOL_TAGS_DIRECTDEPS),$($(x)))) + +$(TOOL_TAGS_A): \ + tool/tags/ \ + $(TOOL_TAGS_A).pkg \ + $(TOOL_TAGS_OBJS) + +$(TOOL_TAGS_A).pkg: \ + $(TOOL_TAGS_OBJS) \ + $(foreach x,$(TOOL_TAGS_DIRECTDEPS),$($(x)_A).pkg) + +o/tool/tags/tags.h.inc: o/tool/tags/tags.c.inc +o/tool/tags/tags.c.inc: \ + tool/tags/tags.y \ + $(THIRD_PARTY_LEMON) + @$(LEMON) -l -d$(@D) $< + +o/$(MODE)/tool/tags/%.com.dbg: \ + $(TOOL_TAGS_DEPS) \ + $(TOOL_TAGS_A) \ + o/$(MODE)/tool/tags/%.o \ + $(TOOL_TAGS_A).pkg \ + $(CRT) \ + $(APE) + @$(APELINK) + +.PHONY: o/$(MODE)/tool/tags +o/$(MODE)/tool/tags: \ + $(TOOL_TAGS_BINS) \ + $(TOOL_TAGS_CHECKS) diff --git a/tool/tags/tags.y b/tool/tags/tags.y new file mode 100644 index 000000000..a913a9158 --- /dev/null +++ b/tool/tags/tags.y @@ -0,0 +1,436 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +%include { +#include "libc/stdio/stdio.h" +#include "tool/tags/tags.h" +#include "libc/calls/calls.h" +#include "libc/str/str.h" +#include "third_party/dtoa/dtoa.h" +#include "libc/x/x.h" +#include "libc/runtime/gc.h" +#include "libc/math.h" +} + +/** + * C parser. + * + * “The grammar of C seems remarkably well-behaved. It is almost + * LALR(1). More precisely, the automaton built for C by an LALR(1) + * parser generator exhibits only two conflicts: (1) a shift/reduce + * conflict caused by dangling else ambiguity and (2) a shift/reduce + * conflict caused by the _Atomic( ambiguity. In both cases, the + * desired behavior can be obtained by instructing the parser to + * prefer shifting.” Quoth Jacques-Henri Jourdan & François Pottier + * + */ + +%token_prefix TK_ +%token_type {struct Token} +%type expr {long double} +%syntax_error { SyntaxError(); } + +%token AUTO BREAK CASE CHAR CONST CONTINUE DEFAULT DO DOUBLE ELSE ENUM EXTERN. +%token FLOAT FOR GOTO IF INLINE INT LONG REGISTER RESTRICT RETURN SHORT SIGNED. +%token SIZEOF STATIC STRUCT SWITCH TYPEDEF UNION UNSIGNED VOID VOLATILE WHILE. +%token ALIGNAS ALIGNOF ATOMIC BOOL COMPLEX GENERIC IMAGINARY NORETURN INCLUDE. +%token STATIC_ASSERT THREAD_LOCAL ELIF ENDIF IFDEF IFNDEF DEFINE UNDEF PRAGMA. +%token LINE ERROR SYMBOL ATTRIBUTE RESTRICT TYPEOF TYPEOF INLINE CONST LABEL. +%token FORCE_ALIGN_ARG_POINTER ALWAYS_INLINE GNU_INLINE ALIGNOF ASM AUTO_TYPE. +%token BYTE COMPLEX IMAG MAY_ALIAS NORETURN PACKED POINTER PRINTF REAL SCANF. +%token STRFMON STRFTIME STRONG TARGET TRANSPARENT_UNION VOLATILE WORD ALIAS. +%token ALIGNED ALLOC_ALIGN ALLOC_SIZE ARTIFICIAL ASSUME_ALIGNED COLD. +%token CONSTRUCTOR DESTRUCTOR COPY DEPRECATED ERROR WARNING EXTERNALLY_VISIBLE. +%token FLATTEN FORMAT GNU_FORMAT GNU_PRINTF GNU_SCANF FORMAT_ARG HOT IFUNC. +%token INTERRUPT INTERRUPT_HANDLER NO_CALLER_SAVED_REGISTERS LEAF MALLOC NO_ICF. +%token NO_INSTRUMENT_FUNCTION NO_PROFILE_INSTRUMENT_FUNCTION NO_REORDER. +%token NO_SANITIZE NO_SANITIZE_ADDRESS NO_ADDRESS_SAFETY_ANALYSIS. +%token NO_SANITIZE_THREAD NO_SANITIZE_UNDEFINED NO_SPLIT_STACK NO_STACK_LIMIT. +%token NOCLONE NOIPA NONNULL NOPLT NOTHROW OPTIMIZE PURE. +%token PATCHABLE_FUNCTION_ENTRY RETURNS_NONNULL RETURNS_TWICE SECTION SENTINEL. +%token SIMD TARGET_CLONES UNUSED USED VISIBILITY WARN_UNUSED_RESULT. +%token PARAMS_NONNULL WEAK VECTOR_SIZE MS_ABI MODE OPTNONE NODEBUG. + +%left IF. +%left ELSE. +%right COMMA. +%right EQ ADD_ASSIGN SUB_ASSIGN MUL_ASSIGN DIV_ASSIGN REM_ASSIGN. +%right SHL_ASSIGN SHR_ASSIGN AND_ASSIGN XOR_ASSIGN OR_ASSIGN. +%right QUESTION COLON. +%left OR_LOGICAL. +%left AND_LOGICAL. +%left OR. +%left XOR. +%left AND. +%left EQUAL NOTEQUAL. +%left LT LE GT GE. +%left SHL SHR. +%left ADD SUB. +%left STAR DIV REM. +%right TILDE EXCLAIM INC DEC AMP SIZEOF ALIGNOF. +%left DOT ARROW LP RP RSB LSB. + +program ::= translation_unit. + +translation_unit ::= external_declaration. +translation_unit ::= translation_unit external_declaration. + +external_declaration ::= function_definition. +external_declaration ::= declaration. + +function_definition ::= declaration_specifiers declarator declaration_list compound_statement. +function_definition ::= declaration_specifiers declarator compound_statement. + +declaration_list ::= declaration. +declaration_list ::= declaration_list declaration. + +jump_statement ::= GOTO IDENTIFIER SEMI. +jump_statement ::= CONTINUE SEMI. +jump_statement ::= BREAK SEMI. +jump_statement ::= RETURN SEMI. +jump_statement ::= RETURN expression SEMI. + +iteration_statement ::= WHILE LP expression RP statement. +iteration_statement ::= DO statement WHILE LP expression RP SEMI. +iteration_statement ::= FOR LP expression_statement expression_statement RP statement. +iteration_statement ::= FOR LP expression_statement expression_statement expression RP statement. +iteration_statement ::= FOR LP declaration expression_statement RP statement. +iteration_statement ::= FOR LP declaration expression_statement expression RP statement. + +selection_statement ::= IF LP expression RP statement ELSE statement. +selection_statement ::= IF LP expression RP statement. +selection_statement ::= SWITCH LP expression RP statement. + +expression_statement ::= SEMI. +expression_statement ::= expression SEMI. + +block_item ::= declaration. +block_item ::= statement. + +block_item_list ::= block_item. +block_item_list ::= block_item_list block_item. + +compound_statement ::= LCB RCB. +compound_statement ::= LCB block_item_list RCB. + +labeled_statement ::= IDENTIFIER COLON statement. +labeled_statement ::= CASE constant_expression COLON statement. +labeled_statement ::= DEFAULT COLON statement. + +statement ::= labeled_statement. +statement ::= compound_statement. +statement ::= expression_statement. +statement ::= selection_statement. +statement ::= iteration_statement. +statement ::= jump_statement. + +static_assert_declaration ::= STATIC_ASSERT LP constant_expression COMMA STRING_LITERAL RP SEMI. + +designator ::= LSB constant_expression RSB. +designator ::= DOT IDENTIFIER. + +designator_list ::= designator. +designator_list ::= designator_list designator. + +designation ::= designator_list EQ. + +initializer_list ::= designation initializer. +initializer_list ::= initializer. +initializer_list ::= initializer_list COMMA designation initializer. +initializer_list ::= initializer_list COMMA initializer. + +initializer ::= LCB initializer_list RCB. +initializer ::= LCB initializer_list COMMA RCB. +initializer ::= assignment_expression. + +direct_abstract_declarator ::= LP abstract_declarator RP. +direct_abstract_declarator ::= LSB RSB. +direct_abstract_declarator ::= LSB STAR RSB. +direct_abstract_declarator ::= LSB STATIC type_qualifier_list assignment_expression RSB. +direct_abstract_declarator ::= LSB STATIC assignment_expression RSB. +direct_abstract_declarator ::= LSB type_qualifier_list STATIC assignment_expression RSB. +direct_abstract_declarator ::= LSB type_qualifier_list assignment_expression RSB. +direct_abstract_declarator ::= LSB type_qualifier_list RSB. +direct_abstract_declarator ::= LSB assignment_expression RSB. +direct_abstract_declarator ::= direct_abstract_declarator LSB RSB. +direct_abstract_declarator ::= direct_abstract_declarator LSB STAR RSB. +direct_abstract_declarator ::= direct_abstract_declarator LSB STATIC type_qualifier_list assignment_expression RSB. +direct_abstract_declarator ::= direct_abstract_declarator LSB STATIC assignment_expression RSB. +direct_abstract_declarator ::= direct_abstract_declarator LSB type_qualifier_list assignment_expression RSB. +direct_abstract_declarator ::= direct_abstract_declarator LSB type_qualifier_list STATIC assignment_expression RSB. +direct_abstract_declarator ::= direct_abstract_declarator LSB type_qualifier_list RSB. +direct_abstract_declarator ::= direct_abstract_declarator LSB assignment_expression RSB. +direct_abstract_declarator ::= LP RP. +direct_abstract_declarator ::= LP parameter_type_list RP. +direct_abstract_declarator ::= direct_abstract_declarator LP RP. +direct_abstract_declarator ::= direct_abstract_declarator LP parameter_type_list RP. + +abstract_declarator ::= pointer direct_abstract_declarator. +abstract_declarator ::= pointer. +abstract_declarator ::= direct_abstract_declarator. + +type_name ::= specifier_qualifier_list abstract_declarator. +type_name ::= specifier_qualifier_list. + +primary_expression ::= IDENTIFIER. +primary_expression ::= constant. +primary_expression ::= string. +primary_expression ::= LP expression RP. +primary_expression ::= generic_selection. + +constant ::= I_CONSTANT. +constant ::= F_CONSTANT. +constant ::= ENUMERATION_CONSTANT. + +enumeration_constant ::= IDENTIFIER. + +string ::= STRING_LITERAL. +string ::= FUNC_NAME. + +generic_selection ::= GENERIC LP assignment_expression COMMA generic_assoc_list RP. + +generic_assoc_list ::= generic_association. +generic_assoc_list ::= generic_assoc_list COMMA generic_association. + +generic_association ::= type_name COLON assignment_expression. +generic_association ::= DEFAULT COLON assignment_expression. + +postfix_expression ::= primary_expression. +postfix_expression ::= postfix_expression LSB expression RSB. +postfix_expression ::= postfix_expression LP RP. +postfix_expression ::= postfix_expression LP argument_expression_list RP. +postfix_expression ::= postfix_expression DOT IDENTIFIER. +postfix_expression ::= postfix_expression ARROW IDENTIFIER. +postfix_expression ::= postfix_expression INC. +postfix_expression ::= postfix_expression DEC. +postfix_expression ::= LP type_name RP LCB initializer_list RCB. +postfix_expression ::= LP type_name RP LCB initializer_list COMMA RCB. + +argument_expression_list ::= assignment_expression. +argument_expression_list ::= argument_expression_list COMMA assignment_expression. + +unary_expression ::= postfix_expression. +unary_expression ::= INC unary_expression. +unary_expression ::= DEC unary_expression. +unary_expression ::= unary_operator cast_expression. +unary_expression ::= SIZEOF unary_expression. +unary_expression ::= SIZEOF LP type_name RP. +unary_expression ::= ALIGNOF LP type_name RP. + +unary_operator ::= AMP. +unary_operator ::= STAR. +unary_operator ::= ADD. +unary_operator ::= SUB. +unary_operator ::= TILDE. +unary_operator ::= EXCLAIM. + +cast_expression ::= unary_expression. +cast_expression ::= LP type_name RP cast_expression. + +multiplicative_expression ::= cast_expression. +multiplicative_expression ::= multiplicative_expression STAR cast_expression. +multiplicative_expression ::= multiplicative_expression DIV cast_expression. +multiplicative_expression ::= multiplicative_expression REM cast_expression. + +additive_expression ::= multiplicative_expression. +additive_expression ::= additive_expression ADD multiplicative_expression. +additive_expression ::= additive_expression SUB multiplicative_expression. + +shift_expression ::= additive_expression. +shift_expression ::= shift_expression SHL additive_expression. +shift_expression ::= shift_expression SHR additive_expression. + +relational_expression ::= shift_expression. +relational_expression ::= relational_expression LT shift_expression. +relational_expression ::= relational_expression GT shift_expression. +relational_expression ::= relational_expression LE shift_expression. +relational_expression ::= relational_expression GE shift_expression. + +equality_expression ::= relational_expression. +equality_expression ::= equality_expression EQUAL relational_expression. +equality_expression ::= equality_expression NOTEQUAL relational_expression. + +and_expression ::= equality_expression. +and_expression ::= and_expression AMP equality_expression. + +exclusive_or_expression ::= and_expression. +exclusive_or_expression ::= exclusive_or_expression XOR and_expression. +inclusive_or_expression ::= exclusive_or_expression. +inclusive_or_expression ::= inclusive_or_expression OR exclusive_or_expression. + +logical_and_expression ::= inclusive_or_expression. +logical_and_expression ::= logical_and_expression AND_LOGICAL inclusive_or_expression. + +logical_or_expression ::= logical_and_expression. +logical_or_expression ::= logical_or_expression OR_LOGICAL logical_and_expression. + +conditional_expression ::= logical_or_expression. +conditional_expression ::= logical_or_expression QUESTION expression COLON conditional_expression. + +assignment_expression ::= conditional_expression. +assignment_expression ::= unary_expression assignment_operator assignment_expression. + +assignment_operator ::= EQ. +assignment_operator ::= MUL_ASSIGN. +assignment_operator ::= DIV_ASSIGN. +assignment_operator ::= REM_ASSIGN. +assignment_operator ::= ADD_ASSIGN. +assignment_operator ::= SUB_ASSIGN. +assignment_operator ::= SHL_ASSIGN. +assignment_operator ::= SHR_ASSIGN. +assignment_operator ::= AND_ASSIGN. +assignment_operator ::= XOR_ASSIGN. +assignment_operator ::= OR_ASSIGN. + +expression ::= assignment_expression. +expression ::= expression COMMA assignment_expression. + +constant_expression ::= conditional_expression. /* with constraints */ + +declaration ::= declaration_specifiers SEMI. +declaration ::= declaration_specifiers init_declarator_list SEMI. +declaration ::= static_assert_declaration. + +declaration_specifiers ::= storage_class_specifier declaration_specifiers. +declaration_specifiers ::= storage_class_specifier. +declaration_specifiers ::= type_specifier declaration_specifiers. +declaration_specifiers ::= type_specifier. +declaration_specifiers ::= type_qualifier declaration_specifiers. +declaration_specifiers ::= type_qualifier. +declaration_specifiers ::= function_specifier declaration_specifiers. +declaration_specifiers ::= function_specifier. +declaration_specifiers ::= alignment_specifier declaration_specifiers. +declaration_specifiers ::= alignment_specifier. + +init_declarator_list ::= init_declarator. +init_declarator_list ::= init_declarator_list COMMA init_declarator. + +init_declarator ::= declarator EQ initializer. +init_declarator ::= declarator. + +storage_class_specifier ::= TYPEDEF. /* identifiers must be flagged as TYPEDEF_NAME */ +storage_class_specifier ::= EXTERN. +storage_class_specifier ::= STATIC. +storage_class_specifier ::= THREAD_LOCAL. +storage_class_specifier ::= AUTO. +storage_class_specifier ::= REGISTER. + +type_specifier ::= VOID. +type_specifier ::= CHAR. +type_specifier ::= SHORT. +type_specifier ::= INT. +type_specifier ::= LONG. +type_specifier ::= FLOAT. +type_specifier ::= DOUBLE. +type_specifier ::= SIGNED. +type_specifier ::= UNSIGNED. +type_specifier ::= BOOL. +type_specifier ::= COMPLEX. +type_specifier ::= IMAGINARY. /* non-mandated extension */ +type_specifier ::= struct_or_union_specifier. +type_specifier ::= enum_specifier. +type_specifier ::= TYPEDEF_NAME. /* after it has been defined as such */ + +struct_or_union_specifier ::= struct_or_union LCB struct_declaration_list RCB. +struct_or_union_specifier ::= struct_or_union IDENTIFIER LCB struct_declaration_list RCB. +struct_or_union_specifier ::= struct_or_union IDENTIFIER. + +struct_or_union ::= STRUCT. +struct_or_union ::= UNION. + +struct_declaration_list ::= struct_declaration. +struct_declaration_list ::= struct_declaration_list struct_declaration. + +struct_declaration ::= specifier_qualifier_list SEMI. /* for anonymous struct/union */ +struct_declaration ::= specifier_qualifier_list struct_declarator_list SEMI. +struct_declaration ::= static_assert_declaration. + +specifier_qualifier_list ::= type_specifier specifier_qualifier_list. +specifier_qualifier_list ::= type_specifier. +specifier_qualifier_list ::= type_qualifier specifier_qualifier_list. +specifier_qualifier_list ::= type_qualifier. + +struct_declarator_list ::= struct_declarator. +struct_declarator_list ::= struct_declarator_list COMMA struct_declarator. + +struct_declarator ::= COLON constant_expression. +struct_declarator ::= declarator COLON constant_expression. +struct_declarator ::= declarator. + +enum_specifier ::= ENUM LCB enumerator_list RCB. +enum_specifier ::= ENUM LCB enumerator_list COMMA RCB. +enum_specifier ::= ENUM IDENTIFIER LCB enumerator_list RCB. +enum_specifier ::= ENUM IDENTIFIER LCB enumerator_list COMMA RCB. +enum_specifier ::= ENUM IDENTIFIER. + +enumerator_list ::= enumerator. +enumerator_list ::= enumerator_list COMMA enumerator. + +enumerator ::= enumeration_constant EQ constant_expression. +enumerator ::= enumeration_constant. + +type_qualifier ::= CONST. +type_qualifier ::= RESTRICT. +type_qualifier ::= VOLATILE. +type_qualifier ::= ATOMIC. + +function_specifier ::= INLINE. +function_specifier ::= NORETURN. + +alignment_specifier ::= ALIGNAS LP type_name RP. +alignment_specifier ::= ALIGNAS LP constant_expression RP. + +declarator ::= pointer direct_declarator. +declarator ::= direct_declarator. + +direct_declarator ::= IDENTIFIER. +direct_declarator ::= LP declarator RP. +direct_declarator ::= direct_declarator LSB RSB. +direct_declarator ::= direct_declarator LSB STAR RSB. +direct_declarator ::= direct_declarator LSB STATIC type_qualifier_list assignment_expression RSB. +direct_declarator ::= direct_declarator LSB STATIC assignment_expression RSB. +direct_declarator ::= direct_declarator LSB type_qualifier_list STAR RSB. +direct_declarator ::= direct_declarator LSB type_qualifier_list STATIC assignment_expression RSB. +direct_declarator ::= direct_declarator LSB type_qualifier_list assignment_expression RSB. +direct_declarator ::= direct_declarator LSB type_qualifier_list RSB. +direct_declarator ::= direct_declarator LSB assignment_expression RSB. +direct_declarator ::= direct_declarator LP parameter_type_list RP. +direct_declarator ::= direct_declarator LP RP. +direct_declarator ::= direct_declarator LP identifier_list RP. + +pointer ::= STAR type_qualifier_list pointer. +pointer ::= STAR type_qualifier_list. +pointer ::= STAR pointer. +pointer ::= STAR. + +type_qualifier_list ::= type_qualifier. +type_qualifier_list ::= type_qualifier_list type_qualifier. + +parameter_type_list ::= parameter_list COMMA ELLIPSIS. +parameter_type_list ::= parameter_list. + +parameter_list ::= parameter_declaration. +parameter_list ::= parameter_list COMMA parameter_declaration. + +parameter_declaration ::= declaration_specifiers declarator. +parameter_declaration ::= declaration_specifiers abstract_declarator. +parameter_declaration ::= declaration_specifiers. + +identifier_list ::= IDENTIFIER. +identifier_list ::= identifier_list COMMA IDENTIFIER. diff --git a/tool/tool.mk b/tool/tool.mk index d0786f6ad..3b050a260 100644 --- a/tool/tool.mk +++ b/tool/tool.mk @@ -4,8 +4,10 @@ .PHONY: o/$(MODE)/tool o/$(MODE)/tool: \ o/$(MODE)/tool/build \ + o/$(MODE)/tool/calc \ o/$(MODE)/tool/decode \ o/$(MODE)/tool/hash \ o/$(MODE)/tool/net \ + o/$(MODE)/tool/tags \ o/$(MODE)/tool/viz \ o/$(MODE)/tool/cc