summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--bin/.gitignore1
-rw-r--r--bin/Makefile3
-rw-r--r--bin/bit.y177
-rw-r--r--bin/bitlex.l67
-rw-r--r--bin/man1/bit.143
5 files changed, 144 insertions, 147 deletions
diff --git a/bin/.gitignore b/bin/.gitignore
index f1d66fc3..1b9d0034 100644
--- a/bin/.gitignore
+++ b/bin/.gitignore
@@ -3,6 +3,7 @@
 aes
 atch
 beef
+bit
 bri
 brot
 config.mk
diff --git a/bin/Makefile b/bin/Makefile
index 50d60133..71540ce0 100644
--- a/bin/Makefile
+++ b/bin/Makefile
@@ -5,7 +5,7 @@ PREFIX = ~/.local
 MANDIR = $(PREFIX)/share/man
 
 CFLAGS += -Wall -Wextra -Wpedantic
-LDLIBS = -lm -lutil -lz
+LDLIBS = -ledit -lm -lutil -lz
 
 CFLAGS_tls = $(CFLAGS) -I$(LIBRESSL_PREFIX)/include
 LDFLAGS_tls = $(LDFLAGS) -L$(LIBRESSL_PREFIX)/lib
@@ -18,6 +18,7 @@ LDLIBS_x11 = $(LDLIBS) -lX11
 -include config.mk
 
 BINS += aes
+BINS += bit
 BINS += dtch
 BINS += glitch
 BINS += hi
diff --git a/bin/bit.y b/bin/bit.y
index 8e9786da..637fe91c 100644
--- a/bin/bit.y
+++ b/bin/bit.y
@@ -25,27 +25,19 @@
 #include <stdlib.h>
 #include <sysexits.h>
 
-int yylex(void);
-void yyerror(const char *str);
+#define MASK(b) ((1ULL << (b)) - 1)
 
 #define YYSTYPE uint64_t
 
-enum { RingLen = 64 };
-static struct {
-	uint64_t vals[RingLen];
-	size_t len;
-} ring;
-
-static void push(uint64_t val) {
-	ring.vals[ring.len++ % RingLen] = val;
-}
-static uint64_t get(size_t i) {
-	return ring.vals[i % RingLen];
+static void yyerror(const char *str) {
+	warnx("%s", str);
 }
 
-%}
+static int yylex(void);
 
-%token Int Shl Shr Sar
+static uint64_t result;
+
+%}
 
 %left '|'
 %left '^'
@@ -56,18 +48,17 @@ static uint64_t get(size_t i) {
 %right '~'
 %left 'K' 'M' 'G' 'T'
 
+%token Int
+
 %%
 
-input:
-	expr { push($1); }
-	| input ',' expr { push($3); }
-	|
+start:
+	expr { result = $1; }
 	;
 
 expr:
 	Int
-	| '_' { $$ = get(ring.len - 1); }
-	| '[' Int ']' { $$ = get($2); }
+	| '_' { $$ = result; }
 	| '(' expr ')' { $$ = $2; }
 	| expr 'K' { $$ = $1 << 10; }
 	| expr 'M' { $$ = $1 << 20; }
@@ -90,69 +81,74 @@ expr:
 
 %%
 
-#include "bitlex.c"
-
-int yywrap(void) {
-	return 1;
-}
-
-void yyerror(const char *str) {
-	if (yychar < 128 && isprint(yychar)) {
-		warnx("%s at '%c'", str, yychar);
-	} else {
-		warnx("%s at %d", str, yychar);
+#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 char *prompt(EditLine *el) {
-	(void)el;
-	static char buf[64];
-	snprintf(buf, sizeof(buf), "[%zu]: ", ring.len);
-	return buf;
-}
-
-static void print(size_t i) {
-	uint64_t val = get(i);
+static int yylex(void) {
+	while (isspace(input[0])) input++;
+	if (!input[0]) return EOF;
 
-	int bits = val > UINT32_MAX ? 64
-		: val > UINT16_MAX ? 32
-		: val > UINT8_MAX ? 16
-		: 8;
-
-	char bin[65] = {0};
-	for (int i = 0; i < 64; ++i) {
-		bin[i] = '0' + (val >> (63 - i) & 1);
+	if (input[0] == '\'' && input[1] && input[2] == '\'') {
+		yylval = input[1];
+		input += 3;
+		return Int;
 	}
 
-	printf(
-		"[%zu]: %"PRId64" 0x%0*"PRIX64" 0b%s",
-		i, (int64_t)val, bits >> 2, val, &bin[64 - bits]
-	);
-
-	if (val) {
-		if (!(val & (1ULL << 40) - 1)) {
-			printf(" %"PRId64"T", val >> 40);
-		} else if (!(val & (1 << 30) - 1)) {
-			printf(" %"PRId64"G", val >> 30);
-		} else if (!(val & (1 << 20) - 1)) {
-			printf(" %"PRId64"M", val >> 20);
-		} else if (!(val & (1 << 10) - 1)) {
-			printf(" %"PRId64"K", val >> 10);
+	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 (val < 128 && isprint(val)) {
-		printf(" '%c'", (char)val);
+	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++;
 	}
+}
 
-	printf("\n");
+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, 64);
+	history(hist, &ev, H_SETSIZE, 100);
 	history(hist, &ev, H_SETUNIQUE, 1);
 
 	EditLine *el = el_init("bit", stdin, stdout, stderr);
@@ -162,23 +158,46 @@ int main(void) {
 
 	for (;;) {
 		int len;
-		const char *line = el_gets(el, &len);
-		if (!len) break;
+		input = el_gets(el, &len);
+		if (len == 0) break;
+		if (len == 1) continue;
+		history(hist, &ev, H_ENTER, input);
 
-		HistEvent ev;
-		history(hist, &ev, H_ENTER, line);
-
-		size_t i = ring.len;
-
-		YY_BUFFER_STATE state = yy_scan_string(line);
 		int error = yyparse();
-		yy_delete_buffer(state);
 		if (error) continue;
 
-		for (; i < ring.len; ++i) {
-			print(i);
+		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);
 		}
-		printf("\n");
+
+		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);
diff --git a/bin/bitlex.l b/bin/bitlex.l
deleted file mode 100644
index 4fac0a59..00000000
--- a/bin/bitlex.l
+++ /dev/null
@@ -1,67 +0,0 @@
-/* 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/>.
- */
-
-%option noinput
-%option nounput
-
-%{
-
-#include <stdint.h>
-
-static uint64_t parseInt(uint64_t base, const char *str) {
-	uint64_t num = 0;
-	for (const char *ch = str; *ch; ++ch) {
-		if (*ch == '_') continue;
-		num *= base;
-		if (*ch >= '0' && *ch <= '9') num += *ch - '0';
-		if (*ch >= 'A' && *ch <= 'F') num += 0xA + *ch - 'A';
-		if (*ch >= 'a' && *ch <= 'f') num += 0xA + *ch - 'a';
-	}
-	return num;
-}
-
-%}
-
-%%
-
-[ \t\n]	;
-
-0b[01_]+ {
-	yylval = parseInt(2, &yytext[2]);
-	return Int;
-}
-0[0-7_]+ {
-	yylval = parseInt(8, &yytext[1]);
-	return Int;
-}
-[0-9][0-9_]* {
-	yylval = parseInt(10, yytext);
-	return Int;
-}
-0x[0-9A-Fa-f_]+ {
-	yylval = parseInt(16, &yytext[2]);
-	return Int;
-}
-'.' {
-	yylval = yytext[1];
-	return Int;
-}
-
-"<<"	return Shl;
-">>"	return Shr;
-"->>"	return Sar;
-
-.	return yytext[0];
diff --git a/bin/man1/bit.1 b/bin/man1/bit.1
new file mode 100644
index 00000000..ddb26535
--- /dev/null
+++ b/bin/man1/bit.1
@@ -0,0 +1,43 @@
+.Dd May 12, 2019
+.Dt BIT 1
+.Os
+.
+.Sh NAME
+.Nm bit
+.Nd a calculator
+.
+.Sh SYNOPSIS
+.Nm
+.
+.Sh DESCRIPTION
+.Nm
+is an integer calculator.
+Its syntax resembles that of C,
+with the following additions:
+.
+.Bl -bullet
+.It
+Underscores are allowed in integer literals.
+.It
+The
+.Sy 0b
+prefix is used for binary literals.
+.It
+The
+.Sy ->
+operator is used for arithmetic shift.
+.It
+The postfix operators
+.Sy K ,
+.Sy M ,
+.Sy G ,
+.Sy T
+are used as constant multipliers.
+.It
+The symbol
+.Sy _
+is used to refer to the previous result.
+.El
+.
+.Sh SEE ALSO
+.Xr operator 7