ref: 76f5318cbd58fbe2bfc8cb51856bfa217c673bb7
dir: /appl/alphabet/alphabet.b/
implement Alphabet, Copy; include "sys.m"; sys: Sys; include "draw.m"; include "readdir.m"; include "sh.m"; sh: Sh; n_BLOCK, n_SEQ, n_LIST, n_ADJ, n_WORD, n_VAR, n_BQ2, n_PIPE: import Sh; include "sets.m"; sets: Sets; Set: import sets; include "alphabet/reports.m"; reports: Reports; Report: import reports; Modulecmd, Typescmd: import Proxy; include "alphabet.m"; evalmod: Eval; Context: import evalmod; Mainsubtypes: module { proxy: fn(): chan of ref Proxy->Typescmd[ref Alphabet->Value]; }; # to do: # - sort out concurrent access to alphabet. # - if multiple options are given where only one is expected, # most modules ignore some values, where they should # discard them correctly. this could cause a malicious user # to hang up an alphabet expression (waiting for report to end) # - proper implementation of endpointsrv: # - resilience to failures # - security of endpoints # - no need for write(0)... (or maybe there is) # - proper implementation of rexecsrv: # - should be aware of user Debug: con 0; autodeclare := 0; Module: adt { modname: string; # used when loading on demand. typeset: ref Typeset; sig: string; c: chan of ref Modulecmd[ref Value]; m: Mainmodule; def: ref Sh->Cmd; defmods: ref Strhash[cyclic ref Module]; refcount: int; find: fn(ctxt: ref Evalctxt, s: string): (ref Module, string); typesig: fn(m: self ref Module): string; run: fn(m: self ref Module, ctxt: ref Evalctxt, errorc: chan of string, opts: list of (int, list of ref Value), args: list of ref Value): ref Value; typename2c: fn(s: string): int; mks: fn(ctxt: ref Evalctxt, s: string): ref Value; mkc: fn(ctxt: ref Evalctxt, c: ref Sh->Cmd): ref Value; ensureloaded: fn(m: self ref Module): string; cvt: fn(ctxt: ref Evalctxt, v: ref Value, tc: int, errorc: chan of string): ref Value; }; Evalctxt: adt { modules: ref Strhash[ref Module]; drawctxt: ref Draw->Context; report: ref Report; # stopc: chan of int; }; # used for rewriting expressions. Rvalue: adt { i: ref Sh->Cmd; tc: int; refcount: int; opts: list of (int, list of ref Rvalue); args: list of ref Rvalue; dup: fn(t: self ref Rvalue): ref Rvalue; free: fn(v: self ref Rvalue, used: int); isstring: fn(v: self ref Rvalue): int; gets: fn(t: self ref Rvalue): string; type2s: fn(tc: int): string; typec: fn(t: self ref Rvalue): int; }; Rmodule: adt { m: ref Module; cvt: fn(ctxt: ref Revalctxt, v: ref Rvalue, tc: int, errorc: chan of string): ref Rvalue; find: fn(nil: ref Revalctxt, s: string): (ref Rmodule, string); typesig: fn(m: self ref Rmodule): string; run: fn(m: self ref Rmodule, ctxt: ref Revalctxt, errorc: chan of string, opts: list of (int, list of ref Rvalue), args: list of ref Rvalue): ref Rvalue; mks: fn(ctxt: ref Revalctxt, s: string): ref Rvalue; mkc: fn(ctxt: ref Revalctxt, c: ref Sh->Cmd): ref Rvalue; typename2c: fn(s: string): int; }; Revalctxt: adt { modules: ref Strhash[ref Module]; used: ref Strhash[ref Module]; defs: int; vals: list of ref Rvalue; }; Renv: adt { items: list of ref Rvalue; n: int; }; Typeset: adt { name: string; c: chan of ref Typescmd[ref Value]; types: ref Table[cyclic ref Type]; # indexed by external type character parent: ref Typeset; gettype: fn(ts: self ref Typeset, tc: int): ref Type; }; Type: adt { id: int; tc: int; transform: list of ref Transform; typeset: ref Typeset; qname: string; name: string; }; Transform: adt { dst: int; # which type we're transforming into. all: Set; # set of all types this transformation can lead to. expr: ref Sh->Cmd; # transformation operation. }; Table: adt[T] { items: array of list of (int, T); nilval: T; new: fn(nslots: int, nilval: T): ref Table[T]; add: fn(t: self ref Table, id: int, x: T): int; del: fn(t: self ref Table, id: int): int; find: fn(t: self ref Table, id: int): T; }; Strhash: adt[T] { items: array of list of (string, T); nilval: T; new: fn(nslots: int, nilval: T): ref Strhash[T]; add: fn(t: self ref Strhash, id: string, x: T); del: fn(t: self ref Strhash, id: string); find: fn(t: self ref Strhash, id: string): T; }; Copy: module { initcopy: fn( typesets: list of ref Typeset, roottypeset: ref Typeset, modules: ref Strhash[ref Module], typebyname: ref Strhash[ref Type], typebyc: ref Table[ref Type], types: array of ref Type, currtypec: int ): Alphabet; }; typesets: list of ref Typeset; roottypeset: ref Typeset; modules: ref Strhash[ref Module]; typebyname: ref Strhash[ref Type]; typebyc: ref Table[ref Type]; # indexed by internal type character. types: array of ref Type; # indexed by id. currtypec := 16r25a0; # pretty graphics. checkload[T](m: T, path: string): T { if(m != nil) return m; sys->fprint(sys->fildes(2), "alphabet: cannot load %s: %r\n", path); raise "fail:bad module"; } init() { sys = load Sys Sys->PATH; sh = load Sh Sh->PATH; sets = checkload(load Sets Sets->PATH, Sets->PATH); evalmod = checkload(load Eval Eval->PATH, Eval->PATH); evalmod->init(); reports = checkload(load Reports Reports->PATH, Reports->PATH); roottypeset = ref Typeset("/", nil, Table[ref Type].new(5, nil), nil); typesets = roottypeset :: typesets; types = array[] of { ref Type(-1, 'c', nil, roottypeset, "/cmd", "cmd"), ref Type(-1, 's', nil, roottypeset, "/string", "string"), ref Type(-1, 'r', nil, roottypeset, "/status", "status"), ref Type(-1, 'f', nil, roottypeset, "/fd", "fd"), ref Type(-1, 'w', nil, roottypeset, "/wfd", "wfd"), ref Type(-1, 'd', nil, roottypeset, "/data", "data"), }; typebyname = typebyname.new(11, nil); typebyc = typebyc.new(11, nil); for(i := 0; i < len types; i++){ types[i].id = i; typebyc.add(types[i].tc, types[i]); typebyname.add(types[i].qname, types[i]); roottypeset.types.add(types[i].tc, types[i]); } # typebyc.add('a', ref Type(-1, 'a', nil, nil, "/any", "any")); # not sure about this anymore modules = modules.new(3, nil); } initcopy( xtypesets: list of ref Typeset, xroottypeset: ref Typeset, xmodules: ref Strhash[ref Module], xtypebyname: ref Strhash[ref Type], xtypebyc: ref Table[ref Type], xtypes: array of ref Type, xcurrtypec: int): Alphabet { # XXX must do copy-on-write, and refcounting on typesets. typesets = xtypesets; roottypeset = xroottypeset; modules = xmodules; typebyname = xtypebyname; typebyc = xtypebyc; types = xtypes; currtypec = xcurrtypec; return load Alphabet "$self"; } copy(): Alphabet { a := load Copy Alphabet->PATH; if(a == nil) return nil; return a->initcopy(typesets, roottypeset, modules, typebyname, typebyc, types, currtypec); } setautodeclare(x: int) { autodeclare = x; } quit() { for(ts := typesets; ts != nil; ts = tl ts) if((hd ts).c != nil) (hd ts).c <-= nil; delmods(modules); } delmods(mods: ref Strhash[ref Module]) { for(i := 0; i < len mods.items; i++){ for(l := mods.items[i]; l != nil; l = tl l){ m := (hd l).t1; if(--m.refcount == 0){ if(m.c != nil){ m.c <-= nil; m.c = nil; }else if(m.defmods != nil) delmods(m.defmods); else if(m.m != nil){ m.m->quit(); m.m = nil; } } } } } # XXX could do some more checking to see whether it looks vaguely like # a valid alphabet expression. parse(expr: string): (ref Sh->Cmd, string) { return sh->parse(expr); } eval(expr: ref Sh->Cmd, drawctxt: ref Draw->Context, args: list of ref Value): string { spawn reports->reportproc(reportc := chan of string, nil, reply := chan of ref Report); r := <-reply; reply = nil; stderr := sys->fildes(2); spawn eval0(expr, "/status", drawctxt, r, reports->r.start("eval"), args, vc := chan of ref Value); reports->r.enable(); v: ref Value; wait: for(;;)alt{ v = <-vc => if(v != nil) v.r().i <-= nil; msg := <-reportc => if(msg == nil) break wait; sys->fprint(stderr, "alphabet: %s\n", msg); } # we'll always get the value before the report ends. if(v == nil) return "no value"; return <-v.r().i; } eval0(expr: ref Sh->Cmd, dsttype: string, drawctxt: ref Draw->Context, r: ref Report, errorc: chan of string, args: list of ref Value, vc: chan of ref Value) { c: Eval->Context[ref Value, ref Module, ref Evalctxt]; ctxt := ref Evalctxt(modules, drawctxt, r); tc := -1; if(dsttype != nil && (tc = Module.typename2c(dsttype)) == -1){ report(errorc, "error: unknown type "+dsttype); vc <-= nil; reports->quit(errorc); } v := c.eval(expr, ctxt, errorc, args); if(tc != -1) v = Module.cvt(ctxt, v, tc, errorc); vc <-= v; reports->quit(errorc); } define(name: string, expr: ref Sh->Cmd, errorc: chan of string): string { if(name == nil || name[0] == '/') return "bad module name"; m := modules.find(name); if(m != nil) return "module already declared"; sig: string; used: ref Strhash[ref Module]; used = used.new(11, nil); (expr, sig) = rewrite0(expr, -1, errorc, used); if(sig == nil) return "cannot rewrite"; modules.add(name, ref Module(name, roottypeset, sig, nil, nil, expr, used, 1)); return nil; } typecompat(t0, t1: string): (int, string) { m: ref Module; (sig0, err) := evalmod->usage2sig(m, t0); if(err != nil) return (0, sys->sprint("bad usage %q: %s", t0, err)); sig1: string; (sig1, err) = evalmod->usage2sig(m, t1); if(err != nil) return (0, sys->sprint("bad usage %q: %s", t1, err)); return (evalmod->typecompat(sig0, sig1), nil); } rewrite(expr: ref Sh->Cmd, dsttype: string, errorc: chan of string): (ref Sh->Cmd, string) { v: ref Value; tc := -1; if(dsttype != nil){ tc = Module.typename2c(dsttype); if(tc == -1){ report(errorc, "error: unknown type "+dsttype); return (nil, nil); } } sig: string; (expr, sig) = rewrite0(expr, tc, errorc, nil); if(sig == nil) return (nil, nil); return (expr, evalmod->cmdusage(v, sig)); } # XXX different kinds of rewrite: # could rewrite forcing all names to qualified # or just leave names as they are. # return (expr, sig). # add all modules used by the expression to mods if non-nil. rewrite0(expr: ref Sh->Cmd, tc: int, errorc: chan of string, used: ref Strhash[ref Module]): (ref Sh->Cmd, string) { m: ref Rmodule; ctxt := ref Revalctxt(modules, used, 1, nil); (sig, err) := evalmod->blocksig(m, ctxt, expr); if(sig == nil){ report(errorc, "error: cannot get expr type: "+err); return (nil, nil); } args: list of ref Rvalue; for(i := len sig - 1; i >= 1; i--) args = ref Rvalue(mk(-1, nil, nil), sig[i], 1, nil, nil) :: args; # N.Vb. cmd node is never used. c: Eval->Context[ref Rvalue, ref Rmodule, ref Revalctxt]; v := c.eval(expr, ctxt, errorc, args); if(v != nil && tc != -1) v = Rmodule.cvt(ctxt, v, tc, errorc); if(v == nil) return (nil, nil); sig[0] = v.tc; v.refcount++; expr = gen(v, ref Renv(nil, 0)); if(len sig > 1){ t := mkw(Value.type2s(sig[1])); for(i = 2; i < len sig; i++) t = mk(n_ADJ, t, mkw(Value.type2s(sig[i]))); expr = mk(n_BLOCK, mk(n_SEQ, mk(n_LIST, t, nil), expr.left), nil); } return (expr, sig); } # generate the expression that gave rise to v. # it puts in parentenv any values referred to externally. gen(v: ref Rvalue, parentenv: ref Renv): ref Sh->Cmd { v.refcount--; if(v.refcount > 0) return mk(n_VAR, mkw(string addenv(parentenv, v)), nil); c := v.i; (opts, args) := (v.opts, v.args); if(opts == nil && args == nil) return c; env := parentenv; if(genblock := needblock(v)) env = ref Renv(nil, 0); for(; opts != nil; opts = tl opts){ c = mk(n_ADJ, c, mkw(sys->sprint("-%c", (hd opts).t0))); for(a := (hd opts).t1; a != nil; a = tl a) c = mk(n_ADJ, c, gen(hd a, env)); } if(args != nil && len (hd args).i.word > 1 && (hd args).i.word[0] == '-') c = mk(n_ADJ, c, mkw("--")); # XXX potentially dodgy; some sigs don't interpret "--"? # use pipe notation when possible arg0: ref Sh->Cmd; if(args != nil){ if((arg0 = gen(hd args, env)).ntype != n_BLOCK){ c = mk(n_ADJ, c, arg0); arg0 = nil; } args = tl args; } for(; args != nil; args = tl args) c = mk(n_ADJ, c, gen(hd args, env)); if(arg0 != nil) c = mk(n_PIPE, arg0.left, c); if(genblock){ args = rev(env.items); m := mkw(Value.type2s((hd args).tc)); for(a := tl args; a != nil; a = tl a) m = mk(n_ADJ, m, mkw(Value.type2s((hd a).tc))); c = mk(n_BLOCK, mk(n_SEQ, mk(n_LIST, m, nil), c), nil); return gen(ref Rvalue(c, v.tc, 1, nil, args), parentenv); } return mk(n_BLOCK, c, nil); } addenv(env: ref Renv, v: ref Rvalue): int { for(i := env.items; i != nil; i = tl i) if(hd i == v) return len i; env.items = v :: env.items; v.refcount++; return ++env.n; } # need a new block if we have any duplicated values we can resolve locally. # i.e. for a particular value, if we're the only thing pointing to that value # and its refcount is > 1 to start with. needblock(v: ref Rvalue): int { dups := getdups(v, nil); for(d := dups; d != nil; d = tl d) --(hd d).refcount; r := 0; for(d = dups; d != nil; d = tl d) if((hd d).refcount++ == 0) r = 1; return r; } # find all values which need $ referencing (but don't go any deeper) getdups(v: ref Rvalue, onto: list of ref Rvalue): list of ref Rvalue { if(v.refcount > 1) return v :: onto; for(o := v.opts; o != nil; o = tl o) for(a := (hd o).t1; a != nil; a = tl a) onto = getdups(hd a, onto); for(a = v.args; a != nil; a = tl a) onto = getdups(hd a, onto); return onto; } loadtypeset(qname: string, c: chan of ref Typescmd[ref Value], errorc: chan of string): string { tsname := canon(qname); if(gettypeset(tsname) != nil) return nil; (parent, name) := splitqname(tsname); if((pts := gettypeset(parent)) == nil) return "parent typeset not found"; if(pts.c != nil){ if(c != nil) return "typecmd channel may only be provided for top-level typesets"; reply := chan of (chan of ref Typescmd[ref Value], string); pts.c <-= ref Typescmd[ref Value].Loadtypes(name, reply); err: string; (c, err) = <-reply; if(c == nil) return err; }else if(c == nil){ tsmod := load Mainsubtypes "/dis/alphabet/"+name+"types.dis"; if(tsmod == nil) return sys->sprint("cannot load %q: %r", name+"types.dis"); c = tsmod->proxy(); } reply := chan of string; c <-= ref Typescmd[ref Value].Alphabet(reply); a := <-reply; ts := ref Typeset(tsname, c, Table[ref Type].new(7, nil), pts); typesets = ts :: typesets; newtypes: list of ref Type; for(i := 0; i < len a; i++){ tc := a[i]; if((t := ts.parent.gettype(tc)) == nil){ t = ref Type(-1, -1, nil, ts, nil, nil); sreply := chan of string; c <-= ref Typescmd[ref Value].Type2s(tc, sreply); t.name = <-sreply; # XXX check that type name is syntactically valid. t.qname = mkqname(tsname, t.name); if(typebyname.find(t.qname) != nil) report(errorc, sys->sprint("warning: oops: typename clash on %q", t.qname)); else typebyname.add(t.qname, t); newtypes = t :: newtypes; } ts.types.add(tc, t); } id := len types; types = (array[len types + len newtypes] of ref Type)[0:] = types; for(; newtypes != nil; newtypes = tl newtypes){ types[id] = hd newtypes; typebyc.add(currtypec, hd newtypes); types[id].tc = currtypec++; types[id].id = id; id++; } return nil; } autoconvert(src, dst: string, expr: ref Sh->Cmd, errorc: chan of string): string { tdst := typebyname.find(dst); if(tdst == nil) return "unknown type " + dst; tsrc := typebyname.find(src); if(tsrc == nil) return "unknown type " + src; if(tdst.typeset != tsrc.typeset && tdst.typeset != roottypeset && tsrc.typeset != roottypeset) return "conversion between incompatible typesets"; if(expr != nil && expr.ntype == n_WORD){ # mod -> {(srctype); mod $1} expr = mk(n_BLOCK, mk(n_SEQ, mk(n_LIST, mkw(src), nil), mk(n_ADJ, mkw(expr.word), mk(n_VAR, mkw("1"), nil) ) ), nil ); } (e, sig) := rewrite0(expr, tdst.tc, errorc, nil); if(sig == nil) return "cannot rewrite transformation "+sh->cmd2string(expr); if(!evalmod->typecompat(sys->sprint("%c%c", tdst.tc, tsrc.tc), sig)) return "incompatible module type"; err := addconversion(tsrc, tdst, e); if(err != nil) return sys->sprint("bad auto-conversion %s->%s via %s: %s", tsrc.qname, tdst.qname, sh->cmd2string(expr), err); return nil; } mk(ntype: int, left, right: ref Sh->Cmd): ref Sh->Cmd { return ref Sh->Cmd(ntype, left, right, nil, nil); } mkw(w: string): ref Sh->Cmd { return ref Sh->Cmd(n_WORD, nil, nil, w, nil); } declare(qname: string, usig: string, flags: int): string { return declare0(qname, usig, flags).t1; } # declare a module. # if (flags&ONDEMAND), then we don't need to actually load # the module (although we do if (flags&CHECK) or if sig==nil, # in order to check or find out the type signature) declare0(qname: string, usig: string, flags: int): (ref Module, string) { sig, err: string; m: ref Module; if(usig != nil){ (sig, err) = evalmod->usage2sig(m, usig); if(sig == nil) return (nil, "bad type sig: " + err); } # if not a qualified name, declare it virtually if(qname != nil && qname[0] != '/'){ if(sig == nil) return (nil, "virtual module declaration must include signature"); m = ref Module(qname, nil, sig, nil, nil, nil, nil, 0); }else{ qname = canon(qname); (typeset, mod) := splitqname(qname); if((ts := gettypeset(typeset)) == nil) return (nil, "unknown typeset"); if((m = modules.find(qname)) != nil){ if(m.typeset == ts) return (m, nil); return (nil, "already imported"); } m = ref Module(mod, ts, sig, nil, nil, nil, nil, 0); if(sig == nil || (flags&CHECK) || (flags&ONDEMAND)==0){ if((e := m.ensureloaded()) != nil) return (nil, e); if(flags&ONDEMAND){ if(m.c != nil){ m.c <-= nil; m.c = nil; } m.m = nil; } } } modules.add(qname, m); m.refcount++; return (m, nil); } undeclare(name: string): string { m := modules.find(name); if(m == nil) return "module not declared"; modules.del(name); if(--m.refcount == 0){ if(m.c != nil){ m.c <-= nil; m.c = nil; }else if(m.defmods != nil){ delmods(m.defmods); } } return nil; } # get info on a module. # return (qname, usage, def) getmodule(name: string): (string, string, ref Sh->Cmd) { (qname, sig, def) := getmodule0(name); if(sig == nil) return (qname, sig, def); v: ref Value; return (qname, evalmod->cmdusage(v, sig), def); } getmodule0(name: string): (string, string, ref Sh->Cmd) { m: ref Module; if(name != nil && name[0] != '/'){ if((m = modules.find(name)) == nil) return (nil, nil, nil); # XXX could add path searching here. }else{ name = canon(name); (typeset, mod) := splitqname(name); if((m = modules.find(name)) == nil){ if(autodeclare == 0) return (nil, nil, nil); ts := gettypeset(typeset); if(ts == nil) return (nil, nil, nil); m = ref Module(mod, ts, nil, nil, nil, nil, nil, 0); if((e := m.ensureloaded()) != nil) return (nil, nil, nil); if(m.c != nil) m.c <-= nil; } } qname := m.modname; if(m.def == nil && m.typeset != nil) qname = mkqname(m.typeset.name, qname); return (qname, m.sig, m.def); } getmodules(): list of string { r: list of string; for(i := 0; i < len modules.items; i++) for(ml := modules.items[i]; ml != nil; ml = tl ml) r = (hd ml).t0 :: r; return r; } #Cmpdeclts: adt { # gt: fn(nil: self ref Cmpdeclts, d1, d2: ref Decltypeset): int #}; #Cmpdeclts.gt(nil: self ref Cmpdeclts, d1, d2: ref Decltypeset) #{ # return d1.name > d2.name; #} #Cmpstring: adt { # gt: fn(nil: self ref Cmpdeclts, d1, d2: string): int #}; #Cmpstring.gt(nil: self ref Cmpstring, d1, d2: string): int #{ # return d1 > d2; #} #Cmptype: adt { # gt: fn(nil: self ref Cmptype, d1, d2: ref Type): int #}; #Cmptype.gt(nil: self ref Cmptype, d1, d2: ref Type): int #{ # return d1.name > d2.name; #} # #getdecls(): ref Declarations #{ # cmptype: ref Cmptype; # d := ref Declarations(array[len typesets] of ref Decltypeset); # i := 0; # ta := array[len types] of ref Type; # for(tsl := typesets; tsl != nil; tsl = tl tsl){ # t := hd tsl; # ts := ref Decltypeset; # ts.name = t.name; # # # all types in the typeset, in alphabetical order. # j := 0; # for(k := 0; k < len t.types.items; k++) # for(tt := t.types.items[k]; tt != nil; tt = tl tt) # ta[j++] = hd tt; # sort(cmptype, ta[0:j]); # ts.types = array[j] of string; # for(k = 0; k < j; k++){ # ts.types[k] = ta[k].name; # ts.alphabet[k] = ta[k].tc; # } # # # all modules in the typeset # c := gettypesetmodules(ts.name); # while((m := <-c) != nil){ # # # d.types = array[len types] of string; # for(i := 0; i < len types; i++){ # d.alphabet[i] = types[i].tc; # d.types[i] = types[i].qname; # } # gettypesetmodules(tsname: string): chan of string { ts := gettypeset(tsname); if(ts == nil) return nil; r := chan of string; if(ts.c == nil) spawn mainmodules(r); else ts.c <-= ref Typescmd[ref Value].Modules(r); return r; } mainmodules(r: chan of string) { if((readdir := load Readdir Readdir->PATH) != nil){ (a, nil) := readdir->init("/dis/alphabet/main", Readdir->NAME|Readdir->COMPACT); for(i := 0; i < len a; i++){ m := a[i].name; if((a[i].mode & Sys->DMDIR) == 0 && len m > 4 && m[len m - 4:] == ".dis") r <-= m[0:len m - 4]; } } r <-= nil; } gettypes(ts: string): list of string { r: list of string; for(i := 0; i < len types; i++){ if(ts == nil) r = Value.type2s(types[i].tc) :: r; else if (types[i].typeset.name == ts) r = types[i].name :: r; } return r; } gettypesets(): list of string { r: list of string; for(t := typesets; t != nil; t = tl t) r = (hd t).name :: r; return r; } getautoconversions(): list of (string, string, ref Sh->Cmd) { cl: list of (string, string, ref Sh->Cmd); for(i := 0; i < len types; i++){ if(types[i] == nil) continue; srct := Value.type2s(types[i].tc); for(l := types[i].transform; l != nil; l = tl l) cl = (srct, Value.type2s(types[(hd l).dst].tc), (hd l).expr) :: cl; } return cl; } importmodule(qname: string): string { qname = canon(qname); (typeset, mod) := splitqname(qname); if(typeset == nil) return "unknown typeset"; if((m := modules.find(mod)) != nil){ if(m.typeset == nil) return "already defined"; if(m.typeset.name == typeset) return nil; return "already imported from "+m.typeset.name; } if((m = modules.find(qname)) == nil){ if(autodeclare == 0) return "module not declared"; err: string; (m, err) = Module.find(nil, qname); if(m == nil) return "cannot import: "+ err; modules.add(qname, m); m.refcount++; } modules.add(mod, m); return nil; } gettypeset(name: string): ref Typeset { name = canon(name); for(l := typesets; l != nil; l = tl l) if((hd l).name == name) break; if(l == nil) return nil; return hd l; } importtype(qname: string): string { qname = canon(qname); (typeset, tname) := splitqname(qname); if((ts := gettypeset(typeset)) == nil) return "unknown typeset"; t := typebyname.find(tname); if(t != nil){ if(t.typeset == ts) return nil; return "type already imported from " + t.typeset.name; } t = typebyname.find(qname); if(t == nil) return sys->sprint("%s does not hold type %s", typeset, tname); typebyname.add(tname, t); return nil; } importvalue(v: ref Value, tname: string): (ref Value, string) { if(v == nil || tagof v != tagof Value.Vz) return (v, nil); if(tname == nil || tname[0] == '/') tname = canon(tname); t := typebyname.find(tname); if(t == nil) return (nil, "no such type"); pick xv := v { Vz => if(t.typeset.types.find(xv.i.typec) != t) return (nil, "value appears to be of different type"); xv.i.typec = t.tc; } return (v, nil); } gettype(tc: int): ref Type { return typebyc.find(tc); } Typeset.gettype(ts: self ref Typeset, tc: int): ref Type { return ts.types.find(tc); } Module.find(ctxt: ref Evalctxt, name: string): (ref Module, string) { mods := modules; if(ctxt != nil) mods = ctxt.modules; m := mods.find(name); if(m == nil){ if(autodeclare == 0 || name == nil || name[0] != '/') return (nil, "module not declared"); err: string; (m, err) = declare0(name, nil, 0); if(m == nil) return (nil, err); }else if((err := m.ensureloaded()) != nil) return (nil, err); return (m, nil); } Module.ensureloaded(m: self ref Module): string { if(m.c != nil || m.m != nil || m.def != nil || m.typeset == nil) return nil; sig: string; if(m.typeset.c == nil){ p := "/dis/alphabet/main/" + m.modname + ".dis"; mod := load Mainmodule p; if(mod == nil) return sys->sprint("cannot load %q: %r", p); { mod->init(); } exception e { "fail:*" => return sys->sprint("init %q failed: %s", m.modname, e[5:]); } m.m = mod; sig = mod->typesig(); }else{ reply := chan of (chan of ref Modulecmd[ref Value], string); m.typeset.c <-= ref Typescmd[ref Value].Load(m.modname, reply); (mc, err) := <-reply; if(mc == nil) return sys->sprint("cannot load: %s", err); m.c = mc; sig = gettypesig(m); } if(m.sig == nil) m.sig = sig; else if(!evalmod->typecompat(m.sig, sig)){ v: ref Value; if(m.c != nil){ m.c <-= nil; m.c = nil; } m.m = nil; return sys->sprint("%q not compatible with %q (%q vs %q, %d)", m.modname+" "+evalmod->cmdusage(v, sig), evalmod->cmdusage(v, m.sig), m.sig, sig, m.sig==sig); } return nil; } Module.typesig(m: self ref Module): string { return m.sig; } # get the type signature of a module in its native typeset. # it's not valid to call this on defined or virtually declared modules. gettypesig(m: ref Module): string { reply := chan of string; m.c <-= ref Modulecmd[ref Value].Typesig(reply); sig := <-reply; origsig := sig; for(i := 0; i < len sig; i++){ tc := sig[i]; if(tc == '-'){ i++; continue; } if(tc != '*'){ t := m.typeset.gettype(sig[i]); if(t == nil){ sys->print("no type found for '%c' in sig %q\n", sig[i], origsig); return nil; # XXX is it alright to break here? } sig[i] = t.tc; } } return sig; } Module.run(m: self ref Module, ctxt: ref Evalctxt, errorc: chan of string, opts: list of (int, list of ref Value), args: list of ref Value): ref Value { if(m.c != nil){ reply := chan of ref Value; m.c <-= ref Modulecmd[ref Value].Run(ctxt.drawctxt, ctxt.report, errorc, opts, args, reply); if((v := <-reply) != nil){ pick xv := v { Vz => xv.i.typec = m.typeset.types.find(xv.i.typec).tc; } } return v; }else if(m.def != nil){ c: Eval->Context[ref Value, ref Module, ref Evalctxt]; return c.eval(m.def, ref Evalctxt(m.defmods, ctxt.drawctxt, ctxt.report), errorc, args); }else if(m.typeset != nil){ v := m.m->run(ctxt.drawctxt, ctxt.report, errorc, opts, args); free(opts, args, v != nil); return v; } report(errorc, "error: cannot run a virtually declared module"); return nil; } free[V](opts: list of (int, list of V), args: list of V, used: int) for{ V => free: fn(v: self V, used: int); } { for(; args != nil; args = tl args) (hd args).free(used); for(; opts != nil; opts = tl opts) for(args = (hd opts).t1; args != nil; args = tl args) (hd args).free(used); } Module.typename2c(s: string): int { if((t := typebyname.find(s)) == nil) return -1; return t.tc; } Module.cvt(ctxt: ref Evalctxt, v: ref Value, tc: int, errorc: chan of string): ref Value { if(v == nil) return nil; srctc := v.typec(); dstid := gettype(tc).id; while((vtc := v.typec()) != tc){ # XXX assumes v always returns a valid typec: might that be dangerous? for(l := gettype(vtc).transform; l != nil; l = tl l) if((hd l).all.holds(dstid)) break; if(l == nil){ report(errorc, sys->sprint("error: no way to get from %s to %s", gettype(v.typec()).qname, types[dstid].qname)); v.free(0); return nil; # should only happen the first time. } t := hd l; c: Eval->Context[ref Value, ref Module, ref Evalctxt]; nv := c.eval(t.expr, ctxt, errorc, v::nil); if(nv == nil){ report(errorc, sys->sprint("error: autoconvert %q failed", sh->cmd2string(t.expr))); return nil; } v = nv; } return v; } Module.mks(nil: ref Evalctxt, s: string): ref Value { return ref Value.Vs(s); } Module.mkc(nil: ref Evalctxt, c: ref Sh->Cmd): ref Value { return ref Value.Vc(c); } show() { for(i := 0; i < len types; i++){ if(types[i] == nil) continue; sys->print("%s =>\n", types[i].qname); for(l := types[i].transform; l != nil; l = tl l) sys->print("\t%s -> %s {%s}\n", set2s((hd l).all), types[(hd l).dst].qname, sh->cmd2string((hd l).expr)); } } set2s(set: Set): string { s := "{"; for(i := 0; i < len types; i++){ if(set.holds(i)){ if(len s > 1) s[len s] = ' '; s += types[i].qname; } } return s + "}"; } Value.dup(v: self ref Value): ref Value { if(v == nil) return nil; pick xv := v { Vr => return nil; Vd => return nil; Vf or Vw => return nil; Vz => rc := chan of ref Value; gettype(xv.i.typec).typeset.c <-= ref Typescmd[ref Value].Dup(xv, rc); nv := <-rc; if(nv == nil) return nil; if(nv == v) return v; pick nxv := nv { Vz => if(nxv.i.typec == xv.i.typec) return nxv; } sys->print("oh dear, invalid duplicated value from typeset %s\n", gettype(xv.i.typec).typeset.name); return nil; } return v; } Value.typec(v: self ref Value): int { pick xv := v { Vc => return 'c'; Vs => return 's'; Vr => return 'r'; Vf => return 'f'; Vw => return 'w'; Vd => return 'd'; Vz => return xv.i.typec; } } Value.typename(v: self ref Value): string { return Value.type2s(v.typec()); } Value.free(v: self ref Value, used: int) { if(v == nil) return; pick xv := v { Vr => if(!used) xv.i <-= "stop"; Vf or Vw=> if(!used){ <-xv.i; xv.i <-= nil; } Vd => if(!used){ alt{ xv.i.stop <-= 1 => ; * => ; } } Vz => gettype(xv.i.typec).typeset.c <-= ref Typescmd[ref Value].Free(xv, used, reply := chan of int); <-reply; } } Value.isstring(v: self ref Value): int { return tagof v == tagof Value.Vs; } Value.gets(v: self ref Value): string { return v.s().i; } Value.c(v: self ref Value): ref Value.Vc { pick xv :=v {Vc => return xv;} raise "type error"; } Value.s(v: self ref Value): ref Value.Vs { pick xv :=v {Vs => return xv;} raise "type error"; } Value.r(v: self ref Value): ref Value.Vr { pick xv :=v {Vr => return xv;} raise "type error"; } Value.f(v: self ref Value): ref Value.Vf { pick xv :=v {Vf => return xv;} raise "type error"; } Value.w(v: self ref Value): ref Value.Vw { pick xv :=v {Vw => return xv;} raise "type error"; } Value.d(v: self ref Value): ref Value.Vd { pick xv :=v {Vd => return xv;} raise "type error"; } Value.z(v: self ref Value): ref Value.Vz { pick xv :=v {Vz => return xv;} raise "type error"; } Value.type2s(tc: int): string { t := gettype(tc); if(t == nil) return "unknown"; if(typebyname.find(t.name) == t) return t.name; return t.qname; } Rmodule.find(ctxt: ref Revalctxt, s: string): (ref Rmodule, string) { m := ctxt.modules.find(s); if(m == nil){ if(autodeclare == 0 || s == nil || s[0] != '/') return (nil, "module not declared"); if(ctxt.modules != modules) return (nil, "shouldn't happen: module not found in defined block"); err: string; (m, err) = declare0(s, nil, ONDEMAND); if(m == nil) return (nil, err); } return (ref Rmodule(m), nil); } Rmodule.cvt(ctxt: ref Revalctxt, v: ref Rvalue, tc: int, errorc: chan of string): ref Rvalue { if(v == nil) return nil; srctc := v.typec(); dstid := gettype(tc).id; while((vtc := v.typec()) != tc){ # XXX assumes v always returns a valid typec: might that be dangerous? for(l := gettype(vtc).transform; l != nil; l = tl l) if((hd l).all.holds(dstid)) break; if(l == nil){ report(errorc, sys->sprint("error: no way to get from %s to %s", gettype(v.typec()).qname, types[dstid].qname)); return nil; # should only happen the first time. } t := hd l; c: Eval->Context[ref Rvalue, ref Rmodule, ref Revalctxt]; v = c.eval(t.expr, ctxt, errorc, v::nil); } return v; } Rmodule.typesig(m: self ref Rmodule): string { return m.m.sig; } Rmodule.typename2c(name: string): int { return Module.typename2c(name); } Rmodule.mks(ctxt: ref Revalctxt, s: string): ref Rvalue { v := ref Rvalue(mkw(s), 's', 0, nil, nil); ctxt.vals = v :: ctxt.vals; return v; } Rmodule.mkc(ctxt: ref Revalctxt, c: ref Sh->Cmd): ref Rvalue { v := ref Rvalue(mk(n_BQ2, c, nil), 'c', 0, nil, nil); ctxt.vals = v :: ctxt.vals; return v; } Rmodule.run(m: self ref Rmodule, ctxt: ref Revalctxt, errorc: chan of string, opts: list of (int, list of ref Rvalue), args: list of ref Rvalue): ref Rvalue { if(ctxt.defs && m.m.def != nil){ c: Eval->Context[ref Rvalue, ref Rmodule, ref Revalctxt]; nctxt := ref Revalctxt(m.m.defmods, ctxt.used, ctxt.defs, ctxt.vals); v := c.eval(m.m.def, nctxt, errorc, args); ctxt.vals = nctxt.vals; return v; } name := mkqname(m.m.typeset.name, m.m.modname); if(ctxt.used != nil){ ctxt.used.add(name, m.m); m.m.refcount++; } v := ref Rvalue(mkw(name), m.m.sig[0], 0, opts, args); if(args == nil && opts == nil) v.i = mk(n_BLOCK, v.i, nil); for(; args != nil; args = tl args) (hd args).refcount++; for(; opts != nil; opts = tl opts) for(args = (hd opts).t1; args != nil; args = tl args) (hd args).refcount++; ctxt.vals = v :: ctxt.vals; return v; } Rvalue.dup(v: self ref Rvalue): ref Rvalue { return v; } Rvalue.free(nil: self ref Rvalue, nil: int) { # XXX perhaps there should be some way of finding out whether a particular # type will allow duplication of values or not. } Rvalue.isstring(v: self ref Rvalue): int { return v.tc == 's'; } Rvalue.gets(t: self ref Rvalue): string { return t.i.word; } Rvalue.type2s(tc: int): string { return Value.type2s(tc); } Rvalue.typec(t: self ref Rvalue): int { return t.tc; } addconversion(src, dst: ref Type, expr: ref Sh->Cmd): string { # allow the same transform to be added again for(l := src.transform; l != nil; l = tl l) if((hd l).all.holds(dst.id)){ if((hd l).dst == dst.id && sh->cmd2string((hd l).expr) == sh->cmd2string(expr)) return nil; } reached := array[len types/8+1] of {* => byte 0}; if((at := ambiguous(dst, reached)) != nil) return sys->sprint("ambiguity: %s", at); src.transform = ref Transform(dst.id, sets->bytes2set(reached), expr) :: src.transform; # check we haven't created ambiguity in nodes that point to src. for(i := 0; i < len types; i++){ for(l = types[i].transform; l != nil; l = tl l){ if((hd l).all.holds(src.id) && (at = ambiguous(types[i], array[len types/8+1] of {* => byte 0})) != nil){ src.transform = tl src.transform; return sys->sprint("ambiguity: %s", at); } } } all := (Sets->None).add(dst.id); for(l = types[dst.id].transform; l != nil; l = tl l) all = all.X(Sets->A|Sets->B, (hd l).all); # add everything pointed to by dst to the all sets of those types # that had previously pointed (indirectly) to src for(i = 0; i < len types; i++) for(l = types[i].transform; l != nil; l = tl l) if((hd l).all.holds(src.id)) (hd l).all = (hd l).all.X(Sets->A|Sets->B, all); return nil; } ambiguous(t: ref Type, reached: array of byte): string { if((dt := ambiguous1(t, reached)) == nil) return nil; (nil, at) := findambiguous(t, dt, array[len reached] of {* =>byte 0}, "self "+types[t.id].qname); s := hd at; for(at = tl at; at != nil; at = tl at) s += ", " + hd at; return s; } # a conversion is ambiguous if there's more than one # way of reaching the same type. # return the type at which the ambiguity is found. ambiguous1(t: ref Type, reached: array of byte): ref Type { if(bsetholds(reached, t.id)) return t; bsetadd(reached, t.id); for(l := t.transform; l != nil; l = tl l) if((at := ambiguous1(types[(hd l).dst], reached)) != nil) return at; return nil; } findambiguous(t: ref Type, dt: ref Type, reached: array of byte, s: string): (int, list of string) { a: list of string; if(t == dt) a = s :: nil; if(bsetholds(reached, t.id)) return (1, a); bsetadd(reached, t.id); for(l := t.transform; l != nil; l = tl l){ (found, at) := findambiguous(types[(hd l).dst], dt, reached, sys->sprint("%s|%s", s, sh->cmd2string((hd l).expr))); # XXX rewite correctly for(; at != nil; at = tl at) a = hd at :: a; if(found) return (1, a); } return (0, a); } bsetholds(x: array of byte, n: int): int { return int x[n >> 3] & (1 << (n & 7)); } bsetadd(x: array of byte, n: int) { x[n >> 3] |= byte (1 << (n & 7)); } mkqname(parent, child: string): string { if(parent == "/") return parent+child; return parent+"/"+child; } # splits a canonical qname into typeset and name components. splitqname(name: string): (string, string) { if(name == nil) return (nil, nil); for(i := len name - 1; i >= 0; i--) if(name[i] == '/') break; if(i == 0) return ("/", name[1:]); return (name[0:i], name[i+1:]); } # compress multiple slashes into single; remove trailing slashes. canon(name: string): string { if(name == nil || name[0] != '/') return nil; slash := nonslash := 0; s := ""; for(i := 0; i < len name; i++){ c := name[i]; if(c == '/') slash = 1; else{ if(slash){ s[len s] = '/'; nonslash++; slash = 0; } s[len s] = c; } } if(slash && !nonslash) s[len s] = '/'; return s; } report(errorc: chan of string, s: string) { if(Debug || errorc == nil) sys->fprint(sys->fildes(2), "%s\n", s); if(errorc != nil) errorc <-= s; } Table[T].new(nslots: int, nilval: T): ref Table[T] { if(nslots == 0) nslots = 13; return ref Table[T](array[nslots] of list of (int, T), nilval); } Table[T].add(t: self ref Table[T], id: int, x: T): int { slot := id % len t.items; for(q := t.items[slot]; q != nil; q = tl q) if((hd q).t0 == id) return 0; t.items[slot] = (id, x) :: t.items[slot]; return 1; } Table[T].del(t: self ref Table[T], id: int): int { slot := id % len t.items; p: list of (int, T); r := 0; for(q := t.items[slot]; q != nil; q = tl q){ if((hd q).t0 == id){ p = joinip(p, tl q); r = 1; break; } p = hd q :: p; } t.items[slot] = p; return r; } Table[T].find(t: self ref Table[T], id: int): T { for(p := t.items[id % len t.items]; p != nil; p = tl p) if((hd p).t0 == id) return (hd p).t1; return t.nilval; } hashfn(s: string, n: int): int { h := 0; m := len s; for(i:=0; i<m; i++){ h = 65599*h+s[i]; } return (h & 16r7fffffff) % n; } Strhash[T].new(nslots: int, nilval: T): ref Strhash[T] { if(nslots == 0) nslots = 13; return ref Strhash[T](array[nslots] of list of (string, T), nilval); } Strhash[T].add(t: self ref Strhash, id: string, x: T) { slot := hashfn(id, len t.items); t.items[slot] = (id, x) :: t.items[slot]; } Strhash[T].del(t: self ref Strhash, id: string) { slot := hashfn(id, len t.items); p: list of (string, T); for(q := t.items[slot]; q != nil; q = tl q) if((hd q).t0 != id) p = hd q :: p; t.items[slot] = p; } Strhash[T].find(t: self ref Strhash, id: string): T { for(p := t.items[hashfn(id, len t.items)]; p != nil; p = tl p) if((hd p).t0 == id) return (hd p).t1; return t.nilval; } 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; } # join x to y, leaving result in arbitrary order. join[T](x, y: list of T): list of T { if(len x > len y) (x, y) = (y, x); for(; x != nil; x = tl x) y = hd x :: y; return y; } # join x to y, leaving result in arbitrary order. joinip[T](x, y: list of (int, T)): list of (int, T) { if(len x > len y) (x, y) = (y, x); for(; x != nil; x = tl x) y = hd x :: y; return y; } sort[S, T](s: S, a: array of T) for{ S => gt: fn(s: self S, x, y: T): int; } { mergesort(s, a, array[len a] of T); } mergesort[S, T](s: S, a, b: array of T) for{ S => gt: fn(s: self S, x, y: T): int; } { r := len a; if (r > 1) { m := (r-1)/2 + 1; mergesort(s, a[0:m], b[0:m]); mergesort(s, a[m:], b[m:]); b[0:] = a; for ((i, j, k) := (0, m, 0); i < m && j < r; k++) { if(s.gt(b[i], b[j])) a[k] = b[j++]; else a[k] = b[i++]; } if (i < m) a[k:] = b[i:m]; else if (j < r) a[k:] = b[j:r]; } }