Glen Whitney
a33c1585d6
And put in generic documentation for all the remaining tests. If any particular tests need individual commentary, it can always be added later.
144 lines
5.5 KiB
Plaintext
144 lines
5.5 KiB
Plaintext
/** md
|
|
Title: Running a Strategy, continued
|
|
|
|
## Disjunctive Normal Form
|
|
|
|
The next Stratego example, in {! ../docrefs/sec4.2.md !}, extends
|
|
`prop-eval-rules` to convert SPL propositions into disjunctive normal form (DNF).
|
|
Once again, we can place the Stratego code in `trans/prop-dnf-rules.str`:
|
|
|
|
```Stratego
|
|
**/
|
|
/** md Include this code in documentation: */
|
|
module prop-dnf-rules
|
|
imports prop-eval-rules
|
|
rules
|
|
E : Impl(x, y) -> Or(Not(x), y)
|
|
E : Eq(x, y) -> And(Impl(x, y), Impl(y, x))
|
|
|
|
E : Not(Not(x)) -> x
|
|
|
|
E : Not(And(x, y)) -> Or(Not(x), Not(y))
|
|
E : Not(Or(x, y)) -> And(Not(x), Not(y))
|
|
|
|
E : And(Or(x, y), z) -> Or(And(x, z), And(y, z))
|
|
E : And(z, Or(x, y)) -> Or(And(z, x), And(z, y))
|
|
/* end of code inclusion **/
|
|
/** md rest of documentation
|
|
```
|
|
and create a `trans/prop-dnf.str` that defines a strategy for applying the
|
|
DNF rules and supplies the glue needed between ESV and Stratego:
|
|
```Stratego
|
|
{! prop-dnf.str !}
|
|
```
|
|
Now just importing `prop-dnf` in `trans/spoofax-propositional-language.str`
|
|
and adding a menu item to `editor/Manual.esv`:
|
|
```ESV
|
|
...
|
|
{! ../editor/Manual.esv extract: {start: menus, stop: dnf3} !}
|
|
```
|
|
should suffice to make the dnf strategy available in the "Spoofax > Manual"
|
|
menu.
|
|
|
|
Indeed, we can try it out on `syntax/examples/sec4.2_test3.spl`:
|
|
```SPL
|
|
{! ../syntax/examples/sec4.2_test3.spl !}
|
|
```
|
|
to produce
|
|
```
|
|
{! ../syntax/examples/sec4.2_test3.dnf.aterm !}
|
|
```
|
|
as verified in the manual (again, modulo the "ATom" typo in the manual).
|
|
|
|
However, we also want to use this second example to show another method of
|
|
running Stratego strategies from the Eclipse IDE.
|
|
|
|
### Spoofax Testing Language
|
|
|
|
The
|
|
[Spoofax Testing Language](http://www.metaborg.org/en/latest/source/langdev/meta/lang/spt/index.html)
|
|
(SPT) is a declarative language that provides for a full range of tests
|
|
for a Spoofax language project. As such, it includes the ability to run an
|
|
arbitrary Stratego strategy on the results of parsing an arbitrary piece of
|
|
the language you're working with -- seemingly perfect for our interest in
|
|
trying our different Stratego transformations.
|
|
|
|
So, we can just take our `test3` expression above and make it a part of
|
|
an SPT test suite, which we will call `test/manual-suite.spt`:
|
|
```SPT
|
|
{! ../test/manual-suite.spt extract:
|
|
start: '\*\*/'
|
|
terminate: '(.*run dnf)' !}
|
|
```
|
|
|
|
Once we have saved this file, the tests run automatically. What does this mean?
|
|
The file seems to be just "sitting there;" there's no indication that anything
|
|
is happening. That's because this test we've just written **succeeds**. All we
|
|
asked is that Spoofax run the `dnf` transformation on the results of parsing
|
|
the test expression. It did that, and the transformation succeeded. So all is
|
|
well, and no output is generated.
|
|
|
|
But of course, we want to see what the _result_ of the transformation was. One
|
|
way of arranging that is to ask that SPT compare that result to a given AST.
|
|
If we use an AST like `Atom("x")` that can't possibly be the actual output
|
|
(since "x" does not occur in the input), then the error message will show
|
|
what the transformation actually produced. So we add just a bit to
|
|
`test/manual-suite.spt`:
|
|
```SPT
|
|
{! ../test/manual-suite.spt extract:
|
|
start: '\*\*/'
|
|
terminate: '(.*run dnf.*)$' !}
|
|
```
|
|
|
|
and now sure enough a little error symbol appears next to the test. If you hover
|
|
over it, a popup will show up, indicating that SPT was expecting `Atom("x")`,
|
|
but got... wait! What's this monstrosity?
|
|
```
|
|
Got: Or(And(Not(Atom("r"{TermIndex("test/manual-suite.spt",1)}){TermIndex
|
|
("test/manual-suite.spt",2)}),Atom("p"{TermIndex("test/manual-suite.spt",9)})
|
|
{TermIndex("test/manual-suite.spt",10)}),And(And(Atom("p"{TermIndex("test/manual-
|
|
suite.spt",3)}){TermIndex("test/manual-suite.spt",4)},Atom("q"{TermIndex
|
|
("test/manual-suite.spt",5)}){TermIndex("test/manual-suite.spt",6)}){TermIndex
|
|
("test/manual-suite.spt",7)},Atom("p"{TermIndex("test/manual-suite.spt",9)})
|
|
{TermIndex("test/manual-suite.spt",10)}))
|
|
```
|
|
|
|
Well, it is actually the result of running the `dnf` strategy on the result of
|
|
parsing ` {! ../syntax/examples/sec4.2_test3.spl !} `. It's just that Spoofax
|
|
automatically annotates every subterm of the parsed term with its location in
|
|
the input, and those annotations are carried through the Stratego
|
|
transformation. While this can be very helpful in figuring out exactly what
|
|
happened in the course of the transformation, it can also make the output
|
|
practically unreadable.
|
|
|
|
Fortunately, we can strip this annotation if we like, by modifying the
|
|
`editor-analyze` rule in `trans/analysis.str` like so:
|
|
```Stratego
|
|
{! ../trans/analysis.str terminate: Debugging!} [rest of file omitted]
|
|
```
|
|
|
|
Now rebuild the project and re-run the SPT manual suite. (You may need to
|
|
do an irrelevant edit and save of the `test/manual-suite.spt` file, or
|
|
you can select this file in the project explorer and select the menu item
|
|
"Spoofax (meta) > Run all selected tests" to pop up a convenient window of
|
|
all test results.) Now you should see the much more readable
|
|
```
|
|
Got: Or(And(Not(Atom("r")),Atom("p")),And(And(Atom("p"),Atom("q")),Atom("p")))
|
|
```
|
|
in the error message for the `sec4_2_test3` test.
|
|
|
|
Of course, if you know the AST you expect to generate with your transformation,
|
|
you can also just tell SPT to compare against that, for example in our case
|
|
by adding a test to the manual suite like this:
|
|
```SPT
|
|
{! ../test/manual-suite.spt extract:
|
|
start: '(test sec4_2_test3_ex.*\n?)$'
|
|
stop: '^(.*run.*)$'
|
|
!}
|
|
```
|
|
|
|
Now if there is no error or warning on this test then you know the
|
|
`dnf` strategy produced the result shown in the `to` clause, and otherwise,
|
|
the actual result will be shown in the error popup.
|
|
**/
|