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 priorimain
parent
ac00be1a4e
commit
4918121810
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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")
|
|
@ -1,3 +1,4 @@
|
|||
(require 'mog-mode)
|
||||
(require 'ld-script)
|
||||
(require 'optinfo-mode)
|
||||
(require 'protobuf-mode)
|
||||
|
|
|
@ -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)
|
Loading…
Reference in New Issue