Programming Made Simple
This article introduces a new high-level computer language called Ubercode. It describes the
reasons for another language, it shows the innovative features, it shows a real working program,
and it discusses the problems involved in language design. Ubercode was launched in June 2005, and
exists as a real product and runs under all versions of Microsoft Windows from 1995 to the
present.
1. Reason for a new language
The key reason is simplicity. Beginner and intermediate level programmers should be able to
write simple programs with the same ease that was available by using QuickBasic and earlier
versions of Visual Basic. Many developers lack the time or inclination to master complex
environments such as Visual Studio or the ".NET" environment.
It is unfortunate that ease of use has recently been lost by the move towards managed
environments such as Java, C# and VB.NET. There is clear evidence for this in the reluctance of
Visual Basic programmers to move to VB.NET [see http://clasicvb.org/petition/] - these programmers feel their
only choice is to move to the more complex environment of C#.
Therefore Ubercode's purpose is "ease of use". Paradoxically "ease of use" is not that easy to
define since it covers more than matters of syntax, or questions such as where the curly brackets
go. As a subjective definition, I consider any language claiming "ease of use" should meet the
following requirements:
- Single point installation. The product should install from a single disk (CD or DVD),
or from a single URL. Installation should not require extra libraries or platforms that must be
downloaded and installed separately, and should not require certain service packs or Internet
Explorer to be pre-installed. Installation should ask the minimum number of questions, should avoid
reboots, and the days of prompting users to choose whether to install some or all of the files in a
50MB package are long gone.
- Type and run. Early developer environments had the feature where you could type in a
simple program, and press the Run key (usually F5) to save, compile and run the program. Even
though it seems trivial, this feature is essential to beginner programmers. It means they can get
started before having to understand the structure of their chosen programming language "in the
large", and before having to understand the build process. But in most cases this feature is now
lost. It is now necessary to create projects, workspaces or solutions before being able to compile
and run a simple program. Ease of use requires that when the Developer environment starts, it
should be possible to create a blank window, type in a program of a few lines, and click Run to
make the program work.
- Documentation. Full online help with examples should be available. If the Help key is
pressed on any keyword, library function, or in any dialog, a help screen should instantly (within
one second) be shown. The help screen should contain more than one sentence, and in the case of
language elements, it should include an example program that actually works. It is my experience
that no computer language (apart from Ubercode) meets this requirement.
- Easy to distribute programs. Programs are generally written in order to run them on
other computers, so ease of use requires it be easy to group the files required by a working
program. The developer environment should include features to copy the required files to a disk, or
at least to identify the required files and their locations. The distributed program should also be
able to work on different types of hardware and with different versions of Windows. In practice,
this means the language runtime must work on all versions of Windows (or all versions of Windows
from a given date) regardless of subsequent service packs or Internet Explorer versions.
- Native look and feel and portability. The developer environment and the programs it
compiles should have the same look and feel as the version of Windows on which it runs. To do
otherwise is to have a program that looks dated, or looks strange to its users. It is worth noting
that when Microsoft want to make a programming style obsolete, they do so by making programs
produced with that version look dated under newer version of Windows. For example Windows 3.1
applications had a dated look under Windows 95, and more recently Visual Basic v6 applications do
not use the new style appearance under Windows XP. Ease of use requires that applications have the
correct look and feel for the version of Windows under which they run.
- Everything is "in the box". To support ease of use, when you buy a programming package,
you should have everything you need to develop and distribute programs. Development should not be
held up because of missing libraries, incomplete documentation, dependencies on external software,
or versions of Windows or service packs or versions of Internet Explorer. The product should
include introductory manuals, reference manuals, installation keys and support contacts.
- Language design. Although this is important, I have put it last since it is the only
part of "ease of use" which is determined by language syntax. Modern languages should include
features such as fully automatic memory handling, simple string types, higher level collection
types such as lists and tables, and automatic error handling. There is some evidence that
self-bracketing keywords (if ... else ... end if) are clearer than begin ... end blocks in Delphi
or curly bracketed blocks in C#. Also tools such as compilers and linkers should be automated as
far as possible. Beginner programmers should never need to know about make files, linker options,
which libraries to include, and it should be impossible for the compiler or linker to use
mis-matched library files or to encounter unresolved references.
This list has shown the main requirements for "ease of use". It is interesting that earlier
languages such as QuickBasic and Visual Basic v3 met most of these requirements, whereas modern
managed languages are failing particularly in terms of complexity, and in terms of ease of
deployment under other versions of Windows. Ubercode is a modern language designed to meet all the
'ease of use' requirements. Ubercode is a deliberate move in the direction of accessible languages
such as Basic and Pascal. This makes Ubercode useful for beginner programmers, and gives them
flexibility to write more useful programs as their skills grow.
2. Parallels with existing languages
I now want to discuss features Ubercode has in common with other mainstream computer languages.
In this section I discuss languages and their Developer environments together, since the two are
usually supplied in the same package. Instead of presenting an exhaustive "laundry list" of
features, this section takes a high level view of features that should be in any modern language.
This forms the basis for the discussion of new features in section 3.
- Applications can be built from multiple files. Languages should support blocks of code
stored in separate files, and each file usually contains a single class (C#, Ubercode) or a unit
(Delphi). Files and classes are the foundation of code re-use, and make it possible to work on
larger real-world projects. It is interesting that lack of support for multiple files was one
reason why Pascal was not widely adopted for commercial development.
- Languages should include a tool chain. Modern languages include their compiler, linker,
and interpreter if required; build utilities and their run time library. The developer environment
should include features for managing the tool chain, and should include extra elements such as
dialog (form) editors and debuggers. Again all modern languages do this.
- Compiled code offers reasonable performance. Some languages (Delphi, C++, and Ubercode)
can be compiled directly to object code, whereas others (C#, Java, Visual Basic) are compiled to an
intermediate format which is then interpreted. Although the interpreted format is slower, the
difference is not significant for most applications. However an interpreted language should make it
possible to call code in external DLLs, or should offer extra library routines (typically for array
handling, mathematics and graphics), to cover situations where performance is important.
- Common data types and functions. Programming tasks use string and numeric types and
many common functions that operate on these types. Although there is not space to give a full list
here, languages as different as Java and Basic and Ubercode provide functions with similar
purposes. For example functions to search through strings, to extract parts of strings, to convert
strings to upper or lower case, and common mathematical functions are available in all modern
languages.
- Arrays and structured types. All languages should support arrays (ideally resizable),
structures (records) and list or collection types. Languages such as Visual Basic and Ubercode
allow types to be nested, for example an array can exist where each element is a list of
structures. In all cases the language should automatically support the memory requirements of these
types, by allocating and destroying (or garbage collecting) memory at run time. Some languages
handle the creation of nested types using pointers and multiple references; others do so using
value semantics (earlier versions of Visual Basic). Although space does not permit a detailed
argument, value semantics are better since they avoid aliasing.
- Graphical user interface. Most programs written for Windows require a user interface,
so languages must allow the creation of menus and dialog boxes with user controls. Inside a program
the UI is handled using properties, methods and events which trigger the running of code. Languages
differ somewhat in terms of how they define the interface (i.e. what controls are in a dialog). C#
and Java use statements at run time, Delphi and Visual Basic use proprietary FRM or DFM files, and
C++ and Ubercode use RC files (standard Windows resource compiler files).
- File handling. Programs need to work with files and folders, so languages should
include functions to create, destroy, read and write from folders and different types of file.
Functions that can read and write common types of file (such as text files, binary files, CSV
files, database files, XML files and bitmap files) are very useful.
- Printing support. Modern languages should make it easy to copy bitmaps and text in
different colors and fonts to any attached printer. In Visual Basic and Ubercode the same graphics
functions that draw images and text can be used for the printer. Strangely enough this is an area
where some modern languages fall short - it is almost as if the developers forgot to add printing
support, making it necessary to print using the Windows API.
This concludes the common features in modern languages that are improvements on earlier 3GLs
such as C and Pascal. For readers that are interested, I have compiled a more detailed list at
compare/index.html.
3. Key new features
I will now look at features that are new to Ubercode, and at features that are implemented in an
improved way.
- Program verification. This feature allows functions to validate their inputs when they
are called, and to check their outputs when they exit. This is an important technique for improving
software reliability and for making the purpose of functions much clearer [see International
Developer April 2005, "Who's afraid of the Big Bad Wolf", p46]. Program verification is also known
as Design by Contract, and the only languages currently supporting it are Eiffel and Ubercode.
Program verification is implemented using preconditions and postconditions. These are Boolean
expressions using the function's parameters which must test True before the function is called, and
which must test True using the function's return values. Here is an example of a postcondition:
function RemoveSpaces(in str1:string[*] out result:string[*])
postcond Instr(" ", result) = 0
...
end function
The RemoveSpaces function is intended to return a copy of its input string with all spaces
removed. The postcondition tests the returned string has no spaces, and serves two purposes:
firstly it makes the behavior of RemoveSpaces clear to the casual reader, and secondly it acts as a
runtime check on the internal logic of RemoveSpaces (the internal code is not shown). The Ubercode
developer environment has an options dialog to specify the degree of precondition and postcondition
checking which takes place at run time:

The checks are specified in the top left corner of the dialog. In general, code under
development should check preconditions and postconditions, whereas release quality code should
check preconditions only.
- Program Security. Ubercode is a secure language, resistant to program crashes and
exploits such as buffer overflows and type misuse. Buffer overflows and type abuse are prevented
because the run time system knows the size, type and address of all data so it can validate all
reads and writes to ensure they are within the buffer size. Ubercode does not store variable sized
data on the stack - this prevents exploits that corrupt function return addresses by overwriting
the local variable stack allocation. Ubercode also has two levels of error handling. A class level
error handler gets first look at all errors within the class, and then a system wide error handler
prints a default message. Error handlers can be customized, and for most errors programmers can
choose whether the program terminates or continues running. This means that error boxes such as the
following:

are a thing of the past for Ubercode programmers.
- High level collection types. Ubercode supports fixed size and resizable arrays, lists
and tables. Arrays may be multi-dimensional, and all these types may have components of any type,
including other structured types. For example lists of arrays or arrays of tables, or any other
combination are possible. Memory is managed automatically without garbage collection. Arrays, lists
and tables may all be iterated using a "for each" loop. Lists are similar to collections in other
languages, and Tables are similar to dictionaries or hash tables.
- Improved memory techniques. All variable-size Ubercode data types are dynamically
allocated, reallocated and freed when required. For example the following code:
MyString <- MyString + "a"
appends a single character to a string, and (other than the initial string declaration) no extra
lines of code are required to manage the memory used by the string. Compare this to C# and Java,
where the above lines of code create a new string object containing the new string text, leaving
the previous string text to be freed when garbage collection takes place. Ubercode avoids garbage
collection by handling all memory automatically. Avoiding garbage collection is important, since it
makes an application's memory requirements more predictable, and it avoids random pauses which can
negatively impact on near real-time applications.
- Simple class structure. To help beginner programmers, the class structure of Ubercode
has been made deliberately simple by storing each class in a separate file. Classes begin and end
with the "class" and "end class" keywords, and classes are inherited by the "uses" keyword.
Functions and values in a class that are intended to be used outside the class use the "public"
keyword. Although this is a simple model, it is well suited for beginner programmers, and it makes
possible a "drag and drop" style of programming where new classes can be added from a pick list.
The Developer Environment also includes tools to view all identifiers and objects in all classes,
and to quickly view all function headers within a class.
- Tools for running under different versions of Windows. After writing and testing an
Ubercode program, you can use the Developer Environment to help distribute the program. There are
menu commands to show the names and paths of all files in the program (including library files),
and commands to copy all these files into a zip file or into a self-installing exe file. This is
the dialog used to create the zip file:

After a program is packaged, it is likely to be installed on a computer that is different from
the one it was developed on. Therefore the Ubercode run time libraries and exe files must be
independent of the ".NET" framework, MFC libraries, or Windows DLLs. To achieve this Ubercode uses
the Windows API for all functions and does not rely on particular elements of Windows that may not
exist. This has the further advantages of making Ubercode exe files more reliable, and of ensuring
Ubercode programs have the correct look and feel for the current version of Windows. For example an
Ubercode program that looks like this under Windows 2000:

looks like this under Windows XP:

Note the same program has the correct Windows look and feel in both situations. This is fully
automatic, and the Ubercode programmer does not have to use any special code to ensure this takes
place. This option is not available in other languages - managed languages such as C# and VB.NET do
not run under versions prior to Windows 2000, and languages such as Visual Basic v6 do not have the
XP style.
4. Introductory program
When introducing beginner's languages such as Basic, it is traditional to show the source code
for a simple game such as the Moon Lander program. It is now over 30 years since man first landed
on the moon, and there is not enough space here for a 100 line program, so here is the Mars
exploration program instead. This program move a sprite (mars explorer) round the screen area:

Here is the source code:
// Sprite.cls
Ubercode 1 Class Sprite
private const
SPRITEX : integer <- 32
SPRITEY : integer <- 32
SPRITEPIC : string[*] <- "sprite.ico"
public callback function main(in EventId:integer
ControlObj:control
Key:integer
out Cancel:boolean)
var
x:integer(0:MAXINT) // Top left corner of sprite
y:integer(0:MAXINT) // Top left corner of sprite
code
select EventId
case LOAD_EVENT =>
call Startgraph(me)
call SetBackcolour(me, DARK_RED)
call Drawshape(me, 0, 0, 0, 0, GetPagewidth(me), GetPageheight(me))
call Drawpicture(me, SPRITEPIC)
call SetCurrentX(me, 0)
call SetCurrentY(me, 0)
case KEYPRESS_EVENT =>
x <- GetCurrentX(me)
y <- GetCurrentY(me)
call Drawshape(me, 0, 0, x, y, x+SPRITEX, y+SPRITEY)
select Key
case KUP =>
if y > 0 then
y <- y - SPRITEY
else
call Sound("uhoh.wav")
end if
case KDOWN =>
if y < GetPageheight(me) - SPRITEY then
y <- y + SPRITEY
else
call Sound("uhoh.wav")
end if
case KLEFT =>
if x > 0 then
x <- x - SPRITEX
else
call Sound("uhoh.wav")
end if
case KRIGHT =>
if x < GetPagewidth(me) - SPRITEX then
x <- x + SPRITEX
else
call Sound("uhoh.wav")
end if
end select
call SetCurrentX(me, x)
call SetCurrentY(me, y)
call Drawpicture(me, SPRITEPIC, x, y, x+32-1, y+32-1,
DRAW_TOPLEFT+DRAW_PIXELS+DRAW_SAVE_CURRENTXY, 0)
end select
end function
end class
This works as follows:
The constants at the top of the program define the size of the sprite and its filename. In this
case the sprite is a Windows icon file. The main function handles all the events in the program,
because it is declared "callback". Callback functions are those passed events by the operating
system.
The code under the Load_event sets the screen into graphics mode and draws the sprite in its
initial position (top left corner). The Keypress_event detects the up, down, left and right keys
and moves the sprite by one unit (32 pixels) in the specified direction. Movement occurs by erasing
the sprite at its current position, calculating the new position, and redrawing the sprite there.
If the keys would move the sprite off the screen, a warning sound is played and the sprite is
redrawn in its previous position. The program can be ended by clicking the close box.
To run the program, go to downloads, download and install the
Ubercode Trial Pack (no personal details are required and the download is free), start the
Developer Environment, use the File - Open Examples menu command and choose the Sprite example. If
you want to run the actual Mars Lander program, choose the Lander example. If you are a
traditionalist who wants to run the Moon Lander program, you can change the constants at the top of
the program and re-run it.
5. Difficulties encountered
This section discusses problems found when designing Ubercode. To quote from Tremblay and
Sorenson, "Nothing seems so easy as the design of a new language, yet nothing is so complex". This
section may therefore be of interest to others embarking on the perilous course of language
design.
- What to leave out. One of the biggest problems is deciding what should be included and
what should be left out. When a new feature is required, the immediate temptation is to add the
feature in the most immediate and straight forward way. This is invariably a mistake - a well
designed computer language has few redundant parts, so the addition of a new feature can have far
reaching effects on the language, compiler and run time library. In most cases the feature is not
really new, but is a more generalized application of an existing part of the language. In such
cases the new feature is not added at all, but the existing part is improved to embrace the new
functionality. There is a real danger of any new language degrading into an uncoordinated mess, and
the danger is only avoided by keeping a clear vision of the language structure.
- Language syntax. All modern languages are defined with deterministic grammars. This
means that when following the syntax rules to parse a valid program in the language, the current
symbol in the program unambiguously specifies the parser's path through the syntax diagram. This is
equivalent to a LALR grammar or LL(k) grammar for recursive descent parsers. Although this is the
current state of compiler technology, this is restrictive for language designers as some work is
necessary to convert a grammar into LALR or LL(k) form. It would help language designers if
compilers could handle ambiguous grammars. But it is possible the savings are not that significant,
since in Ubercode the design of the syntax and the language took much less time than the design and
testing of the run time library.
- Graphical User Interface. The Ubercode UI is fairly straightforward as it is based on
the Windows interface in its present (post 1995) form, although there is a large amount of internal
detail. To simplify the design, the UI objects, properties, methods and events were put into a
separate layer and kept separate from the core run time library. UI objects relate to graphical
controls (checkboxes, pushbuttons etc) and to higher level objects such as dialog boxes and
printers, properties relate to control attributes that can be read and written (get and set),
methods relate to routines that alter the state of objects (Show, Hide, Load, Unload), and events
relate to actions such as menu choices, button clicks, scrollbar moves and similar. Events are most
easily implemented by callbacks which is similar to the Java, C# and C++ method of implementation.
This division of the Ubercode run time into UI and non-UI parts makes it possible to port Ubercode
programs by re-writing the core functionality separately from the program interface.
- Choice of name. Although not a technical decision, it was very difficult to choose a
suitable name! Ubercode was originally called Lingo and was hosted at lingolangage.com. But Lingo
had two potential conflicts: the first was an inactive UK based holder of the Lingo trademark, and
the second was a Macromedia product using the name Lingo, even though Macromedia had not registered
this name as a trademark. In general a name should be (1) memorable, (2) it should be able to be
registered, (3) it should be available as a domain name, and (4) it should not currently be in use
as the name of any kind of significant product. Lingo failed on tests 3 and 4, so the decision was
made to change the name.
6. Future improvements
Ubercode is a new language, having just been released at v1.0 in mid 2005. One important
characteristic of future releases is that they shall not break existing code. In the event this is
unavoidable the new version will have importers that convert from v1 format into v2 format. To
assist this all Ubercode classes have the following structure:
Ubercode 1 Class Myclass
end class
The "1" in the class heading defines the version, so if the same class is opened by a (yet
unreleased) v2 Developer Environment, any required conversions will be automatic. Because Ubercode
is based on a secure and well-tested foundation, most improvements will be to increase the number
of platforms Ubercode runs on, rather than modifications to the language.
- Linux port. Linux includes Windows API compatibility libraries (the wine project) which
are currently under development at v0.9 release - these are probably a year away from being
commercial quality. In principle these libraries make it possible to port Windows applications to
run natively under Linux. Furthermore since the latest versions of the Mac OS are based on Unix,
and Apple are moving to use the Intel x86 platform, these libraries will also permit Windows
applications to run on the latest Apple Macs. Therefore Ubercode has the medium term goal of using
these libraries for a Linux port.
- DLL interface. At present the Ubercode interface to DLLs is via the run time library
itself. Although DLLs are complex, there may be advantage to allowing programmers direct interface
to DLLs from Ubercode classes. However this improvement is only of benefit to more advanced
programmers who are comfortable calling DLLs and debugging those calls.
- Event handler simplification. At present Ubercode event handlers for a window are
implemented as a select statement inside an event function. This system was chosen because each
window then has a single event function of the same name. However it may be better to move to a
more fine grained approach, where the event handlers for a dialog are also qualified with the name
of the event (such as Form1_Load in Visual Basic). This may make programming easier for beginners.
If this is done there may also be advantage to choosing common event names based on JavaScript and
C#, instead of the older style Visual Basic names.
Conclusion
Beginner and intermediate programmers can use Ubercode to create applications that carry out
real-world tasks in a reliable and portable manner. Ubercode has extensive support for existing
data types and file types. The integer, string, currency and floating point types are compatible
with Visual Basic and C/C++/Delphi. The CSV file, text files, binary files, dBase files and XML
files are compatible with Ubercode data types. These files can be created from Ubercode programs,
or can be read into programs. Ubercode is feature-complete without needless complexity, and
Ubercode is powerful enough to write more useful programs as programmers develop in skill.
About the author

I have been working as a software developer for about 20 years, and have programming expertise
is in C, Delphi, Basic, VBA and of course Ubercode! Software development consists of much more than
programming, so my skills also include software testing, documentation, help files and manuals,
licensing, shrink wrap and packaging, marketing, and website development.
|