docs: Update to reflect pending changes in the Spoofax Tutorial/Reference

This commit is contained in:
Glen Whitney 2021-02-03 16:24:47 -08:00
parent ba130ecb0f
commit ae0a798c7b
8 changed files with 107 additions and 385 deletions

View file

@ -5,11 +5,29 @@ Title: Running a Strategy, continued
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`:
As the manual notes, another way to see what the `dnf` strategy does to a given
AST is to add a Spoofax Test Language test that claims it transforms to
something that it definitely does **not** parse to, e.g.
```SPT
{! ../test/manual-suite.spt extract:
start: '\*\*[/]'
terminate: '(.*run dnf.*)$' !}
```
The difficulty is that in a standard Spoofax language project, the error popup
showing what the transformation actually does is nearly unreadable due to
a tremendous amount of annotation produced by the default static analysis process.
Since in this repository we are not actually doing any (meaningful) static
analysis, it includes a hack which makes the editor popup much more readable.
Namely, it strips the annotation, by modifying the
`editor-analyze` rule in `trans/analysis.str` like so:
```Stratego
{! ../trans/analysis.str terminate: Debugging!} [rest of file omitted]
```
**/
/** md Include this code in documentation: */
module prop-dnf-rules
imports prop-eval-rules
rules
@ -23,121 +41,3 @@ rules
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.
**/

View file

@ -4,7 +4,7 @@ Title: Running a Strategy: the command line
## Functionalizing reduction rules
The {! ../docrefs/manual.md !} next goes on, in Chapter 5, to consider ways to
add the ability to also compute conjunctive normal forms for propositional
add the ability to also compute conjunctive normal forms (CNF) for propositional
ASTs. The rules for this form cannot simply be added to those from the prior
chapter for disjunctive normal form, as they would produce a non-terminating
rewrite system.
@ -12,13 +12,26 @@ rewrite system.
The first method for which working examples are provided is the method of
"functionalization" in {! ../docrefs/sec5.1.2.md !}. Although an anti-pattern
in the views of the Spoofax designers, the examples are still instructive and
of course we want to implement them here.
of course they are implemented here.
In fact, the Stratego code
for the CNF is not given explicitly in the manual, but it consists of:
```
{! prop-cnf3.str !}
```
Note in particular we have not repeated the `make-nf` strategy from
`trans/prop-dnf3.str` here --- that's the
entire point of functionalization; we can use the same top-level reduction
scheme because the introduced function symbols keep track of what operations
are being performed. We also introduce a strategy `dcnf` for the simultaneous
construction of DNF and CNF, as noted in the manual.
Again, there are menu items and SPT cases for the `cnf3` and `dcnf` strategies
as well. All operate just as in the previous sections.
So, we can grab the Stratego code given in the section as our
`trans/prop-dnf3.str`:
```Stratego
**/
/** md Include this code in docs: */
module prop-dnf3
imports libstrategolib signatures/-
signature
@ -50,53 +63,8 @@ strategies
do-dnf3: (selected, _, _, path, project-path) -> (filename, result)
with filename := <guarantee-extension(|"dnf.aterm")> path
; result := <dnf3> selected
/* End of code inclusion **/
/** md Conclusion of documentation:
```
Notice the few minor differences from the manual. We import `signatures/-` as
before. We've used a fresh identifier for the `E` and `D` rules, because unlike
in the manual, all of our Stratego files are imported into the same main
`trans/spoofax_propositional_language.str` module. So they must live together,
but as noted in the manual, these functionalized rules do not "play together"
nicely with the evaluation `E` rules introduced [earlier](prop-eval-rules.md).
And we've renamed the `dnf` rule to `make-nf` -- for a reason that will
become clear in a moment -- but incorporated the wrapping by `Dnf` of the AST
we want to compute the DNF of into the `dnf3` strategy, since our concrete
syntax has no way of specifying such a wrapping.
Also as before, we've created a `syntax/examples/sec_5_1_2_test1.spl`
(which it turns out is just the same as `sec_4_2_test3.spl`)
and a menu item and a `test/manual-suite.spt` case called `sec5_1_2_test1_ex` to
see that all is working well. Applying `dnf3` to the test file produces
```
{! ../syntax/examples/sec5.1.2_test1.dnf.aterm !}
```
which is correct. (Note the contrast with what's shown in
{! ../docrefs/sec5.1.2.md !} as the output of the `prop-dnf3` program,
in which the recurring "ATom" typo has blocked
the reductive elimination of some of the occurrences of the Dnf function
symbol. Of course, as the manual itself points out, in proper operation of
this functionalization approach, all of the function symbols must reduce away
to end up back in an AST of the subject language.)
So now we can move on to the conjunctive normal form. The Stratego code
for the CNF is not given explicitly in the manual, but it consists of:
```
{! prop-cnf3.str !}
```
Note in particular we have not repeated the `make-nf` strategy -- that's the
entire point of functionalization; we can use the same top-level reduction
scheme because the introduced function symbols keep track of what operations
are being performed. We also introduce a strategy `dcnf` for the simultaneous
construction of DNF and CNF, as noted in the manual.
Again, there are menu items and SPT cases for the `cnf3` and `dcnf` strategies
as well. All operate just as in the previous two sections.
We can now use this example to show yet another way to try Stratego stragies
with the Spoofax IDE implementation.
/** md
### Command-line Utilities
@ -105,7 +73,8 @@ The Spoofax project offers an executable jar called
that allows several different Spoofax actions to be invoked from the command line.
Let's say you have downloaded it to the path `SUNSHINE_JAR`. Then you can see a
summary of the available actions with `java -jar $SUNSHINE_JAR -h`. In the
repository there's a convenience bash script `bin/spoofax-menu`:
repository there's a convenience bash script `bin/spoofax-menu` you can use to
run the Sunshine jar:
```bash
{! ../bin/spoofax-menu extract:
- stop: --help

View file

@ -5,17 +5,60 @@ Title: Running a Strategy
The first example of running a Stratego strategy
in the {! ../docrefs/manual.md !} is that of _evaluating_ an AST,
via the `prop-eval-rules` module in {! ../docrefs/sec4.1.md !}. This page
describes the most straightforward way to run this same strategy
in the Spoofax Eclipse IDE.
via the `prop-eval-rules` module in {! ../docrefs/sec4.1.md !}. In this
repository, we've placed those rules in `trans/prop-eval-rules.str`,
and the glue strategies that call them in `trans/prop-eval.str`. This latter
module is imported by `trans/spoofax_propositional_language.str`.
In the current project structure, Stratego transformations and strategies go in
the `trans` directory. So, you can place the Stratego code for
this module in `trans/prop-eval-rules.str`, with the following contents:
(I am unclear on what triggers Editor Services to use this particular Stratego
file, but in any automatically-generated Spoofax project there will be a
main language Stratego file in the `trans` directory, and that's the file
from which the rule specified in an ESV `action` has to be visible.)
There's an Editor Services module `editor/Manual.esv` to invoke the eval strategy,
and it's included in in `editor/Main.esv.`
With all of these elements in place, you should now be able to invoke
the `eval` rule from the "Spoofax" menu. For example, to execute the "test1"
example from the Spoofax Tutorial/Reference
{! ../docrefs/sec4.1.md !}, navigate to the
file `syntax/examples/sec4.1_test1.spl`:
```SPL
{! ../syntax/examples/sec4.1_test1.spl !}
```
and select "Spoofax > Manual > prop-eval" from the menu bar to produce:
```
{! ../syntax/examples/sec4.1_test1.eval.aterm !}
```
You can do the same with test2:
```SPL
{! ../syntax/examples/sec4.1_test2.spl !}
```
to produce
```
{! ../syntax/examples/sec4.1_test2.eval.aterm !}
```
Both of these results match the expected output of the `eval` transformation
as shown in {! ../docrefs/sec4.1.md !}.
One other note about the files provided in this repository. The "do-XXX" rule
used as boilerplate glue between ESV and a Stratego strategy in
`trans/prop-eval.str` is:
```Stratego
[End the documentation block to avoid the comment close in the code] **/
/** md [restart processing to include the code in the documentation] */
{! prop-eval.str extract: {start: '^(.*Interface.*\s*)$'} !}
```
The version of this glue being used here corresponds to what's used in the example
[Calc language project](https://github.com/MetaBorgCube/metaborg-calc),
but is slightly different from what is described in the
[Menus section](http://www.metaborg.org/en/latest/source/langdev/meta/lang/esv.html#menus)
of the ESV Manual. I am unclear on the significance of this difference or which
form is "better" -- I simply modeled this Stratego code after the code in the
working Calc repository, and all seems to be well.
**/
module prop-eval-rules
imports signatures/-
rules
@ -37,116 +80,3 @@ rules
E : Eq(x, False()) -> Not(x)
E : Eq(True(), x) -> x
E : Eq(x, True()) -> x
/* [stop to avoid the open-comment in the code block] **/
/** md [conclusion of the documentation]
```
Note an important difference between this code and the `prop-eval-rules` block
in {! ../docrefs/sec4.1.md !}: here, we import `signatures/-` rather than `prop`.
That's because the Spoofax IDE automatically generates the Spoofax Propositional
Language AST signature for us, from its concrete syntax definition. (The
evaluation rules in {! ../docrefs/sec4.1.md !} also happen to be missing the
fourth `Impl` rule, but that's less material; unsurprisingly, none of the examples
given in the manual happen to rely on that rule, but we include it here for the
sake of completeness.)
As in the {! ../docrefs/manual.md !}, we need an additional module, which we can
place in `trans/prop-eval.str`, to describe the strategy by which we want
to apply the above evaluation rules and to facilitate actually invoking
the strategy on an AST. This file starts out much like its counterpart in
{! ../docrefs/sec4.1.md !}:
```Stratego
{! prop-eval.str {terminate: '^\s*$'} !}
```
So far, we've included the evaluation rules and defined a strategy `eval` to
apply them exhaustively to an AST starting with inner subterms. Now we want to
be able to "run" this strategy on an actual AST.
### Editor Services
The most straightforward mechanism to execute a Stratego transformation
in the Spoofax IDE is by use of Editor Services (ESV). ESV is actually a full
declarative language in its own right, covering syntax coloring, menu items,
hover texts, and more, and with its own
[manual](http://www.metaborg.org/en/latest/source/langdev/meta/lang/esv.html).
We will not delve into the details of ESV here, but merely provide a recipe
for using it to add an item to the "Spoofax" Eclipse menu for running Stratego.
Actually, we'll create a submenu "Manual" for our examples. It's
cleanest to put our additions in a separate ESV module, which then must be
included into the `editor/Main.esv` file, like so:
```esv
{! ../editor/Main.esv extract:
replace: ['^\s*$']
terminate: 'provider\s*:' !} [ ... rest of file suppressed for brevity. ]
```
It's just the one line "Manual" in the `imports` section that we have added. The
`editor/Manual.esv` implementing the submenu is also very simple:
```esv
{! ../editor/Manual.esv terminate: dnf !}
```
Note that the quoted string on the `action` line is the text label of the menu
item in the "Manual" submenu, and the identifier on its right-hand side is the
Stratego action to call. But note that `do-eval` is not the same identifier as
`eval` in our last Stratego file above. That's because the Stratego invocation
caused by an ESV action item has a specific, multi-item argument list, whereas
`eval` above expects only a single Spoofax Propositional Language AST to operate
on. To bridge this gap, we must add one more item to `trans/prop-eval.str`,
namely:
```Stratego
{! prop-eval.str extract: {start: '^(.*Interface.*\s*)$'} !}
```
This "do-XXX" rule is pretty much boilerplate glue between ESV and your Stratego
strategy of interest. (Note that this "glue" being used here corresponds to
what's used in the example
[Calc language project](https://github.com/MetaBorgCube/metaborg-calc),
but is slightly different from what is described in the
[Menus section](http://www.metaborg.org/en/latest/source/langdev/meta/lang/esv.html#menus)
of the ESV Manual. I am unclear on the significance of this difference or which
form is "better" -- I simply modeled this Stratego code after the code in the
working Calc repository, and all seems to be well.)
There's one last step before you can run `eval` from inside the IDE.
Editor Services has to be able to find the `do-eval` rule. You can enable that
by importing the `prop-eval` module in the main Stratego file for our language,
in this case `trans/spoofax_propositional_language.str`:
```Stratego
{! spoofax_propositional_language.str extract:
- stop: 'prop-dnf'
- start: '^(.*rule.*)$'
terminate: debug-show
!}
[ ... rest of file suppressed. ]
```
(I am unclear on what triggers ESV to use this particular Stratego file, but
in any automatically-generated Spoofax project there will be a main language
Stratego file in the `trans` directory, and that's the file from which
the rule specified in an ESV `action` has to be visible.)
Ok, with all of these elements in place, you should now be able to invoke
the `eval` rule from the "Spoofax" menu. For example, to execute the next example
from the Spoofax Tutorial/Reference
{! ../docrefs/sec4.1.md !}, navigate to the
file `syntax/examples/sec4.1_test1.spl`:
```SPL
{! ../syntax/examples/sec4.1_test1.spl !}
```
and select "Spoofax > Manual > prop-eval" from the menu bar to produce:
```
{! ../syntax/examples/sec4.1_test1.eval.aterm !}
```
You can do the same with test2:
```SPL
{! ../syntax/examples/sec4.1_test2.spl !}
```
to produce
```
{! ../syntax/examples/sec4.1_test2.eval.aterm !}
```
Both of these results match the expected output of the `eval` transformation
as shown in {! ../docrefs/sec4.1.md !} (allowing for the typo "ATom" in the
manual in the latter case).
**/