~pmikkelsen/lpa

05ae1b3d20f50803768fa3f6d023b3cf80a078cb — Peter Mikkelsen a month ago 2b23d05 front
Too much uncommitted work
18 files changed, 1728 insertions(+), 231 deletions(-)

R lpa => cons
M dat.h
M eval.c
M fns.h
M fs.c
A ide.c
D main.c
M mkfile
M module.c
A optimise.c
M parse.c
M prim.c
M scan.c
M session.c
M symtab.c
M systemcmd.c
M util.c
M value.c
R lpa => cons +2 -2
@@ 43,9 43,9 @@ if(! ~ $#* 0)
	usage

# Start LPA if it isn't already running
lpafs
lpa/fs
# Make /mnt/lpa available to sam and rio..
plumb 'Local lpafs'
plumb 'Local lpa/fs'

if(~ $printlist 1){
	echo `{cd /mnt/lpa; ls | grep -v '^new$'}

M dat.h => dat.h +26 -6
@@ 34,6 34,8 @@ extern DataSpec dataspecs[DataMax]; /* memory.c */

typedef struct Symbol Symbol;
typedef struct Symtab Symtab;
typedef struct Module Module;
typedef struct Session Session;

struct Symbol
{


@@ 51,16 53,16 @@ struct Symtab

	uvlong count;
	Symbol **symbols;
	Module *module;
};

typedef struct Module Module;
struct Module
{
	uvlong id;
	char *name;

	Symtab *symtab;
	Qid qsession;
	Session *session;
	Qid qmodule;
};



@@ 72,13 74,13 @@ struct ModuleList
	Module **modules;
};

typedef struct Session Session;	
struct Session
{
	uvlong id;
	char *name;

	int active; /* is the session alive? */
	int debugtrace; /* slow evaluation with plumb of localtion */

	ModuleList *modules;



@@ 132,6 134,7 @@ struct Token
{
	int tag;
	int nameclass;
	int line;
	union {
		vlong num; /* TokNumber */
		char *name; /* TokName: UTF-8 encoded name */


@@ 187,6 190,9 @@ struct Ast
	int prim;
	int optional; /* optional left arg */

	int linestart;
	int lineend;

	Ast *funcname;
	Ast *funcresult;
	Ast *funcleftarg;


@@ 215,19 221,23 @@ enum Nameclass

struct ByteCode
{
	Module *module;

	uvlong count;
	u8int *instrs;
};

enum Instr
{
	IPushConst,
	IPushConst = 1,
	IPushPrim,
	ILookup,
	IStrand,
	INiladic,
	IMonadic,
	IDyadic,
	ICall,
	ITailCall,
	IParse,
	IReturn,
	IAssign,


@@ 235,11 245,15 @@ enum Instr
	IPop,
	IDisplay,
	IPushVar,
	IFuncres,

	ILine,
};

typedef struct ValueStack ValueStack;
struct ValueStack
{
	int shy; /* is the top-most element shy? */
	uvlong count;
	void **values;
};


@@ 258,6 272,8 @@ struct LocalList
	Local *list;
};

typedef struct Function Function;

typedef struct CallFrame CallFrame;
struct CallFrame
{


@@ 265,6 281,11 @@ struct CallFrame
	ByteCode *code;
	uvlong offset;

	/* For debugtrace */
	int line;
	int instr;
	Function *func;

	/* Old values of symbols before they were localised */
	LocalList *locals;
};


@@ 284,7 305,6 @@ enum Valence
	Variadic = 6,
};

typedef struct Function Function;
struct Function
{
	Ast *ast;


@@ 377,4 397,4 @@ struct ConstraintGraph

	ConstraintVar *vs[128];
	Constraint *cs[128];
};
\ No newline at end of file
};

M eval.c => eval.c +193 -99
@@ 14,10 14,11 @@ eval(Session *s, Ast *a)
	/* Evaluate some ast in module m in session s. */
	Module *m = s->modules->modules[0]; /* TODO: this isn't nice */
	ByteCode *code = codegen(s, m, a);
	optimisebc(code, nil, m->symtab);
	return evalbc(s, m, code);
}

static void
void
emitbyte(ByteCode *c, u8int i)
{
	c->count += 1;


@@ 57,15 58,20 @@ emitlocal(ByteCode *c, Symtab *s, Ast *a, int assign)
}

static void
codegensub(Session *s, Module *m, ByteCode *c, Ast *a)
codegensub(Session *s, ByteCode *c, Ast *a, int funcres, int *line)
{
	uvlong i;

	if(a->linestart != *line){
		emitbyte(c, ILine);
		emituvlong(c, a->linestart);
		*line = a->linestart;
	}

	switch(a->tag){
	case AstProg:
		for(i = 0; i < a->childcount; i++){
			codegensub(s, m, c, a->children[i]);
			emitbyte(c, IPop);
			codegensub(s, c, a->children[i], 0, line);
			emitbyte(c, IDisplay);
		}
		break;


@@ 85,23 91,27 @@ codegensub(Session *s, Module *m, ByteCode *c, Ast *a)
			if(fn->ast->funcresult)
				fn->hasresult = 1;

			fn->symbol = sym(m->symtab, a->funcname->name);
			fn->symbol = sym(c->module->symtab, a->funcname->name);
			fn->code = alloc(DataByteCode);
			fn->code->module = c->module;
			emitbyte(fn->code, ILine);
			emituvlong(fn->code, a->linestart);
			emitbyte(fn->code, IPushConst);
			emitptr(fn->code, fn);
			emitlocal(fn->code, m->symtab, fn->ast->funcname, 1);
			emitlocal(fn->code, m->symtab, fn->ast->funcresult, 0);
			emitlocal(fn->code, m->symtab, fn->ast->funcleftarg, 1);
			emitlocal(fn->code, m->symtab, fn->ast->funcrightarg, 1);
			emitlocal(fn->code, c->module->symtab, fn->ast->funcname, 1);
			emitlocal(fn->code, c->module->symtab, fn->ast->funcresult, 0);
			emitlocal(fn->code, c->module->symtab, fn->ast->funcleftarg, 1);
			emitlocal(fn->code, c->module->symtab, fn->ast->funcrightarg, 1);
			for(i = 0; i < fn->ast->funclocals->childcount; i++)
				emitlocal(fn->code, m->symtab, fn->ast->funclocals->children[i], 0);
				emitlocal(fn->code, c->module->symtab, fn->ast->funclocals->children[i], 0);
			for(i = 0; i < a->childcount; i++){
				codegensub(s, m, fn->code, a->children[i]);
				codegensub(s, fn->code, a->children[i], 0, line);
				emitbyte(fn->code, IPop);
			}
			if(fn->ast->funcresult)
				codegensub(s, m, fn->code, fn->ast->funcresult);
				codegensub(s, fn->code, fn->ast->funcresult, 1, line);
			emitbyte(fn->code, IReturn);
			optimisebc(fn->code, fn->ast, c->module->symtab);

			emitbyte(c, IPushConst);
			emitptr(c, fn);


@@ 112,12 122,12 @@ codegensub(Session *s, Module *m, ByteCode *c, Ast *a)
		break;
	case AstName:
		emitbyte(c, ILookup);
		emituvlong(c, sym(m->symtab, a->name));
		emituvlong(c, sym(c->module->symtab, a->name));
		break;
	case AstAssign:
		codegensub(s, m, c, a->right);
		codegensub(s, c, a->right, 1, line);
		emitbyte(c, IAssign);
		emituvlong(c, sym(m->symtab, a->left->name));
		emituvlong(c, sym(c->module->symtab, a->left->name));
		break;
	case AstConst:
		emitbyte(c, IPushConst);


@@ 126,24 136,33 @@ codegensub(Session *s, Module *m, ByteCode *c, Ast *a)
	case AstStrand:
		/* right to left */
		for(i = a->childcount; i > 0; i--)
			codegensub(s, m, c, a->children[i-1]);
			codegensub(s, c, a->children[i-1], 1, line);
		emitbyte(c, IStrand);
		emituvlong(c, a->childcount);
		break;
	case AstNiladic:
		codegensub(s, m, c, a->func);
		codegensub(s, c, a->func, 1, line);
		emitbyte(c, INiladic);
		emitbyte(c, IFuncres);
		emituvlong(c, funcres);
		emitbyte(c, ICall);
		break;
	case AstMonadic:
		codegensub(s, m, c, a->right);
		codegensub(s, m, c, a->func);
		codegensub(s, c, a->right, 1, line);
		codegensub(s, c, a->func, 1, line);
		emitbyte(c, IMonadic);
		emitbyte(c, IFuncres);
		emituvlong(c, funcres);
		emitbyte(c, ICall);
		break;
	case AstDyadic:
		codegensub(s, m, c, a->right);
		codegensub(s, m, c, a->left);
		codegensub(s, m, c, a->func);
		codegensub(s, c, a->right, 1, line);
		codegensub(s, c, a->left, 1, line);
		codegensub(s, c, a->func, 1, line);
		emitbyte(c, IDyadic);
		emitbyte(c, IFuncres);
		emituvlong(c, funcres);
		emitbyte(c, ICall);
		break;
	case AstPrim:
		emitbyte(c, IPushPrim);


@@ 152,19 171,30 @@ codegensub(Session *s, Module *m, ByteCode *c, Ast *a)
	case AstLater:
		emitbyte(c, IParse);
		emitptr(c, a->tokens);
		emituvlong(c, funcres);
		break;
	default:
		error(EInternal, "Don't know how to do codegen for ast type %d", a->tag);
		break;
	}

	if(a->lineend != *line){
		assert(a->lineend >= a->linestart);
		assert(*line >= a->linestart);
		emitbyte(c, ILine);
		emituvlong(c, a->lineend);
		*line = a->lineend;
	}

}

static ByteCode *
codegen(Session *s, Module *m, Ast *a)
{
	ByteCode *c = alloc(DataByteCode);
	codegensub(s, m, c, a);
	c->module = m;
	int line = 0;
	codegensub(s, c, a, 0, &line);
	return c;
}



@@ 174,6 204,7 @@ pushval(ValueStack *s, void *v)
	s->count++;
	s->values = allocextra(s, s->count * sizeof(v));
	s->values[s->count-1] = v;
	s->shy = 0;
}

static void *


@@ 182,6 213,7 @@ popval(ValueStack *s)
	if(s->count == 0)
		error(EInternal, "popval on empty value stack");
	s->count--; /* no realloc */
	s->shy = 0;
	return s->values[s->count];
}



@@ 194,30 226,49 @@ peekval(ValueStack *s)
}

static void
pushcall(CallStack *s, ByteCode *newcode, ByteCode **c, uvlong *o)
pushcall(CallStack *s, ByteCode *newcode, ByteCode **c, uvlong *o, int *line, int *instr, Function **func, LocalList *locals)
{
	s->count++;
	s->frames = allocextra(s, s->count * sizeof(CallFrame));
	s->frames[s->count-1].code = *c;
	s->frames[s->count-1].offset = *o;
	s->frames[s->count-1].locals = alloc(DataLocalList);
	s->frames[s->count-1].line = *line;
	s->frames[s->count-1].instr = *instr;
	s->frames[s->count-1].func = *func;
	s->frames[s->count-1].locals = locals ? locals : alloc(DataLocalList);

	*c = newcode;
	*o = 0;
	*line = 0;
	*instr = 0;
	*func = nil;
}

static void
popcall(CallStack *s, Symtab *t, ByteCode **c, uvlong *o)
popcall(Session *session, CallStack *s, Symtab *t, ByteCode **c, uvlong *o, int *line, int *instr, Function **func, LocalList **localsp)
{
	if(s->count == 0)
		error(EInternal, "popcall on empty call stack");
	s->count--; /* no realloc */

	if(session->debugtrace && *func){
		char *name = funcname(*func);
		plumbedit(*func, name, session, 0, -1);
		plumbedit((*func)->code, name, session, 0, -1);
	}

	*c = s->frames[s->count].code;
	*o = s->frames[s->count].offset;
	*line = s->frames[s->count].line;
	*instr = s->frames[s->count].instr;
	*func = s->frames[s->count].func;

	LocalList *locals = s->frames[s->count].locals;
	for(uvlong i = 0; i < locals->count; i++)
		symset(t, locals->list[i].id, locals->list[i].value);
	if(localsp)
		*localsp = locals; /* tail call */
	else
		for(uvlong i = 0; i < locals->count; i++)
			symset(t, locals->list[i].id, locals->list[i].value);
}

static void


@@ 233,19 284,6 @@ pushlocal(CallStack *c, Symtab *s, uvlong id)
	symset(s, id, nil);
}

static int
nextinstr(CallStack *calls, ByteCode *c, uvlong o)
{
	if(o < c->count && c->instrs[o] != IReturn)
		return c->instrs[o];
	if(calls->count == 0)
		return -1;
	else{
		CallFrame f = calls->frames[calls->count-1];
		return f.code->instrs[f.offset];
	}
}

static void
checkarray(void *val)
{


@@ 264,15 302,34 @@ evalbc(Session *s, Module *m, ByteCode *c)
	Function *func;
	void *r;
	Array *x, *y, *z;
	Array *(*primfn)(int, Array *, Array *);
	LocalList *locals;

	Function *currentfunc = nil;
	int currentline = 0;
	int currentinstr = 0;

	values = alloc(DataValueStack);
	calls = alloc(DataCallStack);

	func = nil;
	primfn = nil;
	x = y = nil;
	locals = nil;

	o = 0;
	while(o < c->count){
		int instr = c->instrs[o];
		o++;

		currentinstr++;
		if(s->debugtrace && currentfunc){
			char *name = funcname(currentfunc);
			plumbedit(currentfunc, name, s, 0, currentline);
			plumbedit(currentfunc->code, name, s, 0, currentinstr);
			sleep(1000);
		}

		switch(instr){
		case IPushConst:
			o += getuvlong(c->instrs+o, &v);


@@ 313,51 370,21 @@ evalbc(Session *s, Module *m, ByteCode *c)
			break;
		case INiladic:
			func = popval(values);
			if(func->valence != Niladic){
				int next = nextinstr(calls, c, o);
				if(next == IAssign || IPop){
					pushval(values, func);
					break;
				}else
					error(ESyntax, "Function %s is not niladic", funcname(func));
			}
			primfn = primnilad;
			x = nil;
			y = nil;

			if(func->code){
				if(!func->hasresult){
					if(nextinstr(calls, c, o) == IPop)
						pushval(values, nil); /* fake result */
					else
						error(ESyntax, "Function %s does not produce a result", funcname(func));
				}
				pushcall(calls, func->code, &c, &o);
			}else{
				z = primnilad(func->prim);
				pushval(values, z);
			}
			if(!(func->valence == Niladic))
				error(ESyntax, "Function %s is not niladic", funcname(func));
			break;
		case IMonadic:
			/* FIXME: more duplicated code with INiladic and IDyadic than i would like */
			func = popval(values);
			y = popval(values);
			if(!(func->valence & Monadic))
				error(ESyntax, "Function %s is not monadic", funcname(func));
			checkarray(y);

			if(func->code){
				if(!func->hasresult){
					if(nextinstr(calls, c, o) == IPop)
						pushval(values, nil); /* fake result */
					else
						error(ESyntax, "Function %s does not produce a result", funcname(func));
				}
				pushval(values, y);
				if(func->valence & Dyadic) /* ambivalent function */
					pushval(values, nil);
				pushcall(calls, func->code, &c, &o);
			}else{
				z = primmonad(func->prim, y);
				pushval(values, z);
			}
			primfn = primmonad;
			x = nil;
			break;
		case IDyadic:
			func = popval(values);


@@ 367,46 394,54 @@ evalbc(Session *s, Module *m, ByteCode *c)
				error(ESyntax, "Function %s is not dyadic", funcname(func));
			checkarray(x);
			checkarray(y);

			primfn = primdyad;
			break;
		case ITailCall:
			popcall(s, calls, m->symtab, &c, &o, &currentline, &currentinstr, &currentfunc, &locals);
			/* fall through */
		case ICall:
			/* In all valid code, this comes after a INiladic, IMonadic or IDyadic and a
			 * IFuncres which means the call
			 * has been verified, and func is a pointer to the function to call.
			 * For primitives, x and y are setup.
			 */
			if(func->code){
				if(!func->hasresult){
					if(nextinstr(calls, c, o) == IPop)
						pushval(values, nil); /* fake result */
					else
						error(ESyntax, "Function %s does not produce a result", funcname(func));
				}
				pushval(values, y);
				pushval(values, x);
				pushcall(calls, func->code, &c, &o);
				pushcall(calls, func->code, &c, &o, &currentline, &currentinstr, &currentfunc, locals);
				currentfunc = func;
			}else{
				z = primdyad(func->prim, x, y);
				z = primfn(func->prim, x, y);
				pushval(values, z);
			}
			locals = nil;
			break;
		case IParse:
			/* parse at runtime and emit code */
			o += getuvlong(c->instrs+o, &v);
			{
				TokenList *t = (TokenList *)v;
				o += getuvlong(c->instrs+o, &v);
				Ast *a = parse(t, m->symtab);
				newcode = alloc(DataByteCode);
				codegensub(s, m, newcode, a);
				newcode->module = c->module;
				int line = 0;
				codegensub(s, newcode, a, v, &line);
				emitbyte(newcode, IReturn);
				pushcall(calls, newcode, &c, &o);
				optimisebc(newcode, nil, nil);
				pushcall(calls, newcode, &c, &o, &currentline, &currentinstr, &currentfunc, nil);
				currentfunc = nil;
			}
			break;
		case IReturn:
			popcall(calls, m->symtab, &c, &o);
			values->shy = 0;
			popcall(s, calls, m->symtab, &c, &o, &currentline, &currentinstr, &currentfunc, nil);
			break;
		case IAssign:
			o += getuvlong(c->instrs+o, &v);
			{
				void *val = popval(values);
				symset(m->symtab, v, val);

				if(nextinstr(calls, c, o) == IPop)
					val = nil;
				pushval(values, val);
				values->shy = 1;
			}
			break;
		case ILocal:


@@ 414,17 449,41 @@ evalbc(Session *s, Module *m, ByteCode *c)
			pushlocal(calls, m->symtab, v);
			break;
		case IPop:
			r = popval(values);
			if(nextinstr(calls, c, o) == IDisplay && r != nil)
				appendlog(s, printval(r));
			popval(values);
			break;
		case IDisplay:
			/* nothing to do, IPop checks for it */
			{
				int shy = values->shy;
				r = popval(values);
				if(r != nil && !shy){
					appendlog(s, printval(r));
					appendlog(s, "\n");
				}
			}
			break;
		case IPushVar:
			o += getuvlong(c->instrs+o, &v);
			pushval(values, allocvar(symname(m->symtab, v)));
			break;
		case IFuncres:
			o += getuvlong(c->instrs+o, &v);
			if(func->code){
				if(!func->hasresult){
					if(v)
						error(ESyntax, "Function %s does not produce a result", funcname(func));
					pushval(values, nil); /* fake result */
				}
				if(func->valence & Dyadic){
					pushval(values, y);
					pushval(values, x);
				}else if(func->valence & Monadic)
					pushval(values, y);
			}
			break;
		case ILine:
			o += getuvlong(c->instrs+o, &v);
			currentline = v;
			break;
		default:
			error(EInternal, "unknown instruction in evalbc: %d", instr);
		}


@@ 440,3 499,38 @@ evalbc(Session *s, Module *m, ByteCode *c)
		r = popval(values);
	return r;
}

int
instrsize(u8int instr)
{
	int size = 1;

	switch(instr){
	case IParse:
		size += 2 * sizeof(uvlong);
		break;
	case IPushConst:
	case IPushPrim:
	case ILookup:
	case IStrand:
	case IAssign:
	case ILocal:
	case IPushVar:
	case IFuncres:
	case ILine:
		size += sizeof(uvlong);
		break;
	case INiladic:
	case IMonadic:
	case IDyadic:
	case ICall:
	case ITailCall:
	case IReturn:
	case IPop:
	case IDisplay:
		break;
	default:
		error(EInternal, "unknown instruction in instrsize: %d", instr);
	}
	return size;
}
\ No newline at end of file

M fns.h => fns.h +12 -6
@@ 36,10 36,11 @@ _Noreturn void error(int, char *, ...);

/* eval.c */
void *eval(Session *s, Ast *);
void emitbyte(ByteCode *, u8int);
int instrsize(u8int);

/* fs.c */
Qid freshobjqid(void);
void startfs(char *, char *);

/* memory.c */
void *alloc(int);


@@ 54,17 55,20 @@ Enumeration *enummodules(Session *s);
/* parse.c */
Ast *parse(TokenList *, Symtab *);

/* optimise.c */
void optimisebc(ByteCode *, Ast *, Symtab *);

/* prim.c */
char *primsymb(int);
int primclass(int);
int primvalence(int);
int primid(char *);
Array *primnilad(int);
Array *primmonad(int, Array *);
Array *primnilad(int, Array *, Array *);
Array *primmonad(int, Array *, Array *);
Array *primdyad(int, Array *, Array *);

/* scan.c */
TokenList *scan(char *);
TokenList *scan(char *, int);
char *printtok(Token);

/* session.c */


@@ 74,7 78,7 @@ Enumeration *enumsessions(void);
void appendlog(Session *s, char *data);

/* symtab.c */
Symtab *allocsymtab(void);
Symtab *allocsymtab(Module *);
uvlong sym(Symtab *, char *);
char *symname(Symtab *, uvlong);
void *symval(Symtab *, uvlong);


@@ 90,9 94,11 @@ void systemcmd(Session *, char *, int);
Enumeration *allocenum(uvlong);
void trim(char *);
void debugast(Ast *, int);
void debugbc(ByteCode *);
char *printbc(ByteCode *);
int getuvlong(u8int *, uvlong *);
char *funcname(Function *);
void plumbedit(void *, char *, Session *, int, int);
void plumbinfo(Session *, char *);

/* value.c */
char *printval(void *);

M fs.c => fs.c +92 -5
@@ 30,7 30,8 @@ enum {
			Qmodules,
				Qmodule,
			Qthreads,
	Qlpaobj
	Qlpaobj,
	Qbytecode,
};

enum {


@@ 85,6 86,7 @@ mkqid(int type, uvlong id)
	case Qcons:
	case Qlog:
	case Qlpaobj:
	case Qbytecode:
		qid.type = QTFILE;
		break;
	}


@@ 261,6 263,27 @@ sessionctl(Req *r)
}

static char *
bytecode(Req *r)
{
	Aux *aux = r->fid->aux;
	Symbol *symb = aux->symbol;

	/* Pretty print the value and readstr() it. */
	if(aux->cachestr == nil){
		if(getalloctag(symb->value) != DataFunction)
			return Eexist;
		void *val = ((Function *)symb->value)->code;
		aux->cachestr = printval(val);
	}
	readstr(r, aux->cachestr);
	if(r->ofcall.count == 0){
		free(aux->cachestr);
		aux->cachestr = nil;
	}
	return nil;
}

static char *
symbolrw(Req *r)
{
	Aux *aux = r->fid->aux;


@@ 277,8 300,11 @@ symbolrw(Req *r)
			aux->cachestr = nil;
		}
	}else{ /* Twrite */
		if(trap(EAny))
			return errmsg();
		if(trap(EAny)){
			char *msg = errmsg();
			plumbinfo(session, msg);
			return msg;
		}

		char *buf = requeststr(r);
		void *v = parseval(session, buf);


@@ 373,7 399,7 @@ fswalk1(Fid *fid, char *name, Qid *qid)
		break;
	case Qmodule:
		if(strcmp(name, "..") == 0)
			*qid = m->qsession;
			*qid = m->session->qsession;
		else{
			int found = 0;
			e = enumsymbols(m->symtab, 1);


@@ 384,6 410,16 @@ fswalk1(Fid *fid, char *name, Qid *qid)
					aux->symbol = symb;
					found = 1;
				}

				if(symb->value != nil && getalloctag(symb->value) == DataFunction){
					char *bcname = smprint("%s.bc", symb->name);
					if(strcmp(name, bcname) == 0){
						*qid = mkqid(Qbytecode, QID_PATH(symb->qsymbol));
						aux->symbol = symb;
						found = 1;
					}
					free(bcname);
				}
			}
			if(!found)
				err = Eexist;


@@ 438,7 474,6 @@ fsopen(Req *r)
		s->qmodules = mkqid(Qmodules, s->id);
		s->qthreads = mkqid(Qthreads, s->id);

		m->qsession = s->qsession;
		m->qmodule = mkqid(Qmodule, m->id);
	}



@@ 486,6 521,19 @@ fsstat(Req *r)
		mkfilestat(&r->d, symb->name, symb->qsymbol, 0444);
		r->d.length = strlen(printval(symb->value));
		break;
	case Qbytecode:
		{
			void *val = symb->value;
			if(val == nil || getalloctag(val) != DataFunction){
				err = Eexist;
				break;
			}

			char *name = smprint("%s.bc", symb->name);
			mkfilestat(&r->d, name, mkqid(Qbytecode, QID_PATH(symb->qsymbol)), 0444);
			free(name);
		}
		break;
	default:
		err = Enotyet;
	}


@@ 530,6 578,9 @@ fsread(Req *r)
	case Qlpaobj:
		err = symbolrw(r);
		break;
	case Qbytecode:
		err = bytecode(r);
		break;
	default:
		err = Enotyet;
		break;


@@ 641,3 692,39 @@ startfs(char *name, char *mtpt)

	threadpostmountsrv(&fs, name, mtpt, MREPL);
}

void
usage(void)
{
	fprint(2, "usage: lpa/fs [-D] [-n name] [-m mtpt] \n");
	exits("usage");
}

void
threadmain(int argc, char *argv[])
{
	char *name = "lpa";
	char *mtpt = "/mnt/lpa";

	ARGBEGIN{
	case 'm':
		mtpt = EARGF(usage());
		break;
	case 'n':
		name = EARGF(usage());
		break;
	case 'D':
		chatty9p++;
		break;
	default:
		usage();
	}ARGEND
	if(argc != 0)
		usage();

	initarrays();
	initsessions();

	startfs(name, mtpt);
	exits(nil);
}

A ide.c => ide.c +965 -0
@@ 0,0 1,965 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <thread.h>
#include <mouse.h>
#include <keyboard.h>
#include <frame.h>
#include <plumb.h>

enum {
	Stacksize = 8*1024,
};

enum {
	MenuResize,
	MenuMaximize,
	MenuHide,
	MenuClose,
	MenuEdit,
	MenuMax,
};

enum {
	Wfunction,
	Warray,
	Wbytecode,
	Wundefined,
	Winfo,
	Wsession,
};

enum {
	Border = 2,
	Padding = 2,
	Scrollbar = 12,
};

Mousectl *mousectl;
Keyboardctl *keyboardctl;
char *typestrs[] = {
	[Wfunction] = "function",
	[Warray] = "array",
	[Wbytecode] = "bytecode",
	[Wundefined] = "undefined",
	[Winfo] = "info",
	[Wsession] = "session",
};
char *sessionid;
Channel *sessionchan;
Channel *editplumbchan;
Channel *infoplumbchan;
Image *background;

typedef struct Window Window;
struct Window
{
	Rectangle rect;
	Frame frame;
	Image *cols[NCOL];
	int type;
	Rune *title;
	Rune *text;
	Rune *extra;
	char *path;
	int hidden;
	int needsrect;

	int p0;
	int p1;
	Window *next;

	int nlines;
	int firstline;
	int firstchar;
};
Window *windows;
Window *sessionwin;
Window *infowin;
Window *focusedwin;

int nwindows;
Window **windowarray;

int 
windowcmp(void *a, void *b)
{
	Window **wa = a;
	Window **wb = b;

	int r = (*wb)->type - (*wa)->type;
	if(r == 0)
		r = runestrcmp((*wa)->title, (*wb)->title);
	return r;
}

Window *
addwindow(char *name, int type, Rectangle rect)
{
	Rune *title;
	Window *w = mallocz(sizeof(Window), 1);
	if(w == nil)
		sysfatal("malloc: %r");
	w->type = type;
	if(type == Wsession || type == Winfo)
		title = runesmprint("%s %s", typestrs[type], name);
	else
		title = runesmprint("%s [%s]", name, typestrs[type]);
	w->title = title;

	switch(type){
	case Wfunction:
		w->cols[BACK] = allocimagemix(display, DPalebluegreen, DWhite);
		w->cols[HIGH] = allocimagemix(display, DPalegreygreen, DPalegreygreen);
		w->cols[BORD] = allocimagemix(display, DPurpleblue, DPurpleblue);
		break;
	case Warray:
		w->cols[BACK] = allocimagemix(display, DPalegreen, DWhite);
		w->cols[HIGH] = allocimagemix(display, DMedgreen, DMedgreen);
		w->cols[BORD] = allocimagemix(display, DDarkgreen, DDarkgreen);
		break;
	case Wbytecode:
		w->cols[BACK] = allocimagemix(display, 0xFFAAAAFF, DWhite);
		w->cols[HIGH] = allocimagemix(display, 0xFFAAAAFF, 0xFFAAAAFF);
		w->cols[BORD] = allocimagemix(display, 0xBB5D5DFF, 0xBB5D5DFF);
		break;
	case Wundefined:
		w->cols[BACK] = allocimagemix(display, 0xEEEEEEFF, 0xEEEEEEFF);
		w->cols[HIGH] = allocimagemix(display, 0xCCCCCCFF, 0xCCCCCCFF);
		w->cols[BORD] = allocimagemix(display, 0x888888FF, 0x888888FF);
		break;
	case Winfo:
		w->cols[BACK] = allocimagemix(display, 0xEEEEEEFF, 0xEEEEEEFF);
		w->cols[HIGH] = allocimagemix(display, 0xCCCCCCFF, 0xCCCCCCFF);
		w->cols[BORD] = allocimagemix(display, 0x888888FF, 0x888888FF);
		break;
	case Wsession:
		w->cols[BACK] = allocimagemix(display, DPaleyellow, DWhite);
		w->cols[HIGH] = allocimagemix(display, DDarkyellow, DDarkyellow);
		w->cols[BORD] = allocimagemix(display, DYellowgreen, DYellowgreen);
		break;
	}
	w->cols[TEXT] = display->black;
	w->cols[HTEXT] = display->black;
	w->rect = rect;

	if(windows == nil)
		windows = w;
	else{
		Window *tmp = windows;
		while(tmp->next)
			tmp = tmp->next;
		tmp->next = w;
	}

	nwindows++;
	windowarray = realloc(windowarray, sizeof(*windowarray) * nwindows);
	if(windowarray == nil)
		sysfatal("realloc: %r");
	windowarray[nwindows-1] = w;
	qsort(windowarray, nwindows, sizeof(*windowarray), windowcmp);

	return w;
}

void
drawwin(Window *w)
{
	if(w->hidden)
		return;

	Rectangle r = w->rect;
	int fontheight = display->defaultfont->height;

	Image *bordercol;
	if(w == focusedwin)
		bordercol = w->cols[TEXT];
	else
		bordercol = w->cols[BORD];

	draw(screen, r, w->cols[BACK], nil, ZP);
	border(screen, r, Border, bordercol, ZP);
	border(screen, Rect(r.min.x, r.min.y, r.max.x, r.min.y+fontheight+(Border*2)), Border, bordercol, ZP);
	r = insetrect(r, Border);

	Rectangle titlerect = r;
	titlerect.max.y = titlerect.min.y+fontheight;
	draw(screen, titlerect, w->cols[HIGH], nil, ZP);

	r = insetrect(r, Padding);
	titlerect = insetrect(titlerect, Padding);
	_string(screen, r.min, w->cols[HTEXT], ZP, display->defaultfont, nil, w->title, runestrlen(w->title), titlerect, nil, ZP, SoverD);

	r = Rect(r.min.x, r.min.y+fontheight+Border, r.max.x, r.max.y);

	Rectangle scrollrect = insetrect(r, -Padding);
	scrollrect.max.x = scrollrect.min.x + Scrollbar;
	r.min.x += Scrollbar;

	frclear(&w->frame, 0);
	frinit(&w->frame, r, display->defaultfont, screen, w->cols);
	long offset = w->firstchar;

	Rune *strs[2] = {w->text, w->extra};
	for(int n = 0; n < nelem(strs); n++){
		if(strs[n] == nil)
			continue;

		long len = runestrlen(strs[n]);
		if(len <= offset){
			offset -= len;
			continue;
		}

		len -= offset;
		frinsert(&w->frame, strs[n]+offset, strs[n]+offset+len, w->frame.nchars);
		offset = 0;
	}
	if(w->p0 != -1){
		int p0 = w->p0 - w->firstchar;
		int p1 = w->p1 - w->firstchar;
		if(p0 >= 0 && p1 <= w->frame.nchars)
			frdrawsel(&w->frame, frptofchar(&w->frame, p0), p0, p1, 1);
	}

	/* draw the scrollbar */
	draw(screen, scrollrect, bordercol, nil, ZP);
	scrollrect.max.x--;

	if(w->nlines != 0){
		int height = Dy(scrollrect);
		scrollrect.min.y += (height * w->firstline)/w->nlines;

		int hidden = w->nlines - (w->frame.maxlines+w->firstline);
		if(hidden < 0)
			hidden = 0;
		scrollrect.max.y -= (height * hidden)/w->nlines;

		if(scrollrect.min.y >= scrollrect.max.y)
			scrollrect.min.y -= Border;
	}
	draw(screen, scrollrect, w->cols[BACK], nil, ZP);
}

void
redraw(void)
{
	if(background == nil)
		background = allocimagemix(display, 0x00807FFF, 0x00807FFF);
	draw(screen, screen->r, background, nil, ZP);
	for(Window *w = windows; w != nil; w = w->next)
		drawwin(w);
	flushimage(display, 1);
}

int
movept(int oldstart, int oldend, int newstart, int newend, int n)
{
	int oldwidth = oldend-oldstart;
	int newwidth = newend-newstart;

	return ((newwidth*(n-oldstart))/oldwidth)+newstart;
}

void
eresize(int new)
{
	Rectangle oldr, newr;
	oldr = screen->r;
	if(new && getwindow(display, Refnone) < 0)
		sysfatal("getwindow: %r");
	newr = screen->r;
	for(Window *w = windows; w != nil; w = w->next){
		w->rect.min.x = movept(oldr.min.x, oldr.max.x, newr.min.x, newr.max.x, w->rect.min.x);
		w->rect.max.x = movept(oldr.min.x, oldr.max.x, newr.min.x, newr.max.x, w->rect.max.x);
		w->rect.min.y = movept(oldr.min.y, oldr.max.y, newr.min.y, newr.max.y, w->rect.min.y);
		w->rect.max.y = movept(oldr.min.y, oldr.max.y, newr.min.y, newr.max.y, w->rect.max.y);
	}
	redraw();
}

void
resizewin(Window *w)
{
	Rectangle r = getrect(3, mousectl);
	rectclip(&r, screen->r);
	if(Dy(r) > 20 && Dx(r) > 20){ /* arbitrary limit */
		w->rect = r;
		w->hidden = 0;
		w->needsrect = 0;
	}else{
		w->hidden = 1;
		w->needsrect = 1;
	}
}

void
focuswin(Window *w)
{
	if(w)
		focusedwin = w;
	else{
		focusedwin = nil;
		goto end;
	}

	if(w->needsrect){
		resizewin(w);
		if(w->needsrect)
			goto end;
	}
	w->hidden = 0;

	Window *tmp;
	for(tmp = windows; tmp != nil; tmp = tmp->next){
		if(tmp->next == w){
			tmp->next = w->next;
			w->next = nil;
			break;
		}
	}
	if(w == windows){
		if(windows->next == nil)
			goto end;

		windows = windows->next;
		w->next = nil;
	}

	tmp = windows;
	while(tmp->next)
		tmp = tmp->next;
	tmp->next = w;
end:
	redraw();
}

void
removewindow(Window *w)
{
	if(w->type == Wsession || w->type == Winfo)
		return;

	/* TODO: cleanup stuff */
	if(w == windows)
		windows = windows->next;

	for(Window *t = windows; t != nil && t->next != nil; t = t->next){
		if(t->next == w)
			t->next = w->next;
	}

	for(int i = 0; i < nwindows; i++){
		if(windowarray[i] != w)
			continue;
		nwindows--;
		memmove(&windowarray[i], &windowarray[i+1], sizeof(*windowarray) * (nwindows - i));
		break;
	}
}

Window *
findwindow(char *path, int type, char *name)
{
	for(Window *w = windows; w != nil; w = w->next){
		if(w->path && strcmp(w->path, path) == 0){
			if(w->type != type){
				Window *old = w;
				w = addwindow(name, type, old->rect);
				w->path = old->path;
				w->hidden = old->hidden;
				w->needsrect = old->needsrect;

				if(focusedwin == old)
					focusedwin = w;

				removewindow(old);
			}
			return w;
		}
	}
	return nil;
}

void
countlines(Window *w)
{
	int offset = 0;
	w->nlines = 0;
	w->firstchar = 0;

	Rune *strs[2] = {w->text, w->extra};
	for(int n = 0; n < nelem(strs); n++){
		for(Rune *r = strs[n]; r && *r; r++){
			offset++;
			if(*r == '\n'){
				w->nlines++;
				if(w->nlines == w->firstline)
					w->firstchar = offset;
			}
		}
	}
	redraw();
}

void appendtext(Window *, Rune *);


void
fillwindow(Window *w)
{
	int fd = open(w->path, OREAD);
	if(fd < 0){
		Rune *msg = runesmprint("open: %r\n");
		appendtext(infowin, msg);
		return;
	}

	char *buf = mallocz(16*1024, 1);
	if(read(fd, buf, 16*1024) < 0)
		sysfatal("read: %r");
	if(w->text)
		free(w->text);
	w->text = runesmprint("%s", buf);
	free(buf);

	countlines(w);
}

void scroll(Window *, int);

void
scrollto(Window *w, int line)
{
	if(line < w->firstline){
		w->firstline = line;
		countlines(w);
		return;
	}

	int overflow = (line - w->firstline) - w->frame.maxlines;
	if(overflow >= 0)
		scroll(w, overflow+1);	
}

void
appendtext(Window *w, Rune *text)
{
	if(w->text){
		Rune *tmp = runesmprint("%S%S", w->text, text);
		free(w->text);
		free(text);
		w->text = tmp;
	}else
		w->text = text;
	w->p0 = w->p1 = -1;
	countlines(w);
	scrollto(w, w->nlines);
}

void
inputwin(Window *w, Rune k)
{
	if(w != sessionwin)
		return;

	if(k == '\n'){
		char buf[1024];
		sprint(buf, "/mnt/lpa/%s/cons", sessionid);

		int fd = open(buf, OWRITE);
		if(fd < 0)
			sysfatal("open: %r");
		fprint(fd, "%S\n", w->extra ? w->extra : L"");
		close(fd);
		free(w->extra);
		w->extra = nil;
	}else if(k == Kbs){
		if(w->extra && runestrlen(w->extra) > 0)
			w->extra[runestrlen(w->extra)-1] = 0;
	}else{
		w->p0 = w->p1 = -1;
		Rune *tmp = w->extra;
		if(tmp){
			w->extra = runesmprint("%S%C", tmp, k);
			free(tmp);
		}else
			w->extra = runesmprint("%C", k);
	}
	countlines(w);
	scrollto(w, w->nlines);
}

void
sessionthread(void *)
{
	char buf[1024];
	sprint(buf, "/mnt/lpa/%s/cons", sessionid);

	int fd = open(buf, OREAD);
	if(fd < 0)
		sysfatal("open: %r");

	for(;;){
		memset(buf, 0, sizeof(buf));
		if(read(fd, buf, sizeof(buf)) < 0)
			sysfatal("read: %r");
		Rune *text = runesmprint("%s", buf);
		send(sessionchan, &text);
	}
}

void
plumbports(void)
{
	int fd = open("/mnt/plumb/rules", OWRITE);
	if(fd < 0)
		sysfatal("open: %r");
	seek(fd, 0, 2);
	fprint(fd, "plumb to lpaedit\n");
	fprint(fd, "plumb to lpainfo\n");
	close(fd);
}

void
plumbthread(void *arg)
{
	Channel *c = arg;
	char *port;
	Plumbmsg *m;
	int fd;

	if(c == editplumbchan)
		port = "lpaedit";
	else
		port = "lpainfo";
	fd = plumbopen(port, OREAD);
	if(fd < 0)
		sysfatal("open: %r");
	for(;;){
		m = plumbrecv(fd);
		if(m == nil)
			sysfatal("plumbrecv: %r");
		send(c, &m);
	}
}

void
edit(Window *w)
{
	if(w->type == Wsession)
		return;

	static int fd = -1;
	if(fd < 0)
		fd = plumbopen("send", OWRITE);
	if(fd < 0)
		sysfatal("plumbopen: %r");

	if(plumbsendtext(fd, "lpadbg", "edit", "/", w->path) < 0)
		sysfatal("plumb: %r");
}

char *
menugen(int n)
{
	static char charbuf[256];
	static Rune runebuf[256];

	if(focusedwin == nil)
		n += MenuMax;

	switch(n){
	case MenuResize:
		return "Resize";
	case MenuMaximize:
		return "Maximize";
	case MenuHide:
		return "Hide";
	case MenuClose:
		return "Close";
	case MenuEdit:
		return "Edit";
	default:
		n -= MenuMax;
	}

	if(n < nwindows){
		int maxlen = 0;
		for(int i = 0; i < nwindows; i++){
			int len = runestrlen(windowarray[i]->title);
			if(len > maxlen)
				maxlen = len;
		}
		maxlen += 2; /* for status and space */
	
		Window *w = windowarray[n];
		Rune state;
		if(w == focusedwin)
			state = L'→';
		else if(w->hidden)
			state = L'⋯';
		else
			state = L' ';
		int len = runesnprint(runebuf, sizeof(runebuf), "%C %S", state, w->title);
		while(len < maxlen)
			runebuf[len++] = L' ';
		runebuf[len] = 0;
		snprint(charbuf, sizeof(charbuf), "%S", runebuf);

		return charbuf;
	}else
		return nil;
}

void
defaultwins(void)
{
	Rectangle r, info;
	r = screen->r;

	info = r;
	info.min.x += Dx(r)*0.6;
	info.min.y += Dy(r)*0.6;

	infowin = addwindow(sessionid, Winfo, info);
	infowin->hidden = 1;

	r.min.x += Dx(r)*0.05;
	r.min.y += Dy(r)*0.05;
	r.max.x -= Dx(r)*0.5;
	r.max.y -= Dy(r)*0.5;

	sessionwin = addwindow(sessionid, Wsession, r);
	focuswin(sessionwin);
}

void
movewin(Window *w)
{
	w->hidden = 1;
	redraw();
	Rectangle r = w->rect;
	do{
		Point p = mousectl->xy;
		drawgetrect(r, 1);
		readmouse(mousectl);
		drawgetrect(r, 0);
		r = rectaddpt(r, subpt(mousectl->xy, p));
	}while(mousectl->buttons&1);
	w->rect = r;
	w->hidden = 0;
	redraw();
}

void
scroll(Window *w, int n)
{
	if(w == nil)
		return;
	w->firstline += n;
	if(w->firstline < 0)
		w->firstline = 0;
	if(w->firstline > w->nlines)
		w->firstline = w->nlines;
	countlines(w);
}

void
windowselect(Window *w)
{
	w->p0 = w->p1 = -1;
	redraw();
	frselect(&w->frame, mousectl);
	w->p0 = w->frame.p0 + w->firstchar;
	w->p1 = w->frame.p1 + w->firstchar;
	redraw();
}

void
selectline(Window *w, int line)
{
	int start, end;
	start = end = -1;

	int offset = 0;
	int current = 0;

	Rune *strs[2] = {w->text, w->extra};
	for(int n = 0; n < nelem(strs); n++){
		for(Rune *r = strs[n]; r && *r; r++){
			if(start == -1 && current == line)
				start = offset;

			offset++;
			if(*r == '\n'){
				current++;
				if(start != -1){
					end = offset;
					goto done;
				}
			}
		}
	}
done:
	w->p0 = start;
	w->p1 = end;

	scrollto(w, line);

	redraw();
}

void
lpafs(void)
{
	int pid, ret;
	char *name = "/bin/lpa/fs";
	char *argv[] = {name, nil};

	pid = rfork(RFPROC);
	switch(pid){
	case 0:
		exec(name, argv);
		sysfatal("exec: %r");
	case -1:
		sysfatal("rfork: %r");
	default:
		do{
			ret = waitpid();
		}while(ret != -1 && ret != pid);
	}
}

void
usage(void)
{
	fprint(2, "usage: lpadbg [-n id]\n");
	exits("usage");
}

void
threadmain(int argc, char *argv[])
{
	enum {
		Emouse,
		Eresize,
		Ekeyboard,
		Esession,
		Eeditplumb,
		Einfoplumb,
	};
	Mouse m;
	Rune k;
	Menu menu;
	Window *w;
	Rune *text;
	Plumbmsg *p;

	Alt a[] = {
		{nil, &m, CHANRCV},
		{nil, nil, CHANRCV},
		{nil, &k, CHANRCV},
		{nil, &text, CHANRCV},
		{nil, &p, CHANRCV},
		{nil, &p, CHANRCV},
		{nil, nil, CHANEND},
	};

	ARGBEGIN{
	case 'n':
		sessionid = EARGF(usage());
		break;
	default:
		usage();
		break;
	}ARGEND;
	if(argc != 0)
		usage();

	lpafs();

	if(sessionid == nil){
		char buf[256] = {0};
		int fd = open("/mnt/lpa/new", OREAD);
		if(fd < 0)
			sysfatal("open: %r");
		if(read(fd, buf, sizeof(buf)) <= 0)
			sysfatal("read: %r");
		buf[strlen(buf)-1] = 0;
		sessionid = strdup(buf);
		close(fd);
	}

	/* make sure our plumb ports exist */
	plumbports();

	sessionchan = chancreate(sizeof(Rune *), 0);
	editplumbchan = chancreate(sizeof(Plumbmsg *), 0);
	infoplumbchan = chancreate(sizeof(Plumbmsg *), 0);
	proccreate(sessionthread, nil, Stacksize);
	proccreate(plumbthread, editplumbchan, Stacksize);
	proccreate(plumbthread, infoplumbchan, Stacksize);

	char *winname = smprint("LPA session %s", sessionid);
	if(initdraw(nil, nil, winname) < 0)
		sysfatal("initdraw: %r");
	if((mousectl = initmouse(nil, screen)) == nil)
		sysfatal("initmouse: %r");
	if((keyboardctl = initkeyboard(nil)) == nil)
		sysfatal("initkeyboard: %r");

	a[Emouse].c = mousectl->c;
	a[Eresize].c = mousectl->resizec;
	a[Ekeyboard].c = keyboardctl->c;
	a[Esession].c = sessionchan;
	a[Eeditplumb].c = editplumbchan;
	a[Einfoplumb].c = infoplumbchan;

	menu.item = nil;
	menu.gen = menugen;
	menu.lasthit = 0;

	defaultwins();
	eresize(0);

	for(;;){
		int hit, wasfocused;
		w = nil;
		switch(alt(a)){
		case Emouse:
			for(Window *tmp = windows; tmp != nil; tmp = tmp->next){
				if(!tmp->hidden && ptinrect(m.xy, tmp->rect))
					w = tmp;
			}
			wasfocused = w == focusedwin;
			if(m.buttons&7)
				focuswin(w);

			if(m.buttons&1){
				if(w){
					if(ptinrect(m.xy, w->frame.entire)){
						if(wasfocused){
							windowselect(w);
						}
					}else{
						movewin(w);
					}
				}
			}

			if(m.buttons&4){
				hit = menuhit(3, mousectl, &menu, nil);
				if(focusedwin == nil && hit != -1)
					hit += MenuMax;
				switch(hit){
				case MenuResize:
					focusedwin->hidden = 1;
					redraw();
					resizewin(focusedwin);
					redraw();
					break;
				case MenuMaximize:
					focusedwin->rect = screen->r;
					redraw();
					break;
				case MenuHide:
					focusedwin->hidden = 1;
					redraw();
					break;
				case MenuClose:
					removewindow(focusedwin);
					focuswin(nil);
					break;
				case MenuEdit:
					edit(focusedwin);
					break;
				case -1:
					break;
				default:
					hit -= MenuMax;
					w = windowarray[hit];
					focuswin(w);
					moveto(mousectl, frptofchar(&w->frame, w->p1));
					redraw();
				}
			}

			if(m.buttons&8)
				scroll(focusedwin, -1);
			if(m.buttons&16)
				scroll(focusedwin, 1);
			break;
		case Eresize:
			eresize(1);
			break;
		case Ekeyboard:
			if(focusedwin != nil)
				inputwin(focusedwin, k);
			break;
		case Esession:
			appendtext(sessionwin, text);
			redraw();
			break;
		case Eeditplumb:
			{
				char *buf = malloc(p->ndata+1);
				memcpy(buf, p->data, p->ndata);
				buf[p->ndata] = 0;
				int wintype = -1;

				char *type = plumblookup(p->attr, "type");
				char *name = plumblookup(p->attr, "name");
				char *newwin = plumblookup(p->attr, "newwin");
				char *session = plumblookup(p->attr, "session");
				char *line = plumblookup(p->attr, "line");
				int linenr = line ? atoi(line) : 0;

				if(type != nil && name != nil && newwin != nil && session != nil && strcmp(session, sessionid) == 0){
					for(int i = 0; i < Wsession; i++){
						if(strcmp(type, typestrs[i]) == 0){
							wintype = i;
							break;
						}
					}
					if(wintype != -1){
						Window *w = findwindow(buf, wintype, name);
						if(w == nil && strcmp(newwin, "yes") == 0){
							w = addwindow(name, wintype, screen->r);
							w->hidden = 1;
							w->needsrect = 1;
							w->path = strdup(buf);
							edit(w);
						}
						if(w){
							fillwindow(w);
							if(linenr == -1)
								w->p0 = w->p1;
							else if(linenr > 0 && linenr <= w->nlines)
								selectline(w, linenr-1);
						}
						redraw();
					}
				}
				free(buf);
			}
			break;
		case Einfoplumb:
			{
				char *session = plumblookup(p->attr, "session");
				if(session == nil || strcmp(session, sessionid) != 0)
					break;

				char *buf = malloc(p->ndata+1);
				memcpy(buf, p->data, p->ndata);
				buf[p->ndata] = 0;

				appendtext(infowin, runesmprint("%s\n", buf));
				if(!infowin->needsrect)
					focuswin(infowin);
				free(buf);
			}
			break;
		}
	}
}

D main.c => main.c +0 -44
@@ 1,44 0,0 @@
#include <u.h>
#include <libc.h>
#include <fcall.h>
#include <thread.h>
#include <9p.h>

#include "dat.h"
#include "fns.h"

void
usage(void)
{
	fprint(2, "usage: lpafs [-D] [-n name] [-m mtpt] \n");
	exits("usage");
}

void
threadmain(int argc, char *argv[])
{
	char *name = "lpa";
	char *mtpt = "/mnt/lpa";

	ARGBEGIN{
	case 'm':
		mtpt = EARGF(usage());
		break;
	case 'n':
		name = EARGF(usage());
		break;
	case 'D':
		chatty9p++;
		break;
	default:
		usage();
	}ARGEND
	if(argc != 0)
		usage();

	initarrays();
	initsessions();

	startfs(name, mtpt);
	exits(nil);
}
\ No newline at end of file

M mkfile => mkfile +29 -7
@@ 1,16 1,22 @@
</$objtype/mkfile

TARG=lpafs
SCRIPTS=lpa
OFILES=\
BIN=/$objtype/bin/lpa
TARG=\
	fs\
	ide\

SCRIPTS=\
	cons

FS_OFILES=\
	array.$O\
	constraint.$O\
	error.$O\
	eval.$O\
	fs.$O\
	main.$O\
	memory.$O\
	module.$O\
	optimise.$O\
	parse.$O\
	prim.$O\
	scan.$O\


@@ 20,20 26,36 @@ OFILES=\
	util.$O\
	value.$O\

IDE_OFILES=\
	ide.$O\

HFILES=\
	dat.h\
	fns.h\

BIN=/$objtype/bin
</sys/src/cmd/mkmany

$O.fs:	$FS_OFILES
	$LD $LDFLAGS -o $target $prereq

$O.ide:	$IDE_OFILES
	$LD $LDFLAGS -o $target $prereq

CLEANFILES=lpa.ps

default:V: all lpa.ps

install:
	cp $SCRIPTS /rc/bin/
	mkdir -p $BIN
	for(i in $TARG)
		mk $MKFLAGS $i.install
	for(i in $SCRIPTS)
		mk $MKFLAGS $i.rcinstall

%.rcinstall:V:
	cp $stem $BIN/$stem
	chmod +x $BIN/$stem

lpa.ps: lpa.ms
	cat lpa.ms | troff -ms | lp -dstdout > $target

</sys/src/cmd/mkone
\ No newline at end of file

M module.c => module.c +2 -1
@@ 12,8 12,9 @@ addmodule(Session *s, char *name)

	Module *m = alloc(DataModule);
	m->name = strdup(name);
	m->symtab = allocsymtab();
	m->symtab = allocsymtab(m);
	m->id = id++;
	m->session = s;

	wlock(&s->modules->lock);
	s->modules->count++;

A optimise.c => optimise.c +112 -0
@@ 0,0 1,112 @@
#include <u.h>
#include <libc.h>
#include <thread.h>

#include "dat.h"
#include "fns.h"

static int opttailcall(ByteCode *, uvlong, u8int *);
static int optlookup(ByteCode *, uvlong, u8int *);

enum {
	MaxPattern = 3,
};

struct {
	int (*fn)(ByteCode *, uvlong, u8int *);
	int count;
	u8int instrs[MaxPattern];
} patterns[] = {
	{opttailcall, 2, ICall, IReturn},
	{optlookup, 3, IAssign, IPop, ILookup},
};

void
optimisebc(ByteCode *c, Ast *func, Symtab *symtab)
{
	USED(func);
	USED(symtab);

	/* Look for patterns in the byte code and replace them with
	 * something that we believe is "better" in some way.
	 */
	u8int instrs[MaxPattern];
	uvlong start, end, size;

restart:
	memset(instrs, 0, MaxPattern);
	size = start = end = 0;
	while(end < c->count){
		if(size == MaxPattern){
			start += instrsize(c->instrs[start]);
			memmove(instrs, instrs+1, MaxPattern-1);
		}else
			size++;
		instrs[size-1] = c->instrs[end];
		end += instrsize(c->instrs[end]);

		for(int i = 0; i < nelem(patterns); i++){
			int matches = 1;
			int count = patterns[i].count;
			int offset = MaxPattern-count;
			for(int j = 0; j < count; j++)
				matches &= patterns[i].instrs[j] == instrs[j+offset];
			if(!matches)
				continue;
			uvlong matchstart = start;
			while(offset > 0){
				offset--;
				matchstart += instrsize(c->instrs[matchstart]);
			}
			int used = patterns[i].fn(c, matchstart, instrs);
			if(used)
				goto restart;
		}
	}
}

static void
instrmove(ByteCode *c, uvlong dst, uvlong src)
{
	if(dst > src)
		error(EInternal, "invalid dst and src given to instrmove: %ulld %ulld", dst, src);
	uvlong count = c->count-src;

	/* TODO: deal with jumps when they are added */
	memmove(c->instrs+dst, c->instrs+src, count);
	c->count = dst+count;
}

static int
opttailcall(ByteCode *c, uvlong start, u8int *instrs)
{
	USED(instrs);

	/* Change the ICall to ITailCall, and get rid of the return instruction */
	c->instrs[start] = ITailCall;
	instrmove(c, start+1, start+2);
	return 1;
}

static int
optlookup(ByteCode *c, uvlong start, u8int *instrs)
{
	USED(instrs);

	uvlong o = start;
	uvlong sym1, sym2;

	o += 1; /* assign */
	o += getuvlong(c->instrs+o, &sym1);
	start = o;

	o += 2; /* pop, lookup */
	o += getuvlong(c->instrs+o, &sym2);

	if(sym1 != sym2)
		return 0;

	/* Get rid of the pop and lookup instructions */
	instrmove(c, start, o);
	return 1;
}
\ No newline at end of file

M parse.c => parse.c +40 -0
@@ 66,6 66,24 @@ match(TokenList *tokens, int tag)
	tokens->offset++;
}

static int
currentline(TokenList *tokens)
{
	peek(tokens);
	return tokens->tokens[tokens->offset].line;
}

static int
lastline(TokenList *tokens)
{
	int o = tokens->offset;
	if(o > 0)
		o--;
	if(o >= tokens->count)
		error(ESyntax, "unexpected end of token stream");
	return tokens->tokens[o].line;
}

static void
addchild(Ast *ast, Ast *child)
{


@@ 159,6 177,7 @@ parseprog(TokenList *t)
{
	Ast *prog = alloc(DataAst);
	prog->tag = AstProg;
	prog->linestart = currentline(t);

	while(peek(t) != TokEnd){
		Ast *child = nil;


@@ 178,6 197,8 @@ parseprog(TokenList *t)
		if(child)
			addchild(prog, child);
	}
	prog->lineend = lastline(t);

	return prog;
}



@@ 192,6 213,7 @@ parsefuncdef(TokenList *t)
			parseseps(t, 1);
	}
	match(t, TokDel);
	func->lineend = lastline(t);

	return func;
}


@@ 201,6 223,7 @@ parsefuncheader(TokenList *t)
{
	Ast *func = alloc(DataAst);
	func->tag = AstFunc;
	func->linestart = currentline(t);

	match(t, TokDel);



@@ 243,12 266,15 @@ parselocals(TokenList *t)
{
	Ast *locals = alloc(DataAst);
	locals->tag = AstLocals;
	locals->linestart = currentline(t);
	while(peek(t) == TokSemi){
		match(t, TokSemi);
		Ast *name = parsename(t);
		name->nameclass = NameclassLocal;
		addchild(locals, name);
	}
	locals->lineend = lastline(t);

	parseseps(t, 1);
	return locals;
}


@@ 290,6 316,7 @@ parseexpr(TokenList *t, Symtab *symtab, Ast *func)
			uvlong count = end-start;
			Ast *later = alloc(DataAst);
			later->tag = AstLater;
			later->linestart = currentline(t);
			later->tokens = alloc(DataTokenList);
			later->tokens->count = count+1;
			later->tokens->tokens = allocextra(later->tokens, sizeof(Token) * later->tokens->count);


@@ 298,6 325,7 @@ parseexpr(TokenList *t, Symtab *symtab, Ast *func)
				match(t, peek(t));
			}
			later->tokens->tokens[count].tag = TokEnd;
			later->lineend = lastline(t);
			return later;
		}
	}


@@ 325,6 353,7 @@ again:
	if(peekclass(t) == NameclassFunc){
func:
		expr = alloc(DataAst);
		expr->linestart = currentline(t);
		expr->func = parsefunc(t);
		if(val == nil && (isexprsep(t) || peek(t) == TokRparen))
			expr->tag = AstNiladic;


@@ 336,6 365,7 @@ func:
				expr->tag = AstMonadic;
			expr->right = parseexprsub(t);
		}
		expr->lineend = lastline(t);
		val = expr;
		goto end;
	}


@@ 345,9 375,11 @@ func:
		if(peek(t) == TokLarrow){
			match(t, TokLarrow);
			expr = alloc(DataAst);
			expr->linestart = currentline(t);
			expr->tag = AstAssign;
			expr->left = val;
			expr->right = parseexprsub(t);
			expr->lineend = lastline(t);
			val = expr;
			goto end;
		}


@@ 370,8 402,10 @@ func:
		if(!strand){
			strand = alloc(DataAst);
			strand->tag = AstStrand;
			strand->linestart = val->linestart;
		}
		addchild(strand, val);
		strand->lineend = val->lineend;
		goto again;
	}



@@ 389,8 423,10 @@ parsename(TokenList *t)
{
	Ast *name = alloc(DataAst);
	name->tag = AstName;
	name->linestart = currentline(t);
	name->name = t->tokens[t->offset].name;
	match(t, TokName);
	name->lineend = lastline(t);
	return name;
}



@@ 402,9 438,11 @@ parsefunc(TokenList *t)
		func = parsename(t);
	else{
		func = alloc(DataAst);
		func->linestart = currentline(t);
		func->tag = AstPrim;
		func->prim = t->tokens[t->offset].prim;
		match(t, TokPrimitive);
		func->lineend = lastline(t);
	}

	return func;


@@ 414,6 452,7 @@ static Ast *
parseconst(TokenList *t)
{
	Ast *val = alloc(DataAst);
	val->linestart = currentline(t);
	val->tag = AstConst;
	vlong num, len;
	Rune *str;


@@ 440,5 479,6 @@ parseconst(TokenList *t)
		match(t, TokNumber); /* TODO: syntax error could be better here */
	}
	match(t, peek(t));
	val->lineend = lastline(t);
	return val;
}

M prim.c => prim.c +2 -2
@@ 100,7 100,7 @@ primid(char *s)
}

Array *
primnilad(int id)
primnilad(int id, Array *, Array *)
{
	Array *(*fn)(void) = primspecs[id].nilad;
	if(fn == nil)


@@ 109,7 109,7 @@ primnilad(int id)
}

Array *
primmonad(int id, Array *y)
primmonad(int id, Array *, Array *y)
{
	Array *(*fn)(Array *) = primspecs[id].monad;
	if(fn == nil)

M scan.c => scan.c +10 -9
@@ 6,7 6,7 @@
#include "fns.h"

Token *
newtok(TokenList *tokens, int tag)
newtok(TokenList *tokens, int tag, int line)
{
	Token *new;



@@ 14,12 14,13 @@ newtok(TokenList *tokens, int tag)
	tokens->tokens = allocextra(tokens, sizeof(Token) * tokens->count);
	new = tokens->tokens + (tokens->count-1);
	new->tag = tag;
	new->line = line;

	return new;
}

TokenList *
scan(char *buf)
scan(char *buf, int line)
{
	Rune r;
	int n, id;


@@ 37,19 38,19 @@ scan(char *buf)
		case L']': new = TokRbrack; break;
		case L'{': new = TokLbrace; break;
		case L'}': new = TokRbrace; break;
		case L'\n': new = TokNewline; break;
		case L'\n': new = TokNewline; line++; break;
		case L'⋄': new = TokDiamond; break;
		case L'∇': new = TokDel; break;
		case L'←': new = TokLarrow; break;
		case L';': new = TokSemi; break;
		}
		if(new != -1){
			newtok(tokens, new);
			newtok(tokens, new, line);
			goto next;
		}
		if((id = primid(cp)) != -1){
			n = strlen(primsymb(id));
			tok = newtok(tokens, TokPrimitive);
			tok = newtok(tokens, TokPrimitive, line);
			tok->prim = id;
			tok->nameclass = primclass(id);
			goto next;


@@ 60,7 61,7 @@ scan(char *buf)
			char *rest;
			vlong num = strtoll(cp, &rest, 10);
			n = rest - cp;
			tok = newtok(tokens, TokNumber);
			tok = newtok(tokens, TokNumber, line);
			tok->num = num;
			goto next;
		}


@@ 70,7 71,7 @@ scan(char *buf)
				cp += n;
				n = chartorune(&r, cp);
			}while(isalpharune(r) || isdigitrune(r));
			tok = newtok(tokens, TokName);
			tok = newtok(tokens, TokName, line);
			usize size = cp - start;
			tok->name = malloc(size + 1);
			memcpy(tok->name, start, size);


@@ 89,7 90,7 @@ scan(char *buf)
			if(r == 0)
				error(ESyntax, "unmatched '");

			tok = newtok(tokens, TokString);
			tok = newtok(tokens, TokString, line);
			usize size = utfnlen(start, cp - start) + 1;
			tok->string = malloc(sizeof(Rune) * size);
			runesnprint(tok->string, size, "%s", start);


@@ 99,7 100,7 @@ scan(char *buf)
next:
		cp += n;
	}
	newtok(tokens, TokEnd);
	newtok(tokens, TokEnd, line);
	return tokens;
}


M session.c => session.c +1 -1
@@ 51,7 51,7 @@ sessionproc(void *arg)
				continue;
			}

			TokenList *tokens = scan(buf);
			TokenList *tokens = scan(buf, 1); /* TODO: use actual line number */
			Ast *ast = parse(tokens, 0);
			eval(s, ast);
			endtrap();

M symtab.c => symtab.c +11 -6
@@ 6,9 6,10 @@
#include "fns.h"

Symtab *
allocsymtab(void)
allocsymtab(Module *m)
{
	Symtab *s = alloc(DataSymtab);
	s->module = m;
	return s;
}



@@ 74,12 75,16 @@ symptr(Symtab *s, uvlong id)
}

void
symset(Symtab *s, uvlong id, void *newval)
symset(Symtab *symtab, uvlong id, void *newval)
{
	wlock(&s->lock);
	s->symbols[id]->value = newval;
	s->symbols[id]->qsymbol.vers++;
	wunlock(&s->lock);
	wlock(&symtab->lock);
	symtab->symbols[id]->value = newval;
	symtab->symbols[id]->qsymbol.vers++;
	wunlock(&symtab->lock);

	plumbedit(newval, symname(symtab, id), symtab->module->session, 0, 0);
	if(newval && getalloctag(newval) == DataFunction)
		plumbedit(((Function*)newval)->code, symname(symtab, id), symtab->module->session, 0, 0); 
}

Enumeration *

M systemcmd.c => systemcmd.c +50 -19
@@ 6,8 6,10 @@
#include "dat.h"
#include "fns.h"

char *syscmd_off(Session *, char *);
char *syscmd_ed(Session *, char *);
static char *syscmd_off(Session *, char *);
static char *syscmd_ed(Session *, char *);
static char *syscmd_bc(Session *, char *);
static char *syscmd_trace(Session *, char *);

struct {
	char *name;


@@ 15,6 17,8 @@ struct {
} cmdtab[] = {
	{ "off", syscmd_off },
	{ "ed",	syscmd_ed },
	{ "bc", syscmd_bc },
	{ "trace", syscmd_trace },
};

void


@@ 37,9 41,14 @@ systemcmd(Session *s, char *cmd, int ctl)
		if(strcmp(cmdtab[i].name, parts[0]) == 0)
			fn = cmdtab[i].fn;

	if(fn != nil)
		out = fn(s, parts[1]);
	else
	if(fn != nil){
		if(trap(EAny))
			out = strdup(errmsg());
		else{
			out = fn(s, parts[1]);
			endtrap();
		}
	}else
		out = smprint("invalid system command: %s", parts[0]);

	if(!ctl && out){


@@ 49,7 58,7 @@ systemcmd(Session *s, char *cmd, int ctl)
	free(out);
}

char *
static char *
syscmd_off(Session *s, char *args)
{
	if(strcmp(args, "") != 0)


@@ 61,22 70,44 @@ syscmd_off(Session *s, char *args)
	return smprint("bye bye :)");
}

char *
static char *
syscmd_ed(Session *s, char *name)
{
	char *resp = nil;
	int fd = plumbopen("send", OWRITE);
	if(fd < 0)
		return smprint("plumb failed: %r");
	trim(name);

	/* create the symbol */
	sym(s->modules->modules[0]->symtab, name); /* TODO: fix this and the line below. Name and module should be parsed.. */

	char *path = smprint("/mnt/lpa/%ulld/modules/main/%s", s->id, name); 
	if(plumbsendtext(fd, "lpa", "edit", "/", path) < 0)
		resp = smprint("plumb failed: %r");
	close(fd);
	free(path);
	return resp;
	uvlong symid = sym(s->modules->modules[0]->symtab, name); /* TODO: fix this and the line below. Name and module should be parsed.. */

	void *val = symval(s->modules->modules[0]->symtab, symid);
	plumbedit(val, name, s, 1, 0);
	return nil;
}

static char *
syscmd_bc(Session *s, char *name)
{
	trim(name);

	/* create the symbol */
	uvlong symid = sym(s->modules->modules[0]->symtab, name); /* TODO: fix this and the line below. Name and module should be parsed.. */

	void *val = symval(s->modules->modules[0]->symtab, symid);
	if(val == nil || getalloctag(val) != DataFunction)
		error(EDomain, "%s is not a function", name);
	val = ((Function*)val)->code;
	plumbedit(val, name, s, 1, 0);
	return nil;
}

static char *
syscmd_trace(Session *s, char *val)
{
	trim(val);
	if(strcmp(val, "on") == 0)
		s->debugtrace = 1;
	else if(strcmp(val, "off") == 0)
		s->debugtrace = 0;
	else
		return smprint("unknown )trace mode: \"%s\"", val);
	return nil;
}
\ No newline at end of file

M util.c => util.c +175 -20
@@ 1,6 1,7 @@
#include <u.h>
#include <libc.h>
#include <thread.h>
#include <plumb.h>

#include "dat.h"
#include "fns.h"


@@ 131,73 132,155 @@ getuvlong(u8int *p, uvlong *vp)
	return size;
}

void
debugbc(ByteCode *c)
char *
printbc(ByteCode *c)
{
	uvlong o, v;
	static char buf[4096]; /* TODO: fixed size :) */

	uvlong o, v, v2;

	int nlines = 0;
	struct line {
		char *istr;
		char *a1str;
		char *a2str;
		char *comment;
	} *lines = nil;

	o = 0;
	while(o < c->count){
		int instr = c->instrs[o];
		o++;

		nlines++;
		lines = realloc(lines, sizeof(*lines) * nlines);
		struct line *l = &lines[nlines-1];
		memset(l, 0, sizeof(struct line));

		switch(instr){
		case IPushConst:
			o += getuvlong(c->instrs+o, &v);
			print("CONST %p\n", (void*)v);
			l->istr = smprint("CONST");
			l->a1str = smprint("%p", (void*)v);
			break;
		case IPushPrim:
			o += getuvlong(c->instrs+o, &v);
			print("PRIM %p\n", v);
			l->istr = smprint("PRIM");
			l->a1str = smprint("%p", (void*)v);
			l->comment = smprint("%s", primsymb(v));
			break;
		case ILookup:
			o += getuvlong(c->instrs+o, &v);
			print("LOOKUP %ulld\n", v);
			l->istr = smprint("LOOKUP");
			l->a1str = smprint("%ulld", v);
			l->comment = smprint("%s", symname(c->module->symtab, v));
			break;
		case IStrand:
			o += getuvlong(c->instrs+o, &v);
			print("STRAND %ulld\n", v);
			l->istr = smprint("STRAND");
			l->a1str = smprint("%ulld", v);
			break;
		case INiladic:
			print("NILADIC CALL\n");
			l->istr = smprint("NILADIC_CHECK");
			break;
		case IMonadic:
			print("MONADIC CALL\n");
			l->istr = smprint("MONADIC_CHECK");
			break;
		case IDyadic:
			print("DYADIC CALL\n");
			l->istr = smprint("DYADIC_CHECK");
			break;
		case ICall:
			l->istr = smprint("CALL");
			break;
		case ITailCall:
			l->istr = smprint("TAILCALL");
			break;
		case IParse:
			o += getuvlong(c->instrs+o, &v);
			print("PARSE %ulld\n", v);
			o += getuvlong(c->instrs+o, &v2);
			l->istr = smprint("PARSE");
			l->a1str = smprint("%ulld", v);
			l->a2str = smprint("%ulld", v2);
			break;
		case IReturn:
			print("RETURN\n");
			l->istr = smprint("RETURN");
			break;
		case IAssign:
			o += getuvlong(c->instrs+o, &v);
			print("ASSIGN %ulld\n", v);
			l->istr = smprint("ASSIGN");
			l->a1str = smprint("%ulld", v);
			l->comment = smprint("%s", symname(c->module->symtab, v));
			break;
		case ILocal:
			o += getuvlong(c->instrs+o, &v);
			print("LOCAL %ulld\n", v);
			l->istr = smprint("LOCAL");
			l->a1str = smprint("%ulld", v);
			l->comment = smprint("%s", symname(c->module->symtab, v));
			break;
		case IPop:
			print("POP\n");
			l->istr = smprint("POP");
			break;
		case IDisplay:
			print("DISPLAY\n");
			l->istr = smprint("DISPLAY");
			break;
		case IPushVar:
			o += getuvlong(c->instrs+o, &v);
			print("PUSHVAR %ulld\n", v);
			l->istr = smprint("PUSHVAR");
			l->a1str = smprint("%ulld", v);
			l->comment = smprint("%s", symname(c->module->symtab, v));
			break;
		case IFuncres:
			o += getuvlong(c->instrs+o, &v);
			l->istr = smprint("FUNCRES");
			l->a1str = smprint("%ulld", v);
			break;
		case ILine:
			o += getuvlong(c->instrs+o, &v);
			l->istr = smprint("LINE");
			l->a1str = smprint("%ulld", v);
			l->comment = smprint("wouldn't it be nice to have the line of code here?");
			break;
		default:
			print("???");
			return;
			error(EInternal, "unknown instruction in printbc: %d", instr);
		}
	}
	print("\n");

	int maxistr, maxa1str, maxa2str;
	maxistr = maxa1str = maxa2str = 0;

	for(int i = 0; i < nlines; i++){
		struct line *l = &lines[i];
		long len;
		if(l->istr && (len = strlen(l->istr)) > maxistr)
			maxistr = len;
		if(l->a1str && (len = strlen(l->a1str)) > maxa1str)
			maxa1str = len;
		if(l->a2str && (len = strlen(l->a2str)) > maxa2str)
			maxa2str = len;
	}

	char *fmt = smprint("%%-%ds %%-%ds%%s %%-%ds%%s%%s\n", maxistr, maxa1str, maxa2str);

	char *p = buf;
	for(int i = 0; i < nlines; i++){
		struct line *l = &lines[i];
		p += snprint(p, sizeof(buf), fmt,
			l->istr,
		    	l->a1str ? l->a1str : "",
		    	l->a2str ? "," : " ",
		    	l->a2str ? l->a2str : "",
		    	l->comment ? " ⍝ " : "",
		    	l->comment ? l->comment : ""
		);

		free(l->istr);
		free(l->a1str);
		free(l->a2str);
		free(l->comment);
	}
	free(lines);

	return buf;
}

char *


@@ 207,4 290,76 @@ funcname(Function *f)
		return f->ast->funcname->name;
	else
		return primsymb(f->prim);
}

void
plumbedit(void *val, char *name, Session *s, int newwin, int line)
{
	static int fd = -1;
	int bc = 0;

	if(fd < 0)
		fd = plumbopen("send", OWRITE);
	if(fd < 0)
		error(EInternal, "plumb failed: %r");

	char *type = "undefined";
	if(val) switch(getalloctag(val)){
	case DataFunction:
		type = "function";
		break;
	case DataArray:
		type = "array";
		break;
	case DataByteCode:
		bc = 1;
		type = "bytecode";
		break;
	}
	char *path = smprint("/mnt/lpa/%ulld/modules/main/%s%s", s->id, name, bc ? ".bc" : "");
	char *lineinfo = (line != 0) ? smprint("line=%d", line) : smprint("");
	char *attrs = smprint("type=%s name=%s session=%ulld newwin=%s %s", type, name, s->id, newwin ? "yes" : "no", lineinfo);

	Plumbmsg m;

	m.src = "lpa";
	m.dst = "lpaedit";
	m.wdir = "/";
	m.type = "text";
	m.attr = plumbunpackattr(attrs);
	m.ndata = strlen(path);
	m.data = path;

	if(plumbsend(fd, &m) < 0)
		error(EInternal, "plumb failed: %r");
	free(path);
	free(attrs);
	free(lineinfo);
}

void
plumbinfo(Session *s, char *msg)
{
	static int fd = -1;

	if(fd < 0)
		fd = plumbopen("send", OWRITE);
	if(fd < 0)
		error(EInternal, "plumb failed: %r");

	char *attrs = smprint("session=%ulld", s->id);
	Plumbmsg m;

	m.src = "lpa";
	m.dst = "lpainfo";
	m.wdir = "/";
	m.type = "text";
	m.attr = plumbunpackattr(attrs);
	m.ndata = strlen(msg);
	m.data = msg;

	if(plumbsend(fd, &m) < 0)
		error(EInternal, "plumb failed: %r");
	free(attrs);

}
\ No newline at end of file

M value.c => value.c +6 -4
@@ 17,14 17,16 @@ printval(void *v)
		tag = getalloctag(v);
		switch(tag){
		case DataArray:
			return smprint("%s\n", printarray(v));
			return smprint("%s", printarray(v));
		case DataFunction:
			return smprint("%s\n", printfunc(v));
			return smprint("%s", printfunc(v));
		case DataByteCode:
			return smprint("%s", printbc(v));
		default:
			return smprint("some value of type %d\n", tag);
		}
	}else
		return smprint("no value :(\n");
		return smprint("");
}

void *


@@ 33,7 35,7 @@ parseval(Session *s, char *buf)
	Ast *ast;
	void *val;

	TokenList *tokens = scan(buf);
	TokenList *tokens = scan(buf, 1);
	ast = parse(tokens, nil);
	
	if(!(ast->tag == AstProg && ast->childcount == 1))