-
Notifications
You must be signed in to change notification settings - Fork 84
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Modularize happy #191
Modularize happy #191
Conversation
0ca1d45
to
a73c187
Compare
@knothed Still trying to find the time to properly sit down and review this, but I wanted to reaffirm I'm very happy to see this PR! |
@Ericson2314 perfect, I‘m quite happily looking forward to hearing your opinion! |
happy.cabal
Outdated
happy-core == 1.21.0, | ||
happy-frontend == 1.21.0, | ||
happy-middleend == 1.21.0, | ||
happy-backend == 1.21.0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should probably maintain a script that changes all these fixed version bounds to e.g. 1.22.0 once we want to release the next version
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could use configure
to do the job and also check in the generated cabal
files. Alternatively just put up with having to grep for and replace 10ish references to the pretty unique version string 1.21.0
...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
generated
cabal
files
Hmm? What is that?
import Control.Monad.IO.Class | ||
import Control.Applicative | ||
|
||
type TypedShell a = MaybeT IO a |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah as long as this doesn't need to change too often I don't care too much.
I didn't review much code, because I trust you on not having modified the core happy logic. Plus, the tests seem to pass, so your refactoring seems to be successful. I quite like the new package organisation, but ultimately it's up to the maintainers to decide. Edit: Although, I wonder: Did CI really run the tests? I can see no output difference between https://travis-ci.org/github/simonmar/happy/jobs/774274988 (where they should run) and https://travis-ci.org/github/simonmar/happy/jobs/774274987 (where they shouldn't run). |
For CI maybe try GitHub workflows. See Alex, which has made the switch, though I think is prefer a manually written one like I attempted in haskell/alex#190 as last I checked haskell-ci didn't support macOS with GitHub workflows yet. |
Ah just merge in #196 and get it working with what you got. Hopefully we can merge that one soon and fix CI so this can be safe to merge. |
Yes. In the second link, the tests have been run normally, while in the first link, |
Hmm... Isn't GHA-based CI an entirely different issue? As I see it, Travis CI seems to work with this PR: https://travis-ci.org/github/simonmar/happy/builds/774274977. And it indeed runs the tests. |
@knothed Oh when I clicked "all checks have passed" Travis was not there, so I assumed it was broken. Nevermind, then. |
OK I would like this to get merged soon :). It is very good and has sat open too long. A few more things
|
If you check the "maintainers can push to PR" box I can help fix conflicts. |
Also piknotech#1 |
Nice, I'd also like that :)
Yes we could do that. This would however require all of
Sounds good - what would you propose?
Bootstrapping is a frontend-specific thing, and the frontend exposes Using |
I'm told windows support for symlinks is continuously improving. And I think git had some tricks even before that, so that's fine with me. |
I guess first I would need to understand how/why your happy rad still uses regular |
Well |
My |
Should we remove the TODO file? It seems outdated. |
Oh! So it could be skipped to force them into the rad backend? That's great news. |
Could you make a Perhaps that is a better splitting than whatever I was thinking of with the attribute grammars :). |
Yes, I‘ve also thought about splitting the GLR backend off. I‘ll do that. It could however be that the GLR backend has a dependency to the normal backend, as it uses, I think, some of the normal backend logic. But that shouldn’t be a problem – the normal backend (obviously) doesn’t depend on the GLR backend. |
Exactly! |
Extend option parsing to allow different packages to have the same options
Co-authored-by: John Ericson <git@JohnEricson.me>
65f5d1d
to
08a53f2
Compare
I decided for the following path:
Changing all the CLI stuff at once and at the end is the most convenient, both for doing so and for reviewing. @int-index The first MR is #200 which can be reviewed now. Maybe it makes more sense to merge all the MRs into an intermediate branch and then merge that branch into master only at the end. Maybe someone could create a branch (e.g. |
- Put "mapDollarDollar" in Grammar - Put "die" and "dieHappy" in Happy.CLI.Dying - Put the other stuff where it is used.
I've removed GenUtils and distributed its members to sensible places:
|
765be70
to
b6def85
Compare
Following #167 (comment) and #187, this PR aims to split up
happy
into several components. Two main goals are accomplished:happy
like frontend, middleend and backend.happy
or to create and distribute new, possibly experimental,happy
-features and -components (like a TemplateHaskell-frontend or a recursive ascent-descent-backend).Ideally,
happy
programmers should be able to combinehappy
packages almost arbitrarily: Each frontend should, in theory, be able to work with each middleend, which in turn should work with each backend.To achieve this goal, we (@sgraf812 and me) have decided to split
happy
into six packages – five libraries and one executable – as follows:happy-frontend
is responsible for reading .y-files and parsing them into theGrammar
datatype.happy-middleend
transforms theGrammar
into anActionTable
and aGotoTable
.happy-backend
takes the these tables and generates template-based Haskell code.happy-core
defines the interfaces (IRs) between the frontend, middleend and backend packages. Also, it contains core utilities like option parsing.happy-test
hosts the test files and contains logic to execute the test suite.happy
itself just puts the above packages together and contains little additional logic.Why six packages?
We want users to be able to combine different frontend-, middleend- and backend-packages together. Therefore,
happy-core
is vital as it defines data types that every such package should be able to work with; in particularGrammar
,ActionTable
andGotoTable
.We'll look at
happy-test
shortly.How does a package look like?
The three packages
happy-frontend
,happy-middleend
andhappy-backend
are all built similarly: they have a publicrunFrontend
/runMiddleend
/runBackend
function which performs the package's main task.Also, they have a common interface for providing and parsing command line arguments.
Let's take a look at
happy–backend
(packages/backend). It has the following exposed components:A main entry point function,
runBackend
, which performs the code-gen:BackendArgs
are user-provided arguments, whileGrammar
,ActionTable
andGotoTable
come from the frontend and middleend (as results ofrunFrontend
andrunMiddleend
).happy
could callrunBackend
with arbitraryBackendArgs
. There is no requirement for a command line interface per se.But ultimately, we do want to perform
happy
via a CLI, meaning we want to createBackendArgs
from parsing the command line arguments. Just as previously inhappy
, we define the CLI arguments using GetOpt, but on package-level:So, each package defines its own CLI
Flags
andoptions
and, in addition, aparseFlags
function which converts these CLI flags intoPackageArgs
.Then, option parsing takes place as follows:
happy
executable sticks theoptions
from each package together and callsgetOpt
.Flag
s are then used to callparseFlags
and execute each package's respective main entry point function:runFrontend
,runMiddleend
andrunBackend
.In this way, option parsing is fully modularized such that new packages can define and use their own CLI options.
We just have to pay attention to one thing: with getOpt, it is not possible to have multiple options with the same option character or option string. This means, if there are two different packages which both define an
-o
option, these cannot be used together in the samehappy
executable. Package authors have to watch out for such option collisions.Example: happy-rad
As an example for how this modularization can be used to create a new executable, let's take a look at happy-rad.
happy-rad
extendshappy
by a recursive ascent-descent (RAD) backend. It contains two packages:rad-backend
: a backend which produces RAD code. This package is completely independent ofhappy-backend
. Still, it works with the same data types (Grammar
,ActionTable
,GotoTable
) ashappy-backend
.happy-rad
: it depends (a.o.) onhappy-frontend
,happy-middleend
,happy-backend
(which are remote packages) andrad-backend
(local package). First, frontend and middleend are executed normally. Then, depending on whether the--rad
option is set, eitherrad-backend
orhappy-backend
is invoked.As
happy-frontend
,happy-middleend
andhappy-backend
are (will be) remote packages, we can just reuse their functionality. In addition, we get testing for free:testing happy and happy-rad
How does the
happy-test
package help package authors in testing their packages?We've moved all of the existing 26 test grammars (e.g. ParGF.y) into
happy-test
.happy-test
exposes the following function:In their test suite, the package author can call
test
and customize the testing procedure: they can specify which of the 26 grammars should or should not be tested and which argument combinations their executable should be tested with. In addition, they can specify further, custom, testing grammars.For example,
happy-rad
could provide[--rad]
for the set of arguments it should be tested with, while normalhappy
could provide[-a, -ag, -cg, -acg]
.happy-test
can also do something different: The functionality ofmake sdist-test
, which was previously in the Makefile, has now moved intohappy-test
. This way, every extension author can test their sdists. What doesmake sdist-test
do?cabal sdist all
to create an sdist of all local packagesMore
For the end user of
happy
, not much changes: the CLI stays identical, the functionality remains the same. Just the compile time ofhappy
increases a bit.Template Files
As the template files are backend-specific, these are now data-files of
happy-backend
, not ofhappy
itself. Just like the tests, which are data-files ofhappy-test
instead ofhappy
itself.Bootstrapping
Bootstrapping is a process which is purely frontend-specific. Therefore, the bootstrap flag only exists in
happy-frontend
, but still exists. A next step could be to outsource the bootstrapping parser into its own package, as described in #187 (comment).CI
Travis and appveyor used
make sdist; make sdist-test-only
. We have replaced this with our newmake sdist-test
.make sdist-test
currently doesn't work on MinGW.