GPC contains a number of extensions to the ISO 7185 Pascal language.
Most of these extensions are written so that they should conform to the international standard ISO/IEC 10206 : 1991, Information technology -- Programming Languages -- Extended Pascal.
GPC is not yet fully compliant to the requirements of the Extended Pascal language.
The following Extended Pascal features are implemented:
extend(File)
+,-,/,* and monadic -,+
POW and **)
sqr,arctan,sqrt,exp,ln,sin,cos)
re, im and arg functions
cmplx or polar
POW and **)
Succ/Pred (val := succ (val, 5);)
gettimestamp, date, time
halt procedure
maxchar/minreal/maxreal/epsreal values.
base#number
StandardInput and StandardOutput
XOR) and CARD)
AND_THEN, OR_ELSE)
+"
trim,substr,index,length)
str[5..7] := 'foo';)
GPC extensions not in Extended Pascal:
unbind(F) also closes a bound file F)
halt procedure may have a numeric exit status parameter
mark/release
reset/rewrite/extend as a string
return / break / continue statements
sizeof/alignof functions
string[ XX ] works like string(XX) as a string schema
type selector
otherwise: others and default
FOR ch IN [ 'a'..'z','0'..'9' ] DO...)
NEW work with them
GPC implements "lazy" text file I/O, i.e. do a PUT as soon
as you can and do GET as late as you can.
This should avoid most of the problems sometimes considered to be the most stupid feature of Pascal.
When passing a file buffer as parameter the buffer is validated when the parameter is passed. @@ Perhaps it would be nice to hack it to be validated when the VAR parameter is referenced...
When any lazy file is RESET, the file buffer state is set
to undefined. It is validated on the first reference to it.
Now this is also true for terminal devices.
Extended Pascal has a "type selector" feature called schema types.
GPC does not yet implement general schema types, but the
STRING SCHEMA is now implemented.
(An example of a (unimplemented) schemata would be, e.g:
Matrix (N,M: Positive_int) = array [ 1..N, 1..M ] of integer;
Here the M and N are discriminant identifiers.)
A STRING SCHEMA is the only predefined schema type in Extended
Pascal, with one required discriminant identifier "Capacity".
The string schema type, if explicitely defined, could look like:
TYPE string(capacity) = packed array [ 1..capacity ] of char;
Internally GPC implements STRING SCHEMA as follows:
The type representing the SCHEMA TYPE is a RECORD_TYPE node,
with the following fields:
STRING = RECORD
Capacity : integer;
length : integer;
string : packed array [ 1..Capacity ] of char;
END;
The "Capacity" field may be directly referenced by user,
"length" is referenced by a predefined string function
LENGTH(str) and contains the current string length.
"string" contains the chars in the string.
The "string" and "length" fields can not be directly referenced
by a user program.
References to the schema discriminants are allowed, and
the WITH statement is also allowed, so one can say:
var str : string (80);
begin
writeln (str.capacity), (* writes 80 *)
with str do
writeln (capacity); (* writes 80 *)
end;
When a new SCHEMA_TYPE is created, the discriminant identifier
fields need to be initialized. GPC initializes the new schema
type discriminant identifiers of every VAR_DECL node before it
executes any instructions of the procedure, function or
program where the string variable is declared.
If new internal schema types are created (for conversion
of fixed-string or char type parameters to a string schema
formal parameter), the discriminant identifiers are
initialized immediately. The discriminant identifiers
of PARM_DECL nodes are not initialized separately, they
get their values from the actual parameters.
If a parameter is a SCHEMA_NAME (a schema with no discriminant
identifiers), a proto string schema is used as the type
of the parameter.
STRING_SCHEMA type. The type of the actual parameter is used
instead of the proto schema for the formal parameter.
STRING_SCHEMA type, a fixed string type or a char type. If
the actual parameter is a string schema type, that is used
instead of the proto schema. If it is not a schema, a new
variable length string VAR_DECL is created, the actual
parameter is copied to the new variable and the "capacity"
field is set to the length of the actual variable.
Variable length string parameters look like:
PROGRAM Zap (output);
TYPE
stype = string (10);
sptr = ^string;
VAR
str : stype;
str2 : string(100000);
dstr : ^string;
zstr : sptr;
len : integer value 256;
(* "string" accepts any length of strings *)
PROCEDURE foo(z: string);
BEGIN
writeln ('Capacity : ',z.capacity);
writeln ('Length : ',length (z));
writeln ('Contents : ',z);
END;
(* Another way to use dynamic strings *)
PROCEDURE bar(slen : integer);
var
lstring : string (slen);
foostr : type of lstring;
BEGIN
lstring := 'Hello World!';
foo (lstring);
foostr := 'Ent{ miksi juuri t{m{?';
foo(foostr);
END;
BEGIN
str := 'KUKKUU';
str2 := 'A longer string variable';
new (dstr, 1000); { Select the string Capacity with NEW }
dstr^ := 'The max length of this is 1000 chars';
new (zstr, len);
zstr^ := 'This should fit here';
foo(str);
foo(str2);
foo('This is a constant string');
foo('R'); { A char parameter to string routine }
foo("); { An empty string }
foo (dstr^);
foo (zstr^);
bar (10000);
END. (* Zap *)
In the above example, the required procedure NEW was used
to select the capacity of the strings. Procedure "BAR" also has
a string whose size depends of the parameter passed to it
and another string whose type will be the same than the type of
the first string ("type of" construct).
All string and char types are compatible as long as the destination string is long enough to hold the source in assignments. If the source string is shorter than the destination, the destination is automatically blank padded if the destination string is not of string schema type.
S1 and S2 may be of string or char type.
S is of string type.
WRITESTR (s, write-parameter-list)
READSTR (s, read-parameter-list)
TEXT
files. The semantics is closely modeled after
file I/O.
INDEX(s1,s2)
S2 is empty, return 1 else if S1 is empty return 0
else returns the position of s2 in s1 (an integer).
LENGTH (s1)
S1 (an integer from 0..Capacity)
TRIM (s1)
S.
SUBSTR (s1, i)
SUBSTR (s1, i, j)
J is missing it is calculated as: J := LENGTH (S1) - I + 1;
Return a new substring of S1 that contains J characters
starting from I.
EQ (s1,s2)
NE (s1,s2)
LT (s1,s2)
LE (s1,s2)
GT (s1,s2)
GE (s1,s2)
S1 and S2. Returns boolean result.
Strings are not padded with spaces.
s1 = s2
s1 <> s2
s1 < s2
s1 <= s2
s1 > s2
s1 >= s2
S1 and S2. Returns boolean result.
Shorter string is blank padded to length of the longer one.
In GPC you are free to re-define everything that is not a reserved word in ISO 7185 Pascal in your program.
All Extended Pascal additional "reserved words" may be redefined,
so you do not have to modify your code for GPC if you have an
identifier like RESTRICTED or VALUE or some such.
@@ This violates Extended Pascal standard.
You may also redefine words like INTEGER and CHAR if you like.
@@ NOTE: The *only* exception to the redefinition rule currently
is the word INLINE (to make routines inline compiled), because I
added it in front of PROCEDURE or FUNCTION. But I think I will
change the syntax later and make INLINE a directive instead of a
reserved word.
to get info of possible clashes of keywords and other
info of your program constructs that gpc thinks are "non-standard"
use the switch "-pedantic" when compiling. See the GCC info files.
@@ I have not tested the switches like -Wall very much. If you do, @@ give me info of error messages that don't make sense in Pascal.
@@ As a rule, GPC implements most of the switches GCC implements, and a couple of more that can not currently be set.
FORWARD
EXTERNAL
foo()" will actually call "Foo()")
EXTERN
C
foo()" as "foo()"
(no capitalization of the first letter)
C_LANGUAGE
STATIC
PROGRAM foo; PROCEDURE gotoxy(x,y: Integer); C; BEGIN gotoxy(10,10); (* Call external routine "gotoxy" *) END.
GPC supports standard Pascal set operations. In addition it
supports the extended Pascal set operation symmetric
difference (set1 >< set2) operation (a XOR of the set
elements).
It also has a function that counts the elements in the set: `a := card (set1)'
NOTE: the set operations are still under construction, e.g. the set code does not fully work in the 64 bit Alpha machines.
A type (or variable) may be initialized to a value of expression when it is declared, as in:
program zap;
type
int10 = integer value 10;
footype = real;
mytype = char value pred('A');
etype = (a,b,c,d,e,f,g) value d;
var
ii : int10; (* Value of ii set to 10 *)
ch : mytype value pred('z');
aa : integer value ii+10;
foo : footype value sqrt(aa);
e1 : etype; (* value set to d *)
e2 : etype value g; (* value set to g *)
begin
end.
Extended pascal requires the type initializers to be constant expressions. GPC allows any valid expression.
Note, however, that the expressions that affect the size of storage allocated for objects (e.g. the length of arrays) may contain variables only inside functions or procedures.
GPC evaluates the initial values used for the type when an identifier is declared for that type. If a variable is declared with a type-denoter that uses a type-name which already has an initial value the latter initialization has precedence.
@@ GPC does not know how to calculate constant values for math functions in the runtime library at compile time, e.g. `exp(sin(2.4567))', so you should not use these kind of expressions in object size expressions. (Extended Pascal allows this).
Predefined date and time routines:
procedure gettimestamp(VAR t: Timestamp);
function date(t: Timestamp) : packed array [ 1..DATE_LENGTH ] of char;
function time(t: Timestamp) : packed array [ 1..TIME_LENGTH ] of char;
DATE_LENGTH and TIME_LENGTH are implementation dependent
constants. See E.20 and E.22 in chapter IMPLEMENTATION DEPENDENT FEATURES
to find out these values for GPC.
GetTimeStamp(t) fills the record T with values. If they are
valid, the boolean flags are set to TRUE.
TimeStamp is a required predefined type in extended pascal standard.
(It may be extended in an implementation.)
The required part of the type looks like:
TimeStamp = PACKED RECORD DateValid, TimeValid : Boolean; year : integer; month : 1 .. 12; day : 1 .. 31; hour : 0 .. 23; minute : 0 .. 59; second : 0 .. 59; END;
@@ NOTE:
TimeStamp may be later extended in GPC to contain the
following fields at the end of the TimeStamp record:
Dst_used : Boolean; (* If daylight savings are used *)
TimeZone : Integer; (* Positive if WEST, in minutes *)
Weekday : 0..6; (* 0 is Sunday *)
TimerValid : Boolean; (* Is the following timer valid *)
us_Timer : Integer; (* A microsecond timer that is a 32 bit
modulus of the timer returned by the
system. *)
Fields Dst_used, TimeZone and WeekDay will be valid when
DateValid is TRUE. Field us_Timer will be valid when
TimerValid is TRUE.
The following sample programs illustrates most of the
COMPLEX type operations. In addition monadic + and
- are supported and dyadic +,-,*,/ operations.
program complex_test(output);
var
z1,z2 : complex;
len, angle : real;
begin
z1 := cmplx (2,1);
writeln;
writeln ('Complex number Z1 is: (',re(z1):1,',',im(z1):1,')');
writeln;
z2 := conjugate(z1); { GPC extension }
writeln ('Conjugate of Z1 is: (',re(z2):1,',',im(z2):1,')');
writeln;
len := abs (z1);
angle := arg (z1);
writeln ('The polar representation of Z1 is LENGTH=',len:1,
' ANGLE=',angle:1);
writeln;
z2 := polar (len, angle);
writeln ('Converting (LENGTH,ANGLE) back to (X,Y) gives: (',
re(z2):1,',',im(z2):1,')');
writeln;
writeln ('The following operations operate on the complex number Z1');
writeln;
z2 := arctan (z1);
writeln ('arctan: R=',re(z2),', I=',im(z2));
z2 := z1 ** 3.141;
writeln ('**3.141: R=',re(z2),', I=',im(z2));
{ cos, ln, exp, sqrt and sqr exist also }
z2 := sin(z1);
writeln ('sin: R=',re(z2),', I=',im(z2));
z2 := z1 pow 8;
writeln ('POW 8: R=',re(z2),', I=',im(z2));
z2 := z1 pow (-8);
writeln ('POW (-8): R=',re(z2),', I=',im(z2));
end.
@@ Not tested. @@ Write a demo program.
type Dfile = file [ 1 .. 100 ] of integer; var F : Dfile; P, N : 1..100;
Declares a type for a file that contains 100 integers.
The following direct access routines may be applied to a direct access file:
SeekRead (F, N); { Open file in Inspection mode, seek to record N }
SeekWrite (F, N); { Open file in Generation mode, seek to record N }
SeekUpdate (F, N); { Open file in Update mode, seek to record N }
Update (F); { Writes F^, position not changed. F^ kept. }
p := Position (F); { Return current record number }
p := LastPosition (F); { Return the last record number in file }
If the file is open for Inspection or Update, GET may be applied.
If the file is open for Generation or Update, PUT may be applied.
@@ GPC acts like the file would always start at record number 0, and subtracts/adds the lower index from the record number. If you think this is incorrect, let me know.
Extended Pascal defines restricted types as:
restricted-type = 'restricted' type-name .
A value of a restricted type may be passed as a value parameter to a formal parameter possessing its underlying type, or returned as the result of a function. A variable of a restricted type may be passed as a variable parameter to a formal parameter possessing the same type or its underlying type. No other operations, such as accessing a component of a restricted type value or performing arithmetic, are possible.
program zap;
type
unres_rec = record
a : integer;
end;
res = restricted unres_rec;
var
r1 : unres_rec;
r2 : res;
i : restricted integer;
k : integer;
function zap(p : unres_rec) : res;
var
ures : unres_rec;
begin
{ The parameter is treated as unrestricted, even though the actual
parameter may be a restricted object }
ures.a := p.a;
{ Legal to assign a return value }
zap := ures;
end; { zap }
begin
r1.a := 354;
{ Assigning a restricted return value to a restricted object }
{ @@ Verify if this should really be allowed????? }
r2 := zap(r1);
{ Passing a restricted object to unrestericted formal parameter is ok }
r2 := zap(r2);
{ *** The following are illegal *** }
r2.a := 100; { field access }
r1 := r2; { := source is restricted type }
r2 := r1; { := target is restricted type }
r1 := zap(r2); { := a restricted return value to unrestricted object }
i := 16#ffff; { := target is restricted type }
k := i + 2; { Arithmetic with restricted type }
end.
@@ Gpc does not yet support:
=>'
QUALIFIED interfaces
PROTECTED export variables
ONLY
IMPORT does not work semantically correct.
EXPORT does not work semantically correct.
abort())
Gpc should be able to parse full Extended Pascal module syntax. But all the features are not implemented yet.
You may load one PROGRAM and several MODULEs to make up one pascal program. A single file may contain zero or more modules and/or zero or one programs.
Please NOTE: If you have many modules in the same file, the variable and function declarations are visible after the point they have been declared in the implementation even if the interface does not export them. But they do not become visible only by including the interface to another file and separate compiling that (so you do need to export them now). (@@ unfortunately, currently this applies only to variables and functions; all other things are visible after the interface has been compiled whether or not you exported them.)
The nicest way to handle the module interface in separate compilation environment is to use the non-standard
#include "module-interface.ph"
feature. You can collect your module interfaces to a single
directory and include them from there by using the
"-I DIR" switches to specify the include file search paths
to the compiler. (See the GNU CPP manual for more info).
There is currently no attempt to avoid name clashes of separate compiled modules when they are linked together. (The exported variables and functions having the same name in different modules will clash!!!)
Sample module code with separate INTERFACE and IMPLEMENTATION
parts follows:
MODULE foobar Interface; (* INTERFACE *)
EXPORT catch22 = (footype,setfoo,getfoo);
TYPE footype = integer;
PROCEDURE setfoo(f: footype);
FUNCTION getfoo: footype;
END. { module foobar interface }
MODULE foobar Implementation; (* IMPLEMENTATION *)
IMPORT StandardInput;
StandardOutput;
VAR foo : footype;
{ Note: the effect is the same as the Forward directive would have:
parameter lists and return types are not "allowed" in the declaration
of exported routines. }
PROCEDURE setfoo;
BEGIN
foo := f;
END;
FUNCTION getfoo;
BEGIN
getfoo := foo;
END;
TO BEGIN DO
BEGIN
foo := 59;
writeln ('Just an example of a module initializer. See comment below');
END;
TO END DO
BEGIN
foo := 0;
writeln ('Goodbye');
END;
END. { foobar implementation }
Alternatively the module interface and implementation may be combined as follows:
MODULE foobar; (* ALTERNATIVE METHOD *)
EXPORT catch22 = (footype,setfoo,getfoo);
TYPE footype = integer;
PROCEDURE setfoo(f: footype);
FUNCTION getfoo: footype;
END; { NOTE: this END is required here, even if the
module-block below would be empty. }
VAR foo : footype;
PROCEDURE setfoo;
BEGIN
foo := f;
END;
FUNCTION getfoo;
BEGIN
getfoo := foo;
END;
END. { module foobar }
Either one of the two methods may be used with:
PROGRAM what(output); import catch22; BEGIN setfoo (999); writeln (getfoo); END.
The INTERFACE has to be in the same file as the program/module that
uses it's exported names. Otherwise GPC does not know anything
about it and fails to compile the file.
Note: this is not supported in Extended Pascal standard.
This is a simpler module support that does not require exports, imports, module headers etc.
These non-standard simple Gpc modules look like (does not have an export part, does not have a separate module-block, does not use import/export features.)
MODULE foobar;
TYPE footype = integer;
VAR foo: footype;
PROCEDURE setfoo(f: footype);
BEGIN
foo := f;
END;
FUNCTION getfoo: footype;
BEGIN
getfoo := foo;
END;
END.
PROGRAM what(output);
(* In case the module foobar is loaded from another file *)
PROCEDURE setfoo(f: footype); External;
FUNCTION getfoo: footype; External;
BEGIN
setfoo (999);
writeln (getfoo);
END.
TO BEGIN DO module initialization and TO END DO module
finalization constructs are supported if the GNU compiler supports
constructors and destructors in your target machine. (It always does if you
use the GNU Linker).
If the initialization and finalizations do not work by default, but
you have the GNU Linker, use option -fgnu-linker when compiling the
program.
I re-implemeted the standard I/O handling and now the input and output can also be used from the initialization and finalization parts.
@@ Try these, send me bug reports. These are not tested.
GPC supports the extended pascal bind,unbind and binding
operations when applied to files.
The compiler will currently reject binding of other object types (@@ Perhaps the run time system should do the rejection?)
GPC implements extensions to the required predefined record type BindingType:
BindingType = PACKED_RECORD
Bound : Boolean;
Extensions_Valid : Boolean;
Writable : Boolean;
Readable : Boolean;
Existing : Boolean;
Error : Integer; { Unused currently }
Size : Integer; { # of elements or -1 }
Name : String (BINDING_NAME_LENGTH);
END;
The fields BOUND and NAME are required by the standard. All
other fields are extensions.
The meaning of the extensions to the BindingType record type,
and the value of BINDING_NAME_LENGTH is defined in this document,
section IMPLEMENTATION DEFINED FEATURES (E.14). It is a compiler
constant, the run time system accepts any length.
The Size field is a latest addition to BindingType; I added
that because the direct access files actually require that the file
is not bigger that the definition; and lastposition(file) does
not work before the file is opened. The "Size" field can then
be used to determine the size before open, and if the upper
bound of the direct access file is a variable one should be able
to open files of any size without violating the standard.
The following is an example of the binding:
program z(input,output,f);
var
f : text;
procedure bindfile (varf : text);
var
b : BindingType;
begin
unbind (f);
b := binding (f);
repeat
write ('Enter file name:');
readln (b.name);
bind (f, b);
b := binding (f);
if not b.bound then
writeln ('File not bound--try again');
until b.bound;
end;
begin
bindfile (f);
(* Now the file F is bound to an external file.
*
* You can use the implementation defined fields
* to check if the file is Readable, Writable and
* if it Exists. These are valid if the.Extensions_Valid
* field is TRUE.
*)
end.
GPC suports also function pointers and calls through them. This is a non-standard feature.
program zap(output);
type
proc_ptr = ^ procedure (integer);
var
pvar : proc_ptr;
procedure write_int(i: integer);
begin
writeln ('Integer: ',i:1);
end;
begin
(* PVAR points to function WRITE_IT *)
pvar := &write_int;
(* Dereferencing a function pointer calls the function *)
pvar^(12345);
end.
Gpc supports string catenation with the '+' operator.
All string-types are compatible, so you may catenate any chars,
fixed length strings and variable length strings with each other.
program scat (input, output);
var
ch : char;
str : string(100);
str2 : string(50);
fstr : packed array [ 1 .. 20 ] of char;
begin
ch := '$';
fstr := 'demo'; { padded with blanks }
write ('Give me some chars to play with: ');
readln (str);
str := '^' + 'prefix:' + str + ':suffix:' + fstr + ch;
writeln ('Len' + 'gth = ', length (str));
writeln (str);
end.
@ New feature. @ Currently gpc runtime does not know anything about these. @ These may change/or get removed...
As an extension, GPC allows you to use type qualifiers:
__byte__
__short__
__long__
__longlong__
__unsigned__
The __unsigned__ works for all integer types, also those
that have been previously declared with some other type
qualifier, like __short__. The other qualifiers do not accept
types that have already been modified with a type qualifier.
The syntax to use the qualifiers:
type-denoter > TYPE-QUALIFIER type-name
(The metasymbol `>' means type-denoter has also other meanings)
Most of these should be done with subranges anyway.
However, '__short__ real' can not be done like that, neither can
'__unsigned__ integer' or '__longlong__ integer'.
program zap(output); type byte = __byte__ integer; longint = __long__ integer; float = __short__ real; u_long = __unsigned__ longint; verylong = __longlong__ integer; var i8 : byte; i16 : __short__ integer; foo : u_long; pi : float; big : verylong; begin pi := 3.141592654; i16 := 1000; big := MaxInt * i16; i8 := 127; (* * Hmm, does not work because constant is treated as an integer, * and this is too large. Need a method to specify long constants. * * What is the syntax in other Pascal compilers? Suggestions, please! * foo := 16#deadbeef; *) end.
The following module accesses the command line with
ParamStr and ParamCount functions.
These follow the Un*x semantics, so that
arg[0] == program name,
arg[1] .. arg[ParamCount-1] are the arguments.
MODULE command_line interface;
EXPORT cmdline = (Max_length, Arg_type, ParamStr, ParamCount);
CONST
Max_length = 255; { Max length of each argument.
If some arg is longer, the run time system
traps it. }
TYPE
Arg_type = String(Max_length);
FUNCTION ParamCount: Integer;
FUNCTION ParamStr (arg_num: integer): Arg_type;
END. { command_line interface }
MODULE command_line implementation;
{ These are in the GPC runtime library }
FUNCTION _p_paramcount : Integer; C;
FUNCTION _p_paramstr (num: Integer; VAR str: String): Boolean; C;
FUNCTION ParamCount;
BEGIN
ParamCount := _p_paramcount;
END; { ParamCount }
FUNCTION ParamStr;
VAR
Str : Arg_type;
Success : Boolean;
BEGIN
Success := _p_paramstr (arg_num, Str);
(* Should perhaps do something else on failure.
*
* Now it returns the empty string, which is also a valid
* parameter.
*)
IF Success THEN
ParamStr := Str
else
ParamStr := ";
END; { ParamStr }
END. { command_line implementation }
{ The program below, when compiled with the interface module and
linked with the implementation module, accesses the command
line arguments. }
program zap (output);
import cmdline;
var
counter : integer;
begin
writeln ('Program fetches command line arguments and outputs one per line');
writeln ('Max length of each argument is ',Max_Length:1,' characters');
for counter := 0 to ParamCount-1 do
writeln ('Command line arg ',counter:1,' is "',paramstr(counter),'"');
end.
GNU Pascal implements these Borland extensions to the ISO Pascal language:
Program headline may be omitted in TP/BP.
If the headline is given, the parameters Input and Output are
optional. I modified GPC such that it warns about a missing program
header, but warns about missing Input and Output parameters
only if pedantic.
Procedure Foo ( protected a, b, c: Integer ); (* 3 args *) Procedure Foo ( a, b, c, protected: Integer ); (* 4 args *) Procedure Foo ( a, b, protected, c: Integer ); (* 4 args *) Procedure Foo ( protected: Integer ); (* 1 arg *) Procedure Foo ( Var protected: Integer ); (* 1 arg *) Procedure Foo ( protected protected: Integer ); (* 1 arg *)Furthermore, I implemented "Const" as an alternative to "protected" (according to Borland Pascal)
(*$B+*) or {$B+} Boolean complete evaluation
(*$B-*) or {$B-} --short-circuit
(*$c+*) or {$c+} --c-numbers
(*$E+,L+,N+*) --char-escapes, --lazy-io,
--nested-comments
(* These switches are local and can change during one compile *)
{$p+} --pedantic
{$P-} end of --pedantic
(*$I FileName *) #include "filename.pas"
{$m Hello! } write message "Hello!" to stderr
(*$D FOO bar *) #define FOO bar
{$define CMULB} #define CMULB
(*$include <hello.ph> *) #include <hello.ph>
#ifdef FOO
(*$ifdef FOO*) ... (*$endif*) ...
#endif
(* ... and all the other C preprocessor directives ... *)
By the way: I implemented an option --borland-pascal symmetrically
to --extended-pascal and --object-pascal. But I couldn't figure out
what they serve for since I didn't notice any difference in the
compiler's behaviour with and without these options. Nevertheless,
I made --borland-pascal to switch on --nested-comments, so it has
at least one effect :-).
2#100101 and ( 1 shl 5 ) = 2#100000I could not restrain, but also implemented "and", "or", "xor" and "not" as "procedures":
x:= 7; and ( x, 14 ); (* yields 6 *) xor ( x, 3 ); (* yields 5 *)(This is a feature I often missed with Borland Pascal.)
Var x: Integer; c: Char; inc ( i ); (* i:= i + 1; *) dec ( i, 7 ); (* i:= i - 7; *) inc ( c, 3 ); (* c:= chr ( ord ( c ) + 3 ); *)
GetMem ( MyPtr, 1024 ); FreeMem ( MyPtr, 1024 );GPC now supports this and also a "function-style" call to `GetMem':
MyPtr:= GetMem ( 1024 );(see also: New in context of Object Orientated Programming) One somehow strange feature of Borland is *not* supported: You can free parts of a variable with FreeMem, while the rest is still used and can be FreeMem'ed later by another pointer:
Type Vector = array [ 0..65535 ] of Integer; VecPtr = ^Vector; Var p, q: VecPtr; ... GetMem ( p, 1024 * SizeOf ( Integer ) ); q:= &p^ [ 512 ]; ... FreeMem ( p, 512 * SizeOf ( Integer ) ); ... FreeMem ( q, 512 * SizeOf ( Integer ) );
$cafe = 2#1100101011111110
Const
A: Integer = 7;
B: array [ 1..3 ] of Char = ( 'B', 'a', 'r' );
(* TP/BP also would also understand " = 'Bar'; ". *)
Foo: record
x, y: Integer;
end (* Foo *)
= ( x: 3; y: 4 );
Borland and ISO style for the right-hand side are both supported.
Once working on this, I also implemented VAX Pascal variable ini-
tializing with `:=' (as an alternative to `value') and also with
`=' (like in Borland "initialized variables").
Warning: This was one of my last changes and is not yet stable. I
could not, for example, recover from 3 shift/reduce conflicts and 1
reduce/reduce conflict. This causes trouble when the type (for
example a subrange) ends up with an expression such that the parser
takes the `=' as a relational operator.
Procedure ReadVar ( Var x: Void; TypeChoice: Char ); Var xInt: Integer absolute x; xChar: Char absolute x; xStr: String ( 80 ) absolute x; begin (* ReadVar *) ... end (* ReadVar *);
Function MyFunc: Integer; AsmName 'MyPrettyFunc_';With this extension it is possible to access all external functions, for example the XT interface functions, and not only those written in lowercase. My first idea to use `external' for this purpose (to avoid name space pollution) conflicts with another Borland extension not yet implemen- ted: In Borland Pascal, the declaration
Procedure Foo; external 'MyLib';means that the procedure Foo should be imported by name ("Foo") from a dynamic link library "mylib.dll".
@" in TP/BP.
Implemented into GPC as an alternative to "&".
case 1..3 of ..." is allowed now.
Type
MyParentPtr = ^MyParentObj;
MyPtr = ^MyObj;
MyParentObj = object
...
end (* MyParentObj *);
MyObj = object ( MyParentObj )
a, b, c: Integer;
Constructor Init;
d, e: Char; (* GNU extension: Data fields *)
Destructor Fini; virtual;(* and methods may be mixed *)
Procedure Foo ( x: Integer );
Function Bar: Char; virtual;
(* "private" is not (yet) implemented *)
end (* MyObj *);
Var
My: MyParentPtr;
...
Constructor MyObj.Init;
begin (* MyObj.Init *)
inherited Init;
a:= 0;
MyParentObj.Bar;
end (* MyObj.Init *);
...
My:= New ( MyPtr, Init );
My^.Foo ( 3 );
Dispose ( My, Fini );
New ( My, Init );
with My^ do
writeln ( Bar );
I first tried to recycle parts of the C++ and/or ObjC frontend, but
I gave up after a few hours. Since I was not able to understand
how they work, I re-invented and implemented my own Object frontend
-- the third one I noticed in the GNU compiler family. Sorry.
(*$X+*)
Type
Vec3 = record
x, y, z: Real;
end (* Vec3 *);
Var
a, b, c: Vec3;
Operator + ( u, v: Vec3 ) w: Vec3;
begin (* Vec3 + Vec3 *)
w.x:= u.x + v.x;
w.y:= u.y + v.y;
w.z:= u.z + v.z;
end (* Vec3 + Vec3 *);
...
c:= a + b;
Extended Pascal would require an equal sign before the return value
variable specification (`w' in the above example) while PXSC for-
bids it. Therefore I allow the equal sign to be present or not,
both in a function declaration as well as in an operator declaration.