summaryrefslogtreecommitdiff
path: root/ipl/packs/euler/xcode.icn
blob: c8def5fc6195f9b8cea50d26cb4791500ee090af (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
############################################################################
#
#	File:     xcode.icn
#
#	Subject:  Procedures to save and restore Icon data
#
#	Author:   Bob Alexander
#
#	Date:     January 1, 1996
#
############################################################################
#
#	Contributor:  Ralph E. Griswold
#
############################################################################
#
#  Description
#  -----------
#
#     These procedures provide a way of storing Icon values in files
#  and retrieving them.  The procedure xencode(x,f) stores x in file f
#  such that it can be converted back to x by xdecode(f).  These
#  procedures handle several kinds of values, including structures of
#  arbitrary complexity and even loops.  The following sequence will
#  output x and recreate it as y:
#
#	f := open("xstore","w")
#	xencode(x,f)
#	close(f)
#	f := open("xstore")
#	y := xdecode(f)
#	close(f)
#
#  For "scalar" types -- null, integer, real, cset, and string, the
#  above sequence will result in the relationship
#
#	x === y
#
#     For structured types -- list, set, table, and record types --
#  y is, for course, not identical to x, but it has the same "shape" and
#  its elements bear the same relation to the original as if they were
#  encoded and decoded individually.
#
#     Files, co-expressions, and windows cannot generally be restored in any
#  way that makes much sense.  These objects are restored as empty lists so
#  that (1) they will be unique objects and (2) will likely generate
#  run-time errors if they are (probably erroneously) used in
#  computation.  However, the special files &input, &output, and &errout are
#  restored.
#
#     Not much can be done with functions and procedures, except to preserve
#  type and identification.
#
#     The encoding of strings and csets handles all characters in a way
#  that it is safe to write the encoding to a file and read it back.
#
#     xdecode() fails if given a file that is not in xcode format or it
#  the encoded file contains a record for which there is no declaration
#  in the program in which the decoding is done.  Of course, if a record
#  is declared differently in the encoding and decoding programs, the
#  decoding may be bogus.
#
#     xencoden() and xdecoden() perform the same operations, except
#  xencoden() and xdecoden() take the name of a file, not a file.
#
############################################################################
#
#  Complete calling sequences
#  --------------------------
#
#	xencode(x, f, p) # returns f
#
#	where
#
#		x is the object to encode
#
#		f is the file to write (default &output)
#
#		p is a procedure that writes a line on f using the
#		  same interface as write() (the first parameter is
#		  always a the value passed as "file") (default: write)
#
#
#	xencode(f, p) # returns the restored object
#
#	where
#
#		f is the file to read (default &input)
#
#		p is a procedure that reads a line from f using the
#		  same interface as read() (the parameter is
#		  always a the value passed as "file") (default: read)
#
#
#  The "p" parameter is not normally used for storage in text files, but
#  it provides the flexibility to store the data in other ways, such as
#  a string in memory.  If "p" is provided, then "f" can be any
#  arbitrary data object -- it need not be a file.
#
#  For example, to "write" x to an Icon string:
#
#	record StringFile(s)
#
#	procedure main()
#	   ...
#	   encodeString := xencode(x,StringFile(""),WriteString).s
#	   ...
#	end
#
#	procedure WriteString(f,s[])
#	  every f.s ||:= !s
#	  f.s ||:= "\n"
#	  return
#	end
#
############################################################################
#
#  Notes on the encoding
#  ---------------------
#
#     Values are encoded as a sequence of one or more lines written to
#  a plain text file.  The first or only line of a value begins with a
#  single character that unambiguously indicates its type.  The
#  remainder of the line, for some types, contains additional value
#  information.  Then, for some types, additional lines follow
#  consisting of additional object encodings that further specify the
#  object.  The null value is a special case consisting of an empty
#  line.
#
#     Each object other than &null is assigned an integer tag as it is
#  encoded.  The tag is not, however, written to the output file.  On
#  input, tags are assigned in the same order as objects are decoded, so
#  each restored object is associated with the same integer tag as it
#  was when being written.  In encoding, any recurrence of an object is
#  represented by the original object's tag.  Tag references are
#  represented as integers, and are easily recognized since no object's
#  representation begins with a digit.
#
#     Where a structure contains elements, the encodings of the
#  elements follow the structure's specification on following lines.
#  Note that the form of the encoding contains the information needed to
#  separate consecutive elements.
#
#     Here are some examples of values and their encodings:
#
#       x                     encode(x)
#  -------------------------------------------------------
#
#       1                     N1
#       2.0                   N2.0
#       &null                 
#       "\377"                "\377"
#       '\376\377'            '\376\377'
#       procedure main        p
#                             "main"
#       co-expression #1 (0)  C
#       []                    L
#                             N0
#       set()                 "S"
#                             N0
#       table("a")            T
#                             N0
#                             "a"
#       ["hi","there"]        L
#                             N2
#                             "hi"
#                             "there"
#
#  A loop is illustrated by
#
#       L2 := []
#       put(L2,L2)
#
#  for which
#
#       x                     encode(x)
#  -------------------------------------------------------
#
#       L2                    L
#                             N1
#                             2
#
#  The "2" on the third line is a tag referring to the list L2.  The tag
#  ordering specifies that an object is tagged *after* its describing
#  objects, thus the list L2 has the tag 2 (the integer 1 has tag 1).
#
#     Of course, you don't have to know all this to use xencode and
#  xdecode.
#
############################################################################
#
#  Links:  escape
#
############################################################################
#
#  See also:  object.icn, codeobj.icn
#
############################################################################

invocable all

link escape

record xcode_rec(file,ioProc,done,nextTag)

procedure xencode(x,file,writeProc)	#: write structure to file

   /file := &output
   return xencode_1(
      xcode_rec(
	 file,
	 (\writeProc | write) \ 1,
	 table(),
	 0),
      x)
end

procedure xencode_1(data,x)
   local tp,wr,f,im
   wr := data.ioProc
   f := data.file
   #
   #  Special case for &null.
   #
   if /x then {
      wr(f)
      return f
      }
   #
   #  If this object has already been output, just write its tag.
   #
   if tp := \data.done[\x] then {
      wr(f,tp)
      return f
      }
   #
   #  Check to see if it's a "distinguished" that is represented by
   #  a keyword (special files and csets).  If so, just use the keyword
   #  in the output.
   #
   im := image(x)
   if match("integer(", im) then im := string(x)
   else if match("&",im) then {
      wr(f,im)
      data.done[x] := data.nextTag +:= 1
      return f
      }
   #
   #  Determine the type and handle accordingly.
   #
   tp := case type(x) of {
     "cset" | "string": ""
     "file" | "window": "f"
     "integer" | "real": "N"
     "co-expression": "C"
     "procedure": "p"
     "external": "E"
     "list": "L"
     "set": "S"
     "table": "T"
     default: "R"
   }
   case tp of {
      #
      #  String, cset, or numeric outputs its string followed by its
      #  image.
      #
      "" | "N": wr(f,tp,im)
      #
      #  Procedure writes "p" followed (on subsequent line) by its name
      #  as a string object.
      #
      "p": {
	 wr(f,tp)
	 im ? {
	    while tab(find(" ") + 1)
	    xencode_1(data,tab(0))
	    }
	 }
      #
      #  Co-expression, file, or external just outputs its letter.
      #
      !"CEf": wr(f,tp)
      #
      #  Structured type outputs its letter followed (on subsequent
      #  lines) by additional data.  A record writes its type as a
      #  string object; other type writes its size as an integer object.
      #  Structure elements follow on subsequent lines (alternating keys
      #  and values for tables).
      #
      default: {
	 wr(f,tp)
	 case tp of {
	    !"LST": {
	       im ? {
		  tab(find("(") + 1)
		  xencode_1(data,integer(tab(-1)))
		  }
	       if tp == "T" then xencode_1(data,x[[]])
	       }
	    default: xencode_1(data,type(x))
	    }
	 #
	 #  Create the tag.  It's important that the tag is assigned
	 #  *after* other other objects that describe this object (e.g.
	 #  the length of a list) are output (and tagged), but *before*
	 #  the structure elements; otherwise decoding would be
	 #  difficult.
	 #
	 data.done[x] := data.nextTag +:= 1
	 #
	 #  Output the elements of the structure.
	 #
	 every xencode_1(data,
	       !case tp of {"S": sort(x); "T": sort(x,3); default: x})
	 }
      }
   #
   #  Tag the object if it's not already tagged.
   #
   /data.done[x] := data.nextTag +:= 1
   return f
end

procedure xdecode(file,readProc)	#: read structure from file

   /file := &input

   return xdecode_1(
      xcode_rec(
	 file,
	 (\readProc | read) \ 1,
	 []))
end

#  This procedure fails if it encounters bad data

procedure xdecode_1(data)
   local x,tp,sz, i
   data.ioProc(data.file) ? {
      if any(&digits) then {
	 #
	 #  It's a tag -- return its value from the object table.
	 #
	 return data.done[tab(0)]
	 }
      if tp := move(1) then {
	 x := case tp of {
	    "N": numeric(tab(0))
	    "\"": escape(tab(-1))
	    "'": cset(escape(tab(-1)))
	    "p": proc(xdecode_1(data)) | fail
	    "L": list(xdecode_1(data)) | fail
	    "S": {sz := xdecode_1(data) | fail; set()}
	    "T": {sz := xdecode_1(data) | fail; table(xdecode_1(data)) | fail}
	    "R": proc(xdecode_1(data))() | fail
	    "&": case tab(0) of {
	       #
	       #  Special csets.
	       #
	       "cset":		&cset
	       "ascii":		&ascii
	       "digits":	&digits
	       "letters":	&letters
	       "lcase":		&lcase
	       "ucase":		&ucase
	       #
	       #  Special files.
	       #
	       "input":		&input
	       "output":	&output
	       "errout":	&errout
	       default:		[] # so it won't crash if new keywords arise
	       }
            "f" | "C": []	# unique object for things that can't
				# be restored.
	    default: fail
	    }
	 put(data.done,x)
	 case tp of {
	    !"LR": every i := 1 to *x do
              x[i] := xdecode_1(data) | fail
	    "T": every 1 to sz do
               insert(x,xdecode_1(data),xdecode_1(data)) | fail
	    "S": every 1 to sz do
               insert(x,xdecode_1(data)) | fail
	    }
	 return x
	 }
      else return
      }

end

procedure xencoden(x, name, opt)
   local output

   /opt := "w"

   output := open(name, opt) | stop("*** xencoden(): cannot open ", name)
   xencode(x, output)
   close(output)

   return

end

procedure xdecoden(name)
   local input, x

   input := open(name) | stop("*** xdecoden(): cannot open ", name)
   if x := xdecode(input) then {
      close(input)
      return x
      }
   else {
      close(input)
      fail
      }

end