You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
If you've never made a PR before, here's a basic workflow to follow:
71
95
@@ -116,3 +140,263 @@ Here's a diagram to help you illustrate where everything should take place:
116
140
(5) git add ...
117
141
git commit ...
118
142
```
143
+
144
+
## Development Environment Setup
145
+
146
+
The sslang compiler, sslc, is developed using the [Haskell programming language][haskell], and developed using the following tools:
147
+
148
+
-[GHC][ghc]: haskell compiler
149
+
-[Cabal][cabal]: build system
150
+
-[Stack][stack]: project manager
151
+
-[Haddock][haddock]: code documentation generator (packaged with Stack)
152
+
-[HLS][hls]: [language server][lsp] (optional)
153
+
-[Brittany][brittany]: formatter (optional)
154
+
-[Hlint][hlint]: linter (optional)
155
+
156
+
This section will guide you through setting up your development environnment with these tools. We assume that development will take place in a UNIX-like environment (i.e., macOS, WSL, or some kind of Linux distro). Development in Windows is probably possible but unsupported.
You can easily setup most of sslang's project dependencies using [GHCup][ghcup]<sup>[1](#why-ghcup)</sup>, Haskell's toolchain manager. To do so, run the following command:
This will run a short but interactive script that installs GHC, Cabal, and Stack; make sure to let it know where it should add the `PATH` variable. GHCup will also ask if you would like to install HLS, which you may use to extend your LSP-compatible editor with IDE features (optional).
179
+
180
+
The GHCup setup script may also detect that dependencies are missing, and ask you to install them; make sure to do so before proceeding. For instance, if you are running Ubuntu 20.10:
Package names differ depending on your distro and version, so make sure to read the suggestion.
187
+
188
+
<aname="why-ghcup">Footnote 1</a>: _While you can also directly install Stack and use that to manage GHC versions, GHCup is more specialized toward coordinating versioning for just the core components of the toolchain, i.e., GHC, Cabal, Stack, and HLS. You can read more about its rationale [here](https://www.haskell.org/ghcup/about/#faq)._
189
+
190
+
[ghcup]: https://www.haskell.org/ghcup/
191
+
192
+
### Development Tools Setup (optional)
193
+
194
+
If you are helping develop sslc, you may find it helpful to have [HLint][hlint] and [Brittany][brittany] available. You can install these with Stack:
195
+
196
+
```shell
197
+
stack install hlint
198
+
stack install brittany
199
+
```
200
+
201
+
### Git Alias Setup (optional)
202
+
203
+
Convenience scripts are provided under the [`scripts`](./scripts/) subdirectory, to help lint, format, and build this respoitory's code. As long as your current working directory is within this repo, you may invoke these scripts directly.
204
+
205
+
These scripts may be added as Git aliases for even easier access (e.g., to lint your code, just run `git lint`). They are defined in [`.gitconfig`](./.gitconfig), and can be set up by running the following command inside of this repo:
206
+
207
+
```shell
208
+
git config --local include.path ../.gitconfig
209
+
```
210
+
211
+
Though these convenience aliases are optional, they help outline a recommended command-line workflow that you may wish to follow.
212
+
213
+
[convenience-aliases]: #git-alias-setup-optional
214
+
215
+
## Developing `sslc`
216
+
217
+
### Building sslc
218
+
219
+
You can build sslc by running:
220
+
221
+
```shell
222
+
stack build
223
+
```
224
+
225
+
By default, Stack does not link in the test driver, and needs to recompile everything if you later decide to run tests. To work around this behavior, you can ask Stack to link in tests without running them:
226
+
227
+
```shell
228
+
stack build --test --no-run-tests
229
+
git build # equivalent convenience alias
230
+
```
231
+
232
+
You may also start a build server to continuously watch your filesystem and build as soon as it detects changes, eliminating the need to later run `build` manually:
233
+
234
+
```shell
235
+
git build --test --no-run-tests --file-watch
236
+
git watch # equivalent convenience alias
237
+
```
238
+
239
+
You can also continuously build code documentation, though that takes considerably longer, and is not recommended unless you are actively working on documentation:
git watch --haddock # equivalent convenience alias
244
+
```
245
+
246
+
### Running sslc
247
+
248
+
You may run sslc using:
249
+
250
+
```shell
251
+
stack exec sslc <args..>
252
+
```
253
+
254
+
You can optionally install `sslc` to `~/.local/bin/`, so that you can invoke it directly (as long as `~/.local/bin/` is in your `PATH`):
255
+
256
+
```shell
257
+
stack install
258
+
sslc <args..>
259
+
```
260
+
261
+
All existing tests should be passing before merging a PR, and where appropriate, new tests should be added to demonstrate functionality and correctness of your code. Tests may be run using:
262
+
263
+
```shell
264
+
stack test
265
+
```
266
+
267
+
### Building Code Documentation
268
+
269
+
Code documentation for this compiler is generated using [Haddock][haddock]. You can build the documentation by running:
270
+
271
+
```shell
272
+
stack haddock
273
+
```
274
+
275
+
This generates the documentation in `haddock-out/`, in the form of a navigable static website (similar to what is found on [Hackage](https://hackage.haskell.org/)). You may view the HTML files in there using your browser. You can also ask that Haddock open your browser automatically:
276
+
277
+
```shell
278
+
stack haddock --open
279
+
```
280
+
281
+
The langauge reference manual is maintained separately in the [`doc/`](doc) folder; see build instructions there.
282
+
283
+
### Adding Test Suites
284
+
285
+
All existing tests should be passing before merging a PR, and where appropriate, new tests should be added to demonstrate functionality and correctness of your code. Tests may be run using:
286
+
287
+
```shell
288
+
stack test
289
+
```
290
+
291
+
New test suites can be declared by adding items to the `tests` section in [`package.yaml`](package.yaml). For instance, the scanner test is declared as:
292
+
293
+
```yaml
294
+
tests:
295
+
scanner-test: # Name of test suite
296
+
main: Spec.hs # Name of test entry point module, in source-dirs
297
+
source-dirs:
298
+
- test/scanner # Source directories to include in test target
299
+
dependencies:
300
+
- sslang # Allows us to import modules from sslang
301
+
- hspec # Used for managing unit tests; see below
302
+
# Other tests...
303
+
```
304
+
305
+
To run an individual test, you may specify `sslang:<test-name>` as a parameter to `stack test`. For instance, to run the scanner test:
306
+
307
+
```shell
308
+
stack test sslang:scanner-test
309
+
```
310
+
311
+
By convention, the `main` module of tests for Haskell projects is typically named `Spec.hs`.
312
+
313
+
### Adding Regression Tests
314
+
315
+
Test with, e.g.,
316
+
317
+
````shell
318
+
cd regression-tests && ./runtests.sh
319
+
````
320
+
321
+
TODO: Stephen to write more about `runtests.sh`.
322
+
323
+
### Adding New Unit Tests
324
+
325
+
Unit tests provide access to Haskell modules in a way that is more direct than specifying an additional `--dump` option to the executable CLI, and more persistent than using the GHCi REPL. Note, however, that unit tests do not supplant regression tests; code should not be considered tested until it is covered by some regression test. Rather, unit tests provide a way to run your code before you fully integrate it with the rest of the compiler. They should be used throughout the development process to directly test and document the isolated behavior of specific compiler components.
326
+
327
+
This project manages unit tests using [Hspec][hspec], which provides a basic test driver interface and a plugin for [automatic test discovery][hspec-discover]. For example, the `scanner-test` example declares an Hspec test. The directory structure under `test/scanner` might look like this:
328
+
329
+
```
330
+
test/
331
+
|_ scanner/
332
+
|_ Spec.hs # main
333
+
|_ Tests/
334
+
|_ ScanBlockSpec.hs
335
+
|_ ScanCommentSpec.hs
336
+
| # etc.
337
+
```
338
+
339
+
`test/scanner/Spec.hs` consists of only the following, which tells the `hspec-discover` plugin to look for tests in all subdirectories:
340
+
341
+
```haskell
342
+
{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
343
+
```
344
+
345
+
Tests must always end in `Spec.hs` to be included by `hspec-discover`. For instance, `ScanCommentSpec.hs` might look like:
The entry point of each test is the `spec :: Spec` function, which allows individual test cases to be specified in a monadic context. The `it` combinator should be used to provide a succinct description of the behavior in prose (which is reported by the test driver on success), while `shouldBe` tests that its left operand (actual) is equal to its right operand (expected) using their `Eq` instances. If a test fails, Hspec reports the expected and actual outputs using their `Show` instances, and highlights any differences.
366
+
367
+
You can also run individual test modules or test cases. For instance, to run only the test cases in `ScanCommentsSpec`, run:
368
+
369
+
```shell
370
+
stack test sslang:scanner-test --ta '--match "/Tests.ScanComments/"'
371
+
```
372
+
373
+
Or to run only a specific test case:
374
+
375
+
```shell
376
+
stack test sslang:scanner-test --ta '--match "/Tests.ScanComments/ignores single-line comments/"'
To keep code on the main branch clean and consistent, you should always make sure to lint (with [Hlint][hlint]) and format (with [Brittany][brittany]) your code before merging any PR. You may invoke Hlint and Brittany manually, but the following [convenience aliases][convenience-aliases] are provided to coordinate with your development workflow.
385
+
386
+
To lint:
387
+
388
+
```shell
389
+
git lint # lint all files modified since HEAD
390
+
git lint [<files..>] # lint specified files
391
+
git lint --since <commit># lint all files modified since <commit>
392
+
git lint --help # show help menu
393
+
```
394
+
395
+
To format:
396
+
397
+
```shell
398
+
git fmt # format all files modified since HEAD
399
+
git fmt [<files..>] # format specified files
400
+
git fmt --since <commit># format all files modified since <commit>
0 commit comments