feat: Implement Chapter9

Also provides proper associativity on the operators, to avoid
  ambiguous parses.

  This completes all of the examples in the Spoofax Tutorial/Reference
  that use the propositional signature.
This commit is contained in:
Glen Whitney 2021-01-22 09:29:57 -08:00
parent fddc94a906
commit ba130ecb0f
17 changed files with 402 additions and 25 deletions

View File

@ -22,7 +22,22 @@ Stratego for the first time:
Syntax Definition Language SDF3, there does not seem to be any publicly
available repository containing the worked examples to follow along with.
This [Gitea repository](https://code.studioinfinity.org/glen/spoofax_prop) aims to fill both gaps. It can be cloned as an Eclipse project (note the git root is at the project level rather than in the directory above the project, as may be more common; in other words, this repository should be cloned within an existing Eclipse workspace, rather than as the workspace itself). Within that project, (ultimately) every example from the Stratego Tutorial manual starting from Section 4 on can be executed directly. And this (Mkdocs-generated) documentation seeks to clarify the ways that Stratego transformations may be run in the Spoofax/Eclipse IDE environment, serving as a supplement/replacement for the portions of the manual that were specific to Stratego/XT.
This [Gitea repository](https://code.studioinfinity.org/glen/spoofax_prop) aims
to fill both gaps. It can be cloned as an Eclipse project (note the git root
is at the project level rather than in the directory above the project, as may
be more common; in other words, this repository should be cloned within
an existing Eclipse workspace, rather than as the workspace itself). Within that
project, all of the examples from the Stratego Tutorial manual that employ the
propositional language (see below), starting from Section 4 on, can be executed
directly. This repository does not attempt to implement the portion of examples
dealing with an arithmetical/imperative language (except in a couple of
isolated instances in which the relevant phenomenon is recast in the
propositional language).
This (Mkdocs-generated) documentation seeks to clarify the ways that Stratego
transformations may be run in the Spoofax/Eclipse IDE environment, serving as
a supplement/replacement for the portions of the manual that are
specific to Stratego/XT.
## Preliminaries
@ -69,3 +84,11 @@ It consists of the following signature of constructors:
Continue reading in the latest released
[SPL documentation](http://studioinfinity.org/SPL)
to see how to define and run Stratego transformations on ASTs of this language.
### Building the documentation
In order to build the documentation from a clone of this repository, you must
install [MkDocs](http://mkdocs.org) and the `semiliterate` plug-in for MkDocs,
which is available in
[pre-release form](https://code.studioinfinity.org/glen/mkdocs-semiliterate). Then
you can execute `mkdocs build` in the top-level directory of this project.

1
docrefs/sec6.2.md Normal file
View File

@ -0,0 +1 @@
[Section 6.2](http://www.metaborg.org/en/latest/source/langdev/meta/lang/stratego/strategoxt/06-rules-and-strategies.html#what-is-a-strategy)

View File

@ -34,10 +34,10 @@ context-free syntax
Prop.False = <0>
Prop.Atom = String
Prop.Not = <!<Prop>>
Prop.And = <<Prop> & <Prop>>
Prop.Or = <<Prop> | <Prop>>
Prop.Impl = [[Prop] -> [Prop]]
Prop.Eq = <<Prop> = <Prop>>
Prop.And = <<Prop> & <Prop>> {left}
Prop.Or = <<Prop> | <Prop>> {left}
Prop.Impl = [[Prop] -> [Prop]] {right}
Prop.Eq = <<Prop> = <Prop>> {non-assoc}
Prop = <(<Prop>)> {bracket}
String = ID

View File

@ -58,19 +58,18 @@ test sec5_1_2_test1_cnf_ex [[
test sec5_1_2_test1_both [[
(r -> p & q) & p
]] run dcnf to Atom("x")
//(Or(And(Not(Atom("r")),Atom("p")),And(And(Atom("p"),Atom("q")),Atom("p"))),
// And(And(Or(Not(Atom("r")), Atom("p")),
// Or(Not(Atom("r")), Atom("q"))),
// Atom("p")))
]] run dcnf to (Or(And(Not(Atom("r")),Atom("p")),And(And(Atom("p"),Atom("q")),Atom("p"))),
And(And(Or(Not(Atom("r")), Atom("p")),
Or(Not(Atom("r")), Atom("q"))),
Atom("p")))
test sec5_3_1_test1_dnf_ex [[
(r -> p & q) & p
]] run dnf4 to Or(And(Not(Atom("r")),Atom("p")),And(And(Atom("p"),Atom("q")),Atom("p")))
]] run dnf-laws to Or(And(Not(Atom("r")),Atom("p")),And(And(Atom("p"),Atom("q")),Atom("p")))
test sec5_3_1_test1_cnf_ex [[
(r -> p & q) & p
]] run cnf4 to And(And(Or(Not(Atom("r")), Atom("p")),
]] run cnf-laws to And(And(Or(Not(Atom("r")), Atom("p")),
Or(Not(Atom("r")), Atom("q"))),
Atom("p"))
@ -110,15 +109,15 @@ To illustrate extending a rule, we need to give the rule we're going to extend
a different identifier and add both definitions to that (otherwise our original
rule `SwapArgs` would already have been extended).
The `chap6.str` file also includes a solution to the exercise in
[Section 6.2](http://www.metaborg.org/en/latest/source/langdev/meta/lang/stratego/strategoxt/06-rules-and-strategies.html#what-is-a-strategy).
On the other hand, it is not clear whether the Spoofax Eclipse IDE has a "debug"
operator for Stratego, or if so how it would work, so the repository does not
The `chap6.str` file also includes a solution to the exercise
in {! ../docrefs/sec6.2.md !}. On the other hand, it is not clear whether
the Spoofax Eclipse IDE has a "debug" operator for Stratego, or if so how
it would work within the IDE, so the repository does not
attempt to implement the "debug" example.
The remainder of the chapter (from section 6.3 on) consists of general information
about Stratego syntax and information about the Stratego compiler. As it's not
clear if the latter applies to the Spoofax Eclipse IDE, there are no further test
clear whether the latter applies to the Spoofax Eclipse IDE, there are no further test
cases in this repository beyond Section 6.2.
**/
@ -152,10 +151,10 @@ test sec6_2_invoke_rule_ex [[
test sec6_2_innermost_ex [[
p = q
]] run dnf4 to Or(Or(And(Not(Atom("p")),Not(Atom("q"))),
And(Not(Atom("p")),Atom("p"))),
Or(And(Atom("q"),Not(Atom("q"))),
And(Atom("q"),Atom("p"))))
]] run dnf-laws to Or(Or(And(Not(Atom("p")),Not(Atom("q"))),
And(Not(Atom("p")),Atom("p"))),
Or(And(Atom("q"),Not(Atom("q"))),
And(Atom("q"),Atom("p"))))
test sec6_2_dnf_exercise_ex [[
p = q
@ -199,7 +198,7 @@ test sec7_3_repeat_small_ex [[
]] run repeatmaybeAB to Impl(True(), False())
test sec7_3_repeat_big_ex [[
(1 -> (1 -> (1 -> (1 -> 0)))) | 0
(1 -> 1 -> 1 -> 1 -> 0) | 0
]] run repeatmaybeAB to Impl(True(), Impl(True(), Impl(True(),
Impl(True(), False()))))
@ -236,3 +235,232 @@ test sec8_5_1_wrap_ex [[
test sec8_5_2_project_ex [[
p | !p
]] run TermProj to Atom("p")
/** md
### Chapter 9
This chapter is much more in the style of the early chapters: there are several
explicit Stratego modules, and we incorporate each one into this repository. As
before, we need to differentiate the names in some modules since they all end
up being loaded at once; but we also take advantage of the common sections of
some of the modules and re-use rather than repeat the code. In addition, the
disjunctive and conjunctive normal forms of the same test expression as used
in Chapter Five are used for test cases in `trans/manual-suite.spt`.
**/
test chap9_dnf4_ex [[
(r -> p & q) & p
]] run dnf4 to Or(And(Not(Atom("r")),Atom("p")),And(And(Atom("p"),Atom("q")),Atom("p")))
test chap9_dnf6_ex [[
(r -> p & q) & p
]] run dnf6 to Or(And(Not(Atom("r")),Atom("p")),And(And(Atom("p"),Atom("q")),Atom("p")))
test chap9_dnf7_ex [[
(r -> p & q) & p
]] run dnf7 to Or(And(Not(Atom("r")),Atom("p")),And(And(Atom("p"),Atom("q")),Atom("p")))
test chap9_cnf7_ex [[
(r -> p & q) & p
]] run cnf7 to And(And(Or(Not(Atom("r")), Atom("p")),
Or(Not(Atom("r")), Atom("q"))),
Atom("p"))
/** md
The key point to know about
[Chapter 9](http://www.metaborg.org/en/latest/source/langdev/meta/lang/stratego/strategoxt/09-traversal-strategies.html),
however, is that there is a mistake that affects examples `prop-dnf8` through
`prop-dnf10`. The difficulty is in this definition:
```Stratego
strategies
propbu(s) = proptr(propbu(s)); s
```
Recall that `proptr` is defined as follows: (Note that our implementation calls
it `proptr7` to distinguish it from different definitions for `proptr` in other
examples.)
```Stratego
{! ../trans/prop-dnf7.str extract:
start: '(rules\n*)'
stop: strategies
!}
```
So `propbu` must be defined as:
```Stratego
{! ../trans/prop-dnf8.str extract:
start: strategies
stop: '(.*propbu8.*)'
!}
```
The point is that `proptr` cannot apply to `Atom`s and logical constants, and so
fails when it reaches such nodes in the AST. Without the `try` in the definition
of the `propbu` traversal, any such failure propagates to a failure of the entire
strategy.
**/
test chap9_dnf8_ex [[
(r -> p & q) & p
]] run dnf8 to Or(And(Not(Atom("r")),Atom("p")),And(And(Atom("p"),Atom("q")),Atom("p")))
test chap9_cnf8_ex [[
(r -> p & q) & p
]] run cnf8 to And(And(Or(Not(Atom("r")), Atom("p")),
Or(Not(Atom("r")), Atom("q"))),
Atom("p"))
/** md
Example `prop-dnf9` simply re-uses `propbu8`.
**/
test chap9_dnf9_ex [[
(r -> p & q) & p
]] run dnf9 to Or(And(Not(Atom("r")),Atom("p")),And(And(Atom("p"),Atom("q")),Atom("p")))
test chap9_cnf9_ex [[
(r -> p & q) & p
]] run cnf9 to And(And(Or(Not(Atom("r")), Atom("p")),
Or(Not(Atom("r")), Atom("q"))),
Atom("p"))
/** md
And a similar fix is made to `prop-dnf10`:
```Stratego
{! ../trans/prop-dnf10.str !}
```
**/
test sec9_1_dnf10_ex [[
(r -> p & q) & p
]] run dnf10 to Or(And(Not(Atom("r")),Atom("p")),And(And(Atom("p"),Atom("q")),Atom("p")))
test sec9_1_cnf10_ex [[
(r -> p & q) & p
]] run cnf10 to And(And(Or(Not(Atom("r")), Atom("p")),
Or(Not(Atom("r")), Atom("q"))),
Atom("p"))
/** md
There is another issue in the "Format Checking" example in section 9.1. It lies
with the strategy used to determine if an expression is an atom or the negation
of an atom. The expression shown is `Not(Atom(x)) <+ Atom(x)`. This appears to
be a left choice of two congruences. But a congruence expects a strategy as an
argument, whereas here `x` is simply a free variable. There are a couple of
fixes to write an expression that checks whether a term is just an `Atom()` call
or the negation of one. The first possibility is to use match expressions, e.g.
`?Not(Atom(x)) <+ ?Atom(x)`. However, as that can run into issues with the scoping
of the variable `x`, we opted for sticking with the congruences, just using a
strategy that always succeeds in the inner position, namely `id`:
```Stratego
{! ../trans/sec9_1.str terminate: exercise !}
```
**/
test sec9_1_disj-nf_fails [[
(r -> p & q) & p
]] run disj-nf fails
test sec9_1_conj-nf_fails [[
(r -> p & q) & p
]] run conj-nf fails
test sec9_1_disj-nf [[
(!r & p) | (p & q & p)
]] run disj-nf
test sec9_1_conj-nf [[
(!r | p) & (!r | q) & p
]] run conj-nf
test sec9_1_dnf11_ex [[
(r -> p & q) & p
]] run dnf11 to Or(And(Not(Atom("r")),Atom("p")),And(And(Atom("p"),Atom("q")),Atom("p")))
test sec9_1_cnf11_ex [[
(r -> p & q) & p
]] run cnf11 to And(And(Or(Not(Atom("r")), Atom("p")),
Or(Not(Atom("r")), Atom("q"))),
Atom("p"))
/** md
Also note that the repository does not include `prop-dnf12` because at this
point the strategies there have become identical to the ones
in {! ../docrefs/sec6.2.md !} used to introduce the idea of splitting
transformations into local rewrite rules applied via a traversal strategy. So
they have already been implemented and tested above.
On the other hand, in `trans/sec9_1.str`, the repository does contain a
solution for the second exercise in
[Section 9.1.2](http://www.metaborg.org/en/latest/source/langdev/meta/lang/stratego/strategoxt/09-traversal-strategies.html#visiting-one-subterm).
**/
test sec9_1_2_second_exercise_inner_ex [[
!!p | (!!q | r)
]] run inner-soln to Or(Atom("p"), Or(Atom("q"), Atom("r")))
test sec9_1_2_second_exercise_outer_ex [[
!!p | (!!q | r)
]] run outer-soln to Impl(Not(Atom("p")), Impl(Not(Atom("q")), Atom("r")))
/* Note that the actual output of the following test matches
the inner application. However, according to the manual, it would
also be a correct implementation for it to produce
`Impl(Not(Atom("p")), Or(Atom("q"), Atom("r")))`, which could not
result from either outer or inner application.
*/
test sec9_1_2_second_exercise_random_ex [[
!!p | (!!q | r)
]] run random-soln to Atom("x")
test sec9_2_eval_bottomup_ex [[
1 & !(0 | 1)
]] run eval1 to False()
/* This test fails deliberately, to show that topdown evaluation
does not go all the way to completion.
*/
test sec9_2_eval_topdown [[
1 & !(0 | 1)
]] run eval2 to False()
test sec9_2_eval_exercise_bottomup_ex [[
(1 | 0) & (0 | 1)
]] run eval1 to True()
/* Ditto for this one.*/
test sec9_2_eval_exercise_topdown [[
(1 | 0) & (0 | 1)
]] run eval2 to True()
test sec9_2_desugar_topdown_ex [[
p = q
]] run desugar to And(Or(Not(Atom("p")),Atom("q")),Or(Not(Atom("q")),Atom("p")))
/* And ditto for this one. */
test sec9_2_desugar_bottomup [[
p = q
]] run desugar2 to And(Or(Not(Atom("p")),Atom("q")),Or(Not(Atom("q")),Atom("p")))
test sec9_2_impl-nf_repeated_ex [[
p & q
]] run impl-nf1 to Impl(Impl(Atom("p"),Impl(Atom("q"),False())),False())
/* And ditto for this one. */
test sec9_2_impl-nf_once [[
p & q
]] run impl-nf2 to Impl(Impl(Atom("p"),Impl(Atom("q"),False())),False())
/** md
I couldn't think of a natural application of paramorphism in the context of
the Spoofax Propositional Language, so there is no solution provided for the
exercise just before
[Section 9.2.1](http://www.metaborg.org/en/latest/source/langdev/meta/lang/stratego/strategoxt/09-traversal-strategies.html#cascading-transformations).
The rest of Chapter 9 and all of the remaining chapters deal
either with signatures other than SPL or
just with abstract principles, so that concludes the Stratego examples provided
in this repository.
**/

9
trans/prop-dnf10.str Normal file
View File

@ -0,0 +1,9 @@
module prop-dnf10
imports libstrategolib signatures/- prop-laws
strategies
proptr10(s) = Not(s) <+ And(s, s) <+ Or(s, s) <+ Impl(s, s) <+ Eq(s, s)
propbu10(s) = try(proptr10(propbu10(s))); s
strategies
dnf10 = propbu10(try(DN <+ (DefI <+ DefE <+ DMA <+ DMO <+ DAOL <+ DAOR); dnf10))
cnf10 = propbu10(try(DN <+ (DefI <+ DefE <+ DMA <+ DMO <+ DOAL <+ DOAR); cnf10))

6
trans/prop-dnf11.str Normal file
View File

@ -0,0 +1,6 @@
module prop-dnf11
imports libstrategolib prop-laws
strategies
dnf11 = bottomup(try(DN <+ (DefI <+ DefE <+ DMA <+ DMO <+ DAOL <+ DAOR); dnf11))
cnf11 = bottomup(try(DN <+ (DefI <+ DefE <+ DMA <+ DMO <+ DOAL <+ DOAR); cnf11))

14
trans/prop-dnf4.str Normal file
View File

@ -0,0 +1,14 @@
module prop-dnf4
imports libstrategolib prop-laws
rules
dnf4 : True() -> True()
dnf4 : False() -> False()
dnf4 : Atom(x) -> Atom(x)
dnf4 : Not(x) -> <dnfred4> Not (<dnf4>x)
dnf4 : And(x, y) -> <dnfred4> And (<dnf4>x, <dnf4>y)
dnf4 : Or(x, y) -> Or (<dnf4>x, <dnf4>y)
dnf4 : Impl(x, y) -> <dnfred4> Impl(<dnf4>x, <dnf4>y)
dnf4 : Eq(x, y) -> <dnfred4> Eq (<dnf4>x, <dnf4>y)
strategies
dnfred4 = try(DN <+ (DefI <+ DefE <+ DMA <+ DMO <+ DAOL <+ DAOR); dnf4)

12
trans/prop-dnf5.str Normal file
View File

@ -0,0 +1,12 @@
module prop-dnf5
imports libstrategolib prop-laws
rules
dnft5 : Not(x) -> <dnfred5> Not (<dnf5>x)
dnft5 : And(x, y) -> <dnfred5> And (<dnf5>x, <dnf5>y)
dnft5 : Or(x, y) -> Or (<dnf5>x, <dnf5>y)
dnft5 : Impl(x, y) -> <dnfred5> Impl(<dnf5>x, <dnf5>y)
dnft5 : Eq(x, y) -> <dnfred5> Eq (<dnf5>x, <dnf5>y)
strategies
dnf5 = try(dnft5)
dnfred5 = try(DN <+ (DefI <+ DefE <+ DMA <+ DMO <+ DAOL <+ DAOR); dnf5)

12
trans/prop-dnf6.str Normal file
View File

@ -0,0 +1,12 @@
module prop-dnf6
imports libstrategolib prop-laws
rules
dnft6 : Not(x) -> Not (<dnf6>x)
dnft6 : And(x, y) -> And (<dnf6>x, <dnf6>y)
dnft6 : Or(x, y) -> Or (<dnf6>x, <dnf6>y)
dnft6 : Impl(x, y) -> Impl(<dnf6>x, <dnf6>y)
dnft6 : Eq(x, y) -> Eq (<dnf6>x, <dnf6>y)
strategies
dnf6 = try(dnft6); dnfred6
dnfred6 = try(DN <+ (DefI <+ DefE <+ DMA <+ DMO <+ DAOL <+ DAOR); dnf6)

14
trans/prop-dnf7.str Normal file
View File

@ -0,0 +1,14 @@
module prop-dnf7
imports libstrategolib prop-laws
rules
proptr7(s) : Not(x) -> Not (<s>x)
proptr7(s) : And(x, y) -> And (<s>x, <s>y)
proptr7(s) : Or(x, y) -> Or (<s>x, <s>y)
proptr7(s) : Impl(x, y) -> Impl(<s>x, <s>y)
proptr7(s) : Eq(x, y) -> Eq (<s>x, <s>y)
strategies
dnf7 = try(proptr7(dnf7)); dnfred7
dnfred7 = try(DN <+ (DefI <+ DefE <+ DMA <+ DMO <+ DAOL <+ DAOR); dnf7)
cnf7 = try(proptr7(cnf7)); cnfred7
cnfred7 = try(DN <+ (DefI <+ DefE <+ DMA <+ DMO <+ DOAL <+ DOAR); cnf7)

10
trans/prop-dnf8.str Normal file
View File

@ -0,0 +1,10 @@
module prop-dnf8
imports libstrategolib prop-laws prop-dnf7
strategies
propbu8(s) = try(proptr7(propbu8(s))); s
strategies
dnf8 = propbu8(dnfred8)
dnfred8 = try(DN <+ (DefI <+ DefE <+ DMA <+ DMO <+ DAOL <+ DAOR); dnf8)
cnf8 = propbu8(cnfred8)
cnfred8 = try(DN <+ (DefI <+ DefE <+ DMA <+ DMO <+ DOAL <+ DOAR); cnf8)

6
trans/prop-dnf9.str Normal file
View File

@ -0,0 +1,6 @@
module prop-dnf9
imports libstrategolib prop-laws prop-dnf8
strategies
dnf9 = propbu8(try(DN <+ (DefI <+ DefE <+ DMA <+ DMO <+ DAOL <+ DAOR); dnf9))
cnf9 = propbu8(try(DN <+ (DefI <+ DefE <+ DMA <+ DMO <+ DOAL <+ DOAR); cnf9))

View File

@ -82,7 +82,7 @@ included into the `editor/Main.esv` file, like so:
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
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 !}

View File

@ -18,5 +18,5 @@ rules
strategies
dnf4 = innermost(DefI <+ DefE <+ DAOL <+ DAOR <+ DN <+ DMA <+ DMO)
cnf4 = innermost(DefI <+ DefE <+ DOAL <+ DOAR <+ DN <+ DMA <+ DMO)
dnf-laws = innermost(DefI <+ DefE <+ DAOL <+ DAOR <+ DN <+ DMA <+ DMO)
cnf-laws = innermost(DefI <+ DefE <+ DOAL <+ DOAR <+ DN <+ DMA <+ DMO)

18
trans/sec9_1.str Normal file
View File

@ -0,0 +1,18 @@
module sec9_1
imports libstrategolib signatures/- prop-laws prop-desugar
strategies
conj(s) = And(conj(s), conj(s)) <+ s
disj(s) = Or (disj(s), disj(s)) <+ s
// Conjunctive normal form
conj-nf = conj(disj(Not(Atom(id)) <+ Atom(id)))
// Disjunctive normal form
disj-nf = disj(conj(Not(Atom(id)) <+ Atom(id)))
// Second exercise, Section 9.1.2
path-dep-xform = DN <+ IDefI
inner-soln = repeat(oncebu(path-dep-xform))
outer-soln = repeat(oncetd(path-dep-xform))
random-soln = repeat(rec x(one(x) + path-dep-xform))

14
trans/sec9_2.str Normal file
View File

@ -0,0 +1,14 @@
module sec9_2
imports prop-eval2 prop-desugar
rules
DefT : True() -> Impl(False(), False())
strategies
eval1 = bottomup(try(Eval))
eval2 = topdown(try(Eval))
desugar2 = bottomup(try(DefI <+ DefE))
impl-nf1 = topdown(repeat(DefT <+ DefN <+ DefA2 <+ DefO1 <+ DefE))
impl-nf2 = topdown(try(DefT <+ DefN <+ DefA2 <+ DefO1 <+ DefE))

View File

@ -16,6 +16,16 @@ imports
chap6
chap7
chap8
prop-dnf4
prop-dnf5
prop-dnf6
prop-dnf7
prop-dnf8
prop-dnf9
prop-dnf10
sec9_1
prop-dnf11
sec9_2
rules // Debugging