-- Parse.Mesa  Edited by Sandman on July 10, 1980  7:57 AM
-- Copyright  Xerox Corporation 1979, 1980

DIRECTORY
  AltoDefs USING [PageSize],
  BootmesaOps USING [
    BootAbort, BootmesaError, DisableHyperspace, FrameForGFI, ModuleGFI,
    MultipleNames, ResetConfig, SetConfig, SetDefaultGFTLength,
    SetDefaultMemoryLimits, SetDefaultNProcesses, SetResidentFramePages],
  CommanderDefs USING [AddCommand, CommandBlockHandle],
  ControlDefs USING [GFTIndex, GFTNull, GlobalFrameHandle, NullGlobalFrame],
  FSPDefs USING [
    AddToNewZone, DestroyZone, MakeNewZone, MakeNode, NodeOverhead, NoRoomInZone,
    ZoneOverhead, ZonePointer],
  IODefs USING [CR, NUL, SP, WriteChar, WriteString],
  String USING [
    AppendChar, AppendString, EqualString, InvalidNumber, StringToDecimal,
    WordsForString],
  Storage USING [Pages, FreePages, PagesForWords];

Parse: PROGRAM
  IMPORTS BootmesaOps, FSPDefs, IODefs, String, Storage, CommanderDefs
  EXPORTS BootmesaOps =PUBLIC
  
  BEGIN OPEN BootmesaOps;
  
  NUL: CHARACTER = IODefs.NUL;
  CR: CHARACTER = IODefs.CR;
  
  get: PROCEDURE RETURNS [CHARACTER];
  noMore: SIGNAL;
  
  InitParse: PUBLIC PROCEDURE [
    input: PROCEDURE RETURNS [CHARACTER], end: SIGNAL] =
    BEGIN get ← input; noMore ← end END;
    
  FinishParse: PUBLIC PROCEDURE =
    BEGIN
    Control ← Resident ← SwappedIn ← Wart ← Nub ← NoTrap ← NIL;
    lastResident ← lastSwappedIn ← lastNoTrap ← NIL;
    EraseHeap[]
    END;
    
  GetToken: PROCEDURE [token: STRING] RETURNS [term: CHARACTER] =
    BEGIN OPEN IODefs;
    token.length ← 0;
    DO
      ENABLE noMore => BEGIN term ← NUL; EXIT END;
      SELECT term ← get[] FROM
	'- => UNTIL get[] = CR DO NULL ENDLOOP;
	SP, CR => NULL;
	':, ';, '>, ', => RETURN;
	ENDCASE => String.AppendChar[token, term];
      ENDLOOP;
    END;
    
  module: STRING ← [40];
  config: STRING ← [40];
  keyword: STRING ← [40];
  
  GetModule: PROCEDURE [module, config: STRING] RETURNS [term: CHARACTER] =
    BEGIN
    module.length ← config.length ← 0;
    term ← GetToken[module];
    IF term = ': THEN
      BEGIN OPEN IODefs;
      WriteString["Syntax Error. Expected module Found "L];
      WriteString[module];
      WriteChar[term];
      SIGNAL BootAbort;
      END;
    IF term = '> THEN
      BEGIN
      String.AppendString[config, module];
      term ← GetToken[module];
      IF term = ': THEN
	BEGIN OPEN IODefs;
	WriteString["Syntax Error. Expected module Found "L];
	WriteString[module];
	WriteChar[term];
	SIGNAL BootAbort;
	END;
      IF term = '> THEN
	BootmesaModuleError[
	  "Syntax Error. Only one config name allowed "L, config, module];
      END;
    RETURN
    END;
    
  GetNumber: PROCEDURE RETURNS [term: CHARACTER, n: CARDINAL] =
    BEGIN
    token: STRING ← [10];
    null: STRING ← [1];
    term ← GetToken[token];
    n ← String.StringToDecimal[
      token !
      String.InvalidNumber =>
	BootmesaModuleError["Invalid Number"L, null, token]];
    RETURN
    END;
    
  BootmesaModuleError: PUBLIC PROCEDURE [msg, config, module: STRING] =
    BEGIN OPEN IODefs;
    WriteString[msg];
    IF config.length # 0 THEN BEGIN WriteString[config]; WriteChar['>]; END;
    WriteString[module];
    SIGNAL BootAbort;
    RETURN
    END;
    
  GetKeyWord: PROCEDURE [keyword: STRING] RETURNS [term: CHARACTER] =
    BEGIN
    keyword.length ← 0;
    term ← GetToken[keyword];
    IF term # ': AND keyword.length # 0 THEN
      BootmesaError["Syntax Error. Expected KEYWORD:"L];
    RETURN
    END;
    
  ParseInput: PUBLIC PROCEDURE =
    BEGIN OPEN String;
    term: CHARACTER;
    term ← GetKeyWord[keyword];
    UNTIL term = NUL DO
      SELECT TRUE FROM
	EqualString[keyword, "CONTROL"L] =>
	  BEGIN term ← GetModule[module, config]; AddControl[module, config]; END;
	EqualString[keyword, "NUB"L] =>
	  BEGIN term ← GetModule[module, config]; AddNub[module, config]; END;
	EqualString[keyword, "WART"L] =>
	  BEGIN term ← GetModule[module, config]; AddWart[module, config]; END;
	EqualString[keyword, "RESIDENTCODE"L] =>
	  BEGIN
	  DO
	    SELECT term ← GetModule[module, config] FROM
	      '; => EXIT;
	      ', => AddResident[module, config];
	      ENDCASE =>
		BootmesaModuleError[
		  "Syntax Error. Invalid modulelist at "L, config, module];
	    ENDLOOP;
	  AddResident[module, config];
	  END;
	EqualString[keyword, "SWAPPEDINCODE"L] =>
	  BEGIN
	  DO
	    SELECT term ← GetModule[module, config] FROM
	      '; => EXIT;
	      ', => AddSwappedIn[module, config];
	      ENDCASE =>
		BootmesaModuleError[
		  "Syntax Error. Invalid modulelist at "L, config, module];
	    ENDLOOP;
	  AddSwappedIn[module, config];
	  END;
	EqualString[keyword, "NOTRAP"L] =>
	  BEGIN
	  DO
	    SELECT term ← GetModule[module, config] FROM
	      '; => EXIT;
	      ', => AddNoTrap[module, config];
	      ENDCASE =>
		BootmesaModuleError[
		  "Syntax Error. Invalid modulelist at "L, config, module];
	    ENDLOOP;
	  AddNoTrap[module, config];
	  END;
	EqualString[keyword, "RESIDENTFRAMEPAGES"L] =>
	  BEGIN
	  pages: CARDINAL;
	  [term, pages] ← GetNumber[];
	  IF term # '; THEN BootmesaError["Error. Invalid Frame Page Size"L];
	  SetResidentFramePages[pages];
	  END;
	EqualString[keyword, "GFT"L] =>
	  BEGIN
	  length: CARDINAL;
	  [term, length] ← GetNumber[];
	  IF term # '; THEN BootmesaError["Error. Invalid GFT Length"L];
	  SetDefaultGFTLength[length];
	  END;
	EqualString[keyword, "MEMORY"L] =>
	  BEGIN
	  fp, lp: CARDINAL;
	  [term, fp] ← GetNumber[];
	  IF term # ', THEN BootmesaError["Error. Invalid Memory Bounds"L];
	  [term, lp] ← GetNumber[];
	  IF term # '; THEN BootmesaError["Error. Invalid Memory Bounds"L];
	  SetDefaultMemoryLimits[fp, lp];
	  END;
	EqualString[keyword, "PROCESSES"L] =>
	  BEGIN
	  number: CARDINAL;
	  [term, number] ← GetNumber[];
	  IF term # '; THEN BootmesaError["Error. Invalid Process Number"L];
	  SetDefaultNProcesses[number];
	  END;
	EqualString[keyword, "DISABLEHYPERSPACE"L] =>
	  BEGIN term ← GetToken[keyword]; DisableHyperspace[]; END;
	ENDCASE =>
	  BEGIN OPEN IODefs;
	  WriteString["Syntax Error. Expected Keyword: Found "L];
	  WriteString[keyword];
	  WriteChar[term];
	  SIGNAL BootAbort;
	  END;
      term ← GetKeyWord[keyword];
      ENDLOOP;
    RETURN;
    END;
    
  ModuleObject: TYPE = RECORD [
    link: ModuleHandle, gfi: ControlDefs.GFTIndex, module, config: STRING];
  
  ModuleHandle: TYPE = POINTER TO ModuleObject;
  
  Control, Resident, SwappedIn, Wart, Nub, NoTrap: ModuleHandle ← NIL;
  lastResident, lastSwappedIn, lastNoTrap: ModuleHandle ← NIL;
  
  GetModuleObject: PROCEDURE [module, config: STRING] RETURNS [ModuleHandle] =
    BEGIN OPEN Storage;
    m: ModuleHandle ← GetSpace[SIZE[ModuleObject]];
    m↑ ←
      [link: NIL, gfi: ControlDefs.GFTNull, module: GetString[module.length],
	config: GetString[config.length]];
    String.AppendString[m.module, module];
    String.AppendString[m.config, config];
    RETURN[m];
    END;
    
  AddControl: PROCEDURE [module, config: STRING] =
    BEGIN Control ← GetModuleObject[module, config]; END;
    
  AddWart: PROCEDURE [module, config: STRING] =
    BEGIN Wart ← GetModuleObject[module, config]; END;
    
  AddNub: PROCEDURE [module, config: STRING] =
    BEGIN Nub ← GetModuleObject[module, config]; END;
    
  AddResident: PROCEDURE [module, config: STRING] =
    BEGIN
    m: ModuleHandle ← GetModuleObject[module, config];
    IF Resident = NIL THEN Resident ← m ELSE lastResident.link ← m;
    lastResident ← m;
    END;
    
  AddSwappedIn: PROCEDURE [module, config: STRING] =
    BEGIN
    m: ModuleHandle ← GetModuleObject[module, config];
    IF SwappedIn = NIL THEN SwappedIn ← m ELSE lastSwappedIn.link ← m;
    lastSwappedIn ← m;
    END;
    
  AddNoTrap: PROCEDURE [module, config: STRING] =
    BEGIN
    m: ModuleHandle ← GetModuleObject[module, config];
    IF NoTrap = NIL THEN NoTrap ← m ELSE lastNoTrap.link ← m;
    lastNoTrap ← m;
    END;
    
  FindModule: PROCEDURE [m: ModuleHandle] =
    BEGIN OPEN m;
    IF gfi # ControlDefs.GFTNull THEN RETURN;
    IF config.length # 0 THEN SetConfig[config];
    gfi ← ModuleGFI[
      module !
      MultipleNames =>
	IF config.length = 0 THEN CONTINUE
	ELSE BEGIN SetConfig[config]; RETRY END];
    IF gfi = ControlDefs.GFTNull THEN
      BootmesaModuleError["Can't find module: "L, config, module];
    IF config.length # 0 THEN ResetConfig[];
    END;
    
  UserControl: PUBLIC PROCEDURE RETURNS [f: ControlDefs.GlobalFrameHandle] =
    BEGIN
    IF Control = NIL THEN RETURN[ControlDefs.NullGlobalFrame];
    FindModule[Control];
    f ← FrameForGFI[Control.gfi];
    END;
    
  NubFrame: PUBLIC PROCEDURE RETURNS [f: ControlDefs.GlobalFrameHandle] =
    BEGIN
    IF Nub = NIL THEN RETURN[ControlDefs.NullGlobalFrame];
    FindModule[Nub];
    f ← FrameForGFI[Nub.gfi];
    END;
    
  WartFrame: PUBLIC PROCEDURE RETURNS [f: ControlDefs.GlobalFrameHandle] =
    BEGIN
    IF Wart = NIL THEN BootmesaError["Error No Wart"L];
    FindModule[Wart];
    f ← FrameForGFI[Wart.gfi];
    IF f = ControlDefs.NullGlobalFrame THEN
      BootmesaModuleError["Error Can't find Wart: "L, config, module];
    END;
    
  LookUpModules: PUBLIC PROCEDURE =
    BEGIN
    LookUpModuleList[Resident];
    LookUpModuleList[SwappedIn];
    LookUpModuleList[NoTrap];
    RETURN
    END;
    
  LookUpModuleList: PROCEDURE [list: ModuleHandle] =
    BEGIN
    m: ModuleHandle;
    FOR m ← list, m.link UNTIL m = NIL DO FindModule[m] ENDLOOP;
    END;
    
  EnumerateResidentModules: PUBLIC PROCEDURE [
    proc: PROCEDURE [ControlDefs.GFTIndex] RETURNS [BOOLEAN]]
    RETURNS [ControlDefs.GFTIndex] =
    BEGIN RETURN[EnumerateModules[Resident, proc]] END;
    
  EnumerateSwappedInModules: PUBLIC PROCEDURE [
    proc: PROCEDURE [ControlDefs.GFTIndex] RETURNS [BOOLEAN]]
    RETURNS [ControlDefs.GFTIndex] =
    BEGIN RETURN[EnumerateModules[SwappedIn, proc]] END;
    
  EnumerateNoTrapModules: PUBLIC PROCEDURE [
    proc: PROCEDURE [ControlDefs.GFTIndex] RETURNS [BOOLEAN]]
    RETURNS [ControlDefs.GFTIndex] =
    BEGIN RETURN[EnumerateModules[NoTrap, proc]] END;
    
  EnumerateModules: PROCEDURE [
    list: ModuleHandle, proc: PROCEDURE [ControlDefs.GFTIndex] RETURNS [BOOLEAN]]
    RETURNS [ControlDefs.GFTIndex] =
    BEGIN
    m: ModuleHandle;
    FOR m ← list, m.link UNTIL m = NIL DO
      IF m.gfi # ControlDefs.GFTNull AND proc[m.gfi] THEN RETURN[m.gfi]; ENDLOOP;
    RETURN[ControlDefs.GFTNull]
    END;
    
  parseHeap: FSPDefs.ZonePointer ← NIL;
  
  GetString: PROCEDURE [nchars: CARDINAL] RETURNS [s: STRING] =
    BEGIN
    s ← GetSpace[String.WordsForString[nchars]];
    s↑ ← [length: 0, maxlength: nchars, text:];
    RETURN
    END;
    
  GetSpace: PROCEDURE [nwords: CARDINAL] RETURNS [p: POINTER] =
    BEGIN OPEN Storage, FSPDefs;
    np: CARDINAL;
    IF parseHeap = NIL THEN InitHeap[];
    p ← MakeNode[
      parseHeap, nwords !
      NoRoomInZone =>
	BEGIN
	np ← PagesForWords[nwords + ZoneOverhead + NodeOverhead];
	IF np < 2 THEN np ← 2;
	AddToNewZone[parseHeap, Pages[np], np*AltoDefs.PageSize, FreePages];
	RESUME
	END];
    RETURN
    END;
    
  InitHeap: PROCEDURE =
    BEGIN OPEN Storage, FSPDefs;
    IF parseHeap # NIL THEN EraseHeap[];
    parseHeap ← MakeNewZone[Pages[4], 1024, FreePages];
    RETURN
    END;
    
  EraseHeap: PROCEDURE =
    BEGIN FSPDefs.DestroyZone[parseHeap]; parseHeap ← NIL; RETURN END;
    
  FindModuleFromString: PROCEDURE [s, module, config: STRING] =
    BEGIN
    i: CARDINAL;
    c: CHARACTER;
    FOR i IN [0..s.length) DO
      SELECT (c ← s[i]) FROM
	'> => BEGIN String.AppendString[config, module]; module.length ← 0; END;
	IN ['A..'Z], IN ['a..'z], IN ['0..'9] => String.AppendChar[module, c];
	ENDCASE => BEGIN module.length ← 0; EXIT END;
      ENDLOOP;
    RETURN
    END;
    
  ResidentCM: PROCEDURE [s: STRING] =
    BEGIN
    module: STRING ← [40];
    config: STRING ← [40];
    FindModuleFromString[s, module, config];
    IF module.length = 0 THEN IODefs.WriteString["! Bad name"]
    ELSE AddResident[module, config];
    RETURN
    END;
    
  SwappedInCM: PROCEDURE [s: STRING] =
    BEGIN
    module: STRING ← [40];
    config: STRING ← [40];
    FindModuleFromString[s, module, config];
    IF module.length = 0 THEN IODefs.WriteString["! Bad name"]
    ELSE AddSwappedIn[module, config];
    RETURN
    END;
    
  AddCommands: PROCEDURE =
    BEGIN OPEN CommanderDefs;
    c: CommandBlockHandle;
    c ← AddCommand["ResidentCodeModule", LOOPHOLE[ResidentCM], 1];
    c.params[0] ← [type: string, prompt: "Module name"];
    c ← AddCommand["SwappedInCodeModule", LOOPHOLE[SwappedInCM], 1];
    c.params[0] ← [type: string, prompt: "Module name"];
    RETURN
    END;
    
  AddCommands[];
  
  END...