summaryrefslogtreecommitdiff
path: root/src/common/typespec.icn
blob: f86ba9aa483bf4fd770ece28c4076111a8510251 (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
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
#
# typespec - transform Icon type specifications into C tables.
#    Specifications are read from standard input; tables are written
#    to standard output.
#
#  The grammar for the a type specifcation is:
#
#    <type-def>      ::= <identifier> <opt-abrv> : <kind> <opt-return>
#  
#    <kind>          ::= simple |
#                        aggregate(<component>, ... ) |
#                        variable <var-type-spec>
#  
#    <component>     ::= var <identifier> <opt-abrv> |
#                            <identifier>
#  
#    <var-type-spec> ::= initially <type> |
#                        always <type>
#  
#    <type>          ::= <type-name> | <type> ++ <type-name>
#  
#    <opt-abrv>      ::= <nil> |
#                        { <identifier> }
#  
#    <opt-return>    ::= <nil> |
#                        return block_pointer |
#                        return descriptor_pointer |
#                        return char_pointer |
#                        return C_integer

# Information about an Icon type.
#
record icon_type(
   id,          # name of type
   support_new, # supports RTL "new" construct
   deref,       # dereferencing needs
   rtl_ret,     # kind of RTL return supported if any
   typ,         # for variable: initial type
   num_comps,   # for aggregate: number of type components
   compnts,     # for aggregate: index of first component
   abrv)        # abreviation used for type tracing

# Information about a component of an aggregate type.
#
record typ_compnt (
   id,        # name of component
   n,         # position of component within type aggragate
   var,       # flag: this component is an Icon-level variable
   aggregate, # index of type that owns the component
   abrv)      # abreviation used for type tracing

record token(kind, image)

global icontypes, typecompnt, type_indx, compnt_indx
global lex, line_num, saved_token, error_msg, prog_name

procedure main()
   local typ, tok, compnt, indx, x

   prog_name := "typespec"
   lex := create tokenize_input()

   icontypes := []
   typecompnt := []

   #
   # Read each of the type specifications
   #
   while typ := icon_type(ident("may be EOF")) do {
      #
      # Check for abreviation
      #
      typ.abrv := opt_abrv(typ.id)

      if next_token().kind ~== ":" then
          input_err("expected ':'")

      #
      # See what kind of type this is
      #
      case ident() of {
         "simple": {
            typ.support_new := "0"
            typ.deref := "DrfNone"
            typ.num_comps := "0"
            typ.compnts := "0"
            }

         "aggregate": {
            typ.support_new := "1"
            typ.deref := "DrfNone"

            #
            # get the component names for the type
            #
            typ.compnts := *typecompnt
            if next_token().kind ~== "(" then
               input_err("expected '('")
            typ.num_comps := 0
            tok := next_token()
            if tok.kind ~== "id" then
               input_err("expected type component")
            while tok.kind ~== ")" do {
               #
               # See if this component is an Icon variable.
               #
               if tok.image == "var" then {
                  compnt := typ_compnt(ident(), typ.num_comps, "1", *icontypes)
                  compnt.abrv := opt_abrv(compnt.id)
                  }
               else
                  compnt := typ_compnt(tok.image, typ.num_comps, "0",
                     *icontypes)

               put(typecompnt, compnt)
               typ.num_comps +:= 1

               tok := next_token()
               if tok.kind == "," then {
                  tok := next_token()
                  if tok.kind ~== "id" then
                     input_err("expected type component")
                  }
               else if tok.kind ~== ")" then
                  input_err("expected type component")
               }
            }

         "variable": {
            typ.support_new := "0"
            typ.num_comps := "0"
            typ.compnts := "0"
            case ident() of {
                "initially":
                   typ.deref := "DrfGlbl"
                "always":
                   typ.deref :=  "DrfCnst"
                default:
                  input_err("expected 'initially' or 'always'")
               }

            #
            # Get the initial type associated with the variable
            #
            typ.typ := [ident()]
            tok := &null
            while (tok := next_token("may be EOF")).kind == "++" do {
                put(typ.typ, ident())
                tok := &null
                }
            saved_token := tok  # put token back
            }
         default:
            input_err("expected 'simple', 'aggregate', or 'variable'")
         }

      #
      # Check for an optional return clause
      #
      tok := &null
      if (tok := next_token("may be EOF")).image == "return" then {
         case next_token().image of {
            "block_pointer":
               typ.rtl_ret := "TRetBlkP"
            "descriptor_pointer":
               typ.rtl_ret := "TRetDescP"
            "char_pointer":
               typ.rtl_ret := "TRetCharP"
            "C_integer":
               typ.rtl_ret := "TRetCInt"
            default:
               input_err("expected vword type")
            }
         }
      else {
         typ.rtl_ret := "TRetNone"
         saved_token := tok    # put token back
         }

      put(icontypes, typ)
      }

   #
   # Create tables of type and compontent indexes.
   #
   type_indx := table()
   indx := -1
   every type_indx[(!icontypes).id] := (indx +:= 1)
   compnt_indx := table()
   indx := -1
   every compnt_indx[(!typecompnt).id] := (indx +:= 1)

   write("/*")
   write(" * This file was generated by the program ", prog_name, ".")
   write(" */")
   write()

   #
   # Locate the indexes of types with special semantics or which are
   #  explicitly needed by iconc. Output the indexes as assignments to
   #  variables.
   #
   indx := req_type("string")
   icontypes[indx + 1].rtl_ret := "TRetSpcl"
   write("int str_typ = ", indx, ";")

   indx := req_type("integer")
   write("int int_typ = ", indx, ";")

   indx := req_type("record")
   write("int rec_typ = ", indx, ";")

   indx := req_type("proc")
   write("int proc_typ = ", indx, ";")

   indx := req_type("coexpr")
   write("int coexp_typ = ", indx, ";")

   indx := req_type("tvsubs")
   icontypes[indx + 1].deref := "DrfSpcl"
   icontypes[indx + 1].rtl_ret := "TRetSpcl"
   write("int stv_typ = ", indx, ";")

   indx := req_type("tvtbl")
   icontypes[indx + 1].deref := "DrfSpcl"
   write("int ttv_typ = ", indx, ";")

   indx := req_type("null")
   write("int null_typ = ", indx, ";")

   indx := req_type("cset")
   write("int cset_typ = ", indx, ";")

   indx := req_type("real")
   write("int real_typ = ", indx, ";")

   indx := req_type("list")
   write("int list_typ = ", indx, ";")

   indx := req_type("table")
   write("int tbl_typ = ", indx, ";")

   #
   # Output the type table.
   #
   write()
   write("int num_typs = ", *icontypes, ";")
   write("struct icon_type icontypes[", *icontypes, "] = {")
   x := copy(icontypes)
   output_typ(get(x))
   while typ := get(x) do {
      write(",")
      output_typ(typ)
      }
   write("};")

   #
   # Locate the indexes of components which are explicitly needed by iconc.
   #  Output the indexes as assignments to variables.
   #
   write()
   indx := req_compnt("str_var")
   write("int str_var = ", indx, ";")

   indx := req_compnt("trpd_tbl")
   write("int trpd_tbl = ", indx, ";")

   indx := req_compnt("lst_elem")
   write("int lst_elem = ", indx, ";")

   indx := req_compnt("tbl_dflt")
   write("int tbl_dflt = ", indx, ";")

   indx := req_compnt("tbl_val")
   write("int tbl_val = ", indx, ";")

   #
   # Output the component table.
   #
   write()
   write("int num_cmpnts = ", *typecompnt, ";")
   write("struct typ_compnt typecompnt[", *typecompnt, "] = {")
   output_compnt(get(typecompnt))
   while compnt := get(typecompnt) do {
      write(",")
      output_compnt(compnt)
      }
   write("};")
end

#
# ident - insure that next token is an identifier and return its image
#
procedure ident(may_be_eof)
   local tok  

   tok := next_token(may_be_eof) | fail

   if tok.kind == "id" then
      return tok.image
   else 
      input_err("expected identifier")
end

#
# opt_abrv - look for an optional abreviation. If there is none, return the
#   default value supplied by the caller.
#
procedure opt_abrv(abrv)
   local tok

   tok := next_token("may be EOF")
   if tok.kind == "{" then {
      abrv := ident()
      if next_token().kind ~== "}" then
          input_err("expected '}'")
      }
   else
      saved_token := tok   # put token back

   return abrv
end

#
# next_token - get the next token, looking to see if one was put back.
#
procedure next_token(may_be_eof)
   local tok

   if \saved_token then {
      tok := saved_token
      saved_token := &null
      return tok
      }
   else if tok := @lex then
      return tok
   else if \may_be_eof then
      fail
   else {
      write(&errout, prog_name, ", unexpected EOF")
      exit(1)
      }
end

#
# req_type - get the index of a required type.
#
procedure req_type(id)
   local indx

   if indx := \type_indx[id] then
      return indx
   else {
      write(&errout, prog_name, ", the type ", id, " is required")
      exit(1)
      }
end

#
# req_compnt - get the index of a required component.
#
procedure req_compnt(id)
   local indx

   if indx := \compnt_indx[id] then
      return indx
   else {
      write(&errout, prog_name, ", the component ", id, " is required")
      exit(1)
      }
end

#
# output_typ - output the table entry for a type.
#
procedure output_typ(typ)
   local typ_str, s, indx

   writes("  {", image(typ.id), ", ", typ.support_new, ", ", typ.deref, ", ",
      typ.rtl_ret, ", ")
   if \typ.typ then {
      typ_str := repl(".", *type_indx)
      every s := !typ.typ do {
         if s == "any_value" then {
            every indx := 1 to *icontypes do {
               if icontypes[indx].deref == "DrfNone" then
                  typ_str[indx] := icontypes[indx].abrv[1]
               }
            }
         else if indx := \type_indx[s] + 1 then
            typ_str[indx] := icontypes[indx].abrv[1]
         else {
            write(&errout, prog_name, ", the specification for ", typ.id,
               " contains an illegal type: ", s)
            exit(1)
            }
         }
      writes(image(typ_str))
      }
   else
      writes("NULL")
   writes(", ", typ.num_comps, ", ", typ.compnts, ", ", image(typ.abrv), ", ")
   writes(image(map(typ.id[1], &lcase, &ucase) || typ.id[2:0]), "}")
end

#
# output_compnt - output the table entry for a component.
#
procedure output_compnt(compnt)
   writes("  {", image(compnt.id), ", ", compnt.n, ", ", compnt.var, ", ",
      compnt.aggregate, ", ", image(\compnt.abrv) | "NULL", "}")
end

#
# input_err - signal the lexical anaylser to print an error message about
#   the last token
#
procedure input_err(msg)
   error_msg := msg
   @lex
end

#
# tokenize_input - transform standard input into tokens and suspend them
#
procedure tokenize_input()
   local line

   line_num := 0
   while line := read() do {
      line_num +:= 1
      suspend line ? tokenize_line()
      }
   fail
end

#
# tokenize_line - transform the subject of string scanning into tokens and
#   suspend them
#
procedure tokenize_line()
   local s, tok, save_pos
   static id_chars

   initial id_chars := &letters ++ &digits ++ '_'

   repeat {
      tab(many(' \t'))        # skip white space
      if ="#" | pos(0) then
         fail                 # end of input on this line

      save_pos := &pos

      if any(&letters) then
         tok := token("id", tab(many(id_chars)))
      else if s := =(":" | "(" | "," | ")" | "++" | "{" | "}") then
         tok := token(s, s)
      else
         err("unknown symbol")

      suspend tok
      err(\error_msg, save_pos)   # was the last token erroneous?
      }
end

#
# err - print an error message about the current string being scanned
#
procedure err(msg, save_pos)
   local s, strt_msg

   tab(\save_pos)    # error occured here

   strt_msg := prog_name || ", " || msg || "; line " || line_num || ": "
   (s := image(tab(1))) & &fail      # get front of line then undo tab
   strt_msg ||:= s[1:-1]             # strip ending quote from image
   s := image(tab(0))                # get end of line
   s := s[2:0]                       # strip first quote from image
   write(&errout, strt_msg, s)
   write(&errout, repl(" ", *strt_msg), "^")  # show location of error
   exit(1)
end