-- File: [Thyme]<Thyme>System>CSIM01>spInput.mesa
-- Last editted:
-- Wilhelm April 6, 1982  9:59 AM, reformated by Barth and stored under
--   [Cherry]<Barth>Thyme>1.97> .
DIRECTORY spGlobals, spModelDefs, AltoDefs, CWF, Real, AbsAllocDefs;
spInput:  PROGRAM
  IMPORTS spGlobals, CWF, Real, AbsAllocDefs
  EXPORTS spGlobals, spModelDefs =
  BEGIN
    OPEN spGlobals;

    printString:  STRING = [256];

    gndNodeName:  PUBLIC namePtr;
    undefNode:    namePtr;

    cktRoot:     PUBLIC circuitNamePtr ← NIL;
    currentCkt:  circuitNamePtr ← NIL;


    ModelTablePtr:  TYPE = LONG POINTER TO ModelTableBlk;
    ModelTableBlk:  TYPE = RECORD[next:        ModelTablePtr,
                                  name:        LONG STRING,
                                  proc:        spGlobals.model,
                                  numArgs:     CARDINAL,
                                  numParms,
                                  numResults:  CARDINAL];
    ModelTable:  ModelTablePtr ← NIL;

    FuncTablePtr:  TYPE = LONG POINTER TO FuncTableBlk;
    FuncTableBlk:  TYPE = RECORD[next:      FuncTablePtr,
                                 name:      LONG STRING,
                                 proc:      spGlobals.function,
                                 numArgs:   CARDINAL,
                                 numParms:  CARDINAL];
    FuncTable:  FuncTablePtr ← NIL;

    EnterModels:  PUBLIC PROCEDURE[name:        LONG STRING,
                                   proc:        model,
                                   numArgs:     CARDINAL,
                                   numParms,
                                   numResults:  CARDINAL] =
      BEGIN
        new:  ModelTablePtr;

        new ← AbsAllocDefs.Allocate[SIZE[ModelTableBlk]];
        new↑ ← [ModelTable, name, proc, numArgs, numParms, numResults];
        ModelTable ← new
      END;

    EnterFunctions:  PUBLIC PROCEDURE[name:      LONG STRING,
                                      proc:      function,
                                      numArgs:   CARDINAL,
                                      numParms:  CARDINAL] =
      BEGIN
        new:  FuncTablePtr;

        new ← AbsAllocDefs.Allocate[SIZE[FuncTableBlk]];
        new↑ ← [FuncTable, name, proc, numArgs, numParms];
        FuncTable ← new
      END;

    findModel:  PROCEDURE[name:  LONG STRING]
                  RETURNS[model, CARDINAL, CARDINAL, CARDINAL] =
      BEGIN
        m:  ModelTablePtr;

        FOR m ← ModelTable, m↑.next UNTIL m = NIL DO
          IF LongEqualStrings[m↑.name, name] THEN 
            RETURN[m↑.proc, m↑.numArgs, m↑.numParms, m↑.numResults]
        ENDLOOP;
        error[501, FALSE];
        RETURN[NIL, 0, 0, 0]
      END;

    findFunc:  PROCEDURE[name:  LONG STRING]
                 RETURNS[function, CARDINAL, CARDINAL] =
      BEGIN
        f:  FuncTablePtr;

        FOR f ← FuncTable, f↑.next UNTIL f = NIL DO
          IF LongEqualStrings[f↑.name, name] THEN
            RETURN[f↑.proc, f↑.numArgs, f↑.numParms]
        ENDLOOP;
        error[502, FALSE];
        RETURN[NIL, 0, 0]
      END;


    enterName:  PROCEDURE RETURNS[newNamePtr:  namePtr] =
      BEGIN
        ns:  LONG STRING;

        newNamePtr ← searchName[newString, TRUE];
        IF newNamePtr = NIL THEN
          BEGIN
            newNamePtr ← makeName[currentCkt↑.names];
            ns ← makeLongString[newString.length];
            LongStringGetsString[ns, newString];
            newNamePtr↑.name ← ns;
            currentCkt↑.names ← newNamePtr
          END
        ELSE error[250];
        RETURN[newNamePtr]
      END;

    searchName:  PROCEDURE[n:  LONG STRING, oneLevel:  BOOLEAN]
                   RETURNS[np:  namePtr ← NIL] =
      BEGIN
        ckt:  circuitNamePtr ← currentCkt;
        lastName:  namePtr;

        UNTIL ckt = NIL DO
          np ← ckt↑.names;
          lastName ← NIL;
          UNTIL np = NIL DO
            IF LongEqualStrings[n, np↑.name] THEN GOTO found;
            lastName ← np;
            np ← np↑.srchLink
          ENDLOOP;
          IF oneLevel THEN EXIT;
          ckt ← ckt↑.father
        REPEAT
          found =>
            IF lastName # NIL THEN
              BEGIN
                lastName↑.srchLink ← np↑.srchLink;
                np↑.srchLink ← ckt↑.names;
                ckt↑.names ← np
              END
        ENDLOOP
      END;


    getNames:  PROCEDURE[new:  BOOLEAN] RETURNS[names:      namePtr ← NIL,
                                                nameCount:  CARDINAL ← 0] =
      BEGIN
        nPtr:  namePtr;

        UNTIL item # name DO
          nPtr ← IF new THEN enterName[] ELSE searchName[newString, TRUE];
          IF nPtr # NIL THEN
            BEGIN
              nPtr↑.nextName ← names;
              names ← nPtr
            END;
          nameCount ← nameCount + 1;
          next[];
          IF item # comma THEN EXIT ELSE next[];
          IF item # name THEN error[208]
        ENDLOOP
      END;

    getName:  PROCEDURE[new:  BOOLEAN] RETURNS[nPtr:  namePtr ← NIL] =
      BEGIN
        IF item # name THEN error[208]
        ELSE
          nPtr ← IF new THEN enterName[] ELSE searchName[newString, FALSE];
        IF nPtr = NIL THEN error[251];
        next[]
      END;

    variable:  PUBLIC PROCEDURE[context:  circuitNamePtr]
                        RETURNS[nPtr:  namePtr ← NIL] =
      BEGIN
        oldContext:  circuitNamePtr;

        IF context # NIL THEN
          BEGIN
            oldContext ← currentCkt;
            currentCkt ← context;
            nPtr ← searchName[newString, TRUE];
            currentCkt ← oldContext
          END
        ELSE nPtr ← searchName[newString, FALSE];
        IF nPtr = NIL THEN error[252]
        ELSE
          IF nPtr↑.nType # parmName AND
             nPtr↑.nType # nodeName AND
             nPtr↑.nType # conName  THEN error[221];
        next[]
      END;

    findNode:  PROCEDURE RETURNS[n:  namePtr ← undefNode] =
      BEGIN
        nName:  namePtr;

        nName ← getName[FALSE];
        IF nName # NIL THEN
          IF nName↑.nType # nodeName AND
             nName↑.nType # conName THEN error[222]
          ELSE n ← nName
      END;

    getFunction:  PROCEDURE[na, np: CARDINAL]
                    RETURNS[args:    argNames ← NIL,
                            actual:  expressionPtr ← NIL] =
      BEGIN
        index,
        index2:   CARDINAL ← 0;
        aname:    namePtr;
        newExpr:  expressionPtr;

        IF item = leftB THEN
          BEGIN
            next[];
            IF na > 0 THEN
              BEGIN
                args ← makeArgNames[na];
                UNTIL index = na DO
                  aname ← getName[FALSE];
                  args[index] ← aname;
                  IF aname # NIL THEN
                    IF aname↑.nType # conName AND
                       aname↑.nType # nodeName THEN error[222];
                  IF item = quote THEN
                    BEGIN
                      next[];
                      error[299]
                    END;
                  index ← index + 1;
                  IF item = comma THEN next[] ELSE EXIT
                ENDLOOP
              END;
            IF na = 0 OR item = vertical THEN
              BEGIN
                IF item = vertical THEN next[];
                UNTIL index2 = np DO
                  newExpr ← expression[];
                  newExpr↑.nextExpression ← actual;
                  actual ← newExpr;
                  index2 ← index2 + 1;
                  IF item = comma THEN next[] ELSE EXIT
                ENDLOOP
              END;
            IF item = rightB THEN next[] ELSE error[202,, TRUE]
          END;
        IF index # na THEN error[231];
        IF index2 # np THEN error[232]
      END;
              

    controlFunc:  PROCEDURE[b:  branchNamePtr] =
      BEGIN
        nptr:  namePtr;
        f:  functionNamePtr;
        na, np: CARDINAL;

        next[];
        IF item = name THEN
          BEGIN
            nptr ← searchName[newString, FALSE];
            IF nptr = NIL THEN
              BEGIN
                f ← LOOPHOLE[makeName[NIL]];
                f↑.forms ← functionName[,,,,];
                [f↑.functionProc, na, np] ← findFunc[newString];
                next[];
                f↑.branch ← b;
                b↑.controller ← f;
                [f↑.funcArgs, f↑.funcParms] ← getFunction[na, np];
                f↑.funcArgVec ← makeArgList[na]
              END
            ELSE
              WITH n:  nptr↑ SELECT FROM
                modelName =>
                  BEGIN
                    b↑.controller ← nptr;
                    next[];
                    IF item = leftB THEN next[] ELSE error[201,, TRUE];
                    IF item = number THEN
                      BEGIN
                        b↑.modelIndex ← Real.FixC[value];
                        IF value >= LENGTH[n.modelResults] THEN
                          error[233];
                        next[]
                      END
                    ELSE error[209];
                    IF item = rightB THEN next[] ELSE error[202,, TRUE]
                  END
              ENDCASE => error[223]
          END
        ELSE error[208]
      END;

    enterBranches:  PROCEDURE[bName:  branchNamePtr, key:  keys] =
      BEGIN
        pNode, nNode:  namePtr;
        kind:  elements;

        IF item = leftB THEN next[] ELSE error[201,, TRUE];
        pNode ← findNode[];
        IF item = comma THEN next[] ELSE error[200,, TRUE];
        nNode ← findNode[];
        IF item = rightB THEN next[] ELSE error[202,, TRUE];
        SELECT key FROM
          resKey => kind ← resistor;
          capKey => kind ← capacitor;
          indKey => kind ← inductor;
          vsKey  => kind ← vSource;
          isKey  => kind ← iSource
        ENDCASE => error[224, TRUE];
        IF pNode = nNode THEN error[241];
        IF bName # NIL THEN
           bName↑.forms ← branchName[kind, pNode, nNode, NIL, 0, NIL];

        IF item = equal THEN
          BEGIN
            next[];
            bName↑.valExpr ← expression[]
          END
        ELSE
          IF item = leftArrow THEN controlFunc[bName]
          ELSE error[205];
      END;

    enterModel:  PROCEDURE[mName:  modelNamePtr] =
      BEGIN
        na, np, nr:  CARDINAL;

        IF item = leftArrow THEN next[] ELSE error[210];
        IF item = name THEN
          BEGIN
            mName↑.forms ← modelName[,,,,];
            [mName↑.modelProc, na, np, nr] ← findModel[newString];
            next[];
            [mName↑.modelArgs, mName↑.modelParms] ← getFunction[na, np];
            mName↑.modelResults ← makeArgList[nr];
            mName↑.modelArgVec  ← makeArgList[na]
          END
        ELSE error[208]
      END;

    circuitError:  PROCEDURE RETURNS[STRING] =
      BEGIN
        oldProc:  PROCEDURE[CHARACTER];

        printString.length ← 0;
        oldProc ← CWF.WriteToString[printString];
        printCircuitTree[currentCkt];
        [] ← CWF.SetWriteProcedure[oldProc];
        RETURN[printString]
      END;

    printCircuitTree:  PROCEDURE[c:  circuitNamePtr] =
      BEGIN
        IF c # NIL AND c↑.father # NIL THEN
          BEGIN
            printCircuitTree[c↑.father];
            CWF.WF1["%s.", c↑.name]
          END
      END;

    getNewParameters:  PROCEDURE RETURNS[plist:  parmNamePtr ← NIL,
                                         nparms:  CARDINAL ← 0] =
      BEGIN
        n:  namePtr;

        IF item = vertical THEN next[];
        UNTIL item # name DO
          n ← enterName[];
          next[];
          nparms ← nparms + 1;
          IF item = leftArrow THEN
            BEGIN
              ENABLE
                ErrorSignal =>
                  BEGIN
                    ErrorStrings[error, circuitError[], s];
                    n.forms ← parmName[FALSE, 0.0, plist];
                    GOTO badEnd
                  END;
                  
              next[];
              n.forms ← parmName[TRUE, eval[expression[]], plist]
            EXITS
              badEnd => NULL
            END
          ELSE n.forms ← parmName[FALSE, 0.0, plist];
          plist ← LOOPHOLE[n];
          IF item = comma THEN next[] ELSE EXIT;
          IF item # name THEN error[208]
        ENDLOOP;
      END;

    defineCircuit:  PROCEDURE[names:  namePtr, root:  BOOLEAN] =
      BEGIN
        exp:  expressionPtr;

        names↑.forms ← circuitName[NIL, NIL, 0, NIL, currentCkt, NIL];
        currentCkt ← LOOPHOLE[names];
        IF root THEN
          BEGIN
            cktRoot ← currentCkt;
            newString ← "Undefined";
            undefNode ← enterName[];
            undefNode↑.forms ← nodeName[];
            currentCkt↑.names ← NIL; 
            newString ← "Gnd";
            gndNodeName ← enterName[];
            gndNodeName↑.forms ← nodeName[];
            next[]
          END;
        IF item = leftB THEN
          BEGIN
            next[];
            IF ~root AND item # vertical THEN
              BEGIN
                [names, currentCkt↑.conCount] ← getNames[TRUE];
                currentCkt↑.fakeNodes ← names;
                UNTIL names = NIL DO
                  names↑.forms ← conName[];
                  names ← names↑.nextName
                ENDLOOP
              END;
            IF root OR item = vertical THEN
              [currentCkt↑.parms,] ← getNewParameters[];
            IF item = rightB THEN next[] ELSE error[202,, TRUE]
          END;
        IF item = name AND searchKey[] = assertsKey THEN
          BEGIN
            next[];
            IF item = leftB THEN
              BEGIN
                next[];
                DO
                  exp ← expression[];
                  exp↑.nextExpression ← currentCkt↑.assertions;
                  currentCkt↑.assertions ← exp;
                  IF item = comma THEN next[] ELSE EXIT
                ENDLOOP;
                IF item = rightB THEN next[] ELSE error[202,, TRUE]
              END
            ELSE error[201]
          END;
        IF item = equal THEN next[] ELSE error[205, TRUE, TRUE];
        IF item = leftC THEN next[] ELSE error[203, TRUE, FALSE];
        DO
          IF item = name THEN enterDefinition[];
          IF item = rightC OR item = eof THEN EXIT;
          IF item = semi THEN next[] ELSE error[206, TRUE, FALSE]
        ENDLOOP;
        currentCkt ←
 currentCkt↑.father;
        next[]
      END;

    getActualParameters:  PROCEDURE[ckt:  circuitNamePtr]
                            RETURNS[apList:  expressionPtr ← NIL] =
      BEGIN
        e:  expressionPtr;

        IF item = vertical THEN next[];
        DO
          IF item # name THEN GOTO nameErr;
          e ← assignExpr[ckt];
          e↑.nextExpression ← apList;
          apList ← e;
          IF item = comma THEN next[] ELSE EXIT
        REPEAT
          nameErr => error[208]
        ENDLOOP
      END;

    defCktInstance:  PROCEDURE[cn:  LONG STRING, iName:  namePtr] =
      BEGIN
        cktName:  namePtr;
        n:  namePtr;
        cons:  conLinkPtr ← NIL;
        cCnt:  CARDINAL ← 0;
        noConnections:  BOOLEAN;
        actuals:  expressionPtr ← NIL;

        cktName ← searchName[cn, FALSE];
        IF cktName # NIL THEN
          WITH c:  cktName↑ SELECT FROM
            circuitName =>
              BEGIN
                IF item = leftB THEN
                  BEGIN
                    next[];
                    noConnections ← c.conCount = 0;
                    IF ~noConnections THEN
                      WHILE item = name DO
                        n ← findNode[];
                        cons ← makeConLink[cons, n];
                        cCnt ← cCnt + 1;
                        IF item = comma THEN next[] ELSE EXIT
                      ENDLOOP;
                    IF noConnections OR item = vertical THEN
                        actuals ← getActualParameters[@c];
                    IF item = rightB THEN next[] ELSE error[202,, TRUE]
                  END;
                IF cCnt # c.conCount THEN error[260];
                iName↑.forms ← cktInstance[@c, cons, actuals] 
              END
          ENDCASE => error[225, TRUE]
        ELSE error[251, TRUE]
      END;

    enterDefinition:  PROCEDURE =
      BEGIN
        key:  keys;
        nameCount:  CARDINAL;
        names:  namePtr;
        s:  STRING = [256];
        explodeExpr:  expressionPtr ← NIL;

        [names, nameCount] ← getNames[TRUE];
        IF names = NIL THEN error[208];
        IF item # colon THEN error[207,, TRUE] ELSE next[];
        IF item = leftP THEN
          BEGIN
            explodeExpr ← expression[];
            IF names # NIL THEN names↑.expExpr ← explodeExpr;
            IF item # implies THEN error[215,, TRUE] ELSE next[]
          END;
        IF item = name THEN
          BEGIN
            key ← searchKey[];
            LongStringGetsString[s, newString];
            next[];
            IF key # nodeKey AND nameCount # 1 THEN error[261];
            SELECT key FROM
              nodeKey => 
                BEGIN 
                  IF names↑.expExpr # NIL THEN error[253];
                  UNTIL names = NIL DO 
                    names↑.forms ← nodeName[]; 
                    names ← names↑.nextName 
                  ENDLOOP; 
                END;
              resKey, capKey, indKey, vsKey, isKey => 
                enterBranches[LOOPHOLE[names], key];
              circuitKey =>
                BEGIN 
                  IF names↑.expExpr # NIL THEN error[253];
                  defineCircuit[names, FALSE];
                END;
              modelKey =>
                BEGIN 
                  IF names↑.expExpr # NIL THEN error[253];
                  enterModel[LOOPHOLE[names]]
                END;
            ENDCASE =>
              defCktInstance[s, names]
          END
        ELSE error[208, TRUE]
      END;

    input: PUBLIC PROCEDURE =
      BEGIN
        n:  namePtr;

        IF item # name OR searchKey[] # circuitKey THEN error[225];
        n ← makeName[NIL];
        n↑.name ← "Main Circuit";
        defineCircuit[n, TRUE]
      END;

    levelSpace:  PROCEDURE[level:  CARDINAL] =
      BEGIN
        UNTIL level = 0 DO
          CWF.WF0["   "];
          level ← level - 1
        ENDLOOP
      END;

    output:  PUBLIC PROCEDURE[ckt:  circuitNamePtr, level:  CARDINAL] =
      BEGIN
        cLink:  conLinkPtr;
        np:  namePtr;

        np ← ckt↑.names;
        UNTIL np = NIL DO
          levelSpace[level];
          CWF.WF1["%s", np↑.name];
          WITH n:  np↑ SELECT FROM
            nodeName => NULL;
            branchName =>
              BEGIN
                CWF.WF2[":  branch[%s, %s]",
                        n.posNode↑.name,
                        n.negNode↑.name]
              END;
            circuitName =>
              BEGIN
                CWF.WF0[":  circuit.*n"];
                output[@n, level + 1]
              END;
            conName => NULL;
            parmName =>
              BEGIN
                CWF.WF0[":  parameter"];
                IF n.default THEN CWF.WF1[" ← %13.5f", @n.dfltValue]
              END;
            cktInstance =>
              BEGIN
                CWF.WF1[":  %s[", n.of↑.name];
                cLink ← n.connections;
                UNTIL cLink = NIL DO
                  CWF.WF1["%s", cLink↑.namedNode↑.name];
                  cLink ← cLink↑.nextLink;
                  IF cLink # NIL THEN CWF.WF0[", "]
                ENDLOOP;
                CWF.WFC[']]
              END
          ENDCASE;
          np ← np↑.srchLink;
          CWF.WF0[".*n"]
        ENDLOOP
      END;
  END.