diff options
Diffstat (limited to '')
-rw-r--r-- | bin/bit.y | 215 |
1 files changed, 215 insertions, 0 deletions
diff --git a/bin/bit.y b/bin/bit.y new file mode 100644 index 00000000..2d97766e --- /dev/null +++ b/bin/bit.y @@ -0,0 +1,215 @@ +/* Copyright (C) 2019 C. McEnroe <june@causal.agency> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +%{ + +#include <ctype.h> +#include <err.h> +#include <histedit.h> +#include <inttypes.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <sysexits.h> + +#define MASK(b) ((1ULL << (b)) - 1) + +static void yyerror(const char *str); +static int yylex(void); + +#define YYSTYPE uint64_t + +static uint64_t vars[128]; + +%} + +%right '=' +%left '|' +%left '^' +%left '&' +%left Shl Shr Sar +%left '+' '-' +%left '*' '/' '%' +%right '~' +%left 'K' 'M' 'G' 'T' + +%token Int Var + +%% + +stmt: + expr { vars['_'] = $1; } + ; + +expr: + Int + | Var { $$ = vars[$1]; } + | '(' expr ')' { $$ = $2; } + | expr 'K' { $$ = $1 << 10; } + | expr 'M' { $$ = $1 << 20; } + | expr 'G' { $$ = $1 << 30; } + | expr 'T' { $$ = $1 << 40; } + | '~' expr { $$ = ~$2; } + | '-' expr { $$ = -$2; } + | expr '*' expr { $$ = $1 * $3; } + | expr '/' expr { $$ = $1 / $3; } + | expr '%' expr { $$ = $1 % $3; } + | expr '+' expr { $$ = $1 + $3; } + | expr '-' expr { $$ = $1 - $3; } + | expr Shl expr { $$ = $1 << $3; } + | expr Shr expr { $$ = $1 >> $3; } + | expr Sar expr { $$ = (int64_t)$1 >> $3; } + | expr '&' expr { $$ = $1 & $3; } + | expr '^' expr { $$ = $1 ^ $3; } + | expr '|' expr { $$ = $1 | $3; } + | Var '=' expr { $$ = vars[$1] = $3; } + ; + +%% + +static void yyerror(const char *str) { + warnx("%s", str); +} + +#define T(a, b) ((int)(a) << 8 | (int)(b)) + +static const char *input; + +static int lexInt(uint64_t base) { + for (yylval = 0; input[0]; ++input) { + uint64_t digit; + if (input[0] == '_') { + continue; + } else if (input[0] >= '0' && input[0] <= '9') { + digit = input[0] - '0'; + } else if (input[0] >= 'A' && input[0] <= 'F') { + digit = 0xA + input[0] - 'A'; + } else if (input[0] >= 'a' && input[0] <= 'f') { + digit = 0xA + input[0] - 'a'; + } else { + return Int; + } + if (digit >= base) return Int; + yylval *= base; + yylval += digit; + } + return Int; +} + +static int yylex(void) { + while (isspace(input[0])) input++; + if (!input[0]) return EOF; + + if (input[0] == '\'' && input[1] && input[2] == '\'') { + yylval = input[1]; + input += 3; + return Int; + } + + if (input[0] == '0') { + if (input[1] == 'b') { + input += 2; + return lexInt(2); + } else if (input[1] == 'x') { + input += 2; + return lexInt(16); + } else { + input += 1; + return lexInt(8); + } + } else if (isdigit(input[0])) { + return lexInt(10); + } + + if (input[0] == '_' || islower(input[0])) { + yylval = *input++; + return Var; + } + + switch (T(input[0], input[1])) { + case T('<', '<'): input += 2; return Shl; + case T('>', '>'): input += 2; return Shr; + case T('-', '>'): input += 2; return Sar; + default: return *input++; + } +} + +static char *prompt(EditLine *el) { + (void)el; + return ""; +} + +int main(void) { + HistEvent ev; + History *hist = history_init(); + if (!hist) err(EX_OSERR, "history_init"); + history(hist, &ev, H_SETSIZE, 100); + history(hist, &ev, H_SETUNIQUE, 1); + + EditLine *el = el_init("bit", stdin, stdout, stderr); + if (!el) err(EX_IOERR, "el_init"); + el_set(el, EL_PROMPT, prompt); + el_set(el, EL_HIST, history, hist); + + for (;;) { + int len; + input = el_gets(el, &len); + if (len == 0) break; + if (len == 1) continue; + history(hist, &ev, H_ENTER, input); + + int error = yyparse(); + if (error) continue; + + uint64_t result = vars['_']; + + int bits = result > UINT32_MAX ? 64 + : result > UINT16_MAX ? 32 + : result > UINT8_MAX ? 16 + : 8; + + printf("0x%0*"PRIX64" %"PRId64"", bits >> 2, result, (int64_t)result); + + if (bits == 8) { + char bin[9] = {0}; + for (int i = 0; i < 8; ++i) { + bin[i] = '0' + (result >> (7 - i) & 1); + } + printf(" 0b%s", bin); + } + + if (result < 128 && isprint(result)) { + printf(" '%c'", (char)result); + } + + if (result) { + if (!(result & MASK(40))) { + printf(" %"PRIu64"T", result >> 40); + } else if (!(result & MASK(30))) { + printf(" %"PRIu64"G", result >> 30); + } else if (!(result & MASK(20))) { + printf(" %"PRIu64"M", result >> 20); + } else if (!(result & MASK(10))) { + printf(" %"PRIu64"K", result >> 10); + } + } + + printf("\n\n"); + } + + el_end(el); + history_end(hist); +} |