Skip to content

Commit

Permalink
Merge b0e6f3d into 890f709
Browse files Browse the repository at this point in the history
  • Loading branch information
brettz9 committed Jan 9, 2022
2 parents 890f709 + b0e6f3d commit e64cc8d
Showing 1 changed file with 10 additions and 10 deletions.
20 changes: 10 additions & 10 deletions DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,20 @@ How to add a new parse type

This project works best with a test-driven design. This is a write-up of the neccessary steps.

For example let's say we want to add typescript predicates i.e. `@returns {x is string}`. To do that it makes sense to
For example let's say we want to add TypeScript predicates i.e. `@returns {x is string}`. To do that it makes sense to
follow these steps:

1. Add a new result type. In this case it is a `RootResult`, that means it can appear as a root node of an AST.
1. Add a new result type. In this case it is a `RootResult` which means that it can appear as a root node of an AST.
It is important that we use a type that is not used yet and is prefixed with `JsdocType`. We choose `JsdocTypePredicate`.
A predicate can have to child elements, we call them `left` and `righ`. `left` always has to be a name.
A predicate can have two child elements, we call them `left` and `right`. `left` always has to be a name.
We add the following to the file `src/result/NonRootResult`:
```typescript
export type RootResult =
// ...
| PredicateResult

/**
* A typescript prediciate. Is used in return annotations like this: `@return {x is string}`.
* A TypeScript predicate. Is used in return annotations like this: `@return {x is string}`.
*/
export interface PredicateResult {
type: 'JsdocTypePredicate'
Expand All @@ -25,7 +25,7 @@ export interface PredicateResult {
}
```

2. Add a test. To test we think about an example expression and how we expect it to be parsed. Then we specifiy these
2. Add a test. To test we think about an example expression and how we expect it to be parsed. Then we specify these
in a fixture test and use `testFixture` to do this. There the typing can guide us to fill all required fields.
Documentation about this function can be found in `test/fixtures/Fixture.ts`.
There already exist multiple files for different aspects, but for our case we create a new file
Expand Down Expand Up @@ -56,7 +56,7 @@ describe('typescript predicates', () => {

3. Run the tests. With `npm test` we do a typecheck, linting and the unit tests. To do just the unit tests we can use
`npm run test:spec`. If we run `npm test` we first see that there are multiple problems in the transforms.
4. For the `catharsisTransform` and the `jtpTransform` we can simply add `nonAvailableTransform` as these do not support typescript
4. For the `catharsisTransform` and the `jtpTransform` we can simply add `nonAvailableTransform` as these do not support TypeScript
predicates (see the respective files for examples). The `identityTransform` simply needs to return the same type and the
`stringify` transform should create a valid string output of a given type. These transforms look like this:
```typescript
Expand Down Expand Up @@ -115,9 +115,9 @@ export const predicateParslet = composeParslet({
}
})
```
9. Implement `parseInfix`. Here `parser` is the currently used parser and `left` is the already parsed part. So we assure
9. Implement `parseInfix`. Here `parser` is the currently used parser and `left` is the already parsed part. So we ensure
that `left` is indeed a name. If that is the case we can now safely `consume` the `is` token. With this we tell the parser
that we can continue parsing the next token and then proceed to assemble the ast and recursively continue parsing the `right` part of our
that we can continue parsing the next token and then proceed to assemble the AST and recursively continue parsing the `right` part of our
expression. To ensure that we indeed get a `RootResult` for our right expression we can use the function `assertRoot`.
This prevents us from getting special results like a `KeyValueResult` which is only valid in certain contexts
(for example in object types or function parameter lists). The resulting file looks like this:
Expand All @@ -133,7 +133,7 @@ export const predicateParslet = composeParslet({
accept: type => type === 'is',
parseInfix: (parser, left) => {
if (left.type !== 'JsdocTypeName') {
throw new UnexpectedTypeError(left, 'Predicate always have to have names on the left side.')
throw new UnexpectedTypeError(left, 'Predicates always have to have names on the left side.')
}

parser.consume('is')
Expand All @@ -148,6 +148,6 @@ export const predicateParslet = composeParslet({
```
10. Add parslet to grammar. Now we need to tell the parser that we actually want to use this parslet. For this we add
the parslet to the `typescriptyGrammar` array in `src/grammars/typescriptGrammar.ts.
11. Run tests and debug until done. In the end we see that all tests pass and we are done. We can now add some more tests
11. Run tests and debug until done. In the end we see that all tests pass, and we are done. We can now add some more tests
if we like.
12. If there are any problems with this guide, feel free to open an issue!

0 comments on commit e64cc8d

Please sign in to comment.