f149dc12
Andreas Rumpf
first somewhat wo...
|
1
|
|
66d70ca8
Andreas Rumpf
squeaknim doesn't...
|
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
|
import macros
# Note: Since we combine -d:useNimRtl with this module, we cannot use strutils.
# So we use our own helper procs here:
proc toUpper(c: char): char =
result = if c in {'a'..'z'}: chr(c.ord - 'a'.ord + 'A'.ord) else: c
proc capitalize(s: string): string {.noSideEffect.} =
result = toUpper(s[0]) & substr(s, 1)
proc invalidFormatString() {.noinline.} =
raise newException(ValueError, "invalid format string")
proc addf(s: var string, formatstr: string, a: varargs[string, `$`]) =
## The same as ``add(s, formatstr % a)``, but more efficient.
const PatternChars = {'a'..'z', 'A'..'Z', '0'..'9', '\128'..'\255', '_'}
var i = 0
var num = 0
while i < len(formatstr):
if formatstr[i] == '$':
case formatstr[i+1] # again we use the fact that strings
# are zero-terminated here
of '#':
if num >% a.high: invalidFormatString()
add s, a[num]
inc i, 2
inc num
of '$':
add s, '$'
inc(i, 2)
of '1'..'9', '-':
var j = 0
inc(i) # skip $
var negative = formatstr[i] == '-'
if negative: inc i
while formatstr[i] in {'0'..'9'}:
j = j * 10 + ord(formatstr[i]) - ord('0')
inc(i)
let idx = if not negative: j-1 else: a.len-j
if idx >% a.high: invalidFormatString()
add s, a[idx]
else:
invalidFormatString()
else:
add s, formatstr[i]
inc(i)
proc `%`(formatstr: string, a: openArray[string]): string =
result = newStringOfCap(formatstr.len + a.len shl 4)
addf(result, formatstr, a)
proc `%`(formatstr, a: string): string =
result = newStringOfCap(formatstr.len + a.len)
addf(result, formatstr, [a])
proc format(formatstr: string, a: varargs[string, `$`]): string =
result = newStringOfCap(formatstr.len + a.len)
addf(result, formatstr, a)
|
f149dc12
Andreas Rumpf
first somewhat wo...
|
61
62
63
64
65
66
67
68
69
70
|
const
pragmaPos = 4
paramPos = 3
intType = when sizeof(int) == 8: "longlong" else: "long"
uintType = when sizeof(int) == 8: "ulonglong" else: "ulong"
var
dllName {.compileTime.}: string = "SqueakNimTest"
stCode {.compileTime.}: string = ""
|
032258fd
Andreas Rumpf
various improvements
|
71
|
gPrefix {.compileTime.}: string = ""
|
f149dc12
Andreas Rumpf
first somewhat wo...
|
72
|
|
032258fd
Andreas Rumpf
various improvements
|
73
|
template setModulename*(s, prefix: string) =
|
f149dc12
Andreas Rumpf
first somewhat wo...
|
74
|
## Sets the DLL name. This is also used to set the 'category' in the generated
|
032258fd
Andreas Rumpf
various improvements
|
75
76
|
## classes. 'prefix' is added to every generated class that wraps a Nim
## object.
|
f149dc12
Andreas Rumpf
first somewhat wo...
|
77
78
|
static:
dllName = s
|
032258fd
Andreas Rumpf
various improvements
|
79
|
gPrefix = prefix
|
f149dc12
Andreas Rumpf
first somewhat wo...
|
80
81
82
|
template writeExternalLibrary*() =
static:
|
53b6fbaa
Andreas Rumpf
make Smalltalk co...
|
83
84
85
86
87
88
89
|
addf(stCode, "ExternalLibrary subclass: #$1\C" &
"\tinstanceVariableNames: ''\C" &
"\tclassVariableNames: ''\C" &
"\tpoolDictionaries: ''\C" &
"\tcategory: '$1'!\C" &
"!$1 class methodsFor: 'primitives' stamp: 'SqueakNim'!\C",
gPrefix & capitalize(dllName))
|
f149dc12
Andreas Rumpf
first somewhat wo...
|
90
91
92
93
94
95
96
97
|
template writeSmallTalkCode*(filename: string) =
## You need to invoke this template to write the produced SmallTalk code to
## a file.
static:
writeFile(filename, stCode)
proc mapTypeToC(symbolicType: NimNode): string {.compileTime.} =
|
e95712ca
Andreas Rumpf
bugfixes and clea...
|
98
|
if symbolicType.kind == nnkEmpty: return "void"
|
f149dc12
Andreas Rumpf
first somewhat wo...
|
99
100
|
let t = symbolicType.getType
if symbolicType.kind == nnkSym and t.typeKind == ntyObject:
|
032258fd
Andreas Rumpf
various improvements
|
101
|
return gPrefix & $symbolicType
|
f149dc12
Andreas Rumpf
first somewhat wo...
|
102
|
case t.typeKind
|
f149dc12
Andreas Rumpf
first somewhat wo...
|
103
104
105
106
|
of ntyPtr, ntyVar:
expectKind t, nnkBracketExpr
result = mapTypeToC(t[1]) & "*"
of ntyCString: result = "char*"
|
e95712ca
Andreas Rumpf
bugfixes and clea...
|
107
|
of ntyPointer: result = "void*"
|
f149dc12
Andreas Rumpf
first somewhat wo...
|
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
|
of ntyInt: result = intType
of ntyInt8: result = "sbyte"
of ntyInt16: result = "short"
of ntyInt32: result = "long"
of ntyInt64: result = "longlong"
of ntyUInt: result = uintType
of ntyUInt8: result = "ubyte"
of ntyUInt16: result = "ushort"
of ntyUInt32: result = "ulong"
of ntyUInt64: result = "ulonglong"
of ntyFloat, ntyFloat64: result = "double"
of ntyFloat32: result = "float"
of ntyBool, ntyChar, ntyEnum: result = "char"
else: quit "Error: cannot wrap to Squeak " & treeRepr(t)
|
e95712ca
Andreas Rumpf
bugfixes and clea...
|
123
|
macro exportSt*(body: stmt): stmt =
|
f149dc12
Andreas Rumpf
first somewhat wo...
|
124
125
126
127
128
129
130
131
|
# generates something like:
# system: aString
#"Some kind of comment"
#
# <apicall: long 'system' (char*) module: 'libSystem.dylib'>
# ^self externalCallFailed.
result = body
|
66d70ca8
Andreas Rumpf
squeaknim doesn't...
|
132
133
134
135
136
|
when defined(cpp):
result[pragmaPos].add(ident"exportc", ident"dynlib", ident"cdecl",
newColonExpr(ident"codegenDecl", newLit"""extern "C" $# $#$#"""))
else:
result[pragmaPos].add(ident"exportc", ident"dynlib", ident"cdecl")
|
f149dc12
Andreas Rumpf
first somewhat wo...
|
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
|
let params = result[paramPos]
let procName = $result[0]
var st = procName
#echo treeRepr params
if params.len > 1:
expectKind params[1], nnkIdentDefs
let ident = $params[1][0]
if ident.len > 1:
st.add(ident.capitalize & ": " & ident)
else:
st.add(": " & ident)
# return type:
var apicall = "<cdecl: " & mapTypeToC(params[0]) & " '" &
procName & "' ("
var counter = 0
# parameter types:
for i in 1.. <params.len:
let param = params[i]
let L = param.len
for j in 0 .. param.len-3:
let name = param[j]
let typ = param[L-2]
if counter > 0:
apicall.add(" ")
st.addf(" $1: $1", name)
apicall.add(mapTypeToC(typ))
inc counter
|
53b6fbaa
Andreas Rumpf
make Smalltalk co...
|
164
165
166
|
apicall.add(") module: '" & dllName & "'>\C" &
"\t^self externalCallFailed\C!\C\C")
stCode.add(st & "\C\t\"Generated by NimSqueak\"\C\t" & apicall)
|
f149dc12
Andreas Rumpf
first somewhat wo...
|
167
|
|
032258fd
Andreas Rumpf
various improvements
|
168
|
macro wrapObject*(typ: stmt): stmt =
|
f149dc12
Andreas Rumpf
first somewhat wo...
|
169
|
## Declares a SmallTalk wrapper class.
|
f149dc12
Andreas Rumpf
first somewhat wo...
|
170
171
172
173
|
var t = typ.getType()
if t.typeKind == ntyTypeDesc:
expectKind t, nnkBracketExpr
t = t[1]
|
032258fd
Andreas Rumpf
various improvements
|
174
175
|
expectKind t, nnkSym
let name = gPrefix & ($t).capitalize
|
f149dc12
Andreas Rumpf
first somewhat wo...
|
176
177
178
179
180
181
182
|
if t.kind != nnkObjectTy: t = t.getType
expectKind t, nnkObjectTy
t = t[1]
expectKind t, nnkRecList
var fields = ""
for i in 0.. < t.len:
expectKind t[i], nnkSym
|
53b6fbaa
Andreas Rumpf
make Smalltalk co...
|
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
|
fields.addf "\t\t($# '$#')\C", $t[i], mapTypeToC(t[i])
let st = ("ExternalStructure subclass: #$1\C" &
"\tinstanceVariableNames: ''\C" &
"\tclassVariableNames: ''\C" &
"\tpoolDictionaries: 'FFIConstants'\C" &
"\tcategory: '$2'!\C\C" &
"$1 class\C" &
"\tinstanceVariableNames: ''!\C\C" &
"!$1 class methodsFor: 'field definition' stamp: 'SqueakNim'!\C" &
"\tfields\C" &
"\t^#(\C" &
"$3\C" &
"\t)! !\C" &
"$1 defineFields.\C!\C\C") % [name, dllName, fields]
|
f149dc12
Andreas Rumpf
first somewhat wo...
|
198
199
|
stCode.add(st)
result = newStmtList()
|