Skip to content

Commit 779c212

Browse files
committed
New Feature: add matcheeName option
1 parent a541026 commit 779c212

File tree

3 files changed

+170
-142
lines changed

3 files changed

+170
-142
lines changed

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,11 +107,14 @@ export interface Options {
107107
tagName: string
108108
/** the name prefix used for pattern matching functions */
109109
foldName: string
110+
/** the name used for the input of pattern matching functions */
111+
matcheeName: string
110112
}
111113
112114
export const defaultOptions: Options = {
113115
tagName: 'type',
114-
foldName: 'fold'
116+
foldName: 'fold',
117+
matcheeName: 'fa'
115118
}
116119
```
117120

src/ast.ts

Lines changed: 136 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,14 @@ export interface Options {
99
tagName: string
1010
/** the name prefix used for pattern matching functions */
1111
foldName: string
12+
/** the name used for the input of pattern matching functions */
13+
matcheeName: string
1214
}
1315

1416
export const defaultOptions: Options = {
1517
tagName: 'type',
16-
foldName: 'fold'
18+
foldName: 'fold',
19+
matcheeName: 'fa'
1720
}
1821

1922
export interface AST<A> extends Reader<Options, A> {}
@@ -28,7 +31,7 @@ const getType = (t: M.Type): ts.TypeReferenceNode => {
2831

2932
export const data = (d: M.Data): AST<ts.TypeAliasDeclaration> => {
3033
return new Reader(e => {
31-
const type = ts.createUnionTypeNode(
34+
const unionType = ts.createUnionTypeNode(
3235
d.constructors.toArray().map(c => {
3336
const tag: ts.TypeElement = ts.createPropertySignature(
3437
[ts.createModifier(ts.SyntaxKind.ReadonlyKeyword)],
@@ -56,7 +59,7 @@ export const data = (d: M.Data): AST<ts.TypeAliasDeclaration> => {
5659
d.introduction.parameters.map(p =>
5760
ts.createTypeParameterDeclaration(p.name, p.constraint.map(getType).toUndefined())
5861
),
59-
type
62+
unionType
6063
)
6164
})
6265
}
@@ -65,67 +68,96 @@ const getConstructorName = (name: string): string => {
6568
return name.substring(0, 1).toLocaleLowerCase() + name.substring(1)
6669
}
6770

71+
const getNullaryConstructorVariableStatement = (
72+
tagName: string,
73+
c: M.Constructor,
74+
introduction: M.Introduction
75+
): ts.VariableStatement => {
76+
const name = getConstructorName(c.name)
77+
return ts.createVariableStatement(
78+
[ts.createModifier(ts.SyntaxKind.ExportKeyword)],
79+
ts.createVariableDeclarationList(
80+
[
81+
ts.createVariableDeclaration(
82+
name,
83+
ts.createTypeReferenceNode(
84+
introduction.name,
85+
introduction.parameters.map(p =>
86+
p.constraint
87+
.map<ts.TypeNode>(c => getType(c))
88+
.getOrElse(ts.createKeywordTypeNode(ts.SyntaxKind.NeverKeyword))
89+
)
90+
),
91+
ts.createObjectLiteral([ts.createPropertyAssignment(tagName, ts.createStringLiteral(c.name))])
92+
)
93+
],
94+
ts.NodeFlags.Const
95+
)
96+
)
97+
}
98+
99+
const getFunctionDeclaration = (
100+
name: string,
101+
typeParameters: Array<ts.TypeParameterDeclaration>,
102+
parameters: Array<ts.ParameterDeclaration>,
103+
type: ts.TypeNode,
104+
body: ts.Block
105+
) => {
106+
return ts.createFunctionDeclaration(
107+
undefined,
108+
[ts.createModifier(ts.SyntaxKind.ExportKeyword)],
109+
undefined,
110+
name,
111+
typeParameters,
112+
parameters,
113+
type,
114+
body
115+
)
116+
}
117+
118+
const getParameterDeclaration = (name: string, type: ts.TypeNode): ts.ParameterDeclaration => {
119+
return ts.createParameter(undefined, undefined, undefined, name, undefined, type, undefined)
120+
}
121+
122+
const getConstructorFunctionDeclaration = (
123+
tagName: string,
124+
c: M.Constructor,
125+
introduction: M.Introduction
126+
): ts.FunctionDeclaration => {
127+
const name = getConstructorName(c.name)
128+
const typeParameters = introduction.parameters.map(p => {
129+
return ts.createTypeParameterDeclaration(p.name, p.constraint.map(getType).toUndefined())
130+
})
131+
const parameters = c.members.map((m, position) => {
132+
const name = getMemberName(m, position)
133+
const type = getType(m.type)
134+
return getParameterDeclaration(name, type)
135+
})
136+
const type = ts.createTypeReferenceNode(
137+
introduction.name,
138+
introduction.parameters.map(p => ts.createTypeReferenceNode(p.name, []))
139+
)
140+
const body = ts.createBlock([
141+
ts.createReturn(
142+
ts.createObjectLiteral([
143+
ts.createPropertyAssignment(tagName, ts.createStringLiteral(c.name)),
144+
...c.members.map((m, position) => {
145+
const name = getMemberName(m, position)
146+
return ts.createShorthandPropertyAssignment(name)
147+
})
148+
])
149+
)
150+
])
151+
return getFunctionDeclaration(name, typeParameters, parameters, type, body)
152+
}
153+
68154
export const constructors = (d: M.Data): AST<Array<ts.Node>> => {
69155
return new Reader(e => {
70156
return d.constructors.toArray().map(c => {
71-
const name = getConstructorName(c.name)
72157
if (c.members.length === 0) {
73-
return ts.createVariableStatement(
74-
[ts.createModifier(ts.SyntaxKind.ExportKeyword)],
75-
ts.createVariableDeclarationList(
76-
[
77-
ts.createVariableDeclaration(
78-
name,
79-
ts.createTypeReferenceNode(
80-
d.introduction.name,
81-
d.introduction.parameters.map(p =>
82-
p.constraint
83-
.map<ts.TypeNode>(c => getType(c))
84-
.getOrElse(ts.createKeywordTypeNode(ts.SyntaxKind.NeverKeyword))
85-
)
86-
),
87-
ts.createObjectLiteral([ts.createPropertyAssignment(e.tagName, ts.createStringLiteral(c.name))])
88-
)
89-
],
90-
ts.NodeFlags.Const
91-
)
92-
)
158+
return getNullaryConstructorVariableStatement(e.tagName, c, d.introduction)
93159
} else {
94-
return ts.createFunctionDeclaration(
95-
undefined,
96-
[ts.createModifier(ts.SyntaxKind.ExportKeyword)],
97-
undefined,
98-
name,
99-
d.introduction.parameters.map(p => {
100-
return ts.createTypeParameterDeclaration(p.name, p.constraint.map(getType).toUndefined())
101-
}),
102-
c.members.map((m, position) => {
103-
return ts.createParameter(
104-
undefined,
105-
undefined,
106-
undefined,
107-
getMemberName(m, position),
108-
undefined,
109-
getType(m.type),
110-
undefined
111-
)
112-
}),
113-
ts.createTypeReferenceNode(
114-
d.introduction.name,
115-
d.introduction.parameters.map(p => ts.createTypeReferenceNode(p.name, []))
116-
),
117-
ts.createBlock([
118-
ts.createReturn(
119-
ts.createObjectLiteral([
120-
ts.createPropertyAssignment(e.tagName, ts.createStringLiteral(c.name)),
121-
...c.members.map((m, position) => {
122-
const name = getMemberName(m, position)
123-
return ts.createShorthandPropertyAssignment(name)
124-
})
125-
])
126-
)
127-
])
128-
)
160+
return getConstructorFunctionDeclaration(e.tagName, c, d.introduction)
129161
}
130162
})
131163
})
@@ -159,79 +191,54 @@ const getHandlerName = (c: M.Constructor): string => {
159191

160192
const getFold = (d: M.Data, name: string, isEager: boolean): AST<ts.FunctionDeclaration> => {
161193
return new Reader(e => {
194+
const matcheeName = e.matcheeName
162195
const returnTypeParameterName = getFoldReturnTypeParameterName(d.introduction)
163-
return ts.createFunctionDeclaration(
164-
undefined,
165-
[ts.createModifier(ts.SyntaxKind.ExportKeyword)],
166-
undefined,
167-
name,
168-
d.introduction.parameters
169-
.concat(M.parameter(returnTypeParameterName, none))
170-
.map(p => ts.createTypeParameterDeclaration(p.name, p.constraint.map(getType).toUndefined())),
171-
[
172-
ts.createParameter(
173-
undefined,
174-
undefined,
175-
undefined,
176-
'fa',
177-
undefined,
178-
ts.createTypeReferenceNode(
179-
d.introduction.name,
180-
d.introduction.parameters.map(p => ts.createTypeReferenceNode(p.name, []))
181-
),
182-
undefined
183-
),
184-
...d.constructors.toArray().map(c => {
185-
return ts.createParameter(
186-
undefined,
187-
undefined,
188-
undefined,
189-
getHandlerName(c),
190-
undefined,
191-
isEager && isNullaryConstructor(c)
192-
? ts.createTypeReferenceNode(returnTypeParameterName, [])
193-
: ts.createFunctionTypeNode(
194-
undefined,
195-
c.members.map((m, position) => {
196-
return ts.createParameter(
197-
undefined,
198-
undefined,
199-
undefined,
200-
getMemberName(m, position),
201-
undefined,
202-
getType(m.type)
196+
const typeParameters = d.introduction.parameters
197+
.concat(M.parameter(returnTypeParameterName, none))
198+
.map(p => ts.createTypeParameterDeclaration(p.name, p.constraint.map(getType).toUndefined()))
199+
const matchee = getParameterDeclaration(
200+
matcheeName,
201+
ts.createTypeReferenceNode(
202+
d.introduction.name,
203+
d.introduction.parameters.map(p => ts.createTypeReferenceNode(p.name, []))
204+
)
205+
)
206+
const handlers = d.constructors.toArray().map(c => {
207+
const type =
208+
isEager && isNullaryConstructor(c)
209+
? ts.createTypeReferenceNode(returnTypeParameterName, [])
210+
: ts.createFunctionTypeNode(
211+
undefined,
212+
c.members.map((m, position) => getParameterDeclaration(getMemberName(m, position), getType(m.type))),
213+
ts.createTypeReferenceNode(returnTypeParameterName, [])
214+
)
215+
return getParameterDeclaration(getHandlerName(c), type)
216+
})
217+
const parameters = [matchee, ...handlers]
218+
const type = ts.createTypeReferenceNode(returnTypeParameterName, [])
219+
const body = ts.createBlock([
220+
ts.createSwitch(
221+
ts.createPropertyAccess(ts.createIdentifier(matcheeName), e.tagName),
222+
ts.createCaseBlock(
223+
d.constructors.toArray().map(c => {
224+
return ts.createCaseClause(ts.createStringLiteral(c.name), [
225+
ts.createReturn(
226+
isEager && isNullaryConstructor(c)
227+
? ts.createIdentifier(getHandlerName(c))
228+
: ts.createCall(
229+
ts.createIdentifier(getHandlerName(c)),
230+
[],
231+
c.members.map((m, position) => {
232+
return ts.createPropertyAccess(ts.createIdentifier(matcheeName), getMemberName(m, position))
233+
})
203234
)
204-
}),
205-
ts.createTypeReferenceNode(returnTypeParameterName, [])
206-
),
207-
undefined
208-
)
209-
})
210-
],
211-
ts.createTypeReferenceNode(returnTypeParameterName, []),
212-
ts.createBlock([
213-
ts.createSwitch(
214-
ts.createPropertyAccess(ts.createIdentifier('fa'), e.tagName),
215-
ts.createCaseBlock(
216-
d.constructors.toArray().map(c => {
217-
return ts.createCaseClause(ts.createStringLiteral(c.name), [
218-
ts.createReturn(
219-
isEager && isNullaryConstructor(c)
220-
? ts.createIdentifier(getHandlerName(c))
221-
: ts.createCall(
222-
ts.createIdentifier(getHandlerName(c)),
223-
[],
224-
c.members.map((m, position) => {
225-
return ts.createPropertyAccess(ts.createIdentifier('fa'), getMemberName(m, position))
226-
})
227-
)
228-
)
229-
])
230-
})
231-
)
235+
)
236+
])
237+
})
232238
)
233-
])
234-
)
239+
)
240+
])
241+
return getFunctionDeclaration(name, typeParameters, parameters, type, body)
235242
})
236243
}
237244

test/printer.ts

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ export function user(name: string, surname: string): User { return { type: "User
220220
it('should handle custom tag names', () => {
221221
const printer = P.print
222222
assert.strictEqual(
223-
printer(H.Option, { tagName: 'tag', foldName: 'fold' }),
223+
printer(H.Option, { tagName: 'tag', foldName: 'fold', matcheeName: 'fa' }),
224224
`export type Option<A> = {
225225
readonly tag: "None";
226226
} | {
@@ -243,24 +243,42 @@ export function foldL<A, R>(fa: Option<A>, onNone: () => R, onSome: (value0: A)
243243
} }`
244244
)
245245
})
246-
})
247246

248-
it('should handle custom fold names', () => {
249-
assertEqual(
250-
P.fold,
251-
H.Option,
252-
[
253-
`export function match<A, R>(fa: Option<A>, onNone: R, onSome: (value0: A) => R): R { switch (fa.type) {
247+
it('should handle custom fold names', () => {
248+
assertEqual(
249+
P.fold,
250+
H.Option,
251+
[
252+
`export function match<A, R>(fa: Option<A>, onNone: R, onSome: (value0: A) => R): R { switch (fa.type) {
254253
case "None": return onNone;
255254
case "Some": return onSome(fa.value0);
256255
} }`,
257-
`export function matchL<A, R>(fa: Option<A>, onNone: () => R, onSome: (value0: A) => R): R { switch (fa.type) {
256+
`export function matchL<A, R>(fa: Option<A>, onNone: () => R, onSome: (value0: A) => R): R { switch (fa.type) {
258257
case "None": return onNone();
259258
case "Some": return onSome(fa.value0);
260259
} }`
261-
],
262-
{ tagName: 'type', foldName: 'match' }
263-
)
260+
],
261+
{ tagName: 'type', foldName: 'match', matcheeName: 'fa' }
262+
)
263+
})
264+
265+
it('should handle custom matchee name', () => {
266+
assertEqual(
267+
P.fold,
268+
H.Option,
269+
[
270+
`export function fold<A, R>(input: Option<A>, onNone: R, onSome: (value0: A) => R): R { switch (input.type) {
271+
case "None": return onNone;
272+
case "Some": return onSome(input.value0);
273+
} }`,
274+
`export function foldL<A, R>(input: Option<A>, onNone: () => R, onSome: (value0: A) => R): R { switch (input.type) {
275+
case "None": return onNone();
276+
case "Some": return onSome(input.value0);
277+
} }`
278+
],
279+
{ tagName: 'type', foldName: 'fold', matcheeName: 'input' }
280+
)
281+
})
264282
})
265283
})
266284
})

0 commit comments

Comments
 (0)