Libero 2.32

# Libero Documentation

• Introduction to Libero
• Summary
• Terms and Disclaimer
• Introduction
• How It All Started
• Learning To Use Libero
• The Coke Machine Example
• Example of Using a Telephone
• Serious Stuff: Events and Names
• Example of Controlling a Telephone
• Source Code For Phone.c
• Example of a C/C++ Comment Stripper
• Example of Parsing An Arithmetic Expression
• Dialogs For Dummies
• Explain It Like I'm 5 Years Old
• A Bit Of Animation
• Installing Libero
• Getting Your Hands On Libero
• Availability and Distribution
• Documentation
• Deinstalling Libero
• What You Will Need
• Installing Libero for UNIX
• Installing the MS Windows Kit
• Installing the MS-DOS Kit
• Installing the 32-bit MS-DOS Kit
• Installing the OS/2 Kit
• Installing the VAX/VMS Kit
• Installing the Examples
• Configuring and Testing Libero
• Using Libero
• Running The lr Program
• The Dialog File Syntax
• Libero Options
• Using Exceptions
• Using The Defaults State
• Using Sub-Dialogs
• Using Super-States
• Using Templates
• Using Get-External-Event
• Efficiency
• Efficiency of The Generated Code
• Efficiency of The Dialog
• Care and Feeding of Dialogs
• Style - Good, Bad, and Ugly
• Choosing Names
• When To Use Libero
• Libero Examples
• Libero 'install' Script for UNIX
• COBOL Picture Parser
• Expression Parser
• Configuration Management System
• E-mail Robot - ERBOT
• HTML Preprocessor
• Complete Examples Archive
• Libero's Languages
• Choosing A Language
• Standard Public Names
• Contributed Schemas
• The Awk Language
• The C Language
• The C++ Language
• The COBOL Language
• The GNU Borne-Again Shell Language
• The Java Language
• The Microsoft Setup Basic Language
• The MS Visual Basic Language
• The Perl Language
• The Rexx Language
• The UNIX C Shell Language
• The UNIX Bourne Shell Language
• The UNIX Korn Shell Language
• The 80x86 Assembler Language
• Libero Technical Reference
• A First Look At Code Generation
• The Schema
• Schema Commands
• Notes
• Syntax Of Expressions
• Syntax Of Logical Conditions
• Logical Operators
• Standard Tests
• Control Variables
• Standard :do Conditions
• Extra Variables In A :do Loop
• :do while condition
• :do event
• :do event local
• :do state
• :do module
• :do action
• :do nextst
• :do vector
• :do overflow
• :do stubs [filename]...
• General Rules for Code Generation
• Generating Module Stubs
• Generating COBOL Code
• The State Machine Algorithm
• Compiled-In Limitations
• Quick Reference - Libero Schema Language
• General
• Schema Commands
• Expressions
• Logical Conditions
• Control Variables
• Standard :do Conditions

• # Introduction to Libero

## Summary

• Libero is a Programmer's Tool and Code Generator. You define the high-level logic of a problem as a diagram: Libero generates the code to implement this logic. Your applications are easier to write, more robust, easier to understand. Libero uses a finite-state machine as the underlying model.
• Libero generates code in these languages: C, C++, Java, Perl, Awk, 80x86 assembler, COBOL, MS Visual Basic, MS Test Basic, UNIX C Shell, UNIX Korn Shell, UNIX Bourne Shell, GNU Bash Shell, Rexx, PL/SQL, and PHP. You can extend Libero support to other languages via external schema files.
• Libero runs on: UNIX (tested on Linux, HP/UX, SunOS, Irix, AIX, Solaris), MS-DOS, MS-Windows, Digital Open-VMS, OS/2. It is in theory portable to any ANSI C compiler; the Libero sources are part of the package.

## Terms and Disclaimer

This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

## Introduction

"Laziness

The quality that makes you go to great effort to reduce overall energy expenditure. It makes you write labour-saving programs that other people will find useful, and document what you wrote so you don't have to answer so many questions about it. Hence the first great virtue of a programmer."

Larry Wall [Programming Perl, Larry Wall and Randal. L. Schwartz, 1992 O'Reilly & Associates, Inc., page 426.]

### How It All Started

In 1982, as a student, I got into games programming as a way of making some money. I skipped the build-it-yourself-from-an- old-TV-screen-and-a-hex-keypad phase of people computers, and jumped straight into the world of ready-to-go colour and sound. My first PC had 5K of RAM, and 25x22 colour video. It could beep in three-part harmony. My first published work was an article that explained how to tweak the video output to 30x33 (hi-resolution!). Soon I was writing sprite painters, assembly libraries for sound and graphics, even languages to replace the built-in Basic. Anything to let me write my games faster and better. Before I knew what had hit me, I was hooked into the cycle of writing tools to improve the world I lived in.

I haven't written a game for a long time, but I've continued writing tools. Libero is one of the best - it's simple, clean, portable, and has hit the mark so often that I feel it's unfair to keep it for myself.

The ideas behind Libero evolved in Brussels, Belgium during the 1980's and 90's. I worked with Leif Svalgaard, on what we would now call a CASE tool - ETK - that let COBOL programmers produce clean, portable code instead of the mess we generally saw. One of the key techniques we used was a programming method based on finite state machines. Historically, the first real programs that used finite state machines were compilers. In 1967, Peter Naur describes a new way of using FSMs (which he refers to as a Turing machine approach) in a compiler, and shows how they simplify error checking. He goes on to say:

"The above description has stressed the checking aspect of the Turing machine approach. However, an equally important aspect is the ease with which arbitrary actions may be specified. By using this approach it is usually possible to avoid tests in individual actions to a surprisingly high degree. This in our experience is a very effective way of reducing the bulk and execution time of the translator algorithm."

[Annual Review in Automatic Programming, "Design Of The Gier Algol Compiler", Ed. Richard Goodman; 1964 Pergamon Press, page 77.]

ETK provides an interactive editor that you use to describe the logic of the program as a FSM. This approach encourages you to think about the complete problem. You describe everything that can happen, and how the program should react. The end- result looks a little like a flow-chart, but has more arrows, and fewer different kinds of boxes. The value of this approach is that you can abstract a complex problem using the restricted semantics of a FSM. In the same way that a While statement is less powerful but more useful than a Goto, a FSM is less powerful but more useful than a structured programming approach for describing complex problems. Leif Svalgaard once said: "the issue is not one of power, but coping with the human difficulty in understanding complex structures".

Now, in a conventional state machine, these boxes are given numbers, and the programmer builds some tables that encode the arrows. To make such a table by hand - or, just as bad, encode the table directly in the logic of the program, using GOTOs - is a Bad Thing, since the result is near to impossible to maintain. The neat part of our solution was that we took the textual description (called a 'dialog'), and generated the mystical tables directly as COBOL code. This is a Good Thing, since the original dialog is easy to change, and suddenly becomes excellent (and always accurate) documentation. In 1992 I began working as a consultant, and found that I wanted to use these techniques in my work. I was writing in C, so I threw together a code generator that could output C code using the same dialog methodology I was used to. I called this tool 'Libero' after the guy who runs around the sidelines at the football/soccer pitch doing all the dirty work. My first serious job was a bunch of TCP/IP clients and servers. It was nice to come back to the place three years later and find that the guy maintaining my work had only found one bug, and was happy to go into the programs. He said that the dialogs made them easy to understand and modify.

This kind of experience convinced me that there was real value in this tool. I've written lots of cute programs that are useful for my own needs, but that just don't hack it in the real world. Libero is different.

The first public release of Libero (1.7) generated C code using an external model, a kind of script called a 'schema'. I had a couple of schemas; one for ANSI C code, and one for multithreaded DEC/VMS code. I released version 2.0 onto the Net after adding schemas for UNIX scripts. This release gave me a lot of feedback, and I rewrote the code-generator again to make it much more generic. The current version 2.11 has a lot more language schemas and a lot fewer bugs, plus a front-end for MS- Windows.

I hope to continue in both these directions. Libero has become a tool that lets you switch between languages at ease. The first step is to accept the state-machine as a valid method for program development. This takes a little effort, but I hope that the examples which follow will help convince you. The second step is to see that this method is language-independent. You can write a program in C, then recode it in Perl without changing its design.

A hypothetical development team might write mainframe business applications in COBOL, tools in Perl, servers in C, batch scripts in Korn shell, Web clients in Java. Yet instead of a team fragmented into specialists in each domain, you would have a team that enjoys a common design technique and who can quickly take on each other's work. In practice, you also get a certain communality of programming style, which disturbs language purists. However, I like it when - say - a COBOL programmer that knows only Libero take a C program written with Libero and says: "But it looks like COBOL!". Of course neither the Libero+C nor Libero+COBOL programs look anything like 'COBOL'.

### Learning To Use Libero

I've not found a quick way to convey the true nature of state machine programming. In my experience, it invariably takes a few days of practice. After three or four days' exposure to the idea there is a near-audible 'click!' and something in the brain connects all the pieces together and you go 'Oh, it's like THAT!'. Maybe people cultured on event-driven programming will get it faster.

## The Coke Machine Example

The coke machine (once awake and gently humming) sits against the wall, waiting. There is a theory about this humming business, notably that coke machines have terrible memories, and can't remember the words. A second school of thought says that the coke machines are humming because they're thinking about all the terrible things they're going to do to humans with crow bars. A third school of thought reckons that the coke machines have already done something, and are humming because they thought it was really funny. Like all coke machines, it's very good at waiting, due to Natural Selection. All the impatient proto-coke machines ran off screaming 'I can't take it anymore' and were crunched underfoot by brontosaurii back in the mists of prehistory.

A punter arrives with a handful of loose change. The coke-machine (covered by the modern and tasteful metal grid that serves both as a defensive covering and as an attraction for coke machines of the opposite sex) moves into 'attention' mode. When it hears the clink of tasty cash, rather that the ominous clunk of a heavy crow-bar, it relaxes into 'ready mode'. The machine is now ready to handle any eventuality, so long as it includes coinage.

As the last coin falls and the punter presses the button labelled 'healthie waye sparkling spring water', the machine considers the possibilities. Should it play dead, and just take the punter's money? Or maybe it can substitute a can of 30% sucrose 'sweeto-sap'? Finally it rumbles once, then again for added effect, then excretes a can of water. It munches the coins, then settles back against the wall, quietly humming again.

Just to prove that you can describe any sequence of events, however improbable, with a state machine, here goes. (The observant reader will have skipped to the next section by now, realising that this one is not worth the paper it's printed on. If you're reading this on-line, then this section is not worth the phosphor it's been digitised, rasterised, and sprayed by an electronic beam gun onto.) Act 1, scene 1 has our protagonist against the wall, trying to remember the words of the song. Our coke-machine hero is normally OK; if something went terribly wrong with the rehearsals (e.g. a dinosaur got sucked through a five-dimensional space-time vortex stretched between the middle terrasic to five minutes into rehersals; the dinosaur a little off balance after the trip puts down its left foot, and 'crunch'...), it might be Dead, in which case we have to call the whole thing off. We'll call this scene 'Should Be Gently Humming'...

Should-Be-Gently-Humming:
(--) Ok                         -> Something-Happened
+ Wait-For-A-Punter
+ Stop-The-Programme


The coke machine has been waiting, perhaps for days, for a punter to come along. When the footprints fade in from the distance (a coke machine's ears are always against the ground, which explains why you rarely find coke machines on wet or dirty surfaces), the machine hears either a cashy 'clink' or a more sinister 'clunk':

Something-Happened:
+ Accept-Punter's-Cash
+ Wait-For-Punter's-Choice
+ Shall-We-Cooperate?
(--) Clunk                      ->
+ Exit-Stage-Left-Running
+ Stop-The-Programme


The advent of a 'Clunk' tells the machine to beat it, smartly. If a 'Clink' happened, the machine goes on to think about a game plan for the coming few minutes. It accepts whatever spare change the punter has to spare, then pounces, metaphorically:

Before-Cooperating:
(--) Ok                         -> Cooperate
+ Consider-Punter's-Choice
(--) Nasty                      -> Let's-Be-Nasty
+ Consider-Punter's-Choice
+ Switch-Off-All-Lights
+ Wait-Until-Punter-Has-Left
+ Switch-On-The-Lights
+ Wait-For-A-Punter


If it's in a good mood, the machine ejects more or less the drink that the punter chose:

Cooperate:
(--) Coke                       -> Something-Happened
+ Eject-Can-Of-Coke
+ Wait-For-A-Punter
(--) Spring-Water               -> Something-Happened
+ Eject-Can-Of-Spring-Water
+ Wait-For-A-Punter
(--) Juice                      -> Something-Happened
+ Eject-Can-Of-Juice
+ Wait-For-A-Punter
(--) Sweeto-Sap                 -> Something-Happened
+ Eject-Can-Of-Sweeto-Sap
+ Wait-For-A-Punter
(--) Empty                      ->
+ Switch-Off-All-Lights
+ Stop-The-Programme


If the coke machine's pretty weak internal moral system failed, it supplies the opposite of whatever the punter wanted:

Let's-Be-Nasty:
(--) Coke                       -> Something-Happened
+ Eject-Can-Of-Juice
+ Wait-For-A-Punter
(--) Spring-Water               -> Something-Happened
+ Eject-Can-Of-Sweeto-Sap
+ Wait-For-A-Punter
(--) Juice                      -> Something-Happened
+ Eject-Can-Of-Coke
+ Wait-For-A-Punter
(--) Sweeto-Sap                 -> Something-Happened
+ Eject-Can-Of-Spring-Water
+ Wait-For-A-Punter
(--) Empty                      ->
+ Switch-Off-All-Lights
+ Stop-The-Programme


This diagram is a little complex for a first example. Remember however, that we are modelling a rich behaviour pattern that took several hundreds of millions of years to evolve. Real software is usually developed much faster, and should therefore be much simpler, only it isn't.

## Example of Using a Telephone

In a more technical example, we'll model the steps we take in using making a telephone call. By convention, the first state is After-Init. Another convention is to use Terminate-The-Program to halt the dialog.

After initialisation (I would recommend a good coffee or a decent beer, depending on the time of day), we pick-up the handset, and listen to the dialing tone.

After-Init:
(--) Ok                          -> Want-Dialing-Tone
+ Pick-Up-Telephone-Handset
+ Listen-For-Dialing-Tone
(--) Error                       ->
+ Terminate-The-Program


This leads us to the state Want-Dialing-Tone. Here we handle each of the possible events produced by Listen-For-Dialing-Tone. Let's look at these in detail:

• Ok We have a dialing tone, so we can dial the number. This is the normal, expected event.
• Silent, no tone The telephone exchange is having a hard day, or someone unplugged the phone. Or maybe a ninja attack team has cut the phone cables and is at this moment sneaking up the stairs and AAAARGH... In this simple example we loop forever; in a real program we would allow a limited number of attempts.
• Voices on the line Some other member of the household is making the phone company richer. We apologise and forget it.

We give each of these events a short snappy name and list them in a reasonable order. Like a 'case' statement, the actual order makes no difference to the machine, but helps the person reading the dialog. Typically we put the most frequent or expected events first, with the more bizarre ones at the end. Each event provokes one or more actions that correspond to modules of code. A module can be a function, procedure, subroutine, paragraph, etc., depending on the programming language. A module is where you do the real work.

Want-Dialing-Tone:
(--) Ok                          -> Want-Ringing-Tone
+ Dial-Required-Number
+ Listen-For-Ringing-Tone
(--) Silent                      -> Want-Dialing-Tone
+ Put-Down-Telephone-Handset
+ Pick-Up-Telephone-Handset
+ Listen-For-Dialing-Tone
(--) Voices                      ->
+ Apologise-Telephone-Busy
+ Put-Down-Telephone-Handset
+ Terminate-The-Program
(--) Modem                       ->
+ Put-Down-Telephone-Handset
+ Apologise-Cutting-Modem
+ Terminate-The-Program


Here we are waiting for a ringing tone. We can reuse some of the event names - Ok, Silence, Voices - from the previous state. This makes the generated code smaller, and is nice for the reader, since there are fewer names to remember:

Want-Ringing-Tone:
(--) Silence                     -> Want-Dialing-Tone
+ Put-Down-Telephone-Handset
+ Pick-Up-Telephone-Handset
+ Listen-For-Dialing-Tone
(--) Engaged                     -> After-Engaged
+ Put-Down-Telephone-Handset
+ Consider-Trying-Again
(--) Voices                      -> Want-Dialing-Tone
+ Complain-Crossed-Connection
+ Put-Down-Telephone-Handset
+ Pick-Up-Telephone-Handset
+ Listen-For-Dialing-Tone


You get the picture. The remaining states map out the rest of the conversation. The next state has an event, Doorbell, that is not produced by any previous module, but that can happen at any time. We call this an exception event, and we add the handling for it in the state where it can happen:

Want-Answer:
(--) Ok                          ->
+ Have-Conversation
+ Put-Down-Telephone-Handset
+ Terminate-The-Program
(--) Wrong-Number                ->
+ Apologise-Wrong-Number
+ Put-Down-Telephone-Handset
+ Terminate-The-Program
(--) Impatient                   ->
+ Put-Down-Telephone-Handset
+ Terminate-The-Program
+ Consider-Leaving-Message
(--) Modem-Or-Fax
+ Put-Down-Telephone-Handset
+ Terminate-The-Program
(--) Doorbell                    ->
+ End-Conversation-Quickly
+ Put-Down-Telephone-Handset
+ Terminate-The-Program

After-Engaged:
(--) Ok                          -> Want-Ringing-Tone
+ Dial-Required-Number
+ Listen-For-Ringing-Tone
(--) Impatient                   ->
+ Put-Down-Telephone-Handset
+ Terminate-The-Program

(--) Message                     ->
+ Leave-The-Message
+ Put-Down-Telephone-Handset
+ Terminate-The-Program
(--) Impatient                   ->
+ Put-Down-Telephone-Handset
+ Terminate-The-Program


The Defaults state is special; we never come here explicitly. Rather, the state holds events that are implicitly valid in any other state. Here we say that whenever the Doorbell event strikes, we put the phone down and beat it doorwards. Note that the Doorbell event is handled explicitly in Want-Answer - the action in that state is a little different from the other states.

Defaults:
(--) Doorbell                    ->
+ Put-Down-Telephone-Handset
+ Terminate-The-Program


### Serious Stuff: Events and Names

Now I want to show how an action module 'produces' an event. Libero provides a standard variable called The-Next-Event. An event like 'Ok' is actually called Ok-Event in the program (Libero tacks-on '-Event' for you). This is how you would set The-Next-Event in various languages:

• C/C++: the_next_event = ok_event;
• 80x86 assembler: mov the_next_event,ok_event
• UNIX Korn Shell: the_next_event=$ok_event • Perl: the_next_event =$ok_event;
• COBOL: MOVE OK-EVENT TO THE-NEXT-EVENT
• Visual Basic: the_next_event = ok_event

Libero applies this rule: at least one of the action modules for an event must supply a value for The-Next-Event. If no value for The-Next-Event is supplied, the dialog halts with some kind of error message.

The names of events and action modules can take various forms, depending on the language and your preferences. For example, the C/C++ code could take any of these forms:

• Plain style: the_next_event = ok_event;
• Caps style: TheNextEvent = OkEvent;
• Headline style: The_Next_Event = Ok_Event;
• Title style: The_next_event = Ok_event;

Personally I prefer the first style, which is why it's the default.

## Example of Controlling a Telephone

This next example is one that a correspondent at a telephone company proposed. It's a design for a simple but dedicated subprogram (we'll call it 'Rover') that hangs-around on the corner of our telephone central. Rover springs into action (presumably woofing and waggling a shaggy tail) when we pick-up the handset, or when a call comes in for us. The dialog loops around and around, each time driven by an 'incoming message'. This message is one of these events:

• Offhook - we picked-up the handset. Presumably this affects the current on the line, which the central can detect.
• Onhook - we put the phone down.
• Request - another person is calling us from a 'remote' phone.
• Digit - we pressed a digit on the touchpad.
• Whole Number - we pressed enough digits to form a recognised number.
• Accept - the remote phone accepted the call - ie. the remote Rover gets a Request event of its own.
• Busy - the remote phone was busy.
• Reject - the remote phone number was rejected - we made a mistake with the number, or (if dialing into Britain) the British changed their dialing prefixes again.
• Answer - finally, contact. I hope it was worth it.
• Release - the remote phone cut the Talking. At our end we hear a tone that signals this. (This is how my telephone central works.)

Here is the dialog. Rover waits for anything to happen, then leaps:

After-Init:
(--) Ok                           -> Idle
+ Wait-For-Incoming-Message
(--) Error                        ->
+ Terminate-The-Program


The two possible events at this stage are Offhook (coming from the local phone), and Request (from a remote phone). We call this state 'Idle'. Some people get confused at this point, and point out that Rover is exactly not idle, so why the name. Why not call the very first state 'Idle'? I answer so: the name of a state reflects the state we were in when the event happened. Thus, when Rover was idle, the only two events that could happen were Offhook and Request.

Idle:
(--) Offhook                      -> Dialing-First
+ Start-Dial-Tone
+ Reset-Dialed-Number
+ Wait-For-Incoming-Message
(--) Request                      -> Ringing
+ Start-Ringing-Local
+ Wait-For-Incoming-Message


The next two states show what happens when we dial the first digit. Rover stops with the dial tone, and slurps up the digits we press, one by one. I distinguish Dialing-First from Dialing-Next to ensure that Rover does a Stop-Local-Tones the first time only:

Dialing-First:
(--) Digit                        -> Dialing-Next
+ Stop-Local-Tones
+ Have-Dialed-Digit
+ Wait-For-Incoming-Message
(--) Whole-Number                 -> Seizing
+ Stop-Local-Tones
+ Send-Outgoing-Request
+ Wait-For-Incoming-Message
(--) Onhook                       -> Idle
+ Stop-Local-Tones
+ Wait-For-Incoming-Message


Rover doesn't bother handling the Onhook event here, since the Defaults state handles it just fine:

Dialing-Next:
(--) Digit                        -> Dialing-Next
+ Have-Dialed-Digit
+ Wait-For-Incoming-Message
(--) Whole-Number                 -> Seizing
+ Stop-Local-Tones
+ Send-Outgoing-Request
+ Wait-For-Incoming-Message


Okay, we just tried to 'seize' the remote phone. This is either accepted (and the remote phone starts ringing) or rejected for various reasons:

Seizing:
(--) Accept                       -> Ringing
+ Start-Ringing-Remote
+ Wait-For-Incoming-Message
(--) Busy                         -> Seize-Failed
+ Start-Busy-Tone
+ Wait-For-Incoming-Message
(--) Reject                       -> Seize-Failed
+ Start-Reject-Tone
+ Wait-For-Incoming-Message

Seize-Failed:
(--) Onhook                       -> Idle
+ Stop-Local-Tones
+ Wait-For-Incoming-Message


The next three states show how Rover handles a ringing phone (at either end) and the eventual Talking, if any. I combined the handling for the local and remote phones into two states; I could also write this more clearly but verbosely as four states (Ringing-Local, Ringing-Remote, Talking-Local, Talking-Remote).

Ringing:
!  Events from local phone
(--) Offhook                      -> Talking
+ Stop-Ringing-Local
+ Wait-For-Incoming-Message
(--) Onhook                       -> Idle
+ Stop-Ringing-Remote
+ Wait-For-Incoming-Message
!  Event from remote phone
+ Stop-Ringing-Remote
+ Wait-For-Incoming-Message

Talking:
!  Event from local phone
(--) Onhook                       -> Idle
+ Send-Outgoing-Release
+ Wait-For-Incoming-Message
!  Event from remote phone
(--) Release                      -> Released
+ Start-Released-Tone
+ Wait-For-Incoming-Message

Released:
(--) Onhook                       -> Idle
+ Stop-Local-Tones
+ Wait-For-Incoming-Message


The Defaults state handles the Onhook event in those states where there is no local tone. In those states (like Released), Rover silences the local phone, then loops back to the Idle state:

Defaults:
(--) Onhook                       -> Idle
+ Wait-For-Incoming-Message


## Source Code For Phone.c

When I take the above dialog (called phone.l), and give it to Libero, this is what happens:

 C:\CTOOLS\LIBERO>lr phone
LIBERO v2.11 (c) 1991-96 by Pieter A. Hintjens
lr I: processing 'phone.l'...
lr I: creating skeleton program phone.c...
lr I: building phone.d...
lr I: building phone.i...
lr I: Building stub for have dialed digit
lr I: Building stub for reset dialed number
lr I: Building stub for send outgoing release
lr I: Building stub for send outgoing request
lr I: Building stub for start busy tone
lr I: Building stub for start dial tone
lr I: Building stub for start released tone
lr I: Building stub for start ringing local
lr I: Building stub for start ringing remote
lr I: Building stub for stop local tones
lr I: Building stub for stop ringing local
lr I: Building stub for stop ringing remote
lr I: Building stub for wait for incoming message
C:\CTOOLS\LIBERO>


Libero assumes that I want to make a program called phone.c. Since this file does not exist, it creates a skeleton program for me. This only happens the first time. Once the program is created, Libero will add missing pieces to it, at the end, but will never change it again otherwise.

I get two other files as well, phone.d and phone.i. These are the data and interpreter for my dialog. I need these files to compile phone.c. Each time I run Libero, it recreates these two files. If I was working in another language, I would get different files, with different extensions. The default language is C. Here is the skeleton program that Libero gave me, more or less:

/*=========================================================================*
*                                                                         *
*  phone.c     description...                                             *
*                                                                         *
*  Written:    95/07/29    Your Name                                      *
*  Revised:    95/07/29                                                   *
*                                                                         *
*  Skeleton generated by LIBERO 2.10 on 29 Jul, 1995, 11:50.              *
*                                                                         *
*=========================================================================*/

#include "prelude.h"                    /*  Public definitions             */
#include "phone.d"                      /*  Include dialog data            */

/*- Function prototypes ---------------------------------------------------*/

/*- Global variables used in this source file only ------------------------*/

static int
feedback;                           /*  Feedback for calling program   */

static char
*some_param;                        /*  Example of parameter           */

/********************************   M A I N   ******************************/

int my_funct (char *p_some_param)
{
feedback = 0;                       /*  No errors so far               */
some_param = p_some_param;          /*  Local copy of parameters       */

}

/************************   INITIALISE THE PROGRAM   ***********************/

MODULE initialise_the_program (void)
{
the_next_event = ok_event;
}

/**************************   GET EXTERNAL EVENT   *************************/

MODULE get_external_event (void)
{
}

/************************   TERMINATE THE PROGRAM   ***********************/

MODULE terminate_the_program (void)
{
the_next_event = terminate_event;
}

/**************************   HAVE DIALED DIGIT   **************************/

MODULE have_dialed_digit (void)
{
}

/************************   RESET DIALED NUMBER   ************************/

MODULE reset_dialed_number (void)
{
}
... etc.


I discuss the details of Libero+C programming elsewhere. Some rules apply whatever language you use:

• Libero creates a program that is ready to compile and run. The dialog will halt with an error message since we don't supply any events beyond the initial Ok.
• There are some standard modules: a main entry point of some kind, Initialise-The-Program, Get-external-event, and Terminate-The-Program.
• Libero adds a module stub for each action that you use in your dialog.
• The program relies heavily on source-local variables. I.e. variables that are global to all functions in the source file, but not visible to other source files. This can be disturbing to some people, but is a natural consequence of the way Libero restructures your program. It actually works quite well, as we'll see later.

The basic C program consists of a number of standard pieces:

• Main - passes control to the Libero generated code. In some languages, such as the C program above, it must also copy any arguments into variables that can be accessed by other modules.
• Initialise-The-Program - initial sanity checks, then usually passes an Ok event to the dialog. We do this with the line:
the_next_event = ok_event;
• Terminate-The-Program - halts the dialog by passing it a Terminate event. Generally this is the last action that the dialog executes, so it is also a convenient place to shut log files, etc.
• Get-External-Event - the dialog calls this implicitly if it didn't get an event from any of the modules it executed.

## Example of a C/C++ Comment Stripper

One of the classic uses for state machines is in scanning and parsing text - e.g. source programs. People usually do this with tools like lex and yacc, or using languages like Awk and Perl that are good at pattern matching. When I explain that Libero uses a state-machine model, some people think that I am asking them to use Libero in place of the honoured tools I just mentioned. The next example shows why this is not the case.

Here I present a C/C++ comment stripper based on an example by Robert Martin (who makes a finite-state machine code generator for C++). In C and C++, comments are either enclosed between '/*' and '*/', or begin with '//' until the end of the line. This program removes all comments from a C or C++ source program, with some checking for errors like an unmatched '/*':

!
!   C/C++ comment stripper dialog
!
!   Written:    95/03/30  Pieter Hintjens <ph@imatix.com>
!   Revised:    96/05/15  Frans Janssens <frans@sebb.bel.alcatel.be>
!
-source=stripper.c
-schema=lrschema.c

After-Init:
(--) Ok                         -> Outside-Comment
+ Get-Char-From-Input
(--) Error                      ->
+ Terminate-The-Program

Outside-Comment:
(--) Quote                      -> Skip-Quote
+ Put-Char-To-Output
+ Get-Char-From-Input
(--) Slash                      -> After-Slash
+ Get-Char-From-Input
(--) Star                       -> Outside-Comment
+ Put-Char-To-Output
+ Get-Char-From-Input
(--) NewLine                    -> Outside-Comment
+ Put-Char-To-Output
+ Get-Char-From-Input
(--) Other                      -> Outside-Comment
+ Put-Char-To-Output
+ Get-Char-From-Input

Skip-Quote:
(--) Quote                      -> Outside-Comment
+ Put-Char-To-Output
+ Get-Char-From-Input
(--) Slash                      -> Skip-Quote
+ Put-Char-To-Output
+ Get-Char-From-Input
(--) Star                       -> Skip-Quote
+ Put-Char-To-Output
+ Get-Char-From-Input
(--) NewLine                    -> Skip-Quote
+ Put-Char-To-Output
+ Get-Char-From-Input
(--) Other                      -> Skip-Quote
+ Put-Char-To-Output
+ Get-Char-From-Input

After-Slash:
(--) Quote                      -> Outside-Comment
+ Put-Slash-To-Output
+ Put-Char-To-Output
+ Get-Char-From-Input
(--) Slash                      -> Skip-Eoln
+ Get-Char-From-Input
(--) Star                       -> Skip-Comment
+ Start-New-Comment
+ Get-Char-From-Input
(--) NewLine                    -> Outside-Comment
+ Put-Slash-To-Output
+ Put-Char-To-Output
+ Get-Char-From-Input
(--) Other                      -> Outside-Comment
+ Put-Slash-To-Output
+ Put-Char-To-Output
+ Get-Char-From-Input

Skip-Eoln:
(--) Quote                      -> Skip-Eoln
+ Get-Char-From-Input
(--) Slash                      -> Skip-Eoln
+ Get-Char-From-Input
(--) Star                       -> Skip-Eoln
+ Get-Char-From-Input
(--) NewLine                    -> Outside-Comment
+ Put-Char-To-Output
+ Get-Char-From-Input
(--) Other                      -> Skip-Eoln
+ Get-Char-From-Input

Skip-Comment:
(--) Quote                      -> Skip-Comment
+ Get-Char-From-Input
(--) Slash                      -> After-Comment-Slash
+ Get-Char-From-Input
(--) Star                       -> After-Comment-Star
+ Get-Char-From-Input
(--) NewLine                    -> Skip-Comment
+ Get-Char-From-Input
(--) Other                      -> Skip-Comment
+ Get-Char-From-Input
(--) End-Of-File                ->
+ Signal-Unterminated-Comment
+ Terminate-The-Program

After-Comment-Star:
(--) Quote                      -> Skip-Comment
+ Get-Char-From-Input
(--) Slash                      -> Outside-Comment
+ Other-If-Nested-Comment
+ Get-Char-From-Input
(--) Star                       -> After-Comment-Star
+ Get-Char-From-Input
(--) NewLine                    -> Skip-Comment
+ Get-Char-From-Input
(--) Other                      -> Skip-Comment
+ Get-Char-From-Input
(--) End-Of-File                ->
+ Signal-Unterminated-Comment
+ Terminate-The-Program

After-Comment-Slash:
(--) Quote                      -> Skip-Comment
+ Get-Char-From-Input
(--) Slash                      -> Skip-Comment
+ Get-Char-From-Input
(--) Star                       -> Skip-Comment
+ Start-New-Comment
+ Get-Char-From-Input
(--) NewLine                    -> Skip-Comment
+ Get-Char-From-Input
(--) Other                      -> Skip-Comment
+ Get-Char-From-Input
(--) End-Of-File                ->
+ Signal-Unterminated-Comment
+ Terminate-The-Program

Defaults:
(--) Exception                  ->
+ Terminate-The-Program
(--) End-Of-File                ->
+ Terminate-The-Program


This is what Libero generates when I compile the dialog:

/*===========================================================================*
*                                                                           *
*  stripper.c  description...                                               *
*                                                                           *
*  Written:    96/12/31    Pieter Hintjens                   *
*  Revised:    96/12/31                                                     *
*                                                                           *
*  Skeleton generated by LIBERO 2.20 on 31 Dec, 1996, 14:00.                *
*===========================================================================*/

#include "prelude.h"                    /*  Universal include file           */
#include "stripper.d"                   /*  Include dialog data              */

/*- Function prototypes -----------------------------------------------------*/

/*- Global variables used in this source file only --------------------------*/

static int
feedback;                           /*  Feedback for calling program     */

static char
*some_param;                        /*  Example of parameter             */

/********************************   M A I N   ********************************/

int my_funct (char *p_some_param)
{
feedback = 0;                       /*  No errors so far                 */
some_param = p_some_param;          /*  Local copy of parameters         */

}

/*************************   INITIALISE THE PROGRAM   ************************/

MODULE initialise_the_program (void)
{
the_next_event = ok_event;
}

/***************************   GET EXTERNAL EVENT   **************************/

MODULE get_external_event (void)
{
}

/*************************   TERMINATE THE PROGRAM    ************************/

MODULE terminate_the_program (void)
{
the_next_event = terminate_event;
}

/**************************   GET CHAR FROM INPUT   **************************/

MODULE get_char_from_input (void)
{
}

/***************************   PUT CHAR TO OUTPUT   **************************/

MODULE put_char_to_output (void)
{
}

/**************************   PUT SLASH TO OUTPUT   **************************/

MODULE put_slash_to_output (void)
{
}

/***************************   START NEW COMMENT   ***************************/

MODULE start_new_comment (void)
{
}

/**********************   SIGNAL UNTERMINATED COMMENT   **********************/

MODULE signal_unterminated_comment (void)
{
}

/************************   OTHER IF NESTED COMMENT   ************************/

MODULE other_if_nested_comment (void)
{
}


This is the completed comment-stripper program:

/*=========================================================================*
*                                                                         *
*  stripper.c  C/C++ comment stripper                                     *
*                                                                         *
*  Written:    96/12/31    Pieter Hintjens                   *
*  Revised:    96/12/31                                                   *
*                                                                         *
*=========================================================================*/

#include "prelude.h"                    /*  Standard include file          */
#include "stripper.d"                   /*  Include dialog data            */

/*- Global variables used in this source file only ------------------------*/

static int
thisch,                             /*  Current character              */
comment_level,                      /*  Comment nesting level          */
feedback;                           /*  Feedback for calling program   */

int main (void)
{
feedback = 0;                       /*  No errors so far               */
}

/************************   INITIALISE THE PROGRAM   ***********************/

MODULE initialise_the_program (void)
{
the_next_event = ok_event;
}

/*************************   GET CHAR FROM INPUT   *************************/

MODULE get_char_from_input (void)
{
thisch = getchar ();
switch (thisch)
{
case '"':   the_next_event = quote_event;         break;
case '/':   the_next_event = slash_event;         break;
case '*':   the_next_event = star_event;          break;
case '\n':  the_next_event = newline_event;       break;
case EOF:   the_next_event = end_of_file_event;   break;
default:    the_next_event = other_event;
}
}

/**************************   PUT CHAR TO OUTPUT   *************************/

MODULE put_char_to_output (void)
{
putchar (thisch);
}

/*************************   PUT SLASH TO OUTPUT   *************************/

MODULE put_slash_to_output (void)
{
putchar ('/');
}

/**************************   START NEW COMMENT   **************************/

MODULE start_new_comment (void)
{
comment_level++;                    /*  We have a new comment          */
}

/***********************   OTHER IF NESTED COMMENT   ***********************/

MODULE other_if_nested_comment (void)
{
comment_level--;                    /*  We've finished a comment       */
if (comment_level > 0)              /*    but maybe it was nested...   */
raise_exception (other_event);
}

/*********************   SIGNAL UNTERMINATED COMMENT   *********************/

MODULE signal_unterminated_comment (void)
{
fprintf (stderr, "stripper: unterminated comment\n");
feedback = 1;                       /*  Return code for main program   */
}

/***************************   GET EXTERNAL EVENT   ************************/

MODULE get_external_event (void)
{
}

/************************   TERMINATE THE PROGRAM   ************************/

MODULE terminate_the_program (void)
{
the_next_event = terminate_event;
}


The program works and is easily understood. However, because the dialog is really too low-level, it makes the problem seem more complex than it actually is. In fact, this is a good example of the kind of problem that I would never use Libero for. I would write this as a straight C program.

Moral: Libero is not worth the overhead on trivial problems. By contrast, when you are wrestling with a really complex problem, Libero lets you break it down into smaller, simpler pieces, and keep the central logic in a single place.

Second moral: simplicity can be deceptive. It often happens that the first stab at a problem produces a simple solution, as here. However, when the solution meets reality, reality often gets its own way and imposes all kind of nasty complexities that mess-up the clean solution. A dialog is very good at handling such things. So, it can be better to start-out on the right footing and use a dialog from the start.

## Example of Parsing An Arithmetic Expression

The next example is for an arithmetic expression evaluator that was originally proposed by Leif Svalgaard. The program works out the value of a string like '2-SQR(2)*1.414'. The program translates easily into most of the languages I've worked with so far, from assembler to Basic to C and COBOL.

Here I describe the program lrcalc.c, which is a subroutine that I use in Libero. You can find the source for this program along with the other sources for Libero in the lrsrc.zip archive. There are also versions of this program in various languages in the examples archive.

Lrcalc chops the expression into tokens, each representing an operand (numbers) or operator. The operators are classified as:

• term operator: '+' and '-'
• factor operator: '*' and '/'
• left-parenthesis: '('
• right-parenthesis: ')'
• end-mark: indicates the end of the expression.

There are various ways of parsing an expression like this; lrcalc combines two basic techniques: push-down stacks for the operands and operators, and states to indicate how tokens are handled. Each state accepts specific tokens and rejects others. For instance, at the start of the expression, an operator like '*' is not valid. When a state accepts an operand or operator, it adds it to the appropriate stack. When a state stacks an operator, it evaluates any previous operators that have the same, or higher priority. '*' and '/' have a higher priority than '+' and '-'.

Operators like '(' and ')' are placeholders that group parts of the expression together with a higher priority. Operators like '*', '/', '+', and '-' are binary operators that take two values off the operand stack, do their work, and place the result back on the stack.

The program basically takes tokens one by one, stacks and evaluates them according to the priority rules, until it reaches the end of the expression. To make this clean, the program places a special end-mark token at the end of the expression when it starts. When it reaches the end-mark, it evaluates any remaining operators, which leaves the result of the expression sitting on the stack.

After-Init:
(--) Ok                         -> Expecting-Initial
+ Get-Next-Token
(--) Error                      ->
+ Terminate-The-Program


The two states Expecting-Initial and Expecting-Operand are similar, except that the first allows End-Mark while the latter does not. I.e. we accept an empty expression (End-Mark in Expecting-Initial), but don't accept an expression that ends when we expect an operand:

Expecting-Initial:
(--) Term-Op                    ->
+ Allow-Signed-Number
+ Signal-Invalid-Token
+ Terminate-The-Program
(--) Number                     -> Expecting-Operator
+ Stack-The-Number
+ Get-Next-Token
(--) Left-Par                   -> Expecting-Operand
+ Stack-The-Operator
+ Get-Next-Token
(--) End-Mark                   ->
+ Terminate-The-Program


These two states handle signed numbers (e.g.. -12, +100) by accepting Term-Op tokens (ie. '+' or '-') so long as these are stuck to a following number. The module Allow-Signed-Number gets the next token, and if this is a number, it kicks the dialog into accepting a number in the same state. It does this using an exception event called Number:

Expecting-Operand:
(--) Term-Op                    ->
+ Allow-Signed-Number
+ Signal-Invalid-Token
+ Terminate-The-Program
(--) Number                     -> Expecting-Operator
+ Stack-The-Number
+ Get-Next-Token
(--) Left-Par                   -> Expecting-Operand
+ Stack-The-Operator
+ Get-Next-Token


After stacking an operand, the dialog expects an operator:

Expecting-Operator:
(--) Term-Op                    -> Expecting-Operand
+ Unstack-Ge-Operators
+ Stack-The-Operator
+ Get-Next-Token
(--) Factor-Op                  -> Expecting-Operand
+ Unstack-Ge-Operators
+ Stack-The-Operator
+ Get-Next-Token
(--) End-Mark                   ->
+ Unstack-All-Operators
+ Unstack-If-End-Mark
+ Terminate-The-Program
(--) Right-Par                  -> Expecting-Operator
+ Unstack-All-Operators
+ Unstack-If-Left-Par
+ Get-Next-Token


The Defaults state lists all tokens. If a state does not explicitly accept some token, the Defaults state handles it: it issues an error message and terminates the program:

Defaults:
(--) Number                     ->
+ Signal-Invalid-Token
+ Terminate-The-Program
(--) Term-Op                    ->
+ Signal-Invalid-Token
+ Terminate-The-Program
(--) Factor-Op                  ->
+ Signal-Invalid-Token
+ Terminate-The-Program
(--) End-Mark                   ->
+ Signal-Token-Missing
+ Terminate-The-Program
(--) Left-Par                   ->
+ Signal-Invalid-Token
+ Terminate-The-Program
(--) Right-Par                  ->
+ Signal-Invalid-Token
+ Terminate-The-Program
(--) Exception                  ->
+ Terminate-The-Program


The expression parsing technique shown here is easy to elaborate to support variables, functions, other operators, strings, etc. It is true that some languages have easier ways of evaluating expressions, but this technique is easily applied to languages like assembler and COBOL that do not have support from tools like lex and yacc.

## Dialogs For Dummies

### Explain It Like I'm 5 Years Old

You've gone through all the examples and still you can't quite get your mind around the concepts? Don't worry, this is normal. It's like anything new -- it takes a while for your brain to change shape.

So far, we have seen fairly realistic examples - depending on how much booze was consumed for lunch, admittedly. Sometimes it helps to look at the entire issue from other angles. So, here is a small piece extracted from our Liberzine Liberetto I/3. Our correspondent, Big Al, writes...

iMatix asked me to write a small piece on how I learnt to use Libero. After a short discussion about fees and expenses, I quickly learnt to use Libero, applying the principle 'imagine I am a child of 5'. "No, don't try to interest me in plastic bunnies. Explain Libero to me." "Right, kids, get into two groups. You lot sit there, and you lot sit there. Now, we'll play a game..."

The first group of kids sit around a large playing board, marked with a large 'START' square, and paths that twist around. Some end in the Snake Pit, others die horrible deaths in the Bathtime Room. Just one path leads through all the dangers to the Cookie Room. The aim of the game is to get to the Cookie Room, of course. Let's look at the START square. You are in a small room. Twisty passages lead left, right, north, and south. You are holding a candle. There is a small breeze coming from the west. The floor is blue. Blue? Yeah.

The second group of kids have three or four funny dice. Each die is a different colour, with size sides. The blue die says 'Left', 'Right', 'Forwards', 'Backwards', 'Roll Again', and 'Bathtime'. The red die says 'Fight', 'Run', 'Get Eaten', 'Eat it', 'Order a taxi', and 'Bathtime'. (You gotta know how to *really* scare a five-year old.)

The rules are simple. The first group of kids just shout a colour. The second group of kids roll the matching die and - after puzzling over the letters a while - shout out what the die says. Each time the die rolls, the first group move their piece along the board to a new square.

You start to see the balance of the game. Neither group of children controls the game, it moves step-by-step until the piece wins or loses. The two groups only communicate by exchanging colours and results. Well, this is Libero. Colours are actions, results are events. And that was the easiest lunch I ever earned.

### A Bit Of Animation

Sometimes it's hard to mentally connect the dialog to the running program. On the one hand you have the dry, pedantic dialog with its events and states; on the other hand you have a real live program that is actually doing something. A good way to join these two concepts is the -animate option. Use this when you generate code, and you get a display of the dialog steps a program runs through.

The examples archive has a set of programs that work out the value of arithmetic expressions. This is what one of these looks like as it works out the value of '1 + 1':

After-Init:
(--) Ok-Event                       We just started
+ Get-Next-Token                so we collect the next token

Expecting-Operand:
(--) Number-Event                   We got the first '1'
+ Stack-The-Number            Stick it on the operand stack
+ Get-Next-Token                and get the next token

Expecting-Operator:
(--) Term-Op-Event                  We got the '+'
+ Unstack-Ge-Operators        Do any higher-priority operators
+ Stack-The-Operator            then stick the '+' on the stack
+ Get-Next-Token                and get the next token

Expecting-Operand:
(--) Number-Event                   We got the second '1'
+ Stack-The-Number            Stick it on the operand stack
+ Get-Next-Token                and get the next token

Expecting-Operator:
(--) End-Mark-Event                 We hit the end of the expression
+ Unstack-All-Operators       Do all operators on the stack
+ Unstack-If-End-Mark         The operand stack holds the result
2                                       This is displayed by the program


Why is the documentation in HTML format?

We originally wrote the documentation using Microsoft Word 5.0 for MS-DOS, a fine word-processor that satisfied our minimalist preferences for a long time. Our target was PostScript on paper. Since those old days, we have changed our minds a little about the best way of moving information from our brains to yours. We like the idea of keeping the help 'on-line' in the literal sense. Effectively, this means using HTML. Now, our approach is two-fold. Firstly, we use HTML as the basic language. It's excellent for the job. Secondly, we have structured the documentation to suit. This means that the Libero documentation is less of a traditional paper book, and more of a hyper-linked text database. Our guiding principle is that you can take the entire HTML kit and put it on a local hard disk, then browse, print, and copy that. There are HTML browsers available for all of our target platforms, and the market is competitive and generally free.

Why do you not supply binary kits for UNIX (any longer)?

We used to do this... It just does not work very well, and takes a lot of work. It is generally not a good idea to use an executable built under one version of an operating system on an other. Sometimes, but not always, you can go from an earlier version of Brandix to a later version, but never contrarywise. With some exceptions (see next question) it's a snap to install Libero by compiling the source kit. At iMatix we tend to do this for all our products today.

I get compile errors on Sun Sparc

For some bizarre reason, possibly related to the struggle of commercialism over utility, this system does not come with an ANSI C compiler as standard. People often install GNU C, which works well, but there can be conflicts between the Sun header files and GNU runtime library. In theory we fixed these in version 2.11 (by changing prelude.h). If you still get compile errors, send us a listing and we will see what is wrong. On some Sparc systems, the ANSI C compiler is called acc.

I get compile and link errors and then lr dies

If you get compile errors (not warnings), or link errors, don't try to run the resulting executable. Or if you do, don't expect reasonable behaviour, unless one can describe a dump as 'reasonable'.

Does Libero support language X?

If it is not on the list, the answer is 'not yet'. There are several possible reasons why we have not yet built a schema for a particular languages:

• We hate the language and would not touch it with a 10 ft pole.
• We don't have a compiler or interpreter for that language;
• We don't consider the language to be worth it;
• We don't see a good way to implement dialogs in the language;
• We have not had time yet.

So far no language has fallen into the first category, while many have fallen into the last.

How do I add support for language X?

If it is a language that we can easily get hold of, we'll add support more or less on demand. For instance, if anyone actually comes and asks for an Ada implementation, we could install GNU Ada and get an Ada schema up and running in a few days. The bizarre thing is we don't even ask for money for the whole business. Whatever.

If the language is one that we cannot get freely -- and we're not in the habit of buying expensive compilers just for fun -- then we can try to build a schema 'by remote control'. In this scenario, you provide us with the specifications of the language, sample programs, etc. We'll do the schema and send you programs to test. Eventually we get a solid version. This can work surprisingly well.

Finally, you can bite the bullet (more like a 10mm shell) and learn how Libero's schema language works. You can then write the schema yourself. Honestly, if you need to write a schema from scratch, this is probably the hard way.

I want to customise a schema

Firstly, be sure that you can't get what you need by adding code to the template file. Then, drop us a line to tell us what you want so we can try to figure-out a way of supporting it in the standard schemas. As a last resort, copy the schema and change it. If your changes are cosmetic, we'd try to convince you to accept our cosmetics. Some projects have such unusual needs that a new schema is the only practical answer. Be warned, however: if you make your own version of a schema, you lose the benefits of any future improvements or fixes to the schema.

I don't understand Libero

This is quite normal. Anyone who understands Libero the first time is either lying, has a serious mental problem, or is not a real programmer. The concept is simple, and easy to explain to a lay-person. But if you have spent long enough coercing computers to do your bidding, you get a certain world-view of how things work. Libero does things differently. Keep trying, and after a week or so you will suddenly see it.

How do I print-out the documentation?

Probably the best approach is to download the big file that contains the entire documentation kit. Load this into your favourite browser and press 'Print'. Alternatively you can try to convert the HTML file into a word processor format, load it up and print it like that. This is a field that is changing rapidly, for the better.

Why is there no UNIX Makefile?

There are several reasons for this: Libero is a portable product, and makefiles are specific to UNIX; we don't really like makefiles; we prefer to try a more 'intelligent' approach when possible; we don't have the time to make and maintain makefiles for all the possible UNIX platforms. Ok, none of these reasons is enough by itself, but you get the idea. Libero is simple to build and install, so the lack of a makefile is not a serious handicap.

Libero for Windows crashes

There was a bug in previous releases of Libero for Windows. It was caused by a missing Windows font. Install the latest version.

Why can't I get the files by ftp?

Our site does not provide anonymous ftp for reasons of speed and of security. If you do not have http access, send us an e-mail telling us what system you work with, or which files you want, and we'll send them by e-mail back to you.

Is there a mirror site?

Not to our knowledge, though we have seen a few older versions of Libero floating around.

How do I build a mirror site?

Our website does not (currently) support anonymous ftp, nor other protocols for automatic mirror-site replication. If you want to set-up a mirror site for Libero, contact us and we'll work-out the principles.

I want to repackage Libero for a CD-ROM

You do not need our permission, but you must respect our license terms. If your CD-ROM is for a specific platform, e.g. Linux, we suggest that you include the binary kit for that platform, the source kit, and the HTML kit. If you have space on the CD-ROM, supply the HTML tree uncompressed: then people can follow the links through their Internet connection to pick-up other files such as examples.

If your CD-ROM is a source collection, you may want to provide the Windows version of Libero, the source kit, and the HTML kit.

Why is Liberetto so silly?

Everything is relative. When the first issue of Libermanga comes out, you'll see what we mean. Seriously, though, we try to maintain a harmonious balance between education, entertainment, humour, and subtle irony. When that fails, we just go for silly.

Is there a 'pretty printer' for my dialogs?

Yes, since version 2.20 - use the -pretty option.

How is a Libero FSM different from a 'classic' FSM?

• Libero lets you specify several actions in a transition; a classic FSM usually only lets you specify one action.
• Some FSMs allow an 'entry action', a 'transition action', and an 'exit action'. These are all equivalent, as far as we can see, and since Libero allows multiple actions, you can do the same in Libero.
• You describe your FSM as a textual diagram, not as a numbered graph, as is usually the case.
• Libero stores the FSM in a table and interprets this. This is one of several approaches; some classic FSMs use goto's; others use a large switch statement. The table + interpreter approach is a reasonable compromise between space and speed and simplicity.
• Libero has the concept of an exception event.
• Libero has the concepts of super-states and default states.

I want my FSM to handle an event queue in realtime

Use our SMT kernel; this is our solution for event-handling in realtime programs, multi-FSM constructions, communications servers, internet servers, etc.

Are Libero FSMs good for writing parsers?

Well, in principle yes, but in practice there are better tools. For instance, Perl is great for writing ad-hoc parsers. If you are working with formal grammars, there are tools that will generate parsers for such grammars. It is usually pointless to re-invent the wheel by hand. There are exceptions. For instance, sometimes the grammar cannot be formalised enough to work with a parser generator. Sometimes a generated parser is too slow. In these cases, a Libero FSM may produce excellent results.

I downloaded a Libero .zip file to my UNIX, then ftp'd it to my PC - now PKUNZIP won't extract it

When you ftp a binary file like a .zip file, always set ftp to binary mode (use the command 'bin'). Sometimes ftp is configured like this by default, but more often it assumes you want to transfer text files. So, it expands tabs, converts line-ending, and such, generally turning a .zip file into garbage along the way.

Is there a Libero mailing list?

No, not yet. Each month we send out Liberetto, our fanzine. We include the most interesting mail we get, news about changes to Libero, forthcoming releases, etc. This is the closest thing to a discussion group so far. We intend one day to create a usenet news group. A public mailing list may be one step on the way. We welcome any suggestions.

To use Libero for Windows you need to download TWO .zip files; lrmswin.zip and lrmswins.zip. The second contains 'system files' including various .vbx files. To download an update you only take the first .zip file. This saves download time.

# Installing Libero

## Getting Your Hands On Libero

### Availability and Distribution

Libero is available as a ready-to-run binary kit for various platforms. You can also download the source kit and re-compile Libero for a specific platform.

Libero is free software, distributed according to the terms of the GNU General Public License (GPL). This means that you are free to change and re-distribute Libero, even charge for it, but you must always make the source code available under the same terms.

The Windows version of Libero is also free software, but does not fall under the GNU GPL. You can't get the sources, mostly because it would be embarrassing.

If you have trouble accessing the iMatix site, or getting your hands on Libero for any reason, send an e-mail to us at imatix.com and we will provide you with Libero by e-mail, or on diskette. We have used the portable Zip and GNU gzip+tar formats for all archives.

### Documentation

The Libero User's Guide consists of a set of HTML files that you can browse on-line or download and browse locally. You can download:

### Deinstalling Libero

We made Libero as simple as possible to deinstall. It does not change anything in system files, and keeps its own files in the single directory where you installed it. To de-install Libero, delete this directory.

## What You Will Need

To install Libero, you'll need:

1. Either the binary kit for your system, or the source kit which is also available as a GNU gzipped file.
2. Infozip unzip to extract the archives. If you do not have Infozip unzip for your UNIX or VAX system, you can unzip the files under MS-DOS or Windows and upload them individually. Use PKzip version 2.04g or later or a compatible unzip program.
3. Alternatively, for UNIX source installation, GNU gunzip.
4. For UNIX or VMS installations from the source kit, an ANSI C compiler to compile Libero.
5. A compiler or interpreter for the language that you want to work in. You don't necessarily need this on the machine where you want to run Libero; it's possible to work on one system, then transfer the results to another for compiling and running.
6. The Libero documentation kit.

## Installing Libero for UNIX

To install Libero for a UNIX system you must compile it. Download one of:

lrsrc230.tgz    144126 97/07/14 14:20:26  gzip/tar archive
lrsrc230.zip    192945 97/07/14 14:20:08  zip archive
lrsrc231.tgz    151603 98/11/27 10:30:10  gzip/tar archive
lrsrc231.zip    207869 98/11/27 10:29:10  zip archive
lrsrc232.tgz    148536 99/05/13 14:46:40  gzip/tar archive
lrsrc232.zip    201295 99/05/13 14:46:16  zip archive


You'll need an ANSI C compiler. The source kit comes with build scripts for UNIX and VMS. To unpack the sources in portable Zip format, load lrsrc232.zip into a working directory and extract the contents using unzip:

    $mkdir temp$ mv lrsrc232.zip temp
$cd temp$ unzip -a lrsrc
$chmod +x install  To unpack the sources in GNU gzip format, load lrsrc232.tgz into a working directory and extract the contents using gunzip and tar: $ mkdir temp
$mv lrsrc232.tgz temp$ cd temp
$gunzip lrsrc$ tar -xvf lrsrc232.tar


The install script is smart enough to detect the UNIX system and use appropriate compile switches. It proposes various options: start with (b)uild to compile and (maybe) install Libero. You can run Libero as soon as it is built; you don't need to install it into an install directory. Run install like this (you may need root access if you want to install Libero into a system directory):

    $su root$ chmod +x install
$./install  The install script uses the Korn shell, which is not available on all UNIX systems. You can use install.sh instead, which uses the Bourne shell. On some UNIX systems the Bourne shell does not support functions; then this script will not run either. If you have trouble running install or install.sh, try something like this (it's what the scripts basically try to do): $ cc <switches> lr.c lr????.c -o lr
$cp lr lrmesg.txt lrschema.* installdir$ rm *.o


The <switches> are the compiler switches that do an ANSI C compile. Type man cc for details.

Once you have built the lr file, and installed it and its data files, you should check that the install directory is on the PATH definition. By default the install script proposes /usr/local/bin as an install directory.

If you do not like placing non-executable files into a /bin directory, you can place lr there, and the remaining files (lrmesg.txt, lrschema.*) into another directory. This second directory may be on the PATH or be defined in another, arbitrary environment variable; for example, LRPATH. You must then tell Libero which variable you are using with a command-line switch like: -path=LRPATH. You can also specify the path literally: -path=/usr/local/tools/bin. Place such global options in the lr.ini file.

Note that on UNIX, all filenames are lower-case. If you do not manage to install Libero on a non-MS-DOS system, you can always run Libero on a PC, then upload the generated code. You can also run Libero for Windows or MS-DOS, accessing your UNIX disk via a network. This works pretty well for many people.

System-specific notes:

• Linux, gcc: compile switches: -pedantic -O2 -Wall -c.
• HP/UX: compile switches: -Aa.
• SunOS, SunC: compile switches: -O -vc -Xa.
• SunOS, gcc: see Linux.
• AIX 3.2.5: default compiler switches. LIBPATH must be set to/usr/lib when you link lr, or you'll get an error at runtime. This appears to be normal compiler behaviour.

## Installing the MS Windows Kit

Libero for Windows is an interactive editor that provides a drool-proof interface to the standard Libero code generation functions. It runs under Windows 3.1, 95, NT, and other common varieties. The Libero for Windows kit comes in two parts:

1. lrmswin.zip - Libero for Windows editor. Contains the software and associated files.
2. lrmswins.zip - Libero for Windows system runtime. You only need to download this once. Contains 'system' files.

Install as follows:

• Create a directory like C:\LRWIN and go there, or go to your existing Libero directory.
• Unzip lrmswin.zip. This extracts a series of files, including the main executable - LRWIN.EXE. Note that the standard Libero files - e.g.. schema files - are supplied in both versions of Libero (portable and Windows), so if you have a more recent version of portable Libero, or have customised schemas, take care when you install Libero for Windows.
• You must download and unzip lrmswins.zip at least once. This holds a number of .VBX and .DLL files. You can leave these where they are, or move them to your Windows system directory. If you leave them alone, it's easier to de-install Libero afterwards.
• Open the file manager and drag the LRWIN.EXE program into a program group or onto your speedbar if you use such.
• Double-click on the Libero icon and load in your current dialog file. The installation directory does not need to be on your path, so long as the 'Path' option in the Options screen points to this directory.

Libero for Windows is compatible with standard portable Libero; they share the same files and general ideas about the world. However, Libero for Windows is not distributed under the same terms: it is free software, but you may not modify it, and you cannot get hold of the sources.

## Installing the MS-DOS Kit

Create a new directory and extract lrmsdos.zip there. This creates the set of files that you need to run Libero under MS-DOS. You must either add this new directory to your path, or copy certain Libero files into a directory (like C:\UTILS) which is already on your path:

• LR.EXE - the executable Libero program
• LRMESG.TXT - the message file
• LRSCHEMA.* - the code-generation schemas
• LR.INI - default initialisation file

The LR.EXE file is not too large, and runs in a modest amount of memory. If you are writing really enormous dialogs, you may want to rebuild LR.EXE from the source kit using a DOS extender. I haven't tried this, and I have not heard of anyone running out of memory with Libero under MS-DOS. The 32-bit OS/2 version should run under MS-DOS. You may also want to try the 32-bit MS-DOS version if you run Windows NT or 95.

## Installing the 32-bit MS-DOS Kit

Download Libero for MS-DOS. This was compiled with Microsoft VC 4.0 as a 'console application'.

Create a new directory and extract lrdos32.zip there. This creates the set of files that you need to run Libero under Windows 95 or Windows NT in a DOS box. You must either add this new directory to your path, or copy certain Libero files into a directory (like C:\UTILS) which is already on your path:

• LR32.EXE - the executable Libero program
• LRMESG.TXT - the message file
• LRSCHEMA.* - the code-generation schemas
• LR.INI - default initialisation file

LR32.EXE supports long filenames and mixed-case filenames.

## Installing the OS/2 Kit

Download Libero for OS/2 [117Kb]. This was compiled with EMX 0.9b, using GCC. Ewen says:

The binary was compiled using EMX 0.9b (I think it should run fine with EMX 0.9c, the latest version), using: gcc -O2 -Zomf -s lr????.c -o lr.exe As usual people will need the EMX runtime libraries to run the executable; they're on most OS/2 ftp sites including hobbes.nmsu.edu, ftp.cdrom.com, and ftp.leo.org. Since quite a few programs need them, most people have them installed already. It should run okay with the EMX 0.9b runtime libraries and the EMX 0.9c runtime libraries (which are the current ones).
Oh, BTW, it'll probably run okay under DOS as well (as a 32-bit version) with either the EMX.EXE extender (included in the runtime DLL package mentioned above), or with RSX (I think it's called) which is a DMPI equivilent for EMX.EXE. Being a 32-bit version it might solve ram-cramp issues if anyone is having problems.

Create a new directory and extract lros2.zip there. This creates the set of files that you need to run Libero under OS/2. You must either add this new directory to your path, or copy certain Libero files into a directory (like C:\UTILS) which is already on your path:

• LR.EXE - the executable Libero program
• LRMESG.TXT - the message file
• LRSCHEMA.* - the code-generation schemas
• LR.INI - default initialisation file

## Installing the VAX/VMS Kit

To install Libero for Digital OpenVMS you can either download the binary kit for VAX/VMS or the source kit, if you have a C compiler (Vax C, Dec C, or GNU gcc). Currently we do not have a binary kit for Alpha OpenVMS - you must build from the source kit. To rebuild from the source kit, download /pub/libero/src/lrsrc232.zip and place it into a working directory. You need the unzip tool to extract it:

    $set def [.temp]$ create/dir []
$... upload LRSRCxxx.ZIP into this directory$ unzip -a -o lrsrc232
$@buildvax.txt  If you do not have Unzip on your VMS system, consider installing it - it is well worth the bother. You can also unzip the sources on a PC and upload them from there. The file BUILDVAX.TXT is a procedure that compiles and links Libero. Run it (@buildvax.txt). Then copy these files into an installation directory which is world-readable: • LR.EXE - the executable Libero program • LRMESG.TXT - the message file • LRSCHEMA.* - the code-generation schemas • LR.INI - default initialisation file To run Libero your LOGIN.COM file must include these lines: $ define/nolog path "directory";  ! name of Libero directory
==> TheNextEvent = $OkEvent;  ## Read-Only Variables You can use these read-only variables anywhere in a schema:$author
The value of the -author option.
$date The current date in a shortened form: 'yy/mm/dd'. Libero always uses this date format for short dates.$time
The current time in a shortened form: 'hh:mm'.
$fulldate The current date in a long form: 'dd Mmm, yyyy'. The month is abbreviated to 3 letters, in English.$fulltime
The current time including seconds: 'hh:mm:ss'.
$out_count The number of lines output so far, including the current line. This starts at 1.$dialog
The name of the dialog file being processed, without extension.
$schema The name of the schema being read. This is the value of the -schema option.$source
The name of the source file for dialog, without an extension. This name is derived as follows: normally, it is the same as the dialog file name. If -plusd was specified, it is formed by adding 'D' to the dialog file name. If -source=xxx was used, this explicitly tells the source name to use.
$style The value of the -style option.$defaults
The number of the defaults state, or zero if none was specified.
$events The number of events in the dialog. This is used to dimension various tables.$maxaction
The maximum action number (largest vector). This is used to dimension various tables.
$modules The number of modules in the dialog. This is used to dimension various tables.$states
The number of states in the dialog. This is used to dimension various tables.
$vectors The number of action vectors in the dialog.$version
The current Libero version number as 4 characters: 'n.nn'; e.g. '2.32'.

Additionally, you can use $comma,$name, $number,$offset, $tally,$row, and $overflow within a :do block. These variables are explained later. You cannot define a variable with the name of a standard Libero variable. ## Schema Commands You can use the following commands in a schema: :output filename Sends output to filename. Any existing data in the file is lost. :extend filename Appends output to filename. Creates the file if it does not already exist. :close Closes an output file opened by a previous :output or :extend. This command is never required, since Libero does an implicit :close before any :output or :extend, and at the end of the schema. If you use :close when no file is open you'll get a warning message. :copy fromfile tofile Copies one file to another. The tofile is silently overwritten if it already exists. :rename fromfile tofile Renames one file to another. The tofile may not already exist. :delete filename Deletes filename without any error if the file does not exist. :include [optional] filename [from [to]] Includes filename in the generated output code. If you specify from, Libero searches the file for a line containing this text by itself, and starts copying from that point on. If you specify to, Libero ends copying when it finds a line containing just this text. You can put quotes around from and to if these contain embedded spaces. If you use the optional keyword, Libero ignores the :include command if it can't find the specified file. The included file can be anywhere on the path value. :echo text Echoes text to the standard output, followed by a newline. If the text is enclosed in quotes, leading spaces are not discarded. :declare [int | string] [$]name [=expression]
Defines a variable for later use in the schema. Except for those that Libero supplies as standard, you have to declare any variables that you want to use. You can specify the variable name with or without a '$' in the name. Variables are either integers, strings, or variants (either type depending on mood). You cannot assign a string expression to an integer. Otherwise Libero is pretty relaxed about how you mix variable types. The expression provides a default initial value for the variable. If you do not provide an initial value, strings are empty and numbers are zero. You can override any initial value using an -option on the command-line or in the dialog. Use: -opt:var="string" or opt:var=expression. The name of a variable cannot be int or string. Variable names are case-independent; :declare$drink and :declare $DRINK refer to the same variable. If you declare an existing variable, you'll get an error message. :set [$]name = expression
Assigns a new value to the variable specified. You can use a '$' before the variable name if you want to. You cannot set a read-only variable. :push [$]variable [=expression]
Creates a new instance of the variable. If you give an expression the new variable gets this value. The variable must then be a read-write variable. You can push a read-only variable but not specify an expression. This may sound pointless, but can be useful: you can set some read-only variables using the :option command, e.g. :option -style=xxxx.
:pop [$]variable Removes the last instance of a variable. Use with :push to change a variable within a block, then reset it to its previous value. :do condition Starts a code block that is output repeatedly depending on condition. Libero defines a standard set of conditions like :do event. You can also define your own conditions using :do while The do block ends with a matching :enddo. :do while logical-condition Repeats the code block while the logical_condition is true. :enddo [condition] Ends the closest previous :do block. If you specify condition, the :do and :enddo conditions must match; this makes your schemas more robust against errors, but is not required. :if logical-condition Starts a code block that is output if logical_condition is true. The :if block ends with a matching :endif. :else Outputs the following block of code if the previous :if condition was false. :endif [all] Ends the closest previous :if block. If you specify all, all open :ifblocks are closed. :exit [level] Aborts code generation. Use levels 0 for okay, 1 for errors. :option -name[=value] Enforces a Libero option For example: :option -style=cobol. :internal module Tells the code-generator that module is required by the generated dialog code, but not by the dialog. A typical case is initialise_the_program. This lets Libero correctly indicate unused modules in the source code. :substr from [size] Specifies a substring for the next$name or $row insertion. From must be from 1 upwards. If size is not specified, the remainder of the variable's value is used. ### Notes • All commands can contain variables anywhere in the line that makes sense. • You can use '#' to start a comment in a command line. Any text following the '#' is ignored. When a '#' is inside a string, or escaped by '\', it is treated as the '#' character, not a comment. You can change the comment character by setting$comment_ind.
• A line containing just ':' by itself is ignored. This lets you separate blocks of commands without outputting extra blank lines in the code. For instance:
  :if $index > 0 : set$counter = $counter + 1 :endif : :echo "\ncounter is$counter\n"

• In a :set or :declare you can give the variable name with or without the $. Your preference will depend on the other languages you use. When I've used Perl a while, I want to stick a '$' everywhere. When I've programmed in a UNIX shell, I want the $only on the right. When I've been working in any other language I can't understand what all the fuss is about, I just want something that works like I expect it to. So, since the meaning is unambiguous, Libero accepts both. • One or more spaces can come between the ':' and the command name. This is often a nice way to indent commands to show structure. ## Syntax Of Expressions The :set and :declare commands assign the value of an expression to a variable. A couple of examples: :declare$count = 0
:set $count =$count + 1


Libero handles the expression depending on the type of the variable:

• If it is an int, it looks for an arithmetic expression.
• If it is a string, it treats the whole expression as a string. Quotes are optional; you should use these if the string contains spaces.
• If the variable is a variant, Libero tries to evaluate the expression as an arithmetic expression. If this works, the variable gets the resulting number as its new value. If this fails, the variable gets the string as its value. To be certain to assign a string, use quotes.

An arithmetic expression can include '+', '-', '*', '/', parentheses. All number are four-byte signed long values, i.e. fall into the range +2,147,483,647 to -2,147,483,648.

Things pretty much work as you would expect, I hope. In practice I always declare variables as int or string and put explicit quotes around strings.

## Syntax Of Logical Conditions

The :if and :do while commands output some code depending on a logical-condition. Some examples:

:if $debug : echo "version is$version"
:endif
:do while $counter > 0 ... :enddo  A logical condition has two possible forms: :if [not] [test] expr1 [logical_operator expr2] :while [not] [test] expr1 [logical_operator expr2] :if [not] [standard_test] :while [not] [standard_test]  ### Logical Operators You can make a condition by comparing two expressions. The 'test' keyword is optional and I normally don't use it. The expressions can be numeric or string; if either is a string, Libero treats both as strings. The logical operator can be one of: These are the logical operators: • = - expr1 is equal to expr2. == is also okay. • <> - expr1 is not equal to expr2. != is also okay. • < - expr1 is less than expr2. • <= - expr1 is less than or equal to expr2. • > - expr1 is greater than expr2. • >= - expr1 is greater than or equal to expr2. ### Standard Tests Libero provides these standard tests: exist filename filename exists in the current directory. animate The dialog uses the -animation option. check The dialog uses the -check option. caps The dialog uses the -style=caps option. cobol The dialog uses the -style=cobol option. headline The dialog uses the -style=headline option. normal The dialog uses the -style=normal option. plain The dialog uses the -style=plain option. title The dialog uses the -style=title option. defaults The dialog defines a Defaults state. state name name is a state in the dialog. event name name is an event in the dialog. module name name is a module in the dialog. ## Control Variables Libero supplies various control variables. A control variable affects some aspects of code generation; you set the control variable to tell Libero how to handle something specific for the language you want to generate. Control variables are usually 'write-only'. You don't normally refer to these variables in the code, though you can if you want to. To change a control variable you use the :set command:$array_base
Specifies the base value for tables. You use this when building an array of values. By default this is 0; in some languages it is more useful to start at 1. The $array_base affects the value of$number in a :do block.
$comma_before This value is used as$comma when the current item being output is not the last. Default is ",".
$comma_last This value is used as$comma when the current item being output is the last. Default is empty.
$comment_ind Comment indicator in schema command lines. Default is '#'.$module_line
libero looks for single lines that look like this when creating new module stubs. By default: "MODULE %s (void)". The '%s' symbol represents the module name.
$number_fmt This value is used to format the$number value; numbers are always signed long ints. It must be a valid printf format string. Default is "%ld".
$row_first This value is used to format the first numeric item in a$row value. It must be a valid printf format string. Default is "%ld".
$row_after This value is used to format numeric items in a$row value after the first one. It must be a valid printf format string. Default is ",%ld".
$row_clean Tells Libero to clean-up overflow rows. You get overflow rows when you generate a table that is wider than the$row_width. When $row_clean is 1, Libero removes any non-numeric data from the start of overflow rows. When 0, Libero leaves overflow rows as they are. Default is 0.$row_null
This value is used in $rows when there is no event defined in a certain state. This is by default 0, but can be set to any numeric value by the schema.$row_width
Maximum space, in characters, for a $row item in the generated source. Default is the compile-time constant LR_ROW_WIDTH, defined as 60. Rows longer than this are chopped into 'overflow' pieces.$run_away
If a :do loop does more than this number of iterations, Libero halts it with an error message. Default is the compile-time constant LR_RUNAWAY_LIMIT, defined as 1000. This lets you generate at least a table with the maximum number ofstates, events, or modules (up to 1000 depending on the schema).
$stub_before Used to generate new module stubs. If this string value is not empty, it is output before each module stub header. By default it is "" (empty).$stub_between
Used to generate new module stubs: this string value is repeated to build-up a stub header. Default is "*".
$stub_first Used to generate new module stubs: this string value is output at the start of a stub header line. Default is "/".$stub_last
Used to generate new module stubs: this string value is output at the end of a stub header line. Default is "/".
$stub_width Used to generate new module stubs: defines the width of the stub line. Default is the compile-time constant LR_HEADER_WIDTH, defined as 79. To see the value that a control variable had for code-generation, use the -trace option when you generate code. The .lst file produced shows the symbol table, including all control variables, as code generation ended. ## Standard :do Conditions Libero defines this set of :do conditions: :do action For each state defined in the dialog; generates the action for each event in each state. :do event For each event defined in the dialog. :do event local For each event defined in the current :do state. :do module For each dialog module defined in the dialog. :do module local For each dialog module defined in the current :do event. :do nextst For each state defined in the dialog; generates the next-state for each event in each state. :do state For each state defined in the dialog. :do stubs [filename]... For each dialog module defined in the dialog, but not yet present in the source file (or filename(s) if specified). :do vector For each action vector required by the dialog. An action vector is the list of modules to execute for an event in a state. Libero collects these and builds a list of unique action vectors. You cannot nest these standard :do loops cannot be nested (i.e. twice the same condition). ## Extra Variables In A :do Loop Within the different types of :do loop, various extra variables are available. Usually you'll need these to construct tables or lists of names. ### :do while condition Repeats the block of code while condition is true. The condition is a logical condition as described above. The loop is executed 0..n times. •$number - loop iteration 0..n-1, added to $array_base. ### :do event Builds a list of events. Outputs the block for each event in the dialog. •$name - event name, with _event stuck onto the end, and formatted using the current -style setting.
• $number - event number 0..n-1, added to$array_base.

### :do state

Builds a list of states. Outputs the block for each state in the dialog.

• $name - state name, formatted using the current -style setting. •$number - state number 0..n-1, added to $array_base. •$comma - the value of $comma_before until the last iteration; then$comma_last.

### :do module

Builds list of modules. Outputs the block for each module in the dialog.

• $name - module name, formatted using the current -style setting. •$number - module number 0..n-1, added to $array_base. •$comma - the value of $comma_before until the last iteration; then$comma_last.

### :do action

Builds table containing one row per state, with one item per row for each event transition. Each item is the number of an action vector. Invalid state/event transitions are filled by the value of $row_null. •$row - value of row, formatted according to $row_first,$row_after, and $row_null. •$number - state number 0..n-1, added to $array_base. •$comma - the value of $comma_before until the last iteration; then$comma_last.
• $offset - offset of start of current row, added to$array_base. The offset is counted up by 1 for each item in a row.
• $tally - number of items in$row.

### :do nextst

Builds table containing one row per state, with one item per row for each event transition. Each item is the number of the next state. Invalid state/event transitions are marked by the value of $row_null. •$row - value of row, formatted according to $row_first,$row_after, and $row_null. •$number - state number 0..n-1, added to $array_base. •$comma - the value of $comma_before until the last iteration; then$comma_last.
• $offset - offset of start of current row, added to$array_base. The offset is counted up by 1 for each item in a row.
• $tally - number of items in$row.

### :do vector

Builds table of action vectors. An action vector is a list of modules executed in series for a state/event transition. Duplicate vectors are collapsed.

• $row - action vector, formatted according to$row_first, $row_after, and$row_null.
• $number - vector number 0..n-1, added to$array_base.
• $comma - the value of$comma_before until the last iteration; then $comma_last. •$offset - offset of start of current row, added to $array_base. The offset is counted up by 1 for each item in a row, including an assumed terminator value (0xFFFF). •$tally - number of items in $row, including one terminator value. ### :do overflow Used to build source code that has to be output over several lines. The block is output until the overflow row is empty. •$row - overflow row, ie. part of main $row that did not fit onto first line. •$number - iteration number 0..n-1, added to $array_base. •$comma - the value of $comma_before until the last iteration; then$comma_last.
• $tally - number of items in$row.

Builds a set of module stubs at the end of the filename. If no filename is specified, assumes you mean the $source file. You can specify a list of filenames; then Libero will scan each file in the list for existing modules, and build stubs for any undefined modules at the end of the first filename. This is useful if you define standard modules in generated code (e.g. an include file). If you use the -nostubs option, Libero ignores the :stubs command. If you use the -noidle option, Libero does not check the source for idle (ie. unused) modules. ## General Rules for Code Generation • A string may be delimited by a single or double quote character. • Inside a string, the sequence '\n' is treated as a newline character. The sequence '\t' is treated as a tab character. Any other character prefixed by '\' is treated as itself. • Libero issues a warning message if a non-blank line has to be discarded because there is no output open. • If you have trouble generating what you want, use the -trace option: this shows each schema line, before and after any variable expansion. ## Generating Module Stubs Take a hypothetical language where a module stub looks like this: //################### SOME FUNCTION ##################// MODULE some_function () { }  To generate stubs like this we add these commands to the schema: :internal initialise_the_program :internal get_external_event :set$module_line  = "MODULE %s () {"
:set $stub_first = "//" :set$stub_between = "#"
:set $stub_last = "//" :do stubs MODULE$name () {

}

:enddo stubs


This is how it works:

• The :internal commands tell Libero to ignore these two modules when it looks through the source file. Without these two commands, Libero would print a message saying that the source file contained modules no longer used in the dialog. We tell it that some modules are used internally by the generated code.
• The $module_line tells Libero what to look for when it is running its idle module check. This must always match the header of the stub below. • Libero looks at each file specified in the :do stubs line. If you do not specify any files, Libero looks just at the source file. • When it builds a new module stub, Libero outputs a line containing$stub_before before each stub. Usually this is a blank line.

## The State Machine Algorithm

This is the dialog manager logic as pseudo-code. If you need to write a schema for a new language, this section should be your main specifications.

The dialog manager consists of a basic loop, plus extra pieces that are generated only if necessary. For example, handling of the Defaults state, error checking, and handling of the dialog stack. For clarity, I've put in the Defaults handling, but nothing else other than the basic loop. The following code is loosely based on C:

    _LR_state = 0                       //  First state is always zero
initialise_the_program ()
while (the_next_event != terminate_event) {
_LR_event  = the_next_event
_LR_savest = _LR_state
_LR_index  = _LR_action [_LR_state][_LR_event]

if (_LR_index == 0) {           //  Try Defaults state
_LR_state = _LR_defaults_state
_LR_index = _LR_action [_LR_state][_LR_event]
}
the_exception_event = _LR_NULL_EVENT
exception_raised    = FALSE

Get first module in list
for ever {
if (we finished the module list OR exception_raised)
break
Execute next module in list
}
if (exception_raised) {
if (the_exception_event != _LR_NULL_EVENT)
_LR_event = the_exception_event
the_next_event = _LR_event
} else
_LR_state = _LR_nextst [_LR_state][_LR_event]

if (_LR_state == _LR_defaults_state)
_LR_state = _LR_savest
if (the_next_event == _LR_NULL_EVENT)
get_external_event ()
}
return (feedback)


## Compiled-In Limitations

A number of more-or-less arbitrary limits are defined in the header file lrpriv.h. If you need to, you can increase these, then recompile all Libero programs (and if you are smart, drop me a line explaining your reasons):

• LR_SYMBOLMAX - the size of Libero's symbol table. This table is used to hold all state, event, and module names. Defined as 32000; maximum value is 32767.
• LR_STATEMAX - the maximum number of states in the dialog. Defined as 1000. Maximum value is 16383, assuming schema can handle it.
• LR_EVENTMAX - the maximum number of different events in the dialog. Defined as 1000. Maximum value is 16383, assuming schema can handle it.
• LR_MODULEMAX - the maximum number of different modules in the dialog. Defined as 1000. Maximum value is 16383, assuming schema can handle it.
• LR_VECTORMAX - the maximum number of different vectors in the dialog. Defined as 1000. LR_VECTORMAX * 2 * $maxaction should not be larger than 32k. • LR_FILENAMEMAX - the maximum size of a filename with extension, but not path. Defined as 128 characters. • LR_HEADER_WIDTH - the default width of a header line. For various archaic reasons I like this to be 79. You can change this in a schema by setting$stub_width.
• LR_RUNAWAY_LIMIT - the number of times you can go through a :do loop before Libero says thinks you are in trouble. Defined as 1000. Maximum value is 32767.
• LR_ROW_WIDTH - the default width of one line of a $row. For various reasons I like this to be 60. You can change this in a schema by setting$row_width.

The header file prelude.h also defines a constant LINE_MAX, with the value 255, which is used to allocate buffers for input and full filenames (ie. including path). You should not write dialogs wider than a normal page (ie. 80 columns).

Libero allocates memory dynamically to store its dialog definition, so the maximum size of a dialog actually depends on available memory. You should only change the constants in lrpriv.h if you get a message that specifically says that you have reached such a limit.

The code that the standard C schema generates assumes that events can be stored in a signed 16-bit word; while state, module, and action numbers can be stored in an unsigned 16-bit word. A more compact version could be made by using 8-bit values.

If you change Libero, or suspect bugs, compile with the symbol DEBUG defined. This switches on the assertions that are sprinkled through the code. The executable will be a little larger, but if a bug corrupts Libero's internal data structures, you will (usually) get a nice message instead of an unpredictable crash.

## Quick Reference - Libero Schema Language

### General

! at start of line - comment to end of line.
: at start of line - command line.
: by itself on line - ignored as comment.
# in command line - comment to end of line.
$xyz - insert value of variable xyz.$xyz\text - insert value of variable xyz followed by text.
$"text" - format text using -style setting.$$- insert single '$'.

$author - value of -author setting.$date - current date: yy/mm/dd.
$time - current time: hh:mm.$fulldate - current date: dd Mmm, yyyy.
$fulltime - current time: hh:mm:ss.$out_count - lines output including current line (1 up).
$dialog - name of current dialog file, no extension.$schema - name of schema being read; value of -schema.
$source - name of source file, no extension; value of -source.$style - value of -style.
$defaults - number of the defaults state, or zero.$events - number of events in dialog.
$maxaction - maximum action number (largest vector).$modules - number of modules in dialog.
$states - number of states in dialog.$vectors - number of action vectors in dialog.
$version - current Libero version: e.g. 2.32. ### Schema Commands :output filename - outputs following schema text to filename. :extend filename - appends following schema text to filename. :close - closes current output file. :copy fromfile tofile - copies one file to another. :rename fromfile tofile - renames one file to another. :delete filename - deletes filename. :include [optional] filename [from [to]] - includes filename in current output code. From and to are strings of text that sit by themselves on a line in filename. :echo text - echoes text. "\n" is a newline. :declare [int | string]$variable [= expression] - defines variable for later use in the schema.
:set $variable = expression - assigns new value to variable. :push [$]variable [= expression] - creates a new instance of variable.
:pop [$]variable - removes last instance of variable. :do condition - starts code block output repeatedly depending on condition. :do while logical-condition - repeats code block while logical-condition is true. :enddo [condition] - ends closest previous :do block. :if logical-condition - outputs code block if logical-condition is true. :else - outputs code block if previous :if was false. :endif [all] - ends closest previous :if block. :exit [level] - aborts code generation: level 0 = okay, 1= error. :option -name[=value] - enforces Libero option. :internal module - module required by generated dialog code, but not by dialog. :substr from [size] - specifies substring for next$name or $row insertion. From starts at 1. If size is not specified, remainder of value is used. ### Expressions :declare int$number = 1
:set $number = ($number + 10) / 2
:set "$string" = "$string\more_text"


Strings: max. 255 characters. Numbers: from -2,147,483,648 to +2,147,483,647.

:if $flag # non-zero number or non-empty string :if not condition # reverse test :if$var1 =  $var2 # If strings or numbers are equal :if$var1 == $var2 # If equal :if$var1 != $var2 # If not equal :if$var1 <> $var2 # If not equal :if$var1 <  $var2 # If less than :if$var1 >  $var2 # If greater than :if$var1 <= $var2 # If less than or equal :if$var1 >= $var2 # If greater than or equal :if exist file # file exists in current directory :if animate # -animate option :if check # -check option :if caps # -style=caps option :if cobol # -style=cobol option :if headline # -style=headline option :if normal # -style=normal option :if plain # -style=plain option :if title # -style=title option :if defaults # dialog defines a Defaults state :if state name # name is state in dialog :if event name # name is event in dialog :if module name # name is module in dialog  ### Control Variables$array_base - base value for tables. Default 0.
$comma_before - used as$comma when current item is not last. Default ",".
$comma_last - used as$comma when current item is last. Default empty.
$comment_ind - comment indicator in schema command lines. Default "#".$module_line - template for module header in source file. Default "MODULE %s (void)".
$number_fmt - used to format the$number value. Must be valid printf format string. Default "%ld".
$row_first - used to format first numeric item in a$row value. Must be a valid printf format string. Default "%ld".
$row_after - used to format numeric items in$row after first. Must be valid printf format string. Default ",%ld".
$row_clean - when 1, Libero removes non-numeric data from start of overflow rows. Default 0.$row_null - used in $row's when there is no event defined in state. Default 0.$row_width - maximum size of $row item in generated source. Default 60. Longer rows are chopped into 'overflow' pieces.$run_away - maximum times through any :do loop. Default 1000.
$stub_before - output before each stub header. Default "" (empty).$stub_first - output at start of stub header line. Default "/".
$stub_between - repeated to build-up stub header line. Default "*".$stub_last - output at end of stub header line. Default "/".
$stub_width - width of stub header line. Default 79. ### Standard :do Conditions :do while condition - repeats while condition is true. Loop is executed 0..n times. Loop variables:$number.

:do event - builds list of events. Outputs block for each event in dialog. Loop variables: $name,$number, $comma. :do state - builds list of states. Outputs block for each state in dialog. Loop variables:$name, $number,$comma.

:do module - builds list of modules. Outputs block for each module in dialog. Loop variables: $name,$number, $comma. :do action - builds table containing one row per state, with one item per row for each event transition. Each item is number of an action vector. Invalid state/event transitions are filled by value of$row_null. Loop variables: $row,$number, $comma,$offset, $tally. :do nextst - builds table containing one row per state, with one item per row for each event transition. Each item is number of next state. Invalid state/event transitions are marked by value of$row_null. Loop variables: $row,$number, $comma,$offset, $tally. :do vector - builds table of action vectors. An action vector is a list of modules executed in series for a state/event transition. Duplicate vectors are collapsed. Loop variables:$row, $number,$comma, $offset,$tally.

:do overflow - used to build source code that has to be output over several lines. Block is output until overflow row is empty. Loop variables: $row,$number, $comma,$tally.

:do stubs [filename]... builds a set of module stubs at end of filename.

Designed by Pieter Hintjens © 1996 iMatix