Add new calculator app

Cosmopolitan makes it easy to build and maintain programming languages,
since it abstracts system call #ifdef toil, so you can focus on vision.
Here's an example of a language that isn't turing complete, weighing in
at <1,000 lines of modern C, intended to help with testing libc / libm:

    .1 .2 + .3 - abs epsilon < assert
    pi sqrt pi sqrt * pi - abs epsilon < 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
    -.5 round -1 = assert
    -.5 floor -1 = assert
    0 signbit ! assert

CALCULATOR.COM pays homage to CALC.EXE recently removed from Windows 10.
Microsoft should bundle this app instead. It too is roughly 100kb, works
just fine w/ command prompt, and portable since it runs on Mac/Linux/BSD
too while bundling even more features than the calculator on Google.com.

It should be possible to run CALCULATOR.COM on Android and iOS too, just
in case anyone needs a backend pipe driven framework, for graphical user
interfaces of calculators. Sadly we haven't tried it since we don't know
how to run software on telephones so the system call support is a priori
main
Justine Tunney 2020-06-30 19:57:27 -07:00
parent ac00be1a4e
commit 4918121810
6 changed files with 1062 additions and 51 deletions

View File

@ -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)

View File

@ -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 <<EOF\n\
true assert\n\
-1 ~ 0 = assert\n\
3 2 / 1.5 = assert\n\
81 3 // 27 = assert\n\
2 8 ** 256 = assert\n\
pi cos -1 = assert\n\
pi sqrt pi sqrt * pi - abs epsilon < assert\n\
nan isnormal ! assert\n\
0b1010101 0b0110101 ^ 0b1100000 = assert\n\
0b1010101 popcnt 4 = assert\n\
EOF\n\
\n\
CONTACT\n\
\n\
Justine Tunney <jtunney@gmail.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;
}

View File

@ -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

View File

@ -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), "")
M(1, g, "exp10", Exp10, exp10l(x), "10ˣ")
M(2, g, "ldexp", Ldexp, ldexpl(x, y), "𝑥×")
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")

View File

@ -1,3 +1,4 @@
(require 'mog-mode)
(require 'ld-script)
(require 'optinfo-mode)
(require 'protobuf-mode)

View File

@ -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)