-
Notifications
You must be signed in to change notification settings - Fork 10
/
instance.go
314 lines (278 loc) · 9.66 KB
/
instance.go
1
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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
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
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
package clips
// #cgo CFLAGS: -I ../../clips_source
// #cgo LDFLAGS: -L ../../clips_source -l clips -lm
// #include <clips/clips.h>
import "C"
/*
Copyright 2020 Keysight Technologies
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*/
import (
"fmt"
"reflect"
"runtime"
"unsafe"
)
// Instance represents an instance of a class from CLIPS
type Instance struct {
env *Environment
instptr unsafe.Pointer
}
// InstancesChanged returns true if any instance has changed
func (env *Environment) InstancesChanged() bool {
ret := C.EnvGetInstancesChanged(env.env)
C.EnvSetInstancesChanged(env.env, 0)
if ret == 1 {
return true
}
return false
}
// Instances returns all defined instances
func (env *Environment) Instances() []*Instance {
instptr := C.EnvGetNextInstance(env.env, nil)
ret := make([]*Instance, 0, 10)
for instptr != nil {
ret = append(ret, createInstance(env, instptr))
instptr = C.EnvGetNextInstance(env.env, instptr)
}
return ret
}
// FindInstance returns the instance of the given name. module may be the empty string to use the current module
func (env *Environment) FindInstance(name InstanceName, module string) (*Instance, error) {
var modptr unsafe.Pointer
if module != "" {
cmod := C.CString(module)
defer C.free(unsafe.Pointer(cmod))
modptr = C.EnvFindDefmodule(env.env, cmod)
if modptr == nil {
return nil, NotFoundError(fmt.Errorf(`Module "%s" not found`, module))
}
}
cname := C.CString(string(name))
defer C.free(unsafe.Pointer(cname))
instptr := C.EnvFindInstance(env.env, modptr, cname, 1)
if instptr == nil {
return nil, NotFoundError(fmt.Errorf(`Instance "%s" not found`, name))
}
return createInstance(env, instptr), nil
}
// LoadInstancesFromString loads a set of instances into the CLIPS database. Equivalent to the load-instances command
func (env *Environment) LoadInstancesFromString(instances string) error {
cstr := C.CString(instances)
defer C.free(unsafe.Pointer(cstr))
ret := int(C.EnvLoadInstancesFromString(env.env, cstr, -1))
if ret == -1 {
return EnvError(env, "Unable to load instances")
}
return nil
}
// LoadInstances loads a set of instances into the CLIPS database. Equivalent to the load-instances command
func (env *Environment) LoadInstances(filename string) error {
cstr := C.CString(filename)
defer C.free(unsafe.Pointer(cstr))
ret := C.EnvBinaryLoadInstances(env.env, cstr)
if ret != -1 {
return nil
}
ret = C.EnvLoadInstances(env.env, cstr)
if ret == -1 {
return EnvError(env, "Unable to load instances")
}
return nil
}
// RestoreInstancesFromString loads a set of instances into CLIPS, bypassing message handling. Intended for use with save. Equivalent to restore-isntances command
func (env *Environment) RestoreInstancesFromString(instances string) error {
cstr := C.CString(instances)
defer C.free(unsafe.Pointer(cstr))
ret := C.EnvRestoreInstancesFromString(env.env, cstr, -1)
if ret == -1 {
return EnvError(env, "Unable to restore instances")
}
return nil
}
// RestoreInstances loads a set of instances into CLIPS, bypassing message handling. Intended for use with save. Equivalent to restore-isntances command
func (env *Environment) RestoreInstances(filename string) error {
cstr := C.CString(filename)
defer C.free(unsafe.Pointer(cstr))
ret := C.EnvRestoreInstances(env.env, cstr)
if ret == -1 {
return EnvError(env, "Unable to restore instances")
}
return nil
}
// SaveInstances saves the instances in the system to the specified file. If binary is true, instances will be aaved in binary format. Equivalent to save-instances
func (env *Environment) SaveInstances(path string, binary bool, mode SaveMode) error {
cpath := C.CString(path)
defer C.free(unsafe.Pointer(cpath))
var ret C.long
if binary {
ret = C.EnvBinarySaveInstances(env.env, cpath, mode.CVal())
} else {
ret = C.EnvSaveInstances(env.env, cpath, mode.CVal())
}
if ret == 0 {
return EnvError(env, "Unable to save instances")
}
return nil
}
// MakeInstance creates and initializes an instance of a user-defined class. Equivalent to make-instance Command must be a string in the form
// ([<instance-name>] of <class-name> <slot-override>*)
// <slot-override> :== (<slot-name> <constant>*)
func (env *Environment) MakeInstance(command string) (*Instance, error) {
ccmd := C.CString(command)
defer C.free(unsafe.Pointer(ccmd))
instptr := C.EnvMakeInstance(env.env, ccmd)
if instptr == nil {
return nil, EnvError(env, "Unable to create instance")
}
return createInstance(env, instptr), nil
}
func createInstance(env *Environment, instptr unsafe.Pointer) *Instance {
ret := &Instance{
env: env,
instptr: instptr,
}
C.EnvIncrementInstanceCount(env.env, instptr)
runtime.SetFinalizer(ret, func(*Instance) {
ret.Drop()
})
return ret
}
// Drop drops the reference to the instance in CLIPS. should be called when done with the instance
func (inst *Instance) Drop() {
if inst.instptr != nil {
C.EnvDecrementInstanceCount(inst.env.env, inst.instptr)
inst.instptr = nil
}
}
// Equal returns true if the other instance represents the same CLIPS inst as this one
func (inst *Instance) Equal(other *Instance) bool {
return inst.instptr == other.instptr
}
func (inst *Instance) String() string {
var bufsize C.ulong = 1024
buf := (*C.char)(C.malloc(C.sizeof_char * bufsize))
defer C.free(unsafe.Pointer(buf))
C.EnvGetInstancePPForm(inst.env.env, buf, bufsize-1, inst.instptr)
return C.GoString(buf)
}
// Name returns the name of this instance
func (inst *Instance) Name() InstanceName {
ret := C.EnvGetInstanceName(inst.env.env, inst.instptr)
return InstanceName(C.GoString(ret))
}
// Class returns a reference to the class of this instance
func (inst *Instance) Class() *Class {
clptr := C.EnvGetInstanceClass(inst.env.env, inst.instptr)
return createClass(inst.env, clptr)
}
// Slots returns a map of values for each slot by name
func (inst *Instance) Slots(inherited bool) map[string]interface{} {
cl := inst.Class()
slots := cl.Slots(inherited)
ret := make(map[string]interface{}, len(slots))
for _, slot := range slots {
name := slot.Name()
ret[name] = inst.slotValue(name)
}
return ret
}
// Slot returns the value of the given slot. Warning, this function bypasses message-passing
func (inst *Instance) Slot(name string) (interface{}, error) {
cl := inst.Class()
_, err := cl.Slot(name)
if err != nil {
return nil, err
}
return inst.slotValue(name), nil
}
func (inst *Instance) slotValue(name string) interface{} {
cname := C.CString(name)
defer C.free(unsafe.Pointer(cname))
data := createDataObject(inst.env)
defer data.Delete()
C.EnvDirectGetSlot(inst.env.env, inst.instptr, cname, data.byRef())
return data.Value()
}
// SetSlot sets the slot to the given value. Warning, this function bypasses message-passing
func (inst *Instance) SetSlot(name string, value interface{}) error {
typ := reflect.TypeOf(value)
if typ.Kind() == reflect.Ptr {
typ = typ.Elem()
}
if typ.Kind() == reflect.Struct {
// need to insert the struct first, then store its INSTANCE-NAME
subinst, err := inst.env.Insert("", value)
if err != nil {
return err
}
value = subinst.Name()
}
cname := C.CString(name)
defer C.free(unsafe.Pointer(cname))
data := createDataObject(inst.env)
defer data.Delete()
data.SetValue(value)
ret := C.EnvDirectPutSlot(inst.env.env, inst.instptr, cname, data.byRef())
if ret == 0 {
return EnvError(inst.env, `Unable to set slot "%s"`, name)
}
return nil
}
// Send sends a message tot his instance. Message arguments must be provided as a string
func (inst *Instance) Send(message string, arguments string) interface{} {
data := createDataObject(inst.env)
defer data.Delete()
instaddr := createDataObject(inst.env)
defer instaddr.Delete()
instaddr.SetValue(inst)
cmsg := C.CString(message)
defer C.free(unsafe.Pointer(cmsg))
var cargs *C.char
if arguments != "" {
cargs = C.CString(arguments)
defer C.free(unsafe.Pointer(cargs))
}
C.EnvSend(inst.env.env, instaddr.byRef(), cmsg, cargs, data.byRef())
return data.Value()
}
// Delete unmakes the instance within CLIPS, bypassing message passing
func (inst *Instance) Delete() error {
ret := C.EnvDeleteInstance(inst.env.env, inst.instptr)
if ret != 1 {
return EnvError(inst.env, "Unable to delete instance")
}
return nil
}
// Unmake unmakes the instance within CLIPS, using message passing
func (inst *Instance) Unmake() error {
ret := C.EnvUnmakeInstance(inst.env.env, inst.instptr)
if ret != 1 {
return EnvError(inst.env, "Unable to unmake instance")
}
return nil
}
// ExtractSlot obtains the given slot value into the user-provided object
func (inst *Instance) ExtractSlot(retval interface{}, name string) error {
cname := C.CString(name)
defer C.free(unsafe.Pointer(cname))
data := createDataObject(inst.env)
defer data.Delete()
C.EnvDirectGetSlot(inst.env.env, inst.instptr, cname, data.byRef())
return data.ExtractValue(retval, true)
}
// Extract attempts to marshall the CLIPS instance data into the user-provided or pointer
// The return value can be a struct or a map of string to another datatype. If retval points
// to a valid object, that object will be populated. If it is not, one will be created
func (inst *Instance) Extract(retval interface{}) error {
slots := inst.Slots(true)
knownInstances := make(map[InstanceName]interface{})
knownInstances[inst.Name()] = retval
return inst.env.structuredExtract(retval, slots, true, knownInstances)
}