spoofax_prop/trans/prop-dnf-rules.str

144 lines
5.5 KiB
Plaintext
Raw Normal View History

/** 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:
2021-01-20 05:00:56 +00:00
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:
2021-01-20 05:00:56 +00:00
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.
**/