diff options
| author | Thomas Voss <mail@thomasvoss.com> | 2024-09-04 19:10:47 +0200 | 
|---|---|---|
| committer | Thomas Voss <mail@thomasvoss.com> | 2024-09-04 19:10:47 +0200 | 
| commit | 581f61a8ddf535175b03749b215ade43f3fa28c5 (patch) | |
| tree | 44f0cb985d2a717c257347301ecc3d43d98ecfff | |
| parent | ada93069cb5fb292bfb5f3b8a22ba6221e55d7b5 (diff) | |
Support multiple eqns per table
| -rw-r--r-- | Makefile | 2 | ||||
| -rw-r--r-- | src/lexer.l | 1 | ||||
| -rw-r--r-- | src/main.c | 145 | ||||
| -rw-r--r-- | src/parser.y | 60 | ||||
| -rw-r--r-- | src/pinocchio.h | 7 | ||||
| -rw-r--r-- | src/wrapper.c | 27 | ||||
| -rw-r--r-- | src/wrapper.h | 11 | 
7 files changed, 185 insertions, 68 deletions
| @@ -5,7 +5,7 @@ MAKEFLAGS = -j8  CFLAGS = -O3 -march=native -mtune=native -pipe -Wall -Wextra -Wpedantic  target = pinocchio -objs = src/lexer.o src/main.o src/parser.o +objs = src/lexer.o src/main.o src/parser.o src/wrapper.o  all: $(target)  $(target): $(objs) diff --git a/src/lexer.l b/src/lexer.l index 832db79..befd30a 100644 --- a/src/lexer.l +++ b/src/lexer.l @@ -25,6 +25,7 @@ extern const char *current_file;  \<=>|⇔ { return EQUIV; }  \(     { return OPAR;  }  \)     { return CPAR;  } +\|     { return '|';   }  \n     { return EOL;   }  [a-zA-Z] { @@ -1,3 +1,4 @@ +#include <assert.h>  #include <ctype.h>  #include <err.h>  #include <getopt.h> @@ -9,13 +10,16 @@  #include <stdint.h>  #include <stdio.h>  #include <stdlib.h> -#include <string.h>  #include <unistd.h>  #include "lexer.h"  #include "parser.h" +#include "wrapper.h" -#define MAXVARS (26 * 2) +#define MAX(x, y) ((x) > (y) ? (x) : (y)) +#define MAXVARS   (26 * 2) + +#define lengthof(x) ((sizeof(x) / sizeof(*(x))))  #ifndef __has_builtin  #	define __has_builtin(x) (0) @@ -41,8 +45,8 @@ static tbl_style_t tflag = TS_UNSET;  const char *current_file; -static void astprocess_cli(ast_t); -static void astprocess_latex(ast_t); +static void astprocess_cli(asts_t); +static void astprocess_latex(asts_t);  static bool eqnsolve(eqn_t *, uint64_t, uint64_t);  static int  eqnprint(eqn_t *);  static void eqnfree(eqn_t *); @@ -77,11 +81,11 @@ main(int argc, char **argv)  	while ((opt = getopt_long(argc, argv, "b:t:", longopts, NULL)) != -1) {  		switch (opt) {  		case 'b': -			if (strcmp(optarg, "alpha") == 0) +			if (streq(optarg, "alpha"))  				bflag = BS_ALPHA; -			else if (strcmp(optarg, "binary") == 0) +			else if (streq(optarg, "binary"))  				bflag = BS_BINARY; -			else if (strcmp(optarg, "symbols") == 0) +			else if (streq(optarg, "symbols"))  				bflag = BS_SYMBOLS;  			else {  				warnx("invalid bool style -- '%s'", optarg); @@ -89,11 +93,11 @@ main(int argc, char **argv)  			}  			break;  		case 't': -			if (strcmp(optarg, "ascii") == 0) +			if (streq(optarg, "ascii"))  				tflag = TS_ASCII; -			else if (strcmp(optarg, "latex") == 0) +			else if (streq(optarg, "latex"))  				tflag = TS_LATEX; -			else if (strcmp(optarg, "utf8") == 0) +			else if (streq(optarg, "utf8"))  				tflag = TS_UTF8;  			else {  				warnx("invalid table style -- '%s'", optarg); @@ -109,7 +113,7 @@ usage:  	}  	if (tflag == TS_UNSET) { -		tflag = strcmp(nl_langinfo(CODESET), "UTF-8") == 0 +		tflag = streq(nl_langinfo(CODESET), "UTF-8")  			? TS_UTF8 : TS_ASCII;  	} @@ -124,7 +128,7 @@ usage:  			rv = EXIT_FAILURE;  		}  	} else for (int i = 0; i < argc; i++) { -		if (strcmp(argv[i], "-") == 0) +		if (streq(argv[i], "-"))  			yyin = stdin;  		else if ((yyin = fopen(argv[i], "r")) == NULL) {  			warn("fopen: %s", argv[i]); @@ -141,13 +145,16 @@ usage:  }  void -astprocess(ast_t a) +astprocess(asts_t as)  { -	(tflag == TS_LATEX ? astprocess_latex : astprocess_cli)(a); +	(tflag == TS_LATEX ? astprocess_latex : astprocess_cli)(as); +	for (size_t i = 0; i < as.len; i++) +		eqnfree(as.buf[i].eqn); +	free(as.buf);  }  void -astprocess_cli(ast_t a) +astprocess_cli(asts_t as)  {  	enum {  		TBLVBAR, @@ -174,39 +181,68 @@ astprocess_cli(ast_t a)  	const char **tblsyms = tflag == TS_UTF8 ? tblsyms_utf8 : tblsyms_ascii; +	uint64_t varmsk = as.buf[0].vars; +	for (size_t i = 1; i < as.len; i++) +		varmsk |= as.buf[i].vars; +	int varcnt = popcnt(varmsk); +  	for (int i = 0; i < MAXVARS; i++) { -		if ((a.vars & UINT64_C(1)<<i) != 0) +		if ((varmsk & UINT64_C(1)<<i) != 0)  			printf("%c ", i < 26 ? i + 'A' : i + 'a' - 26);  	} -	printf("%s ", tblsyms[TBLVBAR]); -	int eqnw = eqnprint(a.eqn); -	putchar('\n'); -	int varcnt = popcnt(a.vars); +	int *eqnws = xmalloc(sizeof(*eqnws) * as.len); + +	for (size_t i = 0; i < as.len; i++) { +		printf("%s ", tblsyms[TBLVBAR]); +		eqnws[i] = eqnprint(as.buf[i].eqn); +		if (i < as.len - 1) { +			eqnws[i]++; +			putchar(' '); +		} +	} +	putchar('\n');  	for (int i = 0; i < varcnt*2; i++)  		fputs(tblsyms[TBLHBAR], stdout);  	fputs(tblsyms[TBLCROS], stdout); -	for (int i = 0; i <= eqnw; i++) -		fputs(tblsyms[TBLHBAR], stdout); +	for (size_t i = 0; i < as.len; i++) { +		for (int j = 0; j <= eqnws[i]; j++) +			fputs(tblsyms[TBLHBAR], stdout); +		if (i < as.len - 1) +			fputs(tblsyms[TBLCROS], stdout); +	} +  	putchar('\n');  	for (uint64_t msk = 0; msk < (UINT64_C(1) << varcnt); msk++) {  		for (int i = varcnt; i --> 0;)  			printf("%s ", boolsyms[bflag][(bool)(msk & UINT64_C(1)<<i)]); -		int w = (eqnw & 1) == 0 ? eqnw / 2 : eqnw/2 + 1; -		printf("%s %*s\n", -			tblsyms[TBLVBAR], -			/* The symbols are encoded as 3 UTF-8 codepoints */ -			w + (bflag == BS_SYMBOLS)*2, -			boolsyms[bflag][eqnsolve(a.eqn, a.vars, msk)]); + +		for (size_t i = 0; i < as.len; i++) { +			int eqnw = eqnws[i]; +			int w = (eqnw & 1) == 0 ? eqnw / 2 : eqnw/2 + 1; +			printf("%s %*s", +				tblsyms[TBLVBAR], +				/* The symbols are encoded as 3 UTF-8 codepoints */ +				w + (bflag == BS_SYMBOLS)*2, +				boolsyms[bflag][eqnsolve(as.buf[i].eqn, varmsk, msk)]); +			if (i < as.len - 1) { +				if ((eqnw & 1) != 0) +					w--; +				for (int i = 0; i < w; i++) +					putchar(' '); +			} +		} + +		putchar('\n');  	} -	eqnfree(a.eqn); +	free(eqnws);  }  void -astprocess_latex(ast_t a) +astprocess_latex(asts_t as)  {  	static const char *boolsyms[][2] = {  		[BS_ALPHA]   = {"F", "T"}, @@ -215,33 +251,48 @@ astprocess_latex(ast_t a)  	};  	fputs("\\begin{displaymath}\n\t\\begin{array}{|", stdout); -	int varcnt = popcnt(a.vars); + +	uint64_t varmsk = as.buf[0].vars; +	for (size_t i = 1; i < as.len; i++) +		varmsk |= as.buf[i].vars; +	int varcnt = popcnt(varmsk); +  	for (int i = 0; i < varcnt; i++) { -		if (i == 0) -			putchar('c'); -		else -			fputs(" c", stdout); +		if (i > 0) +			putchar(' '); +		putchar('c');  	} -	fputs("|c|}\n\t\t", stdout); + +	for (size_t i = 0; i < as.len; i++) +		fputs("|c", stdout); +	fputs("|}\n\t\t", stdout);  	for (int i = 0; i < MAXVARS; i++) { -		if ((a.vars & UINT64_C(1)<<i) != 0) +		if ((varmsk & UINT64_C(1)<<i) != 0)  			printf("%c & ", i < 26 ? i + 'A' : i + 'a' - 26);  	} -	(void)eqnprint(a.eqn); +	for (size_t i = 0; i < as.len; i++) { +		(void)eqnprint(as.buf[i].eqn); +		if (i < as.len - 1) +			fputs(" & ", stdout); +	}  	puts("\\\\\n\t\t\\hline");  	for (uint64_t msk = 0; msk < (UINT64_C(1) << varcnt); msk++) {  		fputs("\t\t", stdout);  		for (int i = varcnt; i --> 0;)  			printf("%s & ", boolsyms[bflag][(bool)(msk & UINT64_C(1)<<i)]); -		printf("%s\\\\\n", boolsyms[bflag][eqnsolve(a.eqn, a.vars, msk)]); + +		for (size_t i = 0; i < as.len; i++) { +			printf("%s", boolsyms[bflag][eqnsolve(as.buf[i].eqn, varmsk, msk)]); +			if (i < as.len - 1) +				fputs(" & ", stdout); +		} +		fputs("\\\\\n", stdout);  	}  	puts("\t\\end{array}\n\\end{displaymath}"); - -	eqnfree(a.eqn);  }  bool @@ -252,6 +303,18 @@ eqnsolve(eqn_t *e, uint64_t vars, uint64_t msk)  		int i = 0, bitnr = islower(e->ch) ? e->ch-'a'+26 : e->ch-'A';  		for (int j = 0; j < bitnr; j++)  			i += (bool)(vars & UINT64_C(1)<<j); + +		/* We have a bit of a problem, where if given the input +		   ‘a && b | b && c’, when extracting the value of ‘c’ we +		   actually extract the value of ‘a’ because MSK is backwards. + +		   This means that if we have 3 variables (a, b, and c) and we +		   want to get the value of ‘c’, we need to extract bit 0 while +		   for ‘a’ we need bit 2.  This tomfoolery here does the mapping +		   of the ‘logical’ index (i.e. {0, 1, 2} for {a, b, c}) to the +		   ‘real’ index (i.e. {2, 1, 0} for {a, b, c}). */ +		i = -i + popcnt(vars) - 1; +  		return msk & UINT64_C(1)<<i;  	}  	case OPAR: diff --git a/src/parser.y b/src/parser.y index e77af6f..e424068 100644 --- a/src/parser.y +++ b/src/parser.y @@ -6,10 +6,10 @@  #include "lexer.h"  #include "parser.h" +#include "wrapper.h"  static ast_t mkunop(int, ast_t);  static ast_t mkbinop(int, ast_t, ast_t); -static void *xmalloc(size_t);  static void yyerror(const char *);  extern const char *current_file; @@ -20,8 +20,9 @@ extern const char *current_file;  }  %union { -	char  ch; -	ast_t ast; +	char   ch; +	ast_t  ast; +	asts_t asts;  }  %define parse.error verbose @@ -33,7 +34,8 @@ extern const char *current_file;  %token NOT AND OR XOR IMPL  %token EQUIV OPAR CPAR EOL  %token<ch> IDENT -%type<ast> line exp +%type<ast> expr +%type<asts> line exprs  %left OR  %left AND @@ -46,34 +48,51 @@ extern const char *current_file;  input:  	  %empty  	| input line { -		if ($2.eqn != NULL) +		if ($2.len > 0)  			astprocess($2);  	}  	;  line: -	      EOL { $$.eqn = NULL; } -	| exp eol { $$ = $1; } +	  EOL       { $$.len = 0; } +	| exprs eol { $$ = $1; }  	; -eol: EOL | YYEOF; +exprs: +	  expr { +		$$.len = 1; +		$$.cap = 8; +		$$.buf = xmalloc(sizeof(*$$.buf) * $$.cap); +		$$.buf[0] = $1; +	} +	| exprs '|' expr { +		$$ = $1; +		if ($$.len == $$.cap) { +			$$.cap *= 2; +			$$.buf = xrealloc($$.buf, sizeof(*$$.buf) * $$.cap); +		} +		$$.buf[$$.len++] = $3; +	} +	; -exp: +expr:  	  IDENT {  		$$.eqn = xmalloc(sizeof(eqn_t));  		$$.eqn->type = IDENT;  		$$.eqn->ch = $1;  		$$.vars = 1 << (islower($1) ? $1-'a'+26 : $1-'A');  	} -	| NOT exp       { $$ = mkunop(NOT,  $2);       } -	| OPAR exp CPAR { $$ = mkunop(OPAR, $2);       } -	| exp AND   exp { $$ = mkbinop(AND,   $1, $3); } -	| exp OR    exp { $$ = mkbinop(OR,    $1, $3); } -	| exp XOR   exp { $$ = mkbinop(XOR,   $1, $3); } -	| exp IMPL  exp { $$ = mkbinop(IMPL,  $1, $3); } -	| exp EQUIV exp { $$ = mkbinop(EQUIV, $1, $3); } +	| NOT expr        { $$ = mkunop(NOT,  $2);       } +	| OPAR expr CPAR  { $$ = mkunop(OPAR, $2);       } +	| expr AND   expr { $$ = mkbinop(AND,   $1, $3); } +	| expr OR    expr { $$ = mkbinop(OR,    $1, $3); } +	| expr XOR   expr { $$ = mkbinop(XOR,   $1, $3); } +	| expr IMPL  expr { $$ = mkbinop(IMPL,  $1, $3); } +	| expr EQUIV expr { $$ = mkbinop(EQUIV, $1, $3); }  	; +eol: EOL | YYEOF; +  %%  ast_t @@ -101,15 +120,6 @@ mkbinop(int op, ast_t lhs, ast_t rhs)  	return a;  } -void * -xmalloc(size_t n) -{ -	void *p = malloc(n); -	if (p == NULL) -		err(1, "malloc"); -	return p; -} -  void  yyerror(const char *s)  { diff --git a/src/pinocchio.h b/src/pinocchio.h index fb74f3c..1a25672 100644 --- a/src/pinocchio.h +++ b/src/pinocchio.h @@ -19,7 +19,12 @@ typedef struct {  	uint64_t vars;  } ast_t; -void astprocess(ast_t); +typedef struct { +	size_t len, cap; +	ast_t *buf; +} asts_t; + +void astprocess(asts_t);  void user_error(const char *, ...)  #if __GNUC__  	__attribute__((format(printf, 1, 2))) diff --git a/src/wrapper.c b/src/wrapper.c new file mode 100644 index 0000000..16c6300 --- /dev/null +++ b/src/wrapper.c @@ -0,0 +1,27 @@ +#include <err.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> + +void * +xmalloc(size_t n) +{ +	void *p = malloc(n); +	if (p == NULL) +		err(1, "malloc"); +	return p; +} + +void * +xrealloc(void *p, size_t n) +{ +	if ((p = realloc(p, n)) == NULL) +		err(1, "realloc"); +	return p; +} + +bool +streq(const char *x, const char *y) +{ +	return strcmp(x, y) == 0; +} diff --git a/src/wrapper.h b/src/wrapper.h new file mode 100644 index 0000000..a57f71e --- /dev/null +++ b/src/wrapper.h @@ -0,0 +1,11 @@ +#ifndef PINOCCHIO_WRAPPER_H +#define PINOCCHIO_WRAPPER_H + +#include <stdbool.h> +#include <stdint.h> + +void *xmalloc(size_t); +void *xrealloc(void *, size_t); +bool streq(const char *, const char *); + +#endif /* !PINOCCHIO_WRAPPER_H */ |