In the last post we saw some of the history of PIL and how to run it on MTS. We'll now take a closer look at the features of PIL. Examples shown can be entered directly into PIL after starting it with $run *pil.

Direct mode

PIL starts up in direct mode, where statements entered are immediately executed when you press RETURN. * is used by PIL as the prompt to enter input. You can use the TYPE statement and simple arithmetic expressions to make PIL act as a calculator:

* type 22 * 2
  22 * 2 =  44.0
* type 2 ** 16
  2 ** 16 =  65536.0

On MTS, PIL is case-insensitive for keywords and lines can optionally end with a period. Errors are immediately reported, usually starting with Eh?.

* TYPE 2+2.
  2+2 =  4.0
* TYPE
  Eh? IMPROPERLY FORMED STATEMENT

If the last character entered on the line is - then input will continue on the next line before it is executed. The prompt will change to & to show this continuation. * at the start of line can be used to make comments.

 * type 1 + 2 + 3 +-
 & 5 + 6
   1 + 2 + 3 +5 + 6 =  17.0
 * * comment here
 *

Variables and types

Variables can be introduced by the optional keyword SET followed by a variable name and an assignment.

* set a = 2
* b = 3
* type a, b, a+b
  a =  2.0
  b =  3.0
  a+b =  5.0

PIL understands three types: numerical, which are stored as floating point values, character strings and Boolean values.

* a = 1 / 3
* b = "PIL"
* c = The True
* type a, b, c
  a =  0.3333333
  b = "PIL"
  c = The True

Booleans constants are 'The True' or 'The False' - something I've not seen in any other language. Strings can be up to 255 characters long and can be entered with single or double quotes as delimiters. Floats have 7 digits of precision and can be entered with exponential notation. Volume 12 mentions type 9.999999e64 as the maxomum value but on the version I'm running it seems 62 is the maximum exponent.

* type 9.999999e62
  9.999999e62 =  9.999999E+62
* type 9.999999e63
  Eh? EXPONENT OUT OF RANGE

Variable names are up to 8 characters long, are case sensitive and are distinguished from keywords, as this silly example shows.

* set set = 1
* set SET = 2
* type set, SET
  set =  1.0
  SET =  2.0

Arrays are allowed with any number of dimensions, though once set the number of dimensions cannot be changed.

* x(1, -2) = 3
* x(3, 44.0) = 4
* type x
  x(1,-2) =  3.0
  x(3,44) =  4.0
* x(3,4,5) = 42
  Eh? ??-

Expressions

Arithmetic expressions work mostly as expected. The absolute value can be taken by surrounding an expression with |; exponentiation is done with **. Functions for arithmetic operations such as square root, cosine, log etc generally have a short and long form and do not need parentheses unless needed to resolve ambiguities.

* type 1 + |-41|
  1 + |-41| =  42.0
* type the square root of 9
  the square root of 9 =  3.0
* type sqrt of 16
  sqrt of 16 =  4.0
* type sqrt of 25+1
  sqrt of 25+1 =  6.0
* type sqrt of (25+1)
  sqrt of (25+1) =  5.09902

There are functions for min/max, random numbers and extracting parts of a number, as well as special functions to get time (in 300ths of seconds since midnight), date, cpu/elapsed time and storage used.

* type the min of (1,2,3)
  the min of (1,2,3) =  1.0
* type the time, the date, the elapsed time, the cpu time
  the time =  1.963908E+07
  the date =  17133.0
  the elapsed time =  7.819461E+07
  the cpu time =  464.0
* type the total size, the size
  the total size =  188.0
  the size =  165.0

Boolean functions are similar to other languages, with # standing in for logical or.

* type 2 >= 3
  2 >= 3 = The False
* type 2 $lt 3
  2 $lt 3 = The True
* type the true # the false
  the true # the false = The True
* type the true & the false
  the true & the false = The False

Character expressions include length, case conversion, comparison and extraction.

* type the l of "abc"
  the l of "abc" =  3.0
* type the upper of "abC"
  the upper of "abC" = "ABC"
* type the first 3 characters of "abcde"
  the first 3 characters of "abcde" = "abc"
* type 2 $fc "abcde" + "X" + 1 $lc "abcde"
  2 $fc "abcde" + "X" + 1 $lc "abcde" = "abXe"
* type "aaa" > "AAA"
  "aaa" > "AAA" = The False

Finally, the type of an expression can be found and run time evaluation performed.

* type the mode of 42, the mode of the true, the mode of "abc"
  the mode of 42 =  1.0
  the mode of the true =  2.0
  the mode of "abc" =  3.0
* type the value of "2*21"
  the value of "2*21" =  42.0

Control flow

There's an IF statement with an optional ELSE clause. The IF and ELSE can be omitted but the punctuation is required.

* if 1 < 2, then type 'yes'; else type 'no'
  yes
* if 1 > 2, type 'yes'; type 'no'
  no

For loops have a range or an increment and an optional clause to terminate.

* for i = 1 to 5: type i ** 2
  i ** 2 =  1.0
  i ** 2 =  4.0
  i ** 2 =  9.0
  i ** 2 =  16.0
  i ** 2 =  25.0
* for i = 1 by 2 to 10: type i ** 2
  i ** 2 =  1.0
  i ** 2 =  9.0
  i ** 2 =  25.0
  i ** 2 =  49.0
  i ** 2 =  81.0
* for i = 1 by 2 while i < 20: type i
  i =  1.0
  i =  3.0
  i =  5.0
  i =  7.0
  i =  9.0
  i =  11.0
  i =  13.0
  i =  15.0
  i =  17.0
  i =  19.0

Indirect mode

Lines entered that start with a number are treated as stored instructions, broken down by part and step, that can be run later. Here we define a program in part 1 consisting of four steps.

* 1.0 i = 5
* 1.1 type i
* 1.2 i = i * i
* 1.3 type i

This can then be run with DO, which will execute all steps in a part.

* do part 1
  i =  5.0
  i =  25.0

Variables set in a program remain after execution is completed and it is possible to run a single step at a time.

* type i
  i =  25.0
* do step 1.2
* do step 1.3
  i =  625.0

Steps can call other parts with DO: execution will resume after the part is finished. It's also possible to transfer execution with TO which will not return. DONE will return from the current part.

* type part 2, part 3

  2.0    i = 3
  2.1    do step 3
  2.2    i = 5
  2.3    do step 3
  2.4    i = 7
  2.5    to step 3
  2.6    type 'not reached'


  3.0    type i
  3.1    done
  3.2    type 'also not reached'

* do part 2
  i =  3.0
  i =  5.0
  i =  7.0

The above also shows it is possible to list out programs with TYPE PART. You can see all entered parts with TYPE ALL PARTS. With TYPE ALL STUFF you will see all variables and parts defined.

DELETE can be used to remove a step or a variable definition.

I/O and system access

We've seen TYPE used to display variables. You can prompt for a value to be entered with DEMAND

* delete part 1
* 1.0 demand i
* 1.1 i = i * 10
* 1.2 type i
* do part 1
 i = ?_ 3
  i =  30.0

There is also a formatting facility for input and output that is covered in the manual.

Programs can be saved to disk for future list with SAVE. This takes a file name (which must already exist) and what to save. For example:

save as 'x.pil', all stuff  

will save all parts and variables to x.pil. File format is plain text so it can be edited outside of PIL if needed. LOAD 'x.pil' will then load the file back into PIL.

The implementation of PIL on MTS includes access to system facilities such as file creation and device access. For example, CREATE 'x.pil' will create a new file.

In the next post we'll see how to construct a larger PIL program.

Further information

MTS volume 12 has a complete reference to PIL as implemented on MTS.

If you can find a copy of the book 'History of Programming Languages', edited by Richard Wexelblat, this has a great section about JOSS talking about its design principles.