P Language Syntax
Table of contents
1 Lexical structure
1.1 Identifiers
Identifiers denote variables, parameters, constants, types, functions and procedures.
x
foo
Writeln
pi
Qwerty123
string_literal
_abc
1.2 Keywords
Keyword have context dependent meaning in programs. Keywords may not be used as identifiers. The P language has following keywords:
and
array
base
begin
boolean
case
char
const
constructor
div
do
downto
else
end
external
false
for
forward
function
if
implementation
integer
interface
mod
new
nil
not
object
of
or
override
procedure
pointer
program
real
repeat
shl
shr
string
then
this
to
true
type
until
unit
uses
var
while
xor
virtual
1.3 Numbers
0
1
123
1e3
0.5
12E-4
1.4 Strings
''
'abc'
''''
'control-line'#13#10
'345'
'joe.coder@foo.bar'
'''quoted string'''
2 Types
2.1 Simple types
type
Byte = 0..255;
Citrus = orange..lemon;
type
TrafficLight = (red, yellow, green);
Fruit = (apple, banana, orange, grapefruit, lemon);
Size of integer type object is 4 bytes. Range of integer type value is from -2,147,483,647 to 2,147,483,647.
Size of Boolean type object is 1 byte. Boolean type object can have two values: true
and false
.
Size of char type object is 1 byte. Char type value can have ASCII code value from 0 to 255.
Size of real type object is 4 bytes. Range of real type value is from 1.175494351E-38 to 3.402823466E+38.
2.2 String type
String literals and variables have type string
. Strings are immutable. To construct a string piecewise you may use the StringBuilder
object type of the System
library.
2.3 Pointer type
A variable of type pointer
enable external
subroutines to store a C++ pointer to a P object and retrieve it later.
2.4 Object types
type
Empty = object
end;
Point = object
x, y: real;
constructor();
constructor(x, y: real);
procedure Print();
procedure Offset(dx, dy: real);
end;
Size = object
w, h: real;
constructor();
constructor(w, h: real);
procedure Print();
end;
Rect = object
location: Point;
size: Size;
constructor();
constructor(location: Point; size: Size);
procedure Print();
procedure SetLocation(location: Point);
procedure SetSize(size: Size);
function Left(): real;
function Top(): real;
function Right(): real;
function Bottom(): real;
function IsEmpty(): boolean;
function Contains(p: Point): boolean;
function IntersectsWith(r: Rect): boolean;
procedure Offset(dx, dy: real);
procedure Inflate(dx, dy: real);
end;
Shape = object
constructor();
procedure Print(); virtual;
procedure Measure(graphics: Graphics); virtual;
procedure Draw(graphics: Graphics); virtual;
function Bounds(): Rect; virtual;
end;
List = object of T
items: array of T;
count: integer;
constructor();
function IsEmpty(): boolean;
procedure Add(item: T);
function Get(index: integer): T;
function IndexOf(item: T): integer;
procedure Remove(index: integer);
end;
ShapeList = List of Shape;
CompoundShape = object(Shape)
components: ShapeList;
constructor();
procedure Print(); override;
procedure Add(shape: Shape);
procedure Measure(graphics: Graphics); override;
procedure Draw(graphics: Graphics); override;
function CombinedBounds(): Rect;
end;
The following example shows declaring virtual and overridden methods:
program virtual_shape;
type
Shape = object
procedure Print(); virtual;
end;
Circle = object(Shape)
procedure Print(); override;
end;
var
s, c: Shape;
procedure Shape.Print();
begin
Writeln('Shape.Print()');
end;
procedure Circle.Print();
begin
base.Print();
Writeln('Circle.Print()');
end;
begin
s := new Shape();
s.Print();
c := new Circle();
c.Print();
end.
2.5 Array types
type
IntArray = array of integer;
PointArray = array of Point;
Vector = array of real;
Matrix = array of Vector;
2.6 Specializations
type
IntList = List of integer;
StringList = List of string;
ShapeList = List of Shape;
3 Variables and constants
var
i: integer;
r, q: real;
fruit: Fruit;
p: Point;
u, v: Vector;
identity_matrix: Matrix;
type
IntArray = array of integer;
Foo = object
x, y: integer;
end;
const
MB: integer = 1024 * 1024;
pi_c: real = 3.1415926;
arr: IntArray = [1, 2];
foo: Foo = (x: 1; y: 2);
4 Expressions
4.1 Primary expressions
type
Foo = object
field: integer;
procedure method();
end;
Vector = array of real;
var
x, y: integer;
done: boolean;
count: integer;
x
real(x)
0
'string'
true
nil
(y + 1)
not done
not x
-1
integer(1234.5)
new Foo
new Vector[count]
this
base
4.2 Factors
type
A = array of integer;
Foo = object
f: integer;
constructor(f: integer);
procedure m();
end;
var
a: A;
x: Foo;
function func(x: integer; foo: Foo): integer;
begin
func := x;
end;
0
a[4]
x.f
this.f
this.m()
func(1, new Foo(1))
4.3 Terms
type
IntArray = array of integer;
var
a: IntArray;
c: boolean;
const
n: integer = 7;
b: boolean = true;
0
a[4]
2 * n
n / 2
n div 2
n mod 3
n and 2
b and c
n shl 1
n shr 1
4.4 Simple expressions
const
n: integer = 4;
b: boolean = false;
s: string = 'abc';
n + 1
s + 'def'
n - 1
n or 1
n xor 6
b or true
b xor true
4.5 Relational expressions
Integers, reals and strings can be compared with all six relational operators. Booleans and characters only with equality and inequality operators. Using the Ord-function however the ordinal values of Booleans and characters can be compared with all relational operators.
const
n: integer = 4;
m: integer = 5;
r: real = 1.5;
q: real = 2.5;
b: boolean = true;
d: boolean = false;
c: char = 'a';
x: char = 'b';
s: string = 'abc';
t: string = 'abd';
n = m
r = q
b <> d
c <> x
s <> t
n > m
s < t
r >= q
n <= m
t <= s
type
Point = object
x, y: real;
constructor();
constructor(x, y: real);
procedure Print();
procedure Offset(dx, dy: real);
end;
Size = object
w, h: real;
constructor();
constructor(w, h: real);
procedure Print();
end;
Rect = object
location: Point;
size: Size;
constructor();
constructor(location: Point; size: Size);
procedure Print();
procedure SetLocation(location: Point);
procedure SetSize(size: Size);
function Left(): real;
function Top(): real;
function Right(): real;
function Bottom(): real;
function IsEmpty(): boolean;
function Contains(p: Point): boolean;
function IntersectsWith(r: Rect): boolean;
procedure Offset(dx, dy: real);
procedure Inflate(dx, dy: real);
end;
Color = object
a, r, g, b: Byte;
constructor(a, r, g, b: Byte);
end;
const
inchMM: real = 25.4;
var
dpi: real;
mm: real;
pixels: integer;
minX, minY, maxX, maxY: integer;
integer(mm * dpi / inchMM)
pixels * inchMM / dpi
new Rect(new Point(minX, minY), new Size(maxX - minX, maxY - minY))
new Color(Byte(255), Byte(0), Byte(0), Byte(0))
4.6 Constant expressions
Constant expression is syntactically an expression. Besides arithmetic operators, the following standard functions can be used in constant expressions: Abs, Chr, Ord, Pred and Succ.
const
x: integer = -3 - 1;
y: integer = Succ(x);
z: integer = Pred(x);
a: char = 'a';
ordA: integer = Ord(a);
chrA: char = Chr(ordA);
b: char = Succ(a);
predB: char = Pred(b);
absX: integer = Abs(x);
t: boolean = true;
ordT: integer = Ord(t);
c: boolean = Pred(t);
ordC: integer = Ord(c);
5 Statements
5.1 Simple statements
var
x: integer;
p: real;
function func(): integer;
procedure proc(x: integer);
x := 1
p := pi
this.w := w
angleRad := pi / 180.0 * angleDeg
black := new Color(Byte(255), Byte(0), Byte(0), Byte(0))
func := 10
proc(2)
Write(x)
graphics.FillPolygon(blackBrush, points)
5.2 Structured statements
5.2.1 Compound statement
begin
end
begin
angleRad := pi / 180.0 * angleDeg;
v := line.ToVector();
r := Rotate(v, angleRad);
RotateLineSegment := new LineSegment(line.s, r);
end
begin
count := 0;
components := new Shape[4];
end
5.2.2 Conditional statements
5.2.2.1 If statement
if n > 1 then
begin
end
if i < n - 1 then graphics.DrawLine(blackPen, s, e)
else DrawArrowLine(graphics, s, e, lineArrowWidth, lineArrowHeight)
if not r.IsEmpty() then
begin
if bounds.IsEmpty() then bounds := r else bounds := Union(bounds, r);
end
5.2.2.2 Case statement
case direction1 of
east:
begin
points[0] := source.Right();
points[1] := target.Left();
end;
west:
begin
points[0] := source.Left();
points[1] := target.Right();
end;
north:
begin
points[0] := source.Top();
points[1] := source.Bottom();
end;
south:
begin
points[0] := source.Bottom();
points[1] := source.Top();
end;
end
case x of
0..3: Writeln('small');
4: Writeln('four');
else
Writeln('large');
end
5.2.3 Repetitive statements
5.2.3.1 Repeat statement
repeat
Writeln(count);
count := Pred(count);
until count = 0
repeat
done := DoWork();
until done
5.2.3.2 While statement
while column >= count do
begin
Add(new SyntaxCell(row, count));
end;
while not done do
begin
done := DoWork();
end
5.2.3.3 For statement
The initial value and final value expressions are evaluated before entering the loop, so changing them inside the loop body has no effect to the number of times the loop is executed.
for i := 0 to 10 do Writeln(i);
for i := 0 to count - 1 do
begin
s := SyntaxShape(components[i]);
w := w + s.bounds.size.w;
h := Max(h, s.bounds.size.h);
end;
6 Blocks
Programs and subroutines have blocks. Blocks may be nested. An inner block may reference variables, constants and types of outer blocks.
const
N: integer = 10;
type
Vector = array of real;
Point = object
x, y: integer;
constructor(x, y: integer);
end;
var
vec: Vector;
point: Point;
r: integer;
constructor Point(x, y: integer);
begin
this.x := x;
this.y := y;
end;
procedure Proc(v: Vector);
const
M: integer = 20;
type
Object = object
i: integer;
end;
var
z: integer;
begin
end;
function Func(p: Point): integer;
const
F: integer = 30;
type
T = object
end;
var
u: integer;
begin
Func := 1;
end;
begin
vec := new real[2];
vec[0] := 1.5;
vec[1] := 2;
Proc(vec);
point := new Point(5, 6);
r := Func(point);
end;
Subroutines contain subroutine blocks
that are either external or forward declarations, or blocks.
With forward declarations mutually dependent subroutines may call each other.
External subroutines enable P programs to call C++ code. External subroutines should be implemented in the rt
C++ module of the C++ project p.vcxroj
of the solution plang.sln
.
program Forward;
procedure Second(x: integer); forward;
procedure First(x: integer);
var
i: integer;
begin
i := x - 1;
Writeln('first: ', i);
if i > 0 then
begin
Second(i);
end;
end;
procedure Second(x: integer);
begin
Writeln('second: ', x);
First(x);
end;
begin
First(3);
end.
The following example shows how blocks may be nested and inner block may use variables of the outer block:
program Scopes;
var
v: integer;
procedure Outer();
var
outerVar: integer;
procedure Inner();
var
innerVar: integer;
begin
Writeln('outer var: ', outerVar);
innerVar := 10;
outerVar := 2;
end;
begin
outerVar := 1;
Inner();
Writeln('outer var is now: ', outerVar);
v := 3;
end;
begin
v := 0;
Outer();
Writeln('v is now: ', v);
end.
6.1 Procedure declarations
Procedures may not be overloaded so the name of a procedure in a procedure declaration must be unique within a program.
If the parameter qualifier is empty the corresponding actual parameter is passed by value. If the parameter qualifier is var
the actual parameter is passed by reference. If the parameter qualifier is const
the actual parameter is passed by value and you may not assign a new value to it.
program VarParam;
var
v: integer;
procedure P(var x: integer);
begin
Writeln('value of x is ', x);
x := 100;
end;
begin
v := 1;
P(v);
Writeln('value of v is ', v);
end.
6.2 Function declarations
Functions may not be overloaded so the name of a function in a function declaration must be unique within a program.
A function should contain at least one assignment statement that assigns a value to the function result variable that has the same name as the function. There might be more than one executed function result assignment in which case the last such assignment stays valid.
function MMToPixels(mm: real; dpi: real): integer;
var
pixels: integer;
begin
pixels := integer(mm * dpi / inchMM);
MMToPixels := pixels;
end;
6.3 Constructor declarations
Constructors are used to initialize an object type object. The compiler automatically implements a default constructor
, a constructor that take no parameters, for each object type if it is not implemented by user. The generated default constructor initializes each field of an object to a default value: nil
for object type, 0 for integer type, 0.0 for real type and false
for Boolean type object.
Constructors may be overloaded, so there may be multiple constructors with different parameter types in the same object type.
The base
constructor call calls the constructor of the base object type. The this
constructor call calls another constructor of the same object type.
program Constructors;
type
Color = object
a, r, g, b: Byte;
constructor(a, r, g, b: Byte);
end;
Shape = object
color: Color;
constructor(color: Color);
end;
Circle = object(Shape)
constructor(color: Color);
end;
var
red: Color;
circle: Circle;
constructor Color(a, r, g, b: Byte);
begin
this.a := a;
this.r := r;
this.g := g;
this.b := b;
end;
constructor Shape(color: Color);
begin
this.color := color;
end;
constructor Circle(color: Color) : base(color);
begin
end;
begin
red := new Color(Byte(255), Byte(255), Byte(0), Byte(0));
circle := new Circle(red);
end.
7 Programs and units
7.1 Programs
The program-id
specifies the name of the program and sets the file name of the generated .pcode
file. The generated .pcode
file is saved to the prog
subdirectory of the plang
root directory.
The program-parameter-list
is ignored by the compiler.
The uses clause specifies units used by a program or interface or implementation part of a unit. The System
unit is automatically used by all programs and units. The .pcode
file of a unit is searched in the unit
subdirectory of the plang
root directory by the name of the unit.
program SyntaxDiagrams;
uses
System.Graphics.Primitive,
System.Graphics,
Syntax,
System.List;
type
DiagramList = List of Diagram;
var
diagrams: DiagramList;
x, y: integer;
initialBitmap: Bitmap;
initialGraphics: Graphics;
procedure AddDiagram(diagram: Diagram);
begin
diagrams.Add(diagram);
end;
procedure MakeDiagrams();
begin
end;
procedure MeasureDiagrams();
begin
end;
procedure PrintDiagrams();
begin
end;
procedure DrawDiagrams();
begin
end;
begin
x := MMToPixels(100, dpiX);
y := MMToPixels(100, dpiY);
initialBitmap := new Bitmap(x, y);
initialGraphics := initialBitmap.GetGraphics();
diagrams := new DiagramList();
MakeDiagrams();
MeasureDiagrams();
PrintDiagrams();
DrawDiagrams();
end.
7.2 Units
The unit-name
in the unit heading specifies the name of the unit and sets the file name of the generated .pcode
file. The generated .pcode
file is saved to the unit
subdirectory of the plang
root directory.
7.2.1 Interface part
To enable a program or another unit to use a declaration of a unit, the declaration must be placed to the interface part of a unit.
7.2.2 Implementation part
The implementation part contains definitions of the procedures, functions and methods declared in the interface part of a unit.
7.2.3 Initialization part
The initialization parts of the units a program uses are run before the statement part of the program starts executing.
unit System.Graphics.Primitive;
interface
type
Point = object
x, y: real;
constructor();
constructor(x, y: real);
procedure Print();
procedure Offset(dx, dy: real);
end;
Size = object
w, h: real;
constructor();
constructor(w, h: real);
procedure Print();
end;
Rect = object
location: Point;
size: Size;
constructor();
constructor(location: Point; size: Size);
procedure Print();
procedure SetLocation(location: Point);
procedure SetSize(size: Size);
function Left(): real;
function Top(): real;
function Right(): real;
function Bottom(): real;
function IsEmpty(): boolean;
function Contains(p: Point): boolean;
function IntersectsWith(r: Rect): boolean;
procedure Offset(dx, dy: real);
procedure Inflate(dx, dy: real);
end;
Vector = object
x, y: real;
constructor();
constructor(x, y: real);
constructor(p: Point);
procedure Print();
function Length(): real;
function ToPoint(): Point;
end;
type
Direction = (noDir, north, east, south, west);
function Distance(s, e: Point): real;
function Union(a, b: Rect): Rect;
function Sum(u, v: Vector): Vector;
function Difference(u, v: Vector): Vector;
function Product(a: real; v: Vector): Vector;
function Dot(u, v: Vector): real;
function Unit(v: Vector): Vector;
function ProjectionFactor(u, v: Vector): real;
function Proj(u, v: Vector): Vector;
function Rotate(v: Vector; angleRad: real): Vector;
function GetDirection(source, target: Point): Direction;
type
LineSegment = object
s, e: Point;
constructor(s, e: Point);
constructor(s: Point; v: Vector);
procedure Print();
function ToVector(): Vector;
function Length(): real;
end;
function RotateLineSegment(line: LineSegment; angleDeg: real): LineSegment;
implementation
constructor Point();
begin
x := 0;
y := 0;
end;
constructor Point(x, y: real);
begin
this.x := x;
this.y := y;
end;
procedure Point.Print();
begin
Write('(');
Write(x);
Write(', ');
Write(y);
Write(')');
Writeln();
end;
procedure Point.Offset(dx, dy: real);
begin
x := x + dx;
y := y + dy;
end;
constructor Size();
begin
w := 0;
h := 0;
end;
constructor Size(w, h: real);
begin
this.w := w;
this.h := h;
end;
procedure Size.Print();
begin
Write('(');
Write(w);
Write(', ');
Write(h);
Write(')');
Writeln();
end;
constructor Rect();
begin
location := new Point();
size := new Size();
end;
constructor Rect(location: Point; size: Size);
begin
this.location := location;
this.size := size;
end;
procedure Rect.Print();
begin
Write('[');
Write(location.x);
Write(', ');
Write(location.y);
Write(', ');
Write(size.w);
Write(', ');
Write(size.h);
Write(']');
Writeln();
end;
procedure Rect.SetLocation(location: Point);
begin
this.location := location;
end;
procedure Rect.SetSize(size: Size);
begin
this.size := size;
end;
function Rect.Left(): real;
begin
Left := location.x;
end;
function Rect.Top(): real;
begin
Top := location.y;
end;
function Rect.Right(): real;
begin
Right := location.x + size.w;
end;
function Rect.Bottom() : real;
begin
Bottom := location.y + size.h;
end;
function Rect.IsEmpty(): boolean;
begin
IsEmpty := (size.w = 0) and (size.h = 0);
end;
function Rect.Contains(p: Point): boolean;
begin
Contains := (p.x >= Left()) and (p.x < Right()) and
(p.y >= Top()) and (p.y < Bottom());
end;
function Rect.IntersectsWith(r: Rect): boolean;
begin
IntersectsWith := (Left() < r.Right()) and (Top() < r.Bottom()) and
(Right() > r.Left()) and (Bottom() > r.Top());
end;
procedure Rect.Offset(dx, dy: real);
begin
location.Offset(dx, dy);
end;
procedure Rect.Inflate(dx, dy: real);
begin
location.Offset(-dx, -dy);
size.w = size.w + 2 * dx;
size.h = size.h + 2 * dy;
end;
constructor Vector();
begin
x := 0;
y := 0;
end;
constructor Vector(x, y: real);
begin
this.x := x;
this.y := y;
end;
constructor Vector(p: Point);
begin
x := p.x;
y := p.y;
end;
procedure Vector.Print();
begin
Write('(');
Write(x);
Write(', ');
Write(y);
Write(')');
Writeln();
end;
function Vector.Length(): real;
begin
Length := Sqrt(x * x + y * y);
end;
function Vector.ToPoint(): Point;
begin
ToPoint := new Point(x, y);
end;
function Distance(s, e: Point): real;
var
dx, dy: real;
begin
dx := Abs(s.x - e.x);
dy := Abs(s.y - e.y);
Distance := Sqrt(dx * dx + dy * dy);
end;
function Union(a, b: Rect): Rect;
var
right, bottom, left, top: real;
begin
right := Max(a.Right(), b.Right());
bottom := Max(a.Bottom(), b.Bottom());
left := Min(a.Left(), b.Left());
top := Min(a.Top(), b.Top());
Union := new Rect(new Point(left, top),
new Size(right - left, bottom - top));
end;
function Sum(u, v: Vector): Vector;
begin
Sum := new Vector(u.x + v.x, u.y + v.y);
end;
function Difference(u, v: Vector): Vector;
begin
Difference := new Vector(u.x - v.x, u.y - v.y);
end;
function Product(a: real; v: Vector): Vector;
begin
Product := new Vector(a * v.x, a * v.y);
end;
function Dot(u, v: Vector): real;
begin
Dot := u.x * v.x + u.y * v.y;
end;
function Unit(v: Vector): Vector;
var
length: real;
begin
length := v.Length();
Unit := Product(1 / length, v);
end;
function ProjectionFactor(u, v: Vector): real;
begin
ProjectionFactor := Dot(v, Product(1 / u.Length(), u));
end;
function Proj(u, v: Vector): Vector;
begin
Proj := Product(ProjectionFactor(v, u), Unit(u));
end;
function Rotate(v: Vector; angleRad: real): Vector;
var
cosTheta, sinTheta: real;
begin
cosTheta := Cos(angleRad);
sinTheta := Sin(angleRad);
Rotate := new Vector(v.x * cosTheta - v.y * sinTheta,
v.x * sinTheta + v.y * cosTheta);
end;
function GetDirection(source, target: Point): Direction;
var
v, u: Vector;
direction: Direction;
begin
v := new Vector(target.x - source.x, target.y - source.y);
if v.x >= 0 then
begin
if v.y >= 0 then
begin
u := new Vector(1, -1);
if Dot(v, u) >= 0 then direction := east else direction := south;
end else
begin
u := new Vector(-1, -1);
if Dot(v, u) >= 0 then direction := north else direction := east;
end
end else
begin
if v.y >= 0 then
begin
u := new Vector(1, 1);
if Dot(v, u) >= 0 then direction := south else direction := west;
end else
begin
u := new Vector(-1, 1);
if Dot(v, u) >= 0 then direction := west else direction := north;
end
end;
end;
constructor LineSegment(s, e: Point);
begin
this.s := s;
this.e := e;
end;
constructor LineSegment(s: Point; v: Vector);
var
u: Vector;
begin
this.s := s;
u := Sum(new Vector(s), v);
this.e := u.ToPoint();
end;
procedure LineSegment.Print();
begin
Write('{(');
Write(s.x);
Write(', ');
Write(s.y);
Write('); (');
Write(e.x);
Write(', ');
Write(e.y);
Write(')}');
Writeln();
end;
function LineSegment.ToVector(): Vector;
begin
ToVector := new Vector(e.x - s.x, e.y - s.y);
end;
function LineSegment.Length(): real;
begin
Length := Distance(s, e);
end;
function RotateLineSegment(line: LineSegment; angleDeg: real): LineSegment;
var
angleRad: real;
v, r: Vector;
begin
angleRad := pi / 180.0 * angleDeg;
v := line.ToVector();
r := Rotate(v, angleRad);
RotateLineSegment := new LineSegment(line.s, r);
end;
end.