P Language Syntax

Table of contents

1 Lexical structure
    1.1 Identifiers
    1.2 Keywords
    1.3 Numbers
    1.4 Strings
    1.5 Comments
2 Types
    2.1 Simple types
    2.2 String type
    2.3 Pointer type
    2.4 Object types
    2.5 Array types
    2.6 Specializations
3 Variables and constants
4 Expression
    4.1 Primary expressions
    4.2 Factors
    4.3 Terms
    4.4 Simple expressions
    4.5 Relational expressions
    4.6 Constant expressions
5 Statements
    5.1 Simple statements
    5.2 Structured statements
          5.2.1 Compound statement
          5.2.2 Conditional statements
                  5.2.2.1 If statement
                  5.2.2.2 Case statement
          5.2.3 Repetitive statements
                  5.2.3.1 Repeat statement
                  5.2.3.2 While statement
                  5.2.3.3 For statement
6 Blocks
    6.1 Procedure declarations
    6.2 Function declarations
    6.3 Constructor declarations
7 Programs and units
    7.1 Programs
    7.2 Units
          7.2.1 Interface part
          7.2.2 Implementation part
          7.2.3 Initialization part

1 Lexical structure

1.1 Identifiers

Identifiers denote variables, parameters, constants, types, functions and procedures.




{ Identifiers: }

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









{ Numbers: }
 
0 
1
123
1e3
0.5
12E-4

1.4 Strings





{ Strings: }

''
'abc'
''''
'control-line'#13#10
'345'
'joe.coder@foo.bar'
'''quoted string'''

1.5 Comments




{ Comments: }

{ brace comment }
(* paren comment *)
{
multiline comment
}
(*
another
multiline
comment
*)

2 Types





2.1 Simple types




{ Subrange type declarations: }

type 
  Byte = 0..255;
  Citrus = orange..lemon;

{ Enumerated type declarations: }

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









{ Object type declarations: }

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.

{
output of the program:
Shape.Print()
Shape.Print()
Circle.Print()
}

2.5 Array types


{ Array type declarations: }

type
  IntArray = array of integer;

  PointArray = array of Point;

  Vector = array of real;

  Matrix = array of Vector;

2.6 Specializations


{ Specializations: }

type
  IntList = List of integer;

  StringList = List of string;

  ShapeList = List of Shape;

3 Variables and constants


{ Variable declarations: }

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;

{ Constant declarations: }

const
  MB: integer = 1024 * 1024;  { simple constant }
  pi_c: real = 3.1415926;     { simple constant }
  arr: IntArray = [1, 2];     { array constant }
  foo: Foo = (x: 1; y: 2);    { object constant }

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;

{ Primary expressions: }

x                 { identifier }
real(x)           { typecast }
0                 { unsigned constant }
'string'          { unsigned constant }
true              { unsigned constant }
nil               { unsigned constant }
(y + 1)           { parenthesized expression }
not done          { logical NOT expression }
not x             { bitwise complement expression }
-1                { negative number }
integer(1234.5)   { typecast }
new Foo           { new-expression }
new Vector[count] { new-expression }
this              { reference to this-object (inside a method) }
base              { reference to base-object (inside a method) }

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;


{ Factors: }

0                   { primary expression is a factor }
a[4]                { array index operation }
x.f                 { member selection operation }  
this.f              { this-field access (inside Foo) }
this.m()            { this-method call (inside Foo) }
func(1, new Foo(1)) { function call with unsigned constant and }
                    { new-expression arguments }

4.3 Terms



type
  IntArray = array of integer;

var 
  a: IntArray;
  c: boolean;

const
  n: integer = 7;
  b: boolean = true;

{ Terms: }

0           { primary expression is a term }
a[4]        { factor is a term }
2 * n       { integer multiplication (14) }
n / 2       { real division operator produces real result (3.5) }
n div 2     { integer division operator produces integer result (3) }
n mod 3     { integer modulo operation (1) }
n and 2     { integer bitwise AND operation (2) }
b and c     { Boolean logical AND operation }
n shl 1     { integer bitwise shift left operation (14) }
n shr 1     { integer bitwise shift right operation (3) }

4.4 Simple expressions



const 
  n: integer = 4;
  b: boolean = false;
  s: string = 'abc';

{ Simple expressions: }

n + 1       { integer addition operation (5) }
s + 'def'   { string concatenation operation ('abcdef') } 
n - 1       { integer subtraction operation (3) }
n or 1      { integer bitwise OR operation (5) }
n xor 6     { integer bitwise XOR operation (2) }
b or true   { Boolean logical OR operation (true) }
b xor true  { Boolean logical XOR operation (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';

{ Relational expressions: }

n = m { false }
r = q { false }
b <> d { true }
c <> x { true }
s <> t { true }
n > m { false }
s < t { true }
r >= q { false }
n <= m { true }
t <= s { false }
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;

{ Expressions: }

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.

{ Constant expressions: }

const 
  x: integer = -3 - 1;    { -4 }
  y: integer = Succ(x);   { -3 }
  z: integer = Pred(x);   { -5 }
  a: char = 'a';
  ordA: integer = Ord(a); { 97 }
  chrA: char = Chr(ordA); { 'a' }
  b: char = Succ(a);      { 'b' }
  predB: char = Pred(b);  { 'a' }
  absX: integer = Abs(x); { 4 }
  t: boolean = true;
  ordT: integer = Ord(t); { 1 }
  c: boolean = Pred(t);   { false }
  ordC: integer = Ord(c); { 0 }

5 Statements


5.1 Simple statements




var
  x: integer;
  p: real;

function func(): integer;

procedure proc(x: integer);

{ Simple statements: }

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 { as last statement of func }
proc(2)
Write(x)
graphics.FillPolygon(blackBrush, points)

5.2 Structured statements


5.2.1 Compound statement


{ Compound statements: }

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 statements: }

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 statements: }

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 statements: }

repeat
  Writeln(count);
  count := Pred(count);
until count = 0

repeat
  done := DoWork();
until done
5.2.3.2 While statement

{ While statements: }

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 statements: }

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.








{ Block: }

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 .


{ Forward declaration: }

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.

{
output of the program:
first: 2
second: 2
first: 1
second: 1
first: 0
}

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.

{
output of the program is:
outer var: 1
outer var is now: 2
v is now: 3
}

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.

{
output of the program is:
value of x is 1
value of v is 100
}

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: }

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;

{ Constructors: }

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: }

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: }

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.