Back Home

Coding Basics

This page describes the basic syntax and meaning of an ELVIS source file. For a formal description of the syntax, including an EBNF grammar, see this document (PDF letter; LaTeX source).

This is what a typical ELVIS source file might look like:

/*** Example ELVIS source file ***
 *********************************/
 
// That was a multiline comment, delimited by /* and */
// Single-line comments begin with //

// Top-level options

projectName: Example
packageId: com.example.example

// Controller declaration
%controller {
    Action btnClicked;                  // An event handler
}

// Widget declarations
Window win "Example" {                  // The main window
    resizable: true;                    // An attribute

    ColumnLayout {                      // A layout widget
        Label "Please click a button:";

        /* You can declare more than one widget at once, and give them all
         * attributes in one fell swoop: */
        Button btn1 "Button 1", btn2 "Button 2" {
            // See %controller block at top of file
            onClick: btnClicked;        // Pick an event handler
        }

        /* You can accumulate attributes and child widgets onto widgets already
         * declared: */
        btn1 {
            height: 20;
        }

        ComboBox combo {
            /* An attribute can have multiple values, separated by commas.
             * Attribute values are optionally quoted; quotes are necessary in
             * some cases, however, such as when the value contains a comma. */
            list: 42, Gettysburg, Denny Crane, "Hi, Mom!";
        }
    }
}

Comments

/* This comment spans 
   multiple lines */
      
Window win /* This comment is in the middle of a line */ {
    // This comment lasts for one line
    Button "Button"; // As does this one
} 

Comments are as in C, C++, or Java. Single-line comments begin with “//” and extend to the end of the line. Multi-line comments begin with “/*” and extend to the next “*/”.

Options

projectName: Project;
mockup: true;

Options are like attributes that apply to the entire file. They have the same syntax as attributes, but aren't contained in any widget.

Controller

%controller {
    /* These might be triggered by menu items in the File menu or by buttons in
     * the toolbar */
    Action new, open, save;
  
    /* This is triggered when the user interacts with the combo box */
    Action getNewValue;
}

If the widgets on the screen are to do anything, there needs to be a controller to handle each event, such as a button click, and call the appropriate back-end code. The controller is a set of event handlers, each of which needs to be defined here so that they can be specified by the widgets and then implemented in the controller.

The controller block begins with the special string “%controller” and is enclosed in braces.

Event Handlers

// See %controller block

Window win "Example" {
    Menu "File" {
        MenuItem "New ..." { onClick: new; }
        MenuItem "Open ..." { onClick: open; }
        MenuItem "Save ..." { onClick: save; }
    }

    Button "New" { onClick: new; }
    Button "Open" { onClick: open; }
    Button "Save" { onClick: save; }

    ComboBox combo {
       list: Crane, Poole, Schmidt;
       onClick: getNewValue;
    }
}

An event handler is a function or method that is called when the user “does something,” such as clicking a button, choosing a menu item, or pressing a key. The event handler code must be written elsewhere in the target language, and it must conform to the interface defined by the event handler. The event handlers defined in the source file are collectively known as the controller.

Connections between widgets and event handlers are made by certain attributes, which conventionally begin with “on”, such as onClick.

Widget Declarations

MenuItem quitCmd "Quit" {
    mnemonic: N;
    onClick: quit;
}

Widget Type

The type of the widget. This must be one of the widget types listed in the widget library.

This is the only part of the widget declaration that is always required. Most widget types are only useful if more is given; however, some, such as MenuSeparator, may reasonably be declared with nothing but the type.

Identifier

This is the name of the widget, as it will appear in the generated code. An identifier must start with a letter or underscore, and may thereafter contain digits as well. (These are much the same rules as for C, C++, or Java, except that the $ symbol is not allowed.)

The identifier is optional. A widget without an identifier is called an anonymous widget, and it incurs these restrictions:

Notwithstanding these restrictions, leaving widgets anonymous can make code clearer and easier to read, as superfluous identifiers are distracting and unhelpful. Some widgets, such as Labels, are usually anonymous.

Label String

The label string is simply shorthand for the label attribute. The following pieces of code are equivalent:

Button "The Button";
Button {
    label: "The Button";
}

The precise meaning of the label attribute differs by widget type, but generally it is, in some sense, the label. For instance:

Body

The body of the declaration, much like a block of code in C, C++, or Java, is delimited by curly braces (“{” and “}”). If there are no attributes or child widgets being declared, the empty braces may be replaced by a single semicolon:

Label "Push the button, Frank." {
    /* Empty declaration body */
}
Label "Push the button, Frank.";

Attributes

Widget {
    attribute: value;
    otherAttribute: "value";
}

As is suggested by the term, an attribute is some property of a particular widget --- perhaps its width, its color, its displayed text, or the event handler triggered when it is clicked.

An attribute statement consists of the name of the attribute, followed by a colon, a comma-separated list of values, and a terminating semicolon. Most attributes only permit one value.

things: 1, 2, 42, "Say \"Cheese!\"", "I like semicolons; they are useful",
    "This string is really long, and we want to break it across lines in \
    the ELVIS code, but we don't want it to actually span multiple lines \
    the interface, so we use a backslash at the end of each line.",
    "This string spans
    multiple lines.";

An attribute value may be surrounded by quotation marks. This is optional unless one of the following is true:

Within quotes, the following escape sequences are understood:

All other escape sequences (that is, a backslash followed by any other character) will be left alone, to be interpreted by whatever language the ELVIS source is compiled to.

Child Widgets

Just as many widgets contain others on the computer screen, widget declarations contain others in the body, between the delimiting curly braces (“{” and “}”). Generally a widget literally contains its child widgets: A window may contain a menu bar, which contains menus, which contain menu items.

Multiple Declarations

To avoid the heaps of duplicated code typically involved in GUI coding, ELVIS allows declarations for several widgets to be given at once. All of the following pieces of code are equivalent:

Button b1 "1", b2 "2", b3 "3" {
    onClick: digit;
}
Button b1 "1" {
    onClick: digit;
}
Button b2 "2" {
    onClick: digit;
}
Button b3 "3" {
    onClick: digit;
}
Button b1 {
    label: "1";
    onClick: digit;
}
Button b2 {
    label: "2";
    onClick: digit;
}
Button b3 {
    label: "3";
    onClick: digit;
}

Note that all of the widgets declared at once have the same attributes, except that the label attribute may be set differently using label strings.

The body of a multiple-widget declaration may only contain attributes, not child widgets, since a child widget can only have one parent.

Accumulation

Once a widget has been declared, it can still be augmented with more attributes and child widgets. The following pieces of code are equivalent:

GridLayout {
    TextField firstName, middleInitial, lastName {
        row: 1;
    }
    firstName, lastName {
        spanColumns: 2;
        characters: 20;
    }
    middleInitial {
        characters: 1;
    }
    /* ... */   
}
GridLayout {
    TextField firstName {
        row: 1;
        spanColumns: 2;    
        characters: 20;
    }
    TextField lastName {
        row: 1;
        spanColumns: 2;
        characters: 20;
    }
    TextField middleInitial {
        row: 1;
        characters: 1;
    }
    /* ... */
}

In this way, several widgets whose attributes are similar, but not identical, can be “factored” so that any identical attributes can be given in one place. This makes the code quicker to write and to read, and also makes its simpler to change the code later on - in the above example, for instance, if one needs to move the three text fields to the second row, one need only change the number in one place.

Another use of accumulation is for organization. It may be clearer, say, to group all the event handler attributes in one section, having declared the other attributes earlier.

To accumulate onto an existing widget, simply use its identifier alone in a new declaration without a widget type. Any attributes and child widgets declared in the body will then be added to the widget as if they had appeared when the widget was first declared.

You can accumulate onto multiple widgets by separating their identifiers with commas (much like for multiple declarations). However, only attributes may be accumulated this way (see the discussion under multiple declarations).

An accumulation must name a widget which has already been declared in the same declaration body. (In fact, this is the only way the parser knows the difference between an accumulation and an anonymous widget declaration — if the identifier doesn't name an existing widget, it is assumed to be a widget type.)