############################################################################
#
#	Name:	skin.icn
#
#	Title:	Scheme in Icon
#
#	Author: Bob Alexander
#
#	Date:	February 19, 1995
#
#	Description: see skeem.icn
#
############################################################################

#
# skeem -- Scheme in Icon
#
# Input utility procedures
#

global BackToken

#
# ReadAllExprs() - Generate expressions from file f
#
procedure ReadAllExprs(f)
   "" ? (suspend |ScanExpr(FileRec(f)))
end

#
# ReadOneExpr() - Read one expression from f.
#
procedure ReadOneExpr(f)
   local result,fRec
   "" ? {
      result := ScanExpr(fRec := FileRec(f))
      seek(f,fRec.where + &pos - 1)
      }
   return result
end

#
# StringToExpr() - Generate expressions from string s
#
procedure StringToExpr(s)
   s ? (suspend |ScanExpr())
end

procedure ScanExpr(f)
   local token
   return case token := ScanToken(f) | fail of {
      "(": ScanList(f)
      "#(": ScanVector(f)
      !"'`," | ",@": ScanQuote(f,token)
      default:
	 if type(token) == "Symbol" then token.string
	 else token
      }
end

procedure ScanList(f)
   local result,token,dot
   result := LLNull
   while (token := ScanToken(f)) ~=== ")" do {
      if token === "." then {
	 dot := ScanExpr(f)
	 }
      else {
	 BackToken := token
	 result := LLPair(ScanExpr(f),result)
	 }
      }
   return LLInvert(result,dot)
end

procedure ScanVector(f)
   local result,token
   result := []
   while (token := ScanToken(f)) ~=== ")" do {
      BackToken := token
      put(result,ScanExpr(f))
      }
   return result
end

procedure ScanQuote(f,token)
   return LList(
      case token of {
	 "'": "QUOTE"
	 "`": "QUASIQUOTE"
	 ",": "UNQUOTE"
	 ",@": "UNQUOTE-SPLICING"
	 },
      ScanExpr(f))
end

procedure ScanToken(f)
   return 1(\.BackToken,BackToken := &null) | {
      #
      # Skip over leading white space (including comments, possibly
      # spanning lines).
      #
      #showscan("before space")
      while {
	 tab(many(Space)) |
	 (if pos(0) then &subject := ReadFileRec(\f)) |
	 (if =";" then tab(0)) |
	 (if ="#|" then {
	    until tab(find("|#") + 2) do &subject := ReadFileRec(\f) | fail
	    &null
	    })
	 }
      #showscan("after space")
      #
      # Scan then token.
      #
      ScanSymbol() | ScanNumber() | ScanSpecial() | ScanString() |
	    ScanChar() | ScanBoolean() | move(1)
      }
end

procedure ScanSymbol()
   static symFirst,symRest,nonSym
   initial {
      symFirst := &letters ++ '!$%&*/:<=>?~_^'
      symRest := symFirst ++ &digits ++ '.+-'
      nonSym := ~symRest
      }
   return Symbol(
      (match("|"),escape(quotedstring("|")[2:-1])) |
      map(1((tab(any(symFirst)) || (tab(many(symRest)) | "") |
	    =("+" | "-" | "...")),
	 (any(nonSym) | pos(0))),&lcase,&ucase))
end

procedure ScanNumber()
   local nbr
   static nbrFirst,nbrRest
   initial {
      nbrFirst := &digits ++ 'eE.'
      nbrRest := nbrFirst ++ &letters ++ '#+-'
      }
   (nbr := ((tab(any('+-')) | "") || tab(any(nbrFirst))  |
      ="#" || tab(any('bodxeiBODXEI'))) || (tab(many(nbrRest)) | "") &
      nbr ~== ".") | fail
   return StringToNumber(nbr) |
      Error("READER","bad number: ",image(nbr))
end

procedure StringToNumber(nbr,radix)
   local exact,sign,number,c
   radix := if \radix ~= 10 then radix || "r" else ""
   sign := ""
   exact := 1
   map(nbr) ? return {
      while ="#" do case move(1) of {
	 "b": radix := "2r"
	 "o": radix := "8r"
	 "d": radix := ""
	 "x": radix := "16r"
	 "e": exact := Round
	 "i": exact := real
	 default: &null # this case prevents the expression from failing
	 }
      sign := tab(any('+-'))
      number := ""
      while number ||:= tab(upto('#sfdl')) do {
	 c := move(1)
	 number ||:=
	    if c == "#" then {
	       if exact === 1 then exact := real
	       "0"
	       }
	    else "e"
	 }
      number ||:= tab(0)
      #write(&errout,"+++++ exact = ",image(exact),
      #  "; radix = ",image(radix),"; sign = ",image(sign),
      #  "; number = ",image(number))
      exact(numeric(sign || radix || number))
      }
end

procedure ScanSpecial()
   return =("#(" | ",@" | !"()'`,") |
      (="#<",Error("READER","unreadable object #<",tab(find(">") + 1 | 0)),F)
end

procedure ScanBoolean()
   return (="#",(=!"fF",F) | (=!"tT",T))
end

procedure ScanString()
   return String((match("\""),escape(quotedstring()[2:-1])))
end

procedure ScanChar()
   local chName
   return Char((="#\\",
      (case map(1(chName := tab(many(&letters)),*chName > 1)) of {
	 "space": " "
	 "tab": "\t"
	 "newline": "\n"
	 "backspace": "\b"
	 "delete": "\d"
	 "escape": "\e"
	 "formfeed": "\f"
	 "return": "\r"
	 "verticaltab": "\v"
	 default: Error("READER","unknown character name")
	 }) | move(1)))
end

record FileRec(file,where)

procedure ReadFileRec(f)
   local line
   static doPrompt
   initial  doPrompt := if find("MPW",&host) then &null else "true"
   f.where := where(f.file)
   if f.file === &input then {
      if \doPrompt then
	 writes(if BreakLevel = 0 then "> " else "[" || BreakLevel || "] ")
      line := read() | fail
##	 line ? {
##	    if =">" | (="[" || tab(find("]") + 1)) then
##	       \f.where +:= &pos - 1
##	    line := tab(0)
##	    }
      return line
      }
   else return read(f.file)
end
