# Tiny UNIX revision 2. sig str : ((a, [Char])) ~> (a, String) fun str((x, cs)) { (x, implode(cs)) } typename Option(a) = [|None|Some:a|]; sig lookup : (a, [(a, b)]) ~> Option(b) fun lookup(k, kvs) { switch (kvs) { case [] -> None case (k', v) :: kvs' -> if (k == k') Some(v) else lookup(k, kvs') } } sig modify : (a, b, [(a, b)]) ~> [(a, b)] fun modify(k, v, kvs) { switch (kvs) { case [] -> [] case (k', v') :: kvs' -> if (k == k') (k, v) :: kvs' else (k', v') :: modify(k, v, kvs') } } sig remove : (a, [(a, b)]) ~> [(a, b)] fun remove(k, kvs) { switch (kvs) { case [] -> [] case (k', v') :: kvs' -> if (k == k') kvs' else (k', v') :: remove(k, kvs') } } sig fromSome : (Option(a)) ~> a fun fromSome(x) { switch (x) { case None -> error("fromSome") case Some(x) -> x } } ## ## Basic i/o ## typename File = [Char]; typename FileDescr = Int; sig stdout : FileDescr var stdout = 1; sig basicIO : (Comp(a, {Putc:(FileDescr,Char) -> () |e})) {Putc{_} |e}~> (a, File) fun basicIO(m) { handle(m()) { case Return(res) -> (res, []) case Putc(_, c, resume) -> var (res, file) = resume(()); (res, c :: file) } } sig echo : (String) {Putc:(FileDescr,Char) -> () |_}~> () fun echo(cs) { iter(fun(c) { do Putc(stdout, c) }, explode(cs)) } fun example0() { str(basicIO(fun() { echo("Hello"); echo("World") })) } ## ## Exceptions: non-local exits ## sig exit : (Int) {Exit:(Int) -> Zero |_}-> a fun exit(n) { switch(do Exit(n)) {} } sig status : (Comp(a, {Exit:(Int) -> Zero |e})) {Exit{_} |e}~> Int fun status(m) { handle(m()) { case Return(_) -> 0 case Exit(n, _) -> n } } fun example1() { str(basicIO(fun() { status(fun() { echo("dead"); exit(1); echo("code") }) })) } ## ## Dynamic binding: user-specific environments. ## typename User = [|Alice|Bob|Root|]; sig whoami : () {Ask:String |_}-> String fun whoami() { do Ask } sig env : (User, Comp(a, {Ask:String |e})) {Ask{_} |e}~> a fun env(user, m) { handle(m()) { case Return(x) -> x case Ask(resume) -> switch (user) { case Alice -> resume("alice") case Bob -> resume("bob") case Root -> resume("root") } } } fun example2() { env(Root, whoami) } ### ### Session management. ### sig su : (User) {Su:(User) -> () |_}-> () fun su(user) { do Su(user) } sig sessionmgr : (User, Comp(a, {Ask:String,Su:(User) -> () |e})) {Ask{_},Su{_} |e}~> a fun sessionmgr(user, m) { env(user, fun() { handle(m()) { case Return(x) -> x case Su(user', resume) -> env(user', fun() { resume(()) }) } }) } fun example3() { str(basicIO(fun() { sessionmgr(Root, fun() { status(fun() { su(Alice); echo(whoami()); echo(" "); su(Bob); echo(whoami()); echo(" "); su(Root); echo(whoami()) }) }) })) } ## ## Nondeterminism: time sharing. ## sig fork : () {Fork:Bool |_}-> Bool fun fork() { do Fork } sig nondet : (Comp(a, {Fork:Bool |e})) {Fork{_} |e}~> [a] fun nondet(m) { handle(m()) { case Return(res) -> [res] case Fork(resume) -> resume(true) ++ resume(false) } } sig interrupt : () {Interrupt:() |_}-> () fun interrupt() { do Interrupt } typename Pstate(a,e::Eff) = forall q::Presence. [|Done:a |Paused:() {Interrupt{q} |e}~> Pstate(a, { |e})|]; sig slice : (Comp(a, {Interrupt:() |e})) {Interrupt{_} |e}~> Pstate(a, { |e}) fun slice(m) { handle(m()) { case Return(res) -> Done(res) case Interrupt(resume) -> Paused(fun() { resume(()) }) } } sig runNext : ([Pstate(a, { Fork:Bool |e})]) {Fork{_},Interrupt{_} |e}~> [a] fun runNext(ps) { sig run : (Pstate (a, { Fork:() {}-> Bool|e })) {Fork{_},Interrupt{_} |e}~> [a] fun run(p) { switch(p) { case Done(res) -> [res] case Paused(resume) -> runNext(nondet(resume)) } } concatMap(run, ps) } sig timeshare : (Comp(a, {Fork:Bool,Interrupt:() |e})) {Fork{_},Interrupt{_} |e}~> [a] fun timeshare(m) { var p = Paused(fun() { slice(m) }); runNext([p]) } fun example4() { str(basicIO(fun() { timeshare(fun() { sessionmgr(Root, fun() { status(fun() { var parent = fork(); (if (parent) su(Alice) else su(Bob)); echo(whoami() ^^ "> Hello "); interrupt(); echo(whoami() ^^ "> Bye "); interrupt(); if (parent) exit(0) else { var parent = fork(); interrupt(); (if (parent) su(Root) else { echo(whoami() ^^ "> oops "); exit(1) }); echo(whoami() ^^ "> !! ") } }) }) }) })) } ## ## Generic state handling ## sig get : () {Get:s |_}-> s fun get() { do Get } sig put : (s) {Put:(s) -> () |_}-> () fun put(st) { do Put(st) } sig runState : (s, Comp(a, {Get:() -> s,Put:(s) -> () |e})) {Get{_},Put{_} |e}~> (a, s) fun runState(st0, m) { var f = handle(m()) { case Return(x) -> fun(st) { (x, st) } case Get(resume) -> fun(st) { resume(st)(st) } case Put(st,resume) -> fun(_) { resume(())(st) } }; f(st0) } ## ## State: file i/o ## typename INode = (loc:Int,lno:Int); typename IList = [(Int, INode)]; # INode index -> INode typename Directory = [(String, Int)]; # Filename -> INode index typename DataRegion = [(Int, File)]; # Loc -> File typename FileSystem = (dir:Directory,dregion:DataRegion,inodes:IList ,lnext:Int ,inext:Int ); sig falloc : (String, FileSystem) ~> (Int, FileSystem) fun falloc(fname, fsys) { var loc = fsys.lnext; var dregion = (loc, []) :: fsys.dregion; var i = fsys.inext; var inode = (loc=loc,lno=1); var inodes = (i, inode) :: fsys.inodes; var dir = (fname, i) :: fsys.dir; (i, (=dir,=dregion,=inodes,lnext=loc+1,inext=i+1)) } typename WriteMode = [|Create|Append|]; sig fwrite : (WriteMode, String, [Char], FileSystem) ~> FileSystem fun fwrite(mode,fname, cs, fsys) { var (ino,fsys') = switch (lookup(fname, fsys.dir)) { case None -> falloc(fname, fsys) case Some(i) -> (i,fsys) }; var inode = fromSome(lookup(ino, fsys'.inodes)); var file = fromSome(lookup(inode.loc, fsys'.dregion)); var file' = switch (mode) { case Create -> cs case Append -> file ++ cs }; (fsys' with dregion = modify(inode.loc, file, fsys'.dregion)) } sig fread : (String, FileSystem) ~> Option([Char]) fun fread(fname, fsys) { switch (lookup(fname, fsys.dir)) { case None -> None case Some(i) -> var inode = fromSome(lookup(i, fsys.inodes)); lookup(inode.loc, fsys.dregion) } } sig flink : (String, String, FileSystem) ~> Option(FileSystem) fun flink(src, dest, fsys) { switch (lookup(dest, fsys.dir)) { case None -> None case Some(i) -> var inode = fromSome(lookup(i, fsys.inodes)); var inode = (inode with lno = inode.lno + 1); var inodes = modify(i, inode, fsys.inodes); var dir = (src, i) :: fsys.dir; Some((fsys with inodes = inodes, dir = dir)) } } sig funlink : (String, FileSystem) ~> Option(FileSystem) fun funlink(fname, fsys) { switch (lookup(fname, fsys.dir)) { case None -> None case Some(i) -> var dir = remove(fname, fsys.dir); var inode = fromSome(lookup(i, fsys.inodes)); var inode = (inode with lno = inode.lno - 1); if (inode.lno > 0) { var inodes = modify(i, inode, fsys.inodes); Some((fsys with inodes = inodes, dir = dir)) } else { var dregion = remove(inode.loc, fsys.dregion); var inodes = remove(i, fsys.inodes); Some((fsys with inodes = inodes, dir = dir, dregion = dregion)) } } }