Extensions


Liana: A Windows Programming Language Based on C and C++

Jack Krupansky


Jack Krupansky has a B.S. and M.S. in Computer Sciences from the Stevens Institute of Technology. He has 16 years of experience developing compilers, software tools, graphics software, CAD, and other graphics applications for DEC, Wang, Cadnetix, and as an independent consultant. As President of Base Technology he develops and markets Liana as well as C and Windows software as a consultant. He can be reached at (303)440-4558 or by e-mail at [70642,2662] on Compuserve, or by mail at 1543 Pine Street, Boulder, Colorado 80302.

Programmers addressing the growing demand for Windows applications are faced with these obstacles: complexity of the Windows API, trial and error nature of user-interface design, weak software specifications, and continual requests for improvements. Liana gives programmers greater intellectual leverage on these problems.

Better programming environments, object-oriented programming languages, class libraries, and visual programming tools have all helped to some extent. Liana is a new programming language whose approach is based on providing a consistently higher level of language and integral higher-level class library.

The syntax of Liana resembles C and C++, but features of C and C++ have been omitted and extensions added to appeal to a broad range of programmers. The dynamic link library (DLL) feature of Windows may be used to interface Liana code to C and C++ code. The goal is to provide both a good, stand alone development tool as well as a good companion tool for programmer's already committed to C and C++. Liana is especially suited to smaller applications or front-ends for larger applications.

This article and the accompanying sidebar describes the origins of Liana, influences on its design, its philosophy, sample programs, a brief summary of the language and class library, development process and tools, integration with C code, and future directions. A complete description of Liana can be found in the Liana Programmer's Reference Manual[1].

Interpreter vs. Compiler

Liana is an interpreted language, but does have a compiler and linker, which generate an .EXE file. The user always gets a running application that looks like most other windows applications, with little of the excess baggage associated with traditional interpreters.

Although an interpreted language does sacrifice performance for programmer productivity, the rationale is that most Windows applications (other than the relatively few that are commercial, shrink-wrapped products) are not very performance sensitive. They spend much of their time waiting for the user, calling Windows, or calling dynamic link libraries for database access and other performance-intensive operations. The advantages for Liana being an interpreter are comprehensive runtime checking and protection; friendlier run-time error messages; fully-virtual, Smalltalk-like member-function dispatching, based on object value rather than compile-time declaration; and more flexible treatment of function arguments and variables to reduce the amount of code and make it less cryptic and easier to modify and extend.

Sample Programs

Of course, every modern language designer is obliged to show the traditional Hello World program. In Liana, this program creates a window with the desired title and makes it visible:

main
{
   (w = new window("Hello World")).show;
}
As in C++, the new operator creates an instance of the window class, passing the string argument to the constructor function for the class. The result is an object reference which resembles a pointer except that it carries type information. By default, the main function assigns the reference to the undeclared global variable w, then calls the show member function to make the window visible. A careful reader may think they've discovered two typo's in this program: there are no parentheses after main or after show. But there are no typo's. Liana allows you to leave off the empty parentheses for function definitions and member-function calls. If this offends your sensibilities, feel free to put them in whenever desired. So, you could write the program as follows:

window w;

void main (void)
{
   w = new window ("Hello World");
   w.show ();
}
One might reasonably expect that the show function handles all Windows events and returns when the window closes. But this is not the case. In fact, show returns immediately after telling Windows to make the window visible. When main returns to the Liana interpreter the event magic happens, hiding the details needed to make Windows work properly.

The Liana library contains a default main function that creates a window and makes it visible, using the value of the global variable apname as its title. So we get the same results with:

apname = "Hello World";
which has no code and only a single global variable definition wit initialization.

Liana will set the initial value of apname to the executable file name if the application has not initialized it to a non-null value. This causes an empty Liana program to display a window titled with the program name. Although the program name is not often an appropriate window title, this example does illustrate how well the default behavior of Liana works.

The following program will draw a small circle in the center of a window, regardless of how the window is resized by the user:

paint {w.center; w.circle (10);}
This program takes advantage of the default main function creating a window and storing a reference to it in the global variable w. When the window class receives a paint event from Windows it automatically calls the application paint function to draw the contents of the window. The center function sets the current position to be at the center of the window. The circle function with one argument draws a circle of the given radius at the current position. (A variant of circle takes x and y coordinates in addition to the radius.)

Note that Liana's version of hello.world omits the #include statements. Liana does not need and does not directly support #include files. Unlike most compilers, Liana shifts name resolution to the linker. For example, the compiler does not care how you define the window class, even if you derive a new class with member functions that reference members of the window class. This results in a radical reduction of the amount of work that the programmer must perform to keep the compiler happy.

The design philosophy of the Liana class library eliminates most of the need for #define. For example, many of the bits and fields of the Windows API are controlled by simple, high level member functions such as:

w.background = "r"; // Red background
w.caption = true; // Enable title bar
Listing 1 shows how to create a window and draw some graphics. C programmers should be familiar with the use of the main function to initialize the application. It also illustrates the use of callback functions to handle events.

The simple drawing editor in Listing 2 shows how an application gets control of mouse events (the startdrag, drag, and enddrag functions). Liana uses a display list to store data that will automatically be drawn whenever the window needs to be updated. See [4] for a complete description.

Listing 3 shows the complete source code to bring up a window with a menu, dialog box, and some text. A dialog is defined by creating a class derived from the dialog class. All of the magic necessary to make dialog boxes work is inherited from the dialog class. See [2,3] for a complete description.

The Language

Liana supports most of the C expression operators and statements, such as ++, +=, ?:, &&, if, while, for, switch, etc. Liana doesn't support casting, but automatic conversion, flexible member-function dispatching, and the C++ conversion functions seem to be sufficient for a language oriented to higher-level functions rather than low-level bit manipulation. Also, expressions in a switch statement are not restricted to integers, the most useful case being strings for a keyword dispatch. The rules for tokens are very similar to C, including comments, identifiers, operators, numbers, and keywords.

A major departure from C is that variables are merely containers which are capable of containing any type of value. In fact, the type of value in a given variable may vary during the execution of the program. You may omit the type when declaring a variable, or you may specify C-like types such as int, short, long, unsigned, double, char *, or the name of a class. The any type is provided for cases where the type may vary.

String, boolean, and null types are predefined in Liana to simplify setting up an application. There is no need for header files that define TRUE, FALSE, NULL, or conventions for strings. The null value has its own type which differentiates it from zero, although its value is zero in numeric expressions. The boolean values true and false are different from 0 and 1, but equivalent in numeric expressions.

Variables do not need to be declared. Undeclared variables have global scope. Although this can allow bugs to slip through, it simplifies prototyping, keeps the source code simple, and eliminates another need for header files. Programmers may still provide declarations for global variables for documentation purposes.

Local variables may be declared anywhere in a function, including expressions, by merely specifying a type before the variable name. For example:

a = b * (int x = y - z);
Local variables have the scope of the entire function. This may seem strange, but for prototyping and high-level programming it is especially convenient when the first reference is within a loop or compound statement.

All variables are initialized by default to the null value. This guarantees consistency and eliminates superfluous code to initialize local variables.

The syntax of functions is very similar to C. The major difference is that the types of the return value and parameters need not be declared. But the programmer may certainly specify types, primarily for their documentation value. Every function has a return value, with null being the default return value. It is possible for a function to return values of different types on different calls.

Class definitions are similar to C++, but they support only single inheritance and all member functions are virtual and are defined within the class definition. You don't need both a declaration (in a header file) and a definition since class and member references are not resolved until link-time or even runtime.

One interesting extension is that public member variables can be implemented either by using the public keyword for direct access or by supplying a pair of access functions for controlled access. The application references the member variable the same in either case, using structure member access notation. For example, to access the menu of a window the programmer codes

w.menu = new menu;
m = w.menu;
This is the same as coding:

w.menu_set (new menu);
m = w.menu ();
This allows the programmer to view the class as a simple struct having many attributes which are directly accessed rather than calling access functions built for a specific purpose. This results in a simpler, cleaner API.

Liana automates memory management using a reference counting scheme. This means that objects and strings allocated within functions will automatically be freed when the function returns unless the values are stored in other than local variables. The free function can be used to break cyclic data structures.

The Class Library

Table 1 summarizes the classes in the Liana library, most of which are designed to provide a high-level interface to the GUI features of Windows. High-level classes are provided for windows, menus, dialogs, controls, icons, cursors, bitmaps, mouse events, keyboard input, text and graphics output, fonts, colors, patterns, printing, sounds, message boxes, the clipboard, and dynamic data exchange. Other classes support management of data such as the dynamic array, keyword lookup table, graphics display list, file, and directory.

Rather than merely abstracting the existing Windows API, Liana classes try to hide much of the complexity of Windows by consistently providing a higher level of interface. The best example is the dialog class. A programmer defines a dialog by defining a new class derived from the dialog class as shown in Listing 3. The constructor function adds a sequence of controls to the dialog using the << operator. The dialog class will automatically position each control immediately after the preceding control. The programmer may at any time change the direction of flow to be towards the right by calling east or downward by calling south. A control may also be positioned below or to the right of another control. The size of the dialog will default to be the minimal size enclosing the controls. One of the benefits is that the layout will automatically adjust as the programmer adds or deletes controls anywhere in the dialog. There are other conveniences such as group boxes which automatically adjust their size to accommodate the enclosed sequence of controls. Controls automatically fetch their data from associated dialog member variables when the dialog is made visible, and store into those variables when the user accepts the dialog. Even better, the controls default the names of the variables based on the text of the control. The net result is less work for the programmer.

The use of the << operator in many classes is a good example of operator overloading and polymorphism. The << operator places an object at the end of another object:

a << "abc";  // Append to array
w << "Hello"; // Output text to window
w << new pushbutton ("Go"); // Add control to window
d << new ok_button; // Add control to dialog
There is a wide variety of global functions that should appeal to C programmers. The printf, sprintf, malloc, free, math, and a subset of the string functions are available, although there are differences.

Development Process and Tools

Liana supports a very simple, traditional development process. Source programs are text files with the extension .L and are prepared using any text editor. They are compiled and linked using the LCL command in DOS or a DOS window to produce an .EXE file.

A Liana application requires a runtime DLL which consumes about 180KB. The .EXE file for a typical application will consume under 200KB. This is very modest considering that Windows machines are typically equipped with several megabytes of RAM.

Liana eliminates much of the need for make files by eliminating the #include dependencies and automatically comparing dates of source files and their intermediate files.

A 420-page programmer's reference[1] manual includes a tutorial with over 60 fully-documented sample programs. The entire manual, including graphics, is also provided on-line in Windows Help format. The programmer can browse through the sample programs and class library. The source code for sample programs is included with the software.

Liana also provides utilities to build libraries, dump executables, generate class hierarchy maps, capture screens, convert icons and bitmaps, compare and manipulate bitmaps, and support foreign language localization.

Integration with C Code

Although applications can be implemented completely in Liana, there are many situations where you may want to integrate existing C code. You accomplish this with Liana's ability to call DLLs. Many databases, communications libraries, and networking packages provide programming interfaces via DLLs. In fact, the API of Windows is a set of DLLs. The DLL feature of Windows is a very good method for programmers to share code.

The Liana programmer declares a DLL entry point using the usual function syntax, but with an entry-point specification string rather than a function body. For example, the GetTickCount function of Windows which is defined in the USER DLL is declared as:

unsigned long GetTickCount ()
 "user: GetTickCount"
DLL entry point declarations are the only place in Liana where the type specifications of return values and function parameters are more than just documentation.

Another method of accessing C code is to use Dynamic Data Exchange (DDE) to communicate with other applications. DDE is a protocol for passing data between applications. Besides peek, poke, and remote command execution, applications can request notification of changes to named data items.

Future Directions

The basic syntax and semantics of Liana will probably remain fairly stable for the next few years. Most innovation will occur in the class library. As Windows adds more features, Liana will track them with new classes and functions.

Although equipped with a very simple compile/link process, a Windows-hosted integrated development environment (IDE) would make Liana more useable. Liana does not currently have a debugger, primarily because it is such a high-level language that bugs are much easier to track down. The addition of a debugger would make Liana more attractive to a wider audience of C programmers. The IDE and debugger will be written in Liana, allowing C programmers to easily customize and extend them. A current prototype of the IDE is only 150 lines of code! The debugger will run as a separate Windows task and communicate with a stub in the application using DDE.

Market pressure may force Liana to be outfitted with an interface to a browser and visual layout tools. But they won't provide as much benefit as they do with other languages and libraries.

Liana was designed to be portable and will probably be ported to one or more of the other popular GUI environments. Existing Liana applications will then be portable to those environments with very few, if any changes.

Objections to the interpreted nature of Liana would be somewhat resolved by a translator which generates raw C or C++ source code. Unfortunately, features such as the Smalltalk-like pure virtual member-function dispatch would require additional runtime tables and interpretation, so the performance gain would be less than expected. Liana classes would not be totally compatible with C+ + classes.

The compiler might be extended to support a compatibility option that would selectively enforce a subset of Liana which is 100% compatible with C and C+ +. Changes and additions would increase the compatibility between Liana and C, but not force the programmer to give up the advantages of Liana where they are really needed. This has a lot of appeal and would improve performance.

The original prototype of Liana included a tightly integrated object-oriented database management system (OODBMS) which allowed the application to create objects in the database and then access them as if they were in RAM. This feature was shelved due to the pressures of getting Liana to market, the need for additional transaction and concurrency control features, and concern that the market was not yet ready for an OODBMS. Such a feature will probably be in great demand within a few years and will then be a very natural extension to Liana.

Summary

The Liana language provides C and C++ programmers with an alternative that allows them to balance the cost of learning a new language with the productivity of that new language. The similarity to C and ability to easily integrate with C code makes Liana a good companion tool for C and C++ programmers, allowing them to leverage their existing intellectual investment. The use of DLLs to interface to C code allows the developer to balance the flexibility of an interpreter with the performance of a compiler.

Liana also shows the versatility of the C language syntax. There is still enough mileage left in general-purpose programming languages such as Liana to give them an advantage over specialized user interface languages. Programmers do not need to switch to complex visual environments or languages like Smalltalk or BASIC to reap the benefits of OOP and simplified GUI development.

Suggested Reading

[1] Liana Programmer's Reference Manual, Base Technology.

[2] Liana in a Nutshell, Base Technology.

[3] Anatomy of a Liana Application, Base Technology.

[4] Simple Drawing Editor in Liana, Base Technology.