ref: d916a4c3823f55227ffae35738c2497256e307b5
dir: /appl/alphabet/alphabet.shmod.b/
implement Alphabetsh, Shellbuiltin;
include "sys.m";
sys: Sys;
include "draw.m";
include "sh.m";
sh: Sh;
Context, Listnode: import sh;
n_WORD: import sh;
include "alphabet/reports.m";
reports: Reports;
report, Report: import reports;
include "readdir.m";
readdir: Readdir;
include "alphabet.m";
alphabet: Alphabet;
Value, CHECK, ONDEMAND: import alphabet;
include "alphabet/abc.m";
Alphabetsh: module {};
myself: Shellbuiltin;
initbuiltin(ctxt: ref Sh->Context, shmod: Sh): string
{
sys = load Sys Sys->PATH;
myself = load Shellbuiltin "$self";
sh = shmod;
if (myself == nil)
ctxt.fail("bad module", sys->sprint("file2chan: cannot load self: %r"));
alphabet = load Alphabet Alphabet->PATH;
if(alphabet == nil)
ctxt.fail("bad module", sys->sprint("alphabet: cannot load %q: %r", Alphabet->PATH));
reports = load Reports Reports->PATH;
if(reports == nil)
ctxt.fail("bad module", sys->sprint("alphabet: cannot load %q: %r", Reports->PATH));
readdir = load Readdir Readdir->PATH;
if(readdir == nil)
ctxt.fail("bad module", sys->sprint("alphabet: cannot load %q: %r", Readdir->PATH));
alphabet->init();
alphabet->setautodeclare(1);
if((decls := ctxt.get("autodeclares")) != nil){
for(; decls != nil; decls = tl decls){
d := hd decls;
if(d.cmd == nil){
err: string;
(d.cmd, err) = sh->parse(d.word);
if(err != nil){
sys->fprint(sys->fildes(2), "alphabet: warning: bad autodeclaration: %s\n", err);
continue;
}
}
{
declares(ctxt, nil::d::nil);
}exception{
"fail:*" =>
;
}
}
}
ctxt.addbuiltin("declare", myself);
ctxt.addbuiltin("declares", myself);
ctxt.addbuiltin("undeclare", myself);
ctxt.addbuiltin("define", myself);
ctxt.addbuiltin("import", myself);
ctxt.addbuiltin("autodeclare", myself);
ctxt.addbuiltin("type", myself);
ctxt.addbuiltin("typeset", myself);
ctxt.addbuiltin("autoconvert", myself);
ctxt.addbuiltin("-", myself);
ctxt.addbuiltin("info", myself);
ctxt.addbuiltin("clear", myself);
# ctxt.addsbuiltin("-", myself);
ctxt.addsbuiltin("rewrite", myself);
ctxt.addsbuiltin("modules", myself);
ctxt.addsbuiltin("types", myself);
ctxt.addsbuiltin("usage", myself);
return nil;
}
runbuiltin(c: ref Sh->Context, nil: Sh,
cmd: list of ref Listnode, nil: int): string
{
case (hd cmd).word {
"declare" =>
return declare(c, cmd);
"declares" =>
return declares(c, cmd);
"undeclare" =>
return undeclare(c, cmd);
"define" =>
return define(c, cmd);
"import" =>
return importf(c, cmd);
"type" =>
return importtype(c, cmd);
"typeset" =>
return typeset(c, cmd);
"autoconvert" =>
return autoconvert(c, cmd);
"autodeclare" =>
if(len cmd != 2)
usage(c, "usage: autodeclare 0/1");
alphabet->setautodeclare(int word(hd tl cmd));
"info" =>
return info(c, cmd);
"clear" =>
a := load Alphabet Alphabet->PATH;
if(a == nil)
c.fail("bad module", sys->sprint("alphabet: cannot load %q: %r", Alphabet->PATH));
alphabet->quit();
alphabet = a;
alphabet->init();
alphabet->setautodeclare(1);
"-" =>
return eval(c, cmd);
}
return nil;
}
whatis(nil: ref Sh->Context, nil: Sh, mod: string, wtype: int): string
{
if(wtype == OTHER){
(qname, sig, def) := alphabet->getmodule(mod);
if(qname == nil)
return nil;
s := sys->sprint("declare %q %q", qname, sig);
if(def != nil){
for(i := len sig-1; i >= 0; i--){
if(sig[i] == '>'){
sig = sig[0:i-1];
break;
}
}
s += sys->sprint("; define %q {(%s); %s}", qname, sig, sh->cmd2string(def));
}
return s;
}
return nil;
}
getself(): Shellbuiltin
{
return myself;
}
runsbuiltin(ctxt: ref Context, nil: Sh,
argv: list of ref Listnode): list of ref Listnode
{
case (hd argv).word {
"rewrite" =>
return rewrite(ctxt, argv);
"modules" =>
return sh->stringlist2list(alphabet->getmodules());
"types" =>
ts := "";
if(tl argv != nil)
ts = word(hd tl argv);
r := sh->stringlist2list(alphabet->gettypes(ts));
if(r == nil)
ctxt.fail("error", sys->sprint("unknown typeset %q", ts));
return r;
"usage" =>
if(len argv != 2)
usage(ctxt, "usage qname");
(qname, u, nil) := alphabet->getmodule(word(hd tl argv));
if(qname == nil)
ctxt.fail("error", "module not declared");
return ref Listnode(nil, u) :: nil;
}
return nil;
}
usage(ctxt: ref Context, s: string)
{
ctxt.fail("usage", "usage: " + s);
}
declares(ctxt: ref Sh->Context, argv: list of ref Listnode): string
{
argv = tl argv;
if(argv == nil || (hd argv).cmd == nil)
ctxt.fail("usage", "usage: declares decls");
decls := (hd argv).cmd;
declares := load Declares Declares->PATH;
if(declares == nil)
ctxt.fail("bad module", sys->sprint("alphabet: cannot load %q: %r", Declares->PATH));
{
declares->init();
} exception e {
"fail:*" =>
ctxt.fail("declares init", e[5:]);
}
spawn printerrors(errorc := chan of string);
e := declares->declares(alphabet, decls, errorc, nil);
declares->quit();
if(e != nil)
ctxt.fail("bad declaration", sys->sprint("alphabet: declaration failed: %s", e));
return nil;
}
rewrite(ctxt: ref Sh->Context, argv: list of ref Listnode): list of ref Listnode
{
argv = tl argv;
n := len argv;
if(n != 1 && n != 2 || (hd argv).cmd == nil)
usage(ctxt, "rewrite {expr} [desttype]");
spawn printerrors(errorc := chan of string);
desttype := "";
if(n == 2)
desttype = word(hd tl argv);
(c, usage) := alphabet->rewrite((hd argv).cmd, desttype, errorc);
errorc <-= nil;
if(c == nil)
raise "fail:bad expression";
return (ref Listnode(c, nil) :: ref Listnode(nil, usage) :: nil);
}
# XXX add support for optional ONDEMAND and CHECK flags
declare(ctxt: ref Sh->Context, argv: list of ref Listnode): string
{
argv = tl argv;
n := len argv;
if(n < 1 || n > 2)
usage(ctxt, "declare qname [type]");
decltype := "";
if(n == 2)
decltype = word(hd tl argv);
e := alphabet->declare(word(hd argv), decltype, 0);
if(e != nil)
ctxt.fail("error", sys->sprint("cannot declare %s: %s", word(hd argv), e));
return nil;
}
undeclare(ctxt: ref Sh->Context, argv: list of ref Listnode): string
{
argv = tl argv;
if(argv == nil)
usage(ctxt, "undeclare name...");
for(; argv != nil; argv = tl argv){
if((e := alphabet->undeclare(word(hd argv))) != nil)
sys->fprint(sys->fildes(2), "alphabet: cannot undeclare %q: %s\n", word(hd argv), e);
}
return nil;
}
# usage define name expr
define(ctxt: ref Sh->Context, argv: list of ref Listnode): string
{
argv = tl argv;
if(len argv != 2 || (hd tl argv).cmd == nil)
usage(ctxt, "define name {expr}");
spawn printerrors(errorc := chan of string);
err := alphabet->define((hd argv).word, (hd tl argv).cmd, errorc);
errorc <-= nil;
if(err != nil)
raise "fail:bad define: "+err;
return nil;
}
importf(ctxt: ref Sh->Context, argv: list of ref Listnode): string
{
argv = tl argv;
if(argv == nil)
usage(ctxt, "import qname...");
errs := 0;
for(; argv != nil; argv = tl argv){
e := alphabet->importmodule(word(hd argv));
if(e != nil){
sys->fprint(sys->fildes(2), "alphabet: cannot import %s: %s\n", word(hd argv), e);
errs++;
}
}
if(errs)
raise "fail:import error";
return nil;
}
importtype(ctxt: ref Sh->Context, argv: list of ref Listnode): string
{
argv = tl argv;
if(argv == nil)
usage(ctxt, "type qname...");
errs := 0;
for(; argv != nil; argv = tl argv){
e := alphabet->importtype(word(hd argv));
if(e != nil){
sys->fprint(sys->fildes(2), "alphabet: cannot import type %s: %s\n", word(hd argv), e);
errs++;
}
}
if(errs)
raise "fail:type declare error";
return nil;
}
typeset(ctxt: ref Sh->Context, argv: list of ref Listnode): string
{
argv = tl argv;
if(len argv != 1)
usage(ctxt, "typeset qname");
spawn printerrors(errorc := chan of string);
e := alphabet->loadtypeset(word(hd argv), nil, errorc); # XXX errorc?
errorc <-= nil;
if(e != nil)
ctxt.fail("error", sys->sprint("cannot load typeset %q: %s", word(hd argv), e));
return nil;
}
autoconvert(ctxt: ref Sh->Context, argv: list of ref Listnode): string
{
argv = tl argv;
if(len argv != 3)
usage(ctxt, "autoconvert src dst fn");
src := word(hd argv);
dst := word(hd tl argv);
expr := (hd tl tl argv).cmd;
if(expr == nil)
expr = ref Sh->Cmd(Sh->n_WORD, nil, nil, (hd tl tl argv).word, nil);
spawn printerrors(errorc := chan of string);
e := alphabet->autoconvert(src, dst, expr, errorc);
errorc <-= nil;
if(e != nil)
ctxt.fail("error", sys->sprint("cannot autoconvert %s to %s via %s: %s",
src, dst, word(hd tl tl argv), e));
return nil;
}
info(ctxt: ref Sh->Context, argv: list of ref Listnode): string
{
first := 1;
if(tl argv != nil)
usage(ctxt, "info");
for(tsl := alphabet->gettypesets(); tsl != nil; tsl = tl tsl){
ts := hd tsl;
r := alphabet->gettypesetmodules(ts);
if(r == nil)
continue;
if(first == 0)
sys->print("\n");
sys->print("typeset %s\n", ts);
while((mod := <-r) != nil){
(qname, u, nil) := alphabet->getmodule(ts+"/"+mod);
if(qname != nil)
sys->print("%s %s\n", qname, u);
}
first = 0;
}
acl := alphabet->getautoconversions();
if(acl != nil)
sys->print("\n");
for(; acl != nil; acl = tl acl){
(src, dst, via) := hd acl;
sys->print("autoconvert %q %q %s\n", src, dst, sh->cmd2string(via));
}
return nil;
}
eval(ctxt: ref Sh->Context, argv: list of ref Listnode): string
{
argv = tl argv;
if(argv == nil || (hd argv).cmd == nil)
usage(ctxt, "- {expr} [arg...]");
c := (hd argv).cmd;
if(c == nil)
c = mkw((hd argv).word);
args: list of ref Value;
for(argv = tl argv; argv != nil; argv = tl argv){
if((hd argv).cmd != nil)
args = ref Value.Vc((hd argv).cmd) :: args;
else
args = ref Value.Vs((hd argv).word) :: args;
}
return alphabet->eval(c, ctxt.drawcontext, rev(args));
}
rev[T](x: list of T): list of T
{
l: list of T;
for(; x != nil; x = tl x)
l = hd x :: l;
return l;
}
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;
}
printerrors(c: chan of string)
{
while((s := <-c) != nil)
sys->fprint(sys->fildes(2), "e: %s\n", s);
}
mkw(w: string): ref Sh->Cmd
{
return ref Sh->Cmd(n_WORD, nil, nil, w, nil);
}