#
# $Id: decode.icn,v 1.1 2004/02/12 17:07:55 rparlett Exp $
#
# This file is in the public domain.
#
# Author: Robert Parlett (parlett@dial.pipex.com)
#

package lang

import util

#
# Recreate an object from the encoded string.
#
procedure decode(s)
   local d
   d := Decode(s)
   return d.decode()
end

#
# This class is used to decode a string created by the encode process, and
# reproduce the object.
#
# Classes to be decoded must subclass ClassCoding, which allows specification
# of how to recreate the class instance.
#
class Decode : Object(
   tag_count,
   seen,
   buff
   )

   method decode_string(s)
      local buff
      static esc
      initial
         esc := ~'\\'

      buff := StringBuff()
      s ? {
         repeat {
            buff.add(tab(many(esc)))
            if pos(0) then
               return buff.get_string()
            move(1)
            buff.add(char(move(3)))
         }
      }
   end

   method line_in()
      buff ? {
         if s := tab(upto('|')) then {
            move(1)
            buff := tab(0)
            return s
         }
      }
   end

   method decode_record()
      (rname := line_in() &
       n := integer(line_in())) | fail
      res := proc(rname)()
      seen[tag_count +:= 1] := res
      every i := 1 to n do
         res[i] := decode() | fail
      
      return res
   end

   method decode()
      local i, p, n, rname

      t := line_in() | fail

      if i := integer(t) then
         return \seen[i]

      case t of {
         "null" :
            return

         "procedure" :
            return proc(line_in())

         "record" :
            return decode_record()

         "class" :
            return decode_class()

         "string" :
            return decode_string(line_in())

         "integer" :
            return integer(line_in())

         "real" :
            return real(line_in())

         "cset" :
            return cset(decode_string(line_in()))

         "list" : {
            n := integer(line_in()) | fail
            res := []
            seen[tag_count +:= 1] := res
            every 1 to n do 
               put(res, decode()) | fail
            return res
         }

         "set" : {
            n := integer(line_in()) | fail
            res := set([])
            seen[tag_count +:= 1] := res
            every 1 to n do 
               insert(res, decode()) | fail
            return res
         }

         "table" : {
            def := decode() | fail
            res := table(def)
            n := integer(line_in()) | fail
            seen[tag_count +:= 1] := res
            every 1 to n do {
               (key := decode() &
               val := decode()) | fail
               res[key] := val
            }
            return res
         }            

         default :
            return []
      }            
   end

   method decode_class()
      local res, cname, n, e, t, m, v, f

      cname := line_in() | fail

      #
      # Create an instance
      #
      p := proc(cname) | fail
      res := p()
      seen[tag_count +:= 1] := res

      res.decode_obj(self) | fail
      res.post_decode()

      return res
   end

   initially(s)
      tag_count := 0
      seen := table()
      buff := s
end