├── tests ├── config.nims ├── test.nim └── generated │ ├── test.h │ ├── internal.nim │ ├── test.hpp │ ├── test.nim │ ├── test.zig │ ├── test.js │ └── test.py ├── docs └── gennyBanner.png ├── .gitignore ├── genny.nimble ├── .github └── workflows │ ├── build.yml │ └── docs.yml ├── LICENSE ├── src ├── genny │ ├── common.nim │ ├── languages │ │ ├── c.nim │ │ ├── zig.nim │ │ ├── nim.nim │ │ ├── cpp.nim │ │ ├── node.nim │ │ └── python.nim │ └── internal.nim └── genny.nim └── README.md /tests/config.nims: -------------------------------------------------------------------------------- 1 | --path:"../src" 2 | -------------------------------------------------------------------------------- /docs/gennyBanner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/treeform/genny/HEAD/docs/gennyBanner.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # ignore files with no extention: 2 | * 3 | !*/ 4 | !*.* 5 | 6 | # normal ignores: 7 | *.exe 8 | nimcache 9 | *.pdb 10 | *.ilk 11 | .* 12 | -------------------------------------------------------------------------------- /genny.nimble: -------------------------------------------------------------------------------- 1 | version = "0.1.1" 2 | author = "Andre von Houck and Ryan Oldenburg" 3 | description = "Generate a shared library and bindings for many languages." 4 | license = "MIT" 5 | 6 | srcDir = "src" 7 | 8 | requires "nim >= 1.4.8" 9 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Github Actions 2 | on: [push, pull_request] 3 | jobs: 4 | build: 5 | strategy: 6 | fail-fast: false 7 | matrix: 8 | os: [ubuntu-latest, windows-latest] 9 | nim-version: [stable] 10 | 11 | runs-on: ${{ matrix.os }} 12 | 13 | steps: 14 | - uses: actions/checkout@v5 15 | - uses: jiro4989/setup-nim-action@v2 16 | with: 17 | repo-token: ${{ secrets.GITHUB_TOKEN }} 18 | nim-version: ${{ matrix.nim-version }} 19 | - run: nimble test --gc:arc -y -d:gennyNim -d:gennyPython -d:gennyNode -d:gennyC -d:gennyCpp -d:gennyZig 20 | - run: nimble test --gc:orc -y -d:gennyNim -d:gennyPython -d:gennyNode -d:gennyC -d:gennyCpp -d:gennyZig 21 | -------------------------------------------------------------------------------- /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | name: docs 2 | on: 3 | push: 4 | branches: 5 | - master 6 | env: 7 | nim-version: 'stable' 8 | nim-src: src/${{ github.event.repository.name }}.nim 9 | deploy-dir: .gh-pages 10 | jobs: 11 | docs: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v3 15 | - uses: jiro4989/setup-nim-action@v1 16 | with: 17 | nim-version: ${{ env.nim-version }} 18 | - run: nimble install -Y 19 | - run: nimble doc --index:on --project --git.url:https://github.com/${{ github.repository }} --git.commit:master --out:${{ env.deploy-dir }} ${{ env.nim-src }} -d:gennyNim 20 | - name: "Copy to index.html" 21 | run: cp ${{ env.deploy-dir }}/${{ github.event.repository.name }}.html ${{ env.deploy-dir }}/index.html 22 | - name: Deploy documents 23 | uses: peaceiris/actions-gh-pages@v3 24 | with: 25 | github_token: ${{ secrets.GITHUB_TOKEN }} 26 | publish_dir: ${{ env.deploy-dir }} 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2021 Andre von Houck and Ryan Oldenburg 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /src/genny/common.nim: -------------------------------------------------------------------------------- 1 | import macros, strformat, strutils 2 | 3 | const basicTypes* = [ 4 | "bool", 5 | "int8", 6 | "uint8", 7 | "int16", 8 | "uint16", 9 | "int32", 10 | "uint32", 11 | "int64", 12 | "uint64", 13 | "int", 14 | "uint", 15 | "float32", 16 | "float64", 17 | "float" 18 | ] 19 | 20 | proc toSnakeCase*(s: string): string = 21 | ## Converts NimType to nim_type. 22 | var prevCap = false 23 | for i, c in s: 24 | if c in {'A' .. 'Z'}: 25 | if result.len > 0 and result[^1] != '_' and not prevCap: 26 | result.add '_' 27 | prevCap = true 28 | result.add c.toLowerAscii() 29 | else: 30 | prevCap = false 31 | result.add c 32 | 33 | proc toCapSnakeCase*(s: string): string = 34 | ## Converts NimType to NIM_TYPE. 35 | var prevCap = false 36 | for i, c in s: 37 | if c in {'A' .. 'Z'}: 38 | if result.len > 0 and result[^1] != '_' and not prevCap: 39 | result.add '_' 40 | prevCap = true 41 | else: 42 | prevCap = false 43 | result.add c.toUpperAscii() 44 | 45 | proc toCamelCase*(s: string): string = 46 | ## Converts nim_type to NimType. 47 | var cap = true 48 | for i, c in s: 49 | if c == '_': 50 | cap = true 51 | else: 52 | if cap: 53 | result.add c.toUpperAscii() 54 | cap = false 55 | else: 56 | result.add c 57 | 58 | proc toVarCase*(s: string): string = 59 | ## Converts NimType to nimType. 60 | var i = 0 61 | while i < s.len: 62 | if s[i] notin {'A' .. 'Z'}: 63 | break 64 | 65 | result.add s[i].toLowerAscii() 66 | inc i 67 | 68 | if i < s.len: 69 | result.add s[i .. ^1] 70 | 71 | proc getSeqName*(sym: NimNode): string = 72 | if sym.kind == nnkBracketExpr: 73 | result = &"Seq{sym[1]}" 74 | else: 75 | result = &"Seq{sym}" 76 | result[3] = toUpperAscii(result[3]) 77 | 78 | proc getName*(sym: NimNode): string = 79 | if sym.kind == nnkBracketExpr: 80 | sym.getSeqName() 81 | else: 82 | sym.repr 83 | 84 | proc raises*(procSym: NimNode): bool = 85 | for pragma in procSym.getImpl()[4]: 86 | if pragma.kind == nnkExprColonExpr and pragma[0].repr == "raises": 87 | return pragma[1].len > 0 88 | -------------------------------------------------------------------------------- /tests/test.nim: -------------------------------------------------------------------------------- 1 | import genny 2 | 3 | const simpleConst = 123 4 | 5 | exportConsts: 6 | simpleConst 7 | 8 | type SimpleEnum = enum 9 | First 10 | Second 11 | Third 12 | 13 | exportEnums: 14 | SimpleEnum 15 | 16 | proc simpleCall(a: int): int = 17 | ## Returns the integer passed in. 18 | return a 19 | 20 | exportProcs: 21 | simpleCall 22 | 23 | type SimpleObj = object 24 | simpleA*: int 25 | simpleB*: byte 26 | simpleC*: bool 27 | 28 | exportObject SimpleObj: 29 | discard 30 | 31 | type SimpleRefObj* = ref object 32 | simpleRefA*: int 33 | simpleRefB*: byte 34 | simpleRefC*: bool 35 | 36 | proc newSimpleRefObj(): SimpleRefObj = 37 | ## Creates new SimpleRefObj. 38 | SimpleRefObj() 39 | 40 | proc doit*(s: SimpleRefObj) = 41 | ## Does some thing with SimpleRefObj. 42 | echo s.simpleRefA 43 | 44 | exportRefObject SimpleRefObj: 45 | fields: 46 | simpleRefA 47 | simpleRefB 48 | constructor: 49 | newSimpleRefObj() 50 | procs: 51 | doit(SimpleRefObj) 52 | 53 | exportSeq seq[int]: 54 | discard 55 | 56 | type RefObjWithSeq* = ref object 57 | data*: seq[byte] 58 | 59 | proc newRefObjWithSeq(): RefObjWithSeq = 60 | ## Creates new SimpleRefObj. 61 | RefObjWithSeq() 62 | 63 | exportRefObject RefObjWithSeq: 64 | fields: 65 | data 66 | constructor: 67 | newRefObjWithSeq() 68 | 69 | type SimpleObjWithProc = object 70 | simpleA*: int 71 | simpleB*: byte 72 | simpleC*: bool 73 | 74 | proc extraProc(s: SimpleObjWithProc) = 75 | discard 76 | 77 | exportObject SimpleObjWithProc: 78 | procs: 79 | extraProc 80 | 81 | # type ArrayObj = object 82 | # arr1*: array[3, int] 83 | # arr2*: array[3, array[3, int]] 84 | # arr3*: array[3, array[3, array[3, int]]] 85 | 86 | # exportObject ArrayObj: 87 | # discard 88 | 89 | # proc arrayCall1(a: array[2, int]) = 90 | # discard 91 | 92 | # proc arrayCall2(): array[2, int] = 93 | # discard 94 | 95 | # proc arrayCall3(a: array[2, int]): array[2, int] = 96 | # discard 97 | 98 | # proc arrayCall4(a: array[3, array[3, float32]]): array[3, array[3, float32]] = 99 | # discard 100 | 101 | # exportProcs: 102 | # arrayCall1 103 | # arrayCall2 104 | # arrayCall3 105 | # arrayCall4 106 | 107 | proc getDatas(): seq[string] = 108 | @["a", "b", "c"] 109 | 110 | exportSeq seq[string]: 111 | discard 112 | 113 | exportProcs: 114 | getDatas 115 | 116 | writeFiles("tests/generated", "test") 117 | 118 | include generated/internal 119 | -------------------------------------------------------------------------------- /tests/generated/test.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_TEST_H 2 | #define INCLUDE_TEST_H 3 | 4 | #define SIMPLE_CONST 123 5 | 6 | typedef char SimpleEnum; 7 | #define FIRST 0 8 | #define SECOND 1 9 | #define THIRD 2 10 | 11 | typedef struct SimpleObj { 12 | long long simple_a; 13 | char simple_b; 14 | char simple_c; 15 | } SimpleObj; 16 | 17 | typedef long long SimpleRefObj; 18 | 19 | typedef long long SeqInt; 20 | 21 | typedef long long RefObjWithSeq; 22 | 23 | typedef struct SimpleObjWithProc { 24 | long long simple_a; 25 | char simple_b; 26 | char simple_c; 27 | } SimpleObjWithProc; 28 | 29 | typedef long long SeqString; 30 | 31 | /** 32 | * Returns the integer passed in. 33 | */ 34 | long long test_simple_call(long long a); 35 | 36 | SimpleObj test_simple_obj(long long simple_a, char simple_b, char simple_c); 37 | 38 | char test_simple_obj_eq(SimpleObj a, SimpleObj b); 39 | 40 | void test_simple_ref_obj_unref(SimpleRefObj simple_ref_obj); 41 | 42 | SimpleRefObj test_new_simple_ref_obj(); 43 | 44 | long long test_simple_ref_obj_get_simple_ref_a(SimpleRefObj simple_ref_obj); 45 | 46 | void test_simple_ref_obj_set_simple_ref_a(SimpleRefObj simple_ref_obj, long long value); 47 | 48 | char test_simple_ref_obj_get_simple_ref_b(SimpleRefObj simple_ref_obj); 49 | 50 | void test_simple_ref_obj_set_simple_ref_b(SimpleRefObj simple_ref_obj, char value); 51 | 52 | /** 53 | * Does some thing with SimpleRefObj. 54 | */ 55 | void test_simple_ref_obj_doit(SimpleRefObj s); 56 | 57 | void test_seq_int_unref(SeqInt seq_int); 58 | 59 | SeqInt test_new_seq_int(); 60 | 61 | long long test_seq_int_len(SeqInt seq_int); 62 | 63 | long long test_seq_int_get(SeqInt seq_int, long long index); 64 | 65 | void test_seq_int_set(SeqInt seq_int, long long index, long long value); 66 | 67 | void test_seq_int_delete(SeqInt seq_int, long long index); 68 | 69 | void test_seq_int_add(SeqInt seq_int, long long value); 70 | 71 | void test_seq_int_clear(SeqInt seq_int); 72 | 73 | void test_ref_obj_with_seq_unref(RefObjWithSeq ref_obj_with_seq); 74 | 75 | RefObjWithSeq test_new_ref_obj_with_seq(); 76 | 77 | long long test_ref_obj_with_seq_data_len(RefObjWithSeq ref_obj_with_seq); 78 | 79 | char test_ref_obj_with_seq_data_get(RefObjWithSeq ref_obj_with_seq, long long index); 80 | 81 | void test_ref_obj_with_seq_data_set(RefObjWithSeq ref_obj_with_seq, long long index, char value); 82 | 83 | void test_ref_obj_with_seq_data_delete(RefObjWithSeq ref_obj_with_seq, long long index); 84 | 85 | void test_ref_obj_with_seq_data_add(RefObjWithSeq ref_obj_with_seq, char value); 86 | 87 | void test_ref_obj_with_seq_data_clear(RefObjWithSeq ref_obj_with_seq); 88 | 89 | SimpleObjWithProc test_simple_obj_with_proc(long long simple_a, char simple_b, char simple_c); 90 | 91 | char test_simple_obj_with_proc_eq(SimpleObjWithProc a, SimpleObjWithProc b); 92 | 93 | void test_simple_obj_with_proc_extra_proc(SimpleObjWithProc s); 94 | 95 | void test_seq_string_unref(SeqString seq_string); 96 | 97 | SeqString test_new_seq_string(); 98 | 99 | long long test_seq_string_len(SeqString seq_string); 100 | 101 | char* test_seq_string_get(SeqString seq_string, long long index); 102 | 103 | void test_seq_string_set(SeqString seq_string, long long index, char* value); 104 | 105 | void test_seq_string_delete(SeqString seq_string, long long index); 106 | 107 | void test_seq_string_add(SeqString seq_string, char* value); 108 | 109 | void test_seq_string_clear(SeqString seq_string); 110 | 111 | SeqString test_get_datas(); 112 | 113 | #endif 114 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Genny - Generate Nim library bindings for many languages 4 | 5 | So you made a cool Nim library but you want it to be available to other languages as well. With `genny` you can generate a dynamically linked library with a simple C API and generated bindings for many languages. In some ways its similar to [SWIG](http://www.swig.org/) project for C or [djinni](https://github.com/dropbox/djinni) for C++. 6 | 7 | 8 | `nimble install genny` 9 | 10 | ![Github Actions](https://github.com/treeform/genny/workflows/Github%20Actions/badge.svg) 11 | 12 | [API reference](https://treeform.github.io/genny) 13 | 14 | This library has no dependencies other than the Nim standard library. 15 | 16 | 17 | See [Pixie's nimble file](https://github.com/treeform/pixie/blob/master/pixie.nimble#L16) for an example of how to compile bindings with `genny`. 18 | 19 | ## Supported features and languages: 20 | 21 | Language | Method | Enums | Objects | Ref Objects | Seqs | GC | 22 | ------------- | ------------- | ------ | ------- | ----------- | ------ | -- | 23 | Nim | {.importc.} | ✅ | ✅ | ✅ | ✅ | ✅ | 24 | Python | ctypes | ✅ | ✅ | ✅ | ✅ | ✅ | 25 | Node.js | ffi-napi | ✅ | ✅ | ✅ | ✅ | no | 26 | C | .h | ✅ | ✅ | ✅ | ✅ | no | 27 | 28 | ## Quest for "nice" language binding. 29 | 30 | It would be pretty easy to just export a C API from Nim and have users call the ugly C style methods but that is not our goal with Genny. Instead, we try to generate a "nice" API for each language that feels like it was custom-made. This means, where possible, we: 31 | 32 | * Use naming conventions that are familiar in the language (CamelCase, Snake_Case or Kebab-Case). 33 | * Make sure regular `object`s are passed by value and behave simply. 34 | * Make `ref object`s behave like OOP objects with members, methods and constructors. 35 | * Generate helper methods like `==` or `isNull`. 36 | * Export `seq[X]` as something that feels like a native array. 37 | * Export `seq[X]` on a `ref object` behaves like what we call a bound-seq. 38 | * Support the `[]` syntax. 39 | * Support the `.` member syntax. 40 | * Overload math operators `+`, `-`, `*`, `/`. 41 | * Overload `proc`s, where we first unpack them to different C calls with unique prefixes and then repack them back into overloaded methods or functions. 42 | * Pass optional arguments. 43 | * Pass enums and constants. 44 | * Synchronize the binding language's GC and Nim's ARC GC. 45 | * And even copy the comments so that automated tools can use them. 46 | 47 | ## A bindings interface DSL 48 | 49 | Genny provides a DSL that you can use to define how things need to be exported. The DSL is pretty simple to follow: 50 | 51 | ```nim 52 | import genny, pixie 53 | 54 | exportConsts: 55 | defaultMiterLimit 56 | autoLineHeight 57 | 58 | exportEnums: 59 | FileFormat 60 | BlendMode 61 | 62 | exportProcs: 63 | readImage 64 | readmask 65 | readTypeface 66 | 67 | exportObject Matrix3: 68 | constructor: 69 | matrix3 70 | procs: 71 | mul(Matrix3, Matrix3) 72 | 73 | exportRefObject Mask: 74 | fields: 75 | width 76 | height 77 | constructor: 78 | newMask(int, int) 79 | procs: 80 | writeFile(Mask, string) 81 | copy(Mask) 82 | getValue 83 | setValue 84 | 85 | # Must have this at the end. 86 | writeFiles("bindings/generated", "pixie") 87 | include generated/internal 88 | ``` 89 | 90 | See more in the [pixie bindings](https://github.com/treeform/pixie/blob/master/bindings/bindings.nim) 91 | 92 | ## Genny is experimental and opinionated 93 | 94 | Genny generates a dynamic library C API for your Nim library and generates bindings for that dynamic library in many languages. To do this, things like proc overloads, complex types, sequences, and many other Nim features need to be addressed to make them work over a C interface. 95 | 96 | To make that C interface, Genny makes assumptions about what your Nim source looks like and how to give overloaded procedures unique names. This may not work out of the box for every Nim library yet! 97 | 98 | ## Example uses 99 | 100 | This version of Genny was created to generate bindings for [Pixie](https://github.com/treeform/pixie). You can see how Pixie's dynamic library API is exported and the bindings generated [in this file](https://github.com/treeform/pixie/blob/master/bindings/bindings.nim) and the [results here](https://github.com/treeform/pixie-python). 101 | 102 | ## Nim is great, why other languages? 103 | 104 | Nim is a niche language. We believe we can broaden Nim's appeal by creating Nim libraries for other more popular language and have Nim slowly work its way into companies. Maybe after companies see that they already use Nim, they will start writing their own code in it. 105 | 106 | ## Why add Nim binding support for a Nim library? 107 | 108 | "Can't you just import your cool library in Nim?" We thought it was important to test the library in a what we call Nim-C-Nim sandwich. It makes sure everyone uses your library API the same way. This also means you could ship huge Nim libraries as DLLs and use them in your Nim programs without recompiling everything every time. 109 | -------------------------------------------------------------------------------- /tests/generated/internal.nim: -------------------------------------------------------------------------------- 1 | when not defined(gcArc) and not defined(gcOrc): 2 | {.error: "Please use --gc:arc or --gc:orc when using Genny.".} 3 | 4 | when (NimMajor, NimMinor, NimPatch) == (1, 6, 2): 5 | {.error: "Nim 1.6.2 not supported with Genny due to FFI issues.".} 6 | proc test_simple_call*(a: int): int {.raises: [], cdecl, exportc, dynlib.} = 7 | simpleCall(a) 8 | 9 | proc test_simple_obj*(simple_a: int, simple_b: byte, simple_c: bool): SimpleObj {.raises: [], cdecl, exportc, dynlib.} = 10 | result.simple_a = simple_a 11 | result.simple_b = simple_b 12 | result.simple_c = simple_c 13 | 14 | proc test_simple_obj_eq*(a, b: SimpleObj): bool {.raises: [], cdecl, exportc, dynlib.}= 15 | a.simple_a == b.simple_a and a.simple_b == b.simple_b and a.simple_c == b.simple_c 16 | 17 | proc test_simple_ref_obj_unref*(x: SimpleRefObj) {.raises: [], cdecl, exportc, dynlib.} = 18 | GC_unref(x) 19 | 20 | proc test_new_simple_ref_obj*(): SimpleRefObj {.raises: [], cdecl, exportc, dynlib.} = 21 | newSimpleRefObj() 22 | 23 | proc test_simple_ref_obj_get_simple_ref_a*(simple_ref_obj: SimpleRefObj): int {.raises: [], cdecl, exportc, dynlib.} = 24 | simple_ref_obj.simpleRefA 25 | 26 | proc test_simple_ref_obj_set_simple_ref_a*(simple_ref_obj: SimpleRefObj, simpleRefA: int) {.raises: [], cdecl, exportc, dynlib.} = 27 | simple_ref_obj.simpleRefA = simpleRefA 28 | 29 | proc test_simple_ref_obj_get_simple_ref_b*(simple_ref_obj: SimpleRefObj): byte {.raises: [], cdecl, exportc, dynlib.} = 30 | simple_ref_obj.simpleRefB 31 | 32 | proc test_simple_ref_obj_set_simple_ref_b*(simple_ref_obj: SimpleRefObj, simpleRefB: byte) {.raises: [], cdecl, exportc, dynlib.} = 33 | simple_ref_obj.simpleRefB = simpleRefB 34 | 35 | proc test_simple_ref_obj_doit*(s: SimpleRefObj) {.raises: [], cdecl, exportc, dynlib.} = 36 | doit(s) 37 | 38 | type SeqInt* = ref object 39 | s: seq[int] 40 | 41 | proc test_new_seq_int*(): SeqInt {.raises: [], cdecl, exportc, dynlib.} = 42 | SeqInt() 43 | 44 | proc test_seq_int_len*(s: SeqInt): int {.raises: [], cdecl, exportc, dynlib.} = 45 | s.s.len 46 | 47 | proc test_seq_int_add*(s: SeqInt, v: int) {.raises: [], cdecl, exportc, dynlib.} = 48 | s.s.add(v) 49 | 50 | proc test_seq_int_get*(s: SeqInt, i: int): int {.raises: [], cdecl, exportc, dynlib.} = 51 | s.s[i] 52 | 53 | proc test_seq_int_set*(s: SeqInt, i: int, v: int) {.raises: [], cdecl, exportc, dynlib.} = 54 | s.s[i] = v 55 | 56 | proc test_seq_int_delete*(s: SeqInt, i: int) {.raises: [], cdecl, exportc, dynlib.} = 57 | s.s.delete(i) 58 | 59 | proc test_seq_int_clear*(s: SeqInt) {.raises: [], cdecl, exportc, dynlib.} = 60 | s.s.setLen(0) 61 | 62 | proc test_seq_int_unref*(s: SeqInt) {.raises: [], cdecl, exportc, dynlib.} = 63 | GC_unref(s) 64 | 65 | proc test_ref_obj_with_seq_unref*(x: RefObjWithSeq) {.raises: [], cdecl, exportc, dynlib.} = 66 | GC_unref(x) 67 | 68 | proc test_new_ref_obj_with_seq*(): RefObjWithSeq {.raises: [], cdecl, exportc, dynlib.} = 69 | newRefObjWithSeq() 70 | 71 | proc test_ref_obj_with_seq_data_len*(ref_obj_with_seq: RefObjWithSeq): int {.raises: [], cdecl, exportc, dynlib.} = 72 | ref_obj_with_seq.data.len 73 | 74 | proc test_ref_obj_with_seq_data_add*(ref_obj_with_seq: RefObjWithSeq, v: byte) {.raises: [], cdecl, exportc, dynlib.} = 75 | ref_obj_with_seq.data.add(v) 76 | 77 | proc test_ref_obj_with_seq_data_get*(ref_obj_with_seq: RefObjWithSeq, i: int): byte {.raises: [], cdecl, exportc, dynlib.} = 78 | ref_obj_with_seq.data[i] 79 | 80 | proc test_ref_obj_with_seq_data_set*(ref_obj_with_seq: RefObjWithSeq, i: int, v: byte) {.raises: [], cdecl, exportc, dynlib.} = 81 | ref_obj_with_seq.data[i] = v 82 | 83 | proc test_ref_obj_with_seq_data_delete*(ref_obj_with_seq: RefObjWithSeq, i: int) {.raises: [], cdecl, exportc, dynlib.} = 84 | ref_obj_with_seq.data.delete(i) 85 | 86 | proc test_ref_obj_with_seq_data_clear*(ref_obj_with_seq: RefObjWithSeq) {.raises: [], cdecl, exportc, dynlib.} = 87 | ref_obj_with_seq.data.setLen(0) 88 | 89 | proc test_simple_obj_with_proc*(simple_a: int, simple_b: byte, simple_c: bool): SimpleObjWithProc {.raises: [], cdecl, exportc, dynlib.} = 90 | result.simple_a = simple_a 91 | result.simple_b = simple_b 92 | result.simple_c = simple_c 93 | 94 | proc test_simple_obj_with_proc_eq*(a, b: SimpleObjWithProc): bool {.raises: [], cdecl, exportc, dynlib.}= 95 | a.simple_a == b.simple_a and a.simple_b == b.simple_b and a.simple_c == b.simple_c 96 | 97 | proc test_simple_obj_with_proc_extra_proc*(s: SimpleObjWithProc) {.raises: [], cdecl, exportc, dynlib.} = 98 | extraProc(s) 99 | 100 | type SeqString* = ref object 101 | s: seq[string] 102 | 103 | proc test_new_seq_string*(): SeqString {.raises: [], cdecl, exportc, dynlib.} = 104 | SeqString() 105 | 106 | proc test_seq_string_len*(s: SeqString): int {.raises: [], cdecl, exportc, dynlib.} = 107 | s.s.len 108 | 109 | proc test_seq_string_add*(s: SeqString, v: cstring) {.raises: [], cdecl, exportc, dynlib.} = 110 | s.s.add(v.`$`) 111 | 112 | proc test_seq_string_get*(s: SeqString, i: int): cstring {.raises: [], cdecl, exportc, dynlib.} = 113 | s.s[i].cstring 114 | 115 | proc test_seq_string_set*(s: SeqString, i: int, v: cstring) {.raises: [], cdecl, exportc, dynlib.} = 116 | s.s[i] = v.`$` 117 | 118 | proc test_seq_string_delete*(s: SeqString, i: int) {.raises: [], cdecl, exportc, dynlib.} = 119 | s.s.delete(i) 120 | 121 | proc test_seq_string_clear*(s: SeqString) {.raises: [], cdecl, exportc, dynlib.} = 122 | s.s.setLen(0) 123 | 124 | proc test_seq_string_unref*(s: SeqString) {.raises: [], cdecl, exportc, dynlib.} = 125 | GC_unref(s) 126 | 127 | proc test_get_datas*(): SeqString {.raises: [], cdecl, exportc, dynlib.} = 128 | SeqString(s: getDatas()) 129 | 130 | -------------------------------------------------------------------------------- /tests/generated/test.hpp: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_TEST_H 2 | #define INCLUDE_TEST_H 3 | 4 | #include 5 | 6 | #define SIMPLE_CONST 123 7 | 8 | typedef char SimpleEnum; 9 | #define FIRST 0 10 | #define SECOND 1 11 | #define THIRD 2 12 | 13 | struct SimpleObj; 14 | 15 | struct SimpleRefObj; 16 | 17 | struct SeqInt; 18 | 19 | struct RefObjWithSeq; 20 | 21 | struct SimpleObjWithProc; 22 | 23 | struct SeqString; 24 | 25 | struct SimpleObj { 26 | int64_t simple_a; 27 | char simple_b; 28 | bool simple_c; 29 | }; 30 | 31 | struct SimpleRefObj { 32 | 33 | private: 34 | 35 | uint64_t reference; 36 | 37 | public: 38 | 39 | SimpleRefObj(); 40 | 41 | int64_t getSimpleRefA(); 42 | void setSimpleRefA(int64_t value); 43 | 44 | char getSimpleRefB(); 45 | void setSimpleRefB(char value); 46 | 47 | void free(); 48 | 49 | /** 50 | * Does some thing with SimpleRefObj. 51 | */ 52 | void doit(); 53 | 54 | }; 55 | 56 | struct SeqInt { 57 | 58 | private: 59 | 60 | uint64_t reference; 61 | 62 | public: 63 | 64 | void free(); 65 | 66 | }; 67 | 68 | struct RefObjWithSeq { 69 | 70 | private: 71 | 72 | uint64_t reference; 73 | 74 | public: 75 | 76 | RefObjWithSeq(); 77 | 78 | void free(); 79 | 80 | }; 81 | 82 | struct SimpleObjWithProc { 83 | int64_t simple_a; 84 | char simple_b; 85 | bool simple_c; 86 | void extraProc(); 87 | 88 | }; 89 | 90 | struct SeqString { 91 | 92 | private: 93 | 94 | uint64_t reference; 95 | 96 | public: 97 | 98 | void free(); 99 | 100 | }; 101 | 102 | extern "C" { 103 | 104 | int64_t test_simple_call(int64_t a); 105 | 106 | SimpleObj test_simple_obj(int64_t simple_a, char simple_b, bool simple_c); 107 | 108 | char test_simple_obj_eq(SimpleObj a, SimpleObj b); 109 | 110 | void test_simple_ref_obj_unref(SimpleRefObj simple_ref_obj); 111 | 112 | SimpleRefObj test_new_simple_ref_obj(); 113 | 114 | int64_t test_simple_ref_obj_get_simple_ref_a(SimpleRefObj simple_ref_obj); 115 | 116 | void test_simple_ref_obj_set_simple_ref_a(SimpleRefObj simple_ref_obj, int64_t value); 117 | 118 | char test_simple_ref_obj_get_simple_ref_b(SimpleRefObj simple_ref_obj); 119 | 120 | void test_simple_ref_obj_set_simple_ref_b(SimpleRefObj simple_ref_obj, char value); 121 | 122 | void test_simple_ref_obj_doit(SimpleRefObj s); 123 | 124 | void test_seq_int_unref(SeqInt seq_int); 125 | 126 | SeqInt test_new_seq_int(); 127 | 128 | int64_t test_seq_int_len(SeqInt seq_int); 129 | 130 | int64_t test_seq_int_get(SeqInt seq_int, int64_t index); 131 | 132 | void test_seq_int_set(SeqInt seq_int, int64_t index, int64_t value); 133 | 134 | void test_seq_int_delete(SeqInt seq_int, int64_t index); 135 | 136 | void test_seq_int_add(SeqInt seq_int, int64_t value); 137 | 138 | void test_seq_int_clear(SeqInt seq_int); 139 | 140 | void test_ref_obj_with_seq_unref(RefObjWithSeq ref_obj_with_seq); 141 | 142 | RefObjWithSeq test_new_ref_obj_with_seq(); 143 | 144 | int64_t test_ref_obj_with_seq_data_len(RefObjWithSeq ref_obj_with_seq); 145 | 146 | char test_ref_obj_with_seq_data_get(RefObjWithSeq ref_obj_with_seq, int64_t index); 147 | 148 | void test_ref_obj_with_seq_data_set(RefObjWithSeq ref_obj_with_seq, int64_t index, char value); 149 | 150 | void test_ref_obj_with_seq_data_delete(RefObjWithSeq ref_obj_with_seq, int64_t index); 151 | 152 | void test_ref_obj_with_seq_data_add(RefObjWithSeq ref_obj_with_seq, char value); 153 | 154 | void test_ref_obj_with_seq_data_clear(RefObjWithSeq ref_obj_with_seq); 155 | 156 | SimpleObjWithProc test_simple_obj_with_proc(int64_t simple_a, char simple_b, bool simple_c); 157 | 158 | char test_simple_obj_with_proc_eq(SimpleObjWithProc a, SimpleObjWithProc b); 159 | 160 | void test_simple_obj_with_proc_extra_proc(SimpleObjWithProc s); 161 | 162 | void test_seq_string_unref(SeqString seq_string); 163 | 164 | SeqString test_new_seq_string(); 165 | 166 | int64_t test_seq_string_len(SeqString seq_string); 167 | 168 | const char* test_seq_string_get(SeqString seq_string, int64_t index); 169 | 170 | void test_seq_string_set(SeqString seq_string, int64_t index, const char* value); 171 | 172 | void test_seq_string_delete(SeqString seq_string, int64_t index); 173 | 174 | void test_seq_string_add(SeqString seq_string, const char* value); 175 | 176 | void test_seq_string_clear(SeqString seq_string); 177 | 178 | SeqString test_get_datas(); 179 | 180 | } 181 | 182 | int64_t simpleCall(int64_t a) { 183 | return test_simple_call(a); 184 | }; 185 | 186 | SimpleObj simpleObj(int64_t simpleA, char simpleB, bool simpleC) { 187 | return test_simple_obj(simpleA, simpleB, simpleC); 188 | }; 189 | 190 | SimpleRefObj::SimpleRefObj() { 191 | this->reference = test_new_simple_ref_obj().reference; 192 | } 193 | 194 | int64_t SimpleRefObj::getSimpleRefA(){ 195 | return test_simple_ref_obj_get_simple_ref_a(*this); 196 | } 197 | 198 | void SimpleRefObj::setSimpleRefA(int64_t value){ 199 | test_simple_ref_obj_set_simple_ref_a(*this, value); 200 | } 201 | 202 | char SimpleRefObj::getSimpleRefB(){ 203 | return test_simple_ref_obj_get_simple_ref_b(*this); 204 | } 205 | 206 | void SimpleRefObj::setSimpleRefB(char value){ 207 | test_simple_ref_obj_set_simple_ref_b(*this, value); 208 | } 209 | 210 | void SimpleRefObj::free(){ 211 | test_simple_ref_obj_unref(*this); 212 | } 213 | 214 | void SimpleRefObj::doit() { 215 | test_simple_ref_obj_doit(*this); 216 | }; 217 | 218 | void SeqInt::free(){ 219 | test_seq_int_unref(*this); 220 | } 221 | 222 | RefObjWithSeq::RefObjWithSeq() { 223 | this->reference = test_new_ref_obj_with_seq().reference; 224 | } 225 | 226 | void RefObjWithSeq::free(){ 227 | test_ref_obj_with_seq_unref(*this); 228 | } 229 | 230 | SimpleObjWithProc simpleObjWithProc(int64_t simpleA, char simpleB, bool simpleC) { 231 | return test_simple_obj_with_proc(simpleA, simpleB, simpleC); 232 | }; 233 | 234 | void SimpleObjWithProc::extraProc() { 235 | test_simple_obj_with_proc_extra_proc(*this); 236 | }; 237 | 238 | void SeqString::free(){ 239 | test_seq_string_unref(*this); 240 | } 241 | 242 | SeqString getDatas() { 243 | return test_get_datas(); 244 | }; 245 | 246 | #endif 247 | -------------------------------------------------------------------------------- /tests/generated/test.nim: -------------------------------------------------------------------------------- 1 | import bumpy, chroma, unicode, vmath 2 | 3 | export bumpy, chroma, unicode, vmath 4 | 5 | when defined(windows): 6 | const libName = "test.dll" 7 | elif defined(macosx): 8 | const libName = "libtest.dylib" 9 | else: 10 | const libName = "libtest.so" 11 | 12 | {.push dynlib: libName.} 13 | 14 | type testError = object of ValueError 15 | 16 | const simpleConst* = 123 17 | 18 | type SimpleEnum* = enum 19 | First 20 | Second 21 | Third 22 | 23 | type SimpleObj* = object 24 | simpleA*: int 25 | simpleB*: byte 26 | simpleC*: bool 27 | 28 | proc simpleObj*(simple_a: int, simple_b: byte, simple_c: bool): SimpleObj = 29 | result.simple_a = simple_a 30 | result.simple_b = simple_b 31 | result.simple_c = simple_c 32 | 33 | type SimpleRefObjObj = object 34 | reference: pointer 35 | 36 | type SimpleRefObj* = ref SimpleRefObjObj 37 | 38 | proc test_simple_ref_obj_unref(x: SimpleRefObjObj) {.importc: "test_simple_ref_obj_unref", cdecl.} 39 | 40 | proc `=destroy`(x: var SimpleRefObjObj) = 41 | test_simple_ref_obj_unref(x) 42 | 43 | type SeqIntObj = object 44 | reference: pointer 45 | 46 | type SeqInt* = ref SeqIntObj 47 | 48 | proc test_seq_int_unref(x: SeqIntObj) {.importc: "test_seq_int_unref", cdecl.} 49 | 50 | proc `=destroy`(x: var SeqIntObj) = 51 | test_seq_int_unref(x) 52 | 53 | type RefObjWithSeqObj = object 54 | reference: pointer 55 | 56 | type RefObjWithSeq* = ref RefObjWithSeqObj 57 | 58 | proc test_ref_obj_with_seq_unref(x: RefObjWithSeqObj) {.importc: "test_ref_obj_with_seq_unref", cdecl.} 59 | 60 | proc `=destroy`(x: var RefObjWithSeqObj) = 61 | test_ref_obj_with_seq_unref(x) 62 | 63 | type SimpleObjWithProc* = object 64 | simpleA*: int 65 | simpleB*: byte 66 | simpleC*: bool 67 | 68 | proc simpleObjWithProc*(simple_a: int, simple_b: byte, simple_c: bool): SimpleObjWithProc = 69 | result.simple_a = simple_a 70 | result.simple_b = simple_b 71 | result.simple_c = simple_c 72 | 73 | type SeqStringObj = object 74 | reference: pointer 75 | 76 | type SeqString* = ref SeqStringObj 77 | 78 | proc test_seq_string_unref(x: SeqStringObj) {.importc: "test_seq_string_unref", cdecl.} 79 | 80 | proc `=destroy`(x: var SeqStringObj) = 81 | test_seq_string_unref(x) 82 | 83 | proc test_simple_call(a: int): int {.importc: "test_simple_call", cdecl.} 84 | 85 | proc simpleCall*(a: int): int {.inline.} = 86 | result = test_simple_call(a) 87 | 88 | proc test_new_simple_ref_obj(): SimpleRefObj {.importc: "test_new_simple_ref_obj", cdecl.} 89 | 90 | proc newSimpleRefObj*(): SimpleRefObj {.inline.} = 91 | result = test_new_simple_ref_obj() 92 | 93 | proc test_simple_ref_obj_get_simple_ref_a(simpleRefObj: SimpleRefObj): int {.importc: "test_simple_ref_obj_get_simple_ref_a", cdecl.} 94 | 95 | proc simpleRefA*(simpleRefObj: SimpleRefObj): int {.inline.} = 96 | test_simple_ref_obj_get_simple_ref_a(simpleRefObj) 97 | 98 | proc test_simple_ref_obj_set_simple_ref_a(simpleRefObj: SimpleRefObj, simpleRefA: int) {.importc: "test_simple_ref_obj_set_simple_ref_a", cdecl.} 99 | 100 | proc `simpleRefA=`*(simpleRefObj: SimpleRefObj, simpleRefA: int) = 101 | test_simple_ref_obj_set_simple_ref_a(simpleRefObj, simpleRefA) 102 | 103 | proc test_simple_ref_obj_get_simple_ref_b(simpleRefObj: SimpleRefObj): byte {.importc: "test_simple_ref_obj_get_simple_ref_b", cdecl.} 104 | 105 | proc simpleRefB*(simpleRefObj: SimpleRefObj): byte {.inline.} = 106 | test_simple_ref_obj_get_simple_ref_b(simpleRefObj) 107 | 108 | proc test_simple_ref_obj_set_simple_ref_b(simpleRefObj: SimpleRefObj, simpleRefB: byte) {.importc: "test_simple_ref_obj_set_simple_ref_b", cdecl.} 109 | 110 | proc `simpleRefB=`*(simpleRefObj: SimpleRefObj, simpleRefB: byte) = 111 | test_simple_ref_obj_set_simple_ref_b(simpleRefObj, simpleRefB) 112 | 113 | proc test_simple_ref_obj_doit(s: SimpleRefObj) {.importc: "test_simple_ref_obj_doit", cdecl.} 114 | 115 | proc doit*(s: SimpleRefObj) {.inline.} = 116 | test_simple_ref_obj_doit(s) 117 | 118 | proc test_seq_int_len(s: SeqInt): int {.importc: "test_seq_int_len", cdecl.} 119 | 120 | proc len*(s: SeqInt): int = 121 | test_seq_int_len(s) 122 | 123 | proc test_seq_int_add(s: SeqInt, v: int) {.importc: "test_seq_int_add", cdecl.} 124 | 125 | proc add*(s: SeqInt, v: int) = 126 | test_seq_int_add(s, v) 127 | 128 | proc test_seq_int_get(s: SeqInt, i: int): int {.importc: "test_seq_int_get", cdecl.} 129 | 130 | proc `[]`*(s: SeqInt, i: int): int = 131 | test_seq_int_get(s, i) 132 | 133 | proc test_seq_int_set(s: SeqInt, i: int, v: int) {.importc: "test_seq_int_set", cdecl.} 134 | 135 | proc `[]=`*(s: SeqInt, i: int, v: int) = 136 | test_seq_int_set(s, i, v) 137 | 138 | proc test_seq_int_delete(s: SeqInt, i: int) {.importc: "test_seq_int_delete", cdecl.} 139 | 140 | proc delete*(s: SeqInt, i: int) = 141 | test_seq_int_delete(s, i) 142 | 143 | proc test_seq_int_clear(s: SeqInt) {.importc: "test_seq_int_clear", cdecl.} 144 | 145 | proc clear*(s: SeqInt) = 146 | test_seq_int_clear(s) 147 | 148 | proc test_new_seq_int*(): SeqInt {.importc: "test_new_seq_int", cdecl.} 149 | 150 | proc newSeqInt*(): SeqInt = 151 | test_new_seq_int() 152 | 153 | proc test_new_ref_obj_with_seq(): RefObjWithSeq {.importc: "test_new_ref_obj_with_seq", cdecl.} 154 | 155 | proc newRefObjWithSeq*(): RefObjWithSeq {.inline.} = 156 | result = test_new_ref_obj_with_seq() 157 | 158 | type RefObjWithSeqData = object 159 | refObjWithSeq: RefObjWithSeq 160 | 161 | proc data*(refObjWithSeq: RefObjWithSeq): RefObjWithSeqData = 162 | RefObjWithSeqData(refObjWithSeq: refObjWithSeq) 163 | 164 | proc test_ref_obj_with_seq_data_len(s: RefObjWithSeq): int {.importc: "test_ref_obj_with_seq_data_len", cdecl.} 165 | 166 | proc len*(s: RefObjWithSeqData): int = 167 | test_ref_obj_with_seq_data_len(s.refObjWithSeq) 168 | 169 | proc test_ref_obj_with_seq_data_add(s: RefObjWithSeq, v: byte) {.importc: "test_ref_obj_with_seq_data_add", cdecl.} 170 | 171 | proc add*(s: RefObjWithSeqData, v: byte) = 172 | test_ref_obj_with_seq_data_add(s.refObjWithSeq, v) 173 | 174 | proc test_ref_obj_with_seq_data_get(s: RefObjWithSeq, i: int): byte {.importc: "test_ref_obj_with_seq_data_get", cdecl.} 175 | 176 | proc `[]`*(s: RefObjWithSeqData, i: int): byte = 177 | test_ref_obj_with_seq_data_get(s.refObjWithSeq, i) 178 | 179 | proc test_ref_obj_with_seq_data_set(s: RefObjWithSeq, i: int, v: byte) {.importc: "test_ref_obj_with_seq_data_set", cdecl.} 180 | 181 | proc `[]=`*(s: RefObjWithSeqData, i: int, v: byte) = 182 | test_ref_obj_with_seq_data_set(s.refObjWithSeq, i, v) 183 | 184 | proc test_ref_obj_with_seq_data_delete(s: RefObjWithSeq, i: int) {.importc: "test_ref_obj_with_seq_data_delete", cdecl.} 185 | 186 | proc delete*(s: RefObjWithSeqData, i: int) = 187 | test_ref_obj_with_seq_data_delete(s.refObjWithSeq, i) 188 | 189 | proc test_ref_obj_with_seq_data_clear(s: RefObjWithSeq) {.importc: "test_ref_obj_with_seq_data_clear", cdecl.} 190 | 191 | proc clear*(s: RefObjWithSeqData) = 192 | test_ref_obj_with_seq_data_clear(s.refObjWithSeq) 193 | 194 | proc test_simple_obj_with_proc_extra_proc(s: SimpleObjWithProc) {.importc: "test_simple_obj_with_proc_extra_proc", cdecl.} 195 | 196 | proc extraProc*(s: SimpleObjWithProc) {.inline.} = 197 | test_simple_obj_with_proc_extra_proc(s) 198 | 199 | proc test_seq_string_len(s: SeqString): int {.importc: "test_seq_string_len", cdecl.} 200 | 201 | proc len*(s: SeqString): int = 202 | test_seq_string_len(s) 203 | 204 | proc test_seq_string_add(s: SeqString, v: string) {.importc: "test_seq_string_add", cdecl.} 205 | 206 | proc add*(s: SeqString, v: string) = 207 | test_seq_string_add(s, v) 208 | 209 | proc test_seq_string_get(s: SeqString, i: int): string {.importc: "test_seq_string_get", cdecl.} 210 | 211 | proc `[]`*(s: SeqString, i: int): string = 212 | test_seq_string_get(s, i) 213 | 214 | proc test_seq_string_set(s: SeqString, i: int, v: string) {.importc: "test_seq_string_set", cdecl.} 215 | 216 | proc `[]=`*(s: SeqString, i: int, v: string) = 217 | test_seq_string_set(s, i, v) 218 | 219 | proc test_seq_string_delete(s: SeqString, i: int) {.importc: "test_seq_string_delete", cdecl.} 220 | 221 | proc delete*(s: SeqString, i: int) = 222 | test_seq_string_delete(s, i) 223 | 224 | proc test_seq_string_clear(s: SeqString) {.importc: "test_seq_string_clear", cdecl.} 225 | 226 | proc clear*(s: SeqString) = 227 | test_seq_string_clear(s) 228 | 229 | proc test_new_seq_string*(): SeqString {.importc: "test_new_seq_string", cdecl.} 230 | 231 | proc newSeqString*(): SeqString = 232 | test_new_seq_string() 233 | 234 | proc test_get_datas(): SeqString {.importc: "test_get_datas", cdecl.} 235 | 236 | proc getDatas*(): SeqString {.inline.} = 237 | result = test_get_datas() 238 | 239 | -------------------------------------------------------------------------------- /src/genny/languages/c.nim: -------------------------------------------------------------------------------- 1 | import 2 | std/[os, strformat, strutils, macros], 3 | ../common 4 | 5 | var 6 | types {.compiletime.}: string 7 | procs {.compiletime.}: string 8 | 9 | proc exportTypeC(sym: NimNode): string = 10 | if sym.kind == nnkBracketExpr: 11 | if sym[0].repr == "array": 12 | let 13 | entryCount = sym[1].repr 14 | entryType = exportTypeC(sym[2]) 15 | result = &"{entryType}[{entryCount}]" 16 | elif sym[0].repr == "seq": 17 | result = sym.getSeqName() 18 | else: 19 | error(&"Unexpected bracket expression {sym[0].repr}[") 20 | else: 21 | result = 22 | case sym.repr: 23 | of "string": "char*" 24 | of "bool": "char" 25 | of "byte": "char" 26 | of "int8": "char" 27 | of "int16": "short" 28 | of "int32": "int" 29 | of "int64": "long long" 30 | of "int": "long long" 31 | of "uint8": "unsigned char" 32 | of "uint16": "unsigned short" 33 | of "uint32": "unsigned int" 34 | of "uint64": "unsigned long long" 35 | of "uint": "unsigned long long" 36 | of "float32": "float" 37 | of "float64": "double" 38 | of "float": "double" 39 | of "Rune": "int" 40 | of "Vec2": "Vector2" 41 | of "Mat3": "Matrix3" 42 | of "", "nil": "void" 43 | of "None": "void" 44 | else: 45 | sym.repr 46 | 47 | proc exportTypeC(sym: NimNode, name: string): string = 48 | if sym.kind == nnkBracketExpr: 49 | if sym[0].repr == "array": 50 | let 51 | entryCount = sym[1].repr 52 | entryType = exportTypeC(sym[2], &"{name}[{entryCount}]") 53 | result = &"{entryType}" 54 | elif sym[0].repr == "seq": 55 | result = sym.getSeqName() & " " & name 56 | else: 57 | error(&"Unexpected bracket expression {sym[0].repr}[") 58 | else: 59 | result = exportTypeC(sym) & " " & name 60 | 61 | proc dllProc*(procName: string, args: openarray[string], restype: string) = 62 | var argStr = "" 63 | for arg in args: 64 | argStr.add &"{arg}, " 65 | argStr.removeSuffix ", " 66 | procs.add &"{restype} {procName}({argStr});\n" 67 | procs.add "\n" 68 | 69 | proc dllProc*(procName: string, args: openarray[(NimNode, NimNode)], restype: string) = 70 | var argsConverted: seq[string] 71 | for (argName, argType) in args: 72 | argsConverted.add exportTypeC(argType, toSnakeCase(argName.getName())) 73 | dllProc(procName, argsConverted, restype) 74 | 75 | proc dllProc*(procName: string, restype: string) = 76 | var a: seq[(string)] 77 | dllProc(procName, a, restype) 78 | 79 | proc exportConstC*(sym: NimNode) = 80 | types.add &"#define {toCapSnakeCase(sym.repr)} {sym.getImpl()[2].repr}\n" 81 | types.add "\n" 82 | 83 | proc exportEnumC*(sym: NimNode) = 84 | types.add &"typedef char {sym.repr};\n" 85 | for i, entry in sym.getImpl()[2][1 .. ^1]: 86 | types.add &"#define {toCapSnakeCase(entry.repr)} {i}\n" 87 | types.add "\n" 88 | 89 | proc exportProcC*( 90 | sym: NimNode, 91 | owner: NimNode = nil, 92 | prefixes: openarray[NimNode] = [] 93 | ) = 94 | let 95 | procName = sym.repr 96 | procNameSnaked = toSnakeCase(procName) 97 | procType = sym.getTypeInst() 98 | procParams = procType[0][1 .. ^1] 99 | procReturn = procType[0][0] 100 | 101 | var apiProcName = "" 102 | if owner != nil: 103 | apiProcName.add &"{toSnakeCase(owner.getName())}_" 104 | for prefix in prefixes: 105 | apiProcName.add &"{toSnakeCase(prefix.getName())}_" 106 | apiProcName.add &"{procNameSnaked}" 107 | 108 | var defaults: seq[(string, NimNode)] 109 | for identDefs in sym.getImpl()[3][1 .. ^1]: 110 | let default = identDefs[^1] 111 | for entry in identDefs[0 .. ^3]: 112 | defaults.add((entry.repr, default)) 113 | 114 | let comments = 115 | if sym.getImpl()[6][0].kind == nnkCommentStmt: 116 | sym.getImpl()[6][0].repr 117 | elif sym.getImpl[6].kind == nnkAsgn and 118 | sym.getImpl[6][1].kind == nnkStmtListExpr and 119 | sym.getImpl[6][1][0].kind == nnkCommentStmt: 120 | sym.getImpl[6][1][0].repr 121 | else: 122 | "" 123 | if comments != "": 124 | let lines = comments.replace("## ", "").split("\n") 125 | procs.add "/**\n" 126 | for line in lines: 127 | procs.add &" * {line}\n" 128 | procs.add " */\n" 129 | 130 | var dllParams: seq[(NimNode, NimNode)] 131 | for param in procParams: 132 | dllParams.add((param[0], param[1])) 133 | dllProc(&"$lib_{apiProcName}", dllParams, exportTypeC(procReturn)) 134 | 135 | proc exportObjectC*(sym: NimNode, constructor: NimNode) = 136 | let objName = sym.repr 137 | 138 | types.add &"typedef struct {objName} " & "{\n" 139 | for identDefs in sym.getImpl()[2][2]: 140 | for property in identDefs[0 .. ^3]: 141 | types.add &" {exportTypeC(identDefs[^2], toSnakeCase(property[1].repr))};\n" 142 | types.add "} " & &"{objName};\n\n" 143 | 144 | if constructor != nil: 145 | exportProcC(constructor) 146 | else: 147 | procs.add &"{objName} $lib_{toSnakeCase(objName)}(" 148 | for identDefs in sym.getImpl()[2][2]: 149 | for property in identDefs[0 .. ^3]: 150 | procs.add &"{exportTypeC(identDefs[^2], toSnakeCase(property[1].repr))}, " 151 | procs.removeSuffix ", " 152 | procs.add ");\n\n" 153 | 154 | dllProc(&"$lib_{toSnakeCase(objName)}_eq", [&"{objName} a", &"{objName} b"], "char") 155 | 156 | proc genRefObject(objName: string) = 157 | types.add &"typedef long long {objName};\n\n" 158 | 159 | let unrefLibProc = &"$lib_{toSnakeCase(objName)}_unref" 160 | 161 | dllProc(unrefLibProc, [objName & " " & toSnakeCase(objName)], "void") 162 | 163 | proc genSeqProcs(objName, procPrefix, selfSuffix: string, entryType: NimNode) = 164 | let objArg = objName & " " & toSnakeCase(objName) 165 | dllProc(&"{procPrefix}_len", [objArg], "long long") 166 | dllProc(&"{procPrefix}_get", [objArg, "long long index"], exportTypeC(entryType)) 167 | dllProc(&"{procPrefix}_set", [objArg, "long long index", exportTypeC(entryType, "value")], "void") 168 | dllProc(&"{procPrefix}_delete", [objArg, "long long index"], "void") 169 | dllProc(&"{procPrefix}_add", [objArg, exportTypeC(entryType, "value")], "void") 170 | dllProc(&"{procPrefix}_clear", [objArg], "void") 171 | 172 | proc exportRefObjectC*( 173 | sym: NimNode, 174 | fields: seq[(string, NimNode)], 175 | constructor: NimNode 176 | ) = 177 | let 178 | objName = sym.repr 179 | objNameSnaked = toSnakeCase(objName) 180 | objType = sym.getType()[1].getType() 181 | 182 | genRefObject(objName) 183 | 184 | if constructor != nil: 185 | let 186 | constructorLibProc = &"$lib_{toSnakeCase(constructor.repr)}" 187 | constructorType = constructor.getTypeInst() 188 | constructorParams = constructorType[0][1 .. ^1] 189 | constructorRaises = constructor.raises() 190 | 191 | var dllParams: seq[(NimNode, NimNode)] 192 | for param in constructorParams: 193 | dllParams.add((param[0], param[1])) 194 | dllProc(constructorLibProc, dllParams, objName) 195 | 196 | for (fieldName, fieldType) in fields: 197 | let fieldNameSnaked = toSnakeCase(fieldName) 198 | 199 | if fieldType.kind != nnkBracketExpr: 200 | let getProcName = &"$lib_{objNameSnaked}_get_{fieldNameSnaked}" 201 | 202 | let setProcName = &"$lib_{objNameSnaked}_set_{fieldNameSnaked}" 203 | 204 | dllProc(getProcName, [objName & " " & objNameSnaked], exportTypeC(fieldType)) 205 | dllProc(setProcName, [objName & " " & objNameSnaked, exportTypeC(fieldType, "value")], exportTypeC(nil)) 206 | else: 207 | var helperName = fieldName 208 | helperName[0] = toUpperAscii(helperName[0]) 209 | let helperClassName = objName & helperName 210 | 211 | genSeqProcs( 212 | objName, 213 | &"$lib_{objNameSnaked}_{fieldNameSnaked}", 214 | &".{toSnakeCase(objName)}", 215 | fieldType[1] 216 | ) 217 | 218 | proc exportSeqC*(sym: NimNode) = 219 | let 220 | seqName = sym.getName() 221 | seqNameSnaked = toSnakeCase(seqName) 222 | 223 | genRefObject(seqName) 224 | 225 | let newSeqProc = &"$lib_new_{toSnakeCase(seqName)}" 226 | 227 | dllProc(newSeqProc, seqName) 228 | 229 | genSeqProcs( 230 | sym.getName(), 231 | &"$lib_{seqNameSnaked}", 232 | "", 233 | sym[1] 234 | ) 235 | 236 | const header = """ 237 | #ifndef INCLUDE_$LIB_H 238 | #define INCLUDE_$LIB_H 239 | 240 | """ 241 | 242 | const footer = """ 243 | #endif 244 | """ 245 | 246 | proc writeC*(dir, lib: string) = 247 | createDir(dir) 248 | writeFile(&"{dir}/{toSnakeCase(lib)}.h", (header & types & procs & footer) 249 | .replace("$lib", toSnakeCase(lib)).replace("$LIB", lib.toUpperAscii()) 250 | ) 251 | -------------------------------------------------------------------------------- /tests/generated/test.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | pub const simple_const = 123; 4 | 5 | pub const SimpleEnum = enum(u8) { 6 | first = 0, 7 | second = 1, 8 | third = 2, 9 | }; 10 | 11 | extern fn test_simple_call(a: isize) callconv(.C) isize; 12 | /// Returns the integer passed in. 13 | pub inline fn simpleCall(a: isize) isize { 14 | return test_simple_call(a); 15 | } 16 | 17 | pub const SimpleObj = extern struct { 18 | simple_a: isize, 19 | simple_b: u8, 20 | simple_c: bool, 21 | 22 | pub fn init(simple_a: isize, simple_b: u8, simple_c: bool) SimpleObj { 23 | return SimpleObj{ 24 | .simple_a = simple_a, 25 | .simple_b = simple_b, 26 | .simple_c = simple_c, 27 | }; 28 | } 29 | 30 | extern fn test_simple_obj_eq(self: SimpleObj, other: SimpleObj) callconv(.C) bool; 31 | pub inline fn eql(self: SimpleObj, other: SimpleObj) bool { 32 | return test_simple_obj_eq(self, other); 33 | } 34 | }; 35 | 36 | pub const SimpleRefObj = opaque { 37 | extern fn test_simple_ref_obj_unref(self: *SimpleRefObj) callconv(.C) void; 38 | pub inline fn deinit(self: *SimpleRefObj) void { 39 | return test_simple_ref_obj_unref(self); 40 | } 41 | 42 | extern fn test_new_simple_ref_obj() callconv(.C) *SimpleRefObj; 43 | /// Creates new SimpleRefObj. 44 | pub inline fn init() *SimpleRefObj { 45 | return test_new_simple_ref_obj(); 46 | } 47 | 48 | extern fn test_simple_ref_obj_get_simple_ref_a(self: *SimpleRefObj) callconv(.C) isize; 49 | pub inline fn getSimpleRefA(self: *SimpleRefObj) isize { 50 | return test_simple_ref_obj_get_simple_ref_a(self); 51 | } 52 | 53 | extern fn test_simple_ref_obj_set_simple_ref_a(self: *SimpleRefObj, value: isize) callconv(.C) void; 54 | pub inline fn setSimpleRefA(self: *SimpleRefObj, value: isize) void { 55 | return test_simple_ref_obj_set_simple_ref_a(self, value); 56 | } 57 | 58 | extern fn test_simple_ref_obj_get_simple_ref_b(self: *SimpleRefObj) callconv(.C) u8; 59 | pub inline fn getSimpleRefB(self: *SimpleRefObj) u8 { 60 | return test_simple_ref_obj_get_simple_ref_b(self); 61 | } 62 | 63 | extern fn test_simple_ref_obj_set_simple_ref_b(self: *SimpleRefObj, value: u8) callconv(.C) void; 64 | pub inline fn setSimpleRefB(self: *SimpleRefObj, value: u8) void { 65 | return test_simple_ref_obj_set_simple_ref_b(self, value); 66 | } 67 | 68 | extern fn test_simple_ref_obj_doit(self: *SimpleRefObj) callconv(.C) void; 69 | /// Does some thing with SimpleRefObj. 70 | pub inline fn doit(self: *SimpleRefObj) void { 71 | return test_simple_ref_obj_doit(self); 72 | } 73 | }; 74 | 75 | pub const SeqInt = opaque { 76 | extern fn test_seq_int_unref(self: *SeqInt) callconv(.C) void; 77 | pub inline fn deinit(self: *SeqInt) void { 78 | return test_seq_int_unref(self); 79 | } 80 | 81 | extern fn test_new_seq_int() callconv(.C) *SeqInt; 82 | pub inline fn init() *SeqInt { 83 | return test_new_seq_int(); 84 | } 85 | 86 | extern fn test_seq_int_len(self: *SeqInt) callconv(.C) isize; 87 | pub inline fn len(self: *SeqInt) isize { 88 | return test_seq_int_len(self); 89 | } 90 | 91 | extern fn test_seq_int_get(self: *SeqInt, index: isize) callconv(.C) isize; 92 | pub inline fn get(self: *SeqInt, index: isize) isize { 93 | return test_seq_int_get(self, index); 94 | } 95 | 96 | extern fn test_seq_int_set(self: *SeqInt, index: isize, value: isize) callconv(.C) void; 97 | pub inline fn set(self: *SeqInt, index: isize, value: isize) void { 98 | return test_seq_int_set(self, index, value); 99 | } 100 | 101 | extern fn test_seq_int_add(self: *SeqInt, value: isize) callconv(.C) void; 102 | pub inline fn append(self: *SeqInt, value: isize) void { 103 | return test_seq_int_add(self, value); 104 | } 105 | 106 | extern fn test_seq_int_delete(self: *SeqInt, index: isize) callconv(.C) void; 107 | pub inline fn remove(self: *SeqInt, index: isize) void { 108 | return test_seq_int_delete(self, index); 109 | } 110 | 111 | extern fn test_seq_int_clear(self: *SeqInt) callconv(.C) void; 112 | pub inline fn clear(self: *SeqInt) void { 113 | return test_seq_int_clear(self); 114 | } 115 | }; 116 | 117 | pub const RefObjWithSeq = opaque { 118 | extern fn test_ref_obj_with_seq_unref(self: *RefObjWithSeq) callconv(.C) void; 119 | pub inline fn deinit(self: *RefObjWithSeq) void { 120 | return test_ref_obj_with_seq_unref(self); 121 | } 122 | 123 | extern fn test_new_ref_obj_with_seq() callconv(.C) *RefObjWithSeq; 124 | /// Creates new SimpleRefObj. 125 | pub inline fn init() *RefObjWithSeq { 126 | return test_new_ref_obj_with_seq(); 127 | } 128 | 129 | extern fn test_ref_obj_with_seq_data_len(self: *RefObjWithSeq) callconv(.C) isize; 130 | pub inline fn lenData(self: *RefObjWithSeq) isize { 131 | return test_ref_obj_with_seq_data_len(self); 132 | } 133 | 134 | extern fn test_ref_obj_with_seq_data_get(self: *RefObjWithSeq, index: isize) callconv(.C) u8; 135 | pub inline fn getData(self: *RefObjWithSeq, index: isize) u8 { 136 | return test_ref_obj_with_seq_data_get(self, index); 137 | } 138 | 139 | extern fn test_ref_obj_with_seq_data_set(self: *RefObjWithSeq, index: isize, value: u8) callconv(.C) void; 140 | pub inline fn setData(self: *RefObjWithSeq, index: isize, value: u8) void { 141 | return test_ref_obj_with_seq_data_set(self, index, value); 142 | } 143 | 144 | extern fn test_ref_obj_with_seq_data_add(self: *RefObjWithSeq, value: u8) callconv(.C) void; 145 | pub inline fn appendData(self: *RefObjWithSeq, value: u8) void { 146 | return test_ref_obj_with_seq_data_add(self, value); 147 | } 148 | 149 | extern fn test_ref_obj_with_seq_data_delete(self: *RefObjWithSeq, index: isize) callconv(.C) void; 150 | pub inline fn removeData(self: *RefObjWithSeq, index: isize) void { 151 | return test_ref_obj_with_seq_data_delete(self, index); 152 | } 153 | 154 | extern fn test_ref_obj_with_seq_data_clear(self: *RefObjWithSeq) callconv(.C) void; 155 | pub inline fn clearData(self: *RefObjWithSeq) void { 156 | return test_ref_obj_with_seq_data_clear(self); 157 | } 158 | }; 159 | 160 | pub const SimpleObjWithProc = extern struct { 161 | simple_a: isize, 162 | simple_b: u8, 163 | simple_c: bool, 164 | 165 | pub fn init(simple_a: isize, simple_b: u8, simple_c: bool) SimpleObjWithProc { 166 | return SimpleObjWithProc{ 167 | .simple_a = simple_a, 168 | .simple_b = simple_b, 169 | .simple_c = simple_c, 170 | }; 171 | } 172 | 173 | extern fn test_simple_obj_with_proc_eq(self: SimpleObjWithProc, other: SimpleObjWithProc) callconv(.C) bool; 174 | pub inline fn eql(self: SimpleObjWithProc, other: SimpleObjWithProc) bool { 175 | return test_simple_obj_with_proc_eq(self, other); 176 | } 177 | 178 | extern fn test_simple_obj_with_proc_extra_proc(self: SimpleObjWithProc) callconv(.C) void; 179 | pub inline fn extraProc(self: SimpleObjWithProc) void { 180 | return test_simple_obj_with_proc_extra_proc(self); 181 | } 182 | }; 183 | 184 | pub const SeqString = opaque { 185 | extern fn test_seq_string_unref(self: *SeqString) callconv(.C) void; 186 | pub inline fn deinit(self: *SeqString) void { 187 | return test_seq_string_unref(self); 188 | } 189 | 190 | extern fn test_new_seq_string() callconv(.C) *SeqString; 191 | pub inline fn init() *SeqString { 192 | return test_new_seq_string(); 193 | } 194 | 195 | extern fn test_seq_string_len(self: *SeqString) callconv(.C) isize; 196 | pub inline fn len(self: *SeqString) isize { 197 | return test_seq_string_len(self); 198 | } 199 | 200 | extern fn test_seq_string_get(self: *SeqString, index: isize) callconv(.C) [*:0]const u8; 201 | pub inline fn get(self: *SeqString, index: isize) [:0]const u8 { 202 | return std.mem.span(test_seq_string_get(self, index)); 203 | } 204 | 205 | extern fn test_seq_string_set(self: *SeqString, index: isize, value: [*:0]const u8) callconv(.C) void; 206 | pub inline fn set(self: *SeqString, index: isize, value: [:0]const u8) void { 207 | return test_seq_string_set(self, index, value.ptr); 208 | } 209 | 210 | extern fn test_seq_string_add(self: *SeqString, value: [*:0]const u8) callconv(.C) void; 211 | pub inline fn append(self: *SeqString, value: [:0]const u8) void { 212 | return test_seq_string_add(self, value.ptr); 213 | } 214 | 215 | extern fn test_seq_string_delete(self: *SeqString, index: isize) callconv(.C) void; 216 | pub inline fn remove(self: *SeqString, index: isize) void { 217 | return test_seq_string_delete(self, index); 218 | } 219 | 220 | extern fn test_seq_string_clear(self: *SeqString) callconv(.C) void; 221 | pub inline fn clear(self: *SeqString) void { 222 | return test_seq_string_clear(self); 223 | } 224 | }; 225 | 226 | extern fn test_get_datas() callconv(.C) *SeqString; 227 | pub inline fn getDatas() *SeqString { 228 | return test_get_datas(); 229 | } 230 | 231 | -------------------------------------------------------------------------------- /tests/generated/test.js: -------------------------------------------------------------------------------- 1 | var ffi = require('ffi-napi'); 2 | var Struct = require("ref-struct-napi"); 3 | var ArrayType = require('ref-array-napi'); 4 | 5 | var dll = {}; 6 | 7 | function testException(message) { 8 | this.message = message; 9 | this.name = 'testException'; 10 | } 11 | 12 | const SimpleEnum = 'int8' 13 | 14 | /** 15 | * Returns the integer passed in. 16 | */ 17 | function simpleCall(a){ 18 | result = dll.test_simple_call(a) 19 | return result 20 | } 21 | 22 | const SimpleObj = Struct({ 23 | 'simpleA':'int64', 24 | 'simpleB':'int8', 25 | 'simpleC':'bool' 26 | }) 27 | simpleObj = function(simple_a, simple_b, simple_c){ 28 | var v = new SimpleObj(); 29 | v.simple_a = simple_a 30 | v.simple_b = simple_b 31 | v.simple_c = simple_c 32 | return v; 33 | } 34 | SimpleObj.prototype.isEqual = function(other){ 35 | return self.simpleA == other.simpleA && self.simpleB == other.simpleB && self.simpleC == other.simpleC; 36 | }; 37 | 38 | SimpleRefObj = Struct({'nimRef': 'uint64'}); 39 | SimpleRefObj.prototype.isNull = function(){ 40 | return this.nimRef == 0; 41 | }; 42 | SimpleRefObj.prototype.isEqual = function(other){ 43 | return this.nimRef == other.nimRef; 44 | }; 45 | SimpleRefObj.prototype.unref = function(){ 46 | return dll.test_simple_ref_obj_unref(this) 47 | }; 48 | function newSimpleRefObj(){ 49 | var result = dll.test_new_simple_ref_obj() 50 | const registry = new FinalizationRegistry(function(obj) { 51 | console.log("js unref") 52 | obj.unref() 53 | }); 54 | registry.register(result, null); 55 | return result 56 | } 57 | Object.defineProperty(SimpleRefObj.prototype, 'simpleRefA', { 58 | get: function() {return dll.test_simple_ref_obj_get_simple_ref_a(this)}, 59 | set: function(v) {dll.test_simple_ref_obj_set_simple_ref_a(this, v)} 60 | }); 61 | Object.defineProperty(SimpleRefObj.prototype, 'simpleRefB', { 62 | get: function() {return dll.test_simple_ref_obj_get_simple_ref_b(this)}, 63 | set: function(v) {dll.test_simple_ref_obj_set_simple_ref_b(this, v)} 64 | }); 65 | 66 | /** 67 | * Does some thing with SimpleRefObj. 68 | */ 69 | SimpleRefObj.prototype.doit = function(){ 70 | dll.test_simple_ref_obj_doit(this) 71 | } 72 | 73 | SeqInt = Struct({'nimRef': 'uint64'}); 74 | SeqInt.prototype.isNull = function(){ 75 | return this.nimRef == 0; 76 | }; 77 | SeqInt.prototype.isEqual = function(other){ 78 | return this.nimRef == other.nimRef; 79 | }; 80 | SeqInt.prototype.unref = function(){ 81 | return dll.test_seq_int_unref(this) 82 | }; 83 | function seqInt(){ 84 | return dll.test_new_seq_int(); 85 | } 86 | SeqInt.prototype.length = function(){ 87 | return dll.test_seq_int_len(this) 88 | }; 89 | SeqInt.prototype.get = function(index){ 90 | return dll.test_seq_int_get(this, index) 91 | }; 92 | SeqInt.prototype.set = function(index, value){ 93 | dll.test_seq_int_set(this, index, value) 94 | }; 95 | SeqInt.prototype.delete = function(index){ 96 | dll.test_seq_int_delete(this, index) 97 | }; 98 | SeqInt.prototype.add = function(value){ 99 | dll.test_seq_int_add(this, value) 100 | }; 101 | SeqInt.prototype.clear = function(){ 102 | dll.test_seq_int_clear(this) 103 | }; 104 | RefObjWithSeq = Struct({'nimRef': 'uint64'}); 105 | RefObjWithSeq.prototype.isNull = function(){ 106 | return this.nimRef == 0; 107 | }; 108 | RefObjWithSeq.prototype.isEqual = function(other){ 109 | return this.nimRef == other.nimRef; 110 | }; 111 | RefObjWithSeq.prototype.unref = function(){ 112 | return dll.test_ref_obj_with_seq_unref(this) 113 | }; 114 | function newRefObjWithSeq(){ 115 | var result = dll.test_new_ref_obj_with_seq() 116 | const registry = new FinalizationRegistry(function(obj) { 117 | console.log("js unref") 118 | obj.unref() 119 | }); 120 | registry.register(result, null); 121 | return result 122 | } 123 | function RefObjWithSeqData(refObjWithSeq){ 124 | this.refObjWithSeq = refObjWithSeq; 125 | } 126 | RefObjWithSeqData.prototype.length = function(){ 127 | return dll.test_ref_obj_with_seq_data_len(this.ref_obj_with_seq) 128 | }; 129 | RefObjWithSeqData.prototype.get = function(index){ 130 | return dll.test_ref_obj_with_seq_data_get(this.ref_obj_with_seq, index) 131 | }; 132 | RefObjWithSeqData.prototype.set = function(index, value){ 133 | dll.test_ref_obj_with_seq_data_set(this.ref_obj_with_seq, index, value) 134 | }; 135 | RefObjWithSeqData.prototype.delete = function(index){ 136 | dll.test_ref_obj_with_seq_data_delete(this.ref_obj_with_seq, index) 137 | }; 138 | RefObjWithSeqData.prototype.add = function(value){ 139 | dll.test_ref_obj_with_seq_data_add(this.ref_obj_with_seq, value) 140 | }; 141 | RefObjWithSeqData.prototype.clear = function(){ 142 | dll.test_ref_obj_with_seq_data_clear(this.ref_obj_with_seq) 143 | }; 144 | Object.defineProperty(RefObjWithSeq.prototype, 'data', { 145 | get: function() {return new RefObjWithSeqData(this)}, 146 | }); 147 | 148 | const SimpleObjWithProc = Struct({ 149 | 'simpleA':'int64', 150 | 'simpleB':'int8', 151 | 'simpleC':'bool' 152 | }) 153 | simpleObjWithProc = function(simple_a, simple_b, simple_c){ 154 | var v = new SimpleObjWithProc(); 155 | v.simple_a = simple_a 156 | v.simple_b = simple_b 157 | v.simple_c = simple_c 158 | return v; 159 | } 160 | SimpleObjWithProc.prototype.isEqual = function(other){ 161 | return self.simpleA == other.simpleA && self.simpleB == other.simpleB && self.simpleC == other.simpleC; 162 | }; 163 | 164 | SimpleObjWithProc.prototype.extraProc = function(){ 165 | dll.test_simple_obj_with_proc_extra_proc(this) 166 | } 167 | 168 | SeqString = Struct({'nimRef': 'uint64'}); 169 | SeqString.prototype.isNull = function(){ 170 | return this.nimRef == 0; 171 | }; 172 | SeqString.prototype.isEqual = function(other){ 173 | return this.nimRef == other.nimRef; 174 | }; 175 | SeqString.prototype.unref = function(){ 176 | return dll.test_seq_string_unref(this) 177 | }; 178 | function seqString(){ 179 | return dll.test_new_seq_string(); 180 | } 181 | SeqString.prototype.length = function(){ 182 | return dll.test_seq_string_len(this) 183 | }; 184 | SeqString.prototype.get = function(index){ 185 | return dll.test_seq_string_get(this, index) 186 | }; 187 | SeqString.prototype.set = function(index, value){ 188 | dll.test_seq_string_set(this, index, value) 189 | }; 190 | SeqString.prototype.delete = function(index){ 191 | dll.test_seq_string_delete(this, index) 192 | }; 193 | SeqString.prototype.add = function(value){ 194 | dll.test_seq_string_add(this, value) 195 | }; 196 | SeqString.prototype.clear = function(){ 197 | dll.test_seq_string_clear(this) 198 | }; 199 | function getDatas(){ 200 | result = dll.test_get_datas() 201 | return result 202 | } 203 | 204 | 205 | var dllPath = "" 206 | if(process.platform == "win32") { 207 | dllPath = __dirname + '/test.dll' 208 | } else if (process.platform == "darwin") { 209 | dllPath = __dirname + '/libtest.dylib' 210 | } else { 211 | dllPath = __dirname + '/libtest.so' 212 | } 213 | 214 | dll = ffi.Library(dllPath, { 215 | 'test_simple_call': ['int64', ['int64']], 216 | 'test_simple_ref_obj_unref': ['void', [SimpleRefObj]], 217 | 'test_new_simple_ref_obj': [SimpleRefObj, []], 218 | 'test_simple_ref_obj_get_simple_ref_a': ['int64', [SimpleRefObj]], 219 | 'test_simple_ref_obj_set_simple_ref_a': ['void', [SimpleRefObj, 'int64']], 220 | 'test_simple_ref_obj_get_simple_ref_b': ['int8', [SimpleRefObj]], 221 | 'test_simple_ref_obj_set_simple_ref_b': ['void', [SimpleRefObj, 'int8']], 222 | 'test_simple_ref_obj_doit': ['void', [SimpleRefObj]], 223 | 'test_seq_int_unref': ['void', [SeqInt]], 224 | 'test_new_seq_int': [SeqInt, []], 225 | 'test_seq_int_len': ['uint64', [SeqInt]], 226 | 'test_seq_int_get': ['int64', [SeqInt, 'uint64']], 227 | 'test_seq_int_set': ['void', [SeqInt, 'uint64', 'int64']], 228 | 'test_seq_int_delete': ['void', [SeqInt, 'uint64']], 229 | 'test_seq_int_add': ['void', [SeqInt, 'int64']], 230 | 'test_seq_int_clear': ['void', [SeqInt]], 231 | 'test_ref_obj_with_seq_unref': ['void', [RefObjWithSeq]], 232 | 'test_new_ref_obj_with_seq': [RefObjWithSeq, []], 233 | 'test_ref_obj_with_seq_data_len': ['uint64', [RefObjWithSeq]], 234 | 'test_ref_obj_with_seq_data_get': ['int8', [RefObjWithSeq, 'uint64']], 235 | 'test_ref_obj_with_seq_data_set': ['void', [RefObjWithSeq, 'uint64', 'int8']], 236 | 'test_ref_obj_with_seq_data_delete': ['void', [RefObjWithSeq, 'uint64']], 237 | 'test_ref_obj_with_seq_data_add': ['void', [RefObjWithSeq, 'int8']], 238 | 'test_ref_obj_with_seq_data_clear': ['void', [RefObjWithSeq]], 239 | 'test_simple_obj_with_proc_extra_proc': ['void', [SimpleObjWithProc]], 240 | 'test_seq_string_unref': ['void', [SeqString]], 241 | 'test_new_seq_string': [SeqString, []], 242 | 'test_seq_string_len': ['uint64', [SeqString]], 243 | 'test_seq_string_get': ['string', [SeqString, 'uint64']], 244 | 'test_seq_string_set': ['void', [SeqString, 'uint64', 'string']], 245 | 'test_seq_string_delete': ['void', [SeqString, 'uint64']], 246 | 'test_seq_string_add': ['void', [SeqString, 'string']], 247 | 'test_seq_string_clear': ['void', [SeqString]], 248 | 'test_get_datas': [SeqString, []], 249 | }); 250 | 251 | exports.SIMPLE_CONST = 123 252 | exports.SimpleEnum = SimpleEnum 253 | exports.FIRST = 0 254 | exports.SECOND = 1 255 | exports.THIRD = 2 256 | exports.simpleCall = simpleCall 257 | exports.SimpleObj = SimpleObj; 258 | exports.simpleObj = simpleObj; 259 | exports.SimpleRefObjType = SimpleRefObj 260 | exports.SimpleRefObj = newSimpleRefObj 261 | exports.SeqIntType = SeqInt 262 | exports.RefObjWithSeqType = RefObjWithSeq 263 | exports.RefObjWithSeq = newRefObjWithSeq 264 | exports.SimpleObjWithProc = SimpleObjWithProc; 265 | exports.simpleObjWithProc = simpleObjWithProc; 266 | exports.SeqStringType = SeqString 267 | exports.getDatas = getDatas 268 | -------------------------------------------------------------------------------- /src/genny/internal.nim: -------------------------------------------------------------------------------- 1 | import 2 | std/[os, strformat, strutils, macros], 3 | common, languages/nim 4 | 5 | const exportProcPragmas = "{.raises: [], cdecl, exportc, dynlib.}" 6 | 7 | var internal {.compiletime.}: string 8 | 9 | proc exportConstInternal*(sym: NimNode) = 10 | discard 11 | 12 | proc exportEnumInternal*(sym: NimNode) = 13 | discard 14 | 15 | proc exportProcInternal*( 16 | sym: NimNode, 17 | owner: NimNode = nil, 18 | prefixes: openarray[NimNode] = [] 19 | ) = 20 | let 21 | procName = sym.repr 22 | procNameSnaked = toSnakeCase(procName) 23 | procType = sym.getTypeInst() 24 | procParams = procType[0][1 .. ^1] 25 | procReturn = procType[0][0] 26 | procRaises = sym.raises() 27 | procReturnsSeq = procReturn.kind == nnkBracketExpr 28 | 29 | var apiProcName = &"$lib_" 30 | if owner != nil: 31 | apiProcName.add &"{toSnakeCase(owner.getName())}_" 32 | for prefix in prefixes: 33 | apiProcName.add &"{toSnakeCase(prefix.getName())}_" 34 | apiProcName.add &"{procNameSnaked}" 35 | 36 | internal.add &"proc {apiProcName}*(" 37 | for param in procParams: 38 | for i in 0 .. param.len - 3: 39 | var paramType = param[^2] 40 | if paramType.repr.endsWith(":type"): 41 | paramType = prefixes[0] 42 | internal.add &"{toSnakeCase(param[i].repr)}: {exportTypeNim(paramType)}, " 43 | internal.removeSuffix ", " 44 | internal.add ")" 45 | if procReturn.kind != nnkEmpty: 46 | internal.add &": {exportTypeNim(procReturn)}" 47 | internal.add &" {exportProcPragmas} =\n" 48 | if procRaises: 49 | internal.add " try:\n " 50 | if procRaises and procReturn.kind != nnkEmpty: 51 | internal.add " result = " 52 | else: 53 | internal.add " " 54 | if procReturnsSeq: 55 | internal.add &"{procReturn.getSeqName()}(s: " 56 | internal.add &"{procName}(" 57 | for param in procParams: 58 | for i in 0 .. param.len - 3: 59 | internal.add &"{toSnakeCase(param[i].repr)}" 60 | internal.add &"{convertImportToNim(param[^2])}, " 61 | internal.removeSuffix ", " 62 | internal.add ")" 63 | if procReturnsSeq: 64 | internal.add ")" 65 | if procReturn.kind != nnkEmpty: 66 | internal.add convertExportFromNim(procReturn) 67 | if procRaises: 68 | internal.add "\n" 69 | internal.add " except $LibError as e:\n" 70 | internal.add " lastError = e" 71 | internal.add "\n" 72 | internal.add "\n" 73 | 74 | proc exportObjectInternal*(sym: NimNode, constructor: NimNode) = 75 | let 76 | objName = sym.repr 77 | objNameSnaked = toSnakeCase(objName) 78 | 79 | if constructor != nil: 80 | let constructorType = constructor.getTypeInst() 81 | 82 | internal.add &"proc $lib_{objNameSnaked}*(" 83 | for param in constructorType[0][1 .. ^1]: 84 | internal.add &"{param[0].repr.split('`')[0]}: {param[1].repr}, " 85 | internal.removeSuffix ", " 86 | internal.add &"): {objName} {exportProcPragmas} =\n" 87 | internal.add &" {constructor.repr}(" 88 | for param in constructorType[0][1 .. ^1]: 89 | internal.add &"{param[0].repr.split('`')[0]}, " 90 | internal.removeSuffix ", " 91 | internal.add ")\n" 92 | internal.add "\n" 93 | else: 94 | internal.add &"proc $lib_{objNameSnaked}*(" 95 | let objType = sym.getType() 96 | for fieldSym in objType[2]: 97 | let 98 | fieldName = fieldSym.repr 99 | fieldType = fieldSym.getTypeInst() 100 | internal.add &"{toSnakeCase(fieldName)}: {exportTypeNim(fieldType)}, " 101 | internal.removeSuffix ", " 102 | internal.add &"): {objName} {exportProcPragmas} =\n" 103 | for fieldSym in objType[2]: 104 | let 105 | fieldName = fieldSym.repr 106 | internal.add &" result.{toSnakeCase(fieldName)} = {toSnakeCase(fieldName)}\n" 107 | internal.add "\n" 108 | 109 | internal.add &"proc $lib_{objNameSnaked}_eq*(a, b: {objName}): bool {exportProcPragmas}=\n" 110 | let objType = sym.getType() 111 | internal.add " " 112 | for fieldSym in objType[2]: 113 | let 114 | fieldName = fieldSym.repr 115 | internal.add &"a.{toSnakeCase(fieldName)} == b.{toSnakeCase(fieldName)} and " 116 | internal.removeSuffix " and " 117 | internal.add "\n\n" 118 | 119 | proc exportRefObjectInternal*( 120 | sym: NimNode, 121 | fields: seq[(string, NimNode)], 122 | constructor: NimNode 123 | ) = 124 | let 125 | objName = sym.repr 126 | objNameSnaked = toSnakeCase(objName) 127 | objType = sym.getType()[1].getType() 128 | 129 | internal.add &"proc $lib_{objNameSnaked}_unref*(x: {objName}) {exportProcPragmas}" 130 | internal.add " =\n" 131 | internal.add " GC_unref(x)\n" 132 | internal.add "\n" 133 | 134 | if constructor != nil: 135 | exportProcInternal(constructor) 136 | 137 | for (fieldName, fieldType) in fields: 138 | let fieldNameSnaked = toSnakeCase(fieldName) 139 | 140 | if fieldType.kind != nnkBracketExpr: 141 | internal.add "proc " 142 | internal.add &"$lib_{objNameSnaked}_get_{fieldNameSnaked}*" 143 | internal.add &"({objNameSnaked}: {objName}): {exportTypeNim(fieldType)}" 144 | internal.add &" {exportProcPragmas} =\n" 145 | internal.add &" {objNameSnaked}.{fieldName}" 146 | internal.add convertExportFromNim(fieldType) 147 | internal.add "\n" 148 | internal.add "\n" 149 | 150 | internal.add "proc " 151 | internal.add &"$lib_{objNameSnaked}_set_{fieldNameSnaked}*" 152 | internal.add &"({objNameSnaked}: {objName}, " 153 | internal.add &"{fieldName}: {exportTypeNim(fieldType)})" 154 | internal.add &" {exportProcPragmas} =\n" 155 | internal.add &" {objNameSnaked}.{fieldName} = {fieldName}" 156 | internal.add convertImportToNim(fieldType) 157 | internal.add "\n" 158 | internal.add "\n" 159 | else: 160 | let prefix = &"$lib_{objNameSnaked}_{fieldNameSnaked}" 161 | 162 | internal.add &"proc {prefix}_len*({objNameSnaked}: {objName}): int" 163 | internal.add &" {exportProcPragmas} =\n" 164 | internal.add &" {objNameSnaked}.{fieldName}.len\n" 165 | internal.add "\n" 166 | 167 | internal.add &"proc {prefix}_add*({objNameSnaked}: {objName}, v: {fieldType[1].repr})" 168 | internal.add &" {exportProcPragmas} =\n" 169 | internal.add &" {objNameSnaked}.{fieldName}.add(v)\n" 170 | internal.add "\n" 171 | 172 | internal.add &"proc {prefix}_get*({objNameSnaked}: {objName}, i: int): {fieldType[1].repr}" 173 | internal.add &" {exportProcPragmas} =\n" 174 | internal.add &" {objNameSnaked}.{fieldName}[i]\n" 175 | internal.add "\n" 176 | 177 | internal.add &"proc {prefix}_set*({objNameSnaked}: {objName}, i: int, v: {fieldType[1].repr})" 178 | internal.add &" {exportProcPragmas} =\n" 179 | internal.add &" {objNameSnaked}.{fieldName}[i] = v\n" 180 | internal.add "\n" 181 | 182 | internal.add &"proc {prefix}_delete*({objNameSnaked}: {objName}, i: int)" 183 | internal.add &" {exportProcPragmas} =\n" 184 | internal.add &" {objNameSnaked}.{fieldName}.delete(i)\n" 185 | internal.add "\n" 186 | 187 | internal.add &"proc {prefix}_clear*({objNameSnaked}: {objName})" 188 | internal.add &" {exportProcPragmas} =\n" 189 | internal.add &" {objNameSnaked}.{fieldName}.setLen(0)\n" 190 | internal.add "\n" 191 | 192 | proc generateSeqs(sym: NimNode) = 193 | let 194 | seqName = sym.getSeqName() 195 | seqNameSnaked = toSnakeCase(seqName) 196 | 197 | internal.add &"type {seqName}* = ref object\n" 198 | internal.add &" s: {sym.repr}\n" 199 | internal.add "\n" 200 | 201 | internal.add &"proc $lib_new_{seqNameSnaked}*(): {seqName}" 202 | internal.add &" {exportProcPragmas}" 203 | internal.add " =\n" 204 | internal.add &" {seqName}()\n" 205 | internal.add "\n" 206 | 207 | internal.add &"proc $lib_{seqNameSnaked}_len*(s: {seqName}): int" 208 | internal.add &" {exportProcPragmas}" 209 | internal.add " =\n" 210 | internal.add " s.s.len\n" 211 | internal.add "\n" 212 | 213 | internal.add &"proc $lib_{seqNameSnaked}_add*(s: {seqName}, v: {exportTypeNim(sym[1])})" 214 | internal.add &" {exportProcPragmas}" 215 | internal.add " =\n" 216 | internal.add &" s.s.add(v{convertImportToNim(sym[1])})\n" 217 | internal.add "\n" 218 | 219 | internal.add &"proc $lib_{seqNameSnaked}_get*(s: {seqName}, i: int): {exportTypeNim(sym[1])}" 220 | internal.add &" {exportProcPragmas}" 221 | internal.add " =\n" 222 | internal.add &" s.s[i]{convertExportFromNim(sym[1])}\n" 223 | internal.add "\n" 224 | 225 | internal.add &"proc $lib_{seqNameSnaked}_set*(s: {seqName}, i: int, v: {exportTypeNim(sym[1])})" 226 | internal.add &" {exportProcPragmas}" 227 | internal.add " =\n" 228 | internal.add &" s.s[i] = v{convertImportToNim(sym[1])}\n" 229 | internal.add "\n" 230 | 231 | internal.add &"proc $lib_{seqNameSnaked}_delete*(s: {seqName}, i: int)" 232 | internal.add &" {exportProcPragmas}" 233 | internal.add " =\n" 234 | internal.add " s.s.delete(i)\n" 235 | internal.add "\n" 236 | 237 | internal.add &"proc $lib_{seqNameSnaked}_clear*(s: {seqName})" 238 | internal.add &" {exportProcPragmas}" 239 | internal.add " =\n" 240 | internal.add " s.s.setLen(0)\n" 241 | internal.add "\n" 242 | 243 | internal.add &"proc $lib_{seqNameSnaked}_unref*(s: {seqName})" 244 | internal.add &" {exportProcPragmas}" 245 | internal.add " =\n" 246 | internal.add " GC_unref(s)\n" 247 | internal.add "\n" 248 | 249 | proc exportSeqInternal*(sym: NimNode) = 250 | generateSeqs(sym) 251 | 252 | const header = """ 253 | when not defined(gcArc) and not defined(gcOrc): 254 | {.error: "Please use --gc:arc or --gc:orc when using Genny.".} 255 | 256 | when (NimMajor, NimMinor, NimPatch) == (1, 6, 2): 257 | {.error: "Nim 1.6.2 not supported with Genny due to FFI issues.".} 258 | """ 259 | 260 | proc writeInternal*(dir, lib: string) = 261 | createDir(dir) 262 | writeFile( 263 | &"{dir}/internal.nim", 264 | header & internal.replace("$Lib", lib).replace("$lib", toSnakeCase(lib)) 265 | ) 266 | -------------------------------------------------------------------------------- /tests/generated/test.py: -------------------------------------------------------------------------------- 1 | from ctypes import * 2 | import os, sys 3 | 4 | dir = os.path.dirname(sys.modules["test"].__file__) 5 | if sys.platform == "win32": 6 | libName = "test.dll" 7 | elif sys.platform == "darwin": 8 | libName = "libtest.dylib" 9 | else: 10 | libName = "libtest.so" 11 | dll = cdll.LoadLibrary(os.path.join(dir, libName)) 12 | 13 | class testError(Exception): 14 | pass 15 | 16 | class SeqIterator(object): 17 | def __init__(self, seq): 18 | self.idx = 0 19 | self.seq = seq 20 | def __iter__(self): 21 | return self 22 | def __next__(self): 23 | if self.idx < len(self.seq): 24 | self.idx += 1 25 | return self.seq[self.idx - 1] 26 | else: 27 | self.idx = 0 28 | raise StopIteration 29 | 30 | SIMPLE_CONST = 123 31 | 32 | SimpleEnum = c_byte 33 | FIRST = 0 34 | SECOND = 1 35 | THIRD = 2 36 | 37 | def simple_call(a): 38 | """ 39 | Returns the integer passed in. 40 | """ 41 | result = dll.test_simple_call(a) 42 | return result 43 | 44 | class SimpleObj(Structure): 45 | _fields_ = [ 46 | ("simple_a", c_longlong), 47 | ("simple_b", c_byte), 48 | ("simple_c", c_bool) 49 | ] 50 | 51 | def __init__(self, simple_a, simple_b, simple_c): 52 | self.simple_a = simple_a 53 | self.simple_b = simple_b 54 | self.simple_c = simple_c 55 | 56 | def __eq__(self, obj): 57 | return self.simple_a == obj.simple_a and self.simple_b == obj.simple_b and self.simple_c == obj.simple_c 58 | 59 | class SimpleRefObj(Structure): 60 | _fields_ = [("ref", c_ulonglong)] 61 | 62 | def __bool__(self): 63 | return self.ref != None 64 | 65 | def __eq__(self, obj): 66 | return self.ref == obj.ref 67 | 68 | def __del__(self): 69 | dll.test_simple_ref_obj_unref(self) 70 | 71 | def __init__(self): 72 | result = dll.test_new_simple_ref_obj() 73 | self.ref = result 74 | 75 | @property 76 | def simple_ref_a(self): 77 | return dll.test_simple_ref_obj_get_simple_ref_a(self) 78 | 79 | @simple_ref_a.setter 80 | def simple_ref_a(self, simple_ref_a): 81 | dll.test_simple_ref_obj_set_simple_ref_a(self, simple_ref_a) 82 | 83 | @property 84 | def simple_ref_b(self): 85 | return dll.test_simple_ref_obj_get_simple_ref_b(self) 86 | 87 | @simple_ref_b.setter 88 | def simple_ref_b(self, simple_ref_b): 89 | dll.test_simple_ref_obj_set_simple_ref_b(self, simple_ref_b) 90 | 91 | def doit(self): 92 | """ 93 | Does some thing with SimpleRefObj. 94 | """ 95 | dll.test_simple_ref_obj_doit(self) 96 | 97 | class SeqInt(Structure): 98 | _fields_ = [("ref", c_ulonglong)] 99 | 100 | def __bool__(self): 101 | return self.ref != None 102 | 103 | def __eq__(self, obj): 104 | return self.ref == obj.ref 105 | 106 | def __del__(self): 107 | dll.test_seq_int_unref(self) 108 | 109 | def __init__(self): 110 | self.ref = dll.test_new_seq_int() 111 | 112 | def __len__(self): 113 | return dll.test_seq_int_len(self) 114 | 115 | def __getitem__(self, index): 116 | return dll.test_seq_int_get(self, index) 117 | 118 | def __setitem__(self, index, value): 119 | dll.test_seq_int_set(self, index, value) 120 | 121 | def __delitem__(self, index): 122 | dll.test_seq_int_delete(self, index) 123 | 124 | def append(self, value): 125 | dll.test_seq_int_add(self, value) 126 | 127 | def clear(self): 128 | dll.test_seq_int_clear(self) 129 | 130 | def __iter__(self): 131 | return SeqIterator(self) 132 | 133 | class RefObjWithSeq(Structure): 134 | _fields_ = [("ref", c_ulonglong)] 135 | 136 | def __bool__(self): 137 | return self.ref != None 138 | 139 | def __eq__(self, obj): 140 | return self.ref == obj.ref 141 | 142 | def __del__(self): 143 | dll.test_ref_obj_with_seq_unref(self) 144 | 145 | def __init__(self): 146 | result = dll.test_new_ref_obj_with_seq() 147 | self.ref = result 148 | 149 | class RefObjWithSeqData: 150 | 151 | def __init__(self, ref_obj_with_seq): 152 | self.ref_obj_with_seq = ref_obj_with_seq 153 | 154 | def __len__(self): 155 | return dll.test_ref_obj_with_seq_data_len(self.ref_obj_with_seq) 156 | 157 | def __getitem__(self, index): 158 | return dll.test_ref_obj_with_seq_data_get(self.ref_obj_with_seq, index) 159 | 160 | def __setitem__(self, index, value): 161 | dll.test_ref_obj_with_seq_data_set(self.ref_obj_with_seq, index, value) 162 | 163 | def __delitem__(self, index): 164 | dll.test_ref_obj_with_seq_data_delete(self.ref_obj_with_seq, index) 165 | 166 | def append(self, value): 167 | dll.test_ref_obj_with_seq_data_add(self.ref_obj_with_seq, value) 168 | 169 | def clear(self): 170 | dll.test_ref_obj_with_seq_data_clear(self.ref_obj_with_seq) 171 | 172 | def __iter__(self): 173 | return SeqIterator(self) 174 | 175 | @property 176 | def data(self): 177 | return self.RefObjWithSeqData(self) 178 | 179 | class SimpleObjWithProc(Structure): 180 | _fields_ = [ 181 | ("simple_a", c_longlong), 182 | ("simple_b", c_byte), 183 | ("simple_c", c_bool) 184 | ] 185 | 186 | def __init__(self, simple_a, simple_b, simple_c): 187 | self.simple_a = simple_a 188 | self.simple_b = simple_b 189 | self.simple_c = simple_c 190 | 191 | def __eq__(self, obj): 192 | return self.simple_a == obj.simple_a and self.simple_b == obj.simple_b and self.simple_c == obj.simple_c 193 | 194 | def extra_proc(self): 195 | dll.test_simple_obj_with_proc_extra_proc(self) 196 | 197 | class SeqString(Structure): 198 | _fields_ = [("ref", c_ulonglong)] 199 | 200 | def __bool__(self): 201 | return self.ref != None 202 | 203 | def __eq__(self, obj): 204 | return self.ref == obj.ref 205 | 206 | def __del__(self): 207 | dll.test_seq_string_unref(self) 208 | 209 | def __init__(self): 210 | self.ref = dll.test_new_seq_string() 211 | 212 | def __len__(self): 213 | return dll.test_seq_string_len(self) 214 | 215 | def __getitem__(self, index): 216 | return dll.test_seq_string_get(self, index).decode("utf8") 217 | 218 | def __setitem__(self, index, value): 219 | dll.test_seq_string_set(self, index, value.encode("utf8")) 220 | 221 | def __delitem__(self, index): 222 | dll.test_seq_string_delete(self, index) 223 | 224 | def append(self, value): 225 | dll.test_seq_string_add(self, value) 226 | 227 | def clear(self): 228 | dll.test_seq_string_clear(self) 229 | 230 | def __iter__(self): 231 | return SeqIterator(self) 232 | 233 | def get_datas(): 234 | result = dll.test_get_datas() 235 | return result 236 | 237 | dll.test_simple_call.argtypes = [c_longlong] 238 | dll.test_simple_call.restype = c_longlong 239 | 240 | dll.test_simple_ref_obj_unref.argtypes = [SimpleRefObj] 241 | dll.test_simple_ref_obj_unref.restype = None 242 | 243 | dll.test_new_simple_ref_obj.argtypes = [] 244 | dll.test_new_simple_ref_obj.restype = c_ulonglong 245 | 246 | dll.test_simple_ref_obj_get_simple_ref_a.argtypes = [SimpleRefObj] 247 | dll.test_simple_ref_obj_get_simple_ref_a.restype = c_longlong 248 | 249 | dll.test_simple_ref_obj_set_simple_ref_a.argtypes = [SimpleRefObj, c_longlong] 250 | dll.test_simple_ref_obj_set_simple_ref_a.restype = None 251 | 252 | dll.test_simple_ref_obj_get_simple_ref_b.argtypes = [SimpleRefObj] 253 | dll.test_simple_ref_obj_get_simple_ref_b.restype = c_byte 254 | 255 | dll.test_simple_ref_obj_set_simple_ref_b.argtypes = [SimpleRefObj, c_byte] 256 | dll.test_simple_ref_obj_set_simple_ref_b.restype = None 257 | 258 | dll.test_simple_ref_obj_doit.argtypes = [SimpleRefObj] 259 | dll.test_simple_ref_obj_doit.restype = None 260 | 261 | dll.test_seq_int_unref.argtypes = [SeqInt] 262 | dll.test_seq_int_unref.restype = None 263 | 264 | dll.test_new_seq_int.argtypes = [] 265 | dll.test_new_seq_int.restype = c_ulonglong 266 | 267 | dll.test_seq_int_len.argtypes = [SeqInt] 268 | dll.test_seq_int_len.restype = c_longlong 269 | 270 | dll.test_seq_int_get.argtypes = [SeqInt, c_longlong] 271 | dll.test_seq_int_get.restype = c_longlong 272 | 273 | dll.test_seq_int_set.argtypes = [SeqInt, c_longlong, c_longlong] 274 | dll.test_seq_int_set.restype = None 275 | 276 | dll.test_seq_int_delete.argtypes = [SeqInt, c_longlong] 277 | dll.test_seq_int_delete.restype = None 278 | 279 | dll.test_seq_int_add.argtypes = [SeqInt, c_longlong] 280 | dll.test_seq_int_add.restype = None 281 | 282 | dll.test_seq_int_clear.argtypes = [SeqInt] 283 | dll.test_seq_int_clear.restype = None 284 | 285 | dll.test_ref_obj_with_seq_unref.argtypes = [RefObjWithSeq] 286 | dll.test_ref_obj_with_seq_unref.restype = None 287 | 288 | dll.test_new_ref_obj_with_seq.argtypes = [] 289 | dll.test_new_ref_obj_with_seq.restype = c_ulonglong 290 | 291 | dll.test_ref_obj_with_seq_data_len.argtypes = [RefObjWithSeq] 292 | dll.test_ref_obj_with_seq_data_len.restype = c_longlong 293 | 294 | dll.test_ref_obj_with_seq_data_get.argtypes = [RefObjWithSeq, c_longlong] 295 | dll.test_ref_obj_with_seq_data_get.restype = c_byte 296 | 297 | dll.test_ref_obj_with_seq_data_set.argtypes = [RefObjWithSeq, c_longlong, c_byte] 298 | dll.test_ref_obj_with_seq_data_set.restype = None 299 | 300 | dll.test_ref_obj_with_seq_data_delete.argtypes = [RefObjWithSeq, c_longlong] 301 | dll.test_ref_obj_with_seq_data_delete.restype = None 302 | 303 | dll.test_ref_obj_with_seq_data_add.argtypes = [RefObjWithSeq, c_byte] 304 | dll.test_ref_obj_with_seq_data_add.restype = None 305 | 306 | dll.test_ref_obj_with_seq_data_clear.argtypes = [RefObjWithSeq] 307 | dll.test_ref_obj_with_seq_data_clear.restype = None 308 | 309 | dll.test_simple_obj_with_proc_extra_proc.argtypes = [SimpleObjWithProc] 310 | dll.test_simple_obj_with_proc_extra_proc.restype = None 311 | 312 | dll.test_seq_string_unref.argtypes = [SeqString] 313 | dll.test_seq_string_unref.restype = None 314 | 315 | dll.test_new_seq_string.argtypes = [] 316 | dll.test_new_seq_string.restype = c_ulonglong 317 | 318 | dll.test_seq_string_len.argtypes = [SeqString] 319 | dll.test_seq_string_len.restype = c_longlong 320 | 321 | dll.test_seq_string_get.argtypes = [SeqString, c_longlong] 322 | dll.test_seq_string_get.restype = c_char_p 323 | 324 | dll.test_seq_string_set.argtypes = [SeqString, c_longlong, c_char_p] 325 | dll.test_seq_string_set.restype = None 326 | 327 | dll.test_seq_string_delete.argtypes = [SeqString, c_longlong] 328 | dll.test_seq_string_delete.restype = None 329 | 330 | dll.test_seq_string_add.argtypes = [SeqString, c_char_p] 331 | dll.test_seq_string_add.restype = None 332 | 333 | dll.test_seq_string_clear.argtypes = [SeqString] 334 | dll.test_seq_string_clear.restype = None 335 | 336 | dll.test_get_datas.argtypes = [] 337 | dll.test_get_datas.restype = SeqString 338 | 339 | -------------------------------------------------------------------------------- /src/genny/languages/zig.nim: -------------------------------------------------------------------------------- 1 | import 2 | std/[os, strformat, strutils, macros], 3 | ../common 4 | 5 | var 6 | code {.compiletime.}: string 7 | 8 | proc exportTypeZig(sym: NimNode): string = 9 | if sym.kind == nnkBracketExpr: 10 | if sym[0].repr == "array": 11 | let 12 | entryCount = sym[1].repr 13 | entryType = exportTypeZig(sym[2]) 14 | result = &"[{entryCount}]{entryType}" 15 | elif sym[0].repr == "seq": 16 | result = &"*{sym.getSeqName()}" 17 | else: 18 | error(&"Unexpected bracket expression {sym[0].repr}[") 19 | else: 20 | if sym.typeKind == ntyRef and sym.repr != "nil": 21 | result = &"*{sym.repr}" 22 | else: 23 | result = 24 | case sym.repr: 25 | of "string": "[:0]const u8" 26 | of "bool": "bool" 27 | of "int8": "i8" 28 | of "byte": "u8" 29 | of "int16": "i16" 30 | of "int32": "i32" 31 | of "int64": "i63" 32 | of "int": "isize" 33 | of "uint8": "u8" 34 | of "uint16": "u16" 35 | of "uint32": "u32" 36 | of "uint64": "u64" 37 | of "uint": "usize" 38 | of "float32": "f32" 39 | of "float64": "f64" 40 | of "float": "f64" 41 | of "Rune": "u21" 42 | of "Vec2": "Vector2" 43 | of "Mat3": "Matrix3" 44 | of "", "nil": "void" 45 | else: 46 | sym.repr 47 | 48 | proc convertExportFromZig*(inner: string, sym: string): string = 49 | if sym == "[:0]const u8": 50 | inner & ".ptr" 51 | else: 52 | inner 53 | 54 | proc convertImportToZig*(inner: string, sym: string): string = 55 | if sym == "[:0]const u8": 56 | "std.mem.span(" & inner & ")" 57 | else: 58 | inner 59 | 60 | proc toArgSeq(args: seq[NimNode]): seq[(string, string)] = 61 | for i, arg in args[0 .. ^1]: 62 | result.add (arg[0].repr, arg[1].exportTypeZig()) 63 | 64 | proc dllProc*(procName: string, args: openarray[string], resType: string) = 65 | discard 66 | 67 | proc exportConstZig*(sym: NimNode) = 68 | code.add &"pub const {toSnakeCase(sym.repr)} = {sym.getImpl()[2].repr};\n\n" 69 | 70 | proc exportEnumZig*(sym: NimNode) = 71 | code.add &"pub const {sym.repr} = enum(u8) " & "{\n" 72 | for i, entry in sym.getImpl()[2][1 .. ^1]: 73 | code.add &" {toCapSnakeCase(entry.repr).toLowerAscii()} = {i},\n" 74 | code.add "};\n\n" 75 | 76 | proc exportProc( 77 | procName: string, 78 | apiProcName: string, 79 | procParams: seq[(string, string)], 80 | procReturn = "", 81 | procRaises = false, 82 | owner = "", 83 | indent = false, 84 | comments = "" 85 | ) = 86 | let onClass = owner notin ["void", ""] 87 | let indent = 88 | if onClass: 89 | true 90 | else: 91 | indent 92 | 93 | if indent: 94 | code.add " " 95 | 96 | code.add &"extern fn {apiProcName}(" 97 | for i, param in procParams: 98 | if onClass and i == 0: 99 | code.add "self" 100 | else: 101 | code.add toSnakeCase(param[0]) 102 | code.add ": " 103 | code.add param[1].replace("[:0]", "[*:0]") 104 | code.add &", " 105 | code.removeSuffix ", " 106 | code.add ") callconv(.C) " 107 | if procReturn != "": 108 | code.add procReturn.replace("[:0]", "[*:0]"); 109 | else: 110 | code.add "void" 111 | code.add ";\n" 112 | 113 | if comments != "": 114 | for line in comments.split("\n"): 115 | var line = line 116 | line.removePrefix("##") 117 | if indent: 118 | code.add " " 119 | code.add "/// " & line.strip() & "\n" 120 | 121 | if indent: 122 | code.add " " 123 | 124 | code.add &"pub inline fn {procName}(" 125 | for i, param in procParams[0 .. ^1]: 126 | if onClass and i == 0: 127 | code.add "self" 128 | else: 129 | code.add toSnakeCase(param[0]) 130 | code.add ": " 131 | code.add param[1] 132 | code.add &", " 133 | code.removeSuffix ", " 134 | code.add ") " 135 | if procReturn != "": 136 | code.add procReturn; 137 | else: 138 | code.add "void" 139 | code.add " " 140 | code.add "{\n" 141 | 142 | if indent: 143 | code.add " " 144 | code.add " return " 145 | 146 | var call = "" 147 | call.add apiProcName 148 | call.add "(" 149 | for i, param in procParams: 150 | if onClass and i == 0: 151 | call.add "self" 152 | else: 153 | call.add convertExportFromZig( 154 | toSnakeCase(param[0]), 155 | param[1] 156 | ) 157 | call.add ", " 158 | call.removeSuffix ", " 159 | call.add &")" 160 | code.add convertImportToZig(call, procReturn) 161 | code.add ";\n" 162 | if indent: 163 | code.add " " 164 | code.add "}\n\n" 165 | 166 | proc exportProcZig*( 167 | sym: NimNode, 168 | owner: NimNode = nil, 169 | prefixes: openarray[NimNode] = [], 170 | indent = false, 171 | rename = "", 172 | ) = 173 | var 174 | procName = sym.repr 175 | let 176 | procNameSnaked = toSnakeCase(procName) 177 | procType = sym.getTypeInst() 178 | procParams = procType[0][1 .. ^1].toArgSeq() 179 | procReturn = procType[0][0].exportTypeZig() 180 | procRaises = sym.raises() 181 | comments = 182 | if sym.getImpl()[6][0].kind == nnkCommentStmt: 183 | sym.getImpl()[6][0].repr 184 | elif sym.getImpl[6].kind == nnkAsgn and 185 | sym.getImpl[6][1].kind == nnkStmtListExpr and 186 | sym.getImpl[6][1][0].kind == nnkCommentStmt: 187 | sym.getImpl[6][1][0].repr 188 | else: 189 | "" 190 | 191 | var apiProcName = "" 192 | apiProcName.add "$lib_" 193 | if owner != nil: 194 | apiProcName.add &"{toSnakeCase(owner.getName())}_" 195 | for prefix in prefixes: 196 | apiProcName.add &"{toSnakeCase(prefix.getName())}_" 197 | procName.add prefix.getName() 198 | apiProcName.add &"{procNameSnaked}" 199 | 200 | if rename != "": 201 | procName = rename 202 | 203 | exportProc( 204 | procName, 205 | apiProcName, 206 | procParams = procParams, 207 | procReturn = procReturn, 208 | procRaises = procRaises, 209 | owner.exportTypeZig(), 210 | indent, 211 | comments 212 | ) 213 | 214 | proc exportCloseObjectZig*() = 215 | code.removeSuffix "\n" 216 | code.add "};\n\n" 217 | 218 | proc exportObjectZig*(sym: NimNode, constructor: NimNode) = 219 | let objName = sym.repr 220 | 221 | code.add &"pub const {objName} = extern struct " & "{\n" 222 | 223 | for identDefs in sym.getImpl()[2][2]: 224 | for property in identDefs[0 .. ^3]: 225 | code.add &" {toSnakeCase(property[1].repr)}" 226 | code.add ": " 227 | code.add exportTypeZig(identDefs[^2]) 228 | code.add ",\n" 229 | code.add "\n" 230 | 231 | if constructor != nil: 232 | exportProcZig(constructor, indent = true, rename = "init") 233 | else: 234 | code.add " pub fn init(" 235 | for identDefs in sym.getImpl()[2][2]: 236 | for property in identDefs[0 .. ^3]: 237 | code.add toSnakeCase(property[1].repr) 238 | code.add ": " 239 | code.add exportTypeZig(identDefs[^2]) 240 | code.add ", " 241 | code.removeSuffix ", " 242 | code.add ") " 243 | code.add objName 244 | code.add " {\n" 245 | code.add &" return {objName}" & "{\n" 246 | for identDefs in sym.getImpl()[2][2]: 247 | for property in identDefs[0 .. ^3]: 248 | code.add &" .{toSnakeCase(property[1].repr)}" 249 | code.add " = " 250 | code.add &"{toSnakeCase(property[1].repr)}" 251 | code.add ",\n" 252 | code.add " };\n" 253 | code.add " }\n\n" 254 | 255 | exportProc( 256 | "eql", 257 | &"$lib_{toSnakeCase(objName)}_eq", 258 | @[("self", objName), ("other", objName)], 259 | "bool", 260 | indent = true 261 | ) 262 | 263 | proc genRefObject(objName: string) = 264 | code.add &"pub const {objName} = opaque " & "{\n" 265 | 266 | let unrefLibProc = &"$lib_{toSnakeCase(objName)}_unref" 267 | code.add &" extern fn {unrefLibProc}(self: *{objName}) callconv(.C) void;\n" 268 | code.add &" pub inline fn deinit(self: *{objName}) void " & "{\n" 269 | code.add &" return {unrefLibProc}(self);\n" 270 | code.add " }\n\n" 271 | 272 | proc genSeqProcs(objName, procPrefix, selfSuffix: string, entryType: NimNode) = 273 | exportProc( 274 | &"len{selfSuffix}", 275 | &"{procPrefix}_len", 276 | @[("self", objName)], 277 | "isize", 278 | indent = true 279 | ) 280 | exportProc( 281 | &"get{selfSuffix}", 282 | &"{procPrefix}_get", 283 | @[("self", objName), ("index", "isize")], 284 | entryType.exportTypeZig(), 285 | indent = true 286 | ) 287 | exportProc( 288 | &"set{selfSuffix}", 289 | &"{procPrefix}_set", 290 | @[("self", objName), ("index", "isize"), ("value", entryType.exportTypeZig())], 291 | indent = true 292 | ) 293 | exportProc( 294 | &"append{selfSuffix}", 295 | &"{procPrefix}_add", 296 | @[("self", objName), ("value", entryType.exportTypeZig())], 297 | indent = true 298 | ) 299 | exportProc( 300 | &"remove{selfSuffix}", 301 | &"{procPrefix}_delete", 302 | @[("self", objName), ("index", "isize")], 303 | indent = true 304 | ) 305 | exportProc( 306 | &"clear{selfSuffix}", 307 | &"{procPrefix}_clear", 308 | @[("self", objName)], 309 | indent = true 310 | ) 311 | 312 | proc exportRefObjectZig*( 313 | sym: NimNode, 314 | fields: seq[(string, NimNode)], 315 | constructor: NimNode 316 | ) = 317 | discard 318 | let 319 | objName = sym.repr 320 | objNameSnaked = toSnakeCase(objName) 321 | objType = sym.getType()[1].getType() 322 | 323 | genRefObject(objName) 324 | 325 | if constructor != nil: 326 | exportProcZig(constructor, indent = true, rename = "init") 327 | 328 | for (fieldName, fieldType) in fields: 329 | let 330 | fieldNameSnaked = toSnakeCase(fieldName) 331 | fieldNameCapped = capitalizeAscii(fieldName) 332 | 333 | if fieldType.kind != nnkBracketExpr: 334 | exportProc( 335 | "get" & fieldNameCapped, 336 | &"$lib_{objNameSnaked}_get_{fieldNameSnaked}", 337 | @[("self", &"*{objName}")], 338 | fieldType.exportTypeZig(), 339 | indent = true 340 | ) 341 | exportProc( 342 | "set" & fieldNameCapped, 343 | &"$lib_{objNameSnaked}_set_{fieldNameSnaked}", 344 | @[("self", &"*{objName}"), ("value", fieldType.exportTypeZig())], 345 | indent = true 346 | ) 347 | else: 348 | genSeqProcs( 349 | &"*{objName}", 350 | &"$lib_{objNameSnaked}_{fieldNameSnaked}", 351 | fieldNameCapped, 352 | fieldType[1] 353 | ) 354 | 355 | proc exportSeqZig*(sym: NimNode) = 356 | let 357 | seqName = sym.getName() 358 | seqNameSnaked = toSnakeCase(seqName) 359 | 360 | genRefObject(seqName) 361 | 362 | exportProc( 363 | "init", 364 | &"$lib_new_{seqNameSnaked}", 365 | @[], 366 | &"*{seqName}", 367 | indent = true 368 | ) 369 | 370 | genSeqProcs( 371 | &"*{seqName}", 372 | &"$lib_{seqNameSnaked}", 373 | "", 374 | sym[1] 375 | ) 376 | 377 | const header = """ 378 | const std = @import("std"); 379 | 380 | """ 381 | 382 | proc writeZig*(dir, lib: string) = 383 | createDir(dir) 384 | writeFile(&"{dir}/{toSnakeCase(lib)}.zig", 385 | (header & code) 386 | .replace("$Lib", lib) 387 | .replace("$lib", toSnakeCase(lib)) 388 | .replace(" test,", " test_value,") 389 | .replace(" test:", " test_value:") 390 | .replace(" transform,", " transform_value,") 391 | .replace(" transform:", " transform_value:") 392 | .replace(" transform)", " transform_value)") 393 | .replace(" blur,", " blur_value,") 394 | .replace(" blur:", " blur_value:") 395 | ) 396 | -------------------------------------------------------------------------------- /src/genny/languages/nim.nim: -------------------------------------------------------------------------------- 1 | import 2 | std/[os, strformat, strutils, macros], 3 | ../common 4 | 5 | var 6 | types {.compiletime.}: string 7 | procs {.compiletime.}: string 8 | 9 | proc exportTypeNim*(sym: NimNode): string = 10 | if sym.kind == nnkBracketExpr: 11 | if sym[0].repr != "seq": 12 | error(&"Unexpected bracket expression {sym[0].repr}[", sym) 13 | result = sym.getSeqName() 14 | else: 15 | if sym.repr == "string": 16 | result = "cstring" 17 | elif sym.repr == "Rune": 18 | result = "int32" 19 | else: 20 | result = sym.repr 21 | 22 | proc convertExportFromNim*(sym: NimNode): string = 23 | if sym.kind == nnkBracketExpr: 24 | discard 25 | else: 26 | if sym.repr == "string": 27 | result = ".cstring" 28 | elif sym.repr == "Rune": 29 | result = ".int32" 30 | 31 | proc convertImportToNim*(sym: NimNode): string = 32 | if sym.kind == nnkBracketExpr: 33 | if sym[0].repr != "seq": 34 | error(&"Unexpected bracket expression {sym[0].repr}[", sym) 35 | result = ".s" 36 | else: 37 | if sym.repr == "string": 38 | result = ".`$`" 39 | elif sym.repr == "Rune": 40 | result = ".Rune" 41 | 42 | proc exportConstNim*(sym: NimNode) = 43 | let impl = sym.getImpl() 44 | types.add &"const {sym.repr}* = {impl[2].repr}\n" 45 | types.add "\n" 46 | 47 | proc exportEnumNim*(sym: NimNode) = 48 | let symImpl = sym.getImpl()[2] 49 | types.add &"type {sym.repr}* = enum\n" 50 | for i, entry in symImpl[1 .. ^1]: 51 | types.add &" {entry.repr}\n" 52 | types.add "\n" 53 | 54 | proc exportProcNim*( 55 | sym: NimNode, 56 | owner: NimNode = nil, 57 | prefixes: openarray[NimNode] = [] 58 | ) = 59 | let 60 | procName = sym.repr 61 | procNameSnaked = toSnakeCase(procName) 62 | procType = sym.getTypeInst() 63 | procParams = procType[0][1 .. ^1] 64 | procReturn = procType[0][0] 65 | procRaises = sym.raises() 66 | 67 | var apiProcName = &"$lib_" 68 | if owner != nil: 69 | apiProcName.add &"{toSnakeCase(owner.getName())}_" 70 | for prefix in prefixes: 71 | apiProcName.add &"{toSnakeCase(prefix.getName())}_" 72 | apiProcName.add &"{procNameSnaked}" 73 | 74 | var defaults: seq[(string, NimNode)] 75 | for identDefs in sym.getImpl()[3][1 .. ^1]: 76 | let default = identDefs[^1] 77 | for entry in identDefs[0 .. ^3]: 78 | defaults.add((entry.repr, default)) 79 | 80 | procs.add &"proc {apiProcName}(" 81 | for param in procParams: 82 | for i in 0 .. param.len - 3: 83 | var paramType = param[^2] 84 | if paramType.repr.endsWith(":type"): 85 | paramType = owner 86 | procs.add &"{toSnakeCase(param[i].repr)}: {exportTypeNim(paramType)}, " 87 | procs.removeSuffix ", " 88 | procs.add ")" 89 | if procReturn.kind != nnkEmpty: 90 | procs.add &": {exportTypeNim(procReturn)}" 91 | procs.add " {.importc: \"" 92 | procs.add &"{apiProcName}" 93 | procs.add "\", cdecl.}" 94 | procs.add "\n" 95 | procs.add "\n" 96 | 97 | procs.add &"proc {procName}*(" 98 | for i, param in procParams: 99 | var paramType = param[1] 100 | if paramType.repr.endsWith(":type"): 101 | paramType = owner 102 | if param[^2].kind == nnkBracketExpr or paramType.repr.startsWith("Some"): 103 | procs.add &"{param[0].repr}: {exportTypeNim(paramType)}, " 104 | else: 105 | procs.add &"{param[0].repr}: {paramType}" 106 | if defaults[i][1].kind != nnkEmpty: 107 | procs.add &" = {defaults[i][1].repr}" 108 | procs.add ", " 109 | procs.removeSuffix ", " 110 | procs.add ")" 111 | if procReturn.kind != nnkEmpty: 112 | procs.add &": {exportTypeNim(procReturn)}" 113 | procs.add " {.inline.} =\n" 114 | if procReturn.kind != nnkEmpty: 115 | procs.add " result = " 116 | else: 117 | procs.add " " 118 | procs.add &"{apiProcName}(" 119 | for param in procParams: 120 | for i in 0 .. param.len - 3: 121 | procs.add &"{param[i].repr}{convertExportFromNim(param[^2])}, " 122 | procs.removeSuffix ", " 123 | procs.add ")\n" 124 | if procRaises: 125 | procs.add " if checkError():\n" 126 | procs.add " raise newException($LibError, $takeError())\n" 127 | procs.add "\n" 128 | 129 | proc exportObjectNim*(sym: NimNode, constructor: NimNode) = 130 | let objName = sym.repr 131 | 132 | if objName in ["Vector2", "Matrix3", "Rect", "Color"]: 133 | return 134 | 135 | types.add &"type {objName}* = object\n" 136 | for identDefs in sym.getImpl()[2][2]: 137 | for property in identDefs[0 .. ^3]: 138 | types.add &" {property.repr}: {identDefs[^2].repr}\n" 139 | types.add "\n" 140 | 141 | if constructor != nil: 142 | exportProcNim(constructor) 143 | else: 144 | types.add &"proc {toVarCase(objName)}*(" 145 | for identDefs in sym.getImpl()[2][2]: 146 | for property in identDefs[0 .. ^3]: 147 | types.add &"{toSnakeCase(property[1].repr)}: {identDefs[^2].repr}, " 148 | types.removeSuffix ", " 149 | types.add &"): {objName} =\n" 150 | for identDefs in sym.getImpl()[2][2]: 151 | for property in identDefs[0 .. ^3]: 152 | types.add &" result.{toSnakeCase(property[1].repr)} = " 153 | types.add &"{toSnakeCase(property[1].repr)}\n" 154 | types.add "\n" 155 | 156 | proc genRefObject(objName: string) = 157 | types.add &"type {objName}Obj = object\n" 158 | types.add " reference: pointer\n" 159 | types.add "\n" 160 | 161 | types.add &"type {objName}* = ref {objName}Obj\n" 162 | types.add "\n" 163 | 164 | let apiProcName = &"$lib_{toSnakeCase(objName)}_unref" 165 | types.add &"proc {apiProcName}(x: {objName}Obj)" 166 | types.add " {.importc: \"" 167 | types.add &"{apiProcName}" 168 | types.add "\", cdecl.}" 169 | types.add "\n" 170 | types.add "\n" 171 | 172 | types.add &"proc `=destroy`(x: var {objName}Obj) =\n" 173 | types.add &" $lib_{toSnakeCase(objName)}_unref(x)\n" 174 | types.add "\n" 175 | 176 | proc genSeqProcs(objName, niceName, procPrefix, objSuffix, entryName: string) = 177 | procs.add &"proc {procPrefix}_len(s: {objName}): int" 178 | procs.add " {.importc: \"" 179 | procs.add &"{procPrefix}_len" 180 | procs.add "\", cdecl.}\n" 181 | procs.add "\n" 182 | 183 | procs.add &"proc len*(s: {niceName}): int =\n" 184 | procs.add &" {procPrefix}_len(s{objSuffix})\n" 185 | procs.add "\n" 186 | 187 | procs.add &"proc {procPrefix}_add" 188 | procs.add &"(s: {objName}, v: {entryName})" 189 | procs.add " {.importc: \"" 190 | procs.add &"{procPrefix}_add" 191 | procs.add "\", cdecl.}\n" 192 | procs.add "\n" 193 | 194 | procs.add &"proc add*(s: {niceName}, v: {entryName}) =\n" 195 | procs.add &" {procPrefix}_add(s{objSuffix}, v)\n" 196 | procs.add "\n" 197 | 198 | procs.add &"proc {procPrefix}_get" 199 | procs.add &"(s: {objName}, i: int)" 200 | procs.add &": {entryName}" 201 | procs.add " {.importc: \"" 202 | procs.add &"{procPrefix}_get" 203 | procs.add "\", cdecl.}\n" 204 | procs.add "\n" 205 | 206 | procs.add &"proc `[]`*(s: {niceName}, i: int): {entryName} =\n" 207 | procs.add &" {procPrefix}_get(s{objSuffix}, i)\n" 208 | procs.add "\n" 209 | 210 | procs.add &"proc {procPrefix}_set(s: {objName}, " 211 | procs.add &"i: int, v: {entryName})" 212 | procs.add " {.importc: \"" 213 | procs.add &"{procPrefix}_set" 214 | procs.add "\", cdecl.}\n" 215 | procs.add "\n" 216 | 217 | procs.add &"proc `[]=`*(s: {niceName}, i: int, v: {entryName}) =\n" 218 | procs.add &" {procPrefix}_set(s{objSuffix}, i, v)\n" 219 | procs.add "\n" 220 | 221 | procs.add &"proc {procPrefix}_delete(s: {objName}, i: int)" 222 | procs.add " {.importc: \"" 223 | procs.add &"{procPrefix}_delete" 224 | procs.add "\", cdecl.}\n" 225 | procs.add "\n" 226 | 227 | procs.add &"proc delete*(s: {niceName}, i: int) =\n" 228 | procs.add &" {procPrefix}_delete(s{objSuffix}, i)\n" 229 | procs.add "\n" 230 | 231 | procs.add &"proc {procPrefix}_clear(s: {objName})" 232 | procs.add " {.importc: \"" 233 | procs.add &"{procPrefix}_clear" 234 | procs.add "\", cdecl.}\n" 235 | procs.add "\n" 236 | 237 | procs.add &"proc clear*(s: {niceName}) =\n" 238 | procs.add &" {procPrefix}_clear(s{objSuffix})\n" 239 | procs.add "\n" 240 | 241 | proc exportRefObjectNim*( 242 | sym: NimNode, 243 | fields: seq[(string, NimNode)], 244 | constructor: NimNode 245 | ) = 246 | let 247 | objName = sym.getName() 248 | objNameSnaked = toSnakeCase(objName) 249 | objType = sym.getType()[1].getType() 250 | 251 | genRefObject(objName) 252 | 253 | if constructor != nil: 254 | exportProcNim(constructor) 255 | 256 | for (fieldName, fieldType) in fields: 257 | let fieldNameSnaked = toSnakeCase(fieldName) 258 | 259 | if fieldType.kind != nnkBracketExpr: 260 | let getProcName = &"$lib_{objNameSnaked}_get_{fieldNameSnaked}" 261 | 262 | procs.add &"proc {getProcName}(" 263 | procs.add &"{toVarCase(objName)}: {objName}): " 264 | procs.add exportTypeNim(fieldType) 265 | procs.add " {.importc: \"" 266 | procs.add &"{getProcName}" 267 | procs.add "\", cdecl.}" 268 | procs.add "\n" 269 | procs.add "\n" 270 | 271 | procs.add &"proc {fieldName}*(" 272 | procs.add &"{toVarCase(objName)}: {objName}): " 273 | procs.add &"{exportTypeNim(fieldType)}" 274 | procs.add " {.inline.} =\n" 275 | procs.add &" {getProcName}({toVarCase(objName)})" 276 | procs.add convertImportToNim(fieldType) 277 | procs.add "\n" 278 | procs.add "\n" 279 | 280 | let setProcName = &"$lib_{objNameSnaked}_set_{fieldNameSnaked}" 281 | 282 | procs.add &"proc {setProcName}(" 283 | procs.add &"{toVarCase(objName)}: {objName}, " 284 | procs.add &"{fieldName}: {exportTypeNim(fieldType)})" 285 | procs.add " {.importc: \"" 286 | procs.add &"{setProcName}" 287 | procs.add "\", cdecl.}" 288 | procs.add "\n" 289 | procs.add "\n" 290 | 291 | procs.add &"proc `{fieldName}=`*(" 292 | procs.add &"{toVarCase(objName)}: {objName}, " 293 | procs.add &"{fieldName}: {fieldType.repr}) =\n" 294 | procs.add &" {setProcName}({toVarCase(objName)}, " 295 | procs.add &"{fieldName}{convertExportFromNim(fieldType)})" 296 | procs.add "\n" 297 | procs.add "\n" 298 | else: 299 | var helperName = fieldName 300 | helperName[0] = toUpperAscii(helperName[0]) 301 | helperName = objName & helperName 302 | 303 | procs.add &"type {helperName} = object\n" 304 | procs.add &" {toVarCase(objName)}: {objName}\n" 305 | procs.add "\n" 306 | 307 | procs.add &"proc {fieldName}*(" 308 | procs.add &"{toVarCase(objName)}: {objName}" 309 | procs.add &"): {helperName} =\n" 310 | procs.add &" {helperName}({toVarCase(objName)}: {toVarCase(objName)})\n" 311 | procs.add "\n" 312 | 313 | genSeqProcs( 314 | objName, 315 | helperName, 316 | &"$lib_{objNameSnaked}_{fieldNameSnaked}", 317 | &".{toVarCase(objName)}", 318 | fieldType[1].repr 319 | ) 320 | 321 | proc exportSeqNim*(sym: NimNode) = 322 | let 323 | seqName = sym.getName() 324 | seqNameSnaked = toSnakeCase(seqName) 325 | 326 | genRefObject(seqName) 327 | genSeqProcs( 328 | seqName, 329 | seqName, 330 | &"$lib_{seqNameSnaked}", 331 | "", 332 | sym[1].repr 333 | ) 334 | 335 | let newSeqProcName = &"$lib_new_{seqNameSnaked}" 336 | 337 | procs.add &"proc {newSeqProcName}*(): {seqName}" 338 | procs.add " {.importc: \"" 339 | procs.add newSeqProcName 340 | procs.add "\", cdecl.}\n" 341 | procs.add "\n" 342 | 343 | procs.add &"proc new{seqName}*(): {seqName} =\n" 344 | procs.add &" {newSeqProcName}()\n" 345 | procs.add "\n" 346 | 347 | const header = """ 348 | import bumpy, chroma, unicode, vmath 349 | 350 | export bumpy, chroma, unicode, vmath 351 | 352 | when defined(windows): 353 | const libName = "$lib.dll" 354 | elif defined(macosx): 355 | const libName = "lib$lib.dylib" 356 | else: 357 | const libName = "lib$lib.so" 358 | 359 | {.push dynlib: libName.} 360 | 361 | type $LibError = object of ValueError 362 | 363 | """ 364 | 365 | proc writeNim*(dir, lib: string) = 366 | createDir(dir) 367 | writeFile( &"{dir}/{toSnakeCase(lib)}.nim", (header & types & procs) 368 | .replace("$Lib", lib).replace("$lib", toSnakeCase(lib)) 369 | ) 370 | -------------------------------------------------------------------------------- /src/genny.nim: -------------------------------------------------------------------------------- 1 | import genny/internal, genny/languages/c, genny/languages/cpp, 2 | genny/languages/nim, genny/languages/node, genny/languages/python, 3 | genny/languages/zig, macros, strformat 4 | 5 | when not defined(gennyNim) and not defined(gennyPython) and not defined(gennyNode) and not defined(gennyC) and not defined(gennyCpp) and not defined(gennyZig): 6 | {.error: "Please define one of the genny languages. Use -d:gennyNim, -d:gennyPython, -d:gennyNode, -d:gennyC, -d:gennyCpp, -d:gennyZig to define the languages you want to export.".} 7 | 8 | template discard2(f: untyped): untyped = 9 | when(compiles do: discard f): 10 | discard f 11 | else: 12 | f 13 | 14 | proc asStmtList(body: NimNode): seq[NimNode] = 15 | ## Nim optimizes StmtList, reverse that: 16 | if body.kind != nnkStmtList: 17 | result.add(body) 18 | else: 19 | for child in body: 20 | result.add(child) 21 | 22 | proc emptyBlockStmt(): NimNode = 23 | result = quote do: 24 | block: 25 | discard 26 | result[1].del(0) 27 | 28 | macro exportConstsUntyped(body: untyped) = 29 | result = newNimNode(nnkStmtList) 30 | for ident in body: 31 | let varSection = quote do: 32 | var `ident` = `ident` 33 | result.add varSection 34 | 35 | macro exportConstsTyped(body: typed) = 36 | for varSection in body.asStmtList: 37 | let sym = varSection[0][0] 38 | exportConstInternal(sym) 39 | when defined(gennyNim): exportConstNim(sym) 40 | when defined(gennyPython): exportConstPy(sym) 41 | when defined(gennyNode): exportConstNode(sym) 42 | when defined(gennyC): exportConstC(sym) 43 | when defined(gennyCpp): exportConstCpp(sym) 44 | when defined(gennyZig): exportConstZig(sym) 45 | 46 | template exportConsts*(body: untyped) = 47 | ## Exports a list of constants. 48 | exportConstsTyped(exportConstsUntyped(body)) 49 | 50 | macro exportEnumsUntyped(body: untyped) = 51 | result = newNimNode(nnkStmtList) 52 | for i, ident in body: 53 | let 54 | name = ident(&"enum{i}") 55 | varSection = quote do: 56 | var `name`: `ident` 57 | result.add varSection 58 | 59 | macro exportEnumsTyped(body: typed) = 60 | for varSection in body.asStmtList: 61 | let sym = varSection[0][1] 62 | exportEnumInternal(sym) 63 | when defined(gennyNim): exportEnumNim(sym) 64 | when defined(gennyPython): exportEnumPy(sym) 65 | when defined(gennyNode): exportEnumNode(sym) 66 | when defined(gennyC): exportEnumC(sym) 67 | when defined(gennyCpp): exportEnumCpp(sym) 68 | when defined(gennyZig): exportEnumZig(sym) 69 | 70 | template exportEnums*(body: untyped) = 71 | ## Exports a list of enums. 72 | exportEnumsTyped(exportEnumsUntyped(body)) 73 | 74 | proc fieldUntyped(clause, owner: NimNode): NimNode = 75 | result = emptyBlockStmt() 76 | result[1].add quote do: 77 | var 78 | obj: `owner` 79 | f = obj.`clause` 80 | 81 | proc procUntyped(clause: NimNode): NimNode = 82 | result = emptyBlockStmt() 83 | 84 | if clause.kind == nnkIdent: 85 | let 86 | name = clause 87 | varSection = quote do: 88 | var p = `name` 89 | result[1].add varSection 90 | else: 91 | var 92 | name = clause[0] 93 | endStmt = quote do: 94 | discard2 `name`() 95 | for i in 1 ..< clause.len: 96 | var 97 | argType = clause[i] 98 | argName = ident(&"arg{i}") 99 | result[1].add quote do: 100 | var `argName`: `argType` 101 | endStmt[1].add argName 102 | result[1].add endStmt 103 | 104 | proc procTypedSym(entry: NimNode): NimNode = 105 | result = 106 | if entry[1].kind == nnkVarSection: 107 | entry[1][0][2] 108 | else: 109 | if entry[1][^1].kind != nnkDiscardStmt: 110 | entry[1][^1][0] 111 | else: 112 | entry[1][^1][0][0] 113 | 114 | proc procTyped( 115 | entry: NimNode, 116 | owner: NimNode = nil, 117 | prefixes: openarray[NimNode] = [] 118 | ) = 119 | let procSym = procTypedSym(entry) 120 | exportProcInternal(procSym, owner, prefixes) 121 | when defined(gennyNim): exportProcNim(procSym, owner, prefixes) 122 | when defined(gennyPython): exportProcPy(procSym, owner, prefixes) 123 | when defined(gennyNode): exportProcNode(procSym, owner, prefixes) 124 | when defined(gennyC): exportProcC(procSym, owner, prefixes) 125 | when defined(gennyCpp): exportProcCpp(procSym, owner, prefixes) 126 | when defined(gennyZig): exportProcZig(procSym, owner, prefixes) 127 | 128 | macro exportProcsUntyped(body: untyped) = 129 | result = newNimNode(nnkStmtList) 130 | for clause in body: 131 | result.add procUntyped(clause) 132 | 133 | macro exportProcsTyped(body: typed) = 134 | for entry in body.asStmtList: 135 | procTyped(entry) 136 | 137 | template exportProcs*(body: untyped) = 138 | ## Exports a list of procs. 139 | ## Procs can just be a name `doX` or fully qualified with `doX(int): int`. 140 | exportProcsTyped(exportProcsUntyped(body)) 141 | 142 | macro exportObjectUntyped(sym, body: untyped) = 143 | result = newNimNode(nnkStmtList) 144 | 145 | let varSection = quote do: 146 | var obj: `sym` 147 | result.add varSection 148 | 149 | var 150 | constructorBlock = emptyBlockStmt() 151 | procsBlock = emptyBlockStmt() 152 | 153 | for section in body: 154 | if section.kind == nnkDiscardStmt: 155 | continue 156 | 157 | case section[0].repr: 158 | of "constructor": 159 | constructorBlock[1].add procUntyped(section[1][0]) 160 | of "procs": 161 | for clause in section[1]: 162 | procsBlock[1].add procUntyped(clause) 163 | else: 164 | error("Invalid section", section) 165 | 166 | result.add constructorBlock 167 | result.add procsBlock 168 | 169 | macro exportObjectTyped(body: typed) = 170 | let 171 | sym = body[0][0][1] 172 | constructorBlock = body[1] 173 | procsBlock = body[2] 174 | 175 | let constructor = 176 | if constructorBlock[1].len > 0: 177 | procTypedSym(constructorBlock[1]) 178 | else: 179 | nil 180 | 181 | exportObjectInternal(sym, constructor) 182 | exportObjectNim(sym, constructor) 183 | exportObjectPy(sym, constructor) 184 | exportObjectNode(sym, constructor) 185 | exportObjectC(sym, constructor) 186 | exportObjectCpp(sym, constructor) 187 | exportObjectZig(sym, constructor) 188 | 189 | if procsBlock[1].len > 0: 190 | var procsSeen: seq[string] 191 | for entry in procsBlock[1].asStmtList: 192 | var 193 | procSym = procTypedSym(entry) 194 | prefixes: seq[NimNode] 195 | if procSym.repr notin procsSeen: 196 | procsSeen.add procSym.repr 197 | else: 198 | let procType = procSym.getTypeInst() 199 | if procType[0].len > 2: 200 | prefixes.add(procType[0][2][1]) 201 | exportProcInternal(procSym, sym, prefixes) 202 | when defined(gennyNim): exportProcNim(procSym, sym, prefixes) 203 | when defined(gennyPython): exportProcPy(procSym, sym, prefixes) 204 | when defined(gennyNode): exportProcNode(procSym, sym, prefixes) 205 | when defined(gennyC): exportProcC(procSym, sym, prefixes) 206 | when defined(gennyCpp): exportProcCpp(procSym, sym, prefixes) 207 | when defined(gennyZig): exportProcZig(procSym, sym, prefixes) 208 | 209 | exportCloseObjectZig() 210 | exportCloseObjectCpp() 211 | 212 | template exportObject*(sym, body: untyped) = 213 | ## Exports an object, with these sections: 214 | ## * fields 215 | ## * constructor 216 | ## * procs 217 | exportObjectTyped(exportObjectUntyped(sym, body)) 218 | 219 | macro exportSeqUntyped(sym, body: untyped) = 220 | result = newNimNode(nnkStmtList) 221 | 222 | let varSection = quote do: 223 | var s: `sym` 224 | result.add varSection 225 | 226 | for section in body: 227 | if section.kind == nnkDiscardStmt: 228 | continue 229 | 230 | case section[0].repr: 231 | of "procs": 232 | for clause in section[1]: 233 | result.add procUntyped(clause) 234 | else: 235 | error("Invalid section", section) 236 | 237 | macro exportSeqTyped(body: typed) = 238 | let sym = body.asStmtList()[0][0][1] 239 | 240 | exportSeqInternal(sym) 241 | when defined(gennyNim): exportSeqNim(sym) 242 | when defined(gennyPython): exportSeqPy(sym) 243 | when defined(gennyNode): exportSeqNode(sym) 244 | when defined(gennyC): exportSeqC(sym) 245 | when defined(gennyCpp): exportSeqCpp(sym) 246 | when defined(gennyZig): exportSeqZig(sym) 247 | 248 | for entry in body.asStmtList()[1 .. ^1]: 249 | procTyped(entry, sym) 250 | 251 | exportCloseObjectCpp() 252 | exportCloseObjectZig() 253 | 254 | template exportSeq*(sym, body: untyped) = 255 | ## Exports a regular sequence. 256 | ## * procs section 257 | exportSeqTyped(exportSeqUntyped(sym, body)) 258 | 259 | macro exportRefObjectUntyped(sym, body: untyped) = 260 | result = newNimNode(nnkStmtList) 261 | 262 | let varSection = quote do: 263 | var refObj: `sym` 264 | result.add varSection 265 | 266 | var 267 | fieldsBlock = emptyBlockStmt() 268 | constructorBlock = emptyBlockStmt() 269 | procsBlock = emptyBlockStmt() 270 | 271 | for section in body: 272 | if section.kind == nnkDiscardStmt: 273 | continue 274 | 275 | case section[0].repr: 276 | of "fields": 277 | for field in section[1]: 278 | fieldsBlock[1].add fieldUntyped(field, sym) 279 | of "constructor": 280 | constructorBlock[1].add procUntyped(section[1][0]) 281 | of "procs": 282 | for clause in section[1]: 283 | procsBlock[1].add procUntyped(clause) 284 | else: 285 | error("Invalid section", section) 286 | 287 | result.add fieldsBlock 288 | result.add constructorBlock 289 | result.add procsBlock 290 | 291 | macro exportRefObjectTyped(body: typed) = 292 | let 293 | sym = body[0][0][1] 294 | fieldsBlock = body[1] 295 | constructorBlock = body[2] 296 | procsBlock = body[3] 297 | 298 | var fields: seq[(string, NimNode)] 299 | if fieldsBlock[1].len > 0: 300 | for entry in fieldsBlock[1].asStmtList: 301 | case entry[1][1][2].kind: 302 | of nnkCall: 303 | fields.add(( 304 | entry[1][1][2][0].repr, 305 | entry[1][1][2].getTypeInst() 306 | )) 307 | else: 308 | fields.add(( 309 | entry[1][1][2][1].repr, 310 | entry[1][1][2][1].getTypeInst() 311 | )) 312 | 313 | let constructor = 314 | if constructorBlock[1].len > 0: 315 | procTypedSym(constructorBlock[1]) 316 | else: 317 | nil 318 | 319 | exportRefObjectInternal(sym, fields, constructor) 320 | exportRefObjectNim(sym, fields, constructor) 321 | exportRefObjectPy(sym, fields, constructor) 322 | exportRefObjectNode(sym, fields, constructor) 323 | exportRefObjectC(sym, fields, constructor) 324 | exportRefObjectCpp(sym, fields, constructor) 325 | exportRefObjectZig(sym, fields, constructor) 326 | 327 | if procsBlock[1].len > 0: 328 | var procsSeen: seq[string] 329 | for entry in procsBlock[1].asStmtList: 330 | var 331 | procSym = procTypedSym(entry) 332 | prefixes: seq[NimNode] 333 | if procSym.repr notin procsSeen: 334 | procsSeen.add procSym.repr 335 | else: 336 | let procType = procSym.getTypeInst() 337 | if procType[0].len > 2: 338 | prefixes.add(procType[0][2][1]) 339 | exportProcInternal(procSym, sym, prefixes) 340 | when defined(gennyNim): exportProcNim(procSym, sym, prefixes) 341 | when defined(gennyPython): exportProcPy(procSym, sym, prefixes) 342 | when defined(gennyNode): exportProcNode(procSym, sym, prefixes) 343 | when defined(gennyC): exportProcC(procSym, sym, prefixes) 344 | when defined(gennyCpp): exportProcCpp(procSym, sym, prefixes) 345 | when defined(gennyZig): exportProcZig(procSym, sym, prefixes) 346 | 347 | exportCloseObjectCpp() 348 | exportCloseObjectZig() 349 | 350 | template exportRefObject*(sym, body: untyped) = 351 | ## Exports a ref object, with these sections: 352 | ## * fields 353 | ## * constructor 354 | ## * procs 355 | exportRefObjectTyped(exportRefObjectUntyped(sym, body)) 356 | 357 | macro writeFiles*(dir, lib: static[string]) = 358 | ## This needs to be and the end of the file and it needs to be followed by: 359 | ## `include generated/internal` 360 | writeInternal(dir, lib) 361 | when defined(gennyNim): writeNim(dir, lib) 362 | when defined(gennyPython): writePy(dir, lib) 363 | when defined(gennyNode): writeNode(dir, lib) 364 | when defined(gennyC): writeC(dir, lib) 365 | when defined(gennyCpp): writeCpp(dir, lib) 366 | when defined(gennyZig): writeZig(dir, lib) 367 | -------------------------------------------------------------------------------- /src/genny/languages/cpp.nim: -------------------------------------------------------------------------------- 1 | import 2 | std/[os, strformat, strutils, macros], 3 | ../common 4 | 5 | var 6 | types {.compiletime.}: string 7 | procs {.compiletime.}: string 8 | classes {.compiletime.}: string 9 | members {.compiletime.}: string 10 | 11 | proc unCapitalize(s: string): string = 12 | s[0].toLowerAscii() & s[1 .. ^1] 13 | 14 | proc exportTypeCpp(sym: NimNode): string = 15 | if sym.kind == nnkBracketExpr: 16 | if sym[0].repr == "array": 17 | let 18 | entryCount = sym[1].repr 19 | entryType = exportTypeCpp(sym[2]) 20 | result = &"{entryType}[{entryCount}]" 21 | elif sym[0].repr == "seq": 22 | result = sym.getSeqName() 23 | else: 24 | error(&"Unexpected bracket expression {sym[0].repr}[") 25 | else: 26 | result = 27 | case sym.repr: 28 | of "string": "const char*" 29 | of "bool": "bool" 30 | of "byte": "char" 31 | of "int8": "int8_t" 32 | of "int16": "int16_t" 33 | of "int32": "int32_t" 34 | of "int64": "int64_t" 35 | of "int": "int64_t" 36 | of "uint8": "uint8_t" 37 | of "uint16": "uint16_t" 38 | of "uint32": "uint32_t" 39 | of "uint64": "uint64_t" 40 | of "uint": "uint64_t" 41 | of "float32": "float" 42 | of "float64": "double" 43 | of "float": "double" 44 | of "Rune": "int32_t" 45 | of "Vec2": "Vector2" 46 | of "Mat3": "Matrix3" 47 | of "", "nil": "void" 48 | of "None": "void" 49 | else: 50 | if sym.getType().kind == nnkBracketExpr: 51 | sym.repr 52 | else: 53 | sym.repr 54 | 55 | proc exportTypeCpp(sym: NimNode, name: string): string = 56 | if sym.kind == nnkBracketExpr: 57 | if sym[0].repr == "array": 58 | let 59 | entryCount = sym[1].repr 60 | entryType = exportTypeCpp(sym[2], &"{name}[{entryCount}]") 61 | result = &"{entryType}" 62 | elif sym[0].repr == "seq": 63 | result = sym.getSeqName() & " " & name 64 | else: 65 | error(&"Unexpected bracket expression {sym[0].repr}[") 66 | else: 67 | result = exportTypeCpp(sym) & " " & name 68 | 69 | proc dllProc*(procName: string, args: openarray[string], restype: string) = 70 | var argStr = "" 71 | for arg in args: 72 | argStr.add &"{arg}, " 73 | argStr.removeSuffix ", " 74 | procs.add &"{restype} {procName}({argStr});\n" 75 | procs.add "\n" 76 | 77 | proc dllProc*(procName: string, args: openarray[(NimNode, NimNode)], restype: string) = 78 | var argsConverted: seq[string] 79 | for (argName, argType) in args: 80 | argsConverted.add exportTypeCpp(argType, toSnakeCase(argName.getName())) 81 | dllProc(procName, argsConverted, restype) 82 | 83 | proc dllProc*(procName: string, restype: string) = 84 | var a: seq[(string)] 85 | dllProc(procName, a, restype) 86 | 87 | proc exportConstCpp*(sym: NimNode) = 88 | types.add &"#define {toCapSnakeCase(sym.repr)} {sym.getImpl()[2].repr}\n" 89 | types.add "\n" 90 | 91 | proc exportEnumCpp*(sym: NimNode) = 92 | types.add &"typedef char {sym.repr};\n" 93 | for i, entry in sym.getImpl()[2][1 .. ^1]: 94 | types.add &"#define {toCapSnakeCase(entry.repr)} {i}\n" 95 | types.add "\n" 96 | 97 | proc exportProcCpp*( 98 | sym: NimNode, 99 | owner: NimNode = nil, 100 | prefixes: openarray[NimNode] = [] 101 | ) = 102 | let 103 | procName = sym.repr 104 | procNameSnaked = toSnakeCase(procName) 105 | procType = sym.getTypeInst() 106 | procParams = procType[0][1 .. ^1] 107 | procReturn = procType[0][0] 108 | 109 | var apiProcName = "" 110 | if owner != nil: 111 | apiProcName.add &"{toSnakeCase(owner.getName())}_" 112 | for prefix in prefixes: 113 | apiProcName.add &"{toSnakeCase(prefix.getName())}_" 114 | apiProcName.add &"{procNameSnaked}" 115 | 116 | var defaults: seq[(string, NimNode)] 117 | for identDefs in sym.getImpl()[3][1 .. ^1]: 118 | let default = identDefs[^1] 119 | for entry in identDefs[0 .. ^3]: 120 | defaults.add((entry.repr, default)) 121 | 122 | var dllParams: seq[(NimNode, NimNode)] 123 | for param in procParams: 124 | dllParams.add((param[0], param[1])) 125 | dllProc(&"$lib_{apiProcName}", dllParams, exportTypeCpp(procReturn)) 126 | 127 | if owner == nil: 128 | if procReturn.kind != nnkEmpty: 129 | members.add exportTypeCpp(procReturn) 130 | members.add " " 131 | members.add procName 132 | members.add "(" 133 | for param in procParams: 134 | members.add exportTypeCpp(param[1], param[0].getName()) 135 | members.add ", " 136 | members.removeSuffix ", " 137 | members.add ") {\n" 138 | members.add " " 139 | if procReturn.kind != nnkEmpty: 140 | members.add "return " 141 | members.add &"$lib_{apiProcName}(" 142 | for param in procParams: 143 | members.add param[0].getName() 144 | members.add ", " 145 | members.removeSuffix ", " 146 | members.add ");\n" 147 | members.add "};\n\n" 148 | 149 | else: 150 | let comments = 151 | if sym.getImpl()[6][0].kind == nnkCommentStmt: 152 | sym.getImpl()[6][0].repr 153 | elif sym.getImpl[6].kind == nnkAsgn and 154 | sym.getImpl[6][1].kind == nnkStmtListExpr and 155 | sym.getImpl[6][1][0].kind == nnkCommentStmt: 156 | sym.getImpl[6][1][0].repr 157 | else: 158 | "" 159 | if comments != "": 160 | let lines = comments.replace("## ", "").split("\n") 161 | classes.add " /**\n" 162 | for line in lines: 163 | classes.add &" * {line}\n" 164 | classes.add " */\n" 165 | 166 | classes.add &" {exportTypeCpp(procReturn)} {procName}(" 167 | for param in procParams[1..^1]: 168 | classes.add exportTypeCpp(param[1], param[0].getName()) 169 | classes.add ", " 170 | classes.removeSuffix ", " 171 | classes.add ");\n\n" 172 | 173 | members.add &"{exportTypeCpp(procReturn)} {owner.getName()}::{procName}(" 174 | for param in procParams[1..^1]: 175 | members.add exportTypeCpp(param[1], param[0].getName()) 176 | members.add ", " 177 | members.removeSuffix ", " 178 | members.add ") " 179 | members.add "{\n" 180 | if procReturn.kind == nnkEmpty: 181 | members.add &" " 182 | else: 183 | members.add &" return " 184 | members.add &"$lib_{apiProcName}(" 185 | members.add "*this, " 186 | for param in procParams[1..^1]: 187 | members.add param[0].getName() 188 | members.add ", " 189 | members.removeSuffix ", " 190 | members.add ");\n" 191 | members.add "};\n\n" 192 | 193 | proc exportObjectCpp*(sym: NimNode, constructor: NimNode) = 194 | let objName = sym.repr 195 | 196 | types.add &"struct {objName};\n\n" 197 | 198 | classes.add &"struct {objName} " & "{\n" 199 | for identDefs in sym.getImpl()[2][2]: 200 | for property in identDefs[0 .. ^3]: 201 | classes.add &" {exportTypeCpp(identDefs[^2], toSnakeCase(property[1].repr))};\n" 202 | 203 | if constructor != nil: 204 | exportProcCpp(constructor) 205 | else: 206 | procs.add &"{objName} $lib_{toSnakeCase(objName)}(" 207 | for identDefs in sym.getImpl()[2][2]: 208 | for property in identDefs[0 .. ^3]: 209 | procs.add &"{exportTypeCpp(identDefs[^2], toSnakeCase(property[1].repr))}, " 210 | procs.removeSuffix ", " 211 | procs.add ");\n\n" 212 | 213 | members.add &"{objName} {objName.unCapitalize()}(" 214 | for identDefs in sym.getImpl()[2][2]: 215 | for property in identDefs[0 .. ^3]: 216 | members.add &"{exportTypeCpp(identDefs[^2], property[1].repr)}" 217 | members.add ", " 218 | members.removeSuffix ", " 219 | members.add ") " 220 | members.add "{\n" 221 | members.add &" return " 222 | members.add &"$lib_{toSnakeCase(objName)}(" 223 | for identDefs in sym.getImpl()[2][2]: 224 | for property in identDefs[0 .. ^3]: 225 | members.add property[1].repr 226 | members.add ", " 227 | members.removeSuffix ", " 228 | members.add ");\n" 229 | members.add "};\n\n" 230 | 231 | dllProc(&"$lib_{toSnakeCase(objName)}_eq", [&"{objName} a", &"{objName} b"], "char") 232 | 233 | proc genRefObject(objName: string) = 234 | 235 | types.add &"struct {objName};\n\n" 236 | 237 | let unrefLibProc = &"$lib_{toSnakeCase(objName)}_unref" 238 | 239 | dllProc(unrefLibProc, [objName & " " & toSnakeCase(objName)], "void") 240 | 241 | proc genSeqProcs(objName, procPrefix, selfSuffix: string, entryType: NimNode) = 242 | let objArg = objName & " " & toSnakeCase(objName) 243 | dllProc(&"{procPrefix}_len", [objArg], "int64_t") 244 | dllProc(&"{procPrefix}_get", [objArg, "int64_t index"], exportTypeCpp(entryType)) 245 | dllProc(&"{procPrefix}_set", [objArg, "int64_t index", exportTypeCpp(entryType, "value")], "void") 246 | dllProc(&"{procPrefix}_delete", [objArg, "int64_t index"], "void") 247 | dllProc(&"{procPrefix}_add", [objArg, exportTypeCpp(entryType, "value")], "void") 248 | dllProc(&"{procPrefix}_clear", [objArg], "void") 249 | 250 | proc exportRefObjectCpp*( 251 | sym: NimNode, 252 | fields: seq[(string, NimNode)], 253 | constructor: NimNode 254 | ) = 255 | let 256 | objName = sym.repr 257 | objNameSnaked = toSnakeCase(objName) 258 | objType = sym.getType()[1].getType() 259 | 260 | genRefObject(objName) 261 | 262 | classes.add &"struct {objName} " & "{\n\n" 263 | classes.add &" private:\n\n" 264 | classes.add &" uint64_t reference;\n\n" 265 | classes.add &" public:\n\n" 266 | 267 | if constructor != nil: 268 | let 269 | constructorLibProc = &"$lib_{toSnakeCase(constructor.repr)}" 270 | constructorType = constructor.getTypeInst() 271 | constructorParams = constructorType[0][1 .. ^1] 272 | constructorRaises = constructor.raises() 273 | 274 | classes.add &" {objName}(" 275 | for param in constructorParams: 276 | classes.add exportTypeCpp(param[1], param[0].getName()) 277 | classes.add ", " 278 | classes.removeSuffix ", " 279 | classes.add ");\n\n" 280 | 281 | members.add &"{objName}::{objName}(" 282 | for param in constructorParams: 283 | members.add exportTypeCpp(param[1], param[0].getName()) 284 | members.add ", " 285 | members.removeSuffix ", " 286 | members.add ")" 287 | members.add " {\n" 288 | members.add &" this->reference = " 289 | members.add &"{constructorLibProc}(" 290 | for param in constructorParams: 291 | members.add param[0].getName() 292 | members.add ", " 293 | members.removeSuffix ", " 294 | members.add ").reference;\n" 295 | members.add "}\n\n" 296 | 297 | var dllParams: seq[(NimNode, NimNode)] 298 | for param in constructorParams: 299 | dllParams.add((param[0], param[1])) 300 | dllProc(constructorLibProc, dllParams, objName) 301 | 302 | for (fieldName, fieldType) in fields: 303 | let fieldNameSnaked = toSnakeCase(fieldName) 304 | 305 | if fieldType.kind != nnkBracketExpr: 306 | let getProcName = &"$lib_{objNameSnaked}_get_{fieldNameSnaked}" 307 | let setProcName = &"$lib_{objNameSnaked}_set_{fieldNameSnaked}" 308 | 309 | let getMemberName = &"get{fieldName.capitalizeAscii}" 310 | let setMemberName = &"set{fieldName.capitalizeAscii}" 311 | 312 | dllProc(getProcName, [objName & " " & objNameSnaked], exportTypeCpp(fieldType)) 313 | dllProc(setProcName, [objName & " " & objNameSnaked, exportTypeCpp(fieldType, "value")], exportTypeCpp(nil)) 314 | 315 | classes.add &" {exportTypeCpp(fieldType)} {getMemberName}();\n" 316 | 317 | members.add &"{exportTypeCpp(fieldType)} {objName}::{getMemberName}()" & "{\n" 318 | members.add &" return {getProcName}(*this);\n" 319 | members.add "}\n\n" 320 | 321 | classes.add &" void {setMemberName}({exportTypeCpp(fieldType)} value);\n\n" 322 | 323 | members.add &"void {objName}::{setMemberName}({exportTypeCpp(fieldType)} value)" & "{\n" 324 | members.add &" {setProcName}(*this, value);\n" 325 | members.add "}\n\n" 326 | 327 | # TODO: property 328 | # classes.add &" __declspec(property(get={getMemberName},put={setMemberName})) {exportTypeCpp(fieldType)} {fieldName};\n\n" 329 | 330 | else: 331 | var helperName = fieldName 332 | helperName[0] = toUpperAscii(helperName[0]) 333 | let helperClassName = objName & helperName 334 | 335 | genSeqProcs( 336 | objName, 337 | &"$lib_{objNameSnaked}_{fieldNameSnaked}", 338 | &".{toSnakeCase(objName)}", 339 | fieldType[1] 340 | ) 341 | 342 | # TODO: ref/unref 343 | # classes.add &" ~{objName}();\n\n" 344 | 345 | # members.add &"{objName}::~{objName}()" & "{\n" 346 | # members.add &" // $lib_{toSnakeCase(objName)}_unref(*this);\n" 347 | # members.add "}\n\n" 348 | 349 | classes.add &" void free();\n\n" 350 | 351 | members.add &"void {objName}::free()" & "{\n" 352 | members.add &" $lib_{toSnakeCase(objName)}_unref(*this);\n" 353 | members.add "}\n\n" 354 | 355 | proc exportCloseObjectCpp*() = 356 | 357 | classes.add "};\n\n" 358 | 359 | proc exportSeqCpp*(sym: NimNode) = 360 | let 361 | seqName = sym.getName() 362 | seqNameSnaked = toSnakeCase(seqName) 363 | 364 | genRefObject(seqName) 365 | 366 | let newSeqProc = &"$lib_new_{toSnakeCase(seqName)}" 367 | 368 | dllProc(newSeqProc, seqName) 369 | 370 | genSeqProcs( 371 | sym.getName(), 372 | &"$lib_{seqNameSnaked}", 373 | "", 374 | sym[1] 375 | ) 376 | 377 | classes.add &"struct {seqName} " & "{\n\n" 378 | classes.add &" private:\n\n" 379 | classes.add &" uint64_t reference;\n\n" 380 | classes.add &" public:\n\n" 381 | 382 | classes.add &" void free();\n\n" 383 | 384 | members.add &"void {seqName}::free()" & "{\n" 385 | members.add &" $lib_{toSnakeCase(seqName)}_unref(*this);\n" 386 | members.add "}\n\n" 387 | 388 | 389 | const header = """ 390 | #ifndef INCLUDE_$LIB_H 391 | #define INCLUDE_$LIB_H 392 | 393 | #include 394 | 395 | """ 396 | 397 | const footer = """ 398 | #endif 399 | """ 400 | 401 | proc writeCpp*(dir, lib: string) = 402 | createDir(dir) 403 | writeFile(&"{dir}/{toSnakeCase(lib)}.hpp", ( 404 | header & 405 | types & 406 | classes & 407 | "extern \"C\" {\n\n" & 408 | procs & 409 | "}\n\n" & 410 | members & 411 | footer 412 | ).replace("$lib", toSnakeCase(lib)).replace("$LIB", lib.toUpperAscii()) 413 | ) 414 | -------------------------------------------------------------------------------- /src/genny/languages/node.nim: -------------------------------------------------------------------------------- 1 | import 2 | std/[os, strformat, strutils, macros], 3 | ../common 4 | 5 | var 6 | types {.compiletime.}: string 7 | procs {.compiletime.}: string 8 | exports {.compiletime.}: string 9 | 10 | proc exportTypeNode(sym: NimNode): string = 11 | if sym.kind == nnkBracketExpr: 12 | if sym[0].repr == "array": 13 | let 14 | entryCount = sym[1].repr 15 | entryType = exportTypeNode(sym[2]) 16 | result = &"ArrayType({entryType}, {entryCount})" 17 | elif sym[0].repr == "seq": 18 | result = sym.getSeqName() 19 | else: 20 | error(&"Unexpected bracket expression {sym[0].repr}[") 21 | else: 22 | result = 23 | case sym.repr: 24 | of "string": "'string'" 25 | of "bool": "'bool'" 26 | of "byte": "'int8'" 27 | of "int8": "'int8'" 28 | of "int16": "'int16'" 29 | of "int32": "'int32'" 30 | of "int64": "'int64'" 31 | of "int": "'int64'" 32 | of "uint8": "'uint8'" 33 | of "uint16": "'uint16'" 34 | of "uint32": "'uint32'" 35 | of "uint64": "'uint64'" 36 | of "uint": "'uint64'" 37 | of "float32": "'float'" 38 | of "float64": "'double'" 39 | of "float": "'double'" 40 | of "proc () {.cdecl.}": "'pointer'" 41 | 42 | of "Rune": "'int32'" 43 | of "Vec2": "Vector2" 44 | of "Mat3": "Matrix3" 45 | 46 | of "": "'void'" 47 | else: 48 | sym.repr 49 | 50 | proc convertExportFromNode*(sym: NimNode): string = 51 | discard 52 | 53 | proc convertImportToNode*(sym: NimNode): string = 54 | discard 55 | 56 | proc exportConstNode*(sym: NimNode) = 57 | let impl = sym.getImpl() 58 | exports.add &"exports.{toCapSnakeCase(sym.repr)} = {impl[2].repr}\n" 59 | 60 | proc exportEnumNode*(sym: NimNode) = 61 | let symImpl = sym.getImpl()[2] 62 | 63 | types.add &"const {sym.repr} = 'int8'\n" 64 | exports.add &"exports.{sym.repr} = {sym.repr}\n" 65 | for i, entry in symImpl[1 .. ^1]: 66 | exports.add &"exports.{toCapSnakeCase(entry.repr)} = {i}\n" 67 | types.add "\n" 68 | 69 | proc dllProc(proName: string, procParams: seq[NimNode], returnType: string) = 70 | procs.add &" '{proName}': [{returnType}, [" 71 | for param in procParams: 72 | for i in 0 .. param.len - 3: 73 | var paramType = param[^2] 74 | procs.add &"{exportTypeNode(paramType)}, " 75 | procs.removeSuffix ", " 76 | procs.add "]],\n" 77 | 78 | proc exportProcNode*( 79 | sym: NimNode, 80 | owner: NimNode = nil, 81 | prefixes: openarray[NimNode] = [] 82 | ) = 83 | let 84 | procName = sym.repr 85 | procNameSnaked = toSnakeCase(procName) 86 | procType = sym.getTypeInst() 87 | procParams = procType[0][1 .. ^1] 88 | procReturn = procType[0][0] 89 | procRaises = sym.raises() 90 | onClass = owner != nil 91 | 92 | let comments = 93 | if sym.getImpl()[6][0].kind == nnkCommentStmt: 94 | sym.getImpl()[6][0].repr 95 | elif sym.getImpl[6].kind == nnkAsgn and 96 | sym.getImpl[6][1].kind == nnkStmtListExpr and 97 | sym.getImpl[6][1][0].kind == nnkCommentStmt: 98 | sym.getImpl[6][1][0].repr 99 | else: 100 | "" 101 | if comments != "": 102 | let lines = comments.replace("## ", "").split("\n") 103 | types.add "/**\n" 104 | for i, line in lines: 105 | types.add &" * {line}\n" 106 | types.add " */\n" 107 | 108 | ## Nim bug, must set to "" first, otherwise crazy! 109 | var apiProcName = "" 110 | apiProcName.add "$lib_" 111 | if owner != nil: 112 | apiProcName.add &"{toSnakeCase(owner.getName())}_" 113 | for prefix in prefixes: 114 | apiProcName.add &"{toSnakeCase(prefix.getName())}_" 115 | apiProcName.add &"{procNameSnaked}" 116 | 117 | var defaults: seq[(string, NimNode)] 118 | for identDefs in sym.getImpl()[3][1 .. ^1]: 119 | let default = identDefs[^1] 120 | for entry in identDefs[0 .. ^3]: 121 | defaults.add((entry.repr, default)) 122 | 123 | if onClass: 124 | types.add &"{owner.getName()}.prototype." 125 | var name = "" 126 | if prefixes.len > 0: 127 | if prefixes[0].getImpl().kind != nnkNilLIt: 128 | if prefixes[0].getImpl()[2].kind != nnkEnumTy: 129 | name.add &"{prefixes[0].repr}_" 130 | name.add sym.repr 131 | types.add &"{toVarCase(toCamelCase(name))} = function(" 132 | else: 133 | types.add &"function {sym.repr}(" 134 | exports.add &"exports.{sym.repr} = {sym.repr}\n" 135 | 136 | for i, param in procParams[0 .. ^1]: 137 | if onClass and i == 0: 138 | discard #types.add "this?" 139 | else: 140 | types.add toSnakeCase(param[0].repr) 141 | case defaults[i][1].kind: 142 | of nnkIntLit, nnkFloatLit: 143 | types.add &" = {defaults[i][1].repr}" 144 | of nnkIdent: 145 | if defaults[i][1].repr == "true": 146 | types.add " = True" 147 | elif defaults[i][1].repr == "false": 148 | types.add " = False" 149 | else: 150 | types.add &" = {toCapSnakeCase(defaults[i][1].repr)}" 151 | else: 152 | if defaults[i][1].kind != nnkEmpty: 153 | types.add &" = " 154 | types.add &"{exportTypeNode(param[1])}(" 155 | for d in defaults[i][1][1 .. ^1]: 156 | types.add &"{d.repr}, " 157 | types.removeSuffix ", " 158 | types.add ")" 159 | types.add &", " 160 | types.removeSuffix ", " 161 | types.add "){\n" 162 | types.add " " 163 | if procReturn.kind != nnkEmpty: 164 | types.add "result = " 165 | types.add &"dll.{apiProcName}(" 166 | for i, param in procParams[0 .. ^1]: 167 | if onClass and i == 0: 168 | types.add "this" 169 | else: 170 | types.add &"{toSnakeCase(param[0].repr)}{convertExportFromNode(param[1])}" 171 | types.add &", " 172 | types.removeSuffix ", " 173 | types.add &"){convertImportToNode(procReturn)}\n" 174 | if procRaises: 175 | types.add &" if(checkError()) " 176 | types.add "throw new $LibException(" 177 | types.add "takeError()" 178 | types.add ");\n" 179 | if procReturn.kind != nnkEmpty: 180 | types.add " return result\n" 181 | types.add "}\n\n" 182 | 183 | dllProc(apiProcName, procParams, exportTypeNode(procReturn)) 184 | 185 | proc exportObjectNode*(sym: NimNode, constructor: NimNode) = 186 | let objName = sym.repr 187 | 188 | exports.add &"exports.{objName} = {objName};\n" 189 | types.add &"const {objName} = Struct(" & "{\n" 190 | for identDefs in sym.getImpl()[2][2]: 191 | for property in identDefs[0 .. ^3]: 192 | types.add &" '{property[1].repr}':" 193 | types.add &"{exportTypeNode(identDefs[^2])},\n" 194 | types.removeSuffix ",\n" 195 | types.add "\n})\n" 196 | 197 | if constructor != nil: 198 | let 199 | constructorType = constructor.getTypeInst() 200 | constructorParams = constructorType[0][1 .. ^1] 201 | constructorLibProc = &"$lib_{toSnakeCase(objName)}" 202 | 203 | exports.add &"exports.{toVarCase(objName)} = {toVarCase(objName)};\n" 204 | types.add &"{toVarCase(objName)} = function(" 205 | for param in constructorParams: 206 | types.add &"{toSnakeCase(param[0].repr)}" 207 | types.add ", " 208 | types.removeSuffix ", " 209 | types.add "){\n" 210 | types.add &" return dll.{constructorLibProc}(" 211 | for param in constructorParams: 212 | types.add &"{toSnakeCase(param[0].repr)}" 213 | types.add ", " 214 | types.removeSuffix ", " 215 | types.add ");\n" 216 | types.add "}\n" 217 | dllProc(constructorLibProc, constructorParams, objName) 218 | 219 | else: 220 | exports.add &"exports.{toVarCase(objName)} = {toVarCase(objName)};\n" 221 | types.add &"{toVarCase(objName)} = function(" 222 | for identDefs in sym.getImpl()[2][2]: 223 | for property in identDefs[0 .. ^3]: 224 | types.add &"{toSnakeCase(property[1].repr)}, " 225 | types.removeSuffix ", " 226 | types.add "){\n" 227 | types.add &" var v = new {objName}();\n" 228 | for identDefs in sym.getImpl()[2][2]: 229 | for property in identDefs[0 .. ^3]: 230 | types.add " " 231 | types.add &"v.{toSnakeCase(property[1].repr)} = " 232 | types.add &"{toSnakeCase(property[1].repr)}\n" 233 | 234 | types.add " return v;\n" 235 | types.add "}\n" 236 | 237 | types.add &"{objName}.prototype.isEqual = function(other){{\n" 238 | types.add &" return " 239 | for identDefs in sym.getImpl()[2][2]: 240 | for property in identDefs[0 .. ^3]: 241 | types.add &"self.{property[1].repr} == other.{property[1].repr} && " 242 | types.removeSuffix " && " 243 | types.add ";\n" 244 | types.add "};\n" 245 | types.add "\n" 246 | 247 | proc genRefObject(objName: string) = 248 | exports.add &"exports.{objName}Type = {objName}\n" 249 | types.add &"{objName} = " & "Struct({'nimRef': 'uint64'});\n" 250 | 251 | types.add &"{objName}.prototype.isNull = function(){{\n" 252 | types.add &" return this.nimRef == 0;\n" 253 | types.add "};\n" 254 | 255 | types.add &"{objName}.prototype.isEqual = function(other){{\n" 256 | types.add &" return this.nimRef == other.nimRef;\n" 257 | types.add "};\n" 258 | 259 | ## TODO: maybe https://nodejs.org/api/n-api.html#n_api_napi_finalize ? 260 | types.add &"{objName}.prototype.unref = function(){{\n" 261 | types.add &" return dll.$lib_{toSnakeCase(objName)}_unref(this)\n" 262 | types.add "};\n" 263 | 264 | procs.add &" '$lib_{toSnakeCase(objName)}_unref': ['void', [{objName}]],\n" 265 | 266 | proc genSeqProcs(objName, ownObjName, procPrefix, selfSuffix: string, entryType: NimNode) = 267 | 268 | types.add &"{ownObjName}.prototype.length = function(){{\n" 269 | types.add &" return dll.{procPrefix}_len(this{selfSuffix})\n" 270 | types.add "};\n" 271 | procs.add &" '{procPrefix}_len': ['uint64', [{objName}]],\n" 272 | 273 | types.add &"{ownObjName}.prototype.get = function(index){{\n" 274 | types.add &" return dll.{procPrefix}_get(this{selfSuffix}, index)\n" 275 | types.add "};\n" 276 | procs.add &" '{procPrefix}_get': [{exportTypeNode(entryType)}, [{objName}, 'uint64']],\n" 277 | 278 | types.add &"{ownObjName}.prototype.set = function(index, value){{\n" 279 | types.add &" dll.{procPrefix}_set(this{selfSuffix}, index, value)\n" 280 | types.add "};\n" 281 | procs.add &" '{procPrefix}_set': ['void', [{objName}, 'uint64', {exportTypeNode(entryType)}]],\n" 282 | 283 | types.add &"{ownObjName}.prototype.delete = function(index){{\n" 284 | types.add &" dll.{procPrefix}_delete(this{selfSuffix}, index)\n" 285 | types.add "};\n" 286 | procs.add &" '{procPrefix}_delete': ['void', [{objName}, 'uint64']],\n" 287 | 288 | types.add &"{ownObjName}.prototype.add = function(value){{\n" 289 | types.add &" dll.{procPrefix}_add(this{selfSuffix}, value)\n" 290 | types.add "};\n" 291 | procs.add &" '{procPrefix}_add': ['void', [{objName}, {exportTypeNode(entryType)}]],\n" 292 | 293 | types.add &"{ownObjName}.prototype.clear = function(){{\n" 294 | types.add &" dll.{procPrefix}_clear(this{selfSuffix})\n" 295 | types.add "};\n" 296 | procs.add &" '{procPrefix}_clear': ['void', [{objName}]],\n" 297 | 298 | proc exportRefObjectNode*( 299 | sym: NimNode, 300 | fields: seq[(string, NimNode)], 301 | constructor: NimNode 302 | ) = 303 | let 304 | objName = sym.repr 305 | objNameSnaked = toSnakeCase(objName) 306 | objType = sym.getType()[1].getType() 307 | 308 | genRefObject(objName) 309 | 310 | if constructor != nil: 311 | let 312 | constructorLibProc = &"$lib_{toSnakeCase(constructor.repr)}" 313 | constructorType = constructor.getTypeInst() 314 | constructorParams = constructorType[0][1 .. ^1] 315 | constructorRaises = constructor.raises() 316 | 317 | exports.add &"exports.{objName} = new{objName}\n" 318 | types.add &"function new{objName}(" 319 | for i, param in constructorParams[0 .. ^1]: 320 | types.add &"{toSnakeCase(param[0].repr)}" 321 | types.add ", " 322 | types.removeSuffix ", " 323 | types.add "){\n" 324 | types.add &" var result = " 325 | types.add &"dll.{constructorLibProc}(" 326 | for i, param in constructorParams[0 .. ^1]: 327 | types.add &"{toSnakeCase(param[0].repr)}{convertExportFromNode(param[1])}" 328 | types.add ", " 329 | types.removeSuffix ", " 330 | types.add ")\n" 331 | 332 | types.add " const registry = new FinalizationRegistry(function(obj) {\n" 333 | types.add " console.log(\"js unref\")\n" 334 | types.add " obj.unref()\n" 335 | types.add " });\n" 336 | types.add " registry.register(result, null);\n" 337 | 338 | 339 | if constructorRaises: 340 | types.add &" if(checkError()) " 341 | types.add "throw new $LibException(" 342 | types.add "takeError()" 343 | types.add ");\n" 344 | types.add " return result\n" 345 | types.add "}\n" 346 | 347 | dllProc(constructorLibProc, constructorParams, objName) 348 | 349 | for (fieldName, fieldType) in fields: 350 | let fieldNameSnaked = toSnakeCase(fieldName) 351 | 352 | if fieldType.kind != nnkBracketExpr: 353 | let getProcName = &"$lib_{objNameSnaked}_get_{fieldNameSnaked}" 354 | let setProcName = &"$lib_{objNameSnaked}_set_{fieldNameSnaked}" 355 | 356 | types.add &"Object.defineProperty({objName}.prototype, '{fieldName}', {{\n" 357 | types.add &" get: function() {{return dll.{getProcName}(this)}},\n" 358 | types.add &" set: function(v) {{dll.{setProcName}(this, v)}}\n" 359 | types.add "});\n" 360 | 361 | procs.add &" '{getProcName}': [{exportTypeNode(fieldType)}, [{objName}]],\n" 362 | procs.add &" '{setProcName}': ['void', [{objName}, {exportTypeNode(fieldType)}]],\n" 363 | 364 | else: 365 | discard 366 | var helperName = fieldName 367 | helperName[0] = toUpperAscii(helperName[0]) 368 | let helperClassName = objName & helperName 369 | 370 | types.add &"function {helperClassName}({toVarCase(objName)}){{\n" 371 | types.add &" this.{toVarCase(objName)} = {toVarCase(objName)};\n" 372 | types.add "}\n" 373 | 374 | genSeqProcs( 375 | objName, 376 | helperClassName, 377 | &"$lib_{objNameSnaked}_{fieldNameSnaked}", 378 | &".{toSnakeCase(objName)}", 379 | fieldType[1] 380 | ) 381 | 382 | types.add &"Object.defineProperty({objName}.prototype, '{fieldName}', {{\n" 383 | types.add &" get: function() {{return new {helperClassName}(this)}},\n" 384 | types.add "});\n" 385 | 386 | types.add "\n" 387 | 388 | proc exportSeqNode*(sym: NimNode) = 389 | let 390 | seqName = sym.getName() 391 | seqNameSnaked = toSnakeCase(seqName) 392 | 393 | genRefObject(seqName) 394 | 395 | let newSeqProc = &"$lib_new_{toSnakeCase(seqName)}" 396 | 397 | types.add &"function {toVarCase(seqName)}(){{\n" 398 | types.add &" return dll.{newSeqProc}();\n" 399 | types.add "}\n" 400 | 401 | procs.add &" '{newSeqProc}': [{seqName}, []],\n" 402 | 403 | genSeqProcs( 404 | sym.getName(), 405 | sym.getName(), 406 | &"$lib_{seqNameSnaked}", 407 | "", 408 | sym[1] 409 | ) 410 | 411 | const header = """ 412 | var ffi = require('ffi-napi'); 413 | var Struct = require("ref-struct-napi"); 414 | var ArrayType = require('ref-array-napi'); 415 | 416 | var dll = {}; 417 | 418 | function $LibException(message) { 419 | this.message = message; 420 | this.name = '$LibException'; 421 | } 422 | 423 | """ 424 | const loader = """ 425 | 426 | var dllPath = "" 427 | if(process.platform == "win32") { 428 | dllPath = __dirname + '/$lib.dll' 429 | } else if (process.platform == "darwin") { 430 | dllPath = __dirname + '/lib$lib.dylib' 431 | } else { 432 | dllPath = __dirname + '/lib$lib.so' 433 | } 434 | 435 | dll = ffi.Library(dllPath, { 436 | """ 437 | const footer = """ 438 | }); 439 | 440 | """ 441 | 442 | proc writeNode*(dir, lib: string) = 443 | createDir(dir) 444 | writeFile( 445 | &"{dir}/{toSnakeCase(lib)}.js", 446 | (header & types & loader & procs & footer & exports) 447 | .replace("$Lib", lib).replace("$lib", toSnakeCase(lib)) 448 | ) 449 | -------------------------------------------------------------------------------- /src/genny/languages/python.nim: -------------------------------------------------------------------------------- 1 | import 2 | std/[os, strformat, strutils, macros], 3 | ../common 4 | 5 | var 6 | types {.compiletime.}: string 7 | procs {.compiletime.}: string 8 | 9 | const operators = ["add", "sub", "mul", "div"] 10 | 11 | proc exportTypePy(sym: NimNode): string = 12 | if sym.kind == nnkBracketExpr: 13 | if sym[0].repr == "array": 14 | let 15 | entryCount = sym[1].repr 16 | entryType = exportTypePy(sym[2]) 17 | result = &"{entryType} * {entryCount}" 18 | elif sym[0].repr == "seq": 19 | result = sym.getSeqName() 20 | else: 21 | error(&"Unexpected bracket expression {sym[0].repr}[") 22 | else: 23 | result = 24 | case sym.repr: 25 | of "string": "c_char_p" 26 | of "cstring": "c_char_p" 27 | of "pointer": "c_void_p" 28 | of "bool": "c_bool" 29 | of "int8": "c_byte" 30 | of "byte": "c_byte" 31 | of "int16": "c_short" 32 | of "int32": "c_int" 33 | of "int64": "c_longlong" 34 | of "int": "c_longlong" 35 | of "uint8": "c_ubyte" 36 | of "uint16": "c_ushort" 37 | of "uint32": "c_uint" 38 | of "uint64": "c_ulonglong" 39 | of "uint": "c_ulonglong" 40 | of "float32": "c_float" 41 | of "float64": "c_double" 42 | of "float": "c_double" 43 | of "Rune": "c_int" 44 | of "Vec2": "Vector2" 45 | of "Mat3": "Matrix3" 46 | of "", "nil": "None" 47 | else: 48 | sym.repr 49 | 50 | proc convertExportFromPy*(sym: NimNode): string = 51 | if sym.repr == "string": 52 | result = ".encode(\"utf8\")" 53 | 54 | proc convertImportToPy*(sym: NimNode): string = 55 | if sym.repr == "string": 56 | result = ".decode(\"utf8\")" 57 | 58 | proc toArgTypes(args: openarray[NimNode]): seq[string] = 59 | for arg in args: 60 | result.add exportTypePy(arg) 61 | 62 | proc dllProc*(procName: string, args: openarray[string], restype: string) = 63 | var argtypes = join(args, ", ") 64 | argtypes.removeSuffix ", " 65 | procs.add &"{procName}.argtypes = [{argtypes}]\n" 66 | procs.add &"{procName}.restype = {restype}\n" 67 | procs.add "\n" 68 | 69 | proc exportConstPy*(sym: NimNode) = 70 | types.add &"{toCapSnakeCase(sym.repr)} = {sym.getImpl()[2].repr}\n" 71 | types.add "\n" 72 | 73 | proc exportEnumPy*(sym: NimNode) = 74 | types.add &"{sym.repr} = c_byte\n" 75 | for i, entry in sym.getImpl()[2][1 .. ^1]: 76 | types.add &"{toCapSnakeCase(entry.repr)} = {i}\n" 77 | types.add "\n" 78 | 79 | proc exportProcPy*( 80 | sym: NimNode, 81 | owner: NimNode = nil, 82 | prefixes: openarray[NimNode] = [] 83 | ) = 84 | let 85 | procName = sym.repr 86 | procNameSnaked = toSnakeCase(procName) 87 | procType = sym.getTypeInst() 88 | procParams = procType[0][1 .. ^1] 89 | procReturn = procType[0][0] 90 | procRaises = sym.raises() 91 | onClass = owner != nil 92 | 93 | var apiProcName = "" 94 | if owner != nil: 95 | apiProcName.add &"{toSnakeCase(owner.getName())}_" 96 | for prefix in prefixes: 97 | apiProcName.add &"{toSnakeCase(prefix.getName())}_" 98 | apiProcName.add &"{procNameSnaked}" 99 | 100 | var defaults: seq[(string, NimNode)] 101 | for identDefs in sym.getImpl()[3][1 .. ^1]: 102 | let default = identDefs[^1] 103 | for entry in identDefs[0 .. ^3]: 104 | defaults.add((entry.repr, default)) 105 | 106 | if onClass: 107 | types.add " def " 108 | if sym.repr in operators and 109 | procReturn.kind != nnkEmpty and 110 | prefixes.len == 0: 111 | types.add &"__{sym.repr}__(" 112 | else: 113 | if prefixes.len > 0: 114 | if prefixes[0].getImpl().kind != nnkNilLIt: 115 | if prefixes[0].getImpl()[2].kind != nnkEnumTy: 116 | types.add &"{toSnakeCase(prefixes[0].repr)}_" 117 | types.add &"{toSnakeCase(sym.repr)}(" 118 | else: 119 | types.add &"def {apiProcName}(" 120 | for i, param in procParams[0 .. ^1]: 121 | if onClass and i == 0: 122 | types.add "self" 123 | else: 124 | types.add toSnakeCase(param[0].repr) 125 | case defaults[i][1].kind: 126 | of nnkIntLit, nnkFloatLit: 127 | types.add &" = {defaults[i][1].repr}" 128 | of nnkIdent: 129 | if defaults[i][1].repr == "true": 130 | types.add " = True" 131 | elif defaults[i][1].repr == "false": 132 | types.add " = False" 133 | else: 134 | types.add &" = {toCapSnakeCase(defaults[i][1].repr)}" 135 | else: 136 | if defaults[i][1].kind != nnkEmpty: 137 | types.add &" = None" 138 | types.add &", " 139 | types.removeSuffix ", " 140 | types.add "):\n" 141 | let comments = 142 | if sym.getImpl()[6][0].kind == nnkCommentStmt: 143 | sym.getImpl()[6][0].repr 144 | elif sym.getImpl[6].kind == nnkAsgn and 145 | sym.getImpl[6][1].kind == nnkStmtListExpr and 146 | sym.getImpl[6][1][0].kind == nnkCommentStmt: 147 | sym.getImpl[6][1][0].repr 148 | else: 149 | "" 150 | if comments != "": 151 | let lines = comments.replace("## ", "").split("\n") 152 | if onClass: types.add " " 153 | types.add " \"\"\"\n" 154 | for line in lines: 155 | if onClass: types.add " " 156 | types.add &" {line}\n" 157 | if onClass: types.add " " 158 | types.add " \"\"\"\n" 159 | for i, param in procParams: 160 | if i == 0: 161 | continue 162 | if defaults[i][1].kind notin {nnkEmpty, nnkIntLit, nnkFloatLit, nnkIdent}: 163 | if onClass: 164 | types.add " " 165 | types.add &" if {toSnakeCase(param[0].repr)} is None:\n" 166 | if onClass: 167 | types.add " " 168 | types.add &" {toSnakeCase(param[0].repr)} = " 169 | types.add &"{exportTypePy(param[1])}(" 170 | if defaults[i][1].kind == nnkCall: 171 | for d in defaults[i][1][1 .. ^1]: 172 | types.add &"{d.repr}, " 173 | types.removeSuffix ", " 174 | types.add ")\n" 175 | 176 | if onClass: 177 | types.add " " 178 | types.add " " 179 | if procReturn.kind != nnkEmpty: 180 | types.add "result = " 181 | types.add &"dll.$lib_{apiProcName}(" 182 | for i, param in procParams[0 .. ^1]: 183 | if onClass and i == 0: 184 | types.add "self" 185 | else: 186 | types.add &"{toSnakeCase(param[0].repr)}{convertExportFromPy(param[1])}" 187 | types.add &", " 188 | types.removeSuffix ", " 189 | types.add &"){convertImportToPy(procReturn)}\n" 190 | if procRaises: 191 | if onClass: 192 | types.add " " 193 | types.add &" if check_error():\n" 194 | if onClass: 195 | types.add " " 196 | types.add " raise $LibError(" 197 | types.add "take_error()" 198 | types.add ")\n" 199 | if procReturn.kind != nnkEmpty: 200 | if onClass: 201 | types.add " " 202 | types.add " return result\n" 203 | types.add "\n" 204 | 205 | var dllParams: seq[NimNode] 206 | for param in procParams: 207 | dllParams.add(param[1]) 208 | dllProc(&"dll.$lib_{apiProcName}", toArgTypes(dllParams), exportTypePy(procReturn)) 209 | 210 | proc exportObjectPy*(sym: NimNode, constructor: NimNode) = 211 | let objName = sym.repr 212 | 213 | types.add &"class {objName}(Structure):\n" 214 | types.add " _fields_ = [\n" 215 | for identDefs in sym.getImpl()[2][2]: 216 | for property in identDefs[0 .. ^3]: 217 | types.add &" (\"{toSnakeCase(property[1].repr)}\"" 218 | types.add ", " 219 | types.add &"{exportTypePy(identDefs[^2])}),\n" 220 | types.removeSuffix ",\n" 221 | types.add "\n" 222 | types.add " ]\n" 223 | types.add "\n" 224 | 225 | if constructor != nil: 226 | let 227 | constructorType = constructor.getTypeInst() 228 | constructorParams = constructorType[0][1 .. ^1] 229 | types.add " def __init__(self, " 230 | for param in constructorParams: 231 | types.add &"{toSnakeCase(param[0].repr)}" 232 | types.add ", " 233 | types.removeSuffix ", " 234 | types.add "):\n" 235 | types.add &" tmp = dll.$lib_{toSnakeCase(objName)}(" 236 | for param in constructorParams: 237 | types.add &"{toSnakeCase(param[0].repr)}" 238 | types.add ", " 239 | types.removeSuffix ", " 240 | types.add ")\n" 241 | for identDefs in sym.getImpl()[2][2]: 242 | for property in identDefs[0 .. ^3]: 243 | types.add &" self.{toSnakeCase(property[1].repr)} = " 244 | types.add &"tmp.{toSnakeCase(property[1].repr)}\n" 245 | types.add "\n" 246 | var dllParams: seq[NimNode] 247 | for param in constructorParams: 248 | dllParams.add(param[1]) 249 | dllProc(&"dll.$lib_{toSnakeCase(objName)}", toArgTypes(dllParams), objName) 250 | else: 251 | types.add " def __init__(self, " 252 | for identDefs in sym.getImpl()[2][2]: 253 | for property in identDefs[0 .. ^3]: 254 | types.add &"{toSnakeCase(property[1].repr)}, " 255 | types.removeSuffix ", " 256 | types.add "):\n" 257 | for identDefs in sym.getImpl()[2][2]: 258 | for property in identDefs[0 .. ^3]: 259 | types.add " " 260 | types.add &"self.{toSnakeCase(property[1].repr)} = " 261 | types.add &"{toSnakeCase(property[1].repr)}\n" 262 | types.add "\n" 263 | 264 | types.add " def __eq__(self, obj):\n" 265 | types.add " return " 266 | for identDefs in sym.getImpl()[2][2]: 267 | for property in identDefs[0 .. ^3]: 268 | if identDefs[^2].len > 0 and identDefs[^2][0].repr == "array": 269 | for i in 0 ..< identDefs[^2][1].intVal: 270 | types.add &"self.{toSnakeCase(property[1].repr)}[{i}] == obj.{toSnakeCase(property[1].repr)}[{i}] and " 271 | else: 272 | types.add &"self.{toSnakeCase(property[1].repr)} == obj.{toSnakeCase(property[1].repr)} and " 273 | types.removeSuffix " and " 274 | types.add "\n" 275 | types.add "\n" 276 | 277 | proc genRefObject(objName: string) = 278 | types.add &"class {objName}(Structure):\n" 279 | types.add " _fields_ = [(\"ref\", c_ulonglong)]\n" 280 | types.add "\n" 281 | 282 | types.add " def __bool__(self):\n" 283 | types.add " return self.ref != None\n" 284 | types.add "\n" 285 | 286 | types.add " def __eq__(self, obj):\n" 287 | types.add " return self.ref == obj.ref\n" 288 | types.add "\n" 289 | 290 | let unrefLibProc = &"dll.$lib_{toSnakeCase(objName)}_unref" 291 | 292 | types.add " def __del__(self):\n" 293 | types.add &" {unrefLibProc}(self)\n" 294 | types.add "\n" 295 | 296 | dllProc(unrefLibProc, [objName], "None") 297 | 298 | proc genSeqProcs(objName, procPrefix, selfSuffix: string, entryType: NimNode) = 299 | var baseIndent = " " 300 | if selfSuffix != "": # This is a bound seq 301 | baseIndent = " " 302 | 303 | types.add &"{baseIndent}def __len__(self):\n" 304 | types.add &"{baseIndent} return dll.{procPrefix}_len(self{selfSuffix})\n" 305 | types.add "\n" 306 | 307 | types.add &"{baseIndent}def __getitem__(self, index):\n" 308 | types.add &"{baseIndent} return dll.{procPrefix}_get(self{selfSuffix}, index){convertImportToPy(entryType)}\n" 309 | types.add "\n" 310 | 311 | types.add &"{baseIndent}def __setitem__(self, index, value):\n" 312 | types.add &"{baseIndent} dll.{procPrefix}_set(self{selfSuffix}, index, value{convertExportFromPy(entryType)})\n" 313 | types.add "\n" 314 | 315 | types.add &"{baseIndent}def __delitem__(self, index):\n" 316 | types.add &"{baseIndent} dll.{procPrefix}_delete(self{selfSuffix}, index)\n" 317 | types.add "\n" 318 | 319 | types.add &"{baseIndent}def append(self, value):\n" 320 | types.add &"{baseIndent} dll.{procPrefix}_add(self{selfSuffix}, value)\n" 321 | types.add "\n" 322 | 323 | types.add &"{baseIndent}def clear(self):\n" 324 | types.add &"{baseIndent} dll.{procPrefix}_clear(self{selfSuffix})\n" 325 | types.add "\n" 326 | 327 | types.add &"{baseIndent}def __iter__(self):\n" 328 | types.add &"{baseIndent} return SeqIterator(self)\n" 329 | types.add "\n" 330 | 331 | dllProc(&"dll.{procPrefix}_len", [objName], "c_longlong") 332 | dllProc(&"dll.{procPrefix}_get", [objName, "c_longlong"], exportTypePy(entryType)) 333 | dllProc(&"dll.{procPrefix}_set", [objName, "c_longlong", exportTypePy(entryType)], "None") 334 | dllProc(&"dll.{procPrefix}_delete", [objName, "c_longlong"], "None") 335 | dllProc(&"dll.{procPrefix}_add", [objName, exportTypePy(entryType)], "None") 336 | dllProc(&"dll.{procPrefix}_clear", [objName], "None") 337 | 338 | proc exportRefObjectPy*( 339 | sym: NimNode, 340 | fields: seq[(string, NimNode)], 341 | constructor: NimNode 342 | ) = 343 | let 344 | objName = sym.repr 345 | objNameSnaked = toSnakeCase(objName) 346 | objType = sym.getType()[1].getType() 347 | 348 | genRefObject(objName) 349 | 350 | if constructor != nil: 351 | let 352 | constructorLibProc = &"dll.$lib_{toSnakeCase(constructor.repr)}" 353 | constructorType = constructor.getTypeInst() 354 | constructorParams = constructorType[0][1 .. ^1] 355 | constructorRaises = constructor.raises() 356 | 357 | types.add " def __init__(self, " 358 | for i, param in constructorParams[0 .. ^1]: 359 | types.add &"{toSnakeCase(param[0].repr)}" 360 | types.add ", " 361 | types.removeSuffix ", " 362 | types.add "):\n" 363 | types.add &" result = " 364 | types.add &"{constructorLibProc}(" 365 | for param in constructorParams: 366 | types.add &"{toSnakeCase(param[0].repr)}{convertExportFromPy(param[1])}" 367 | types.add ", " 368 | types.removeSuffix ", " 369 | types.add ")\n" 370 | if constructorRaises: 371 | types.add &" if check_error():\n" 372 | types.add " raise $LibError(" 373 | types.add "take_error()" 374 | types.add ")\n" 375 | types.add " self.ref = result\n" 376 | types.add "\n" 377 | 378 | var dllParams: seq[NimNode] 379 | for param in constructorParams: 380 | dllParams.add(param[1]) 381 | dllProc(constructorLibProc, toArgTypes(dllParams), "c_ulonglong") 382 | 383 | for (fieldName, fieldType) in fields: 384 | let fieldNameSnaked = toSnakeCase(fieldName) 385 | 386 | if fieldType.kind != nnkBracketExpr: 387 | let getProcName = &"dll.$lib_{objNameSnaked}_get_{fieldNameSnaked}" 388 | 389 | types.add " @property\n" 390 | types.add &" def {fieldNameSnaked}(self):\n" 391 | types.add " " 392 | types.add &"return {getProcName}(self){convertImportToPy(fieldType)}\n" 393 | 394 | let setProcName = &"dll.$lib_{objNameSnaked}_set_{fieldNameSnaked}" 395 | 396 | types.add "\n" 397 | types.add &" @{fieldNameSnaked}.setter\n" 398 | types.add &" def {fieldNameSnaked}(self, {fieldNameSnaked}):\n" 399 | types.add " " 400 | types.add &"{setProcName}(self, " 401 | types.add &"{fieldNameSnaked}{convertExportFromPy(fieldType)}" 402 | types.add ")\n" 403 | types.add "\n" 404 | 405 | dllProc(getProcName, toArgTypes([sym]), exportTypePy(fieldType)) 406 | dllProc(setProcName, toArgTypes([sym, fieldType]), exportTypePy(nil)) 407 | else: 408 | var helperName = fieldName 409 | helperName[0] = toUpperAscii(helperName[0]) 410 | let helperClassName = objName & helperName 411 | 412 | types.add &" class {helperClassName}:\n" 413 | types.add "\n" 414 | types.add &" def __init__(self, {toSnakeCase(objName)}):\n" 415 | types.add &" self.{toSnakeCase(objName)} = {toSnakeCase(objName)}\n" 416 | types.add "\n" 417 | 418 | genSeqProcs( 419 | objName, 420 | &"$lib_{objNameSnaked}_{fieldNameSnaked}", 421 | &".{toSnakeCase(objName)}", 422 | fieldType[1] 423 | ) 424 | 425 | types.add " @property\n" 426 | types.add &" def {toSnakeCase(helperName)}(self):\n" 427 | types.add &" return self.{helperClassName}(self)\n" 428 | types.add "\n" 429 | 430 | proc exportSeqPy*(sym: NimNode) = 431 | let 432 | seqName = sym.getName() 433 | seqNameSnaked = toSnakeCase(seqName) 434 | 435 | genRefObject(seqName) 436 | 437 | let newSeqProc = &"dll.$lib_new_{toSnakeCase(seqName)}" 438 | 439 | types.add " def __init__(self):\n" 440 | types.add &" self.ref = {newSeqProc}()\n" 441 | types.add "\n" 442 | 443 | dllProc(newSeqProc, [], "c_ulonglong") 444 | 445 | genSeqProcs( 446 | sym.getName(), 447 | &"$lib_{seqNameSnaked}", 448 | "", 449 | sym[1] 450 | ) 451 | 452 | const header = """ 453 | from ctypes import * 454 | import os, sys 455 | 456 | dir = os.path.dirname(sys.modules["$lib"].__file__) 457 | if sys.platform == "win32": 458 | libName = "$lib.dll" 459 | elif sys.platform == "darwin": 460 | libName = "lib$lib.dylib" 461 | else: 462 | libName = "lib$lib.so" 463 | dll = cdll.LoadLibrary(os.path.join(dir, libName)) 464 | 465 | class $LibError(Exception): 466 | pass 467 | 468 | class SeqIterator(object): 469 | def __init__(self, seq): 470 | self.idx = 0 471 | self.seq = seq 472 | def __iter__(self): 473 | return self 474 | def __next__(self): 475 | if self.idx < len(self.seq): 476 | self.idx += 1 477 | return self.seq[self.idx - 1] 478 | else: 479 | self.idx = 0 480 | raise StopIteration 481 | 482 | """ 483 | 484 | proc writePy*(dir, lib: string) = 485 | createDir(dir) 486 | writeFile(&"{dir}/{toSnakeCase(lib)}.py", (header & types & procs) 487 | .replace("$Lib", lib).replace("$lib", toSnakeCase(lib)) 488 | ) 489 | --------------------------------------------------------------------------------