shithub: purgatorio

ref: a411870ee4640241e3c494367d922847da84f972
dir: /appl/cmd/sh/sexprs.b/

View raw version
implement Shellbuiltin;

# parse/generate sexprs.

include "sys.m";
	sys: Sys;
include "draw.m";
include "sh.m";
	sh: Sh;
	Listnode, Context: import sh;
	myself: Shellbuiltin;
include "bufio.m";
	bufio: Bufio;
	Iobuf: import bufio;
include "sexprs.m";
	sexprs: Sexprs;
	Sexp: import sexprs;

# getsexprs cmd
# islist val
# ${els se}
# ${text se}
# ${textels se}

# ${mktext val}
# ${mklist [val...]}
# ${mktextlist [val...]}

Maxerrs: con 10;

initbuiltin(ctxt: ref Context, shmod: Sh): string
{
	sys = load Sys Sys->PATH;
	sh = shmod;
	myself = load Shellbuiltin "$self";
	if (myself == nil)
		ctxt.fail("bad module", sys->sprint("sexpr: cannot load self: %r"));
	bufio = load Bufio Bufio->PATH;
	if (bufio == nil)
		ctxt.fail("bad module", sys->sprint("sexpr: cannot load: %s: %r", Bufio->PATH));
	sexprs = load Sexprs Sexprs->PATH;
	if(sexprs == nil)
		ctxt.fail("bad module", sys->sprint("sexpr: cannot load: %s: %r", Sexprs->PATH));
	sexprs->init();
	ctxt.addbuiltin("getsexprs", myself);
	ctxt.addbuiltin("islist", myself);
	ctxt.addsbuiltin("els", myself);
	ctxt.addsbuiltin("text", myself);
	ctxt.addsbuiltin("b64", myself);
	ctxt.addsbuiltin("textels", myself);
	ctxt.addsbuiltin("mktext", myself);
	ctxt.addsbuiltin("mklist", myself);
	ctxt.addsbuiltin("mktextlist", myself);

	return nil;
}

whatis(nil: ref Sh->Context, nil: Sh, nil: string, nil: int): string
{
	return nil;
}

getself(): Shellbuiltin
{
	return myself;
}

runbuiltin(c: ref Sh->Context, nil: Sh,
			cmd: list of ref Sh->Listnode, nil: int): string
{
	case (hd cmd).word {
	"getsexprs" =>
		return builtin_getsexprs(c, tl cmd);
	"islist" =>
		return builtin_islist(c, tl cmd);
	}
	return nil;
}

runsbuiltin(c: ref Sh->Context, nil: Sh,
			cmd: list of ref Sh->Listnode): list of ref Listnode
{
	case (hd cmd).word {
	"els" =>
		return sbuiltin_els(c, tl cmd);
	"text" =>
		return sbuiltin_text(c, tl cmd);
	"b64" =>
		return sbuiltin_b64(c, tl cmd);
	"textels" =>
		return sbuiltin_textels(c, tl cmd);
	"mktext" =>
		return sbuiltin_mktext(c, tl cmd);
	"mklist" =>
		return sbuiltin_mklist(c, tl cmd);
	"mktextlist" =>
		return sbuiltin_mktextlist(c, tl cmd);
	}
	return nil;
}

builtin_getsexprs(ctxt: ref Context, argv: list of ref Listnode): string
{
	n := len argv;
	if (n != 1 || !iscmd(hd argv))
		builtinusage(ctxt, "getsexprs {cmd}");
	cmd := hd argv :: ctxt.get("*");
	stdin := bufio->fopen(sys->fildes(0), Sys->OREAD);
	if (stdin == nil)
		ctxt.fail("bad input", sys->sprint("getsexprs: cannot open stdin: %r"));
	status := "";
	nerrs := 0;
	ctxt.push();
	for(;;){
		{
			for (;;) {
				(se, err) := Sexp.read(stdin);
				if(err != nil){
					sys->fprint(sys->fildes(2), "getsexprs: error on read: %s\n", err);
					if(++nerrs > Maxerrs)
						raise "fail:too many errors";
					continue;
				}
				if(se == nil)
					break;
				nerrs = 0;
				ctxt.setlocal("sexp", ref Listnode(nil, se.text()) :: nil);
				status = setstatus(ctxt, ctxt.run(cmd, 0));
			}
			ctxt.pop();
			return status;
		}exception e{
		"fail:*" =>
			ctxt.pop();
			if (loopexcept(e) == BREAK)
				return status;
			ctxt.push();
		}
	}
}

builtin_islist(ctxt: ref Context, argv: list of ref Listnode): string
{
	if(argv == nil || tl argv != nil)
		builtinusage(ctxt, "islist sexp");
	w := word(hd argv);
	if(w != nil && w[0] =='(')
		return nil;
	if(parse(ctxt, hd argv).islist())
		return nil;
	return "not a list";
}

CONTINUE, BREAK: con iota;
loopexcept(ename: string): int
{
	case ename[5:] {
	"break" =>
		return BREAK;
	"continue" =>
		return CONTINUE;
	* =>
		raise ename;
	}
	return 0;
}

iscmd(n: ref Listnode): int
{
	return n.cmd != nil || (n.word != nil && n.word[0] == '{');
}
	
builtinusage(ctxt: ref Context, s: string)
{
	ctxt.fail("usage", "usage: " + s);
}

setstatus(ctxt: ref Context, val: string): string
{
	ctxt.setlocal("status", ref Listnode(nil, val) :: nil);
	return val;
}

sbuiltin_els(ctxt: ref Context, val: list of ref Listnode): list of ref Listnode
{
	if (val == nil || tl val != nil)
		builtinusage(ctxt, "els sexp");
	r, rr: list of ref Listnode;
	for(els := parse(ctxt, hd val).els(); els != nil; els = tl els)
		r = ref Listnode(nil, (hd els).text()) :: r;
	for(; r != nil; r = tl r)
		rr = hd r :: rr;
	return rr;
}

sbuiltin_text(ctxt: ref Context, val: list of ref Listnode): list of ref Listnode
{
	if(val == nil || tl val != nil)
		builtinusage(ctxt, "text sexp");
	return ref Listnode(nil, parse(ctxt, hd val).astext()) :: nil;
}

sbuiltin_b64(ctxt: ref Context, val: list of ref Listnode): list of ref Listnode
{
	if(val == nil || tl val != nil)
		builtinusage(ctxt, "b64 sexp");
	return ref Listnode(nil, parse(ctxt, hd val).b64text()) :: nil;
}

sbuiltin_textels(ctxt: ref Context, val: list of ref Listnode): list of ref Listnode
{
	if (val == nil || tl val != nil)
		builtinusage(ctxt, "textels sexp");
	r, rr: list of ref Listnode;
	for(els := parse(ctxt, hd val).els(); els != nil; els = tl els)
		r = ref Listnode(nil, (hd els).astext()) :: r;
	for(; r != nil; r = tl r)
		rr = hd r :: rr;
	return rr;
}

sbuiltin_mktext(ctxt: ref Context, val: list of ref Listnode): list of ref Listnode
{
	if (val == nil || tl val != nil)
		builtinusage(ctxt, "mktext sexp");
	return ref Listnode(nil, (ref Sexp.String(word(hd val), nil)).text()) :: nil;
}

sbuiltin_mklist(nil: ref Context, val: list of ref Listnode): list of ref Listnode
{
	if(val == nil)
		return ref Listnode(nil, "()") :: nil;
	s := "(" + word(hd val);
	for(val = tl val; val != nil; val = tl val)
		s += " " + word(hd val);
	s[len s] = ')';
	return ref Listnode(nil, s) :: nil;
}

sbuiltin_mktextlist(nil: ref Context, val: list of ref Listnode): list of ref Listnode
{
	if(val == nil)
		return ref Listnode(nil, "()") :: nil;
	s := "(" + (ref Sexp.String(word(hd val), nil)).text();
	for(val = tl val; val != nil; val = tl val)
		s += " " + (ref Sexp.String(word(hd val), nil)).text();
	s[len s] = ')';
	return ref Listnode(nil, s) :: nil;
}

parse(ctxt: ref Context, val: ref Listnode): ref Sexp
{
	(se, rest, err) := Sexp.parse(word(val));
	if(rest != nil){
		for(i := 0; i < len rest; i++)
			if(rest[i] != ' ' && rest[i] != '\t' && rest[i] != '\n')
				ctxt.fail("bad sexp", sys->sprint("extra text found at end of s-expression %#q", word(val)));
	}
	if(err != nil)
		ctxt.fail("bad sexp", err);
	return se;
}

word(n: ref Listnode): string
{
	if (n.word != nil)
		return n.word;
	if (n.cmd != nil)
		n.word = sh->cmd2string(n.cmd);
	return n.word;
}