├── 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 | 
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 |
--------------------------------------------------------------------------------