1 ! Copyright (C) 2022 Doug Coleman.
2 ! See https://factorcode.org/license.txt for BSD license.
3 USING: accessors alien alien.c-types alien.data alien.enums
4 alien.strings ascii assocs byte-arrays classes classes.struct
5 combinators combinators.short-circuit combinators.smart discord
6 io io.backend io.encodings.utf8 io.files.info kernel layouts
7 libc libclang.ffi make math math.parser multiline namespaces
8 prettyprint sequences sequences.private sets sorting splitting
12 INITIALIZED-SYMBOL: unnamed-counter [ 0 ]
13 INITIALIZED-SYMBOL: defs-counter [ 0 ]
15 INITIALIZED-SYMBOL: c-defs [ H{ } clone ]
16 INITIALIZED-SYMBOL: c-defs-order [ H{ } clone ]
17 INITIALIZED-SYMBOL: c-forms [ V{ } clone ]
18 INITIALIZED-SYMBOL: child-forms [ H{ } clone ]
19 INITIALIZED-SYMBOL: unnamed-table [ H{ } clone ]
20 INITIALIZED-SYMBOL: unnamed-set [ HS{ } clone ]
22 : peek-current-form ( -- n )
23 c-forms get-global ?last ; inline
27 : push-child-form ( form -- )
28 dup parent-order>> child-forms get-global push-at ; inline
30 : with-new-form ( quot -- n )
31 defs-counter counter c-forms get-global push
33 c-forms get-global pop ; inline
35 : ?unnamed ( string type -- string' ? )
36 "(unnamed" pick subseq? [
37 nip [ "Unnamed" \ unnamed-counter counter number>string ] dip glue t
42 : unnamed? ( string -- ? ) "(unnamed" swap subseq? ; inline
43 : set-unnamed ( obj string -- ) unnamed-table get-global set-at ; inline
44 : lookup-unnamed ( string -- type ) unnamed-table get-global at ; inline
46 : record-unnamed ( string -- ) unnamed-set get-global adjoin ;
49 { return-type string }
54 : <c-function> ( return-type name args -- c-function )
59 defs-counter counter >>order ;
66 : <c-struct> ( name order -- c-struct )
76 : <c-union> ( name order -- c-union )
87 : <c-enum> ( name order -- c-enum )
96 { parent-order integer }
99 : <c-arg> ( name type -- c-arg )
103 peek-current-form >>parent-order
104 defs-counter counter >>order ;
110 { parent-order integer }
113 : <c-field> ( name type -- c-field )
117 peek-current-form >>parent-order
118 defs-counter counter >>order ;
126 : <c-typedef> ( type name -- c-typedef )
130 defs-counter counter >>order ;
133 GENERIC: libclang>string ( obj -- string )
135 M: c-function libclang>string
138 [ drop "FUNCTION: " ]
139 [ return-type>> " " ]
141 [ args>> dup empty? ")\n" " )\n" ? ]
143 ] "" append-outputs-as ;
145 M: c-typedef libclang>string
146 dup [ type>> ] [ name>> ] bi = [
155 ] "" append-outputs-as
158 ERROR: unknown-child-forms order ;
159 M: c-field libclang>string
166 ] "" append-outputs-as ;
168 M: c-struct libclang>string
174 order>> child-forms get-global ?at [ drop { } ] unless
175 [ libclang>string ] map "\n" join " ;\n" append
178 ] "" append-outputs-as ;
180 M: c-enum libclang>string
186 order>> child-forms get-global ?at [ unknown-child-forms ] unless
187 [ libclang>string ] map "\n" join " ;\n" append
190 ] "" append-outputs-as ;
192 M: c-union libclang>string
195 [ drop "UNION-STRUCT: " ]
198 order>> child-forms get-global ?at [ unknown-child-forms ] unless
199 [ libclang>string ] map "\n" join " ;\n" append
202 ] "" append-outputs-as ;
204 M: object libclang>string
205 class-of name>> "unknown object: " prepend ;
207 : reset-c-defs ( -- )
208 0 unnamed-counter set-global
209 0 defs-counter set-global
210 H{ } clone c-defs set-global
211 H{ } clone c-defs-order set-global
212 V{ } clone c-forms set-global
213 H{ } clone child-forms set-global
214 H{ } clone unnamed-table set-global
215 HS{ } clone unnamed-set set-global ;
217 : set-definition ( named -- )
218 [ dup name>> c-defs get-global set-at ]
219 [ dup order>> c-defs-order get-global set-at ] bi ;
221 : clang-get-cstring ( CXString -- string )
222 clang_getCString [ utf8 alien>string ] [ clang_disposeString ] bi ;
224 : trim-blanks ( string -- string' )
225 [ blank? ] trim ; inline
227 : cut-tail ( string quot -- before after ) (trim-tail) cut ; inline
229 : cell-bytes ( -- n )
230 cell-bits 8 /i ; inline
232 : get-tokens ( tokens ntokens -- tokens )
234 _ * swap <displaced-alien>
238 : clang-get-file-max-range ( CXTranslationUnit path -- CXSourceRange )
239 [ dupd clang_getFile 0 clang_getLocationForOffset ]
240 [ dupd [ clang_getFile ] [ nip file-info size>> ] 2bi clang_getLocationForOffset ] 2bi
243 : clang-tokenize ( CXTranslationUnit CXSourceRange -- tokens ntokens )
246 [ clang_tokenize ] 2keep
250 : tokenize-path ( tu path -- tokens ntokens )
251 [ drop ] [ clang-get-file-max-range ] 2bi
254 : tokenize-translation-unit ( CXTranslationUnit -- tokens ntokens )
255 [ ] [ clang_getTranslationUnitCursor clang_getCursorExtent ] bi
258 : tokenize-cursor ( cursor -- tokens ntokens )
259 [ clang_Cursor_getTranslationUnit ] [ clang_getCursorExtent ] bi
262 : dispose-tokens ( cursor tokens ntokens -- )
263 [ clang_Cursor_getTranslationUnit ] 2dip clang_disposeTokens ;
265 :: with-cursor-tokens ( cursor quot: ( tu token -- obj ) -- )
266 cursor clang_Cursor_getTranslationUnit :> tu
267 cursor tokenize-cursor :> ( tokens ntokens )
268 tokens ntokens <iota>
269 cell-bytes :> bytesize
272 [ tu ] 2dip bytesize * swap <displaced-alien> @
274 tu tokens ntokens dispose-tokens ; inline
276 : clang-get-token-spelling ( CXTranslationUnit CXToken -- string )
277 clang_getTokenSpelling clang-get-cstring ;
279 DEFER: cursor>c-struct
280 DEFER: cursor>c-union
282 :: cursor-type ( cursor -- string )
285 clang_getTypeSpelling clang-get-cstring
287 dup unnamed? [ dup record-unnamed ] when
291 [ CHAR: * = ] cut-tail
292 [ [ trim-blanks ] dip append ] when*
296 { [ "struct " ?head ] [
299 cursor cursor>c-struct
302 { [ "union " ?head ] [
303 "Union" ?unnamed [ cursor cursor>c-union ] when
305 { [ dup "_Bool" = ] [ drop "bool" ] }
306 { [ "int8_t" ?head ] [ trim-blanks "char" prepend ] }
307 { [ "int16_t" ?head ] [ trim-blanks "short" prepend ] }
308 { [ "int32_t" ?head ] [ trim-blanks "int" prepend ] }
309 { [ "int64_t" ?head ] [ trim-blanks "longlong" prepend ] }
310 { [ "uint8_t" ?head ] [ trim-blanks "uchar" prepend ] }
311 { [ "uint16_t" ?head ] [ trim-blanks "ushort" prepend ] }
312 { [ "uint32_t" ?head ] [ trim-blanks "uint" prepend ] }
313 { [ "uint64_t" ?head ] [ trim-blanks "ulonglong" prepend ] }
314 { [ "signed char" ?head ] [ trim-blanks "char" prepend ] }
315 { [ "signed short" ?head ] [ trim-blanks "short" prepend ] }
316 { [ "signed int" ?head ] [ trim-blanks "int" prepend ] }
317 { [ "signed long" ?head ] [ trim-blanks "long" prepend ] }
318 { [ "unsigned char" ?head ] [ trim-blanks "uchar" prepend ] }
319 { [ "unsigned short" ?head ] [ trim-blanks "ushort" prepend ] }
320 { [ "unsigned int" ?head ] [ trim-blanks "uint" prepend ] }
321 { [ "unsigned long" ?head ] [ trim-blanks "ulong" prepend ] }
322 { [ dup "(*)" swap subseq? ] [ drop "void*" ] }
326 : cursor-name ( cursor -- string )
327 clang_getCursorSpelling clang-get-cstring "" ?unnamed drop ;
329 : ?cursor-name ( cursor unnamed-type -- string )
330 [ clang_getCursorSpelling clang-get-cstring ] dip ?unnamed drop ;
332 : arg-info ( cursor -- string )
333 [ cursor-type ] [ cursor-name [ "dummy" ] when-empty ] bi " " glue ;
335 : cursor>args ( CXCursor -- args/f )
336 dup clang_Cursor_getNumArguments dup -1 = [
340 clang_Cursor_getArgument
344 : cxprimitive-type>factor ( CXType -- string )
346 { CXType_Bool [ "bool" ] }
347 { CXType_Char_S [ "char" ] }
348 { CXType_Char_U [ "uchar" ] }
349 { CXType_SChar [ "char" ] }
350 { CXType_UChar [ "uchar" ] }
351 { CXType_Short [ "short" ] }
352 { CXType_UShort [ "ushort" ] }
353 { CXType_Int [ "int" ] }
354 { CXType_UInt [ "uint" ] }
355 { CXType_Long [ "long" ] }
356 { CXType_ULong [ "ulong" ] }
357 { CXType_LongLong [ "longlong" ] }
358 { CXType_ULongLong [ "ulonglong" ] }
359 { CXType_Float [ "float" ] }
360 { CXType_Double [ "double" ] }
361 { CXType_Void [ "void" ] }
365 : cxreturn-type>factor ( CXType -- string )
367 { [ dup kind>> CXType_Pointer = ] [
368 clang_getPointeeType dup g... gflush cxreturn-type>factor "*" append
370 { [ dup kind>> CXType_Elaborated = ] [
371 clang_getCanonicalType cxreturn-type>factor
373 { [ dup kind>> CXType_Record = ] [
374 clang_getTypeDeclaration cursor-name
376 { [ dup kind>> CXType_FunctionProto = ] [
377 ! inside a CXType_Pointer, so we get `void*` from that case
380 [ kind>> cxprimitive-type>factor ]
383 : cursor>args-info ( CXCursor -- args-info )
384 cursor>args [ arg-info ] map ", " join ;
386 : cursor>c-function ( CXCursor -- )
387 [ clang_getCursorResultType cxreturn-type>factor ]
389 [ cursor>args-info ] tri <c-function> set-definition ;
391 : cursor>c-typedef ( CXCursor -- )
392 [ clang_getTypedefDeclUnderlyingType cxreturn-type>factor ]
393 [ cursor-name ] bi <c-typedef> set-definition ;
395 : cursor>c-field ( CXCursor -- )
396 [ cursor-name ] [ cursor-type ] bi <c-field> dup g... gflush push-child-form ;
398 : struct-visitor ( -- callback )
400 2drop dup clang_getCursorKind
401 "struct-visitor got: " gwrite dup g... gflush
402 peek-current-form g... gflush
404 { CXCursor_FieldDecl [
405 cursor>c-field CXChildVisit_Continue
407 { CXCursor_UnionDecl [
408 ! cursor>c-union CXChildVisit_Continue
409 cursor>c-field CXChildVisit_Continue
411 [ dup g... gflush 2drop CXChildVisit_Recurse ]
415 : cursor>struct ( CXCursor -- )
417 "cursor>struct start" g...
418 peek-current-form g... gflush
421 [ struct-visitor f clang_visitChildren drop ]
423 "cursor>struct finish" g... gflush
424 peek-current-form g... gflush
427 <c-struct> set-definition ;
429 : enum-visitor ( -- callback )
432 dup clang_getCursorKind
434 { CXCursor_EnumConstantDecl [
436 [ clang-get-token-spelling ] with-cursor-tokens
439 clang_getEnumConstantDeclUnsignedValue number>string
441 <c-field> push-child-form
442 CXChildVisit_Continue
444 ! { CXCursor_IntegerLiteral [
446 ! [ clang-get-token-spelling ] with-cursor-tokens
447 ! CXChildVisit_Continue
449 [ "omg unhandled enum case" g... 2dup [ g... ] bi@ 2drop CXChildVisit_Recurse ]
454 : cursor>enum ( CXCursor -- )
456 [ cursor-name ] [ enum-visitor ] bi
457 f clang_visitChildren drop
458 ] with-new-form <c-enum> set-definition ;
460 : union-visitor ( -- callback )
463 dup clang_getCursorKind
464 dup "union-visitor got: " gwrite g... gflush
466 { CXCursor_FieldDecl [
467 cursor>c-field CXChildVisit_Continue
469 { CXCursor_UnionDecl [
470 "union-visitor union...!" gprint
471 drop CXChildVisit_Continue
473 [ "unhandled union case" g...
476 2drop CXChildVisit_Recurse ]
481 : cursor>c-union ( CXCursor -- )
483 "cursor>c-union start" g...
484 peek-current-form g... gflush
486 [ "Union" ?cursor-name ] keep
487 union-visitor f clang_visitChildren drop
489 "cursor>c-union finish" g... gflush
490 peek-current-form g... gflush
492 <c-union> dup g... gflush set-definition ;
494 : cursor>c-struct ( CXCursor -- )
496 "cursor>c-struct start" g...
497 peek-current-form g... gflush
499 [ "Struct" ?cursor-name ] keep
500 struct-visitor f clang_visitChildren drop
502 "cursor>c-struct finish" g... gflush
503 peek-current-form g... gflush
505 <c-struct> dup g... gflush set-definition ;
507 : cursor-visitor ( -- callback )
510 dup clang_getCursorKind
511 dup "cursor-visitor got: " gwrite g... gflush
513 { CXCursor_Namespace [ drop CXChildVisit_Recurse ] }
514 { CXCursor_FunctionDecl [ cursor>c-function CXChildVisit_Continue ] }
515 { CXCursor_TypedefDecl [ cursor>c-typedef CXChildVisit_Continue ] }
516 { CXCursor_UnionDecl [ cursor>c-union CXChildVisit_Continue ] }
517 { CXCursor_StructDecl [ cursor>struct CXChildVisit_Continue ] }
518 { CXCursor_EnumDecl [ cursor>enum CXChildVisit_Continue ] }
519 { CXCursor_VarDecl [ drop CXChildVisit_Continue ] }
521 "cursor-visitor unhandled: " gwrite dup g... gflush
522 2drop CXChildVisit_Recurse
528 : with-clang-index ( quot: ( index -- ) -- )
529 [ 0 0 clang_createIndex ] dip keep clang_disposeIndex ; inline
531 : with-clang-translation-unit ( idx source-file command-line-args nargs unsaved-files nunsaved-files options quot: ( tu -- ) -- )
532 [ enum>number clang_parseTranslationUnit ] dip
533 keep clang_disposeTranslationUnit ; inline
535 : with-clang-default-translation-unit ( path quot: ( tu path -- ) -- )
537 _ f 0 f 0 CXTranslationUnit_None [
539 ] with-clang-translation-unit
540 ] with-clang-index ; inline
542 : with-clang-cursor ( path quot: ( tu path cursor -- ) -- )
544 _ f 0 f 0 CXTranslationUnit_None [
545 _ over clang_getTranslationUnitCursor @
546 ] with-clang-translation-unit
547 ] with-clang-index ; inline
549 ! : parse-c-defines ( path -- )
554 ! cell-bits 8 /i * swap <displaced-alien>
557 ! ] with-clang-default-translation-unit ;
559 : parse-c-exports ( path -- )
561 2nip cursor-visitor f clang_visitChildren drop
562 ] with-clang-cursor ;
564 : write-c-defs ( -- )
565 c-defs-order get-global
567 [ libclang>string [ print ] unless-empty ] each ;
569 : parse-include ( path -- )
573 ! [ parse-c-defines ]
580 ! "/Library/Developer/CommandLineTools/SDKs/MacOSX10.15.sdk/usr/include/php/ext/sqlite3/libsqlite/sqlite3.h" parse-include
582 ! "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/15.0.0/include"
584 ! "resource:elf.h" parse-include
587 "resource:elf.h" parse-include
588 c-defs-order get-global write-c-defs