/*-*- 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.internal.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/math.h" #include "libc/mem/mem.h" #include "libc/nexgen32e/bsf.h" #include "libc/nexgen32e/bsr.h" #include "libc/nexgen32e/ffs.h" #include "libc/nexgen32e/x86feature.h" #include "libc/rand/rand.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 FnRand(struct Numbers *a) { return rand(); } static long double FnRand32(struct Numbers *a) { return rand32(); } static long double FnRand64(struct Numbers *a) { return rand64(); } static long double FnRdrand(struct Numbers *a) { if (X86_HAVE(RDRND)) { return rdrand(); } else { return NAN; } } static long double FnRdseed(struct Numbers *a) { if (X86_HAVE(RDSEED)) { return rdseed(); } else { return NAN; } } 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 FnBsrl(struct Numbers *a) { if (!a) MissingArgumentError(); return bsrl(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}, {"bsrl", FnBsrl}, {"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}, {"rand", FnRand}, {"rand32", FnRand32}, {"rand64", FnRand64}, {"rdrand", FnRdrand}, {"rdseed", FnRdseed}, {"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; }