Bladeren bron

wiki mirror and examples

Vladislav Folts 11 jaren geleden
bovenliggende
commit
ec38d78fa5
3 gewijzigde bestanden met toevoegingen van 256 en 0 verwijderingen
  1. 70 0
      doc/wiki/refinements.md
  2. 161 0
      test/examples/String.ob
  3. 25 0
      test/examples/TestString.ob

+ 70 - 0
doc/wiki/refinements.md

@@ -0,0 +1,70 @@
+Here are implemented refinements to the original language report. 
+* These refinements do not contradict to the original report.
+* These refinements are not extensions of the original report because they do not extend syntax or language semantics.
+* These refinements are not directly inherited from original report using "common sense".
+
+However these refinements are crucial from programming techniques standpoint so they are worth to be documented explicitly.
+
+1. Support opaque data types.
+-----------------------------
+
+Consider you want to encapsulate some data structure inner details. But you need this data to be passed to/from the module. You can do it in this manner:
+
+    MODULE impl;
+
+    TYPE T* = RECORD hiddenField: INTEGER END;
+
+    PROCEDURE do(t: T);
+    (* t.hiddenField can be accessed only in this module *)
+    END do;
+
+    END impl.
+
+This example works just fine until you need some specific initialization for 'hiddenField'. And only module 'impl' knows how to initialize T in the right way. We can export special procedure for initialization:
+
+    PROCEDURE init*(VAR t: T);
+    BEGIN
+        t.hiddenField := 123;
+    END init;
+
+All other modules have to call impl.init(t) every time they need to initialize T. But this requirement can be easily ignored leading to some run-time errors.
+
+To resolve this problem we can export only pointer type and **forbid creation (NEW) for non-exported RECORD types**.
+Full example:
+
+    MODULE impl;
+
+    TYPE T* = POINTER TO RECORD hiddenField: INTEGER END;
+
+    PROCEDURE init*(): T;
+    VAR result: T;
+    BEGIN
+    	NEW(result);
+        result.hiddenField := 123;
+    	RETURN result
+    END init;
+
+    PROCEDURE do*(t: T);
+    (* t.hiddenField can be accessed only in this module *)
+    END do;
+
+    END impl.
+
+    MODULE test;
+    IMPORT impl;
+    VAR p: impl.T;
+    BEGIN
+    	(* NEW(p) will produce complier error *)
+    	p := impl.init();
+    	impl.do(p);
+    END test.
+
+2. Variables default values
+---------------------------
+All variables have zero as a default value (before first assignment). For pointer/procedure types it is NIL. For sets - empty set.
+
+    MODULE m;
+    VAR r: RECORD field: INTEGER END;
+    BEGIN
+        ASSERT(r.field = 0);
+    END m.

+ 161 - 0
test/examples/String.ob

@@ -0,0 +1,161 @@
+MODULE String;
+CONST
+    offs8 = 8;
+    offs16 = 8 + 16;
+    offs32 = offs16 + 32;
+    offs64 = offs32 + 64;
+    offs128 = offs64 + 128;
+    offs256 = offs128 + 256;
+    offs512 = offs256 + 512;
+    offs1024 = offs512 + 1024;
+    chunkSize = 4096;
+TYPE
+    Type* = POINTER TO RECORD
+        size: INTEGER;
+        data: ARRAY 8 OF CHAR;
+        next: POINTER TO Data16
+    END;
+    Data16 = RECORD
+        data: ARRAY 16 OF CHAR;
+        next: POINTER TO Data32
+    END;
+    Data32 = RECORD
+        data: ARRAY 32 OF CHAR;
+        next: POINTER TO Data64
+    END;
+    Data64 = RECORD
+        data: ARRAY 64 OF CHAR;
+        next: POINTER TO Data128
+    END;
+    Data128 = RECORD
+        data: ARRAY 128 OF CHAR;
+        next: POINTER TO Data256
+    END;
+    Data256 = RECORD
+        data: ARRAY 256 OF CHAR;
+        next: POINTER TO Data512
+    END;
+    Data512 = RECORD
+        data: ARRAY 512 OF CHAR;
+        next: POINTER TO Data1024
+    END;
+    Data1024 = RECORD
+        data: ARRAY 1024 OF CHAR;
+        next: POINTER TO Chunk
+    END;
+    Chunk = RECORD
+        data: ARRAY chunkSize OF CHAR;
+        next: POINTER TO Chunk
+    END;
+
+PROCEDURE fromArray*(a: ARRAY OF CHAR): Type;
+VAR 
+    result: Type;
+    
+    PROCEDURE copy(from: INTEGER; VAR dst: ARRAY OF CHAR): BOOLEAN;
+    VAR
+        i: INTEGER;
+    BEGIN
+        WHILE (i < LEN(dst)) & (i + from < LEN(a)) DO
+            dst[i] := a[i + from];
+            INC(i);
+        END;
+        RETURN i + from < LEN(a)
+    END copy;
+
+    PROCEDURE copyToChunks();
+    VAR
+        i: INTEGER;
+        from: INTEGER;
+        chunk: POINTER TO Chunk;
+    BEGIN
+        from := offs1024;
+        NEW(chunk); 
+        result.next.next.next.next.next.next.next.next := chunk;
+        WHILE from < LEN(a) DO
+            IF i = LEN(chunk.data) THEN
+                NEW(chunk.next);
+                chunk := chunk.next;
+                i := 0;
+            END;
+            chunk.data[i] := a[from];
+            INC(i);
+            INC(from);
+        END;
+    END copyToChunks;
+
+BEGIN
+    NEW(result);
+    IF copy(0, result.data) THEN
+        NEW(result.next);
+        IF copy(offs8, result.next.data) THEN
+            NEW(result.next.next);
+            IF copy(offs16, result.next.next.data) THEN
+                NEW(result.next.next.next);
+                IF copy(offs32, result.next.next.next.data) THEN
+                    NEW(result.next.next.next.next);
+                    IF copy(offs64, result.next.next.next.next.data) THEN
+                        NEW(result.next.next.next.next.next);
+                        IF copy(offs128, result.next.next.next.next.next.data) THEN
+                            NEW(result.next.next.next.next.next.next);
+                            IF copy(offs256, result.next.next.next.next.next.next.data) THEN
+                                NEW(result.next.next.next.next.next.next.next);
+                                IF copy(offs512, result.next.next.next.next.next.next.next.data) THEN
+                                    copyToChunks();
+                                END;
+                            END;
+                        END;
+                    END;
+                END;
+            END;
+        END;
+    END;
+
+    result.size := LEN(a);
+    RETURN result
+END fromArray;    
+
+PROCEDURE size*(s: Type): INTEGER;
+    RETURN s.size
+END size;
+
+PROCEDURE at*(s: Type; index: INTEGER): CHAR;
+VAR
+    result: CHAR;
+
+    PROCEDURE chunkAt(): CHAR;
+    VAR
+        i: INTEGER;
+        chunk: POINTER TO Chunk;
+    BEGIN
+        chunk := s.next.next.next.next.next.next.next.next;
+        FOR i := 0 TO ((index - offs1024) DIV chunkSize) - 1 DO
+            chunk := chunk.next;
+        END;
+        RETURN chunk.data[(index - offs1024) MOD chunkSize]
+    END chunkAt;
+
+BEGIN
+    IF index < offs8 THEN
+        result := s.data[index];
+    ELSIF index < offs16 THEN
+        result := s.next.data[index - offs8];
+    ELSIF index < offs32 THEN
+        result := s.next.next.data[index - offs16];
+    ELSIF index < offs64 THEN
+        result := s.next.next.next.data[index - offs32];
+    ELSIF index < offs128 THEN
+        result := s.next.next.next.next.data[index - offs64];
+    ELSIF index < offs256 THEN
+        result := s.next.next.next.next.next.data[index - offs128];
+    ELSIF index < offs512 THEN
+        result := s.next.next.next.next.next.next.data[index - offs256];
+    ELSIF index < offs1024 THEN
+        result := s.next.next.next.next.next.next.next.data[index - offs512];
+    ELSE
+        result := chunkAt();
+    END
+    RETURN result
+END at;
+
+END String.

+ 25 - 0
test/examples/TestString.ob

@@ -0,0 +1,25 @@
+MODULE TestString;
+IMPORT String;
+CONST
+    longStringSize = 1024 * 100 + 1;
+VAR
+    s: String.Type;
+    testArray: ARRAY longStringSize OF CHAR;
+    i: INTEGER;
+BEGIN
+    s := String.fromArray("abc");
+    ASSERT(String.size(s) = 3);
+    ASSERT(String.at(s, 0) = "a");
+    ASSERT(String.at(s, 1) = "b");
+    ASSERT(String.at(s, 2) = "c");
+
+    FOR i := 0 TO LEN(testArray) - 1 DO
+        testArray[i] := CHR(i);
+    END;
+    s := String.fromArray(testArray);
+    ASSERT(String.size(s) = LEN(testArray));
+    FOR i := 0 TO LEN(testArray) - 1 DO
+        ASSERT(testArray[i] = String.at(s, i));
+    END;
+
+END TestString.