In this lesson, a simple report program to print "Hello, world!" is written and the program is run from llines (the user interface) and from the command line with llexec.
Executive summaries are very concentrated statements of the facts mentioned in the lesson. If you need this lesson, you will not feel confident that you understand the executive summary or have the ability to do the things described. If everything in the executive summary makes perfect sense to you, you may be able to skip the lesson without missing anything of importance.
Lifeline report programs have a file extension of .ll . Both the command-line interpreter (llexec) and the user interface main menu command "r" will add the .ll to a filename, if necessary, to find a report program if it exists. Lifelines searches the paths in the LLPROGRAMS configuration variable in the order given and will attempt to execute the first matching report. LLPROGRAMS also determines the order in which reports are listed when the "p" command is given from the main menu of the user interface. Every report program must have exactly one main procedure named main with no arguments, that is: proc main(){ }. By convention, Lifeline libraries have a file extension of .li and are not listed by the "p" command in the main menu of the user interface. Libraries do not have main procedures and cannot be executed. Report files, both executables and libraries, have headers in comments (/*&hellip*/) at the top of their files. Header variables @progname, @version, and @output are used in building the list displayed by the "p" command in user interface. The value of the variables seem to be whatever comes after white space following @variable_name. Header variable values seem to be free-form strings that may contain spaces. By convention, bundled reports use the filename for the value of @progname, but more descriptive values are allowed. The other header variables do not seem to be used by Lifelines, but may generate reports about reports with external software. print() takes a string arugment and outputs the string to STDOUT. Literal strings may be enclosed in double quotes. Newline maybe entered as backslash n. Anything in a report procedure that can be interpreted as a string is sent to a file and if the output filename is not supplied, it is prompted. Messages from the command-line version llexec cannot be suppressed or redirected. Both llexec and llines will not run without a database specified and if one is not specified on the command line it is prompted.
First read section 1.3 of Lifelines Programming Subsystem and Report Generator (herein referred to as the Manual). This section is about templates for new reports, in case you have a version of the Manual that has different section numbering. The Manual appropriate for your version of Lifelines should have been installed somewhere on your system when you installed Lifelines.
From the Manual you should gather that a minimal program consists of some stuff in comments (/*…*/) at the top of the file and one procedure called main (proc main). Programs may have many procedures, but must have exactly one called main. Unlike comments used elsewhere in reports, Lifelines looks at some stuff in the ones at the top of a report program. In particular Lifelines looks at @progname, @version, and @output when it lists reports for you to pick from with the "p" command in the llines main menu. If you copy someone else's program in order to make some changes to it, you should change the @progname or the @version so you can tell which program is the original and which is the copy when you pick a report from the list. Traditionally, authors of bundled reports have kept @progname in sync with the filename of report programs. However you have the ability to use more descriptive program names and to make better use of the @version part of the header.
Every report program that will be run must contain exactly one main procedure. This procedure must be named main. So this goes in every report program:
proc main(){
}
Do not worry about the parentheses for the moment. In other procedures, including ones you write, there may be something to do with them, but in proc main they must always be empty. On the other hand, Lifelines gets very unhappy if there is nothing between the curly braces, so there has to be something there for Lifelines to do, even if it is trivial and accomplishes nothing.
Of course there has to be stuff between the curly braces if the report program is to do anything. If you do not have much programming experience you should know that most of the action in any very complicated program does not occur in the main procedure but occurs in many small procedures and functions which are launched from the main procedure either directly or through intermediate procedures.
The philosophy behind successful programming in procedural languages like the Lifelines report language is divide and conquer. A complicated problem is broken down into many smaller problems and each smaller problem is solved by a small procedure or function. If your main procedure is running many hundreds of lines long, you almost certainly have failed to understand how to approach programming.
If the smaller bits of programming are correctly written many of them will be useful in other programs. The ones that are otherwise useful are removed to files called libraries which can be used by other programs. We will come to how to do this, but the thing to know now is that those library files in Lifelines have an .li extension by convention. This is why you may find report files with an .li extension that do not have a main procedure. These files are not meant to be used (and cannot be used) to generate reports on their own, but hold code that various reports may borrow.
I went to some length about that because in many of our examples we will write simple reports entirely in the main procedure. There is nothing wrong with this for very simple reports and for testing and demonstration purposes. But when we get a little bit of code that works and does something uesful, we should be thinking of making a function or procedure out of it and moving it to a library.
The rule is the file foo.ll is a program file that contains one and only one main procedure. It may contain some other procedures and functions that are useful only to it. (Perhaps I should mention that "foo," "bar," "foobar," and a few other similar things are used as example names for whatever might need naming in an arbitrary example. In this case, file foo.ll means a file with some name ending in .ll, in which case bar.li would be some other file with name ending .li which will not have a main procedure because library files do not have main procedures.)
If foo.ll is a bona fide report program, it can be called with the "r" command in the main menu of llines with either the name foo or foo.ll and also by llexec with -x foo or -x foo.ll. However, you cannot use either method to force execution of a report in a file with any other extension, even if the file is — internally — a legitimate report program.
Move to your Reports directory. See what is there. There should be nothing or some previous reports you have written or tried to write, which may be why you are here. If there are bunch of bundled reports (reports that came with Lifelines) you are in the wrong place for experimenting and writing your own reports. You need have your own directory in which to write experimental reports. You may have to create that directory and add it to the LLPROGRAMS variable in your Lifelines configuration file or environment. You are on your own on how to do this. Consult LifeLines Documentation (aka the User Guide). If you cannot get over this hurdle, you are not ready for programming.
In some of what follows, I make the assumption that your Reports directory (where you write your experimental programs) is "next to" your Databases directory where you have created (with the Lifelines user interface) a Database called Test. "Next to" means that the path from your Reports to your Databases is ../Databases . Eventually I will assume that there are also Inputs and Outputs directories "next to" your Reports directory. If this is not so, it is up to you to make adjustments in how you read material here or in your directory setup. Do not attempt to create a database in Databases in any way other than using Lifelines.
We are not going to cover how Lifelines creates and organizes its databases. This is about programing reports for the Lifelines system, not about reprogramming Lifelines itself.
Create a file named test.ll in your Reports directory with these contents:
/*
* @progname Hello, world!
* @version 1.0
* @author YOUR NAME
* @category programing tutorial
* @output STDOUT
* @description
Tutorial example one.
*/
proc main(){
print("\nHello, world!\n")
}
Give yourself extra credit if you managed to cut and paste the contents from here to your file instead of typing them in. Real programmers are lazy. More than that, however, copying code by retyping it is relatively prone to error. If you want to ask someone for help or send them a few lines of code, use whatever copy-and-paste function you may have in your editor instead of retyping freehand.
Beware, however, of cutting and pasting from HTML source because certain characters (particularlly <>&) have to be specially encoded in HTML. Also give yourself extra credit if, however you entered this program, you realized you were supposed to substitute your name for YOUR NAME.If your LLPROGRAMS variable is correct, however you set it, you can run test.ll in the Lifelines user interface (llines).
Even though test.ll does not do anything to or with a database, you must select a database to get into llines (or have llines create a database for you).
When you get to main menu in llines, select "p" (for "Pick a report from a list and run). This should bring up a list of reports including Hello, world! If Hello, world! is not on the list, LLPROGRAMS is not correct or you have not entered Hello, world! for @progname in the comments at the head of the file. If Hello, world! shows up, but it is way at the bottom, you can make it show up near the top by adjusting your LLPROGRAMS variable to put the directory you use for testing and writing your own reports first in the list of directories in LLPROGRAMS. It is up to you at this point to fix or adjust LLPROGRAMS or @progname.
Run Hello, world! by selecting it from the list. Lifelines should show you a screen with:
Hello, world!
along with Lifelines own name, a report of the time the program took, a "Strike any key…" message, and a report that the program ran successfully.
If you get an error message instead, you have made a mistake in copying the program. The error message may be helpful in finding the mistake. The error message may indicate a problem farther down in the program than where your mistake actually is. If you have not closed quotation marks, parentheses, comments, or braces properly, Lifelines may not realize there is a problem until it has processed several lines or until it reaches the end of the file. That's where it reports the problem being, but that is well past where you made your mistake.
You can (and should) deliberately introduce these kinds of mistakes — one at a time — in test.ll to see what errors Lifelines reports. You are going to make these kinds of mistakes in programming, and you need to understand error messages you get when you do.
Notice that you can also run test.ll by using the "r" ("Generate report by entering report name") command. When you do that, you may enter only test at the prompt. Lifelines assumes that an executable report program will have a filename ending in .ll and will supply this for you. You must know the filename to use "r." The prompt implies that you should not enter the .ll, but Lifelines quietly fixes things if you do.
The "p" command will list reports according to the value of @progname in the header of the report file (and so looks at the headers of all report programs to build the list for you to pick from). The "r" command looks only for files: when you enter test, it looks for the file test.ll and will fail if it does not find it. It also will run the first test.ll it finds by going through directories in LLPROGRAMS in order. It may be a bad idea to have report programs with the same filenames even though you can do this by putting them in different directories listed in LLPROGRAMS. Only the first one found will ever be excuted by "r," but you can scroll through "p" to find programs that are contained in files with identical names.
Test part of the above by changing the name in @progname from Hello, world! to foo.ll and using the "p" command. foo.ll should now show up in the list and should run just as Hello, world! did if you select it. But if you use the "r" command and enter foo at the prompt, Lifelines looks for the file foo.ll and if it does not exist, "r" will fail.
It seems to be traditional for authors of the bundled reports to keep @progname in sync with the filename of the program. This may be useful in some ways. It makes it easy to find the file associated with a particular report program in case you want to fix or crib something, and there is only one thing to remember (the base filename) to use either "p" or "r" commands. However, for reports you write yourself this may not be the best way, so I have departed from it. You can follow my example or you can follow the example of the authors of the bundled reports.
I am going to be brutally honest here, you probably would not have remained interested in Lifelines if had not come with some bundled reports and if some of the reports had not done some of the things you wanted to do. And many of the bundled reports are very good. But the truth to tell, a few of the bundled reports stink and a few more do work, more or less, but contain some really horrible code. There is a lot to be learned from studying the bundled reports, and when you find one that works well — well there is no point in reinventing the wheel. But take them with a grain of salt.
The important thing is to keep the header comments up to date by whatever system you use. If you copy a program to make changes to it and you use the system that names programs the same as filenames, be sure to keep @progname in sync with the new filename. If you use the system of more descriptive names in @progname, update (at least) the verison number when you alter a program (at least if you are keeping a copy of the older version, which is often desirable for back up, especially if the older version worked at all).
Notice that @progname, @version, and @output are rather free form. @version does not have to be a number, and @program does not have to be a filename. @output is meant to tell what form output takes.
Your program test.ll (aka Hello, world!) will run with the command line version of Lifelines: llexec. You may have used Lifelines for a long time without using llexec. The reason for introducing llexec now is that it is often more convenient for programming. In many cases you want to run a report over and over to get bugs out and to make the output prettier. The llines interface may only get in the way, and in many cases you want to rig a variable that you might otherwise ask the user to set so that you can compare the results when the input is consistent.
Okay, even if you do not understand that, try running your test report with llexec. As we have seen, you can test reports with llines if you just hate llexec or are testing parts of your report that ask for user input.
First, quit llines if you have it open in some window or terminal. (In truth, you can leave llines open if it is using a different database, but let's not complicate things.)
Switch to your Reports directory if you are not there already.
In the following I make the assumption that you have test database named Test, that your database directory is named Databases and is "next to" your Reports directory, and that your test report (aka Hello, world!) is named test.ll and is in your Reports directory. If any of that is not so, you should make appropriate adjustments to the suggested command. Also, if you are on a system that uses backslashes in paths, you will have to make that adjustment. When I give examples of commands to enter at the command line, $ at the beginning of the line represents your command line prompt. It may be something else on your system (such as > or #) and you do not type it. Also "enter" means you hit the "Enter" key at the end of the line. Now enter this at your command line (with whatever adjustments you may have to make):
$llexec -x test.ll ../Databases/Test
If that worked, you will get something like this:
Program is running... Hello, world! Program was run successfully.
The part of the command at the end (../Databases/Test) should be the path to the database you want to use. If the path is not valid, llexec may ask if you want to create such a database. You do not. You want to fix the path so it points to the test database you have already created.
If you do not enter the end part at all, llexec will prompt you to enter a path to a database. Just like llines, llexec insists on opening some database first even if you want to run a report which does not do anything with or to a database. It will even look for the database before it considers the report name.
The part of the command in the middle (-x test.ll) tells llexec to run the program test.ll (see man llines or the equivalent documentation on your system for other options that work with llexec). llexec works like running a report from the "r" command in the main menu of llines. It will even add the .ll to the end of your filename if you do not provide it. But it will not look for programs according @progname as the "p" command in the llines main menu will do. This might or might not be a consideration in whether you want to keep @progname in sync with your filenames.
If llexec cannot find your report program it will abort with an appropriately severe message. The reason llexec might not find your report is that you did not type the filename of the report correctly. If your report is in your Report directory and that directory is in your LLPROGRAMS directory llexec will find it no matter what your current directory is. But it might find some other report with filename test.ll in some other directory if that directory comes first in LLPROGRAMS.
If llexec finds your report and runs it, the report might still contain errors, and llexec will report those errors like llines did when you ran erroneous reports in llines.
Notice that llexec prints its own messages when you run it on the command line (such as "Program is running..."). You cannot suppress these messages or redirect them. This is a flaw in llexec which makes llexec less useful than it could be, but it is not a problem for our purposes. which is using llexec in testing and debugging reports. Getting out of the way of these messages was the reason for the first new line in "\nHello, world!\n" The messages generally start with a newline, but do not always end with a newline.
Now copy test.ll to another filename in your Reports directory. If you use the method of program naming which keeps @progname in sync with filenames, change @progname in the copy.
Edit the header comment to make the @version 1.1 and @output text. You should always practice keeping the header comment current. Doing so will save much frustration if you get very far in report programming.
Almost all of the time, the point of a report is to write something to a file. So we introduce writing to a file here, although we will go into the gory details later.
When running a report llexec or llines will try to write anything that it finds that looks like a naked string to a file. If it does not know what file to write to, it will prompt for a name. So this will work:
/*
* @progname Hello, world!
* @version 1.1
* @author YOUR NAME
* @category programing tutorial
* @output text
* @description Tutorial example one.
*/
proc main(){
"Hello, world!\n"
}
If you run this in either llines or llexec you will be prompted for a filename to be written to. Beware: this will zap any existing file with that name which may already exist. foobar.txt is my pet filename for testing and experimentation. I will never write anything precious to a file with this name, so if a previous result gets zapped, it is no tragedy. When I clean up a directory, I do not have many experimental files with various creative names mixed in with serious results. I know foobar.txt and its relatives foo1.li, foo2.html, and so forth are all expendible.
When a report program is done, of course, you want it to write to a safe place and you do not want it to zap previous results. You can make report programs which are better behaved, and we will come to that.
Version 1.1 of Hello, world! will be developed further in the next lesson (which means do not flush it unless you want to recreate it).
I note too serious limitations to Lifeline here. You cannot make Lifelines treat STDOUT as a file. You can make Lifelines write to STDOUT with print(), but that would mean you would have to have different report programs to write essentially the same thing to STDOUT as to a real file. And I have mentioned that Lifelines writes it running, done, and error messages to STDOUT so you cannot separate them from any program output you my have by redirecting STDERR. These things make Lifelines less effective in a stream processing environment.
©Copyright 2009 by Lars Eighner. Original material may be copied for personal use, but may not be sold, made available contingent on the payment of any fee or access charge and may not be bundled in any product which is sold for a fee or media charge or which requires any payment for access. In short, you cannot charge money for material I have made freely available. Software and other products mentioned may be trademarks belonging to their respective owners.