shithub: pico

ref: 90c1e6b8bad7302018388c4e9c1d30fd975ad134
dir: /pico.y/

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

char *newname, *cmd, buf[64*1024], *bufp = buf;

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

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

char *
sym(char *fmt, ...)
{
	va_list args;
	char *s;

	s = bufp;
	va_start(args, fmt);
	bufp = vseprint(s, buf+sizeof buf, fmt, args);
	va_end(args);
	if(bufp >= buf + sizeof(buf) - 1)
		sysfatal("NOPE NOPE NOPE");
	*bufp++ = 0;
	return s;
}

%}

%union
{
	char* s;
	int i;
	double d;
}

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

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

%%

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

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

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

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

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

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

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

value:
	NUM { $$ = sym("%d", $1); }
|	DBL { $$ = sym("%f", $1); }

%%

char *inp;

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
isnum(Rune r, char *s, char *p)
{
	if(isdigit(r)
	|| p - s == 1 && (r == 'x' || r == 'X')
	|| p - s > 2 && (s[1] == 'x' || s[1] == 'X') && (r >= 'a' && r <= 'f' || r >= 'A' && r <= 'F'))
		return 1;
	else if(r == '.')
		return 2;
	else
		return 0;
}

int
getnum(Rune r)
{
	int n, x, dbl;
	char s[128], *p;

	for(p=s, n=0, dbl=0; x = isnum(r, s, p); n=chartorune(&r, inp), inp+=n){
		if(p < s+sizeof(s)-1){
			*p++ = (char)r;
			if(x == 2)
				dbl = 1;
		}
	}
	*p = 0;
	inp -= n;
	if(dbl){
		yylval.d = strtod(s, nil);
		return DBL;
	}else{
		yylval.i = strtol(s, nil, 0);
		return NUM;
	}
}

int
getname(Rune r)
{
	int n;
	Rune s[128], *p;

	for(p=s, n=0; isalpharune(r) || isdigitrune(r) || r >= 0x2080 && r <= 0x2089; n=chartorune(&r, inp), inp+=n)
		if(p < s+nelem(s)-1)
			*p++ = r;
	*p = 0;
	inp -= n;
	if(runestrcmp(s, L"x") == 0
	|| runestrcmp(s, L"X") == 0
	|| runestrcmp(s, L"y") == 0
	|| runestrcmp(s, L"Y") == 0
	|| runestrcmp(s, L"z") == 0
	|| runestrcmp(s, L"Z") == 0)
		return s[0];
	yylval.s = sym("%S", s);
	if(runestrcmp(s, L"new") == 0)
		return NEW;
	else if(runestrcmp(s, L"log") == 0
	|| runestrcmp(s, L"sin") == 0
	|| runestrcmp(s, L"cos") == 0
	|| runestrcmp(s, L"sqrt") == 0)
		return FN;
	return NAME;
}

int
follow2(Rune r0, Rune r1, int op1, Rune r2, int op2)
{
	int n;
	Rune r;

	if(*inp == 0)
		return 0;
	n = chartorune(&r, inp);
	if(r == r1){
		inp += n;
		return op1;
	}else if(r == r2){
		inp += n;
		return op2;
	}
	return r0;
}

int
follow(Rune r0, Rune rr, int op)
{
	int n;
	Rune r;

	if(*inp == 0)
		return 0;
	n = chartorune(&r, inp);
	if(r == rr){
		inp += n;
		return op;
	}
	return r0;
}

int 
yylex(void)
{
	Rune r;

	for(;;){
		if(*inp == 0)
			return EOF;
		inp += chartorune(&r, inp);
		if(!isspacerune(r))
			break;
	}
	switch(r){
	case '+':
	case '-':
	case '/':
	case '%':
	case '?':
	case ':':
	case '^':
	case '$':
	case '[':
	case ']':
	case ',':
	case '(':
	case ')': return r;
	case '&': return follow(r, '&', AND);
	case '|': return follow(r, '|', OR);
	case '*': return follow(r, '*', POW);
	case '<': return follow2(r, '=', LE, '<', LSH);
	case '>': return follow2(r, '=', GE, '>', RSH);
	case '=': return follow(r, '=', EQ);
	case '!': return follow(r, '=', NE);
	}
	if(isdigit(r))
		return getnum(r);
	else if(isalpharune(r))
		return getname(r);
	kaboom("unexpected %C", r);
	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);
		free(w);
		return -1;
	}
	free(w);
	return 0;
}

void show(Memimage*);

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

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

int DX = 248, DY = 248;

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 != ABGR32){
		m1 = allocmemimage(m->r, ABGR32);
		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 tmp)
{
	int fd;
	
	if((fd = create(name, OWRITE|(tmp?ORCLOSE:0), 0666)) < 0){
		fprint(2, "create %s: %r\n", name);
		return -1;
	}
	if(writememimage(fd, m) < 0){
		close(fd);
		fprint(2, "writememimage %s: %r\n", name);
		return -1;
	}
	if(tmp)
		return fd;
	close(fd);
	return 0;
}

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, char *path)
{
	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]);
	if(strlen(name) == 0)
		files[i].name = smprint("$%d", i+1);
	else
		files[i].name = strdup(name);
	files[i].path = path == nil ? nil : strdup(path);
	files[i].m = m;
	files[i].fd = writei(files[i].m, sym("/tmp/pico-run.%d.bit", i), 1);
	DX = Dx(m->r);
	DY = Dy(m->r);
}

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++;
}

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

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]);
	if(m != nil)
		iput(argv[1], m, argc == 3 ? argv[2] : argv[1]);
}

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], 0);
}

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 Z 255\n"
	"\n"
	"uchar\n"
	"IMAGE(Memimage *m, int x, int y, int z)\n"
	"{\n"
	"	if(x < 0 || y < 0 || z < 0 || x >= Dx(m->r) || y >= Dy(m->r) || z > 3) return 0;\n"
	"	return byteaddr(m, addpt(m->r.min, Pt(x,y)))[z];\n"
	"}\n"
	"\n"
	"uchar*\n"
	"WIMAGE(Memimage *m, int x, int y, int z)\n"
	"{\n"
	"	static uchar devnull;\n"
	"	if(x < 0 || y < 0 || z < 0 || x >= Dx(m->r) || y >= Dy(m->r) || z > 3) return &devnull;\n"
	"	return byteaddr(m, addpt(m->r.min, Pt(x,y))) + z;\n"
	"}\n"
	"\n"
	"#define CLIP(x) ((x) < 0 ? 0 : (x) > 255 ? 255 : (x))\n"
	"\n"
	"void main(void) {\n"
	"	int x, y, z, 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, "\tint X = %d, Y = %d;\n", DX, DY);
	fprint(fd, "\tMemimage *old, *OLD[%d+1];\n", nfiles);

	isnew = namei(name, 0) == nil;
	if(isnew){
		fprint(fd, "\tMemimage *new = allocmemimage(Rect(0, 0, X, Y), ABGR32);\n");
		fprint(fd, "\tif(new == nil) sysfatal(\"allocmemimage: %%r\");\n");
		if(strcmp(name, "new") != 0)
			fprint(fd, "\tMemimage *%s = new;\n", name);
	}

	for(i=0; i<nfiles; i++){
		if(!files[i].ref)
			continue;
		fprint(fd, "\tOLD[%d] = old = READ(\"/tmp/pico-run.%d.bit\");\n", i, i);
		if(files[i].name[0] != '$'){
			fprint(fd, "\tMemimage *%s = old;\n", files[i].name);
			if(strcmp(files[i].name, name) == 0)
				fprint(fd, "Memimage* new = %s;\n", files[i].name);
		}
	}

	fprint(fd, "\tfor(z=0; z<4; z++) for(y=0; y<Y; y++) for(x=0; x<X; x++) {\n");
	fprint(fd, "\t\t%s\n", cmd);
	fprint(fd, "\t}");
	fprint(fd, "\tWRITE(new, \"/tmp/pico-run.out.bit\");\n");
	fprint(fd, "\texits(0);\n}\n");
	close(fd);

	if(access("/tmp/pico-run.mk", AEXIST) < 0){
		if((fd = create("/tmp/pico-run.mk", OWRITE|ORCLOSE, 0666)) < 0){
			fprint(2, "create /tmp/pico-run.mk: %r");
			goto cleanup;
		}
		fprint(fd, "</$objtype/mkfile\n"
			"/tmp/pico-run:Q: /tmp/pico-run.c\n"
			"\t$CC -o /tmp/pico-run.$O /tmp/pico-run.c\n"
			"\t$LD -o $target /tmp/pico-run.$O\n"
			"\t$target\n"
			"\trm -f /tmp/pico-run.$O\n");
	}
	if(system("mk -f /tmp/pico-run.mk /tmp/pico-run") < 0)
		goto cleanup;

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

	remove("/tmp/pico-run.c");
cleanup:
	remove("/tmp/pico-run");
	remove("/tmp/pico-run.out.bit");
	for(i=0; i<nfiles; i++)
		files[i].ref = 0;
}

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

	if(memimageinit() < 0)
		sysfatal("memimageinit: %r");
	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);	
}

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+8 < 100 ? 100 : DX+8, DY+8 < 48 ? 48 : DY+8);
	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;
}

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, displayed->r.min);
	flushimage(display, 1);
}

void
showloop(Memimage *m)
{
	Rectangle r;

	if(initdraw(0, 0, "pico") < 0){
		fprint(2, "initdraw: %r\n");
		return;
	}
	einit(Emouse|Ekeyboard);
	if((displayed = allocimage(display, m->r, m->chan, 0, DNofill)) == nil){
		fprint(2, "allocimage: %r\n");
		return;
	}
	r = displayed->r;
	while(r.min.y < displayed->r.max.y){
		r.max.y = r.min.y + 1;
		if(loadimage(displayed, r, byteaddr(m, r.min), Dx(r)*m->depth/8) < 0){
			fprint(2, "loadimage: %r\n");
			return;
		}
		r.min.y++;
	}
	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)
{
	DX = Dx(m->r);
	DY = Dy(m->r);
	if(newwin() != 0)
		return;
	showloop(m);
	exits(0);
}