Walnut/Ordinary Programming/Quasi-Literals and Quasi-Parsers

From Erights

Revision as of 10:58, 9 June 2010 by ThomasLeonard (Talk)
Jump to: navigation, search


Quasi-Literals and Quasi-Parsers

E supports quasi-parsers. A quasi-parser allows one to compress large numbers of operations into a succinct notation (a quasi-literal) in a specific problem domain. Writing your own quasi-parsers (which can be done in E itself, as the JPanel quasiparser described below was written) is beyond the scope of this book. However, E comes with several quasi-parsers built in: a simple quasi-parser, a regular expression quasi-parser, a JPanel quasi-parser for Swing, and a swtGrid quasi-parser for SWT.

Simple quasi-parser

The default quasi-parser is a text manipulating parser capable of extracting data from strings and constructing new strings. In its simplest form, it is a clean and simple way of constructing strings for printing:

 # E sample
 def a := 3
 def answerString := `The value a is $a, and a times two is ${2 * a}.`

Here we use a simple quasi-parser to build an output string in a fashion similar to the printf statement in C. Quasi literals are enclosed in back-ticks. A dollar sign denotes the beginning of a description of a value to be inserted at the dollar sign location. If the dollar sign is immediately followed by a variable name, the value of that variable is used. If the dollar sign is followed by an open brace, everything up to the close brace is evaluated to compute the value to be substituted (so you could put "$" inside the braces to put a dollar sign in the string, as well as doing arithmetic as in the above example). Quasi-literals can span multiple lines, in which case the carriage return is part of the structure.

A more sophisticated use of simple quasi-literals is for pattern matching. Here we parse a sentence:

 # E sample
 def line := "By the rude bridge that arched the flood"
 if (line =~ `@word1 @{word2} rude @remainder`) {
     println(`text matches, word1 = $word1`)

The string on the left of =~ is compared to the quasi literal on the right, evaluating true if the string can be parsed in conformance with the quasi literal. The "@" asserts that any text can match this part of the string, and the variable declared after the "@" contains that text at the end of the evaluation. The variable "word2" is enclosed in braces to offset it from the word "rude" immediately following it, which would look like part of the variable name without the offset.

In this example, the minimal string that can get a match would be space-space-"rude ", in which case the data extracted for variables word1, word2, and remainder would all be zero-length strings. As it is, at the end of the evaluation, word1=="By", word2=="the", and remainder == "bridge that arched the flood".

A single quasi-literal can contain dollar signs as well as "@"'s, in which case the results of the dollar sign evaluations will be included in the matching conditions.

The int guard will not convert strings to ints, so to extract an integer from a string, use something like this:

def line := "The number 5"
def `The number @xString` := line
def x := __makeInt(xString)
# value: 5

For floating-point numbers, you can use <import:java.lang.makeDouble>.parseDouble(...).

Quasi-literals can almost always be treated as strings. They accept almost all of the string protocol (technically, the quasi-literals are of type "twine"). The one place where they cannot be treated as strings is in comparisons to actual strings. To compare a quasi-literal to a string, use the "bare" method: this is now wrong! all strings seem to now be twines

def equalStrings := "abc".bare() == `abc`.bare()

Regular expression quasi parser

The regular expression quasi-parser gives E much of the CGI scripting power that Perl and Python share. Since E runs on top of a jvm with all the startup time such jvms entail, using E for CGI scripts per se is not recommended. However, if one uses the distributed computing power of E to run CGI-like E programs as services for a Web server, one can achieve the same effect, and receive a number of bonuses unavailable in Perl and Python. The example Web server written in E, shown at the end of the book, was designed with just this thought in mind.


The JPanel quasi-parser processes visually understandable strings into complex window panel layouts for gui applications. It is a most remarkable and useful example of quasi-parsers in action, giving the developer a rather WYSIWYG presentation of his windows. Thus the E programmer has no need to resort to the typical inflexible IDE-specific drawing tools that produce code no one can read and absolutely no one can edit. Under the covers, the JPanel uses the GridBagLayout manager to compose the panel, giving it a flexibility comparable to the TK layout system from TCL (which actually inspired the JPanel). Unlike the typical visual layout editors in Java IDEs, the JPanel system is able to define a broad range of resizable windows simply and intuitively.

 # E syntax
 # define the panels explanation,label, field, okButton, cancelButton, logo
 # pad1, pad2, which are label fields before 
 # this example begins
 def composedPanel := 
 JPanel`$explanation.Y     >              >
 $label                    $field         >
 $okButton                 $cancelButton  $pad1.X
 V                         $pad2          $logo`

In this layout, the explanation (presumably a textArea) is at the top of the composedPanel, with the label and field left-to-right underneath, and the okButton to the left of the cancelButton/logo area arranged top-to-bottom. This is a layout for a 3-wide, 4-high grid of cells, though some the panes fill multiple cells, and the rules for which cells grow are sophisticated, as described next:

The ".Y" says that the explanation should soak up any extra vertical space. The ".X" says the field should soak up any extra horizontal space. The two ">" symbols say that the explanation should span all three of the columns of this pane. The field fills two horizontal cells as denoted by the ">" which follows it. The "V" in the lower lefthand corner says that the okButton should fill two vertical cells.

When this pane is part of a resizable window, enlarging vertically makes the explanation larger. Enlarging the window horizontally enlarges the field but not the label. Both the cancel button and the okButton should remain the same size regardless of resizing since the extra space is being soaked up elsewhere.

If several elements have ".Y", the extra vertical space is divided evenly among them; similarly fields with ".X" share extra horizontal space.

The space characters used to separate the elements of this layout have no meaning to the quasi-parser; we have used the space to create a visual representation of the layout that makes it easy to see the layout even though this is just code.

It is not possible to lay out all possible compositions with a single JPanel, but JPanels can dramatically reduce the nesting of panels compared to Java applications, while making the layout visually clear in the code itself. And of course, you can always place JPanel-laid-out panels inside of other JPanel layouts. The result is tremendously more compact, easier to understand, and easier to maintain than the result of nesting large numbers of Swing Box layouts.

A similar quasi-parser, the swtGrid, is included for layout of SWT panels. The main difference, as shown in later example code, is that the swtGrid requires a first entry that is the parent panel for the laid out components, e.g.

def label := <widget:makeLabel>(frame, SWT.getLEFT())
swtGrid`$frame: $chatArea.X.Y

Improve this discussion


The SQL quasi-parser can be used to execute SQL statements and queries. Many languages use simple string substitution to build SQL queries, but this opens the risk of SQL injection attacks, where the user (attacker) supplies input to a query that is misinterpreted as part of the SQL statement. Using a quasi-parser, E prevents this possibility. For example, we don't have to worry about the quote character in this user's name:

 def name := "O'No! $@?"
 sql`INSERT INTO users (userName, karma) VALUES ($name, 0)`

For details, see Safe database access in E

Personal tools
more tools