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-by-name [ H{ } clone ]
16 INITIALIZED-SYMBOL: c-defs-by-order [ H{ } clone ]
17 INITIALIZED-SYMBOL: c-forms [ H{ } clone ]
18 INITIALIZED-SYMBOL: child-forms [ H{ } clone ]
19 INITIALIZED-SYMBOL: unnamed-table [ H{ } clone ]
20 INITIALIZED-SYMBOL: deferred-set [ HS{ } clone ]
21 INITIALIZED-SYMBOL: out-forms [ H{ } clone ]
22 INITIALIZED-SYMBOL: out-forms-written [ HS{ } clone ]
23 INITIALIZED-SYMBOL: out-form-counter [ 0 ]
26 GENERIC: def>out-form ( obj -- string )
28 : save-out-form ( string -- )
30 dup out-forms-written get-global in? [
34 out-form-counter counter
35 out-forms get-global set-at
37 [ out-forms-written get-global adjoin ] bi
41 ! some forms must be defined out of order, e.g. anonymous unions/structs
42 : def>out-forms ( obj -- )
43 def>out-form save-out-form ;
45 : peek-current-form ( -- n )
46 c-forms get-global ?last ; inline
51 : push-child-form ( form -- )
52 ! dup order>> c-defs-by-order get-global set-at ; inline
53 dup parent-order>> child-forms get-global push-at ; inline
55 : with-new-form ( quot -- n )
56 defs-counter counter c-forms get-global push
58 c-forms get-global pop ; inline
60 ERROR: unknown-form name ;
61 GENERIC: print-deferred ( obj -- )
62 M: object print-deferred
63 type>> c-defs-by-name get-global ?at [ def>out-forms ] [ unknown-form ] if ;
65 : maybe-defer ( n -- )
66 dup deferred-set get-global key? [
69 [ deferred-set get-global adjoin ]
73 : unnamed? ( string -- ? ) "(unnamed" swap subseq? ; inline
74 : append-counter ( string counter -- string' ) counter number>string append ; inline
75 ! : record-unnamed ( form-name string -- ) unnamed-table get-global set-at ;
76 : unnamed-exists? ( string -- value/key ? ) unnamed-table get-global ?at ; inline
77 : lookup-unnamed ( type string -- type-name )
81 [ \ unnamed-counter append-counter ] dip
83 "RECORDING: " gwrite dup g... gflush
84 [ unnamed-table get-global set-at ] keepd
87 : ?unnamed ( string type -- string' ? )
95 { return-type string }
100 : <c-function> ( return-type name args -- c-function )
105 defs-counter counter >>order ;
112 : <c-struct> ( name order -- c-struct )
122 : <c-union> ( name order -- c-union )
133 : <c-enum> ( name order -- c-enum )
142 { parent-order integer }
145 : <c-arg> ( name type -- c-arg )
149 peek-current-form >>parent-order
150 defs-counter counter >>order ;
156 { parent-order integer }
159 : <c-field> ( name type -- c-field )
163 peek-current-form >>parent-order
164 defs-counter counter >>order ;
172 : <c-typedef> ( type name -- c-typedef )
176 defs-counter counter >>order ;
178 M: c-function def>out-form
181 [ drop "FUNCTION: " ]
182 [ return-type>> " " ]
184 [ args>> dup empty? ")\n" " )\n" ? ]
186 ] "" append-outputs-as ;
188 M: c-typedef def>out-form
189 dup [ type>> ] [ name>> ] bi = [
198 ] "" append-outputs-as
201 ERROR: unknown-child-forms order ;
202 M: c-field def>out-form
209 ] "" append-outputs-as ;
211 : lookup-order ( obj -- order )
212 type>> c-defs-by-name get-global at [ order>> ] ?call -1 or ;
214 : print-defers ( current-order slots -- )
216 tuck lookup-order < [
223 M: c-struct def>out-form
229 order>> dup child-forms get-global ?at [ drop { } ] unless
231 [ nip [ def>out-form ] map "\n" join " ;\n" append ] 2bi
234 ] "" append-outputs-as ;
236 M: c-enum def>out-form
242 order>> dup child-forms get-global ?at [ drop { } ] unless
244 [ nip [ def>out-form ] map "\n" join " ;\n" append ] 2bi
247 ] "" append-outputs-as ;
249 M: c-union def>out-form
252 [ drop "UNION-STRUCT: " ]
255 order>> dup child-forms get-global ?at [ drop { } ] unless
257 [ nip [ def>out-form ] map "\n" join " ;\n" append ] 2bi
260 ] "" append-outputs-as ;
262 M: object def>out-form
263 class-of name>> "unknown object: " prepend ;
265 : reset-c-defs ( -- )
266 0 unnamed-counter set-global
267 0 defs-counter set-global
268 H{ } clone c-defs-by-name set-global
269 H{ } clone c-defs-by-order set-global
270 V{ } clone c-forms set-global
271 H{ } clone child-forms set-global
272 H{ } clone unnamed-table set-global
273 HS{ } clone deferred-set set-global
274 H{ } clone out-forms set-global
275 HS{ } clone out-forms-written set-global
276 0 out-form-counter set-global ;
278 : set-definition ( named -- )
279 [ dup name>> c-defs-by-name get-global set-at ]
280 [ dup order>> c-defs-by-order get-global set-at ] bi ;
282 : clang-get-cstring ( CXString -- string )
283 clang_getCString [ utf8 alien>string ] [ clang_disposeString ] bi ;
285 : trim-blanks ( string -- string' )
286 [ blank? ] trim ; inline
288 : cut-tail ( string quot -- before after ) (trim-tail) cut ; inline
290 : cell-bytes ( -- n )
291 cell-bits 8 /i ; inline
293 : get-tokens ( tokens ntokens -- tokens )
295 _ * swap <displaced-alien>
299 : clang-get-file-max-range ( CXTranslationUnit path -- CXSourceRange )
300 [ dupd clang_getFile 0 clang_getLocationForOffset ]
301 [ dupd [ clang_getFile ] [ nip file-info size>> ] 2bi clang_getLocationForOffset ] 2bi
304 : clang-tokenize ( CXTranslationUnit CXSourceRange -- tokens ntokens )
307 [ clang_tokenize ] 2keep
311 : tokenize-path ( tu path -- tokens ntokens )
312 [ drop ] [ clang-get-file-max-range ] 2bi
315 : tokenize-translation-unit ( CXTranslationUnit -- tokens ntokens )
316 [ ] [ clang_getTranslationUnitCursor clang_getCursorExtent ] bi
319 : tokenize-cursor ( cursor -- tokens ntokens )
320 [ clang_Cursor_getTranslationUnit ] [ clang_getCursorExtent ] bi
323 : dispose-tokens ( cursor tokens ntokens -- )
324 [ clang_Cursor_getTranslationUnit ] 2dip clang_disposeTokens ;
326 :: with-cursor-tokens ( cursor quot: ( tu token -- obj ) -- )
327 cursor clang_Cursor_getTranslationUnit :> tu
328 cursor tokenize-cursor :> ( tokens ntokens )
329 tokens ntokens <iota>
330 cell-bytes :> bytesize
333 [ tu ] 2dip bytesize * swap <displaced-alien> @
335 tu tokens ntokens dispose-tokens ; inline
337 : clang-get-token-spelling ( CXTranslationUnit CXToken -- string )
338 clang_getTokenSpelling clang-get-cstring ;
340 DEFER: cursor>c-struct
341 DEFER: cursor>c-union
343 :: cursor-type ( cursor -- string )
344 cursor clang_getCursorType clang_getTypeSpelling clang-get-cstring
348 [ CHAR: * = ] cut-tail
349 [ [ trim-blanks ] dip append ] when*
353 { [ dup "struct " head? ] [
355 unnamed-table get-global ?at or
358 ! libclang uses two forms for unnamed union (why!?)
359 ! union (unnamed at /Users/erg/factor/elf2.h:39:3)
360 ! union (unnamed union at /Users/erg/factor/elf2.h:39:3)
361 { [ dup "union " head? ] [
363 unnamed-table get-global ?at or
365 { [ dup "_Bool" = ] [ drop "bool" ] }
366 { [ "int8_t" ?head ] [ trim-blanks "char" prepend ] }
367 { [ "int16_t" ?head ] [ trim-blanks "short" prepend ] }
368 { [ "int32_t" ?head ] [ trim-blanks "int" prepend ] }
369 { [ "int64_t" ?head ] [ trim-blanks "longlong" prepend ] }
370 { [ "uint8_t" ?head ] [ trim-blanks "uchar" prepend ] }
371 { [ "uint16_t" ?head ] [ trim-blanks "ushort" prepend ] }
372 { [ "uint32_t" ?head ] [ trim-blanks "uint" prepend ] }
373 { [ "uint64_t" ?head ] [ trim-blanks "ulonglong" prepend ] }
374 { [ "signed char" ?head ] [ trim-blanks "char" prepend ] }
375 { [ "signed short" ?head ] [ trim-blanks "short" prepend ] }
376 { [ "signed int" ?head ] [ trim-blanks "int" prepend ] }
377 { [ "signed long" ?head ] [ trim-blanks "long" prepend ] }
378 { [ "unsigned char" ?head ] [ trim-blanks "uchar" prepend ] }
379 { [ "unsigned short" ?head ] [ trim-blanks "ushort" prepend ] }
380 { [ "unsigned int" ?head ] [ trim-blanks "uint" prepend ] }
381 { [ "unsigned long" ?head ] [ trim-blanks "ulong" prepend ] }
382 { [ dup "(*)" swap subseq? ] [ drop "void*" ] }
386 : cursor-name ( cursor -- string )
387 clang_getCursorSpelling clang-get-cstring "" ?unnamed drop ;
389 : ?cursor-name ( cursor unnamed-type -- string )
390 [ clang_getCursorSpelling clang-get-cstring ] dip ?unnamed drop ;
392 : arg-info ( cursor -- string )
393 [ cursor-type ] [ cursor-name [ "dummy" ] when-empty ] bi " " glue ;
395 : cursor>args ( CXCursor -- args/f )
396 dup clang_Cursor_getNumArguments dup -1 = [
400 clang_Cursor_getArgument
404 : cxprimitive-type>factor ( CXType -- string )
406 { CXType_Bool [ "bool" ] }
407 { CXType_Char_S [ "char" ] }
408 { CXType_Char_U [ "uchar" ] }
409 { CXType_SChar [ "char" ] }
410 { CXType_UChar [ "uchar" ] }
411 { CXType_Short [ "short" ] }
412 { CXType_UShort [ "ushort" ] }
413 { CXType_Int [ "int" ] }
414 { CXType_UInt [ "uint" ] }
415 { CXType_Long [ "long" ] }
416 { CXType_ULong [ "ulong" ] }
417 { CXType_LongLong [ "longlong" ] }
418 { CXType_ULongLong [ "ulonglong" ] }
419 { CXType_Float [ "float" ] }
420 { CXType_Double [ "double" ] }
421 { CXType_Void [ "void" ] }
425 : cxreturn-type>factor ( CXType -- string )
427 { [ dup kind>> CXType_Pointer = ] [
428 clang_getPointeeType dup g... gflush cxreturn-type>factor "*" append
430 { [ dup kind>> CXType_Elaborated = ] [
431 clang_getCanonicalType cxreturn-type>factor
433 { [ dup kind>> CXType_Record = ] [
434 clang_getTypeDeclaration cursor-name
436 { [ dup kind>> CXType_FunctionProto = ] [
437 ! inside a CXType_Pointer, so we get `void*` from that case
440 [ kind>> cxprimitive-type>factor ]
443 : cursor>args-info ( CXCursor -- args-info )
444 cursor>args [ arg-info ] map ", " join ;
446 : cursor>c-function ( CXCursor -- )
447 [ clang_getCursorResultType cxreturn-type>factor ]
449 [ cursor>args-info ] tri <c-function> set-definition ;
451 : cursor>c-typedef ( CXCursor -- )
452 [ clang_getTypedefDeclUnderlyingType cxreturn-type>factor ]
453 [ cursor-name ] bi <c-typedef> set-definition ;
455 : cursor>c-field ( CXCursor -- )
456 [ cursor-name ] [ cursor-type ] bi <c-field> dup g... gflush push-child-form ;
458 DEFER: cursor-visitor
460 : cursor>enum ( CXCursor -- )
462 [ cursor-name ] [ cursor-visitor ] bi
463 f clang_visitChildren drop
464 ] with-new-form <c-enum> set-definition ;
466 : cursor>c-union ( CXCursor -- )
468 "cursor>union start" g...
469 peek-current-form g... gflush
471 [ "Union" ?cursor-name "name: " gwrite dup g... gflush ] keep
472 cursor-visitor f clang_visitChildren drop
474 "cursor>union finish" g... gflush
475 peek-current-form g... gflush
477 <c-union> dup g... gflush set-definition ;
479 : cursor>c-struct ( CXCursor -- )
481 "cursor>c-struct start" g...
482 peek-current-form g... gflush
484 [ "Struct" ?cursor-name ] keep
485 cursor-visitor f clang_visitChildren drop
487 "cursor>c-struct finish" g... gflush
488 peek-current-form g... gflush
490 <c-struct> dup g... gflush set-definition ;
492 : cursor-visitor ( -- callback )
495 dup clang_getCursorKind
496 dup "cursor-visitor got: " gwrite g... gflush
498 { CXCursor_Namespace [ drop CXChildVisit_Recurse ] }
499 { CXCursor_FunctionDecl [ cursor>c-function CXChildVisit_Continue ] }
500 { CXCursor_TypedefDecl [ cursor>c-typedef CXChildVisit_Continue ] }
501 { CXCursor_UnionDecl [ cursor>c-union CXChildVisit_Continue ] }
502 { CXCursor_StructDecl [ cursor>c-struct CXChildVisit_Continue ] }
503 { CXCursor_EnumDecl [ cursor>enum CXChildVisit_Continue ] }
504 { CXCursor_VarDecl [ drop CXChildVisit_Continue ] }
506 { CXCursor_FieldDecl [
507 cursor>c-field CXChildVisit_Continue
509 { CXCursor_EnumConstantDecl [
511 [ clang-get-token-spelling ] with-cursor-tokens
514 clang_getEnumConstantDeclUnsignedValue number>string
516 <c-field> push-child-form
517 CXChildVisit_Continue
520 "cursor-visitor unhandled: " gwrite dup g... gflush
521 2drop CXChildVisit_Recurse
527 : with-clang-index ( quot: ( index -- ) -- )
528 [ 0 0 clang_createIndex ] dip keep clang_disposeIndex ; inline
530 : with-clang-translation-unit ( idx source-file command-line-args nargs unsaved-files nunsaved-files options quot: ( tu -- ) -- )
531 [ enum>number clang_parseTranslationUnit ] dip
532 keep clang_disposeTranslationUnit ; inline
534 : with-clang-default-translation-unit ( path quot: ( tu path -- ) -- )
536 _ f 0 f 0 CXTranslationUnit_None [
538 ] with-clang-translation-unit
539 ] with-clang-index ; inline
541 : with-clang-cursor ( path quot: ( tu path cursor -- ) -- )
543 _ f 0 f 0 CXTranslationUnit_None [
544 _ over clang_getTranslationUnitCursor @
545 ] with-clang-translation-unit
546 ] with-clang-index ; inline
548 ! : parse-c-defines ( path -- )
553 ! cell-bits 8 /i * swap <displaced-alien>
556 ! ] with-clang-default-translation-unit ;
558 : parse-c-exports ( path -- )
560 2nip cursor-visitor f clang_visitChildren drop
561 ] with-clang-cursor ;
563 : write-c-defs ( -- )
564 c-defs-by-order get-global
566 [ def>out-forms ] each
568 sort-keys values [ print ] each ;
570 : parse-include ( path -- )
574 ! [ parse-c-defines ]
581 ! "/Library/Developer/CommandLineTools/SDKs/MacOSX10.15.sdk/usr/include/php/ext/sqlite3/libsqlite/sqlite3.h" parse-include
583 ! "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/15.0.0/include"
585 ! "resource:elf.h" parse-include
588 "resource:elf.h" parse-include
589 c-defs-by-order get-global write-c-defs
591 "resource:elf2.h" parse-include