ORDER(1) General Commands Manual ORDER(1)

orderoperator precedence

order [expr ...]

order parses C expressions and prints them with parentheses according to the precedence rules in operator(7). If no expr are given, an expression is read from standard input.

$ order 'a & b << 1'
(a & (b << 1))

operator(7)

order does not support the operator.

July 18, 2020 OpenBSD 7.4

order.y in git

/* 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 <string.h>

#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(1, "vasprintf");
	return str;
}

static int yylex(void);
static void yyerror(const char *str);

%}

%token Ident

%left ','
%right '=' MulAss DivAss ModAss AddAss SubAss ShlAss ShrAss AndAss XorAss OrAss
%right '?' ':'
%left Or
%left And
%left '|'
%left '^'
%left '&'
%left Eq Ne
%left '<' Le '>' Ge
%left Shl Shr
%left '+' '-'
%left '*' '/' '%'
%right '!' '~' Inc Dec Sizeof
%left '(' ')' '[' ']' Arr '.'

%%

start:
	expr { printf("%s\n", $1); }
	;

expr:
	Ident
	| '(' expr ')' { $$ = $2; }
	| expr '[' expr ']' { $$ = fmt("(%s[%s])", $1, $3); }
	| expr Arr Ident { $$ = fmt("(%s->%s)", $1, $3); }
	| expr '.' Ident { $$ = 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 %prec '!' { $$ = fmt("(+%s)", $2); }
	| '-' expr %prec '!' { $$ = fmt("(-%s)", $2); }
	| '*' expr %prec '!' { $$ = fmt("(*%s)", $2); }
	| '&' expr %prec '!' { $$ = fmt("(&%s)", $2); }
	| Sizeof expr { $$ = fmt("(sizeof %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 ass expr %prec '=' { $$ = fmt("(%s %s %s)", $1, $2, $3); }
	| expr ',' expr { $$ = fmt("(%s, %s)", $1, $3); }
	;

ass:
	'=' { $$ = "="; }
	| MulAss { $$ = "*="; }
	| DivAss { $$ = "/="; }
	| ModAss { $$ = "%="; }
	| AddAss { $$ = "+="; }
	| SubAss { $$ = "-="; }
	| ShlAss { $$ = "<<="; }
	| ShrAss { $$ = ">>="; }
	| AndAss { $$ = "&="; }
	| XorAss { $$ = "^="; }
	| OrAss { $$ = "|="; }
	;

%%

#define T(a, b) ((int)(a) << 8 | (int)(b))

static FILE *in;

static int yylex(void) {
	char ch;
	while (isspace(ch = getc(in)));

	if (isalnum(ch)) {
		char ident[64] = { ch, '\0' };
		for (size_t i = 1; i < sizeof(ident) - 1; ++i) {
			ch = getc(in);
			if (!isalnum(ch) && ch != '_') break;
			ident[i] = ch;
		}
		ungetc(ch, in);
		if (!strcmp(ident, "sizeof")) return Sizeof;
		yylval = fmt("%s", ident);
		return Ident;
	}

	char ne = getc(in);
	switch (T(ch, ne)) {
		case T('-', '>'): return Arr;
		case T('+', '+'): return Inc;
		case T('-', '-'): return Dec;
		case T('<', '='): return Le;
		case T('>', '='): return Ge;
		case T('=', '='): return Eq;
		case T('!', '='): return Ne;
		case T('&', '&'): return And;
		case T('|', '|'): return Or;
		case T('*', '='): return MulAss;
		case T('/', '='): return DivAss;
		case T('%', '='): return ModAss;
		case T('+', '='): return AddAss;
		case T('-', '='): return SubAss;
		case T('&', '='): return AndAss;
		case T('^', '='): return XorAss;
		case T('|', '='): return OrAss;
		case T('<', '<'): {
			if ('=' == (ne = getc(in))) return ShlAss;
			ungetc(ne, in);
			return Shl;
		}
		case T('>', '>'): {
			if ('=' == (ne = getc(in))) return ShrAss;
			ungetc(ne, in);
			return Shr;
		}
		default: {
			ungetc(ne, in);
			return ch;
		}
	}
}

static void yyerror(const char *str) {
	errx(1, "%s", str);
}

int main(int argc, char *argv[]) {
	for (int i = 1; i < argc; ++i) {
		in = fmemopen(argv[i], strlen(argv[i]), "r");
		if (!in) err(1, "fmemopen");
		yyparse();
		fclose(in);
	}
	if (argc > 1) return 0;
	in = stdin;
	yyparse();
}