Charles.Forsyth20060303a828770d19 years agoHistory
648 lines · 14.0 KB · Brainfuck
RawDownload
implement Eval;include "sys.m";	sys: Sys;include "draw.m";include "sh.m";	sh: Sh;	Context: import sh;include "readdir.m";#include "env.m";#	env: Env;#include "string.m";#	str: String;include "fslib.m";	fslib: Fslib;	Report, Value, type2s: import fslib;	Fschan, Fsdata, Entrychan, Entry,	Gatechan, Gatequery, Nilentry, Option,	Quit: import Fslib; # more general:#	eval: fn[V, M](ctxt: ref Context, r: ref Report, expr: string, args:...) with {#		V =>#			typec:	fn(t: self V): int;#			cvt:		fn(t: self V, tc: int): V;#			cvt2s:	fn(t: self V): (int, string);#			cvt2v:	fn(t: self V): chan of int;#			mkstring:	fn(s: string): V;#			mkcmd:	fn(c: ref Sh->Cmd): V;#			discard:	fn(t: self V);#			type2s:	fn(c: int): string;#			loadmod:	fn(cmd: string): M;#		M =>#			types:	fn(): string;#			init:		fn();#			run:		fn(ctxt: ref Draw->Context, r: ref Report, cmd: string,#						opts: list of (int, list of V), args: list of V): V;#		}# how to call eval?# (eval with [V=>ref Value, M=>Fsmodule])(## sort out error reporting; stderr is not good.  # possible things to do:#	pipe [-1pP] [-t command] command fs -> void#		pipe all files in fs through command.#	extract [-r root] gate fs -> fs#		extract the first entry within fs which#		passes through the gate.#		if -r is specified, the entry is placed#		within the given root, and may be a file,#		otherwise files are not allowed.#	apply string fs#		for each file in fs, evaluates string as an fs expression#		(which should yield fs), and replace the file in the#		original hierarchy with the result.#		e.g.#		fs apply '{unbundle $file}' {filter {or {mode +d} *.bundle} .}#		a bit fanciful this...#	merge could take an optional boolean operator##	venti?##	Cmpgate: chan of Cmpgatequery;#	Cmpgatequery: type (Entry, Entry, chan of int);#		returns 00, 01, 10 or 11#	used by merge to decide what to do when merging#	used by write to decide what to do when writing##	cmpdate [-u] '>'#	cmpquery command Eval: module {	types: fn(): string;	init:	fn();	run: fn(ctxt: ref Draw->Context, r: ref Fslib->Report,		opts: list of Fslib->Option, args: list of ref Fslib->Value): ref Fslib->Value;	eval: fn(ctxt: ref Draw->Context, r: ref Fslib->Report,		expr: string, args: list of ref Fslib->Value, ret: int): ref Fslib->Value;}; WORD, SHCMD, VAR: con iota; Evalstate: adt {	s:	string;	spos: int;	drawctxt: ref Draw->Context;	report: ref Report;	args: array of ref Value;	verbose: int; 	expr: fn(p: self ref Evalstate): ref Value;	getc: fn(p: self ref Evalstate): int;	ungetc: fn(p: self ref Evalstate);	gettok: fn(p: self ref Evalstate): (int, string);}; ops: list of (string, Fsmodule);lock: chan of int; # to do:# - change value letters to more appropriate (e.g. fs->f, entries->e, gate->g).# - allow shell $variable expansions types(): string{	return "as-v";} badmod(p: string){	sys->fprint(sys->fildes(2), "fs: eval: cannot load %s: %r\n", p);	raise "fail:bad module";} init(){	sys = load Sys Sys->PATH;	fslib = load Fslib Fslib->PATH;	if(fslib == nil)		badmod(Fslib->PATH);	fslib->init();#	env = load Env Env->PATH;#	if(env == nil)#		badmod(Env->PATH);#	str = load String String->PATH;#	if(str == nil)#		badmod(String->PATH);	lock = chan[1] of int;} run(ctxt: ref Draw->Context, report: ref Report,			opts: list of Option, args: list of ref Value): ref Value{	return (ref Evalstate((hd args).s().i, 0, ctxt, report, nil, opts != nil)).expr();} eval(ctxt: ref Draw->Context, report: ref Report,	expr: string, args: list of ref Value, rtype: int): ref Value{	a := array[len args] of ref Value;	for(i := 0; args != nil; args = tl args)		a[i++] = hd args;	e := ref Evalstate(expr, 0, ctxt, report, a, 0);	v := e.expr();	vl: list of ref Value;	for(i = 0; i < len a; i++)		if(a[i] != nil)			vl = a[i] :: vl;	nv := cvt(e, v, rtype);	if(nv == nil){		vl = v :: vl;		sys->fprint(stderr(), "fs: eval fn: %s cannot be converted to %s\n",			type2s(v.typec()), type2s(rtype));	}	if(vl != nil)		spawn discard(nil, vl);	return nv;} tok2s(t: int, s: string): string{	case t {	WORD =>		return s;	SHCMD =>		return "@";	VAR =>		return "$" + s;	}	return sys->sprint("%c", t);} # expr: WORD exprs# exprs:#	| exprs '{' expr '}'#	| exprs WORD#	| exprs SHCMD#	| exprs VAREvalstate.expr(p: self ref Evalstate): ref Value{	args: list of ref Value;	t: int;	s: string;	{		(t, s) = p.gettok();	} exception e {	"parse error" =>		return nil;	}	if(t != WORD){		sys->fprint(stderr(), "fs: eval: syntax error (char %d), expected word, found %#q\n",				p.spos, tok2s(t, s));		return nil;	}	cmd := s;loop:	for(;;){		{			(t, s) = p.gettok();		} exception e {		"parse error" =>			spawn discard(nil, args);			return nil;		}		case t {		'{' =>			v := p.expr();			if(v == nil){				spawn discard(nil, args);				return nil;			}			args = v :: args;		'}' =>			break loop;		WORD =>			args = ref Value.S(s) :: args;		VAR =>			n := int s;			if(n < 0 || n >= len p.args){				sys->fprint(stderr(), "fs: eval: invalid arg reference $%s\n", s);				spawn discard(nil, args);				return nil;			}			if(p.args[n] == nil){				sys->fprint(stderr(), "fs: eval: cannot use $%d twice\n", n);				spawn discard(nil, args);				return nil;			}			args = p.args[n] :: args;			p.args[n] = nil;		SHCMD =>			if(sh == nil && (sh = load Sh Sh->PATH) == nil){				sys->fprint(stderr(), "fs: eval: cannot load %s: %r\n", Sh->PATH);				spawn discard(nil, args);				return nil;			}			(c, err) := sh->parse(s);			if(c == nil){				sys->fprint(stderr(), "fs: eval: cannot parse shell command @%s: %s\n", s, err);				spawn discard(nil, args);				return nil;			}			args = ref Value.C(c) :: args;		-1 =>			break loop;		* =>			spawn discard(nil, args);			sys->fprint(stderr(), "fs: eval: syntax error; unexpected token %d before char %d\n", t, p.spos);			return nil;		}	}	return runcmd(p, cmd, rev(args));} runcmd(p: ref Evalstate, cmd: string, args: list of ref Value): ref Value{	m := loadmodule(cmd);	if(m == nil){		spawn discard(nil, args);		return nil;	}	otype := m->types();	ok: int;	opts: list of Option;	(ok, opts, args) = cvtargs(p, args, cmd, otype);	if(ok == -1){		sys->fprint(stderr(), "fs: eval: usage: %s\n", fslib->cmdusage(cmd, otype));		spawn discard(opts, args);		return nil;	}	r := m->run(p.drawctxt, p.report, opts, args);	if(r == nil)		spawn discard(opts, args);	return r;} cvtargs(e: ref Evalstate, args: list of ref Value, cmd, otype: string): (int, list of Option, list of ref Value){	ok: int;	opts: list of Option;	(nil, at, t) := fslib->splittype(otype);	(ok, opts, args) = cvtopts(e, t, cmd, args);	if(ok == -1)		return (-1, opts, args);	if(len at < 1 || at[0] == '*'){		sys->fprint(stderr(), "fs: eval: invalid type descriptor %#q for %#q\n", at, cmd);		return (-1, opts, args);	}	n := len args;	if(at[len at - 1] == '*'){		tc := at[len at - 2];		at = at[0:len at - 2];		for(i := len at; i < n; i++)			at[i] = tc;	}	if(n != len at){		sys->fprint(stderr(), "fs: eval: wrong number of arguments to %#q\n", cmd);		return (-1, opts, args);	}	d: list of ref Value;	(ok, args, d) = cvtvalues(e, at, cmd, args);	if(ok == -1)		args = join(args, d);	return (ok, opts, args);} cvtvalues(e: ref Evalstate, t: string, cmd: string, args: list of ref Value): (int, list of ref Value, list of ref Value){	cargs: list of ref Value;	for(i := 0; i < len t; i++){		tc := t[i];		if(args == nil){			sys->fprint(stderr(), "fs: eval: %q missing argument of type %s\n", cmd, type2s(tc));			return (-1, cargs, args);		}		v := cvt(e, hd args, tc);		if(v == nil){			sys->fprint(stderr(), "fs: eval: %q: %s cannot be converted to %s\n",				cmd, type2s((hd args).typec()), type2s(tc));			return (-1, cargs, args);		}		cargs = v :: cargs;		args = tl args;	}	return (0, rev(cargs), args);} cvtopts(e: ref Evalstate, opttype: string, cmd: string, args: list of ref Value): (int, list of Option, list of ref Value){	if(opttype == nil)		return (0, nil, args);	opts: list of Option;getopts:	while(args != nil){		s := "";		pick v := hd args {		S =>			s = v.i;			if(s == nil || s[0] != '-' || len s == 1)				s = nil;			else if(s == "--"){				args = tl args;				s = nil;			}		}		if(s == nil)			return (0, opts, args);		s = s[1:];		while(len s > 0){			opt := s[0];			if(((ok, t) := fslib->opttypes(opt, opttype)).t0 == -1){				sys->fprint(stderr(), "fs: eval: %s: unknown option -%c\n", cmd, opt);				return (-1, opts, args);			}			if(t == nil){				s = s[1:];				opts = (opt, nil) :: opts;			}else{				if(len s > 1)					args = ref Value.S(s[1:]) :: tl args;				else					args = tl args;				vl: list of ref Value;				(ok, vl, args) = cvtvalues(e, t, cmd, args);				if(ok == -1)					return (-1, opts, join(vl, args));				opts = (opt, vl) :: opts;				continue getopts;			}		}		args = tl args;	}	return (0, opts, args);} discard(ol: list of (int, list of ref Value), vl: list of ref Value){	for(; ol != nil; ol = tl ol)		for(ovl := (hd ol).t1; ovl != nil; ovl = tl ovl)			vl = (hd ovl) :: vl;	for(; vl != nil; vl = tl vl)		(hd vl).discard();} loadmodule(cmd: string): Fsmodule{	lock <-= 0;	for(ol := ops; ol != nil; ol = tl ol)		if((hd ol).t0 == cmd)			break;	if(ol != nil){		<-lock;		return (hd ol).t1;	}	p := cmd + ".dis";	if(p[0] != '/' && !(p[0] == '.' && p[1] == '/'))		p = "/dis/fs/" + p;	m := load Fsmodule p;	if(m == nil){		sys->fprint(stderr(), "fs: eval: cannot load %s: %r\n", p);		sys->fprint(stderr(), "fs: eval: unknown verb %#q\n", cmd);		sys->werrstr(sys->sprint("cannot load module %q", cmd));		<-lock;		return nil;	}	{		m->init();	} exception e {	"fail:*" =>		<-lock;		sys->werrstr(sys->sprint("module init failed: %s", e[5:]));		return nil;	}	ops = (cmd, m) :: ops;	<-lock;	return m;} runexternal(p: ref Evalstate, cmd: string, t: string, opts: list of Option, args: list of ref Value): ref Value{	m := loadmodule(cmd);	if(m == nil)		return nil;	if(!fslib->typecompat(t, m->types())){		sys->fprint(stderr(), "fs: eval: %s has incompatible type\n", cmd);		sys->fprint(stderr(), "fs: eval: expected usage: %s\n", fslib->cmdusage(cmd, t));		sys->fprint(stderr(), "fs: eval: actually usage: %s\n", fslib->cmdusage(cmd, m->types()));		return nil;	}	return m->run(p.drawctxt, p.report, opts, args);} cvt(e: ref Evalstate, v: ref Value, t: int): ref Value{	{		return cvt1(e, v, t);	} exception {	"type conversion" =>		return nil;	}} cvt1(e: ref Evalstate, v: ref Value, t: int): ref Value{	if(v.typec() == t)		return v;	r: ref Value;	case t {	't' =>		r = runexternal(e, "entries", "tx", nil, cvt1(e, v, 'x') :: nil);	'x' =>		r = runexternal(e, "walk", "xs", nil, cvt1(e, v, 's') :: nil);	'p' =>		r = runexternal(e, "match", "ps", nil, cvt1(e, v, 's') :: nil);	's' =>		r = runexternal(e, "run", "sc", nil, cvt1(e, v, 'c') :: nil);	'v' =>		r = runexternal(e, "print", "vt", nil, cvt1(e, v, 't') :: nil);	}	if(r == nil)		raise "type conversion";	return r;} Evalstate.getc(p: self ref Evalstate): int{	c := -1;	if(p.spos < len p.s)		c = p.s[p.spos];	p.spos++;	return c;} Evalstate.ungetc(p: self ref Evalstate){	p.spos--;} # XXX backslash escapes newline?Evalstate.gettok(p: self ref Evalstate): (int, string){	while ((c := p.getc()) == ' ' || c == '\t')		;	t: int;	s: string; 	case c {	-1 =>		t = -1;	'\n' =>		t = '\n';	'{' =>		t = '{';	'}' =>		t = '}';	'@' =>		# embedded shell command		while((nc := p.getc()) == ' ' || nc == '\t')			;		if(nc != '{'){

Lines 1–500 of 648.