diff --git a/tool/build/build.mk b/tool/build/build.mk index b32d69ee..604e9be5 100644 --- a/tool/build/build.mk +++ b/tool/build/build.mk @@ -6,77 +6,91 @@ PKGS += TOOL_BUILD TOOL_BUILD_FILES := $(wildcard tool/build/*) TOOL_BUILD_SRCS = $(filter %.c,$(TOOL_BUILD_FILES)) TOOL_BUILD_HDRS = $(filter %.h,$(TOOL_BUILD_FILES)) +TOOL_BUILD_CTESTS = $(filter %.ctest,$(TOOL_BUILD_FILES)) TOOL_BUILD_COMS = $(TOOL_BUILD_OBJS:%.o=%.com) TOOL_BUILD_BINS = $(TOOL_BUILD_COMS) $(TOOL_BUILD_COMS:%=%.dbg) -TOOL_BUILD_OBJS = \ - $(TOOL_BUILD_SRCS:%=o/$(MODE)/%.zip.o) \ +TOOL_BUILD_OBJS = \ + $(TOOL_BUILD_SRCS:%=o/$(MODE)/%.zip.o) \ $(TOOL_BUILD_SRCS:%.c=o/$(MODE)/%.o) -TOOL_BUILD_LINK = \ - $(TOOL_BUILD_DEPS) \ - o/$(MODE)/tool/build/%.o \ - $(CRT) \ +TOOL_BUILD_LINK = \ + $(TOOL_BUILD_DEPS) \ + o/$(MODE)/tool/build/%.o \ + $(CRT) \ $(APE) -TOOL_BUILD_DIRECTDEPS = \ - DSP_CORE \ - DSP_SCALE \ - LIBC_ALG \ - LIBC_CALLS \ - LIBC_CALLS_HEFTY \ - LIBC_CONV \ - LIBC_DNS \ - LIBC_ELF \ - LIBC_FMT \ - LIBC_LOG \ - LIBC_TINYMATH \ - LIBC_MEM \ - LIBC_NEXGEN32E \ - LIBC_RUNTIME \ - LIBC_SOCK \ - LIBC_STDIO \ - LIBC_STR \ - LIBC_RAND \ - LIBC_STUBS \ - LIBC_SYSV \ - LIBC_SYSV_CALLS \ - LIBC_TESTLIB \ - LIBC_TIME \ - LIBC_UNICODE \ - LIBC_X \ - TOOL_BUILD_LIB \ - THIRD_PARTY_DTOA \ - THIRD_PARTY_GETOPT \ - THIRD_PARTY_XED \ - THIRD_PARTY_ZLIB \ +TOOL_BUILD_CHECKS = \ + $(TOOL_BUILD).pkg \ + $(TOOL_BUILD_HDRS:%=o/$(MODE)/%.ok) \ + $(TOOL_BUILD_CTESTS:%=o/$(MODE)/%.ok) + +TOOL_BUILD_DIRECTDEPS = \ + DSP_CORE \ + DSP_SCALE \ + DSP_TTY \ + LIBC_ALG \ + LIBC_BITS \ + LIBC_CALLS \ + LIBC_CALLS_HEFTY \ + LIBC_CONV \ + LIBC_DNS \ + LIBC_ELF \ + LIBC_FMT \ + LIBC_LOG \ + LIBC_TINYMATH \ + LIBC_MEM \ + LIBC_NEXGEN32E \ + LIBC_RUNTIME \ + LIBC_SOCK \ + LIBC_STDIO \ + LIBC_STR \ + LIBC_RAND \ + LIBC_STUBS \ + LIBC_SYSV \ + LIBC_SYSV_CALLS \ + LIBC_TESTLIB \ + LIBC_TIME \ + LIBC_UNICODE \ + LIBC_X \ + TOOL_BUILD_LIB \ + THIRD_PARTY_COMPILER_RT \ + THIRD_PARTY_DTOA \ + THIRD_PARTY_GETOPT \ + THIRD_PARTY_XED \ + THIRD_PARTY_ZLIB \ THIRD_PARTY_STB -TOOL_BUILD_DEPS := \ +TOOL_BUILD_DEPS := \ $(call uniq,$(foreach x,$(TOOL_BUILD_DIRECTDEPS),$($(x)))) -o/$(MODE)/tool/build/build.pkg: \ - $(TOOL_BUILD_OBJS) \ +o/$(MODE)/tool/build/build.pkg: \ + $(TOOL_BUILD_OBJS) \ $(foreach x,$(TOOL_BUILD_DIRECTDEPS),$($(x)_A).pkg) -o/$(MODE)/tool/build/%.com.dbg: \ - $(TOOL_BUILD_DEPS) \ - o/$(MODE)/tool/build/build.pkg \ - o/$(MODE)/tool/build/%.o \ - $(CRT) \ +o/$(MODE)/tool/build/%.com.dbg: \ + $(TOOL_BUILD_DEPS) \ + o/$(MODE)/tool/build/build.pkg \ + o/$(MODE)/tool/build/%.o \ + $(CRT) \ $(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/generatematrix.o \ -o/$(MODE)/tool/build/mkdeps.o: \ - OVERRIDE_COPTS += \ +o/$(MODE)/tool/build/generatematrix.o \ +o/$(MODE)/tool/build/mkdeps.o: \ + OVERRIDE_COPTS += \ -O2 +o/$(MODE)/%.ctest.ok: %.ctest o/$(MODE)/tool/build/calculator.com + @TARGET=$@ ACTION=MKWIDES build/do \ + o/$(MODE)/tool/build/calculator.com $< && \ + touch $@ + .PHONY: o/$(MODE)/tool/build -o/$(MODE)/tool/build: \ - o/$(MODE)/tool/build/lib \ - $(TOOL_BUILD_BINS) \ +o/$(MODE)/tool/build: \ + o/$(MODE)/tool/build/lib \ + $(TOOL_BUILD_BINS) \ $(TOOL_BUILD_CHECKS) diff --git a/tool/build/calculator.c b/tool/build/calculator.c new file mode 100644 index 00000000..4f007914 --- /dev/null +++ b/tool/build/calculator.c @@ -0,0 +1,711 @@ +#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 "dsp/tty/tty.h" +#include "libc/alg/arraylist2.h" +#include "libc/assert.h" +#include "libc/bits/bits.h" +#include "libc/bits/morton.h" +#include "libc/bits/popcnt.h" +#include "libc/calls/calls.h" +#include "libc/conv/conv.h" +#include "libc/conv/itoa.h" +#include "libc/dce.h" +#include "libc/fmt/fmt.h" +#include "libc/log/log.h" +#include "libc/macros.h" +#include "libc/math.h" +#include "libc/mem/mem.h" +#include "libc/rand/rand.h" +#include "libc/runtime/runtime.h" +#include "libc/stdio/stdio.h" +#include "libc/str/str.h" +#include "libc/sysv/consts/ex.h" +#include "libc/sysv/consts/exit.h" +#include "libc/sysv/consts/sig.h" +#include "libc/tinymath/emodl.h" +#include "third_party/dtoa/dtoa.h" +#include "third_party/getopt/getopt.h" + +#define INT intmax_t +#define FLOAT long double +#define EPSILON 1e-16l + +#define BANNER \ + "\ +Reverse Polish Notation Calculator\n\ +Copyright 2020 Justine Alexandra Roberts Tunney\n\ +This is fast portable lightweight software. You have the freedom to build\n\ +software just like it painlessly. See http://github.com/jart/cosmopolitan\n\ +" + +#define USAGE1 \ + "SYNOPSIS\n\ +\n\ + Reverse Polish Notation Calculator\n\ +\n\ +USAGE\n\ +\n" + +#define USAGE2 \ + " [FLAGS] [FILE...]\n\ +\n\ +FLAGS\n\ +\n\ + -h\n\ + -? shows this information\n\ + -i force interactive mode\n\ +\n\ +KEYBOARD SHORTCUTS\n\ +\n\ + CTRL-D Closes standard input\n\ + CTRL-C Sends SIGINT to program\n\ + CTRL-U Redraw line\n\ + CTRL-L Redraw display\n\ +\n\ +FUNCTIONS\n\ +\n" + +#define USAGE3 \ + "\n\ +EXAMPLES\n\ +\n\ + calculator.com\n\ + 40 2 +\n\ + 42\n\ +\n\ + echo '2 2 + . cr' | calculator.com\n\ + 4\n\ +\n\ + calculator.com <\n\ + https://github.com/jart/cosmooplitan\n\ +\n\ +" + +#define CTRL(C) ((C) ^ 0100) +#define Fatal(...) Log(kFatal, __VA_ARGS__) +#define Warning(...) Log(kWarning, __VA_ARGS__) + +enum Severity { + kFatal, + kWarning, +}; + +struct Bytes { + size_t i, n; + char *p; +}; + +struct History { + size_t i, n; + struct Bytes *p; +}; + +struct Value { + enum Type { + kInt, + kFloat, + } t; + union { + INT i; + FLOAT f; + }; +}; + +struct Function { + const char sym[16]; + void (*fun)(void); + const char *doc; +}; + +const char *file; +jmp_buf underflow; +struct Bytes token; +struct History history; +struct Value stack[128]; +int sp, comment, line, column, interactive; + +INT Popcnt(INT x) { + uintmax_t word = x; + return popcnt(word >> 64) + popcnt(word); +} + +char *Repr(struct Value x) { + static char buf[64]; + if (x.t == kFloat) { + g_fmt(buf, x.f); + } else { + sprintf(buf, "%jd", x.i); + } + return buf; +} + +char *ReprStack(void) { + int i, j, l; + char *s, *p; + static char buf[80]; + p = memset(buf, 0, sizeof(buf)); + for (i = 0; i < sp; ++i) { + s = Repr(stack[i]); + l = strlen(s); + if (p + l + 2 > buf + sizeof(buf)) break; + p = mempcpy(p, s, l); + *p++ = ' '; + } + return buf; +} + +void ShowStack(void) { + if (interactive) { + printf("\r\e[K"); + fputs(ReprStack(), stdout); + } +} + +void Cr(FILE *f) { + fputs(interactive || IsWindows() ? "\r\n" : "\n", f); +} + +void Log(enum Severity l, const char *fmt, ...) { + va_list va; + const char *severity[] = {"FATAL", "WARNING"}; + if (interactive) fflush(/* key triggering throw not echo'd yet */ stdout); + fprintf(stderr, "%s:%d:%d:%s: ", file, line + 1, column, severity[l & 1]); + va_start(va, fmt); + vfprintf(stderr, fmt, va); + va_end(va); + Cr(stderr); + if (l == kFatal) exit(EXIT_FAILURE); + if (interactive) ShowStack(); +} + +struct Value Push(struct Value x) { + if (sp >= ARRAYLEN(stack)) Fatal("stack overflow"); + return (stack[sp++] = x); +} + +struct Value Pop(void) { + if (sp) { + return stack[--sp]; + } else { + longjmp(underflow, 1); + } +} + +INT Popi(void) { + struct Value x = Pop(); + return x.t == kInt ? x.i : x.f; +} + +void Pushi(INT i) { + struct Value x; + x.t = kInt; + x.i = i; + Push(x); +} + +FLOAT Popf(void) { + struct Value x = Pop(); + return x.t == kInt ? x.i : x.f; +} + +void Pushf(FLOAT f) { + struct Value x; + x.t = kFloat; + x.f = f; + Push(x); +} + +void OpDrop(void) { + Pop(); +} + +void OpDup(void) { + Push(Push(Pop())); +} + +void OpSwap(void) { + struct Value a, b; + b = Pop(); + a = Pop(); + Push(b); + Push(a); +} + +void OpOver(void) { + struct Value a, b; + b = Pop(); + a = Pop(); + Push(a); + Push(b); + Push(a); +} + +void OpSrand(void) { + srand(Popi()); +} + +void OpKey(void) { + wint_t c; + ttyraw(kTtyCursor | kTtySigs | kTtyLfToCrLf); + c = fgetwc(stdin); + ttyraw(-1); + if (c != -1) Pushi(c); +} + +void OpEmit(void) { + fputwc(Popi(), stdout); +} + +void OpCr(void) { + Cr(stdout); +} + +void OpPrint(void) { + printf("%s ", Repr(Pop())); +} + +void OpComment(void) { + comment = true; +} + +void OpAssert(void) { + if (!Popi()) Fatal("assert failed"); +} + +void OpExpect(void) { + if (!Popi()) Warning("expect failed"); +} + +void OpExit(void) { + exit(Popi()); +} + +void OpMeminfo(void) { + OpCr(); + OpCr(); + meminfo(stdout); +} + +void Glue0f(FLOAT fn(void)) { + Pushf(fn()); +} + +void Glue0i(INT fn(void)) { + Pushi(fn()); +} + +void Glue1f(FLOAT fn(FLOAT)) { + Pushf(fn(Popf())); +} + +void Glue1i(INT fn(INT)) { + Pushi(fn(Popi())); +} + +void Glue2f(FLOAT fn(FLOAT, FLOAT)) { + FLOAT x, y; + y = Popf(); + x = Popf(); + Pushf(fn(x, y)); +} + +void Glue2i(INT fn(INT, INT)) { + INT x, y; + y = Popi(); + x = Popi(); + Pushi(fn(x, y)); +} + +void Glue1g(FLOAT fnf(FLOAT), INT fni(INT)) { + struct Value x; + x = Pop(); + switch (x.t) { + case kInt: + Pushi(fni(x.i)); + break; + case kFloat: + Pushf(fnf(x.f)); + break; + default: + Warning("type mismatch"); + } +} + +void Glue2g(FLOAT fnf(FLOAT, FLOAT), INT fni(INT, INT)) { + struct Value x, y; + y = Pop(); + x = Pop(); + if (x.t == kInt && y.t == kInt) { + Pushi(fni(x.i, y.i)); + } else if (x.t == kFloat && y.t == kFloat) { + Pushf(fnf(x.f, y.f)); + } else if (x.t == kInt && y.t == kFloat) { + Pushf(fnf(x.i, y.f)); + } else if (x.t == kFloat && y.t == kInt) { + Pushf(fnf(x.f, y.i)); + } else { + Warning("type mismatch"); + } +} + +#define LB { +#define RB } +#define SEMI ; +#define FNTYPEi INT +#define FNTYPEf FLOAT +#define FORM(F) LB F SEMI RB +#define FNPL0(T) void +#define FNPL1(T) FNTYPE##T x +#define FNPL2(T) FNTYPE##T x, FNTYPE##T y +#define FNDEF(A, T, S, C) FNTYPE##T Fn##S##T(FNPL##A(T)) FORM(return C) +#define FNDEFf(A, S, C) FNDEF(A, f, S, C) +#define FNDEFi(A, S, C) FNDEF(A, i, S, C) +#define FNDEFg(A, S, C) FNDEF(A, f, S, C) FNDEF(A, i, S, C) +#define OPDEF(A, T, S, C) void Op##S(void) FORM(Glue##A##T(Fn##S##T)) +#define OPDEFf(A, S, C) OPDEF(A, f, S, C) +#define OPDEFi(A, S, C) OPDEF(A, i, S, C) +#define OPDEFg(A, S, C) void Op##S(void) FORM(Glue##A##g(Fn##S##f, Fn##S##i)) +#define M(A, T, N, S, C, D) FNDEF##T(A, S, C) OPDEF##T(A, S, C) +#include "tool/build/calculator.inc" +#undef M + +const struct Function kFunctions[] = { + {".", OpPrint, "pops prints value repr"}, + {"#", OpComment, "line comment"}, +#define M(A, T, N, S, C, D) {N, Op##S, D}, +#include "tool/build/calculator.inc" +#undef M + {"dup", OpDup, "pushes copy of last item on stack"}, + {"drop", OpDrop, "pops and discards"}, + {"swap", OpSwap, "swaps last two items on stack"}, + {"over", OpOver, "pushes second item on stack"}, + {"cr", OpCr, "prints newline"}, + {"key", OpKey, "reads and pushes unicode character from keyboard"}, + {"emit", OpEmit, "pops and writes unicode character to output"}, + {"assert", OpAssert, "crashes if top of stack isn't nonzero"}, + {"expect", OpExpect, "prints warning if top of stack isn't nonzero"}, + {"meminfo", OpMeminfo, "prints memory mappings"}, + {"exit", OpExit, "exits program with status"}, +}; + +bool CallFunction(const char *sym) { + int i; + char s[16]; + strncpy(s, sym, sizeof(s)); + for (i = 0; i < ARRAYLEN(kFunctions); ++i) { + if (memcmp(kFunctions[i].sym, s, sizeof(s)) == 0) { + kFunctions[i].fun(); + return true; + } + } + return false; +} + +bool ConsumeLiteral(const char *literal) { + char *e; + struct Value x; + x.t = kInt; + x.i = strtoimax(literal, &e, 0); + if (!e || *e) { + x.t = kFloat; + x.f = strtod(literal, &e); + if (!e || *e) return false; + } + Push(x); + return true; +} + +void ConsumeToken(void) { + if (!token.i) return; + token.p[token.i] = 0; + token.i = 0; + if (history.i) history.p[history.i - 1].i = 0; + if (comment) return; + if (startswith(token.p, "#!")) return; + if (!setjmp(underflow)) { + if (CallFunction(token.p)) return; + if (ConsumeLiteral(token.p)) return; + Warning("bad token: %s", token.p); + } else { + Warning("stack underflow"); + } +} + +void CleanupRepl(void) { + if (sp && interactive) { + Cr(stdout); + } +} + +void AppendByte(struct Bytes *a, char b) { + APPEND(&a->p, &a->i, &a->n, &b); +} + +void AppendHistory(void) { + struct Bytes line; + if (interactive) { + memset(&line, 0, sizeof(line)); + APPEND(&history.p, &history.i, &history.n, &line); + } +} + +void AppendLine(char b) { + if (interactive) { + if (!history.i) AppendHistory(); + AppendByte(&history.p[history.i - 1], b); + } +} + +void Echo(int c) { + if (interactive) { + fputc(c, stdout); + } +} + +void Backspace(int c) { + struct Bytes *line; + if (interactive) { + if (history.i) { + line = &history.p[history.i - 1]; + if (line->i && token.i) { + do { + line->i--; + token.i--; + } while (line->i && token.i && (line->p[line->i] & 0x80)); + fputs("\e[D \e[D", stdout); + } + } + } +} + +void NewLine(void) { + line++; + column = 0; + comment = false; + if (interactive) { + Cr(stdout); + AppendHistory(); + ShowStack(); + } +} + +void KillLine(void) { + if (interactive) { + if (history.i) { + history.p[history.i - 1].i--; + } + } +} + +void ClearLine(void) { + if (interactive) { + if (token.i) sp = 0; + ShowStack(); + } +} + +void RedrawDisplay(void) { + int i; + struct Bytes *line; + if (interactive) { + printf("\e[H\e[2J%s", BANNER); + ShowStack(); + if (history.i) { + line = &history.p[history.i - 1]; + for (i = 0; i < line->i; ++i) { + fputc(line->p[i], stdout); + } + } + } +} + +void GotoStartOfLine(void) { + if (interactive) { + printf("\r"); + } +} + +void GotoEndOfLine(void) { +} + +void GotoPrevLine(void) { +} + +void GotoNextLine(void) { +} + +void GotoPrevChar(void) { +} + +void GotoNextChar(void) { +} + +void Calculator(FILE *f) { + int c; + while (!feof(f)) { + switch ((c = getc(f))) { + case -1: + case CTRL('D'): + goto Done; + case CTRL('@'): + break; + case ' ': + column++; + Echo(c); + AppendLine(c); + ConsumeToken(); + break; + case '\t': + column = ROUNDUP(column, 8); + Echo(c); + AppendLine(c); + ConsumeToken(); + break; + case '\r': + case '\n': + ConsumeToken(); + NewLine(); + break; + case CTRL('A'): + GotoStartOfLine(); + break; + case CTRL('E'): + GotoEndOfLine(); + break; + case CTRL('P'): + GotoPrevLine(); + break; + case CTRL('N'): + GotoNextLine(); + break; + case CTRL('B'): + GotoPrevChar(); + break; + case CTRL('F'): + GotoNextChar(); + break; + case CTRL('L'): + RedrawDisplay(); + break; + case CTRL('U'): + ClearLine(); + break; + case CTRL('K'): + KillLine(); + break; + case CTRL('?'): + case CTRL('H'): + Backspace(c); + break; + default: + column++; + /* fallthrough */ + case 0x80 ... 0xff: + Echo(c); + AppendLine(c); + AppendByte(&token, c); + break; + } + fflush(/* needed b/c this is event loop */ stdout); + } +Done: + CleanupRepl(); +} + +int Calculate(const char *path) { + FILE *f; + file = path; + line = column = 0; + if (!(f = fopen(file, "r"))) Fatal("file not found: %s", file); + Calculator(f); + return fclose(f); +} + +void CleanupTerminal(void) { + ttyraw(-1); +} + +void StartInteractive(void) { + if (!interactive && !isterminalinarticulate() && isatty(fileno(stdin)) && + isatty(fileno(stdout)) && cancolor()) { + interactive = true; + } + if (interactive) { + fputs(BANNER, stdout); + fflush(/* needed b/c entering tty mode */ stdout); + ttyraw(kTtyCursor | kTtySigs); + atexit(CleanupTerminal); + } +} + +void PrintUsage(int rc, FILE *f) { + char c; + int i, j; + fprintf(f, "%s %s%s", USAGE1, program_invocation_name, USAGE2); + for (i = 0; i < ARRAYLEN(kFunctions); ++i) { + fputs(" ", f); + for (j = 0; j < ARRAYLEN(kFunctions[i].sym); ++j) { + c = kFunctions[i].sym[j]; + fputc(c ? c : ' ', f); + } + fprintf(f, " %s\n", kFunctions[i].doc); + } + fputs(USAGE3, f); + exit(rc); +} + +void GetOpts(int argc, char *argv[]) { + int opt; + while ((opt = getopt(argc, argv, "?hi")) != -1) { + switch (opt) { + case 'i': + interactive = true; + break; + case 'h': + case '?': + PrintUsage(EXIT_SUCCESS, stdout); + default: + PrintUsage(EX_USAGE, stderr); + } + } +} + +int main(int argc, char *argv[]) { + int i, rc; + GetOpts(argc, argv); + if (optind == argc) { + file = "/dev/stdin"; + StartInteractive(); + Calculator(stdin); + return fclose(stdin); + } else { + for (i = optind; i < argc; ++i) { + if (Calculate(argv[i]) == -1) { + return -1; + } + } + } + return 0; +} diff --git a/tool/build/calculator.ctest b/tool/build/calculator.ctest new file mode 100755 index 00000000..527aada0 --- /dev/null +++ b/tool/build/calculator.ctest @@ -0,0 +1,83 @@ +# INTEGER +2 3 + 5 = assert +3 2 + 5 = assert +5 2 - 3 = assert +2 5 - -3 = assert +81 3 / 27 = assert +81 3 // 27 = assert +2 8 ** 256 = assert +17 10 % 7 = assert +17 10 mod 7 = assert + +# FLOATING POINT +.1 .2 + .3 - abs epsilon < assert +pi sqrt pi sqrt * pi - abs epsilon < assert +3 2 / 1.5 = assert +pi pi = assert +pi cos -1 = assert +pi 2 / sin 1 = assert +81 3 / 27 = assert +inf isinf assert +inf isnormal ! assert +nan isnormal ! assert +1 0 / isnormal ! assert +0 signbit ! assert +-.5 round -1 = assert +-.5 floor -1 = assert +-.5 rint dup 0 = assert signbit assert +-.5 nearbyint dup 0 = assert signbit assert +-.5 ceil dup 0 = assert signbit assert +-.5 trunc dup 0 = assert signbit assert +0 0 / dup isnan assert signbit assert # is this right? +1 0 / dup isinf assert signbit ! assert # is this right? + nan nan != assert # is this right? +-nan -nan != assert # is this right? + inf inf = assert # is this right? +-inf -inf = assert # is this right? + +# BIT ARITHMETIC +-1 ~ 0 = assert +0xffffffffffffffffffffffffffffffff ~ 0 = assert +0b1010101 popcnt 4 = assert +0b1010101 0b0110101 ^ 0b1100000 = assert +0b1010101 0b0110101 | 0b1110101 = assert +0b1010101 0b0110101 & 0b0010101 = assert +0b1010101 1 >> 0b000101010 = assert +0b1010101 2 >> 0b000010101 = assert +0b1010101 1 << 0b010101010 = assert +0b1010101 2 << 0b101010100 = assert + +# BOOLEAN +true assert +false ! assert +true ! ! assert +true true && assert +true false && ! assert +false true && ! assert +true false && ! assert +false true && ! assert +true true || assert +false true || assert +true false || assert +false false || ! assert +4 5 < assert +5 4 < ! assert +-5 4 < assert +5 5 < ! assert +5 5 <= assert +5 4 > assert +4 5 > ! assert +4 -5 > assert +5 5 > ! assert +5 5 >= assert + +# MISC +1 abs 1 = assert +-1 abs 1 = assert +-1 1 max 1 = assert +1 -1 max 1 = assert +1 2 max 2 = assert +-1 1 min -1 = assert +1 -1 min -1 = assert +1 2 min 1 = assert +rand64 rand64 rand64 rand64 != != && assert diff --git a/tool/build/calculator.inc b/tool/build/calculator.inc new file mode 100644 index 00000000..3c4dd844 --- /dev/null +++ b/tool/build/calculator.inc @@ -0,0 +1,86 @@ +M(2, g, "+", Add, x + y, "add") +M(2, g, "-", Sub, x - y, "sub") +M(2, g, "*", Mul, x *y, "multiply") +M(2, f, "/", Div, x / y, "division") +M(2, i, "%", Rem, x % y, "integer remainder") +M(2, i, "//", Idiv, x / y, "integer division") +M(2, g, "**", Expo, powl(x, y), "exponentiation") + +M(1, i, "~", Not, ~x, "bitwise not") +M(2, i, "^", Xor, x ^ y, "bitwise xor") +M(2, i, "|", Or, x | y, "bitwise or") +M(2, i, "&", And, x &y, "bitwise and") +M(2, i, ">>", Shr, x >> y, "shift right") +M(2, i, "<<", Shl, x << y, "shift left") + +M(1, i, "!", LogicalNot, !x, "logical not") +M(2, i, "||", LogicalOr, x || y, "logical or") +M(2, i, "&&", LogicalAnd, x &&y, "logical and") + +M(2, g, "=", Equal1, x == y && fpclassify(x) == fpclassify(y), "equal to") +M(2, g, "!=", Notequal, x != y, "not equal to") +M(2, g, "<", LessThan, x < y, "less than") +M(2, g, ">", GreaterThan, x > y, "greater than") +M(2, g, "<=", LessThanEqual, x <= y, "less than or equal to") +M(2, g, ">=", GreaterThanEqual, x >= y, "greater than or equal to") + +M(1, i, "gray", Gray, gray(x), "gray coding") +M(1, i, "ungray", Ungray, ungray(x), "inverse gray coding") +M(1, i, "popcnt", Popcnt, Popcnt(x), "count bits") + +M(1, g, "abs", Abs, fabsl(x), "absolute value") +M(2, g, "min", Min, MIN(x, y), "pops two values and pushes minimum") +M(2, g, "max", Max, MAX(x, y), "pops two values and pushes maximum") + +M(2, g, "mod", Euclidean, emodl(x, y), "euclidean remainder") +M(2, g, "rem", Remainder, remainderl(x, y), "float remainder") + +M(0, i, "false", False, 0, "0") +M(0, i, "true", True, 1, "1") +M(0, f, "e", Euler, M_E, "𝑒") +M(0, f, "pi", Fldpi, fldpi(), "π") +M(0, f, "epsilon", Epsilon, EPSILON, "ɛ") +M(0, f, "inf", Inf, INFINITY, "∞") +M(0, f, "nan", Nan, NAN, "NAN") +M(0, f, "-0", Negzero, -0., "wut") +M(0, f, "l2t", Fldl2t, fldl2t(), "log₂10") +M(0, f, "lg2", Fldlg2, fldlg2(), "log₁₀2") +M(0, f, "ln2", Fldln2, fldln2(), "logₑ2") +M(0, f, "l2e", Fldl2e, fldl2e(), "logₑ10") + +M(1, f, "sqrt", Sqrt, sqrtl(x), "√𝑥") +M(1, f, "exp", Exp, expl(x), "𝑒ˣ") +M(1, g, "exp2", Exp2, exp2l(x), "2ˣ") +M(1, g, "exp10", Exp10, exp10l(x), "10ˣ") +M(2, g, "ldexp", Ldexp, ldexpl(x, y), "𝑥×2ʸ") + +M(2, f, "log", Log, logl(x), "logₑ𝑥") +M(1, g, "log2", Log2, log2l(x), "log₂𝑥") +M(1, g, "log10", Log10, log10l(x), "log₁₀𝑥") + +M(1, g, "sin", Sin, sinl(x), "sine") +M(1, g, "cos", Cos, cosl(x), "cosine") +M(1, g, "tan", Tan, tanl(x), "tangent") +M(1, g, "asin", Asin, asinl(x), "arcsine") +M(1, g, "acos", Acos, acosl(x), "arccosine") +M(1, g, "atan", Atan, atanl(x), "arctangent") +M(2, g, "atan2", Atan2, atan2l(x, y), "arctangent of 𝑥/𝑦") + +M(1, g, "round", Round, roundl(x), "round away from zero") +M(1, g, "trunc", Trunc, truncl(x), "round towards zero") +M(1, g, "rint", Rint, rintl(x), "round to even") +M(1, g, "nearbyint", Nearbyint, nearbyint(x), "round to nearest integer") +M(1, g, "ceil", Ceil, ceill(x), "smallest integral not less than 𝑥") +M(1, g, "floor", Floor, floorl(x), "largest integral not greater than 𝑥") + +M(1, f, "isnan", Isnan, isnan(x), "returns true if 𝑥=NAN") +M(1, f, "isinf", Isinf, isinf(x), "returns true if 𝑥=INFINITY") +M(1, f, "signbit", Signbit, signbit(x), "clears all bits but sign bit") +M(1, f, "isfinite", Isfinite, isfinite(x), "returns true if 𝑥≠INFINITY") +M(1, f, "isnormal", Isnormal, isnormal(x), "returns true if not denormal") +M(1, f, "fpclassify", Fpclassify, fpclassify(x), + "nan=0,inf=1,zero=2,subnorm=3,normal=4") + +M(0, i, "rand", Rand, rand(), "deterministic random number") +M(0, i, "rand32", Rand32, rand32(), "32-bit random number") +M(0, i, "rand64", Rand64, rand64(), "64-bit random number") diff --git a/tool/emacs/cosmo.el b/tool/emacs/cosmo.el index 88fb36d7..0e780295 100644 --- a/tool/emacs/cosmo.el +++ b/tool/emacs/cosmo.el @@ -1,3 +1,4 @@ +(require 'mog-mode) (require 'ld-script) (require 'optinfo-mode) (require 'protobuf-mode) diff --git a/tool/emacs/ctest-mode.el b/tool/emacs/ctest-mode.el new file mode 100644 index 00000000..cfa2c2ab --- /dev/null +++ b/tool/emacs/ctest-mode.el @@ -0,0 +1,116 @@ +(defconst ctest-operators + '("+" + "-" + "*" + "/" + "%" + "//" + "**" + "~" + "^" + "|" + "&" + ">>" + "<<" + "!" + "||" + "&&" + "=" + "!=" + "<" + ">" + "<=" + ">=")) + +(defconst ctest-consts + '("false" + "true" + "e" + "pi" + "epsilon" + "inf" + "nan" + "l2t" + "lg2" + "ln2" + "l2e")) + +(defconst ctest-builtins + '("emit" + "exit" + "meminfo" + "isnan" + "isinf" + "signbit" + "isfinite" + "isnormal" + "fpclassify")) + +(defconst ctest-functions + '("emit" + "cr" + "key" + "dup" + "swap" + "over" + "drop" + "expect" + "assert" + "abs" + "min" + "max")) + +(defconst ctest-functions-libm + '("mod" + "rem" + "gray" + "ungray" + "popcnt" + "sqrt" + "exp" + "exp2" + "exp10" + "ldexp" + "log" + "log2" + "log10" + "sin" + "cos" + "tan" + "asin" + "acos" + "atan" + "atan2" + "round" + "trunc" + "rint" + "nearbyint" + "ceil" + "floor" + "rand" + "rand32" + "rand64")) + +(defun ctest--make-regex (words) + (concat "\\_<" (regexp-opt words) "\\_>")) + +(defconst ctest-highlights + `(("\\(#.*\\)$" 1 font-lock-comment-face) + ("\\b0\\([xX]\\)[[:xdigit:]]+\\([ulUL]*\\)\\b" + (1 font-lock-constant-face) + (2 font-lock-constant-face)) + ("\\b0\\([bB]\\)[01]+\\([ulUL]*\\)\\b" + (1 font-lock-constant-face) + (2 font-lock-constant-face)) + (,(concat "\\_<" (regexp-opt ctest-consts) "\\_>") . + font-lock-constant-face) + (,(concat "\\_<" (regexp-opt ctest-builtins) "\\_>") . + font-lock-builtin-face))) + +(define-derived-mode ctest-mode fundamental-mode "CALCULATOR.COM" + "Major mode for editing CALCULATOR.COM smoke tests." + (setq font-lock-defaults '(ctest-highlights))) + +(add-to-list 'auto-mode-alist '("\\.ctest$" . ctest-mode)) + +(provide 'ctest-mode)