summary refs log tree commit diff
path: root/bin/bit.y
diff options
context:
space:
mode:
Diffstat (limited to 'bin/bit.y')
-rw-r--r--bin/bit.y215
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);
+}