Heading:
Poplar Language Manual
Margins: Top: 1.3"
Page Numbers: Yes X: 527 Y: 10.5" First Page: 24
Appendix A: Examples
The following examples are biased towards the kind of tasks Mesa programmers find themselves doing. This is more a function of the examples that come to our minds naturally, rather than the intent of the language.

Example 1. A pattern to eliminate Bravo format trailers
P ← {(...("↑Z" ...)* "↑M")! ...}
P says: Find all occurrences of "↑Z" ... "↑M" and delete the "↑Z" ... part.
To transform old.bravo into new.text one types
"old.bravo"/file/P/write"new.text"

Example 2.
Find all the mesa files for which a bcd file does not exist.
""/dir//tolower
//{... (".bcd" | ".mesa")}
/RelevantFiles := ["a.bcd", "foo.bcd", "ajax.mesa", "foo.mesa", "ed.mesa", "al.bcd",
"zug.bcd", "al.mesa"];
RelevantFiles//{... ".mesa"*} = ["ajax", "foo", "ed", "al"]
/MesaFiles:
RelevantFiles//{... ".bcd"*} /// ([x, y]: {y | x}) = {"zug" | {"al" | {"foo" | "a"}}}
/BcdPattern:
MesaFiles//{~BcdPattern ...} = ["ajax", "ed"]
The tolower maplist is required since Poplar considers "bcd" and "Bcd" distinct.

Example 3.
Finding substrings in files.
FindAndShow (Pat: f: f/file/lines//{... Pat .../t: f ": " t/print; fail});
FindAndShow is a function that, given a string Pat, produces another function which takes a file name f and displays the lines f which contain Pat. Each line is prefixed with the file name. The final value is the empty list; the fail at the end is used to assure that printed lines are not saved.
Here is a program which searches mesa files a.mesa, b.mesa, and c.mesa.

"pattern:"/key/p ="PUBLIC":
p/FindAndShow = (f: f/file/lines//{..."PUBLIC".../t: f ": " t/print; fail})
/FindPat:
["a", "b", "c"] ".mesa" // FindPat
Here is a program that prompts for the file names as well.

"pattern:"/key/FindAndShow/FindPat:
"" % (x: "file:"/key/FN: FN >FN/FindPat)
The prompt "file:" appears on the screen, and key returns the filename, FN. If the user types DEL, FN will be fail and everything stops.
Example 4. Print a set of files ending in ".pl" using Bravo, but save paper by combining them into one file and inserting headers.

Files ← ""/dir // tolower // {... ".pl"} / sort;
~(Files/isnull) >
(Text ← Files // file "↑Z↑M↑L↑M";
(("File: " Files "↑M↑M↑M") Text) / conc / (x: x "↑Z↑M") / write "out.out$";
"Hardcopy out.out$" / exec;
"out.out$" / delete)
Files is a list of the files to be printed. If Files is not the null list, it continues the computation. Text is a list of the contents of each of the files, appended with some Bravo formatting information to print each file on a new page. The third expression generates a list of headers for the beginning of each file, with carriage returns between the header and the file. That list is concatenated into a string, a final bit of Bravo formatting is added, and it is written on a dummy file out.out$. The Hardcopy command is executed, and then the file out.out$ is deleted.

Example 5. Automate the programming cycle.

Files ← ""/dir // tolower // {... ".errlog"*};
~(Files/isnull) >
"Bravo/m " Files "; "/conc " del " (Files ".errlog "/conc)
"; compile " (Files " "/conc) / quit
Files is a list of Mesa filenames for which a .errlog file exists. If Files is not the null list, each Mesa file and its .errlog are brought in with the Bravo m’ macro, each of the .errlogs is deleted, and all those files are recompiled.
Example 6. The function takes a list as input, prints each list element and a question mark afterwards. If the letter y’ is typed, that element will be an element in the resulting list, otherwise it will not.
Confirm ← (x: x//e: e "?"/key/{"y"}> e )

Example 7. Programs to add carriage returns to a paragraph
Assume that the input, Paragraph, contains spaces but no carriage returns. To make the example typographically tractable we shall limit lines to 20 characters. All spaces and carriage returns are inside strings are written explicitly. The algorithm goes as follows:
Initialize In to be Paragraph, and Out to be the empty string. As long as In is non-empty, Juggle In and Out so as to put another line on Out. Finally, return Out..
AddCrs1 ← (Paragraph := "012356789013467891234567901245679"
"0134567891235689134578912345789"
"0134579013456890134678012356789"
"013457";
[Paragraph, ""]
% ([In, Out]: In/{# ...} > [In, Out]/Juggle)
/[In, Out]: Out
= "0123567890134↑M"
"6789
12345679012↑M"
"4567
9013456789↑M"
"123
56891345↑M"
"789
1234578901↑M"
"345
79013456↑M"
"8901
346780123↑M"
"5678901
3457"
);
Juggle chops 19 characters off In, producing Line and RestOfIn. Then it breaks Line into everything up through the final blank, Most, and the remainder, Stub. The new value of In becomes the Stub followed by RestOfIn. The new Out becomes, Out followed by Most followed by a carriage return. If there are not 80 characters, In is set to be the empty string and Out to Out followed by In.

Juggle ← ([In, Out ]: := ["0123
567890134678912345679.....", "\\\"]
In/
{[len 19, ...] = ["0123567890134678",
"912345679....."]
/[Line, RestOfIn]:
Line/{[(... " ")!, ...]} = ["0123
567890134", "678"];
/[Most, Stub]:
[Stub RestOfIn = "6789
12345679.....",
Out Most "↑M"= "\\\0123
567890134↑M"]
}
| ["", Out In]
);

The following recursive program is an alternative.
AddCrs2 ←
(Paragraph:
Paragraph/
{[len 80, ...]
/[Line, RestOfLine]:
Line/{[(... " ")!, ...]}/[Most, Stub]:
Most "↑M" (Stub RestOfLine/AddCrs2)
}
| Paragraph
)

Example 8. A program print all the files on the JuniperX directory which were written after the 9th of August. We assume that the file ftp.log has already been created as shown below. (The FTP subsystem will not execute a list command from the command line.)

Month ← {"Jan" > 01 | "Feb" > 02 | "Mar" > 03 | "Apr" > 04 | "May" > 05 | "Jun" > 06
| "Jul" >07 | "Aug" > 08 | "Sep" > 09 | "Oct" > 10 | "Nov" > 11 | "Dec" > 12};

Date ← {[integer "-"* , Month "-"* , integer]
/ [d, m, y] : y m (d/length/{2} > d | 0 d)};

File ← {(..."<JuniperX>")* (word ">")!? word ".mesa" ("!" number)*};

Line ← {[File " "!* , Date (... "↑M")*]};

Later ← ([f, d] : "9-Aug-78"/Date - d/{"-"...}>f);
"ftp.log"/file /f := "....
<JuniperX>defs>BTreeDefs.mesa!3 8-Aug-78 18:05:13
<JuniperX>defs>FileSystemDefs.mesa!3 8-Aug-78 18:05:07
<JuniperX>defs>FileSystemDefs.mesa!4 8-Aug-78 18:05:15
<JuniperX>defs>triconprivatedefs.mesa!3 11-Aug-78 11:22:49
<JuniperX>hes>nelsonenv.mesa!3 4-Aug-78 13:59:26
<JuniperX>progs>CommonPineCold.mesa!3 11-Aug-78 17:36:24
<JuniperX>progs>eventmanager.mesa!2 11-Aug-78 17:41:41
<JuniperX>progs>eventmanager.mesa!3 11-Aug-78 11:40:39
<JuniperX>progs>eventmanager.mesa!4 11-Aug-78 11:44:17
<JuniperX>progs>wdisk.mesa!3 11-Aug-78 18:01:41
....";
f / {Line,! ...*} = [ ["defs>BTreeDefs.mesa", 780808],
["defs>FileSystemDefs.mesa", 780808],
["defs>FileSystemDefs.mesa", 780808],
["defs>triconprivatedefs.mesa", 780811],
["hes>nelsonenv.mesa", 780804],
["progs>CommonPineCold.mesa", 780811],
["progs>eventmanager.mesa", 780811],
["progs>eventmanager.mesa", 780811],
["progs>eventmanager.mesa", 780811],
["progs>wdisk.mesa", 780811]
]
// Later = [ "defs>triconprivatedefs.mesa",
"progs>CommonPineCold.mesa",
"progs>eventmanager.mesa",
"progs>eventmanager.mesa",
"progs>eventmanager.mesa",
"progs>wdisk.mesa"
]
/usort = [ "defs>triconprivatedefs.mesa",
"progs>CommonPineCold.mesa",
"progs>eventmanager.mesa",
"progs>wdisk.mesa"
]
/ fileList :
"ftp ivy di/c juniperx ret/c " (fileList " " / conc) "↑M"
(fileList // { (word ">")!?* ... }/ fileList:
"bravo/h " fileList "↑M" / conc
) =
"ftp ivy di/c juniperx ret/c progs>CommonPineCold.mesa progs>eventmanager.mesa defs>triconprivatedefs.mesa progs>wdisk.mesa
bravo/h CommonPineCold.mesa
bravo/h eventmanager.mesa
bravo/h triconprivatedefs.mesa
bravo/h wdisk.mesa
"

/exec
Date produces numerical dates like 780809 for "9-Aug-78". The file is read in and broken up into a list of file-date pairs. All the things after the given date are filtered out and sorted, eliminating duplicates. This produces fileList. Then a giant Alto command is created which fetches the files and prints them using a Bravo macro.
Example 9. A cross reference program
The following program produces a cross reference listing for the files a.mesa, b.mesa, and c.mesa. It assumes that all the imported references begin with the prefix "P.".

Xref ← (FileList:
FileList
//(FileName := "a.mesa";
FileName/file ="..A1:PUBLIC ...P.B1...P.C2....A2: PUBLIC .. P.B1.....P.B4..."
/{(((... "P.")* thing),! | []) ...*} = ["B1", "C2", "B1", "B4"]
/usort = ["B1", "B4", "C2"]
//(x: [x, FileName]) = [["B1", "a.mesa"], ["B4", "a.mesa"], ["C2", "a.mesa"]]
/Imports:
FileName/file
/{((... / LastWord "PUBLIC"*),! | []) ...*} = ["A1", "A2"]
//(x: [x, FileName "*"])
/Exports:
Exports,, Imports = [["A1", "a.mesa*], ["A2", "a.mesa"*], ["B1", "a.mesa"],
["B4", "a.mesa"], ["C2", "a.mesa"]])
///([x,y]: x,,y) = [["A1", "a.mesa*"],
["A2", "a.mesa*"],
["B1", "a.mesa"],
["B4", "a.mesa"],
["C2", "a.mesa"],
["B1", "b.mesa*"],
["A2", "c.mesa"]]
/factor =[["A1", ["a.mesa*"]],
["A2", ["c.mesa"], ["a.mesa*"]],
["B1", ["b.mesa*"], ["a.mesa"]],
["B4", ["a.mesa"]],
["C2", ["a.mesa"]]]
//(x: x/1 " " (x/-1//1 " "/conc))
/(x: x "↑M"/conc) =
"A1 a.mesa*
A2 c.mesa a.mesa*
B1 b.mesa* a.mesa
B4 a.mesa
C2 a.mesa
");

LastWord ← (s ="...A2 : ": s/reverse/{Sp* ":"* Sp* thing ...*}/reverse = "A2");
Sp ← {" "!?};

["a.mesa", "b.mesa", "c.mesa"]/Xref


Appendix B: Primitive Functions
The following primitive functions can be used in Poplar programs. A function is said to take as input the value it is applied to. It returns a value. A few functions take a parameter, which follows its name.


append filename
Appends its string argument to the file.
asort
Like sort (see below) but uses the ASCII collating sequence for all strings, including strings of numbers.

cfile
Functionally exactly like file, but copies the file into virtual memory. This allows one subsequently to overwrite the original file.

chop
Takes a string, breaks the string into single characters, and returns a list of strings of one character each.
"abc"/chop = ["a","b","c"]
check
Has the same effect as run, but checks the program as described in the section on equality assertions.

conc
Takes a list of strings as input and returns a string which is the concatenation of the list elements from first to last. A string as input will be returned as is. Thus, x///conc is equivalent to x/conc.
["a","b","c"]/conc = "abc"
daytime
Returns the day and time in a string.
""/daytime = "December 1, 1978 9:12 AM"
delete
Takes a string which is a file name on the local disk and deletes that file.
""/dir//{... "$"}//delete
deletes all files whose names end in "$", equivalent to "delete *$"/exec.

differ
Takes a list of two strings and compares them character by character. It returns a list of the two strings with any common prefix removed.
["abcX","abcy"]/differ = ["X","y"]
dir
Returns a list of file file names in the local directory. Ignores its input.
display
Prints its input on the screen. The input may be any Poplar value, e.g. a string, list, pattern. Returns its argument. See also print.
divide
Takes a list of two numbers [a,b] as input. Returns [a/b, a mod b]. If b is 0 it returns [a,a].
edit
Takes a filename as input, quits Poplar and executes Bravo, then after the user quits from Bravo, invokes Poplar executing the editted file.
exec
Takes any command, saves the current environment (via a Mesa checkpoint), has the Alto Operating System execute the command, and re-invokes Poplar in its saved state. exec returns its input.
factor
Takes a list of lists, sorts it according the the method of sort and then merges all adjacent sub-lists which have the same first element.
[[1,2,3], [3,4,6], [1,7], [3,8], [1,6,9], [9,0]]/factor =
[[1,[2,3],[7],[6,9]], [3,[4,6],[8]], [9,[0]]]
This function turns out to be quite useful for adding structure to data. See Example 9.
file
Its input is a string which is the name of a file. It returns a string with the contents of that file for future processing. See also listin.
ident
Does nothing and simply returns its input (the identity function).
islist
Returns its input if its input is a list, returns fail otherwise.
isnull
Returns its input if its input is the null list [], returns fail otherwise.
isstring
Returns its input if its input is a string, returns fail otherwise.
key
Prompts the typist with its input string. Waits for him to type in a sequence of characters terminated by RETURN. Returns the string without the RETURN. The Mesa ReadEditedString routine is used so the usual control characters can be used. If DEL is typed it returns fail.
length
Its input must be a string or a list. If its input is a string, returns the length of the string. If its input is a list, returns the number of list elements.
lines
Breaks the incoming string into a list of strings, one for each "line" or sequence of characters separated by carriage return. It is equivalent to
{((... "↑M"),! | []) ,, [...]}
listin
Its argument is the name of a file created by listout. Returns the parameter to listout when the file was created, or fail if the file can’t be processed.
listin = (x: x/file/x:x>x/run)
listout filename
Takes as input a Poplar expression, such as a list or string, and writes it in a special form on file filename. The expression may be recovered using listin.
marry
Takes a list of two lists of equal length. Each element of one list is paired with its corresponding element in the other list, and marry returns that list. See also zip.
[["a","b",1],["c","d",2]]/marry = [["a","c"],["b","d"],[1,2]]
max
Returns the maximum element of its input, which must be a list of numbers. Thus x///max is equivalent to x/max.
micas
A pattern which matches a single character and returns the number of micas it would take in Times Roman 10 pt. font.
"a"/{micas} = 165
"aA"/{micas,!} = [165, 265]
One can use scaling factors to get approximate answers for other fonts.
min
Like max, but the minimum.
minus
Takes a list of two numbers [a,b] as input. Returns a-b.
plus
Takes a list of two numbers [a,b] as input. Returns a+b.
print
Is like display but prints only strings. It prints them with no quotes. See display.
quit
Takes a string as input, executes the string as a command. Does not return to Poplar. See also exec.
reverse
Takes a list or string and reverses the order of its elements or characters.
["a","b","c"]/reverse = ["c","b","a"]
"abc"/reverse = "cba"
run
Takes a string, treats it like a Poplar program, and evaluates it, returning the value.
stop
Exits poplar like quit, but does not attempt to execute a command.

subst
Takes as input a string and performs substitutions for occurrences of a string (the pattern string). The pattern and the substitution string ("new" string) are prompted from the terminal. Typing DEL for either will abort the substitution. This function is similar in function to the Bravo Substitute command. It returns the input string with all occurrences of the pattern replaced by the substitution string. Typing
/subst M P M Q M
is equivalent to typing
/{(...("Q">"P"))!...} M
sort
Takes a list and returns its sorted permutation. If all the items are numbers the result is ordered by numerical value.
See also usort and asort.
symbol
Returns a list of all variables referenced or defined (sans primitives). Ignores its input.
times
Takes a list of two numbers [a,b] as input. Returns a*b.
tolower
Takes a string as input. Returns the string with all upper case letters [A-Z] changed to their lower case equivalents [a-z].
toupper
Takes a string as input. Returns the string with all lower case letters [a-z] changed to their upper case equivalents [A-Z].
usort
Uses asort to sort the incoming list, which must be a list of strings, and returns a sorted list with duplicate strings removed. See also asort, sort.
write filename
Takes the incoming string and writes it on file filename. If file filename exists, it will be overwritten. An error will occur if the file is open for reading. The simplest way to avoid such errors is to never write on a file which as previously been the input to a file or listin. See also listout.
zip
Takes a list of two lists. Elements in the two lists are interleaved in the resulting list.
[[1,2],[3,4]]/zip = [1,3,2,4]
The following functions are used in debugging Poplar and are not normally useful or necessary.
garbage
Forces a garbage collection. Ignores its input.
snap
Gives a snapshot of the storage allocator. Ignores its input.
Appendix C: Syntax Equations
This grammar describes how the Poplar parser treats a program. Therefore it describes many illegal programs whose illegality does not come to light until the program runs. Don’t be confused by the names given to grammatical categories. They are meant to be suggestive of the first one or two alternatives, but not all of them. Thus a Function can, degenerately, be a Sequence which can be a Statement, etc.
Non-terminals are in italic, literal characters are in bold
PoplarProgram ::= Function end-of-file
Function ::= Variable : Function
| [ VariableList ] : Function
| Prelude Function
| Sequence
Prelude ::= Statement ;
| Variable
| ChoiceExp |
| ThenExp >
| BinaryExp BinOp
Sequence ::= Statement ; Sequence
| Statement ;
| Statement
Statement ::= Variable Statement
| ChoiceExp
ChoiceExp ::= ChoiceExp | ThenExp
| ThenExp
ThenExp ::= ThenExp > BinaryExp
| BinaryExp
BinaryExp ::= BinaryExp BinOp PrefixExp
| BinaryExp PrefixExp
| PrefixExp
BinOp ::= / | // | /// | % | + | - | ,, | --
PrefixExp ::= UnaryFunction PreFixExp
| - PrefixExp
| ~ PrefixExp
| PostFixExp
PostFixExp ::= PostFixExp !
| PostFixExp ,!
| PostFixExp ?
| PostFixExp *
| SimpleExp
SimpleExp ::= String
| Variable
| PrimitiveFunction
| @
| #
| fail
| ...
| [ List ]
| [ ]
| { Function }
| ( Function )
List ::= List , Function
| Function
VariableList ::= VariableList , Variable
| Variable
Variable ::= a single letter | any sequence of letters and digits including one capital letter
UnaryFunction ::= len | blanks | write | listout
String ::= " any sequence of characters in which a ↑ precedes every " "
PrimitiveFunction ::= a sequence of two or more small letters (see Appendix B.)

Poplar uses these literal symbols:
; / // /// % | > : + - ,, -- ~ ! ,! ? * " @ # ... [ ] { } ( ) ,
Appendix D
Getting Started
Get [maxc]<morris>poplar.image and run it.
On [maxc]<morris>pl> is a set of files ex1.pl - ex9.pl for each of the examples in Appendix A.
Please send comments to Morris.