Skip to content

Commit 9c620b4

Browse files
committed
Add support for user-defined operators
1 parent ed33af8 commit 9c620b4

File tree

3 files changed

+110
-15
lines changed

3 files changed

+110
-15
lines changed

src/ASTBuilder.ts

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -533,32 +533,48 @@ export class ASTBuilder
533533
typeName = this.visitTypeName(ctxTypeName)
534534
}
535535

536-
const isGlobal = ctx.GlobalKeyword() !== undefined;
536+
const isGlobal = ctx.GlobalKeyword() !== undefined
537+
538+
const usingForObjectCtx = ctx.usingForObject()
539+
540+
const userDefinedTypeNameCtx = usingForObjectCtx.userDefinedTypeName()
537541

538-
// the object of the `usingForDeclaration` can be a single identifier
539-
// (the library name) or a group of functions:
540-
// using Lib for uint;
541-
// using { f } for uint;
542542
let node: AST.UsingForDeclaration
543-
const usingForObject = ctx.usingForObject()
544-
const firstChild = this._toText(usingForObject.getChild(0))
545-
if (firstChild === '{') {
543+
if (userDefinedTypeNameCtx !== undefined) {
544+
// using Lib for ...
546545
node = {
547546
type: 'UsingForDeclaration',
548547
isGlobal,
549548
typeName,
550-
libraryName: null,
551-
functions: usingForObject
552-
.userDefinedTypeName()
553-
.map((x) => this._toText(x)),
549+
libraryName: this._toText(userDefinedTypeNameCtx),
550+
functions: [],
551+
operators: [],
554552
}
555553
} else {
554+
// using { } for ...
555+
const usingForObjectDirectives = usingForObjectCtx.usingForObjectDirective()
556+
const functions: string[] = []
557+
const operators: Array<string | null> = []
558+
559+
for (const usingForObjectDirective of usingForObjectDirectives) {
560+
functions.push(
561+
this._toText(usingForObjectDirective.userDefinedTypeName())
562+
)
563+
const operator = usingForObjectDirective.userDefinableOperators()
564+
if (operator !== undefined) {
565+
operators.push(this._toText(operator))
566+
} else {
567+
operators.push(null)
568+
}
569+
}
570+
556571
node = {
557572
type: 'UsingForDeclaration',
558573
isGlobal,
559574
typeName,
560-
libraryName: this._toText(usingForObject.userDefinedTypeName(0)),
561-
functions: [],
575+
libraryName: null,
576+
functions,
577+
operators,
562578
}
563579
}
564580

@@ -1726,7 +1742,7 @@ export class ASTBuilder
17261742
value: this._toText(ctx.BooleanLiteral()!) === 'true',
17271743
}
17281744

1729-
return this._addMeta(node, ctx);
1745+
return this._addMeta(node, ctx)
17301746
}
17311747

17321748
if (ctx.DecimalNumber()) {

src/ast-types.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,9 @@ export interface UsingForDeclaration extends BaseASTNode {
152152
type: 'UsingForDeclaration'
153153
typeName: TypeName | null
154154
functions: string[]
155+
// for each item in `functions`, the item with the same index in `operators`
156+
// will be the defined operator, or null if it's just an attached function
157+
operators: Array<string | null>
155158
libraryName: string | null
156159
isGlobal: boolean;
157160
}

test/ast.ts

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ describe('AST', () => {
5252
},
5353
libraryName: 'Lib',
5454
functions: [],
55+
operators: [],
5556
})
5657

5758
ast = parseNode('using Lib for *;')
@@ -61,6 +62,7 @@ describe('AST', () => {
6162
typeName: null,
6263
libraryName: 'Lib',
6364
functions: [],
65+
operators: [],
6466
})
6567

6668
ast = parseNode('using Lib for S;')
@@ -73,6 +75,7 @@ describe('AST', () => {
7375
},
7476
libraryName: 'Lib',
7577
functions: [],
78+
operators: [],
7679
})
7780

7881
ast = parseNode('using L.Lib for S;')
@@ -85,6 +88,7 @@ describe('AST', () => {
8588
},
8689
libraryName: 'L.Lib',
8790
functions: [],
91+
operators: [],
8892
})
8993
})
9094

@@ -100,6 +104,7 @@ describe('AST', () => {
100104
},
101105
libraryName: 'Lib',
102106
functions: [],
107+
operators: [],
103108
})
104109

105110
ast = parseNode('using Lib for * global;')
@@ -109,6 +114,7 @@ describe('AST', () => {
109114
typeName: null,
110115
libraryName: 'Lib',
111116
functions: [],
117+
operators: [],
112118
})
113119

114120
ast = parseNode('using Lib for S global;')
@@ -121,6 +127,7 @@ describe('AST', () => {
121127
},
122128
libraryName: 'Lib',
123129
functions: [],
130+
operators: [],
124131
})
125132

126133
ast = parseNode('using L.Lib for S global;')
@@ -133,6 +140,7 @@ describe('AST', () => {
133140
},
134141
libraryName: 'L.Lib',
135142
functions: [],
143+
operators: [],
136144
})
137145
})
138146

@@ -148,6 +156,7 @@ describe('AST', () => {
148156
},
149157
libraryName: null,
150158
functions: ['f'],
159+
operators: [null],
151160
})
152161

153162
ast = parseNode('using { f, g } for uint;')
@@ -161,6 +170,7 @@ describe('AST', () => {
161170
},
162171
libraryName: null,
163172
functions: ['f', 'g'],
173+
operators: [null, null],
164174
})
165175

166176
ast = parseNode('using { f } for *;')
@@ -170,6 +180,7 @@ describe('AST', () => {
170180
typeName: null,
171181
libraryName: null,
172182
functions: ['f'],
183+
operators: [null],
173184
})
174185

175186
ast = parseNode('using { f } for S;')
@@ -182,6 +193,7 @@ describe('AST', () => {
182193
},
183194
libraryName: null,
184195
functions: ['f'],
196+
operators: [null],
185197
})
186198
})
187199

@@ -197,6 +209,7 @@ describe('AST', () => {
197209
},
198210
libraryName: null,
199211
functions: ['f'],
212+
operators: [null],
200213
})
201214

202215
ast = parseNode('using { f, g } for uint global;')
@@ -210,6 +223,7 @@ describe('AST', () => {
210223
},
211224
libraryName: null,
212225
functions: ['f', 'g'],
226+
operators: [null, null],
213227
})
214228

215229
ast = parseNode('using { f } for * global;')
@@ -219,6 +233,7 @@ describe('AST', () => {
219233
typeName: null,
220234
libraryName: null,
221235
functions: ['f'],
236+
operators: [null],
222237
})
223238

224239
ast = parseNode('using { f } for S global;')
@@ -231,6 +246,67 @@ describe('AST', () => {
231246
},
232247
libraryName: null,
233248
functions: ['f'],
249+
operators: [null],
250+
})
251+
})
252+
253+
describe('user-defined operators', function () {
254+
it('defining a single operator', function () {
255+
const ast = parseNode('using { add as + } for Fixed18 global;')
256+
assert.deepEqual(ast, {
257+
type: 'UsingForDeclaration',
258+
isGlobal: true,
259+
typeName: {
260+
type: 'UserDefinedTypeName',
261+
namePath: 'Fixed18',
262+
},
263+
libraryName: null,
264+
functions: ['add'],
265+
operators: ['+'],
266+
})
267+
})
268+
269+
it('defining two operators', function () {
270+
const ast = parseNode('using { add as +, sub as - } for Fixed18 global;')
271+
assert.deepEqual(ast, {
272+
type: 'UsingForDeclaration',
273+
isGlobal: true,
274+
typeName: {
275+
type: 'UserDefinedTypeName',
276+
namePath: 'Fixed18',
277+
},
278+
libraryName: null,
279+
functions: ['add', 'sub'],
280+
operators: ['+', '-'],
281+
})
282+
})
283+
284+
it('mixing attached functions and user-defined operators', function () {
285+
let ast = parseNode('using { add, sub as - } for Fixed18 global;')
286+
assert.deepEqual(ast, {
287+
type: 'UsingForDeclaration',
288+
isGlobal: true,
289+
typeName: {
290+
type: 'UserDefinedTypeName',
291+
namePath: 'Fixed18',
292+
},
293+
libraryName: null,
294+
functions: ['add', 'sub'],
295+
operators: [null, '-'],
296+
})
297+
298+
ast = parseNode('using { add as +, sub } for Fixed18 global;')
299+
assert.deepEqual(ast, {
300+
type: 'UsingForDeclaration',
301+
isGlobal: true,
302+
typeName: {
303+
type: 'UserDefinedTypeName',
304+
namePath: 'Fixed18',
305+
},
306+
libraryName: null,
307+
functions: ['add', 'sub'],
308+
operators: ['+', null],
309+
})
234310
})
235311
})
236312
})

0 commit comments

Comments
 (0)