-- SchemeSysOpsMXCode.mesa
DIRECTORY Atom, BasicTime, Commander, CommanderOps, IO, ProcessProps, Rope, Scheme, SchemeRegistry, SchemeSys;
SchemeSysOpsMXCode: CEDAR PROGRAM
IMPORTS Atom, BasicTime, CommanderOps, IO, ProcessProps, Rope, Scheme, SchemeRegistry, SchemeSys
= BEGIN OPEN Scheme;
  SymbolFromRope: PROC [rope: Rope.ROPE] RETURNS [Symbol] ~ {RETURN[Atom.MakeAtom[rope]]};
  RopeFromSymbol: PROC [Symbol] RETURNS [Rope.ROPE] ~ Atom.GetPName;
  TheEnvironment: PROC [a: Any] RETURNS [Scheme.Environment] = {
    WITH a SELECT FROM
      a: Scheme.Environment => RETURN [a];
      ENDCASE => Complain[a, "not a Scheme.Environment"];
    };
  TheTimer: PROC [a: Any] RETURNS [Timer] = {
    WITH a SELECT FROM
      a: Timer => RETURN [a];
      ENDCASE => Complain[a, "not a Timer"];
    };
  TheSymbol: PROC [a: Any] RETURNS [Scheme.Symbol] = {
    WITH a SELECT FROM
      a: Scheme.Symbol => RETURN [a];
      ENDCASE => Complain[a, "not a Scheme.Symbol"];
    };
  SchemeSysOpsPrim: PROC [SELF: Primitive, ARG1,ARG2,ARG3: Any, REST: ProperList] RETURNS [result: Any ← unspecified] = {
    POP: PROC RETURNS [a: Any ← undefined] = {
      IF REST#NIL THEN {a ← REST.car; REST ← NARROW[REST.cdr]}};
    DATA: Pair ~ NARROW[SELF.data];
    env: Environment ~ NARROW[DATA.cdr];
    SELECT NAT[NARROW[DATA.car, REF INT]↑] FROM
      11 => {
        result ← SchemeRegistry.ListNamedInitializers[];
        };
      10 => {
        world: Any ← ARG1;
        component: Any ← ARG2;
        name: Any ← ARG3;
        env: Any ← POP[];
        worldRope: ROPE ~ Rope.Concat[RopeFromString[TheString[world]], " "];
        componentRope: ROPE ~ Rope.Concat[RopeFromString[TheString[component]], " "];
        nameRope: ROPE ~ Rope.Concat[RopeFromString[TheString[name]], " "];
        result ← InitializationCommand[env, "Require", nameRope, worldRope, componentRope];
        };
      9 => {
        componentName: Any ← ARG1;
        env: Any ← ARG2;
        name: ROPE ~ RopeFromString[TheString[componentName]];
        result ← InitializationCommand[env, "Install -a", name];
        };
      8 => {
        componentName: Any ← ARG1;
        env: Any ← ARG2;
        name: ROPE ~ RopeFromString[TheString[componentName]];
        result ← InitializationCommand[env, "Install", name];
        };
      7 => {
        commandString: Any ← ARG1;
        outputPort: Any ← ARG2;
        parent: Commander.Handle ~ WITH ProcessProps.GetProp[$CommanderHandle] SELECT FROM
        h: Commander.Handle => h,
        ENDCASE => NIL;
        commandLine: ROPE ~ RopeFromString[TheString[commandString]];
        savedOut: IO.STREAM;
        IF parent # NIL THEN {
        savedOut ← parent.out;
        IF outputPort # undefined THEN parent.out ← ThePort[outputPort];
        };
        result ← CommanderOps.DoCommand[commandLine: commandLine, parent: parent ! UNWIND => IF parent # NIL THEN parent.out ← savedOut];
        IF parent # NIL THEN parent.out ← savedOut;
        SELECT result FROM NIL => {result ← true}; $Failure => {result ← false} ENDCASE;
        };
      6 => {
        obj: Any ← ARG1;
        result ← WITH obj SELECT FROM t: Timer => true ENDCASE => false;
        };
      5 => {
        timer: Any ← ARG1;
        t: Timer ~ TheTimer[timer];
        t.pulses ← BasicTime.GetClockPulses[];
        };
      4 => {
        timer: Any ← ARG1;
        now: BasicTime.Pulses ~ BasicTime.GetClockPulses[];
        result ← MakeFixnum[BasicTime.PulsesToMicroseconds[now-TheTimer[timer].pulses]]
        };
      3 => {
        timer: REF TimerRep ~ NEW[TimerRep];
        timer.pulses ← BasicTime.GetClockPulses[];
        result ← timer;
        };
      2 => {
        atom: Any ← ARG1;
        key: Any ← ARG2;
        value: Any ← ARG3;
        Atom.PutProp[TheSymbol[atom], key, value];
        };
      1 => {
        atom: Any ← ARG1;
        key: Any ← ARG2;
        result ← Atom.GetProp[TheSymbol[atom], key];
        };
      0 => {
        portOrString: Any ← ARG1;
        args: Any ← REST;
        output, input: IO.STREAM;
        string: String;
        stringResult: BOOL ← FALSE;
        
        IF ISTYPE[portOrString, String] THEN {     -- Handle silly Chez Scheme compatibility
        args ← Cons[portOrString, args];
        portOrString ← false;
        };
        SELECT portOrString FROM
        true => output ← SchemeSys.GetPort[undefined, FALSE];
        false => {
        output ← IO.ROS[];
        stringResult ← TRUE;
        };
        ENDCASE => output ← ThePort[portOrString];
        IF args = NIL THEN Complain[NIL, "Missing format-string argument"];
        input ← IO.RIS[RopeFromString[string ← TheString[Car[args]]]];
        args ← Cdr[args];
        
        DO
        char: CHAR ~ input.GetChar[! IO.EndOfStream => EXIT];
        IF char = '~ THEN {
        directive: CHAR ~
        input.GetChar[! IO.EndOfStream => Complain[string, "Format-string ends with a tilde"]];
        NextArg: PROC [] RETURNS [Any] ~ {
        IF args = NIL THEN
        Complain[NIL, "Too few arguments given to FORMAT"]
        ELSE {
        a: Any ← Car[args];
        args ← Cdr[args];
        RETURN[a];
        };
        };
        SELECT directive FROM
        's, 'S => Print[NextArg[], output];
        'a, 'A => Print[NextArg[], output, INT.FIRST];
        'c, 'C => output.PutChar[TheChar[NextArg[]]↑];
        '% => output.PutChar['\n];
        '\l, '\r => [] ← input.SkipWhitespace[];
        '~ => output.PutChar['~];
        'l, 'L => output.PutF1["%l", [rope[RopeFromString[TheString[NextArg[]]]]]];
        ENDCASE => Complain[MakeChar[directive], "Unknown FORMAT directive"];
        }
        ELSE
        output.PutChar[char];
        ENDLOOP;
        
        IF stringResult THEN result ← StringFromRope[IO.RopeFromROS[output]];
        };
      ENDCASE => ERROR
    };
    
  SchemeSysOpsInit: PROC [env: Environment] = {
    DefinePrimitive[name: "installation-names", nArgs: 0, proc: SchemeSysOpsPrim, doc: "() Returns the list of component names that have been installed", env: env, optional: 0, dotted: FALSE, data: Cons[MakeFixnum[11], env]];
    DefinePrimitive[name: "require", nArgs: 4, proc: SchemeSysOpsPrim, doc: "(world component name [ env ]) require a cedar subsystem to be running", env: env, optional: 1, dotted: FALSE, data: Cons[MakeFixnum[10], env]];
    DefinePrimitive[name: "reinstall", nArgs: 2, proc: SchemeSysOpsPrim, doc: "(component-name [ env ]) reinstall some object code", env: env, optional: 1, dotted: FALSE, data: Cons[MakeFixnum[9], env]];
    DefinePrimitive[name: "install", nArgs: 2, proc: SchemeSysOpsPrim, doc: "(component-name [ env ]) install some object code", env: env, optional: 1, dotted: FALSE, data: Cons[MakeFixnum[8], env]];
    DefinePrimitive[name: "do-command", nArgs: 2, proc: SchemeSysOpsPrim, doc: "(command-string [ output-port ]) Quick-and-dirty Commander interface", env: env, optional: 1, dotted: FALSE, data: Cons[MakeFixnum[7], env]];
    DefinePrimitive[name: "timer?", nArgs: 1, proc: SchemeSysOpsPrim, doc: "(obj) test for a Timer", env: env, optional: 0, dotted: FALSE, data: Cons[MakeFixnum[6], env]];
    DefinePrimitive[name: "timer-start!", nArgs: 1, proc: SchemeSysOpsPrim, doc: "(timer) Start a Timer", env: env, optional: 0, dotted: FALSE, data: Cons[MakeFixnum[5], env]];
    DefinePrimitive[name: "timer-ref", nArgs: 1, proc: SchemeSysOpsPrim, doc: "(timer) Get the number of microseconds since the timer start", env: env, optional: 0, dotted: FALSE, data: Cons[MakeFixnum[4], env]];
    DefinePrimitive[name: "make-timer", nArgs: 0, proc: SchemeSysOpsPrim, doc: "() Create a timer object", env: env, optional: 0, dotted: FALSE, data: Cons[MakeFixnum[3], env]];
    DefinePrimitive[name: "atom-put-prop", nArgs: 3, proc: SchemeSysOpsPrim, doc: "(atom key value) Set a Cedar ATOM property", env: env, optional: 0, dotted: FALSE, data: Cons[MakeFixnum[2], env]];
    DefinePrimitive[name: "atom-get-prop", nArgs: 2, proc: SchemeSysOpsPrim, doc: "(atom key) Get a Cedar ATOM property", env: env, optional: 0, dotted: FALSE, data: Cons[MakeFixnum[1], env]];
    DefinePrimitive[name: "format", nArgs: 1, proc: SchemeSysOpsPrim, doc: "([port] string . args) Formatted output; PORT is an output-port or a boolean: #t => (current-output-port), #f => a string (default)", env: env, optional: 0, dotted: TRUE, data: Cons[MakeFixnum[0], env]];
    };
    
  ROPE: TYPE ~ Rope.ROPE;
  Timer: TYPE ~ REF TimerRep;
  TimerRep: TYPE ~ RECORD [pulses: BasicTime.Pulses]; 
  
  BailOut: ERROR ~ CODE;
  InitializationCommand: PROC [env: Any, command, name: ROPE, t1, t2: ROPE ← NIL] RETURNS [result: Any] ~ {
  Inner: PROC = {
  App: PROC [rope: ROPE] ~ {IF rope.Size # 0 THEN cr ← Rope.Cat[cr, " ", rope]};
  cr: ROPE ← command; App[t1]; App[t2]; App[name];
  result ← false;
  IF CommanderOps.DoCommand[cr, NIL] = $Failure THEN BailOut;
  result ← true;
  };
  SchemeRegistry.CollectInitializers[name, Inner ! BailOut => CONTINUE];
  IF result # false THEN {
  nInit: INT = SchemeRegistry.CallNamedInitializers[name, IF env = undefined THEN GetUserEnvironment[] ELSE TheEnvironment[env]];
  IF result = true THEN result ← MakeFixnum[nInit];
  };
  };
  
  RegisterInit[SchemeSysOpsInit];
END.