summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--bin/.gitignore1
-rw-r--r--bin/Makefile4
-rw-r--r--bin/README1
-rw-r--r--bin/bin.73
-rw-r--r--bin/man1/order.134
-rw-r--r--bin/order.y151
6 files changed, 194 insertions, 0 deletions
diff --git a/bin/.gitignore b/bin/.gitignore
index ce3a976f..f1d66fc3 100644
--- a/bin/.gitignore
+++ b/bin/.gitignore
@@ -15,6 +15,7 @@ hi
 hnel
 modem
 open
+order
 pbcopy
 pbd
 pbpaste
diff --git a/bin/Makefile b/bin/Makefile
index 48cfb184..50d60133 100644
--- a/bin/Makefile
+++ b/bin/Makefile
@@ -23,6 +23,7 @@ BINS += glitch
 BINS += hi
 BINS += hnel
 BINS += modem
+BINS += order
 BINS += pbd
 BINS += pngo
 BINS += psf2png
@@ -127,6 +128,9 @@ $(HTMLS): ttpre hi html.sh
 .c.html:
 	sh html.sh $< man1/$(<:.c=.1) > $@
 
+.y.html:
+	sh html.sh $< man1/$(<:.y=.1) > $@
+
 .sh.html:
 	sh html.sh $< man1/$(<:.sh=.1) > $@
 
diff --git a/bin/README b/bin/README
index d3d05ade..6bfdb10e 100644
--- a/bin/README
+++ b/bin/README
@@ -19,6 +19,7 @@ DESCRIPTION
      hi(1)       syntax highlighter
      hnel(1)     PTY input remapper
      modem(1)    fixed baud rate wrapper
+     order(1)    operator precedence
      pbd(1)      macOS pasteboard daemon
      pngo(1)     PNG optimizer
      psf2png(1)  PSF2 to PNG renderer
diff --git a/bin/bin.7 b/bin/bin.7
index 0c7c3184..ba1e7e7c 100644
--- a/bin/bin.7
+++ b/bin/bin.7
@@ -52,6 +52,9 @@ PTY input remapper
 .It Xr modem 1
 fixed baud rate wrapper
 .
+.It Xr order 1
+operator precedence
+.
 .It Xr pbd 1
 macOS pasteboard daemon
 .
diff --git a/bin/man1/order.1 b/bin/man1/order.1
new file mode 100644
index 00000000..696a24d1
--- /dev/null
+++ b/bin/man1/order.1
@@ -0,0 +1,34 @@
+.Dd May 15, 2019
+.Dt ORDER 1
+.Os
+.
+.Sh NAME
+.Nm order
+.Nd operator precedence
+.
+.Sh SYNOPSIS
+.Nm
+.Ar expr ...
+.
+.Sh DESCRIPTION
+.Nm
+parses C expressions
+and prints them with parentheses
+according to the precedence rules in
+.Xr operator 7 .
+.
+.Sh EXAMPLES
+.Bd -literal
+$ order 'a & b << 1'
+(a & (b << 1))
+.Ed
+.
+.Sh SEE ALSO
+.Xr operator 7
+.
+.Sh CAVEATS
+.Nm
+does not support the
+.Sy (type) ,
+.Sy sizeof
+or assignment operators.
diff --git a/bin/order.y b/bin/order.y
new file mode 100644
index 00000000..fb5a052a
--- /dev/null
+++ b/bin/order.y
@@ -0,0 +1,151 @@
+/* Copyright (C) 2019  June 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 <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sysexits.h>
+
+static void yyerror(const char *str) {
+	errx(EX_DATAERR, "%s", str);
+}
+
+#define YYSTYPE char *
+
+static char *fmt(const char *format, ...) {
+	char *str = NULL;
+	va_list ap;
+	va_start(ap, format);
+	vasprintf(&str, format, ap);
+	va_end(ap);
+	if (!str) err(EX_OSERR, "vasprintf");
+	return str;
+}
+
+static int yylex(void);
+
+%}
+
+%token Var Arr Inc Dec Shl Shr Le Ge Eq Ne And Or
+
+%left ','
+%right '?' ':'
+%left Or
+%left And
+%left '|'
+%left '^'
+%left '&'
+%left Eq Ne
+%left '<' Le '>' Ge
+%left Shl Shr
+%left '+' '-'
+%left '*' '/' '%'
+%right '!' '~' Inc Dec
+%left '[' ']' Arr '.'
+
+%%
+
+start:
+	expr { printf("%s\n", $1); }
+	;
+
+expr:
+	Var
+	| '(' expr ')' { $$ = $2; }
+	| expr '[' expr ']' { $$ = fmt("(%s[%s])", $1, $3); }
+	| expr Arr Var { $$ = fmt("(%s->%s)", $1, $3); }
+	| expr '.' Var { $$ = fmt("(%s.%s)", $1, $3); }
+	| '!' expr { $$ = fmt("(!%s)", $2); }
+	| '~' expr { $$ = fmt("(~%s)", $2); }
+	| Inc expr { $$ = fmt("(++%s)", $2); }
+	| Dec expr { $$ = fmt("(--%s)", $2); }
+	| expr Inc { $$ = fmt("(%s++)", $1); }
+	| expr Dec { $$ = fmt("(%s--)", $1); }
+	| '-' expr { $$ = fmt("(-%s)", $2); }
+	| '*' expr { $$ = fmt("(*%s)", $2); }
+	| '&' expr { $$ = fmt("(&%s)", $2); }
+	| expr '*' expr { $$ = fmt("(%s * %s)", $1, $3); }
+	| expr '/' expr { $$ = fmt("(%s / %s)", $1, $3); }
+	| expr '%' expr { $$ = fmt("(%s %% %s)", $1, $3); }
+	| expr '+' expr { $$ = fmt("(%s + %s)", $1, $3); }
+	| expr '-' expr { $$ = fmt("(%s - %s)", $1, $3); }
+	| expr Shl expr { $$ = fmt("(%s << %s)", $1, $3); }
+	| expr Shr expr { $$ = fmt("(%s >> %s)", $1, $3); }
+	| expr '<' expr { $$ = fmt("(%s < %s)", $1, $3); }
+	| expr Le expr { $$ = fmt("(%s <= %s)", $1, $3); }
+	| expr '>' expr { $$ = fmt("(%s > %s)", $1, $3); }
+	| expr Ge expr { $$ = fmt("(%s >= %s)", $1, $3); }
+	| expr Eq expr { $$ = fmt("(%s == %s)", $1, $3); }
+	| expr Ne expr { $$ = fmt("(%s != %s)", $1, $3); }
+	| expr '&' expr { $$ = fmt("(%s & %s)", $1, $3); }
+	| expr '^' expr { $$ = fmt("(%s ^ %s)", $1, $3); }
+	| expr '|' expr { $$ = fmt("(%s | %s)", $1, $3); }
+	| expr And expr { $$ = fmt("(%s && %s)", $1, $3); }
+	| expr Or expr { $$ = fmt("(%s || %s)", $1, $3); }
+	| expr '?' expr ':' expr { $$ = fmt("(%s ? %s : %s)", $1, $3, $5); }
+	| expr ',' expr { $$ = fmt("(%s, %s)", $1, $3); }
+	;
+
+%%
+
+#define T(a, b) ((int)(a) << 8 | (int)(b))
+
+static const char *input;
+
+static int yylex(void) {
+	while (isspace(input[0])) input++;
+	if (!input[0]) return EOF;
+
+	int len;
+	for (len = 0; isalnum(input[len]) || input[len] == '_'; ++len);
+	if (len) {
+		yylval = fmt("%.*s", len, input);
+		input += len;
+		return Var;
+	}
+
+	int tok = 0;
+	switch (T(input[0], input[1])) {
+		break; case T('-', '>'): tok = Arr;
+		break; case T('+', '+'): tok = Inc;
+		break; case T('-', '-'): tok = Dec;
+		break; case T('<', '<'): tok = Shl;
+		break; case T('>', '>'): tok = Shr;
+		break; case T('<', '='): tok = Le;
+		break; case T('>', '='): tok = Ge;
+		break; case T('=', '='): tok = Eq;
+		break; case T('!', '='): tok = Ne;
+		break; case T('&', '&'): tok = And;
+		break; case T('|', '|'): tok = Or;
+	}
+	if (tok) {
+		input += 2;
+		return tok;
+	}
+
+	return *input++;
+}
+
+int main(int argc, char *argv[]) {
+	for (int i = 1; i < argc; ++i) {
+		input = argv[i];
+		yyparse();
+	}
+}