Soft Waffle
Soft Waffle

Reputation: 386

Can I define my own string type in Pascal?

I’ve read you cannot dynamically allocate an array in Pascal, but I’m also thinking of implementing a string struct.

In C, I would go about it by creating a struct containing a pointer to an array of chars (containing the characters), a length integer, and a size one. I would then malloc the char * and realloc it when it needs to be resized.

typedef struct {
    size_t size;
    size_t length;
    char* contents;
} String;

Can this be done in (ISO) Pascal? I don’t want to use a built‐in Pascal dynamic array because it defeats the purpose of making my own string type.

From the comments, it seems like ISO Pascal (both Standard and Extended) doesn’t support such things. Is it possible in Free Pascal?

Upvotes: 1

Views: 409

Answers (2)

Kai Burghardt
Kai Burghardt

Reputation: 1556

Can I define my own string type in Pascal?

You need to observe following characteristics of a string data type: In Pascal as defined by the ISO standard 7185 and 10206 a string data type is a

  • linear (exactly one dimension)
  • packed
  • array of
  • (the built‑in data type) char
  • starting at an integer index of 1, and
  • ending at an integer index of greater than 1.

For example packed array[1..2] of char is a string data type. You can pass such string variables to the built‑in procedures write and writeLn if the destination is a text file, and in Extended Pascal also read and readLn if the source is a text file.

I’ve read I can’t really dynamically allocate an array in Pascal […]

Yes/no:

  • For Standard Pascal as defined by ISO standard 7185, this claim is in practice true, in theory (usually) false. Depending on the implementation, you can dynamically allocate varying amounts of memory, but the overhead is prohibitively impracticable:

    program variableLengthStringDemo(output);
        const
            stringMaximumLength = 4;
        type
            integerNonNegative = 1..maxInt;
            stringLength       = 0..stringMaximumLength;
            stringCapacity     = 1..stringMaximumLength;
            string = record
                    length: stringLength;
                    case capacity: stringCapacity of
                        1: (character:           packed array[1..1] of char);
                        2: (characterPair:       packed array[1..2] of char);
                        3: (characterTriplet:    packed array[1..3] of char);
                        4: (characterQuadruplet: packed array[1..4] of char);
                end;
        var
            word: ^string;
        begin
            new(word, 3);
            word^.characterTriplet := 'ABC';
            word^.length := 3;
            writeLn(word^.characterTriplet)
        end.
    

    For identified record variables (identified means in ISO terminolgy essentially pointers) the built‑in procedures new and dispose accept further arguments to select a particular variant. This allows implementors of a processor to allocate only as much memory as necessary, yet doing so is not mandated by the standards. What is mandated, though, is that such a variable cannot change the variant anymore. In the above code word^.capacity is locked to 3.

    Moreover, since identifiers in all record variants must be unique, you have to handle multiple identifiers in your code too. This makes using this approach for character strings for productive purposes pretty useless. You cannot branch every time in your code just to select the correct identifier for the currently active variant. This would be insane.

  • For Extended Pascal as defined by ISO standard 10206, this claim is completely false. EP defines the notion of schema data types. Schema data types denote a set of data types. You need to discriminate this set, “select” one the data types out of the bag, to use it. The discriminants can be supplied to new and dispose, too.

    program variableLengthArrayDemo(output);
        type
            characterSequence(capacity: integer)
            = packed array[1..capacity] of char;
        var
            cheerfulMessage: ^characterSequence;
        begin
            new(cheerfulMessage, 12);
            cheerfulMessage^ := 'Hello world!';
            writeLn(cheerfulMessage^)
        end.
    

    There are other means to achieve variable‑length arrays in EP, this is just one.

NB: In EP and SP, should you dispose identified variables, dispose needs exactly the same arguments as of the corresponding new. new(variable, 3) → dispose(variable, 3).

: Actually, there are fixed string type (presented here), variable string type (EP’s string schema) and canonical string type (a meta data type purely for definitory purposes), all collectively known as string type.


[…] How do I do it in Free Pascal then?

Well …

  • In FreePascal

    • any kind of array of
    • char or a derivative of char as its base type
    • that has a single dimension
    • that is a built‑in ordinal data type or a derivative of such

    is a string. The FreePascal dialect permits you to typecast variables.

  • New (and dispose) accept further arguments only in {$mode ISO} and {$mode extendedPascal}.

  • As of 2024, the FreePascal Compiler (still) intends to support ISO 10206 but support for schema data types is still missing.

Upvotes: 1

Doj
Doj

Reputation: 1311

In Free Pascal it can be implemented similar to the mentioned C approach:

type
TMyString = record
  size: SizeUInt;
  length: SizeUInt;
  contents: PAnsiChar;
end;

...

procedure AllocMyString(var S: TMyString; L: SizeUInt);
begin
  S.size := 0;
  S.length := L;
  GetMem(Pointer(S.contents), L);
end;

procedure ReallocMyString(var S: TMyString; L: SizeUInt);
begin
  S.size := 0;
  S.length := L;
  ReAllocMem(Pointer(S.contents), L);
end;

Upvotes: 3

Related Questions