@@ -37,12 +37,7 @@ function main() {
37
37
const typedefMatches = matchAll ( TYPEDEF_RE , interfaceFile )
38
38
39
39
if ( command === 'symbols' ) {
40
- const symbols = matches
41
- . map ( match => {
42
- const name = match [ 2 ]
43
- return `_${ name } `
44
- } )
45
- . concat ( '_malloc' , '_free' )
40
+ const symbols = buildSymbols ( matches )
46
41
writeFile ( destination , JSON . stringify ( symbols ) )
47
42
return
48
43
}
@@ -70,32 +65,46 @@ function main() {
70
65
throw new Error ( `Bad command "${ command } ". ${ USAGE } ` )
71
66
}
72
67
73
- const MaybeAsync = 'MaybeAsync('
68
+ // $1: attribute name
69
+ // $2: inner type
70
+ const ATTRIBUTE_REGEX = / ^ ( \w + ) \( ( .+ ) \) $ /
71
+ type Attribute = 'MaybeAsync' | 'AsyncifyOnly'
72
+
73
+ function parseAttributes ( type : string ) {
74
+ let text = type
75
+ let match : RegExpExecArray | null = null
76
+ const attributes = new Set < Attribute > ( )
77
+ while ( ( match = ATTRIBUTE_REGEX . exec ( text . trim ( ) ) ) !== null ) {
78
+ attributes . add ( match [ 1 ] as Attribute )
79
+ text = match [ 2 ]
80
+ }
81
+
82
+ if ( text . includes ( ')' ) ) {
83
+ throw new Error ( `parseAttributes should consume all attributes, but did not: ${ text } ` )
84
+ }
85
+
86
+ return { type : text , attributes }
87
+ }
74
88
75
89
interface ParsedType {
76
90
typescript : string
77
91
ffi : string | null
78
92
ctype : string
79
- async : boolean
93
+ attributes : Set < Attribute >
80
94
}
81
95
82
96
function cTypeToTypescriptType ( ctype : string ) : ParsedType {
97
+ let { type, attributes } = parseAttributes ( ctype )
98
+
83
99
// simplify
84
- let type = ctype
85
100
// remove const: ignored in JS
86
- type = ctype . replace ( / \b c o n s t \b / , '' ) . trim ( )
101
+ type = type . replace ( / \b c o n s t \b / , '' ) . trim ( )
87
102
// collapse spaces (around a *, maybe)
88
103
type = type . split ( ' ' ) . join ( '' )
89
104
90
- let async = false
91
- if ( type . startsWith ( MaybeAsync ) && type . endsWith ( ')' ) ) {
92
- async = true
93
- type = type . slice ( MaybeAsync . length , - 1 )
94
- }
95
-
96
105
// mapping
97
106
if ( type . includes ( 'char*' ) ) {
98
- return { ffi : 'string' , typescript : 'string' , ctype, async }
107
+ return { ffi : 'string' , typescript : 'string' , ctype, attributes }
99
108
}
100
109
101
110
let typescript = type . replace ( / \* / g, 'Pointer' )
@@ -116,7 +125,7 @@ function cTypeToTypescriptType(ctype: string): ParsedType {
116
125
ffi = 'number'
117
126
}
118
127
119
- return { typescript : typescript , ffi, ctype, async }
128
+ return { typescript, ffi, ctype, attributes }
120
129
}
121
130
122
131
function renderFunction ( args : {
@@ -137,16 +146,16 @@ function renderFunction(args: {
137
146
} )
138
147
. join ( ', ' )
139
148
140
- const forceSync = ASYNCIFY && ! async && returnType . async
141
- const markAsync = async && returnType . async
149
+ const forceSync = ASYNCIFY && ! async && returnType . attributes . has ( 'MaybeAsync' )
150
+ const markAsync = async && returnType . attributes . has ( 'MaybeAsync' )
142
151
143
152
let typescriptFunctionName = functionName
144
153
if ( markAsync ) {
145
154
typescriptFunctionName += '_MaybeAsync'
146
155
}
147
156
148
157
const typescriptReturnType =
149
- async && returnType . async
158
+ async && returnType . attributes . has ( 'MaybeAsync' )
150
159
? `${ returnType . typescript } | Promise<${ returnType . typescript } >`
151
160
: returnType . typescript
152
161
const typescriptFunctionType = `(${ typescriptParams } ) => ${ typescriptReturnType } `
@@ -179,6 +188,17 @@ function renderFunction(args: {
179
188
return ` ${ typescriptFunctionName } : ${ typescriptFunctionType } =\n ${ cwrap } `
180
189
}
181
190
191
+ function buildSymbols ( matches : RegExpMatchArray [ ] ) {
192
+ const parsed = matches . map ( match => {
193
+ const [ , returnType , functionName , , rawParams ] = match
194
+ const params = parseParams ( rawParams )
195
+ return { functionName, returnType : cTypeToTypescriptType ( returnType . trim ( ) ) , params }
196
+ } )
197
+ const filtered = parsed . filter ( fn => ! fn . returnType . attributes . has ( 'AsyncifyOnly' ) || ASYNCIFY )
198
+ const names = filtered . map ( fn => '_' + fn . functionName )
199
+ return names . concat ( '_malloc' , '_free' )
200
+ }
201
+
182
202
function buildFFI ( matches : RegExpExecArray [ ] ) {
183
203
const parsed = matches . map ( match => {
184
204
const [ , returnType , functionName , , rawParams ] = match
@@ -187,8 +207,11 @@ function buildFFI(matches: RegExpExecArray[]) {
187
207
} )
188
208
const decls : string [ ] = [ ]
189
209
parsed . forEach ( fn => {
190
- decls . push ( renderFunction ( { ...fn , async : false } ) )
191
- if ( fn . returnType . async && ASYNCIFY ) {
210
+ if ( ! fn . returnType . attributes . has ( 'AsyncifyOnly' ) || ASYNCIFY ) {
211
+ decls . push ( renderFunction ( { ...fn , async : false } ) )
212
+ }
213
+
214
+ if ( fn . returnType . attributes . has ( 'MaybeAsync' ) && ASYNCIFY ) {
192
215
decls . push ( renderFunction ( { ...fn , async : true } ) )
193
216
}
194
217
} )
0 commit comments