[Oberon] Intermediate scopes in Oberon-07

Andreas Pirklbauer andreas_pirklbauer at yahoo.com
Sat Feb 10 13:01:42 CET 2018


Just for the record: Below is a possible implementation of the Oberon-07
compiler that disallows access to ALL intermediate objects, i.e. not just
intermediate variables, but also intermediate types and constants. Five
lines needed to be changed in the Oberon-07 compiler.

This will now catch the type T in the following test program provided earlier:

  MODULE Test;

    PROCEDURE P;
      TYPE T = INTEGER;

      PROCEDURE Q(x: T): T;   (* this is now caught -> “pos 78 must be strictly local or global" *)
        RETURN 0
      END Q;

    END P;
  END Test.

  ORP.Compile Test.Mod ~

Everything else stays the same, e.g. local procedures continue to be
allowed, all types of intermediate objects continue to be allowed (i.e.
constants, types, variables can all be declared as local objects inside
any procedure, whether global or local and at any nesting level). It’s
just that the *access* for ALL intermediate objects is now restricted
to either the strictly local level or the strictly global (module) level.

Andreas


IMPLEMENTATION:  (see the output of the Unix diff command further down)
---------------

1. ORP.Declarations:

  PROCEDURE Declarations(VAR varsize: LONGINT);
    VAR obj, first: ORB.Object;
      x: ORG.Item; tp: ORB.Type; ptbase: PtrBase;
      expo: BOOLEAN; id: ORS.Ident;
  BEGIN (*sync*) pbsList := NIL;
    IF (sym < ORS.const) & (sym # ORS.end) & (sym # ORS.return) THEN ORS.Mark("declaration?");
      REPEAT ORS.Get(sym) UNTIL (sym >= ORS.const) OR (sym = ORS.end) OR (sym = ORS.return)
    END ;
    IF sym = ORS.const THEN
      ORS.Get(sym);
      WHILE sym = ORS.ident DO
        ORS.CopyId(id); ORS.Get(sym); CheckExport(expo);
        IF sym = ORS.eql THEN ORS.Get(sym) ELSE ORS.Mark("= ?") END;
        expression(x);
        IF (x.type.form = ORB.String) & (x.b = 2) THEN ORG.StrToChar(x) END ;
        ORB.NewObj(obj, id, ORB.Const); obj.expo := expo; obj.lev := level;
        IF x.mode = ORB.Const THEN obj.type := x.type;
          IF x.type.form = ORB.String THEN obj.val := x.a + x.b*10000H ELSE obj.val := x.a END
        ELSE ORS.Mark("expression not constant"); obj.type := ORB.intType
        END;
        Check(ORS.semicolon, "; missing")
      END
    END ;
    ...
  END Declarations;

2. ORP.CheckLevel (new procedure inserted e.g. after ORP.Check)

  PROCEDURE CheckLevel(lev: INTEGER);
  BEGIN
    IF (lev > 0) & (lev # level) THEN ORS.Mark("must be strictly local or global") END
  END CheckLevel;

3. ORG.MakeItem:

  PROCEDURE MakeItem*(VAR x: Item; y: ORB.Object; curlev: LONGINT);
  BEGIN x.mode := y.class; x.type := y.type; x.a := y.val; x.rdo := y.rdo;
    IF y.class = ORB.Par THEN x.b := 0
    ELSIF y.class = ORB.Typ THEN x.a := y.type.len; x.r := -y.lev
    ELSIF (y.class = ORB.Const) & (y.type.form = ORB.String) THEN
      x.a := y.val MOD 10000H; (*strx*) x.b := y.val DIV 10000H (*len*)
    ELSE x.r := y.lev
    END ;
    IF (y.lev > 0) & (y.lev # curlev) THEN ORS.Mark("level error, not accessible") END
  END MakeItem;


COMMENTS:
---------

1. ORP.Declarations: In essence one now sets obj.lev := level also for constants
   (previously this was done only for types and variables, but not for constants)
   I.e. the variable obj.lev is no longer “abused” to hold the length of a (string)
   constant, but to hold the scope level (this makes it possible to check for it in
   ORP.CheckLevel). Instead both the address and the length of a string constant are
   now “encoded” (16 bits each) into a single field (obj.val). This in turn frees up
   the obj.lev field (which can now used to hold the actual scope level of the
   constant). Note that x.a and x.b are themselves set by ORG.MakeStringItem
   (see there; but it does not need to be changed)

2. ORG. MakeItem: It simply performs the inverse operation as in ORP.Declarations.
   It also removes the guard (y.class # ORB.Const) in the last line of MakeItem that
   used to be there for handle the special case of constants (..was kind of shady anyway).

3. This implementation makes local procedures “self-contained”, i.e.
   one can move them around freely, make them global etc.


DIFFERENCES:
-------------

Differences to the Original Oberon 2013 version (as of Feb 10, 2018)
at https://www.inf.ethz.ch/personal/wirth are as follows:


$ diff ORP.Mod OriginalOberon2013/Sources/ORP.Mod
31,35d30
<   PROCEDURE CheckLevel(lev: INTEGER);
<   BEGIN
<     IF (lev > 0) & (lev # level) THEN ORS.Mark("must be strictly local or global") END
<   END CheckLevel;
< 
45d39
<     ELSE CheckLevel(obj.lev)
803,805c797,798
<         ORB.NewObj(obj, id, ORB.Const); obj.expo := expo; obj.lev := level;
<         IF x.mode = ORB.Const THEN obj.type := x.type;
<           IF x.type.form = ORB.String THEN obj.val := x.a + x.b*10000H ELSE obj.val := x.a END
---
>         ORB.NewObj(obj, id, ORB.Const); obj.expo := expo;
>         IF x.mode = ORB.Const THEN obj.val := x.a; obj.lev := x.b; obj.type := x.type


$ diff ORG.Mod OriginalOberon2013/Sources/ORG.Mod 
253c253
<     ELSIF (y.class = ORB.Const) & (y.type.form = ORB.String) THEN x.a := y.val MOD 10000H; (*strx*) x.b := y.val DIV 10000H (*len*)
---
>     ELSIF (y.class = ORB.Const) & (y.type.form = ORB.String) THEN x.b := y.lev  (*len*)
256c256
<     IF (y.lev > 0) & (y.lev # curlev) THEN ORS.Mark("level error, not accessible") END
---
>     IF (y.lev > 0) & (y.lev # curlev) & (y.class # ORB.Const) THEN ORS.Mark("level error, not accessible") END 



-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.inf.ethz.ch/pipermail/oberon/attachments/20180210/23a3341a/attachment.html>


More information about the Oberon mailing list