Plan 9 from Bell Labs’s /usr/web/sources/contrib/rsc/pico/pico.y

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


%{
#include <u.h>
#include <libc.h>
#include <ctype.h>
#include <bio.h>
#include <draw.h>
#include <event.h>
#include <memdraw.h>

#define DX 248
#define DY 248

#ifdef USED /* plan9port */
#define system system9
#define CC "9c"
#define LD "9l"
#else
#define CC "8c"
#define LD "8l"
#endif

char *newname;
char *cmd;
Fmt codefmt;

int yylex(void);
int yyparse(void);
void yyerror(char*);

void checkref(char*);
void checkrefn(int);

%}

%union
{
	char* s;
	int i;
}

%token NAME NEW OLD NUM EOF ERROR
%token <s> FN NAME
%type <s> index zindex newname command expr fileref value
%type <i> NUM

%right '='
%right '?' ':'
%left '|'
%left '^'
%left '&'
%left EQ NE
%right LSH RSH
%left '<' '>' LE GE
%left '+' '-'
%left '*' '/' '%'
%right POW
%right '!'

%%

start: command EOF { cmd = $1; return 0; }

index:
	'[' expr ',' expr ']'  { $$ = smprint("%s, %s", $2, $4); }

zindex:
	index
|	{ $$ = "x, y"; }

newname:
	NEW { $$ = "new"; }
|	NEW NAME { $$ = $2; }
|	NAME { $$ = $1; }

command:
	newname zindex '=' expr  
	{ newname = $1; $$ = smprint("T = %s; *WIMAGE(%s, %s) = CLIP(T);", $4, $1, $2); }
|	expr
	{ newname = "new"; $$ = smprint("T = %s; *WIMAGE(new, x,y) = CLIP(T);", $1); }

expr:
	value
|	fileref
|	'x' { $$ = "x"; }
|	'y' { $$ = "y"; }
|	'X' { $$ = "X"; }
|	'Y' { $$ = "Y"; }
|	'Z' { $$ = "Z"; }
|	"(" expr ")" { $$ = $2; }
|	FN "(" expr ")" { $$ = smprint("%s(%s)", $1, $3); }
|	'-' expr %prec '!'  { $$ = smprint("-(%s)", $2); }
|	'!' expr { $$ = smprint("!(%s)", $2); }
|	expr '+' expr { $$ = smprint("(%s)+(%s)", $1, $3); }
|	expr '-' expr { $$ = smprint("(%s)-(%s)", $1, $3); }
|	expr '*' expr { $$ = smprint("(%s)*(%s)", $1, $3); }
|	expr '/' expr { $$ = smprint("DIV(%s, %s)", $1, $3); }
|	expr '%' expr { $$ = smprint("MOD(%s, %s)", $1, $3); }
|	expr '<' expr { $$ = smprint("(%s) < (%s)", $1, $3); }
|	expr '>' expr { $$ = smprint("(%s) > (%s)", $1, $3); }
|	expr LE expr { $$ = smprint("(%s) <= (%s)", $1, $3); }
|	expr GE expr { $$ = smprint("(%s) >= (%s)", $1, $3); }
|	expr EQ expr { $$ = smprint("(%s) == (%s)", $1, $3); }
|	expr NE expr { $$ = smprint("(%s) != (%s)", $1, $3); }
|	expr LSH expr { $$ = smprint("(%s) << (%s)", $1, $3); }
|	expr RSH expr { $$ = smprint("(%s) >> (%s)", $1, $3); }
|	expr '^' expr { $$ = smprint("(%s) ^ (%s)", $1, $3); }
|	expr '&' expr { $$ = smprint("(%s) & (%s)", $1, $3); }
|	expr '|' expr { $$ = smprint("(%s) | (%s)", $1, $3); }
|	expr '?' expr ':' expr { $$ = smprint("(%s) ? (%s) : (%s)", $1, $3, $5); }
|	expr POW expr { $$ = smprint("POW(%s, %s)", $1, $3); }

fileref:
	NAME zindex { checkref($1); $$ = smprint("IMAGE(%s, %s)", $1, $2); }
|	"$" NUM zindex { checkrefn($2); $$ = smprint("IMAGE(OLD[%d-1], %s)", $2, $3); }
|	OLD zindex { checkrefn(1); $$ = smprint("IMAGE(old, %s)", $2); }

value:
	NUM { $$ = smprint("%d", $1); }

%%

char *inp;

#define follow(x, y) (*inp == x ? (inp++, y) : c)
#define follow2(x, y, x1, y1) (*inp == x ? (inp++, y) : *inp == x1 ? (inp++, y1) : c)

jmp_buf boomer;

void
kaboom(char *fmt, ...)
{
	va_list arg;
	va_start(arg, fmt);
	vfprint(2, fmt, arg);
	va_end(arg);
	fprint(2, "\n");
	longjmp(boomer, 1);
}

void
yyerror(char *msg)
{
	kaboom("%s", msg);
}

int 
yylex()
{
	int c;

	while((c = *inp) != 0 && isspace(c))
		inp++;
	if(c == 0)
		return EOF;
	inp++;

	switch(c){
	case '+':
	case '-':
	case '/':
	case '%':
	case '?':
	case ':':
	case '|':
	case '^':
	case '&':
	case '$':
	case '[':
	case ']':
	case ',':
	case 'X':
	case 'Y':
	case 'Z':
	case '(':
	case ')':
		return c;
	case '*':
		return follow('*', POW);
	case '<':
		return follow2('=', LE, '<', LSH);
	case '>':
		return follow2('=', GE, '>', RSH);
	case '=':
		return follow('=', EQ);
	case '!':
		return follow('=', NE);
	}
	
	if(isdigit(c)){
		int n = c - '0';
		while((c = *inp) != 0 && isdigit(c)) {
			n = n * 10 + c - '0';
			inp++;
		}
		yylval.i = n;
		return NUM;
	}
	
	if(islower(c)){
		char buf[100];
		char *p = buf;
		char *ep = buf+sizeof buf-1;
		*p++ = c;
		while(p<ep && islower(*inp))
			*p++ = *inp++;
		*p = 0;
		if(buf[1]==0 && (buf[0]=='x' || buf[0]=='y'))
			return buf[0];
		yylval.s = strdup(buf);
		if(strcmp(buf, "new") == 0)
			return NEW;
		if(strcmp(buf, "log") == 0 || strcmp(buf, "sin") == 0 || strcmp(buf, "cos") == 0 || strcmp(buf, "sqrt") == 0)
			return FN;
		return NAME;
	}
	
	kaboom("unexpected %c", c);
	return ERROR;
}

int
system(char *s)
{
	int pid;
	
	pid = fork();
	if(pid == 0){
		execl("/bin/rc", "rc", "-c", s, nil);
		_exits(0);
	}
	if(pid == -1){
		fprint(2, "fork: %r\n");
		return -1;
	}
	Waitmsg *w;
	while((w = wait()) != nil && w->pid != pid)
		;
	if(w == nil){
		fprint(2, "%s: wait not found\n", s);
		return -1;
	}
	if(w->msg && w->msg[0]) {
		fprint(2, "%s: failed\n", s);
		return -1;
	}
	return 0;
}

void show(Memimage*);

void
xquit(int argc, char **argv)
{
	USED(argc);
	USED(argv);
	exits(0);
}

struct {
	char *name;
	char *path;
	Memimage *m;
	int ref;
} *files;
int nfiles;

Memimage*
namei(char *name, int warn)
{
	int i;
	
	for(i=0; i<nfiles; i++)
		if(strcmp(name, files[i].name) == 0){
			files[i].ref++;
			return files[i].m;
		}
	if(warn)
		fprint(2, "no image %s\n", name);
	return nil;
}

void
iput(char *name, Memimage *m)
{
	int i;

	for(i=0; i<nfiles; i++)
		if(name[0] && strcmp(name, files[i].name) == 0){
			freememimage(files[i].m);
			files[i].m = m;
			return;
		}
	files = realloc(files, ++nfiles*sizeof files[0]);
	files[i].name = strdup(name);
	files[i].path = nil;
	files[i].m = m;
}

void
checkref(char *name)
{
	if(namei(name, 1) == nil)
		longjmp(boomer, 1);
}

void
checkrefn(int n)
{
	if(n < 1 || n > nfiles)
		kaboom("no image $%d", n);
	files[n-1].ref++;
}

Memimage*
readi(char *name)
{
	int fd;
	Memimage *m, *m1;

	if((fd = open(name, OREAD)) < 0){
		fprint(2, "open %s: %r\n", name);
		return nil;
	}
	m = readmemimage(fd);
	close(fd);
	if(m == nil){
		fprint(2, "readmemimage: %r\n");
		return nil;
	}
	if(m->chan != GREY8 || !eqrect(m->r, Rect(0, 0, DX, DY))){
		m1 = allocmemimage(Rect(0, 0, DX, DY), GREY8);
		memfillcolor(m1, DBlack);
		memimagedraw(m1, m1->r, m, m->r.min, memopaque, ZP, S);
		freememimage(m);
		m = m1;
	}
	return m;
}

int
writei(Memimage *m, char *name)
{
	int fd;
	
	if((fd = create(name, OWRITE, 0666)) < 0){
		fprint(2, "create %s: %r\n", name);
		return -1;
	}
	if(writememimage(fd, m) < 0){
		fprint(2, "writememimage %s: %r\n", name);
		close(fd);
		return -1;
	}
	close(fd);
	return 0;
}

void
xfiles(int argc, char **argv)
{
	USED(argv);
	int i;
	if(argc != 1){
		fprint(2, "usage: f\n");
		return;
	}
	for(i=0; i<nfiles; i++)
		print("$%d %s %s\n", i+1, files[i].name, files[i].path ? files[i].path : "");
}

void
xread(int argc, char **argv)
{
	Memimage *m;

	if(argc < 2 || argc > 3){
		fprint(2, "usage: r image [filename]\n");
		return;
	}
	m = readi(argc == 3 ? argv[2] : argv[1]);
	iput(argv[1], m);
}

void
xwrite(int argc, char **argv)
{
	if(argc < 2 || argc > 3){
		fprint(2, "usage: w image [filename]\n");
		return;
	}
	Memimage *m = namei(argv[1], 1);
	if(m == nil)
		return;
	writei(m, argc == 3 ? argv[2] : argv[1]);
}

void
xdisplay(int argc, char **argv)
{
	int i;

	if(argc == 1){
		if(nfiles > 0)
			show(files[nfiles-1].m);
		return;
	}
	
	for(i=1; i<argc; i++){
		Memimage *m = namei(argv[i], 1);
		if(m)
			show(m);
	}
}

char* prolog = 
	"#include <u.h>\n"
	"#include <libc.h>\n"
	"#include <draw.h>\n"
	"#include <memdraw.h>\n"
	"\n"
	"Memimage*\n"
	"READ(char *file)\n"
	"{\n"
	"	Memimage *m;\n"
	"	int fd = open(file, OREAD);\n"
	"	if(fd < 0) sysfatal(\"open %s: %r\", file);\n"
	"	m = readmemimage(fd);\n"
	"	if(m == nil) sysfatal(\"readmemimage %s: %r\", file);\n"
	"	return m;\n"
	"}\n\n"
	"void\n"
	"WRITE(Memimage *m, char *file)\n"
	"{\n"
	"	int fd = create(file, OWRITE, 0666);\n"
	"	if(fd < 0) sysfatal(\"create %s: %r\", file);\n"
	"	if(writememimage(fd, m) < 0) sysfatal(\"writememimage %s: %r\", file);\n"
	"}\n\n"
	"int\n"
	"POW(int a, int b)\n"
	"{\n"
	"	int t;\n"
	"	if(b <= 0) return 1;\n"
	"	if(b == 1) return a;\n"
	"	t = POW(a, b/2);\n"
	"	t *= t;\n"
	"	if(b%2) t *= a;\n"
	"	return t;\n"
	"}\n"
	"\n"
	"int\n"
	"DIV(int a, int b)\n"
	"{\n"
	"	if(b == 0) return 0;\n"
	"	return a/b;\n"
	"}\n"
	"\n"
	"int\n"
	"MOD(int a, int b)\n"
	"{\n"
	"	if(b == 0) return 0;\n"
	"	return a%b;\n"
	"}\n"
	"\n"
	"#define X 248\n"
	"#define Y 248\n"
	"#define Z 255\n"
	"\n"
	"uchar\n"
	"IMAGE(uchar *p, int x, int y)\n"
	"{\n"
	"	if(x < 0 || y < 0 || x >= X || y >= Y) return 0;\n"
	"	return p[y*X+x];\n"
	"}\n"
	"\n"
	"uchar*\n"
	"WIMAGE(uchar *p, int x, int y)\n"
	"{\n"
	"	static uchar devnull;\n"
	"	if(x < 0 || y < 0 || x >= X || y >= Y) return &devnull;\n"
	"	return &p[y*X+x];\n"
	"}\n"
	"\n"
	"#define CLIP(x) ((x) < 0 ? 0 : (x) > 255 ? 255 : (x))\n"
	"\n"
	"void main(void) {\n"
	"	int x, y, T;\n"
	;

int quiet;

void
runprog(char *name, char *cmd)
{
	int i, fd, isnew;
	Memimage *m;
	
	if((fd = create("/tmp/pico-run.c", OWRITE, 0666)) < 0){
		fprint(2, "create /tmp/pico-run.c: %r");
		return;
	}
	
	write(fd, prolog, strlen(prolog));

	fprint(fd, "\tuchar* OLD[%d+1];  USED(OLD);\n", nfiles);
	fprint(fd, "\tuchar* old = 0; USED(old);\n");
	fprint(fd, "\tMemimage *M[%d+1];\n", nfiles);

	isnew = namei(name, 0) == 0;
	if(isnew){
		fprint(fd, "\tMemimage *NEW = allocmemimage(Rect(0, 0, X, Y), GREY8);\n");
		fprint(fd, "\tif(NEW == nil) sysfatal(\"allocmemimage: %%r\");\n");
		fprint(fd, "\tuchar* %s = NEW->data->bdata;\n", name);
	}

	for(i=0; i<nfiles; i++){
		fprint(fd, "\tM[%d] = READ(\"/tmp/pico-run.%d.png\");\n", i, i);
		fprint(fd, "\t%s %s %s old = OLD[%d] = M[%d]->data->bdata;\n", *files[i].name ? "uchar* " :"", files[i].name, *files[i].name ? " = " : "", i, i);
		if(strcmp(files[i].name, name) == 0)
			fprint(fd, "Memimage* NEW = M[%d];\n", i);
	}
	
	fprint(fd, "\tfor(x=0; x<%d; x++) for(y=0; y<%d; y++) {\n", DX, DY);
	fprint(fd, "\t\t%s\n", cmd);
	fprint(fd, "\t}");
	fprint(fd, "\tWRITE(NEW, \"/tmp/pico-run.out.png\");\n");
	fprint(fd, "\texits(0);\n}\n");
	close(fd);

	if(system(CC " -o /tmp/pico-run.o /tmp/pico-run.c") < 0)
		goto cleanup;
	if(system(LD " -o /tmp/pico-run /tmp/pico-run.o") < 0)
		goto cleanup;

	for(i=0; i<nfiles; i++)
		writei(files[i].m, smprint("/tmp/pico-run.%d.png", i));
	
	if(system("/tmp/pico-run") < 0)
		goto cleanup;

	m = readi("/tmp/pico-run.out.png");
	if(m){
		if(strcmp(name, "new") != 0)
			iput(name, m);
		else
			iput("", m);
		if(!quiet)
			show(m);
	}

	remove("/tmp/pico-run.c");
cleanup:
	remove("/tmp/pico-run.o");
	remove("/tmp/pico-run");
	remove("/tmp/pico-run.out.png");
	for(i=0; i<nfiles; i++)
		remove(smprint("/tmp/pico-run.%d.png", i));
}

struct {
	char *s;
	void (*f)(int, char**);
} cmds[] = {
	"d", xdisplay,
	"f", xfiles,
	"q",  xquit,
	"r", xread,
	"w", xwrite,
};

void
main(int argc, char **argv)
{
	Biobuf b;
	char *p, *f[10];
	int nf;
	int i, l;

	ARGBEGIN{
	case 'q':
		quiet = 1;
		break;
	}ARGEND

	Binit(&b, 0, OREAD);
	setjmp(boomer);
	for(;;){
	reread:
		if(!quiet)
			fprint(2, "-> ");
		if((p = Brdline(&b, '\n')) == 0)
			break;
		p[Blinelen(&b)-1] = 0;
		while(*p != 0 && isspace(*p))
			p++;
		if(*p == 0)
			goto reread;
		for(i=0; i<nelem(cmds); i++){
			l = strlen(cmds[i].s);
			if(strncmp(p, cmds[i].s, l) == 0 && (p[l] == 0 || isspace(p[l]))){
				nf = tokenize(p, f, nelem(f));
				cmds[i].f(nf, f);
				goto reread;
			}
		}
		
		inp = p;
		newname = nil;
		cmd = nil;
		yyparse();
		runprog(newname, cmd);
	}
	exits(0);	
}

#ifdef USED /* plan9port */

void
doresize(void)
{
	// wish this worked
	drawresizewindow(Rect(0, 0, DX, DY));
}

int
newwin(void)
{
	int pid;
	winsize = "248x248";
	notedisable("sys: child");
	pid = rfork(RFPROC|RFFDG|RFNOWAIT);
	if(pid < 0)
		fprint(2, "fork: %r\n");
	return pid;
}

#else /* plan 9 */

void
doresize(void)
{
}

int
newwin(void)
{
	char *srv;
	char spec[100];
	int srvfd, pid;

	rfork(RFNAMEG);

	srv = getenv("wsys");
	if(srv == 0){
		fprint(2, "no graphics: $wsys not set\n");
		return -1;
	}
	srvfd = open(srv, ORDWR);
	free(srv);
	if(srvfd == -1){
		fprint(2, "no graphics: can't open %s: %r\n", srv);
		return -1;
	}

	sprint(spec, "new -dx %d -dy %d-pid 0", DX, DY);
	if(mount(srvfd, -1, "/mnt/wsys", 0, spec) == -1){
		fprint(2, "no graphics: mount /mnt/wsys: %r (spec=%s)\n", spec);
		return -1;
	}
	close(srvfd);

	switch(pid = rfork(RFFDG|RFPROC|RFNAMEG|RFENVG|RFNOTEG|RFNOWAIT)){
	case -1:
		fprint(2, "no graphics: can't fork: %r\n");
		break;
	}
	if(pid == 0)
		bind("/mnt/wsys", "/dev", MBEFORE);
	else
		unmount(nil, "/mnt/wsys");
	return pid;
}

#endif

Image *displayed;

void
eresized(int new)
{
	if(new && getwindow(display, Refnone) < 0)
		fprint(2,"can't reattach to window");
	draw(screen, screen->r, displayed, nil, ZP);
	flushimage(display, 1);
}

void
showloop(Memimage *m)
{
	if(initdraw(0, 0, "pico") < 0){
		fprint(2, "initdraw: %r\n");
		return;
	}

	einit(Emouse|Ekeyboard);
	doresize();
	if((displayed = allocimage(display, Rect(0, 0, DX, DY), GREY8, 0, DBlack)) == nil){
		fprint(2, "allocimage: %r\n");
		return;
	}
	if(loadimage(displayed, displayed->r, byteaddr(m, ZP), DX*DY) < 0){
		fprint(2, "loadimage: %r\n");
		return;
	}
	close(0);
	eresized(0);
	for(;;){
		Event e;
		flushimage(display, 0);
		switch(eread(Emouse|Ekeyboard, &e)){
		case Ekeyboard:
			if(e.kbdc == 'q')
				return;
			eresized(0);
			break;
		case Emouse:
			if(e.mouse.buttons&4)
				return;
			break;
		}
	}
}

void
show(Memimage *m)
{
	if(newwin() != 0)
		return;
	showloop(m);
	exits(0);
}

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.