1MODULE OPC;  (* copyright (c) J. Templ 12.7.95 / 3.7.96 *)
2(* C source code generator version
3
4  30.4.2000 jt, synchronized with BlackBox version, in particular
5    various promotion rules changed (long) => (LONGINT), xxxL avoided
6*)
7
8  IMPORT OPT, OPM, Configuration, SYSTEM;
9
10  CONST demoVersion = FALSE;
11
12
13  CONST
14    UndefinedType  = 0; (* named type not yet defined *)
15    ProcessingType = 1; (* pointer type is being processed *)
16    PredefinedType = 2; (* for all predefined types *)
17
18    DefinedInHdr = 3+OPM.HeaderFile; (* named type has been defined in header file *)
19    DefinedInBdy = 3+OPM.BodyFile;   (* named type has been defined in body file *)
20
21    BasicIncludeFile = "SYSTEM";
22
23    Export      = "export ";  (* particularily introduced for VC++ declspec() *)
24    Extern      = "import ";  (* particularily introduced for VC++ declspec() *)
25    LocalScope  = "_s";       (* name of a local intermediate scope (variable name) *)
26    GlobalScope = "_s";       (* pointer to current scope extension *)
27    LinkName    = "lnk";      (* pointer to previous scope field *)
28    FlagExt     = "__h";
29    LenExt      = "__len";
30    DynTypExt   = "__typ";
31    TagExt      = "__typ";
32    Tab         = 9X;
33
34    (* The following are defined as hex to avoid confusing editor syntax highlighting *)
35    Backslash   = 5CX;
36    DoubleQuote = 22X;
37
38
39  VAR
40    indentLevel: INTEGER;
41    hashtab:     ARRAY 105 OF SHORTINT;
42    keytab:      ARRAY 50, 9 OF CHAR;
43    GlbPtrs:     BOOLEAN;
44    BodyNameExt: ARRAY 13 OF CHAR;
45
46
47  PROCEDURE Init*;
48  BEGIN
49    indentLevel := 0;
50    BodyNameExt := "__init(void)"
51  END Init;
52
53  PROCEDURE Indent* (count: INTEGER);
54  BEGIN INC(indentLevel, count)
55  END Indent;
56
57  PROCEDURE BegStat*;
58    VAR i: INTEGER;
59  BEGIN i := indentLevel;
60    WHILE i > 0 DO OPM.Write(Tab); DEC (i) END
61  END BegStat;
62
63  PROCEDURE EndStat*;
64  BEGIN OPM.Write(';'); OPM.WriteLn
65  END EndStat;
66
67  PROCEDURE BegBlk*;
68  BEGIN OPM.Write('{'); OPM.WriteLn; INC(indentLevel)
69  END BegBlk;
70
71  PROCEDURE EndBlk*;
72  BEGIN DEC(indentLevel); BegStat; OPM.Write('}'); OPM.WriteLn
73  END EndBlk;
74
75  PROCEDURE EndBlk0*;
76  BEGIN DEC(indentLevel); BegStat; OPM.Write('}')
77  END EndBlk0;
78
79  PROCEDURE Str1(s: ARRAY OF CHAR; x: LONGINT);
80    VAR ch: CHAR; i: INTEGER;
81  BEGIN ch := s[0]; i := 0;
82    WHILE ch # 0X DO
83      IF ch = "#" THEN OPM.WriteInt(x)
84      ELSE OPM.Write(ch);
85      END ;
86      INC(i); ch := s[i]
87    END
88  END Str1;
89
90  PROCEDURE Length(VAR s: ARRAY OF CHAR): INTEGER;
91    VAR i: INTEGER;
92  BEGIN i := 0;
93    WHILE s[i] # 0X DO INC(i) END ;
94    RETURN i
95  END Length;
96
97  PROCEDURE PerfectHash (VAR s: ARRAY OF CHAR): INTEGER;
98    VAR i, h: INTEGER;
99  BEGIN i := 0; h := 0;
100    WHILE (s[i] # 0X) & (i < 5) DO h := 3*h + ORD(s[i]); INC(i) END;
101    RETURN h MOD 105
102  END PerfectHash;
103
104  PROCEDURE Ident* (obj: OPT.Object);
105    VAR mode, level, h: INTEGER;
106  BEGIN
107    mode := obj^.mode; level := obj^.mnolev;
108    IF (mode IN {OPT.Var, OPT.Typ, OPT.LProc}) & (level > 0) OR (mode IN {OPT.Fld, OPT.VarPar}) THEN
109      OPM.WriteStringVar(obj^.name);
110      h := PerfectHash(obj^.name);
111      IF hashtab[h] >= 0 THEN
112        IF keytab[hashtab[h]] = obj^.name THEN OPM.Write('_') END
113      END
114    ELSIF (mode = OPT.Typ) & (obj.typ.form IN {OPT.Int, OPT.Set}) THEN
115      IF obj.typ = OPT.adrtyp THEN OPM.WriteString("ADDRESS")
116      ELSE
117        IF obj.typ.form = OPT.Int THEN OPM.WriteString("INT") ELSE OPM.WriteString("UINT") END;
118        OPM.WriteInt(obj.typ.size*8)
119      END
120    ELSE
121      IF (mode # OPT.Typ) OR (obj^.linkadr # PredefinedType) THEN
122        IF mode = OPT.TProc THEN Ident(obj^.link^.typ^.strobj)
123        ELSIF level < 0 THEN (* use unaliased module name *)
124          OPM.WriteStringVar(OPT.GlbMod[-level].name);
125          IF OPM.currFile = OPM.HeaderFile THEN OPT.GlbMod[-level].vis := 1 (*include in header file*) END ;
126        ELSE OPM.WriteStringVar(OPM.modName)
127        END ;
128        OPM.Write('_')
129      ELSIF (obj = OPT.sysptrtyp^.strobj)
130         OR (obj = OPT.bytetyp^.strobj) THEN
131        OPM.WriteString("SYSTEM_")
132      END;
133      OPM.WriteStringVar(obj^.name);
134    END
135  END Ident;
136
137  PROCEDURE Stars (typ: OPT.Struct; VAR openClause: BOOLEAN);
138    VAR pointers: INTEGER;
139  BEGIN
140    openClause := FALSE;
141    IF ((typ^.strobj = NIL) OR (typ^.strobj^.name = "")) & (typ^.comp # OPT.Record) THEN
142      IF typ^.comp IN {OPT.Array, OPT.DynArr} THEN
143        Stars (typ^.BaseTyp, openClause);
144        openClause := (typ^.comp = OPT.Array)
145      ELSIF typ^.form = OPT.ProcTyp THEN
146        OPM.Write('('); OPM.Write('*')
147      ELSE
148        pointers := 0;
149        (*WHILE (typ^.strobj = NIL) & (typ^.form = OPT.Pointer) DO INC (pointers); typ := typ^.BaseTyp END ;
150        IF (typ^.comp # OPT.DynArr) & (pointers # 0) THEN Stars (typ, openClause) END ;*)
151        WHILE ((typ^.strobj = NIL) OR (typ^.strobj^.name = "")) & (typ^.form = OPT.Pointer) DO
152           INC (pointers); typ := typ^.BaseTyp
153        END ;
154        IF pointers > 0 THEN
155            IF typ^.comp # OPT.DynArr THEN Stars (typ, openClause) END ;
156          IF openClause THEN OPM.Write('('); openClause := FALSE END ;
157          WHILE pointers > 0 DO OPM.Write('*'); DEC (pointers) END
158        END
159      END
160    END
161  END Stars;
162
163  PROCEDURE ^AnsiParamList (obj: OPT.Object; showParamNames: BOOLEAN);
164
165  PROCEDURE DeclareObj(dcl: OPT.Object; scopeDef: BOOLEAN);
166    VAR
167      typ: OPT.Struct;
168      varPar, openClause: BOOLEAN; form, comp: INTEGER;
169  BEGIN
170    typ := dcl^.typ;
171    varPar := ((dcl^.mode = OPT.VarPar) & (typ^.comp # OPT.Array)) OR (typ^.comp = OPT.DynArr) OR scopeDef;
172    Stars(typ, openClause);
173    IF varPar THEN
174      IF openClause THEN OPM.Write('(') END ;
175      OPM.Write('*')
176    END ;
177    IF dcl.name # "" THEN Ident(dcl) END ;
178    IF varPar & openClause THEN OPM.Write(')') END ;
179    openClause := FALSE;
180    LOOP
181      form := typ^.form;
182      comp := typ^.comp;
183      IF ((typ^.strobj # NIL) & (typ^.strobj^.name # "")) OR (form = OPT.NoTyp) OR (comp = OPT.Record) THEN EXIT
184      ELSIF (form = OPT.Pointer) & (typ^.BaseTyp^.comp # OPT.DynArr) THEN
185        openClause := TRUE
186      ELSIF (form = OPT.ProcTyp) OR (comp IN {OPT.Array, OPT.DynArr}) THEN
187        IF openClause THEN OPM.Write(')'); openClause := FALSE END ;
188        IF form = OPT.ProcTyp THEN
189          OPM.Write(")"); AnsiParamList(typ^.link, FALSE);
190          EXIT
191        ELSIF comp = OPT.Array THEN
192          OPM.Write('['); OPM.WriteInt(typ^.n); OPM.Write(']')
193        END
194      ELSE
195        EXIT
196      END ;
197      typ := typ^.BaseTyp
198    END
199  END DeclareObj;
200
201  PROCEDURE Andent*(typ: OPT.Struct);  (* ident of possibly anonymous record type *)
202  BEGIN
203    IF (typ^.strobj = NIL) OR (typ^.align >= 10000H) THEN
204      OPM.WriteStringVar(OPM.modName); Str1("__#", typ^.align DIV 10000H)
205    ELSE Ident(typ^.strobj)
206    END
207  END Andent;
208
209  PROCEDURE Undefined(obj: OPT.Object): BOOLEAN;
210  BEGIN
211    (* imported anonymous types have obj^.name = "";
212       used e.g. for repeating inherited fields *)
213    RETURN (obj^.name = "")
214        OR   (obj^.mnolev >= 0)
215           & (obj^.linkadr # 3+OPM.currFile )
216           & (obj^.linkadr # PredefinedType)
217  END Undefined;
218
219  PROCEDURE ^FieldList (typ: OPT.Struct; last: BOOLEAN; VAR off, n, curAlign: LONGINT);
220
221  PROCEDURE DeclareBase(dcl: OPT.Object); (* declare the specifier of object dcl*)
222    VAR typ, prev: OPT.Struct; obj: OPT.Object; nofdims: INTEGER; off, n, dummy: LONGINT;
223  BEGIN
224    typ := dcl^.typ; prev := typ;
225    WHILE ((typ^.strobj = NIL) OR (typ^.comp = OPT.DynArr) OR Undefined(typ^.strobj))
226        & (typ^.comp # OPT.Record)
227        & (typ^.form # OPT.NoTyp)
228        & ~((typ^.form = OPT.Pointer) & (typ^.BaseTyp^.comp = OPT.DynArr)) DO
229      prev := typ; typ := typ^.BaseTyp;
230    END ;
231    obj := typ^.strobj;
232    IF typ^.form = OPT.NoTyp THEN  (* proper procedure *)
233      OPM.WriteString('void')
234    ELSIF (obj # NIL) & ~Undefined(obj) THEN  (* named type, already declared *)
235      Ident(obj)
236    ELSIF typ^.comp = OPT.Record THEN
237      OPM.WriteString('struct '); Andent(typ);
238      IF (prev.form # OPT.Pointer) & ((obj # NIL) OR (dcl.name = "")) THEN
239        (* named record type not yet declared OR anonymous record with empty name *)
240        IF (typ^.BaseTyp # NIL) & (typ^.BaseTyp^.strobj.vis # OPT.internal) THEN
241          OPM.WriteString(" { /* "); Ident(typ^.BaseTyp^.strobj); OPM.WriteString(" */"); OPM.WriteLn; Indent(1)
242        ELSE OPM.Write(' '); BegBlk
243        END ;
244        FieldList(typ, TRUE, off, n, dummy);
245        EndBlk0
246      END
247    ELSIF (typ^.form = OPT.Pointer) & (typ^.BaseTyp^.comp = OPT.DynArr) THEN
248      typ := typ^.BaseTyp^.BaseTyp; nofdims := 1;
249      WHILE typ^.comp = OPT.DynArr DO INC(nofdims); typ := typ^.BaseTyp END ;
250      OPM.WriteString('struct '); BegBlk;
251      BegStat; Str1("ADDRESS len[#]", nofdims); EndStat;
252      BegStat; NEW(obj); NEW(obj.typ);  (* aux. object for easy declaration *)
253      obj.typ.form := OPT.Comp; obj.typ.comp := OPT.Array; obj.typ.n := 1; obj.typ.BaseTyp := typ; obj.mode := OPT.Fld; obj.name := "data";
254      obj.linkadr := UndefinedType; DeclareBase(obj); OPM.Write(' ');  DeclareObj(obj, FALSE);
255      EndStat; EndBlk0
256    END
257  END DeclareBase;
258
259  PROCEDURE NofPtrs* (typ: OPT.Struct): LONGINT;
260    VAR fld: OPT.Object; btyp: OPT.Struct; n: LONGINT;
261  BEGIN
262    IF (typ^.form = OPT.Pointer) & (typ^.sysflag = 0) THEN RETURN 1
263    ELSIF (typ^.comp = OPT.Record) & (typ^.sysflag MOD 100H = 0) THEN
264      btyp := typ^.BaseTyp;
265      IF btyp # NIL THEN n := NofPtrs(btyp) ELSE n := 0 END ;
266      fld := typ^.link;
267      WHILE (fld # NIL) & (fld^.mode = OPT.Fld) DO
268        IF fld^.name # OPM.HdPtrName THEN n := n + NofPtrs(fld^.typ)
269        ELSE INC(n)
270        END ;
271        fld := fld^.link
272      END ;
273      RETURN n
274    ELSIF typ^.comp = OPT.Array THEN
275      btyp := typ^.BaseTyp; n := typ^.n;
276      WHILE btyp^.comp = OPT.Array DO n := btyp^.n * n; btyp := btyp^.BaseTyp END ;
277      RETURN NofPtrs(btyp) * n
278    ELSE RETURN 0
279    END
280  END NofPtrs;
281
282  PROCEDURE PutPtrOffsets (typ: OPT.Struct; adr: LONGINT; VAR cnt: LONGINT);
283    VAR fld: OPT.Object; btyp: OPT.Struct; n, i: LONGINT;
284  BEGIN
285    IF (typ^.form = OPT.Pointer) & (typ^.sysflag = 0) THEN
286      OPM.WriteInt(adr); OPM.WriteString(", "); INC(cnt);
287      IF cnt MOD 16 = 0 THEN OPM.WriteLn; OPM.Write(Tab) END
288    ELSIF (typ^.comp = OPT.Record) & (typ^.sysflag MOD 100H = 0) THEN
289      btyp := typ^.BaseTyp;
290      IF btyp # NIL THEN PutPtrOffsets(btyp, adr, cnt) END ;
291      fld := typ^.link;
292      WHILE (fld # NIL) & (fld^.mode = OPT.Fld) DO
293        IF fld^.name # OPM.HdPtrName THEN PutPtrOffsets(fld^.typ, adr + fld^.adr, cnt)
294        ELSE
295          OPM.WriteInt(adr + fld^.adr); OPM.WriteString(", "); INC(cnt);
296          IF cnt MOD 16 = 0 THEN OPM.WriteLn; OPM.Write(Tab) END
297        END ;
298        fld := fld^.link
299      END
300    ELSIF typ^.comp = OPT.Array THEN
301      btyp := typ^.BaseTyp; n := typ^.n;
302      WHILE btyp^.comp = OPT.Array DO n := btyp^.n * n; btyp := btyp^.BaseTyp END ;
303      IF NofPtrs(btyp) > 0 THEN i := 0;
304        WHILE i < n DO PutPtrOffsets(btyp, adr + i * btyp^.size, cnt); INC(i) END
305      END
306    END
307  END PutPtrOffsets;
308
309  PROCEDURE InitTProcs(typ, obj: OPT.Object);
310  BEGIN
311    IF obj # NIL THEN
312      InitTProcs(typ, obj^.left);
313      IF obj^.mode = OPT.TProc THEN
314        BegStat;
315        OPM.WriteString("__INITBP(");
316        Ident(typ); OPM.WriteString(', '); Ident(obj);
317        Str1(", #)", obj^.adr DIV 10000H);
318        EndStat
319      END ;
320      InitTProcs(typ, obj^.right)
321    END
322  END InitTProcs;
323
324  PROCEDURE PutBase(typ: OPT.Struct);
325  BEGIN
326    IF typ # NIL THEN
327      PutBase(typ^.BaseTyp);
328      Ident(typ^.strobj); OPM.WriteString(DynTypExt); OPM.WriteString(", ")
329    END
330  END PutBase;
331
332  PROCEDURE LenList(par: OPT.Object; ansiDefine, showParamName: BOOLEAN);
333    VAR typ: OPT.Struct; dim: INTEGER;
334  BEGIN
335    IF showParamName THEN Ident(par); OPM.WriteString(LenExt) END ;
336    dim := 1; typ := par^.typ^.BaseTyp;
337    WHILE typ^.comp = OPT.DynArr DO
338      IF ansiDefine THEN OPM.WriteString(", ADDRESS ") ELSE OPM.WriteString(', ') END ;
339      IF showParamName THEN Ident(par); OPM.WriteString(LenExt); OPM.WriteInt(dim) END ;
340      typ := typ^.BaseTyp; INC(dim)
341    END
342  END LenList;
343
344  PROCEDURE DeclareParams(par: OPT.Object; macro: BOOLEAN);
345  BEGIN
346    OPM.Write('(');
347    WHILE par # NIL DO
348      IF macro THEN OPM.WriteStringVar(par.name)
349      ELSE
350        IF (par^.mode = OPT.Var) & (par^.typ^.form = OPT.Real) THEN OPM.Write("_") END ;
351        Ident(par)
352      END ;
353      IF par^.typ^.comp = OPT.DynArr THEN
354        OPM.WriteString(', '); LenList(par, FALSE, TRUE);
355      ELSIF (par^.mode = OPT.VarPar) & (par^.typ^.comp = OPT.Record) THEN
356        OPM.WriteString(', '); OPM.WriteStringVar(par.name); OPM.WriteString(TagExt)
357      END ;
358      par := par^.link;
359      IF par # NIL THEN OPM.WriteString(', ') END
360    END ;
361    OPM.Write(')')
362  END DeclareParams;
363
364  PROCEDURE ^DefineType(str: OPT.Struct);
365  PROCEDURE ^ProcHeader(proc: OPT.Object; define: BOOLEAN);
366
367  PROCEDURE DefineTProcTypes(obj: OPT.Object);  (* define all types that are used in a OPT.TProc definition *)
368    VAR par: OPT.Object;
369  BEGIN
370    IF obj^.typ # OPT.notyp THEN DefineType(obj^.typ) END ;
371    par := obj^.link; WHILE par # NIL DO DefineType(par^.typ); par := par^.link END
372  END DefineTProcTypes;
373
374  PROCEDURE DeclareTProcs(obj: OPT.Object; VAR empty: BOOLEAN);
375  BEGIN
376    IF obj # NIL THEN
377      DeclareTProcs(obj^.left, empty);
378      IF obj^.mode = OPT.TProc THEN
379        IF obj^.typ # OPT.notyp THEN DefineType(obj^.typ) END ;
380        IF OPM.currFile = OPM.HeaderFile THEN
381          IF obj^.vis = OPT.external THEN
382            DefineTProcTypes(obj);
383            OPM.WriteString(Extern); empty := FALSE;
384            ProcHeader(obj, FALSE)
385          END
386        ELSE empty := FALSE;
387          DefineTProcTypes(obj);
388          IF obj^.vis = OPT.internal THEN OPM.WriteString('static ')
389          ELSE OPM.WriteString(Export)
390          END ;
391          ProcHeader(obj, FALSE)
392        END
393      END ;
394      DeclareTProcs(obj^.right, empty)
395    END
396  END DeclareTProcs;
397
398  PROCEDURE BaseTProc*(obj: OPT.Object): OPT.Object;
399    VAR typ, base: OPT.Struct; mno: LONGINT;
400  BEGIN typ := obj^.link^.typ;  (* receiver type *)
401    IF typ^.form = OPT.Pointer THEN typ := typ^.BaseTyp END ;
402    base := typ^.BaseTyp; mno := obj^.adr DIV 10000H;
403    WHILE (base # NIL) & (mno < base^.n) DO typ := base; base := typ^.BaseTyp END ;
404    OPT.FindField(obj^.name, typ, obj);
405    RETURN obj
406  END BaseTProc;
407
408  PROCEDURE DefineTProcMacros(obj: OPT.Object; VAR empty: BOOLEAN);
409  BEGIN
410    IF obj # NIL THEN
411      DefineTProcMacros(obj^.left, empty);
412      IF (obj^.mode = OPT.TProc) & (obj = BaseTProc(obj)) & ((OPM.currFile # OPM.HeaderFile) OR (obj^.vis = OPT.external)) THEN
413        OPM.WriteString("#define __");
414        Ident(obj);
415        DeclareParams(obj^.link, TRUE);
416        OPM.WriteString(" __SEND(");
417        IF obj^.link^.typ^.form = OPT.Pointer THEN
418          OPM.WriteString("__TYPEOF("); Ident(obj^.link); OPM.Write(")")
419        ELSE Ident(obj^.link); OPM.WriteString(TagExt)
420        END ;
421        Str1(", #, ", obj^.adr DIV 10000H);
422        IF obj^.typ = OPT.notyp THEN OPM.WriteString('void') ELSE Ident(obj^.typ^.strobj) END ;
423        OPM.WriteString("(*)");
424        AnsiParamList(obj^.link, FALSE);
425        OPM.WriteString(", ");
426        DeclareParams(obj^.link, TRUE);
427        OPM.Write(")"); OPM.WriteLn
428      END ;
429      DefineTProcMacros(obj^.right, empty)
430    END
431  END DefineTProcMacros;
432
433  PROCEDURE DefineType(str: OPT.Struct); (* define a type object *)
434    VAR obj, field, par: OPT.Object; empty: BOOLEAN;
435  BEGIN
436    IF (OPM.currFile = OPM.BodyFile) OR (str^.ref < OPM.MaxStruct (*for hidden exports*) ) THEN
437      obj := str^.strobj;
438      IF (obj = NIL) OR Undefined(obj) THEN
439        IF obj # NIL THEN (* check for cycles *)
440          IF obj^.linkadr = ProcessingType THEN
441            IF str^.form # OPT.Pointer THEN OPM.Mark(244, str^.txtpos); obj^.linkadr := PredefinedType END
442          ELSE obj^.linkadr := ProcessingType
443          END
444        END ;
445        IF str^.comp = OPT.Record THEN
446          (* the following exports the base type of an exported type even if the former is non-exported *)
447          IF str^.BaseTyp # NIL THEN DefineType(str^.BaseTyp) END ;
448          field := str^.link;
449          WHILE (field # NIL) & (field^.mode = OPT.Fld) DO
450            IF (field^.vis # OPT.internal) OR (OPM.currFile = OPM.BodyFile) THEN DefineType(field^.typ) END ;
451            field := field^.link
452          END
453        ELSIF str^.form = OPT.Pointer THEN
454          IF str^.BaseTyp^.comp # OPT.Record THEN DefineType(str^.BaseTyp) END
455        ELSIF str^.comp IN {OPT.Array, OPT.DynArr} THEN
456          IF (str^.BaseTyp^.strobj # NIL) & (str^.BaseTyp^.strobj^.linkadr = ProcessingType) THEN (*cyclic base type*)
457            OPM.Mark(244, str^ .txtpos); str^.BaseTyp^.strobj^.linkadr := PredefinedType
458					END ;
459          DefineType(str^.BaseTyp)
460        ELSIF str^.form = OPT.ProcTyp THEN
461          IF str^.BaseTyp # OPT.notyp THEN DefineType(str^.BaseTyp) END ;
462          field := str^.link;
463          WHILE field # NIL DO DefineType(field^.typ); field := field^.link END
464        END
465      END ;
466      IF (obj # NIL) & Undefined(obj) THEN
467        OPM.WriteString("typedef"); OPM.WriteLn; OPM.Write(Tab); Indent(1);
468        obj^.linkadr := ProcessingType;
469        DeclareBase(obj); OPM.Write(' ');
470        obj^.typ^.strobj := NIL; (* SG: trick to make DeclareObj declare the type *)
471        DeclareObj(obj, FALSE);
472        obj^.typ^.strobj := obj; (* SG: revert trick *)
473        obj^.linkadr := 3+OPM.currFile;
474        EndStat; Indent(-1); OPM.WriteLn;
475        IF obj^.typ^.comp = OPT.Record THEN empty := TRUE;
476          DeclareTProcs(str^.link, empty); DefineTProcMacros(str^.link, empty);
477          IF ~empty THEN OPM.WriteLn END
478        END
479      END
480    END
481  END DefineType;
482
483  PROCEDURE Prefixed(x: OPT.ConstExt;  y: ARRAY OF CHAR): BOOLEAN;
484    VAR i: INTEGER;
485  BEGIN i := 0;
486    WHILE x[i+1] = y[i] DO INC(i) END;
487    RETURN y[i] = 0X;
488  END Prefixed;
489
490  PROCEDURE CProcDefs(obj: OPT.Object; vis: INTEGER);
491    VAR i: INTEGER; ext: OPT.ConstExt;
492  BEGIN
493    IF obj # NIL THEN
494      CProcDefs(obj^.left, vis);
495      (* bug: obj.history cannot be used to cover unexported and deleted CProcs; use special flag obj.adr = 1 *)
496      IF (obj^.mode = OPT.CProc) & (obj^.vis >= vis) & (obj^.adr = 1) THEN
497        ext := obj.conval.ext; i := 1;
498        IF (ext[1] # "#") & ~(Prefixed(ext, "extern ") OR Prefixed(ext, Extern)) THEN
499          OPM.WriteString("#define "); Ident(obj);
500          DeclareParams(obj^.link, TRUE);
501          OPM.Write(Tab);
502        END ;
503        FOR i := i TO ORD(obj.conval.ext[0]) DO OPM.Write(obj.conval.ext[i]) END;
504        OPM.WriteLn
505      END ;
506      CProcDefs(obj^.right, vis)
507    END
508  END CProcDefs;
509
510  PROCEDURE TypeDefs* (obj: OPT.Object; vis(*replaced by test on currFile in DefineType*): INTEGER);
511  BEGIN
512    IF obj # NIL THEN
513      TypeDefs(obj^.left, vis);
514      (* test typ.txtpos to skip types that have been unexported; obj.history # removed is not enough!*)
515      IF (obj^.mode = OPT.Typ) & (obj^.typ^.txtpos > 0) THEN DefineType(obj^.typ) END ;
516      TypeDefs(obj^.right, vis)
517    END
518  END TypeDefs;
519
520  PROCEDURE DefAnonRecs(n: OPT.Node);
521    VAR o: OPT.Object; typ: OPT.Struct;
522  BEGIN
523    WHILE (n # NIL) & (n^.class = OPT.Ninittd) DO
524      typ := n^.typ;
525      IF (typ^.strobj = NIL) & ((OPM.currFile = OPM.BodyFile) OR (typ.ref < OPM.MaxStruct)) THEN
526        DefineType(typ);  (* declare base and field types, if any *)
527        NEW(o); o.typ := typ; o.name := ""; DeclareBase(o); EndStat; OPM.WriteLn
528        (* simply defines a named struct, but not a type;
529          o.name = "" signals field list expansion for DeclareBase in this very special case *)
530      END ;
531      n := n^.link
532    END
533  END DefAnonRecs;
534
535  PROCEDURE TDescDecl* (typ: OPT.Struct);
536    VAR nofptrs: LONGINT;
537      o: OPT.Object;
538  BEGIN
539    BegStat; OPM.WriteString("__TDESC(");
540    Andent(typ);
541    Str1(", #", typ^.n + 1); Str1(", #) = {__TDFLDS(", NofPtrs(typ));
542    OPM.Write(DoubleQuote);
543    IF typ^.strobj # NIL THEN OPM.WriteStringVar(typ^.strobj^.name) END ;
544    OPM.Write(DoubleQuote);
545    Str1(', #), {', typ^.size);
546    nofptrs := 0; PutPtrOffsets(typ, 0, nofptrs); Str1("#}}", -(nofptrs + 1) * OPM.AddressSize);
547    EndStat
548  END TDescDecl;
549
550  PROCEDURE InitTDesc*(typ: OPT.Struct);
551  BEGIN
552    BegStat; OPM.WriteString("__INITYP(");
553    Andent(typ); OPM.WriteString(", ");
554    IF typ^.BaseTyp # NIL THEN Andent(typ^.BaseTyp) ELSE Andent(typ) END ;
555    Str1(", #)", typ^.extlev);
556    EndStat;
557    IF typ^.strobj # NIL THEN InitTProcs(typ^.strobj, typ^.link) END
558  END InitTDesc;
559
560  PROCEDURE FillGap(gap, off, align: LONGINT; VAR n, curAlign: LONGINT);
561  (* gap:      Required gap - already calculated based on alignment requirements
562     off:      Current offset - where gap begins
563     align:    Containing record type alignment
564     n:        Next ordinal to use for private field names
565     curAlign: Largest alignment of any field so far
566  *)
567    VAR adr: LONGINT;
568  BEGIN
569    adr := off; OPT.Align(adr, align);
570    IF (curAlign < align) & (gap - (adr - off) >= align) THEN (* preserve alignment of the enclosing struct! *)
571      DEC(gap, (adr - off) + align);
572      BegStat;
573      CASE align OF
574      |2: OPM.WriteString("INT16")
575      |4: OPM.WriteString("INT32")
576      |8: OPM.WriteString("INT64")
577      ELSE OPM.LogWLn; OPM.LogWStr("Unexpected enclosing alignment in FillGap.")
578      END;
579      Str1(" _prvt#", n); INC(n); EndStat;
580      curAlign := align
581    END ;
582    IF gap > 0 THEN BegStat; Str1("char _prvt#", n); INC(n); Str1("[#]", gap); EndStat END
583  END FillGap;
584
585  PROCEDURE FieldList (typ: OPT.Struct; last: BOOLEAN; VAR off, n, curAlign: LONGINT);
586    VAR fld: OPT.Object; base: OPT.Struct; gap, adr, align, fldAlign: LONGINT;
587  BEGIN
588    fld   := typ.link;
589    align := typ^.align MOD 10000H;
590    IF typ.BaseTyp # NIL THEN FieldList(typ.BaseTyp, FALSE, off, n, curAlign)
591    ELSE off := 0; n := 0; curAlign := 1
592    END;
593    (* off:      Current offset into record
594       align:    Overall (RECORD) alignment
595       curAlign: Current alignment - largest alignment of any field so far
596       n:        Next ordinal to use for private field names
597    *)
598    WHILE (fld # NIL) & (fld.mode = OPT.Fld) DO
599      IF (OPM.currFile = OPM.HeaderFile) & (fld.vis = OPT.internal)
600      OR (OPM.currFile = OPM.BodyFile)   & (fld.vis = OPT.internal) & (typ^.mno # 0) THEN
601        (* Skip private fields *)
602        fld := fld.link;
603        WHILE (fld # NIL) & (fld.mode = OPT.Fld) & (fld.vis = OPT.internal) DO fld := fld.link END;
604      ELSE
605        (* mimic OPV.TypSize to detect gaps caused by private fields *)
606        adr := off;
607        fldAlign := OPT.BaseAlignment(fld^.typ);
608        OPT.Align(adr, fldAlign);
609        gap := fld.adr - adr;
610        IF fldAlign > curAlign THEN curAlign := fldAlign END;
611        IF gap > 0 THEN
612          FillGap(gap, off, align, n, curAlign)
613        END;
614        BegStat; DeclareBase(fld); OPM.Write(' '); DeclareObj(fld, FALSE);
615        off := fld.adr + fld.typ.size; base := fld.typ; fld := fld.link;
616        WHILE (fld # NIL)
617            & (fld.mode = OPT.Fld)
618            & (fld.typ  = base)
619            & (fld.adr  = off)
620(* ?? *)    & ((OPM.currFile = OPM.BodyFile) OR (fld.vis # OPT.internal) OR (fld.typ.strobj = NIL)) DO
621          OPM.WriteString(", "); DeclareObj(fld, FALSE); off := fld.adr + fld.typ.size; fld := fld.link
622        END;
623        EndStat
624      END
625    END;
626    IF last THEN
627      adr := typ.size - typ^.sysflag DIV 100H;
628      IF adr = 0 THEN gap := 1 (* avoid empty struct *) ELSE gap := adr - off END ;
629      IF gap > 0 THEN FillGap(gap, off, align, n, curAlign) END
630    END
631  END FieldList;
632
633  PROCEDURE IdentList (obj: OPT.Object; vis: INTEGER);
634  (* generate var and param lists; vis: 0 all global vars, local var, 1 exported(R) var, 2 par list, 3 scope var *)
635    VAR base: OPT.Struct; first: BOOLEAN; lastvis: INTEGER;
636  BEGIN
637    base := NIL; first := TRUE;
638    WHILE (obj # NIL) & (obj^.mode # OPT.TProc) DO
639      IF (vis IN {0, 2}) OR ((vis = 1) & (obj^.vis # 0)) OR ((vis = 3) & ~obj^.leaf) THEN
640        IF (obj^.typ # base) OR (obj^.vis # lastvis) THEN  (* new variable base type definition required *)
641          IF ~first THEN EndStat END ;
642          first := FALSE;
643          base := obj^.typ; lastvis := obj^.vis;
644          BegStat;
645          IF (vis = 1) & (obj^.vis # OPT.internal) THEN OPM.WriteString(Extern)
646          ELSIF (obj^.mnolev = 0) & (vis = 0) THEN
647            IF obj^.vis = OPT.internal THEN OPM.WriteString('static ')
648            ELSE OPM.WriteString(Export)
649            END
650          END ;
651          IF (vis = 2) & (obj^.mode = OPT.Var) & (base^.form = OPT.Real) THEN OPM.WriteString("double")
652          ELSE DeclareBase(obj)
653          END
654        ELSE OPM.Write(",");
655        END ;
656        OPM.Write(' ');
657        IF (vis = 2) & (obj^.mode = OPT.Var) & (base^.form = OPT.Real) THEN OPM.Write("_") END ;
658        DeclareObj(obj, vis = 3);
659        IF obj^.typ^.comp = OPT.DynArr THEN (* declare len parameter(s) *)
660          EndStat; BegStat;
661          base := OPT.adrtyp;
662          OPM.WriteString("ADDRESS "); LenList(obj, FALSE, TRUE)
663        ELSIF (obj^.mode = OPT.VarPar) & (obj^.typ^.comp = OPT.Record) THEN
664          EndStat; BegStat;
665          OPM.WriteString("ADDRESS *"); Ident(obj); OPM.WriteString(TagExt);
666          base := NIL
667        ELSIF (OPM.ptrinit IN OPM.Options) & (vis = 0) & (obj^.mnolev > 0) & (obj^.typ^.form = OPT.Pointer) THEN
668          OPM.WriteString(" = NIL")
669        END
670      END ;
671      obj := obj^.link
672    END ;
673    IF ~first THEN EndStat END
674  END IdentList;
675
676  PROCEDURE AnsiParamList (obj: OPT.Object; showParamNames: BOOLEAN);
677    VAR name: ARRAY 32 OF CHAR;
678  BEGIN
679    OPM.Write("(");
680    IF (obj = NIL) OR (obj^.mode = OPT.TProc) THEN OPM.WriteString("void")
681    ELSE
682      LOOP
683        DeclareBase(obj);
684        IF showParamNames THEN
685          OPM.Write(' '); DeclareObj(obj, FALSE)
686        ELSE
687          COPY(obj^.name, name);  obj^.name := ""; DeclareObj(obj, FALSE); COPY(name, obj^.name)
688        END ;
689        IF obj^.typ^.comp = OPT.DynArr THEN
690          OPM.WriteString(", ADDRESS ");
691          LenList(obj, TRUE, showParamNames)
692        ELSIF (obj^.mode = OPT.VarPar) & (obj^.typ^.comp = OPT.Record) THEN
693          OPM.WriteString(", ADDRESS *");
694          IF showParamNames THEN Ident(obj); OPM.WriteString(TagExt) END
695        END ;
696        IF (obj^.link = NIL) OR (obj^.link.mode = OPT.TProc) THEN EXIT END ;
697        OPM.WriteString(", ");
698        obj := obj^.link
699      END
700    END ;
701    OPM.Write(")")
702  END AnsiParamList;
703
704  PROCEDURE ProcHeader(proc: OPT.Object; define: BOOLEAN);
705  BEGIN
706    IF proc^.typ = OPT.notyp THEN OPM.WriteString('void') ELSE Ident(proc^.typ^.strobj) END ;
707    OPM.Write(' '); Ident(proc); OPM.Write(' ');
708    AnsiParamList(proc^.link, TRUE);
709    IF ~define THEN OPM.Write(";") END ;
710    OPM.WriteLn
711  END ProcHeader;
712
713  PROCEDURE ProcPredefs (obj: OPT.Object; vis: SHORTINT); (* forward declaration of procedures *)
714  BEGIN
715    IF obj # NIL THEN
716      ProcPredefs(obj^.left, vis);
717      IF (obj^.mode IN {OPT.LProc, OPT.XProc}) & (obj^.vis >= vis) & ((obj^.history # OPT.removed) OR (obj^.mode = OPT.LProc)) THEN
718        (* previous OPT.XProc may be deleted or become OPT.LProc after interface change*)
719        IF vis = OPT.external THEN OPM.WriteString(Extern)
720        ELSIF obj^.vis = OPT.internal THEN OPM.WriteString('static ')
721        ELSE OPM.WriteString(Export)
722        END ;
723        ProcHeader(obj, FALSE);
724      END ;
725      ProcPredefs(obj^.right, vis);
726    END;
727  END ProcPredefs;
728
729  PROCEDURE Include(name: ARRAY OF CHAR);
730  BEGIN
731    OPM.WriteString("#include "); OPM.Write(DoubleQuote); OPM.WriteStringVar(name);
732    OPM.WriteString(".h"); OPM.Write(DoubleQuote); OPM.WriteLn
733  END Include;
734
735  PROCEDURE IncludeImports(obj: OPT.Object; vis: INTEGER);
736  BEGIN
737    IF obj # NIL THEN
738      IncludeImports(obj^.left, vis);
739      IF (obj^.mode = OPT.Mod) & (obj^.mnolev # 0) & (OPT.GlbMod[-obj^.mnolev].vis >= vis) THEN  (* @self and SYSTEM have mnolev = 0 *)
740        Include(OPT.GlbMod[-obj^.mnolev].name)  (* use unaliased module name *)
741      END;
742      IncludeImports(obj^.right, vis);
743    END;
744  END IncludeImports;
745
746  PROCEDURE GenDynTypes (n: OPT.Node; vis: INTEGER);
747    VAR typ: OPT.Struct;
748  BEGIN
749    WHILE (n # NIL) & (n^.class = OPT.Ninittd) DO
750      typ := n^.typ;
751      IF (vis = OPT.internal) OR (typ^.ref < OPM.MaxStruct (*type needed in symbol file*)) THEN
752        BegStat;
753        IF vis = OPT.external THEN OPM.WriteString(Extern)
754        ELSIF (typ^.strobj # NIL) & (typ^.strobj^.mnolev > 0) THEN OPM.WriteString('static ')
755        ELSE OPM.WriteString(Export)
756        END ;
757        OPM.WriteString("ADDRESS *"); Andent(typ); OPM.WriteString(DynTypExt);
758        EndStat
759      END ;
760      n := n^.link
761    END
762  END GenDynTypes;
763
764  PROCEDURE GenHdr*(n: OPT.Node);
765  BEGIN
766    (* includes are delayed until it is known which ones are needed in the header *)
767    OPM.currFile := OPM.HeaderFile;
768    DefAnonRecs(n);
769    TypeDefs(OPT.topScope^.right, 1); OPM.WriteLn;
770    IdentList(OPT.topScope^.scope, 1); OPM.WriteLn;
771    GenDynTypes(n, OPT.external); OPM.WriteLn;
772    ProcPredefs(OPT.topScope^.right, 1);
773    OPM.WriteString(Extern); OPM.WriteString("void *");
774    OPM.WriteStringVar(OPM.modName); OPM.WriteString(BodyNameExt);
775    EndStat; OPM.WriteLn;
776    CProcDefs(OPT.topScope^.right, 1); OPM.WriteLn;
777    OPM.WriteString("#endif // "); OPM.WriteStringVar(OPM.modName); OPM.WriteLn
778  END GenHdr;
779
780  PROCEDURE GenHeaderMsg;
781    VAR i: INTEGER;
782  BEGIN
783    OPM.WriteString("/* "); OPM.WriteString(Configuration.name);
784    OPM.Write(" "); OPM.WriteString(Configuration.versionLong); OPM.Write (" "); (* noch *)
785    FOR i := 0 TO MAX(SET) DO
786      IF i IN OPM.Options THEN
787        CASE i OF  (* c.f. ScanOptions in OPM *)
788        | OPM.inxchk:         OPM.Write("x")
789        | OPM.ranchk:         OPM.Write("r")
790        | OPM.typchk:         OPM.Write("t")
791        | OPM.newsf:          OPM.Write("s")
792        | OPM.ptrinit:        OPM.Write("p")
793        | OPM.assert:         OPM.Write("a")
794        | OPM.extsf:          OPM.Write("e")
795        | OPM.mainprog:       OPM.Write("m")
796        | OPM.dontasm:        OPM.Write("S")
797        | OPM.dontlink:       OPM.Write("c")
798        | OPM.mainlinkstat:   OPM.Write("M")
799        | OPM.notcoloroutput: OPM.Write("f")
800        | OPM.forcenewsym:    OPM.Write("F")
801        | OPM.verbose:        OPM.Write("v")
802        ELSE OPM.LogWStr ("( more options defined in OPM than checked in OPC.GenHeaderMsg, if you are a compiler developer, add them to OPC.GenHeaderMsg"); OPM.LogWLn;
803        END
804      END
805    END;
806    OPM.WriteString(" */"); OPM.WriteLn
807  END GenHeaderMsg;
808
809  PROCEDURE GenHdrIncludes*;
810  BEGIN
811    OPM.currFile := OPM.HeaderInclude;
812    GenHeaderMsg;
813    OPM.WriteLn;
814    OPM.WriteString("#ifndef "); OPM.WriteStringVar(OPM.modName); OPM.WriteString(FlagExt); OPM.WriteLn;
815    OPM.WriteString("#define "); OPM.WriteStringVar(OPM.modName); OPM.WriteString(FlagExt); OPM.WriteLn;
816    OPM.WriteLn;
817
818    Include(BasicIncludeFile);
819    IncludeImports(OPT.topScope^.right, 1); OPM.WriteLn
820  END GenHdrIncludes;
821
822  PROCEDURE GenBdy*(n: OPT.Node);
823  BEGIN
824    OPM.currFile := OPM.BodyFile;
825    GenHeaderMsg;
826    OPM.WriteLn;
827
828    (* Define model dependent type sizes *)
829    OPM.WriteString("#define SHORTINT INT");  OPM.WriteInt(OPT.sinttyp.size*8); OPM.WriteLn;
830    OPM.WriteString("#define INTEGER  INT");  OPM.WriteInt(OPT.inttyp.size*8);  OPM.WriteLn;
831    OPM.WriteString("#define LONGINT  INT");  OPM.WriteInt(OPT.linttyp.size*8); OPM.WriteLn;
832    OPM.WriteString("#define SET      UINT"); OPM.WriteInt(OPT.settyp.size*8);  OPM.WriteLn;
833    OPM.WriteLn;
834
835    Include(BasicIncludeFile);
836    IncludeImports(OPT.topScope^.right, 0); OPM.WriteLn;
837    DefAnonRecs(n);
838    TypeDefs(OPT.topScope^.right, 0); OPM.WriteLn;
839    IdentList(OPT.topScope^.scope, 0); OPM.WriteLn;
840    GenDynTypes(n, OPT.internal); OPM.WriteLn;
841    ProcPredefs(OPT.topScope^.right, 0); OPM.WriteLn;
842    CProcDefs(OPT.topScope^.right, 0); OPM.WriteLn
843  END GenBdy;
844
845  PROCEDURE RegCmds(obj: OPT.Object);
846  BEGIN
847    IF obj # NIL THEN
848      RegCmds(obj^.left);
849      IF (obj^.mode = OPT.XProc) & (obj^.history # OPT.removed) THEN
850        IF (obj^.vis # 0) & (obj^.link = NIL) & (obj^.typ = OPT.notyp) THEN (*command*)
851          BegStat; OPM.WriteString('__REGCMD("');
852          OPM.WriteStringVar(obj.name); OPM.WriteString('", '); Ident(obj); OPM.Write(")"); EndStat
853        END
854      END ;
855      RegCmds(obj^.right)
856    END
857  END RegCmds;
858
859  PROCEDURE InitImports(obj: OPT.Object);
860  BEGIN
861    IF obj # NIL THEN
862      InitImports(obj^.left);
863      IF (obj^.mode = OPT.Mod) & (obj^.mnolev # 0) THEN
864        BegStat; OPM.WriteString("__MODULE_IMPORT(");
865        OPM.WriteStringVar(OPT.GlbMod[-obj^.mnolev].name);
866        OPM.Write(')'); EndStat
867      END ;
868      InitImports(obj^.right)
869    END
870  END InitImports;
871
872  PROCEDURE GenEnumPtrs* (var: OPT.Object);
873    VAR typ: OPT.Struct; n: LONGINT;
874  BEGIN GlbPtrs := FALSE;
875    WHILE var # NIL DO
876      typ := var^.typ;
877      IF NofPtrs(typ) > 0 THEN
878        IF ~GlbPtrs THEN GlbPtrs := TRUE;
879          OPM.WriteString("static void EnumPtrs(void (*P)(void*))"); OPM.WriteLn;
880          BegBlk
881        END ;
882        BegStat;
883        IF typ^.form = OPT.Pointer THEN
884          OPM.WriteString("P("); Ident(var); OPM.Write(")");
885        ELSIF typ^.comp = OPT.Record THEN
886          OPM.WriteString("__ENUMR(&"); Ident(var); OPM.WriteString(", ");
887          Andent(typ); OPM.WriteString(DynTypExt); Str1(", #", typ^.size); OPM.WriteString(", 1, P)")
888        ELSIF typ^.comp = OPT.Array THEN
889          n := typ^.n; typ := typ^.BaseTyp;
890          WHILE typ^.comp = OPT.Array DO n := n * typ^.n; typ := typ^.BaseTyp END ;
891          IF typ^.form = OPT.Pointer THEN
892            OPM.WriteString("__ENUMP("); Ident(var); Str1(", #, P)", n)
893          ELSIF typ^.comp = OPT.Record THEN
894            OPM.WriteString("__ENUMR("); Ident(var); OPM.WriteString(", ");
895            Andent(typ); OPM.WriteString(DynTypExt); Str1(", #", typ^.size); Str1(", #, P)", n)
896          END
897        END ;
898        EndStat
899      END ;
900      var := var^.link
901    END ;
902    IF GlbPtrs THEN
903      EndBlk; OPM.WriteLn
904    END
905  END GenEnumPtrs;
906
907  PROCEDURE EnterBody*;
908  BEGIN
909    OPM.WriteLn; OPM.WriteString(Export);
910    IF OPM.mainprog IN OPM.Options THEN
911      OPM.WriteString("int main(int argc, char **argv)"); OPM.WriteLn;
912    ELSE
913      OPM.WriteString("void *");
914      OPM.WriteString(OPM.modName); OPM.WriteString(BodyNameExt); OPM.WriteLn;
915    END ;
916    BegBlk; BegStat;
917    IF OPM.mainprog IN OPM.Options THEN OPM.WriteString("__INIT(argc, argv)") ELSE OPM.WriteString("__DEFMOD") END ;
918    EndStat;
919    IF (OPM.mainprog IN OPM.Options) & demoVersion THEN BegStat;
920      OPM.WriteString('/*don`t do it!*/ printf("DEMO VERSION: DO NOT USE THIS PROGRAM FOR ANY COMMERCIAL PURPOSE\n")');
921      EndStat
922    END ;
923    InitImports(OPT.topScope^.right);
924    BegStat;
925    IF OPM.mainprog IN OPM.Options THEN OPM.WriteString('__REGMAIN("') ELSE OPM.WriteString('__REGMOD("') END ;
926    OPM.WriteString(OPM.modName);
927    IF GlbPtrs THEN OPM.WriteString('", EnumPtrs)') ELSE OPM.WriteString('", 0)') END ;
928    EndStat;
929    IF OPM.modName # "SYSTEM" THEN RegCmds(OPT.topScope) END
930  END EnterBody;
931
932  PROCEDURE ExitBody*;
933  BEGIN
934    BegStat;
935    IF OPM.mainprog IN OPM.Options THEN OPM.WriteString("__FINI;") ELSE OPM.WriteString("__ENDMOD;") END ;
936    OPM.WriteLn; EndBlk
937  END ExitBody;
938
939  PROCEDURE DefineInter* (proc: OPT.Object); (* define intermediate scope record and variable *)
940    VAR scope: OPT.Object;
941  BEGIN
942    scope := proc^.scope;
943    OPM.WriteString('static '); OPM.WriteString('struct '); OPM.WriteStringVar(scope^.name); OPM.Write(' ');
944    BegBlk;
945    IdentList(proc^.link, 3); (* parameters *)
946    IdentList(scope^.scope, 3); (* local variables *)
947    BegStat; (* scope link field declaration *)
948    OPM.WriteString('struct '); OPM.WriteStringVar (scope^.name);
949    OPM.Write(' '); OPM.Write('*'); OPM.WriteString(LinkName); EndStat;
950    EndBlk0; OPM.Write(' ');
951    OPM.Write('*'); OPM.WriteStringVar (scope^.name); OPM.WriteString(GlobalScope); EndStat; OPM.WriteLn;
952    ProcPredefs (scope^.right, 0);
953    OPM.WriteLn;
954  END DefineInter;
955
956  PROCEDURE NeedsRetval*(proc: OPT.Object): BOOLEAN; (* aux. variable __retval needed for return *)
957  BEGIN (* simple rule; ignores DUPlicated value arrays because they use alloca. *)
958    RETURN (proc^.typ # OPT.notyp) & ~proc^.scope^.leaf
959  END NeedsRetval;
960
961  PROCEDURE EnterProc* (proc: OPT.Object);
962    VAR var, scope: OPT.Object; typ: OPT.Struct; dim: INTEGER;
963  BEGIN
964    IF proc^.vis # OPT.external THEN OPM.WriteString('static ') END ;
965    ProcHeader(proc, TRUE);
966    BegBlk;
967    scope := proc^.scope;
968    IdentList(scope^.scope, 0);
969    IF ~scope^.leaf THEN (* declare intermediate procedure scope record variable*)
970      BegStat; OPM.WriteString('struct '); OPM.WriteStringVar (scope^.name);
971      OPM.Write(' '); OPM.WriteString(LocalScope); EndStat
972    END ;
973    IF NeedsRetval(proc) THEN BegStat; Ident(proc^.typ^.strobj); OPM.WriteString(" __retval"); EndStat END;
974    var := proc^.link;
975    WHILE var # NIL DO (* declare copy of fixed size value array parameters *)
976      IF (var^.typ^.comp = OPT.Array) & (var^.mode = OPT.Var) THEN
977        BegStat;
978        IF var^.typ^.strobj = NIL THEN OPM.Mark(200, var^.typ^.txtpos) ELSE Ident(var^.typ^.strobj) END ;
979        OPM.Write(' '); Ident(var); OPM.WriteString("__copy");
980        EndStat
981      END ;
982      var := var^.link
983    END ;
984    var := proc^.link;
985    WHILE var # NIL DO (* copy value array parameters *)
986      IF (var^.typ^.comp IN {OPT.Array, OPT.DynArr}) & (var^.mode = OPT.Var) & (var^.typ^.sysflag = 0) THEN
987        BegStat;
988        IF var^.typ^.comp = OPT.Array THEN
989          OPM.WriteString("__DUPARR(");
990          Ident(var); OPM.WriteString(', ');
991          IF var^.typ^.strobj = NIL THEN OPM.Mark(200, var^.typ^.txtpos) ELSE Ident(var^.typ^.strobj) END
992        ELSE
993          OPM.WriteString('__DUP(');
994          Ident(var); OPM.WriteString(', '); Ident(var); OPM.WriteString(LenExt);
995          typ := var^.typ^.BaseTyp; dim := 1;
996          WHILE typ^.comp = OPT.DynArr DO
997            OPM.WriteString(" * "); Ident(var); OPM.WriteString(LenExt); OPM.WriteInt(dim);
998            typ := typ^.BaseTyp; INC(dim)
999          END ;
1000          OPM.WriteString(', ');
1001          IF (typ^.strobj = NIL) THEN OPM.Mark(200, typ^.txtpos)
1002          ELSE Ident(typ^.strobj)
1003          END
1004        END ;
1005        OPM.Write(')'); EndStat
1006      END ;
1007      var := var^.link
1008    END ;
1009    IF ~scope^.leaf THEN
1010      var := proc^.link; (* copy addresses of parameters into local scope record *)
1011      WHILE var # NIL DO
1012        IF ~var^.leaf THEN (* only if used by a nested procedure *)
1013          BegStat;
1014          OPM.WriteString(LocalScope); OPM.Write('.'); Ident(var);
1015          OPM.WriteString(' = ');
1016          IF var^.typ^.comp IN {OPT.Array, OPT.DynArr} THEN OPM.WriteString("(void*)")
1017            (* K&R and ANSI differ in the type: array or element type*)
1018          ELSIF var^.mode # OPT.VarPar THEN OPM.Write("&")
1019          END ;
1020          Ident(var);
1021          IF var^.typ^.comp = OPT.DynArr THEN
1022            typ := var^.typ; dim := 0;
1023            REPEAT (* copy len(s) *)
1024              OPM.WriteString("; ");
1025              OPM.WriteString(LocalScope); OPM.Write('.'); Ident(var); OPM.WriteString(LenExt);
1026              IF dim # 0 THEN OPM.WriteInt(dim) END ;
1027              OPM.WriteString(' = '); Ident(var); OPM.WriteString(LenExt);
1028              IF dim # 0 THEN OPM.WriteInt(dim) END ;
1029              typ := typ^.BaseTyp
1030            UNTIL typ^.comp # OPT.DynArr;
1031          ELSIF (var^.mode = OPT.VarPar) & (var^.typ^.comp = OPT.Record) THEN
1032            OPM.WriteString("; ");
1033            OPM.WriteString(LocalScope); OPM.Write('.'); Ident(var); OPM.WriteString(TagExt);
1034            OPM.WriteString(' = '); Ident(var); OPM.WriteString(TagExt)
1035          END ;
1036          EndStat
1037        END;
1038        var := var^.link;
1039      END;
1040      var := scope^.scope; (* copy addresses of local variables into scope record *)
1041      WHILE var # NIL DO
1042        IF ~var^.leaf THEN (* only if used by a nested procedure *)
1043          BegStat;
1044          OPM.WriteString(LocalScope); OPM.Write('.'); Ident(var); OPM.WriteString(' = ');
1045          IF var^.typ^.comp # OPT.Array THEN OPM.Write("&")
1046          ELSE OPM.WriteString("(void*)")  (* K&R and ANSI differ in the type: array or element type*)
1047          END ;
1048          Ident(var); EndStat
1049        END ;
1050        var := var^.link
1051      END;
1052      (* now link new scope *)
1053      BegStat; OPM.WriteString(LocalScope); OPM.Write('.'); OPM.WriteString(LinkName);
1054      OPM.WriteString(' = '); OPM.WriteStringVar(scope^.name); OPM.WriteString(GlobalScope); EndStat;
1055      BegStat; OPM.WriteStringVar(scope^.name); OPM.WriteString(GlobalScope); OPM.WriteString(' = ');
1056      OPM.Write("&"); OPM.WriteString(LocalScope); EndStat
1057    END
1058  END EnterProc;
1059
1060  PROCEDURE ExitProc*(proc: OPT.Object; eoBlock, implicitRet: BOOLEAN);
1061    VAR var: OPT.Object; indent: BOOLEAN;
1062  BEGIN
1063    indent := eoBlock;
1064    IF implicitRet & (proc^.typ # OPT.notyp) THEN
1065      OPM.Write(Tab); OPM.WriteString("__RETCHK;"); OPM.WriteLn
1066    ELSIF ~eoBlock OR implicitRet THEN
1067      IF ~proc^.scope^.leaf THEN
1068        (* link scope pointer of nested proc back to previous scope *)
1069        IF indent THEN BegStat ELSE indent := TRUE END ;
1070        OPM.WriteStringVar(proc^.scope^.name); OPM.WriteString(GlobalScope);
1071        OPM.WriteString(' = '); OPM.WriteString(LocalScope); OPM.Write('.'); OPM.WriteString(LinkName);
1072        EndStat
1073      END;
1074      (* delete array value parameters *)
1075      var := proc^.link;
1076      WHILE var # NIL DO
1077        IF (var^.typ^.comp = OPT.DynArr) & (var^.mode = OPT.Var) & (var^.typ^.sysflag = 0) THEN
1078          IF indent THEN BegStat ELSE indent := TRUE END ;
1079          OPM.WriteString('__DEL('); Ident(var); OPM.Write(')'); EndStat
1080        END ;
1081        var := var^.link
1082      END
1083    END ;
1084    IF eoBlock THEN EndBlk; OPM.WriteLn
1085    ELSIF indent THEN BegStat
1086    END
1087  END ExitProc;
1088
1089  PROCEDURE CompleteIdent*(obj: OPT.Object);
1090    VAR comp, level: INTEGER;
1091  BEGIN
1092    (* obj^.mode IN {OPT.Var, OPT.VarPar} *)
1093    level := obj^.mnolev;
1094    IF obj^.adr = 1 THEN  (* WITH-variable *)
1095      IF obj^.typ^.comp = OPT.Record THEN Ident(obj); OPM.WriteString("__")
1096      ELSE (* cast with guard pointer type *)
1097        OPM.WriteString("(*("); Ident(obj^.typ^.strobj); OPM.WriteString("*)&"); Ident(obj); OPM.Write(")")
1098      END
1099    ELSIF (level # OPM.level) & (level > 0) THEN (* intermediate var *)
1100      comp := obj^.typ^.comp;
1101      IF (obj^.mode # OPT.VarPar) & (comp # OPT.DynArr) THEN OPM.Write('*'); END;
1102      OPM.WriteStringVar(obj^.scope^.name); OPM.WriteString(GlobalScope);
1103      OPM.WriteString("->"); Ident(obj)
1104    ELSE
1105      Ident(obj)
1106    END
1107  END CompleteIdent;
1108
1109  PROCEDURE TypeOf*(ap: OPT.Object);
1110    VAR i: INTEGER;
1111  BEGIN
1112    ASSERT(ap.typ.comp = OPT.Record);
1113    IF ap.mode = OPT.VarPar THEN
1114      IF ap.mnolev # OPM.level THEN  (*intermediate level var-par record; possible WITH-guarded*)
1115        OPM.WriteStringVar(ap^.scope^.name); OPM.WriteString("_s->"); Ident(ap)
1116      ELSE (*local var-par record*)
1117        Ident(ap)
1118      END ;
1119      OPM.WriteString(TagExt)
1120    ELSIF ap^.typ^.strobj # NIL THEN
1121      Ident(ap^.typ^.strobj); OPM.WriteString(DynTypExt)
1122    ELSE Andent(ap.typ)  (*anonymous ap type, p^ *)
1123    END
1124  END TypeOf;
1125
1126  PROCEDURE Cmp*(rel: INTEGER);
1127  BEGIN
1128    CASE rel OF
1129    | OPT.eql: OPM.WriteString(" == ")
1130    | OPT.neq: OPM.WriteString(" != ")
1131    | OPT.lss: OPM.WriteString(" < ")
1132    | OPT.leq: OPM.WriteString(" <= ")
1133    | OPT.gtr: OPM.WriteString(" > ")
1134    | OPT.geq: OPM.WriteString(" >= ")
1135    ELSE OPM.LogWStr("unhandled case in OPC.Cmp, rel = "); OPM.LogWNum(rel, 0); OPM.LogWLn;
1136    END;
1137  END Cmp;
1138
1139  PROCEDURE CharacterLiteral(c: SYSTEM.INT64);
1140  BEGIN
1141    IF (c < 32) OR (c > 126) THEN
1142      OPM.WriteString("0x"); OPM.WriteHex(c)
1143    ELSE
1144      OPM.Write("'");
1145      IF (c = ORD(Backslash)) OR (c = ORD("'"))  OR (c = ORD("?")) THEN
1146        OPM.Write(Backslash)
1147      END;
1148      OPM.Write(CHR(c));
1149      OPM.Write("'")
1150    END
1151  END CharacterLiteral;
1152
1153  PROCEDURE StringLiteral(s: ARRAY OF CHAR; l: LONGINT);
1154    VAR i: LONGINT; c: INTEGER;
1155  BEGIN
1156    OPM.Write(DoubleQuote);
1157    i := 0; WHILE i < l DO
1158      c := ORD(s[i]);
1159      IF (c < 32) OR (c > 126) THEN
1160        (* Encode binary character value using exactly 3 octal digits.
1161           Use octal in preference to hex as only the octal escape
1162           syntax ensures a subsequent character will not be absorbed
1163           into this literal. *)
1164        OPM.Write(Backslash);
1165        OPM.Write(CHR(ORD("0") + c DIV 64)); c := c MOD 64;
1166        OPM.Write(CHR(ORD("0") + c DIV 8));  c := c MOD 8;
1167        OPM.Write(CHR(ORD("0") + c))
1168      ELSE
1169        IF (c = ORD(Backslash)) OR (c = ORD(DoubleQuote))  OR (c = ORD("?")) THEN
1170          OPM.Write(Backslash)
1171        END;
1172        OPM.Write(CHR(c));
1173      END;
1174      INC(i);
1175    END;
1176    OPM.Write(DoubleQuote)
1177  END StringLiteral;
1178
1179  PROCEDURE Case*(caseVal: SYSTEM.INT64; form: INTEGER);
1180  VAR
1181    ch: CHAR;
1182  BEGIN
1183    OPM.WriteString('case ');
1184    CASE form OF
1185    | OPT.Char: CharacterLiteral(caseVal)
1186    | OPT.Int:  OPM.WriteInt(caseVal);
1187    ELSE OPM.LogWStr("unhandled case in OPC.Case, form = "); OPM.LogWNum(form, 0); OPM.LogWLn;
1188    END;
1189    OPM.WriteString(': ');
1190  END Case;
1191
1192  PROCEDURE SetInclude* (exclude: BOOLEAN);
1193  BEGIN
1194    IF exclude THEN OPM.WriteString(" &= ~"); ELSE OPM.WriteString(" |= "); END;
1195  END SetInclude;
1196
1197  PROCEDURE Increment* (decrement: BOOLEAN);
1198  BEGIN
1199    IF decrement THEN OPM.WriteString(" -= "); ELSE OPM.WriteString(" += "); END;
1200  END Increment;
1201
1202  PROCEDURE Halt* (n: LONGINT);
1203  BEGIN
1204    Str1("__HALT(#)", n)
1205  END Halt;
1206
1207  PROCEDURE IntLiteral*(n: SYSTEM.INT64; size: LONGINT);
1208  BEGIN
1209    IF (size > OPM.CIntSize) & (n <= OPM.CIntMax) & (n > OPM.CIntMin) THEN
1210      OPM.WriteString("((INT"); OPM.WriteInt(size*8); OPM.WriteString(")(");
1211      OPM.WriteInt(n); OPM.WriteString("))")
1212    ELSE
1213      OPM.WriteInt(n)
1214    END
1215  END IntLiteral;
1216
1217  PROCEDURE Len* (obj: OPT.Object; array: OPT.Struct; dim: SYSTEM.INT64);
1218  VAR
1219    d: SYSTEM.INT64;
1220  BEGIN
1221    d := dim;
1222    WHILE d > 0 DO array := array^.BaseTyp; DEC(d) END;
1223    IF array^.comp = OPT.DynArr THEN
1224      CompleteIdent(obj); OPM.WriteString(LenExt);
1225      IF dim # 0 THEN OPM.WriteInt(dim) END
1226    ELSE (* array *)
1227      OPM.WriteInt(array.n)
1228    END
1229  END Len;
1230
1231  PROCEDURE Constant* (con: OPT.Const; form: INTEGER);
1232    VAR i: INTEGER; s: SYSTEM.SET64;
1233      hex: SYSTEM.INT64; skipLeading: BOOLEAN;
1234  BEGIN
1235    CASE form OF
1236    | OPT.Byte:   OPM.WriteInt(con^.intval)
1237    | OPT.Bool:   OPM.WriteInt(con^.intval)
1238    | OPT.Char:   CharacterLiteral(con.intval)
1239    | OPT.Int:    OPM.WriteInt(con^.intval)
1240    | OPT.Real:   OPM.WriteReal(con^.realval, "f")
1241    | OPT.LReal:  OPM.WriteReal(con^.realval, 0X)
1242    | OPT.Set:    OPM.WriteString("0x");
1243                  skipLeading := TRUE;
1244                  s := con^.setval; i := MAX(SYSTEM.SET64) + 1;
1245                  REPEAT
1246                    hex := 0;
1247                    REPEAT
1248                      DEC(i); hex := 2 * hex;
1249                      IF i IN s THEN INC(hex) END
1250                    UNTIL i MOD 8 = 0;
1251                    IF (hex # 0) OR ~skipLeading THEN
1252                      OPM.WriteHex(hex);
1253                      skipLeading := FALSE
1254                    END
1255                  UNTIL i = 0;
1256                  IF skipLeading THEN OPM.Write("0") END
1257    | OPT.String: StringLiteral(con.ext^, con.intval2-1)
1258    | OPT.NilTyp: OPM.WriteString('NIL');
1259    ELSE OPM.LogWStr("unhandled case in OPC.Constant, form = "); OPM.LogWNum(form, 0); OPM.LogWLn;
1260    END;
1261  END Constant;
1262
1263
1264  PROCEDURE InitKeywords;
1265    VAR n, i: SHORTINT;
1266
1267    PROCEDURE Enter(s: ARRAY OF CHAR);
1268      VAR h: INTEGER;
1269    BEGIN h := PerfectHash(s); hashtab[h] := n; COPY(s, keytab[n]); INC(n)
1270    END Enter;
1271
1272  BEGIN n := 0;
1273    FOR i := 0 TO LEN(hashtab)-1 DO hashtab[i] := -1 END ;
1274    Enter("ADDRESS");  (* pseudo keyword used by voc *)
1275    Enter("INT16");    (* pseudo keyword used by voc *)
1276    Enter("INT32");    (* pseudo keyword used by voc *)
1277    Enter("INT64");    (* pseudo keyword used by voc *)
1278    Enter("INT8");     (* pseudo keyword used by voc *)
1279    Enter("UINT16");   (* pseudo keyword used by voc *)
1280    Enter("UINT32");   (* pseudo keyword used by voc *)
1281    Enter("UINT64");   (* pseudo keyword used by voc *)
1282    Enter("UINT8");    (* pseudo keyword used by voc *)
1283
1284    Enter("asm");
1285    Enter("auto");
1286    Enter("break");
1287    Enter("case");
1288    Enter("char");
1289    Enter("const");
1290    Enter("continue");
1291    Enter("default");
1292    Enter("do");
1293    Enter("double");
1294    Enter("else");
1295    Enter("enum");
1296    Enter("extern");
1297    Enter("export");   (* pseudo keyword used by voc *)
1298    Enter("float");
1299    Enter("for");
1300    Enter("fortran");
1301    Enter("goto");
1302    Enter("if");
1303    Enter("import");   (* pseudo keyword used by voc *)
1304    Enter("int");
1305    Enter("long");
1306    Enter("register");
1307    Enter("return");
1308    Enter("short");
1309    Enter("signed");
1310    Enter("sizeof");
1311    Enter("size_t");
1312    Enter("static");
1313    Enter("struct");
1314    Enter("switch");
1315    Enter("typedef");
1316    Enter("union");
1317    Enter("unsigned");
1318    Enter("void");
1319    Enter("volatile");
1320    Enter("while");
1321
1322(* what about common predefined names from cpp as e.g.
1323               Operating System:   ibm, gcos, os, tss and unix
1324               Hardware:           interdata, pdp11,  u370,  u3b,
1325                                   u3b2,   u3b5,  u3b15,  u3b20d,
1326                                   vax, ns32000,  iAPX286,  i386,
1327                                   sparc , and sun
1328               UNIX system variant:
1329                                   RES, and RT
1330               The lint(1V) command:
1331                                   lint
1332 *)
1333  END InitKeywords;
1334
1335BEGIN InitKeywords
1336END OPC.
1337