bocl 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
bocl/Evaluator.py ADDED
@@ -0,0 +1,456 @@
1
+ from besser.BUML.notations.ocl.FactoryInstance import Factory
2
+ from besser.BUML.metamodel.ocl.rules import *
3
+ from besser.BUML.metamodel.structural.structural import Property
4
+ class Evaluator:
5
+ """The Evaluator class evaluates the OCL constraints based on the object model.
6
+
7
+ Args:
8
+
9
+ Attributes:
10
+ debug = this attribute is used for debugging purposes and with default value false
11
+ allObjSat = list of all objects that satisfy the contraint
12
+ """
13
+ def __init__(self):
14
+ self.debug = False
15
+ self.allObjSat = []
16
+
17
+
18
+ def get_value(self,name, obj):
19
+ """The get_value function to retrieve value of attribute from object.
20
+
21
+ Args:
22
+ name: name of the attribute
23
+ object: Object from object model
24
+ """
25
+ for slot in obj.slots:
26
+ if name == slot.attribute.name:
27
+ if slot.attribute.type.name == 'str':
28
+ return '"'+ slot.value.value +'"'
29
+ else:
30
+ return slot.value.value
31
+ def checkInObj(self,obj,source):
32
+ """The checkInObj function identifies the object that contains the source in slots.
33
+
34
+ Args:
35
+ obj: Object from object model
36
+ source: source of the current object in the CSTree
37
+ """
38
+ for s in obj.slots:
39
+ if s.attribute.name == source.name:
40
+ return obj
41
+ def checkInLinkEnds(self,obj, source):
42
+ """The checkInLinkEnds function identifies the source in the linkend of the object.
43
+
44
+ Args:
45
+ obj: Object from object model
46
+ source: source of the current object in the CSTree
47
+ """
48
+ toRet = []
49
+ for link in obj.links:
50
+ for connection in link.connections:
51
+ if connection.association_end.name == source.name:
52
+ toRet.append(connection.object)
53
+
54
+ return toRet
55
+ def handleForAll(self, tree, allObjs, logicalExp):
56
+ """The handleForAll function handles forAll expression from OCL MM.
57
+
58
+ Args:
59
+ tree: Tree that is constructed using OCL Parser
60
+ allObjs: all objects from object model
61
+ logicalExp: Expression to evaluate at the end to get the result
62
+ """
63
+ logicalExp[0] = logicalExp[0] +" ("
64
+ for index in range(len(allObjs)):
65
+ if len(tree.get_body)>0:
66
+ self.update_logical_exp(tree.get_body[0],logicalExp,allObjs[index])
67
+ if index < len(allObjs)-1:
68
+ logicalExp[0] = logicalExp[0] + " and "
69
+ logicalExp[0] = logicalExp[0] +" )"
70
+ pass
71
+ def handle(self, source, obj, logicalExp,rightSide = False):
72
+ """The handle function handles Property Call expression from OCL MM.
73
+
74
+ Args:
75
+ source: one child in OCL CST
76
+ obj: one object from object model
77
+ logicalExp: Expression to evaluate at the end to get the result
78
+ rightSide: to identify the side of logical expression
79
+ """
80
+
81
+ if isinstance(source, PropertyCallExpression) or isinstance(source,Property):
82
+ # if len(source.name.split('.'))==2:
83
+ allObjs = self.checkInObj(obj,source)
84
+ if allObjs is None:
85
+ allObjs = self.checkInLinkEnds(obj, source)
86
+ for index in range(len(allObjs)):
87
+ objs = allObjs[index]
88
+ if len(allObjs) > 1 or rightSide:
89
+ logicalExp[0] = logicalExp[0] + " [ "
90
+ for i in range (len(objs.slots)):
91
+ logicalExp[0] = logicalExp[0] + "\""+str(objs.slots[i].attribute.name)+ str(objs.slots[i].value.value) + "\""
92
+ if i < len(objs.slots)-1:
93
+ logicalExp[0] = logicalExp[0]+","
94
+ if not rightSide:
95
+ if len(allObjs) > 1 and index< len(allObjs)-1:
96
+ logicalExp[0] = logicalExp[0] + " ], "
97
+ else:
98
+ logicalExp[0] = logicalExp[0] + " ] "
99
+ if len(allObjs) > 1 and index< len(allObjs)-1:
100
+ logicalExp[0] = logicalExp[0] + " , "
101
+
102
+
103
+ pass
104
+ pass
105
+ def handleExcludes(self, tree, obj, logicalExp):
106
+ """The handleExcludes function handles excludes construct.
107
+
108
+ Args:
109
+ tree: Tree that is constructed using OCL Parser
110
+ obj: one object from object model
111
+ logicalExp: Expression to evaluate at the end to get the result
112
+ """
113
+ # tempLogicalExp =[""]
114
+ args = tree.arguments
115
+ self.checkAndAdd(logicalExp, " and ")
116
+ logicalExp[0] = logicalExp[0] + " [ "
117
+ self.handle(tree.source, obj, logicalExp)
118
+ logicalExp[0] = logicalExp[0] + " ] not in "
119
+ if len(args)==1: #Property
120
+ logicalExp[0] = logicalExp[0] + " [ "
121
+ self.handle(args[0],obj,logicalExp,True)
122
+ logicalExp[0] = logicalExp[0] + " ]"
123
+ pass
124
+
125
+ def handleIncludes(self, tree, obj, logicalExp):
126
+ """The handleIncludes function handles includes construct.
127
+
128
+ Args:
129
+ tree: Tree that is constructed using OCL Parser
130
+ obj: one object from object model
131
+ logicalExp: Expression to evaluate at the end to get the result
132
+ """
133
+
134
+ # tempLogicalExp =[""]
135
+ args = tree.arguments
136
+ self.checkAndAdd(logicalExp, " and ")
137
+ logicalExp[0] = logicalExp[0] + " [ "
138
+ self.handle(tree.source, obj, logicalExp)
139
+ logicalExp[0] = logicalExp[0] + " ] in "
140
+ if len(args)==1: #Property
141
+ logicalExp[0] = logicalExp[0] + " [ "
142
+ self.handle(args[0],obj,logicalExp,True)
143
+ logicalExp[0] = logicalExp[0] + " ]"
144
+ pass
145
+ def handleExists(self,tree,allObjs,logicalExp):
146
+ """The handleExists function handles exists construct.
147
+
148
+ Args:
149
+ tree: Tree that is constructed using OCL Parser
150
+ allObjs: all objects from object model
151
+ logicalExp: Expression to evaluate at the end to get the result
152
+ """
153
+ logicalExp[0] = logicalExp[0] + " ( "
154
+ temp = logicalExp[0]
155
+ for index in range(len(allObjs)):
156
+ if len(tree.get_body) > 0:
157
+ self.update_logical_exp(tree.get_body[0], logicalExp, allObjs[index])
158
+ if index < len(allObjs) - 1:
159
+ logicalExp[0] = logicalExp[0] + " or "
160
+ if temp == logicalExp[0]:
161
+ logicalExp[0] = logicalExp[0] + " False "
162
+
163
+ logicalExp[0] = logicalExp[0] + " )"
164
+ def handleCollect(self,tree,allObjs,logicalExp):
165
+ """The handleCollect function handles collect construct.
166
+
167
+ Args:
168
+ tree: Tree that is constructed using OCL Parser
169
+ allObjs: all objects from object model
170
+ logicalExp: Expression to evaluate at the end to get the result
171
+ """
172
+ self.allObjSat.append([])
173
+ for obj in allObjs:
174
+ expression = [""]
175
+ if len(tree.get_body) > 0:
176
+ self.update_logical_exp(tree.get_body[0], expression, obj)
177
+ self.preprocessLogicalExp(expression)
178
+ if eval (expression[0]) == True:
179
+ self.allObjSat[-1].append(obj)
180
+
181
+ def verifyBody (self,tree, obj,logicalExp,source,allObjs= None):
182
+ """The verifyBody function verifies the body of different constructs (e.g., forAll, exists).
183
+
184
+ Args:
185
+ tree: Tree that is constructed using OCL Parser
186
+ obj: one object from object model
187
+ logicalExp: Expression to evaluate at the end to get the result
188
+ source: source of the obj in CSTree
189
+ """
190
+ expressionType = tree.name
191
+ if allObjs is None:
192
+ allObjs = self.checkInObj(obj,source)
193
+ if allObjs is None:
194
+ allObjs = self.checkInLinkEnds(obj, source)
195
+ if source.name == "ALLInstances":
196
+ className = source.source.name
197
+ allObjs = self.getValidObjects(className, self.om)
198
+
199
+ if expressionType == "forAll":
200
+ self.handleForAll(tree, allObjs, logicalExp)
201
+ elif expressionType == "exists":
202
+ self.handleExists(tree,allObjs,logicalExp)
203
+ elif expressionType == "collect":
204
+ self.handleCollect(tree,allObjs,logicalExp)
205
+ elif expressionType == "select":
206
+ self.handleCollect(tree,allObjs,logicalExp)
207
+ pass
208
+ def getID(self,slots):
209
+ """The getID function gets the ID attribute from the Object.
210
+
211
+ Args:
212
+ slots: slots of the object
213
+ """
214
+ for s in slots:
215
+ if s.get_attribute.is_id:
216
+ return s
217
+ def addToExp(self, item, logicalExp):
218
+ """The addToExp function adds the item to logicalExpression.
219
+
220
+ Args:
221
+ item: item to add in Logical Expression
222
+ logicalExp: logical Expression
223
+ """
224
+ if logicalExp[0] == "":
225
+ logicalExp[0] = logicalExp[0] + item
226
+ else:
227
+ logicalExp[0] = logicalExp[0] + " and "+item
228
+ def handleSize(self, tree,obj,logicalExp):
229
+ """The handleSize function handles the size construct.
230
+
231
+ Args:
232
+ tree: Tree that is constructed using OCL Parser
233
+ obj: one object from object model
234
+ logicalExp: Expression to evaluate at the end to get the result
235
+ """
236
+ self.checkAndAdd(logicalExp , " and ")
237
+ logicalExp[0] = logicalExp[0] + "("
238
+ if isinstance(tree.source, Property):
239
+ allObjs = self.checkInObj(obj, tree.source)
240
+ if allObjs is None:
241
+ allObjs = self.checkInLinkEnds(obj, tree.source)
242
+ logicalExp[0] = logicalExp[0] + str(len(allObjs))
243
+ for arg in tree.arguments:
244
+
245
+ if isinstance(arg, IntegerLiteralExpression) or isinstance(arg,
246
+ RealLiteralExpression) or isinstance(arg,
247
+ BooleanLiteralExpression):
248
+ logicalExp[0] = logicalExp[0] + str(arg.value)
249
+ else:
250
+ logicalExp[0] = logicalExp[0]+str(arg)
251
+ elif isinstance(tree.source,LoopExp):
252
+ if tree.source.source is not None:
253
+ source = tree.source.source
254
+ if source.name == "ALLInstances":
255
+ className = source.source.name
256
+ allObjs = self.getValidObjects(className, self.om)
257
+ else:
258
+ allObjs = self.checkInObj(obj, source)
259
+ if allObjs is None:
260
+ allObjs = self.checkInLinkEnds(obj, source)
261
+ self.handleCollect(tree.source,allObjs,logicalExp)
262
+ if len(self.allObjSat)>0:
263
+ logicalExp[0] = logicalExp[0] +str(len(self.allObjSat[-1]))
264
+ self.allObjSat.pop()
265
+ for arg in tree.arguments:
266
+
267
+ if isinstance(arg, IntegerLiteralExpression) or isinstance(arg,
268
+ RealLiteralExpression) or isinstance(arg,
269
+ BooleanLiteralExpression):
270
+ logicalExp[0] = logicalExp[0] + str(arg.value)
271
+ else:
272
+ logicalExp[0] = logicalExp[0]+str(arg)
273
+ logicalExp[0] = logicalExp[0] + ")"
274
+
275
+ def handleIFExp(self,tree, obj, logicalExp):
276
+ """The handleIFExp function handles the If construct.
277
+
278
+ Args:
279
+ tree: Tree that is constructed using OCL Parser
280
+ obj: one object from object model
281
+ logicalExp: Expression to evaluate at the end to get the result
282
+ """
283
+
284
+ tempExp = [""]
285
+ self.update_logical_exp(tree.ifCondition, tempExp, obj)
286
+ self.preprocessLogicalExp(tempExp)
287
+ self.checkAndAdd(logicalExp," and ")
288
+ if eval(tempExp[0])==True:
289
+ self.update_logical_exp(tree.thenExpression, logicalExp, obj)
290
+ else:
291
+ self.update_logical_exp(tree.elseCondition, logicalExp, obj)
292
+
293
+ pass
294
+ def handleOCLIsTypeOf(self, tree,obj,logicalExp):
295
+ """The handleOCLIsTypeOf function handles the OCLIsTypeOf construct.
296
+
297
+ Args:
298
+ tree: Tree that is constructed using OCL Parser
299
+ obj: one object from object model
300
+ logicalExp: Expression to evaluate at the end to get the result
301
+ """
302
+ if isinstance(tree.source, Property):
303
+ sourceType = tree.source.type.name
304
+ self.checkAndAdd(logicalExp, " and ")
305
+ for index in range(len(tree.arguments)):
306
+ arg = tree.arguments[index].name
307
+ if arg == "String":
308
+ logicalExp[0] = logicalExp[0] + '\"' + sourceType + '\"' + " = \"str\""
309
+ elif arg == "Integer":
310
+ logicalExp[0] = logicalExp[0] + '\"' + sourceType + '\"' + " = \"int\""
311
+ elif arg == "Boolean":
312
+ logicalExp[0] = logicalExp[0] + '\"' + sourceType + '\"' + " = \"bool\""
313
+ elif arg == "Real":
314
+ logicalExp[0] = logicalExp[0] + '\"' + sourceType + '\"' + " = \"float\""
315
+ if index >0 and index < len(tree.arguments)-1:
316
+ self.checkAndAdd(logicalExp, "and")
317
+
318
+
319
+ def update_logical_exp(self, tree, logicalExp, obj):
320
+ """The update_logical_exp function updates the logical expression.
321
+
322
+ Args:
323
+ tree: Tree that is constructed using OCL Parser
324
+ obj: one object from object model
325
+ logicalExp: Expression to evaluate at the end to get the result
326
+ """
327
+
328
+ if isinstance(tree,PropertyCallExpression):
329
+ if tree.source == None:
330
+ pass
331
+
332
+ if isinstance(tree, LoopExp):#forAll, select,..etc
333
+ source = tree.source
334
+ if obj.classifier.name == self.roothandler.get_context_name():
335
+ self.verifyBody(tree,obj,logicalExp,source)
336
+ if isinstance(tree, IfExp):
337
+ self.handleIFExp(tree, obj, logicalExp)
338
+ if isinstance(tree, OperationCallExpression):
339
+
340
+ if tree.name == "EXCLUDES":
341
+ self.handleExcludes(tree, obj, logicalExp)
342
+ elif tree.name == "INCLUDES":
343
+ self.handleIncludes(tree, obj, logicalExp)
344
+ elif tree.name == "Size":
345
+ self.handleSize(tree,obj,logicalExp)
346
+ self.checkAndAdd(logicalExp, " and ")
347
+ elif tree.name == "OCLISTYPEOF":
348
+ self.handleOCLIsTypeOf(tree,obj,logicalExp)
349
+ elif tree.name == "ALLInstances":
350
+ pass #handled elsewhere
351
+ else:
352
+ args = tree.arguments
353
+ for arg in args:
354
+ if hasattr(arg,'name'):
355
+ if isinstance(arg,IntegerLiteralExpression) or isinstance(arg,RealLiteralExpression) or isinstance(arg,BooleanLiteralExpression):
356
+ logicalExp[0] = logicalExp[0] + str(arg.value)
357
+ elif (isinstance(arg,StringLiteralExpression)):
358
+ logicalExp[0] = logicalExp[0] + '"'+str(arg.value)+'"'
359
+ else:
360
+ logicalExp[0]= logicalExp[0] + str(self.get_value(arg.name,obj))
361
+ else:
362
+ if str(arg).lower() == "and" or str(arg).lower() =='or':
363
+ self.checkAndAdd(logicalExp[0],str(arg))
364
+ else:
365
+ logicalExp[0] = logicalExp[0] +" " +str(arg)+ " "
366
+ if tree.referredOperation is not None and( tree.referredOperation.get_infix_operator() == "and" or tree.referredOperation.get_infix_operator() == "or"):
367
+ self.checkAndAdd(logicalExp[0],tree.referredOperation.get_infix_operator())
368
+ if hasattr(tree, "source"):
369
+ if tree.source is not None:
370
+ self.update_logical_exp(tree.source, logicalExp, obj)
371
+ def checkAndAdd(self, logicalExpTemplate, toAdd):
372
+ """The checkAndAdd function checks and updates the logical expression.
373
+
374
+ Args:
375
+ toAdd: item to add in the logical expression
376
+ logicalExpTemplate: Expression to evaluate at the end to get the result
377
+ """
378
+ if len(logicalExpTemplate[0])>5:
379
+ if toAdd == " and ":
380
+ if logicalExpTemplate[0][-5:] != " and ":
381
+ logicalExpTemplate[0] = logicalExpTemplate[0]+toAdd
382
+ elif toAdd == " and ":
383
+ if len(logicalExpTemplate[0]) > 0 and len(logicalExpTemplate[0])<=5:
384
+ logicalExpTemplate[0] = logicalExpTemplate[0] + toAdd
385
+
386
+ if len(logicalExpTemplate[0]) > 4:
387
+ if toAdd == " or ":
388
+ if logicalExpTemplate[0][-4:] != " or ":
389
+ logicalExpTemplate[0] = logicalExpTemplate[0] + toAdd
390
+
391
+ def valid_object(self,contextName, object):
392
+ """the valid_object verifies if the current object is the one constraint is applied on
393
+
394
+ Args:
395
+ contextName: name of the context of OCL constraint
396
+ Object: Object from object model to check
397
+
398
+ """
399
+ idSlot = self.getID(object.slots)
400
+ if idSlot.get_attribute.name == contextName:
401
+ return True
402
+ return False
403
+ def getValidObjects(self,contextName, objs):
404
+ """the getValidObjects returns all the objects the constraint should be applied on
405
+
406
+ Args:
407
+ contextName: name of the context of OCL constraint
408
+ objs: all objects from object model
409
+
410
+ """
411
+
412
+ toRet = []
413
+ for obj in objs.instances:
414
+ # if self.valid_object(contextName,obj):
415
+ if obj.classifier.name == contextName:
416
+ toRet.append(obj)
417
+ return toRet
418
+
419
+ def preprocessLogicalExp(self,exp):
420
+ """the preprocessLogicalExp function preprocesses before evaluating to correct the syntax.
421
+
422
+ Args:
423
+ exp: logical expression to be evaluated
424
+
425
+ """
426
+ exp[0]=exp[0].replace(' = ', ' == ').replace('<>', ' != ')
427
+ if len(exp[0])>5:
428
+ if exp[0][-5:] == " and ":
429
+ exp[0] = exp[0][:-5]
430
+ if len(exp[0]) > 4:
431
+ if exp[0][-4:] == " or ":
432
+ exp[0] = exp[0][:-4]
433
+ def evaluate(self, rootHandler,objectModel):
434
+ """the evaluate function is the main function to be called for evaluating any contraint.
435
+
436
+ Args:
437
+ rootHandler: Object of class RootHandler that handles the tree
438
+ objectModel: Object Model from BUML models
439
+ """
440
+ self.om = objectModel
441
+ self.roothandler = rootHandler
442
+
443
+ objs = self.getValidObjects(self.roothandler.get_context_name(),self.om)
444
+ tree = rootHandler.get_root()
445
+ logicalExpTemplate= [""]
446
+ for i in range(len(objs)):
447
+ self.update_logical_exp(tree, logicalExpTemplate, objs[i])
448
+ if len(objs)>1 and i < len(objs) -1:
449
+ self.checkAndAdd(logicalExpTemplate," and ")
450
+
451
+
452
+ if self.debug:
453
+ print("Logical Expression")
454
+ print(logicalExpTemplate[0])
455
+ self.preprocessLogicalExp(logicalExpTemplate)
456
+ return eval(logicalExpTemplate[0])
bocl/OCLWrapper.py ADDED
@@ -0,0 +1,44 @@
1
+ from bocl.Evaluator import Evaluator
2
+ from besser.BUML.notations.ocl.BOCLLexer import BOCLLexer
3
+ from besser.BUML.notations.ocl.BOCLParser import BOCLParser
4
+ from besser.BUML.notations.ocl.BOCLListener import BOCLListener
5
+ from besser.BUML.notations.ocl.RootHandler import Root_Handler
6
+ from antlr4 import *
7
+
8
+ class OCLWrapper:
9
+ """The OCLWrapper class is the wrapper around the evaluator class to prepare the construct needed by evaluator.
10
+
11
+ Args:
12
+ dm: Domain model in BUML
13
+ om: object model in BUML
14
+ Attributes:
15
+ dm: Domain model in BUML
16
+ om: object model in BUML
17
+ """
18
+ def __init__(self,dm,om):
19
+ self.dm = dm
20
+ self.om = om
21
+
22
+
23
+ def evaluate(self,ocl):
24
+ """the evaluate function takes the OCL constraint and evaluate using evaluator
25
+ Args:
26
+ ocl: Object of OCL class that constaints the OCL expression and context class
27
+ """
28
+
29
+ # self.preprocess(ocl.expression)
30
+ input_stream = InputStream(ocl.expression)
31
+ rootHandler = Root_Handler(ocl,self.dm,self.om)
32
+ lexer = BOCLLexer(input_stream)
33
+ stream = CommonTokenStream(lexer)
34
+ parser = BOCLParser(stream)
35
+ tree = parser.oclFile()
36
+ listener = BOCLListener(rootHandler)
37
+ walker = ParseTreeWalker()
38
+ walker.walk(listener,tree)
39
+
40
+ eval = Evaluator()
41
+
42
+ return eval.evaluate(rootHandler, self.om)
43
+
44
+ pass
bocl/__init__.py ADDED
File without changes
@@ -0,0 +1,90 @@
1
+ Metadata-Version: 2.1
2
+ Name: bocl
3
+ Version: 0.1.0
4
+ Summary: BOCL
5
+ Author: Luxembourg Institute of Science and Technology
6
+ License: MIT
7
+ Project-URL: Documentation, https://bocl-interpreter.readthedocs.io/en/latest/
8
+ Project-URL: Source Code, https://github.com/BESSER-PEARL/BOCL-Interpreter
9
+ Project-URL: Bug Tracker, https://github.com/BESSER-PEARL/BOCL-Interpreter/issues
10
+ Keywords: uml,code generation,python
11
+ Requires-Python: >=3.9
12
+ Description-Content-Type: text/markdown
13
+ Requires-Dist: Arpeggio ==2.0.2
14
+ Requires-Dist: colorama ==0.4.6
15
+ Requires-Dist: iniconfig ==2.0.0
16
+ Requires-Dist: packaging
17
+ Requires-Dist: pluggy ==1.2.0
18
+ Requires-Dist: pytest ==7.4.0
19
+ Requires-Dist: setuptools ==68.2.2
20
+ Requires-Dist: textX ==3.1.1
21
+ Requires-Dist: jinja2 ==3.1.2
22
+ Requires-Dist: antlr4-python3-runtime ==4.13.1
23
+ Requires-Dist: requests ==2.31.0
24
+ Requires-Dist: fastapi ==0.110.0
25
+ Requires-Dist: pydantic ==2.6.3
26
+ Requires-Dist: uvicorn ==0.28.0
27
+ Requires-Dist: besser
28
+
29
+ # BESSER Object Constraint Language Interpreter
30
+
31
+ **BOCL** is a tool designed to parse and evaluate the OCL constraints designed on **BESSER** models.
32
+ It is a python based implementation, that utilises parser and listener generated with the ANTLR4 grammer.
33
+
34
+ **BESSER** is a low-modeling low-code open-source platform built on top
35
+ of our Python-based personal interpretation of a "Universal Modeling Language"
36
+
37
+ ## Basic Installation
38
+ We have tested BOCL with python 3.9+. We recommend creating a virtual environment (e.g. `venv <https://docs.python.org/3/tutorial/venv.html>`_,
39
+ `conda <https://docs.conda.io/en/latest/>`_).
40
+
41
+ Dependencies
42
+ ************
43
+ BOCL depends on BESSER for parsing the OCL constraints and providing a concrete syntax tree (CST) to evaluation. The latest stable version of BESSER is available in the Python Package Index (PyPi) and can be installed using
44
+
45
+
46
+ $ pip install besser
47
+
48
+
49
+ # BOCL Installation
50
+
51
+
52
+ Stable version of BOCL interpreter can be installed via
53
+
54
+ $ pip install bocl
55
+
56
+
57
+
58
+ ## Building From Source
59
+
60
+ To obtain the full code, including examples and tests, you can clone the git repository.
61
+
62
+ $ git clone https://github.com/BESSER-PEARL/BOCL-Interpreter
63
+ $ cd BOCL-Interpreter
64
+
65
+ To install the reqs please run the following commands in virtual environment (recommended)
66
+
67
+ $ pip install -r requirements.txt
68
+
69
+ ## Contributing
70
+
71
+ We encourage contributions from the community and any comment is welcome!
72
+
73
+ If you are interested in contributing to this project, please read the [CONTRIBUTING.md](CONTRIBUTING.md) file.
74
+
75
+ ## Code of Conduct
76
+
77
+ At BESSER, our commitment is centered on establishing and maintaining development environments that are welcoming, inclusive, safe and free from all forms of harassment. All participants are expected to voluntarily respect and support our [Code of Conduct](CODE_OF_CONDUCT.md).
78
+
79
+ ## Governance
80
+
81
+ The development of this project follows the governance rules described in the [GOVERNANCE.md](GOVERNANCE.md) document.
82
+
83
+ ## Contact
84
+ You can reach us at: [info@besser-pearl.org](mailto:info@besser-pearl-org)
85
+
86
+ Website: https://besser-pearl.github.io/teampage/
87
+
88
+ ## License
89
+
90
+ This project is licensed under the [MIT](https://mit-license.org/) license.
@@ -0,0 +1,10 @@
1
+ bocl/Evaluator.py,sha256=RtkRAiQNBxf1yvpLEoPWPGrNJ8j3rxcPPjzQ2i3cnFE,20231
2
+ bocl/OCLWrapper.py,sha256=wu2YsEca6LlmClXVaeBeS4oIElBFALROSyTXrFtjz0o,1462
3
+ bocl/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ models/library_object.py,sha256=kuKXMhhuWAiNqHxEXWMnJhMSJTwt2bT7rVDiG8FRCTU,10452
6
+ models/researcher_object.py,sha256=cDpJdGu0hzmrhdZUF5dfO1P7UdjVNaBOiwilMVH8Rvk,8842
7
+ bocl-0.1.0.dist-info/METADATA,sha256=3SAWHY0HP9uBgWPIMuf7CJK7xvBJFC7RTumBoVDZP68,3242
8
+ bocl-0.1.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
9
+ bocl-0.1.0.dist-info/top_level.txt,sha256=d5ZFSziu4ds27nZoZcgWLHKMJsID-7w5M7grHxSYjoE,12
10
+ bocl-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: bdist_wheel (0.43.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ bocl
2
+ models
models/__init__.py ADDED
File without changes
@@ -0,0 +1,161 @@
1
+
2
+ from besser.BUML.metamodel.structural import DomainModel, Class, Property, \
3
+ PrimitiveDataType, Multiplicity, BinaryAssociation,Constraint
4
+ from besser.BUML.metamodel.object import *
5
+ import datetime
6
+
7
+ #############################################
8
+ # Library - structural model definition #
9
+ #############################################
10
+
11
+ # Primitive DataTypes
12
+ t_int: PrimitiveDataType = PrimitiveDataType("int")
13
+ t_str: PrimitiveDataType = PrimitiveDataType("str")
14
+ t_date: PrimitiveDataType = PrimitiveDataType("date")
15
+
16
+ # Library attributes definition
17
+ library_name: Property = Property(name="name", type=t_str)
18
+ address: Property = Property(name="address", type=t_str)
19
+ # Library class definition
20
+ library: Class = Class (name="Library", attributes={library_name, address})
21
+
22
+ # Book attributes definition
23
+ title: Property = Property(name="title", type=t_str)
24
+ pages: Property = Property(name="pages", type=t_int)
25
+ release: Property = Property(name="release", type=t_date)
26
+ # Book class definition
27
+ book: Class = Class (name="Book", attributes={title, pages, release})
28
+
29
+ # Author attributes definition
30
+ author_name: Property = Property(name="name", type=t_str)
31
+ email: Property = Property(name="email", type=t_str)
32
+ # Author class definition
33
+ author: Class = Class (name="Author", attributes={author_name, email})
34
+
35
+ # Library-Book association definition
36
+ located_in: Property = Property(name="locatedIn",type=library, multiplicity=Multiplicity(1, 1))
37
+ has: Property = Property(name="has", type=book, multiplicity=Multiplicity(0, "*"))
38
+ lib_book_association: BinaryAssociation = BinaryAssociation(name="lib_book_assoc", ends={located_in, has})
39
+
40
+ # Book-Author association definition
41
+ publishes: Property = Property(name="publishes", type=book, multiplicity=Multiplicity(0, "*"))
42
+ writed_by: Property = Property(name="writedBy", type=author, multiplicity=Multiplicity(1, "*"))
43
+ book_author_association: BinaryAssociation = BinaryAssociation(name="book_author_assoc", ends={writed_by, publishes})
44
+
45
+ constraintPageNumber: Constraint = Constraint(name = "libraryPageNumber",context=library,expression="context Library inv inv1: self.has ->forAll(b:Book|b.pages>0)",language="OCL")
46
+
47
+ constraintBookPageNumber: Constraint = Constraint(name = "BookPageNumber",context=book,expression="context Book inv inv2: self.pages>0",language="OCL")
48
+
49
+ constraintTitleIncorrect: Constraint = Constraint(name = "BookTitleInc",context=book,expression="context Book inv inv2: self.title= 'NI')",language="OCL")
50
+
51
+ constraintTitleCorrect: Constraint = Constraint(name = "BookTitle",context=book,expression="context Book inv inv2: self.title <> 'NI'",language="OCL")
52
+
53
+ constraintLibraryExists: Constraint = Constraint(name = "LibarayExistsConst",context=library,expression="context Library inv inv3: self.has->exists( i_book : Book | i_book.pages <= 110 )",language="OCL")
54
+
55
+ # constraintLibraryExclude: Constraint = Constraint(name = "LibarayExclude",context=library,expression="context Library inv inv3: self.has->excludes()",language="OCL")
56
+ constraintLibrarySize: Constraint = Constraint(name = "LibaraySize",context=library,expression="context Library inv inv3: self.has->size()>1",language="OCL")
57
+
58
+ constraintLibraryCollect: Constraint = Constraint(name = "LibaryCollect",context=library,
59
+ expression="context Library inv inv3: self.has->collect(i_book : Book | i_book.pages <= 110)"
60
+ "->size()>0",language="OCL")
61
+
62
+ constraintLibraryIf: Constraint = Constraint(name ="LibaryIf", context=library,
63
+ expression="context Library inv inv3: if self.name <> 'NI' then self.has->exists"
64
+ "( i_book : Book | i_book.pages <= 110 ) else self.has->forAll(b:Book|b.pages>0)"
65
+ " endif", language="OCL")
66
+
67
+ constraintLibraryElse: Constraint = Constraint(name ="LibaryElse", context=library,
68
+ expression="context Library inv inv3: if self.name = 'NI' then self.has->exists"
69
+ "( i_book : Book | i_book.pages <= 110 ) else self.has->forAll(b:Book|b.pages>0)"
70
+ " endif", language="OCL")
71
+
72
+ constraintLibraryElseFalse: Constraint = Constraint(name ="LibaryElseFalse", context=library, expression="context Library inv inv3: if self.name = 'NI' then self.has->exists( i_book : Book | i_book.pages <= 110 ) else self.has->forAll(b:Book|b.pages<0) endif", language="OCL")
73
+
74
+
75
+ constraintNameOCLIsTypeOf: Constraint = Constraint(name ="BookIsTypeOfStr", context=book, expression="context Book inv inv3: self.title.oclIsTypeOf(String)", language="OCL")
76
+
77
+ constraintNameOCLIsTypeOfInt: Constraint = Constraint(name ="BookIsTypeOfInt", context=book, expression="context Book inv inv3: self.pages.oclIsTypeOf(Integer)", language="OCL")
78
+
79
+ constraintNameOCLIsTypeOfIntFalse: Constraint = Constraint(name ="BookIsTypeOfIntFalse", context=book, expression="context Book inv inv3: self.pages.oclIsTypeOf(String)", language="OCL")
80
+ constraintifElseSize: Constraint = Constraint(name ="constraintifElseSize", context=library, expression="context Library inv inv3: if self.name <> 'NI' then self.has->exists( i_book : Book | i_book.pages <= 110 )->size()<3 else self.has->forAll(b:Book|b.pages>0) endif", language="OCL")
81
+
82
+
83
+ # Domain model definition
84
+ library_model : DomainModel = DomainModel(name="Library model", types={library, book, author},
85
+ associations={lib_book_association, book_author_association},
86
+ constraints={
87
+ constraintBookPageNumber,
88
+ # constraintTitleIncorrect,
89
+ constraintPageNumber,
90
+ constraintTitleCorrect,
91
+ constraintLibraryExists,
92
+ constraintLibrarySize,
93
+ constraintLibraryCollect,
94
+ constraintLibraryIf,
95
+ constraintLibraryElse,
96
+ # constraintLibraryElseFalse,
97
+ constraintNameOCLIsTypeOf,
98
+ constraintNameOCLIsTypeOfInt,
99
+ # constraintNameOCLIsTypeOfIntFalse
100
+ }
101
+
102
+ # constraints={constraintLibrarySize}
103
+ )
104
+
105
+
106
+ #########################################
107
+ # Library - object model definition #
108
+ #########################################
109
+
110
+
111
+ # Library object attributes
112
+ library_obj_name: AttributeLink = AttributeLink(attribute=library_name, value=DataValue(classifier=t_str, value="Library test"))
113
+ library_obj_address: AttributeLink = AttributeLink(attribute=address, value=DataValue(classifier=t_str, value="street 123"))
114
+ # Library object
115
+ library_obj: Object = Object(name="Library Object", classifier=library, slots=[library_obj_name, library_obj_address])
116
+
117
+ # Book object attributes
118
+ book_obj_name: AttributeLink = AttributeLink(attribute=title, value=DataValue(classifier=t_str, value="Book tittle"))
119
+ book_obj_pages: AttributeLink = AttributeLink(attribute=pages, value=DataValue(classifier=t_int, value=100))
120
+ book_obj_release: AttributeLink = AttributeLink(attribute=release, value=DataValue(classifier=t_date, value=datetime.datetime(2020, 3, 15)))
121
+ # Book object
122
+ book_obj: Object = Object(name="Book Object", classifier=book, slots=[book_obj_name, book_obj_pages])
123
+
124
+ # Book_2 object attributes
125
+
126
+ book_obj_name_2: AttributeLink = AttributeLink(attribute=title, value=DataValue(classifier=t_str, value="Book tittle_2"))
127
+ book_obj_pages_2: AttributeLink = AttributeLink(attribute=pages, value=DataValue(classifier=t_int, value=400))
128
+ book_obj_release_2: AttributeLink = AttributeLink(attribute=release, value=DataValue(classifier=t_date, value=datetime.datetime(2024, 3, 15)))
129
+ # Book object
130
+ book_obj_2: Object = Object(name="Book 2 Object", classifier=book, slots=[book_obj_name_2, book_obj_pages_2])
131
+
132
+ # Author object attributes
133
+ author_obj_name: AttributeLink = AttributeLink(attribute=author_name, value=DataValue(classifier=t_str, value="John Doe"))
134
+ author_obj_email: AttributeLink = AttributeLink(attribute=email, value=DataValue(classifier=t_str, value="john@doe.com"))
135
+ # Author object
136
+ author_obj: Object = Object(name="Author Object", classifier=author, slots=[author_obj_name, author_obj_email])
137
+
138
+ # Book object and Author object link
139
+ book_link_end1: LinkEnd = LinkEnd(name="book_end1", association_end=publishes, object=book_obj)
140
+ author_link_end: LinkEnd = LinkEnd(name="author_end", association_end=writed_by, object=author_obj)
141
+ author_book_link: Link = Link(name="author_book_link", association=book_author_association, connections=[book_link_end1,author_link_end])
142
+
143
+ # Book Library and Book object link
144
+ book_link_end2: LinkEnd = LinkEnd(name="book_end2", association_end=has, object=book_obj)
145
+ library_link_end: LinkEnd = LinkEnd(name="library_end", association_end=located_in, object=library_obj)
146
+ library_book_link: Link = Link(name="library_book_link", association=book_author_association, connections=[book_link_end2,library_link_end])
147
+
148
+ # Book object and Author object link
149
+ book_link_end2: LinkEnd = LinkEnd(name="book_end3", association_end=publishes, object=book_obj_2)
150
+ author_link_end2: LinkEnd = LinkEnd(name="author_end2", association_end=writed_by, object=author_obj)
151
+ author_book_link2: Link = Link(name="author_book_link2", association=book_author_association, connections=[book_link_end2,author_link_end2])
152
+
153
+ # Book Library and Book object link
154
+ book_link_end3: LinkEnd = LinkEnd(name="book_end4", association_end=has, object=book_obj_2)
155
+ library_link_end3: LinkEnd = LinkEnd(name="library_end3", association_end=located_in, object=library_obj)
156
+ library_book_link3: Link = Link(name="library_book_link3", association=book_author_association, connections=[book_link_end3,library_link_end3])
157
+
158
+
159
+
160
+ # Object model definition
161
+ object_model: ObjectModel = ObjectModel(name="Object model", instances={library_obj, author_obj, book_obj,book_obj_2}, links={author_book_link, library_book_link,author_book_link2,library_book_link3})
@@ -0,0 +1,136 @@
1
+ from besser.BUML.metamodel.structural import *
2
+ from besser.BUML.metamodel.object import *
3
+
4
+ #########################################
5
+ # Structural Model #
6
+ #########################################
7
+
8
+ # Primitive DataTypes
9
+ t_int: PrimitiveDataType = PrimitiveDataType("int")
10
+ t_str: PrimitiveDataType = PrimitiveDataType("str")
11
+ t_bool: PrimitiveDataType = PrimitiveDataType("bool")
12
+
13
+ # Paper class
14
+ tittle: Property = Property(name="tittle", type=t_str)
15
+ wordCount: Property = Property(name="wordcount", type=t_int)
16
+ studentPaper: Property = Property(name="studentPaper", type=t_bool)
17
+ paper: Class = Class(name="Paper", attributes={tittle, wordCount, studentPaper})
18
+
19
+ # Researcher class
20
+ name: Property = Property(name="name", type=t_str)
21
+ isStudent: Property = Property(name="isStudent", type=t_bool)
22
+ researcher: Class = Class(name="Researcher", attributes={name, isStudent})
23
+
24
+ # paper-researcher association
25
+ manuscript: Property = Property(name="manuscript", type=paper, multiplicity=Multiplicity(1, 1))
26
+ author: Property = Property(name="author", type=researcher, multiplicity=Multiplicity(1, 2))
27
+ writes: BinaryAssociation = BinaryAssociation(name="writes", ends={manuscript, author})
28
+
29
+ # researcher-paper association
30
+ submission: Property = Property(name="submission", type=paper, multiplicity=Multiplicity(1, 1))
31
+ referee: Property = Property(name="referee", type=researcher, multiplicity=Multiplicity(3, 3))
32
+ reviews: BinaryAssociation = BinaryAssociation(name="reviews", ends={submission, referee})
33
+
34
+ constraintResearcher: Constraint = Constraint(name="NoSelfReviews", context=researcher,
35
+ expression="context Researcher inv NoSelfReviews: self.submission->excludes(self.manuscript)", language="OCL")
36
+
37
+ constraintPaper: Constraint = Constraint(name="PaperLength", context=paper,
38
+ expression="context Paper inv :self.wordcount < 10000", language="OCL")
39
+
40
+ constraintAuthorsOfStudentPaper: Constraint = Constraint(name="AuthorsOfStudentPaper", context=paper,
41
+ expression="context Paper inv AuthorsOfStudentPaper: self.author->exists(x:Researcher | x.isStudent =True)", language="OCL")
42
+
43
+ constraintNoStudentReviewers: Constraint = Constraint(name="NoStudentReviewers", context=paper,
44
+ expression="context Paper inv NoStudentReviewers:self.referee->forAll(r:Researcher | r.isStudent =False)", language="OCL")
45
+
46
+ constraintLimitsOnStudentPapers: Constraint = Constraint(name="LimitsOnStudentPapers", context=paper,
47
+ expression="context Paper inv LimitsOnStudentPapers: Paper::allInstances()->exists(p:Paper | p.studentPaper = True) and Paper::allInstances()->select(p:Paper | p.studentPaper=True) ->size() < 5", language="OCL")
48
+
49
+ constraintPaperTitle: Constraint = Constraint(name="constraintPaperTitle", context=paper,
50
+ expression="context Paper inv title: self.tittle<> 'test'", language="OCL")
51
+ constraintResearcherName:Constraint = Constraint(name="consResearcherName", context=researcher,
52
+ expression="context Researcher inv title: self.name<> 'test'", language="OCL")
53
+
54
+ constraintPaperWordCountgreater: Constraint = Constraint(name="PaperLength100", context=paper,
55
+ expression="context Paper inv :self.wordcount > 1000", language="OCL")
56
+
57
+
58
+ # Structural model
59
+ domain_model: DomainModel = DomainModel(name="my_model", types={paper, researcher}, associations={writes, reviews}, constraints = {
60
+ constraintResearcher,
61
+ constraintPaper,
62
+ constraintAuthorsOfStudentPaper,
63
+ constraintNoStudentReviewers,
64
+ constraintLimitsOnStudentPapers,
65
+ constraintPaperTitle,
66
+ constraintResearcherName,
67
+ constraintPaperWordCountgreater
68
+ })
69
+
70
+
71
+ ###################################
72
+ # Object model #
73
+ ###################################
74
+
75
+ # paper object
76
+ paper_name: AttributeLink = AttributeLink(value=DataValue(value="besser lowcode platform", classifier=None), attribute=tittle)
77
+ paper_words: AttributeLink = AttributeLink(value=DataValue(value=5000, classifier=None), attribute=wordCount)
78
+ paper_student_paper: AttributeLink = AttributeLink(value=DataValue(value=True, classifier=None), attribute=studentPaper)
79
+ paper_obj: Object = Object(name="besser paper", classifier=paper, slots=[paper_name, paper_words, paper_student_paper])
80
+
81
+ # researcher 1 object
82
+ r1_name: AttributeLink = AttributeLink(value=DataValue(value="Marc", classifier=None), attribute=name)
83
+ r1_is_student: AttributeLink = AttributeLink(value=DataValue(value=True, classifier=None), attribute=isStudent)
84
+ researcher_1: Object = Object(name="Marc researcher", classifier=researcher, slots=[r1_name, r1_is_student])
85
+
86
+ # researcher 2 object
87
+ r2_name: AttributeLink = AttributeLink(value=DataValue(value="James", classifier=None), attribute=name)
88
+ r2_is_student: AttributeLink = AttributeLink(value=DataValue(value=True, classifier=None), attribute=isStudent)
89
+ researcher_2: Object = Object(name="James researcher", classifier=researcher, slots=[r2_name, r2_is_student])
90
+
91
+ # researcher 3 object
92
+ r3_name: AttributeLink = AttributeLink(value=DataValue(value="Adam", classifier=None), attribute=name)
93
+ r3_is_student: AttributeLink = AttributeLink(value=DataValue(value=False, classifier=None), attribute=isStudent)
94
+ researcher_3: Object = Object(name="Adam researcher", classifier=researcher, slots=[r3_name, r3_is_student])
95
+
96
+ # researcher 4 object
97
+ r4_name: AttributeLink = AttributeLink(value=DataValue(value="Lola", classifier=None), attribute=name)
98
+ r4_is_student: AttributeLink = AttributeLink(value=DataValue(value=False, classifier=None), attribute=isStudent)
99
+ researcher_4: Object = Object(name="Lola researcher", classifier=researcher, slots=[r4_name, r4_is_student])
100
+
101
+ # researcher 5 object
102
+ r5_name: AttributeLink = AttributeLink(value=DataValue(value="Sarah", classifier=None), attribute=name)
103
+ r5_is_student: AttributeLink = AttributeLink(value=DataValue(value=False, classifier=None), attribute=isStudent)
104
+ researcher_5: Object = Object(name="Sarah researcher", classifier=researcher, slots=[r5_name, r5_is_student])
105
+
106
+ # Links definition
107
+ writes_1: Link = Link(name="writes", association=writes, connections=[
108
+ LinkEnd(name="manuscript", association_end=manuscript, object=paper_obj),
109
+ LinkEnd(name="author", association_end=author, object=researcher_1)
110
+ ])
111
+
112
+ writes_2: Link = Link(name="writes", association=writes, connections=[
113
+ LinkEnd(name="researcher", association_end=manuscript, object=paper_obj),
114
+ LinkEnd(name="author", association_end=author, object=researcher_2)
115
+ ])
116
+
117
+ reviews_1: Link = Link(name="writes", association=writes, connections=[
118
+ LinkEnd(name="submission", association_end=submission, object=paper_obj),
119
+ LinkEnd(name="referee", association_end=referee, object=researcher_3)
120
+ ])
121
+
122
+ reviews_2: Link = Link(name="writes", association=writes, connections=[
123
+ LinkEnd(name="submission", association_end=submission, object=paper_obj),
124
+ LinkEnd(name="referee", association_end=referee, object=researcher_4)
125
+ ])
126
+
127
+ reviews_3: Link = Link(name="writes", association=writes, connections=[
128
+ LinkEnd(name="submission", association_end=submission, object=paper_obj),
129
+ LinkEnd(name="referee", association_end=referee, object=researcher_5)
130
+ ])
131
+
132
+
133
+
134
+ # object model
135
+ object_model: ObjectModel = ObjectModel(name="Object_model", instances={paper_obj, researcher_1, researcher_2, researcher_3, researcher_4, researcher_5},
136
+ links={writes_1, writes_2, reviews_1, reviews_2, reviews_3})