Walnut/Ordinary Programming/Quasi-Literals and Quasi-Parsers
From Erights
Kevin Reid (Talk | contribs) m (Reverted edits by 193.203.99.31 (Talk); changed back to last version by Kevin Reid) |
(Removed out-of-date comment about bare strings) |
||
(18 intermediate revisions not shown) | |||
Line 38: | Line 38: | ||
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. | 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 | + | 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(...)'''. | ||
====Regular expression quasi parser==== | ====Regular expression quasi parser==== | ||
Line 76: | Line 81: | ||
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. | 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 <span class="note" style="color:red"> | + | 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 | ||
+ | $label.X | ||
+ | $commandLine.X` | ||
+ | |||
+ | <span class="note" style="color:red">Improve this discussion</span> | ||
+ | |||
+ | ====SQL==== | ||
+ | |||
+ | 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 the [[SQL]] page. |
Latest revision as of 11:17, 3 August 2011
Contents |
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}.` println(answerString)
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(...).
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.
JPanel
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 $label.X $commandLine.X`
Improve this discussion
SQL
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 the SQL page.