// ResistParse.bcpl -- parse/unparse routines

// Last modified December 6, 1979  11:56 AM

get "Resist.decl"
get "Streams.d"

external
[
// outgoing procedures
ReadResistFile; ReadWireListFile; UnparseIC

// incoming procedures
LookupOrDefineSymbol; DefineSymbol; LookupSymbol; InsertSymbol
SymbolsObject; ObjectsString; SymbolsString; CreateNet
ReportError; ExpandTemplate
FilePos; SetFilePos
Ws; Wss; Gets; Puts; Endofs; PutTemplate; DefaultArgs
Allocate; Free; Zero; MoveBlock

// outgoing statics
unknownNet

// incoming statics
dsp; sysZone; maxNPins
]

static [ unknownNet = 0 ]

manifest ecEof = 1302
manifest [ ok = 0; bad = 1; ignore = 2 ]

// ---------------------------------------------------------------------------
let ReadResistFile(stream, symbolTable, readingExceptions; numargs na) = valof
// ---------------------------------------------------------------------------
[
DefaultArgs(lv na, -2, false)
let firstIC = 0
let lvNextIC = lv firstIC
let locationString, tempString = vec 127, vec 127
stream>>ST.par1 = 0
let filePos = vec 1
stream>>ST.par2 = stream>>ST.error
stream>>ST.error = ReadTokenError

let symDIP3 = LookupOrDefineSymbol(symbolTable, "DIP3", stICPackage)
let symSIP = LookupOrDefineSymbol(symbolTable, "SIP", stICPackage)
if unknownNet eq 0 then unknownNet = CreateNet(symbolTable, "?")

let ic = Allocate(sysZone, lenIC + maxNPins*lenPin)

   [ // repeat
   FilePos(stream, filePos)
   let token = ReadToken(stream, locationString)
   if token eq -1 then break
   if token eq $*n loop
   
   let result = valof
      [
      if token ne 0 resultis bad
      if ReadToken(stream, tempString) ne $( resultis bad
      if ReadToken(stream, tempString) ne 0 resultis bad
      ic>>IC.package =
         LookupOrDefineSymbol(symbolTable, tempString, stICPackage)
      if ReadToken(stream, tempString) ne $, resultis bad
      unless ReadNumber(stream, lv ic>>IC.nPins) resultis bad
      if ic>>IC.nPins ls 1 % ic>>IC.nPins gr maxNPins then
         resultis ExpandTemplate("IC has too many pins -- only $D allowed.",
          maxNPins)

      // Ignore SIPs and bypass capacitors
      if ic>>IC.package eq symSIP %
       (ic>>IC.package eq symDIP3 & ic>>IC.nPins eq 2) resultis ignore

      if ReadToken(stream, tempString) ne $, resultis bad
      if ReadToken(stream, tempString) ne 0 resultis bad
      for i = 1 to tempString>>String.length do
         if tempString>>String.char↑i eq $/ then
            [ tempString>>String.length = i-1; break ]
      ic>>IC.type = LookupOrDefineSymbol(symbolTable, tempString, stICType)
      if ReadToken(stream, tempString) ne $) resultis bad
   
      for i = 1 to ic>>IC.nPins do
         [
         let pin = lv ic>>IC.pins↑i
         let expectedOhms = nil
         if ReadToken(stream, tempString) ne 0 resultis bad
         unless GetNumber(tempString, lv expectedOhms) do
            [
            if tempString>>String.length ne 1 resultis bad
            switchon tempString>>String.char↑1 into
               [
               case $$:
                  expectedOhms = open; endcase
               case $?:
                  expectedOhms = unknown; endcase
               default:
                  resultis bad
               ]
            ]
         let token = ReadToken(stream, tempString)
         if token eq $< then
            [
            token = ReadToken(stream, tempString) repeatuntil token eq $>
            token = ReadToken(stream, tempString)
            ]
         if token ne (i eq ic>>IC.nPins? $*n, $,) resultis bad
         pin>>Pin.expectedOhms = expectedOhms
         pin>>Pin.measuredOhms = unknown
         pin>>Pin.nextPin = lv unknownNet>>Net.dummyPin
         ]

      resultis ok
      ]
   
   switchon result into
      [
      case ignore:
         until ReadToken(stream, tempString) eq $*n do loop
         endcase
      case ok:
         [
         let icSymbol = LookupSymbol(symbolTable, locationString)
         test icSymbol eq 0
            ifso unless readingExceptions do
               [
               let icObject = SymbolsObject(DefineSymbol(symbolTable,
                locationString, stIC, ic, lenIC + ic>>IC.nPins*lenPin))
               @lvNextIC = icObject
               lvNextIC = lv icObject>>IC.next
               icObject>>IC.next = 0
               ]
            ifnot
               [
               unless readingExceptions do
                  [
                  result = ExpandTemplate("IC at $S defined multiple times.",
                   locationString)
                  docase bad
                  ]
               let icObject = SymbolsObject(icSymbol)
               if ic>>IC.nPins ne icObject>>IC.nPins %
                ic>>IC.package ne icObject>>IC.package %
                ic>>IC.type ne icObject>>IC.type then
                  [
                  result = ExpandTemplate("Exceptions file inconsistent with Resist file for IC at $S.",
                   locationString)
                  docase bad
                  ]
               MoveBlock(lv icObject>>IC.pins, lv ic>>IC.pins,
                ic>>IC.nPins*lenPin)
               ]
         endcase
         ]
      default:
         Ws("*n")
         SetFilePos(stream, filePos)
            [ // repeat
            let char = Gets(stream)
            if char eq $*n % char eq -1 break
            Puts(dsp, char)
            ] repeat
         test result eq bad
            ifso ReportError("Syntax error in $S file.",
                (readingExceptions? "Exceptions", "Resist"))
            ifnot [ ReportError(result); Free(sysZone, result) ]
         stream>>ST.par1 = 0
         endcase
      ]
   ] repeat

Free(sysZone, ic)
stream>>ST.error = stream>>ST.par2
resultis firstIC
]

// ---------------------------------------------------------------------------
and ReadWireListFile(stream, symbolTable) be
// ---------------------------------------------------------------------------
[
let netNameString, tempString = vec 127, vec 127
stream>>ST.par1 = 0
let filePos = vec 1
stream>>ST.par2 = stream>>ST.error
stream>>ST.error = ReadTokenError

let symCALIBRATE = LookupOrDefineSymbol(symbolTable, "CALIBRATE", stDummy)
let symDISCONNECT = LookupOrDefineSymbol(symbolTable, "DISCONNECT", stDummy)

until ReadToken(stream, tempString) eq 0 &
 tempString>>String.length eq 1 & tempString>>String.char↑1 eq $@ do loop

   [ // repeat
   FilePos(stream, filePos)
   let token = ReadToken(stream, netNameString, true)
   if token eq -1 then break
   if token eq $*n loop

   let result = valof
      [
      if token ne 0 resultis bad
      let length = netNameString>>String.length
      if netNameString>>String.char↑length ne $: resultis bad
      netNameString>>String.length = length-1
      let netSymbol = LookupSymbol(symbolTable, netNameString)
      if netSymbol eq symCALIBRATE % netSymbol eq symDISCONNECT then
         resultis ignore
      if netSymbol ne 0 then
         resultis ExpandTemplate("Multiply-defined net: $S",
          SymbolsString(netSymbol))

      until ReadToken(stream, tempString) eq $*n do loop
      let net, pLastNextPin = 0, nil

         [ // repeat
         let token = ReadToken(stream, tempString)
         if token eq $*n then
            [
            token = ReadToken(stream, tempString)
            if token eq $*n % token eq -1 resultis ok
            ]
         if token ne 0 resultis bad

         result = valof
            [
            let period = 0
            for i = 1 to tempString>>String.length do
               if tempString>>String.char↑i eq $. then period = i
            if period eq 0 resultis ignore
            let pinNumber = 0
            for i = period+1 to tempString>>String.length do
               [
               let digit = tempString>>String.char↑i - $0
               if digit ls 0 % digit gr 9 then
                  test i eq tempString>>String.length
                     ifso break
                     ifnot resultis ExpandTemplate("Illegal pin name: $S",
                         tempString)
               pinNumber = 10*pinNumber + digit
               ]
            tempString>>String.length = period-1
   
            let icSymbol = LookupSymbol(symbolTable, tempString)
            if icSymbol eq 0 resultis ignore
            let ic = SymbolsObject(icSymbol)
            if pinNumber ls 1 % pinNumber gr ic>>IC.nPins then
               resultis ExpandTemplate("Pin number $D illegal for IC at $S",
                pinNumber, SymbolsString(icSymbol))
   
            let pin = lv ic>>IC.pins↑pinNumber
            if net eq 0 then
               [
               net = CreateNet(symbolTable, netNameString)
               pLastNextPin = lv net>>Net.firstPin
               ]
            @pLastNextPin = pin
            pLastNextPin = lv pin>>Pin.nextPin
            pin>>Pin.nextPin = lv net>>Net.dummyPin
            resultis ok
            ]

         unless result eq ok % result eq ignore resultis result
         until ReadToken(stream, tempString) eq $} do loop
         ] repeat
      ]

   switchon result into
      [
      case ok:
         endcase
      case ignore:
         [ // repeat
         let token = ReadToken(stream, tempString)
         if token eq $*n then
            if ReadToken(stream, tempString) eq $*n then endcase
         if token eq -1 then endcase
         ] repeat
      default:
         Ws("*n")
         SetFilePos(stream, filePos)
            [ // repeat
            let char = Gets(stream)
            if char eq $*n % char eq -1 break
            Puts(dsp, char)
            ] repeat
         test result eq bad
            ifso ReportError("Syntax error in Wire List file.")
            ifnot [ ReportError(result); Free(sysZone, result) ]
         stream>>ST.par1 = 0
         endcase
      ]
   ] repeat

stream>>ST.error = stream>>ST.par2
]

// ---------------------------------------------------------------------------
and ReadToken(stream, string, dontStopOnParen; numargs na) = valof
// ---------------------------------------------------------------------------
// Reads the next token from the input stream.
// If the token is a separator character, returns it.
// If the token is an atom, copies it into string and returns zero.
// If end of file has been reached, returns -1.
// Separator characters are: { } < > , *n
// plus ( ) if dontStopOnParen is false (the default).
// Ignores comments (beginning with ;) and spaces between tokens.
[
let char = stream>>ST.par1
stream>>ST.par1 = 0
let length = 0

   [ // repeat
   switchon char into
      [
      case $*s:
         if length ne 0 then [ char = 0; break ]
         endcase
      case $;:
         char = Gets(stream) repeatuntil char eq $*n % char eq -1
         docase char
      case $(: case $):
         if na ge 3 & dontStopOnParen then docase $A  // docase default
      case $<: case $>:
      case ${: case $}:
      case $,: case $*n: case -1:
         if length eq 0 break
         stream>>ST.par1 = char
         char = 0
         break
      default:
         length = length+1
         string>>String.char↑length = char
      case 0:
         endcase
      ]
   char = Gets(stream)
   ] repeat

string>>String.length = length
resultis char
]

// ---------------------------------------------------------------------------
and ReadTokenError(stream, ec) =
// ---------------------------------------------------------------------------
   ec eq ecEof? -1, stream>>ST.par2(stream, ec)

// ---------------------------------------------------------------------------
and ReadNumber(stream, lvNumber, radix; numargs na) = valof
// ---------------------------------------------------------------------------
[
if na ls 3 then radix = 10
let tempString = vec 127
if ReadToken(stream, tempString) ne 0 resultis false
resultis GetNumber(tempString, lvNumber, radix)
]

// ---------------------------------------------------------------------------
and GetNumber(string, lvNumber, radix; numargs na) = valof
// ---------------------------------------------------------------------------
// Converts string to a number and stores the result in @lvNumber.
// Returns true normally, false if string wasn't a number.
[
if na ls 3 then radix = 10
let number = 0
for i = 1 to string>>String.length do
   [
   let digit = string>>String.char↑i - $0
   if digit ls 0 % digit ge radix resultis false
   number = 10*number + digit
   ]
@lvNumber = number
resultis true
]

// ---------------------------------------------------------------------------
and UnparseIC(stream, ic) be
// ---------------------------------------------------------------------------
// Unparses ic and sends the result to stream.
[
PutTemplate(stream, "$S ($S, $D, $S)", ObjectsString(ic),
 SymbolsString(ic>>IC.package), ic>>IC.nPins, SymbolsString(ic>>IC.type))
for i = 1 to ic>>IC.nPins do
   [
   Wss(stream, (i eq 1? " ", ", "))
   let pin = lv ic>>IC.pins↑i
   switchon pin>>Pin.expectedOhms into
      [
      case open:
         Puts(stream, $$); endcase
      case unknown:
         Puts(stream, $?); endcase
      default:
         PutTemplate(stream, "$D", pin>>Pin.expectedOhms); endcase
      ]
   ]
Puts(stream, $*n)
]