easycoder 251215.2__py2.py3-none-any.whl → 260111.1__py2.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.
Files changed (48) hide show
  1. easycoder/__init__.py +4 -3
  2. easycoder/debugger/ec_dbg_value_display copy.py +1 -1
  3. easycoder/debugger/ec_dbg_value_display.py +12 -11
  4. easycoder/debugger/ec_dbg_watchlist.py +146 -12
  5. easycoder/debugger/ec_debug.py +85 -8
  6. easycoder/ec_classes.py +228 -25
  7. easycoder/ec_compiler.py +29 -8
  8. easycoder/ec_core.py +364 -242
  9. easycoder/ec_gclasses.py +20 -9
  10. easycoder/ec_graphics.py +42 -26
  11. easycoder/ec_handler.py +4 -3
  12. easycoder/ec_mqtt.py +248 -0
  13. easycoder/ec_program.py +63 -28
  14. easycoder/ec_psutil.py +1 -1
  15. easycoder/ec_value.py +57 -36
  16. easycoder/pre/README.md +3 -0
  17. easycoder/pre/__init__.py +17 -0
  18. easycoder/pre/debugger/__init__.py +5 -0
  19. easycoder/pre/debugger/ec_dbg_value_display copy.py +195 -0
  20. easycoder/pre/debugger/ec_dbg_value_display.py +24 -0
  21. easycoder/pre/debugger/ec_dbg_watch_list copy.py +219 -0
  22. easycoder/pre/debugger/ec_dbg_watchlist.py +293 -0
  23. easycoder/pre/debugger/ec_debug.py +1014 -0
  24. easycoder/pre/ec_border.py +67 -0
  25. easycoder/pre/ec_classes.py +470 -0
  26. easycoder/pre/ec_compiler.py +291 -0
  27. easycoder/pre/ec_condition.py +27 -0
  28. easycoder/pre/ec_core.py +2772 -0
  29. easycoder/pre/ec_gclasses.py +230 -0
  30. easycoder/pre/ec_graphics.py +1682 -0
  31. easycoder/pre/ec_handler.py +79 -0
  32. easycoder/pre/ec_keyboard.py +439 -0
  33. easycoder/pre/ec_program.py +557 -0
  34. easycoder/pre/ec_psutil.py +48 -0
  35. easycoder/pre/ec_timestamp.py +11 -0
  36. easycoder/pre/ec_value.py +124 -0
  37. easycoder/pre/icons/close.png +0 -0
  38. easycoder/pre/icons/exit.png +0 -0
  39. easycoder/pre/icons/run.png +0 -0
  40. easycoder/pre/icons/step.png +0 -0
  41. easycoder/pre/icons/stop.png +0 -0
  42. easycoder/pre/icons/tick.png +0 -0
  43. {easycoder-251215.2.dist-info → easycoder-260111.1.dist-info}/METADATA +1 -1
  44. easycoder-260111.1.dist-info/RECORD +59 -0
  45. easycoder-251215.2.dist-info/RECORD +0 -31
  46. {easycoder-251215.2.dist-info → easycoder-260111.1.dist-info}/WHEEL +0 -0
  47. {easycoder-251215.2.dist-info → easycoder-260111.1.dist-info}/entry_points.txt +0 -0
  48. {easycoder-251215.2.dist-info → easycoder-260111.1.dist-info}/licenses/LICENSE +0 -0
easycoder/ec_core.py CHANGED
@@ -1,5 +1,5 @@
1
1
  import json, math, hashlib, threading, os, subprocess, time
2
- import base64, binascii, random, requests, paramiko
2
+ import base64, binascii, random, requests, paramiko, uuid
3
3
  from copy import deepcopy
4
4
  from datetime import datetime
5
5
  from .ec_classes import (
@@ -11,10 +11,13 @@ from .ec_classes import (
11
11
  NoValueRuntimeError,
12
12
  ECObject,
13
13
  ECVariable,
14
+ ECDictionary,
15
+ ECList,
14
16
  ECFile,
15
17
  ECStack,
16
18
  ECSSH,
17
- ECValue
19
+ ECValue,
20
+ ECModule
18
21
  )
19
22
 
20
23
  from .ec_handler import Handler
@@ -67,7 +70,7 @@ class Core(Handler):
67
70
  if not isinstance(self.getObject(record), ECVariable): return False
68
71
  # If 'giving' comes next, this variable is the second value
69
72
  if self.peek() == 'giving':
70
- v2 = ECValue(domain=self.getName(), type='symbol', content=record['name'])
73
+ v2 = ECValue(type='symbol', content=record['name'])
71
74
  command['value2'] = v2
72
75
  self.nextToken()
73
76
  # Now get the target variable
@@ -104,7 +107,7 @@ class Core(Handler):
104
107
  # If value2 exists, we are adding two values and storing the result in target
105
108
  if value2 != None:
106
109
  # add X to Y giving Z
107
- targetValue = ECValue(domain=self.getName(), type='int', content=int(value1) + int(value2))
110
+ targetValue = ECValue(type=int, content=int(value1) + int(value2))
108
111
  else:
109
112
  # add X to Y
110
113
  targetValue = self.getSymbolValue(target)
@@ -112,14 +115,14 @@ class Core(Handler):
112
115
  self.putSymbolValue(target, targetValue)
113
116
  return self.nextPC()
114
117
 
115
- # Append a value to an array
116
- # append {value} to {array}
118
+ # Append a value to an list
119
+ # append {value} to {list}
117
120
  def k_append(self, command):
118
121
  command['value'] = self.nextValue()
119
122
  if self.nextIs('to'):
120
123
  if self.nextIsSymbol():
121
124
  record = self.getSymbolRecord()
122
- self.program.checkObjectType(self.getObject(record), ECVariable)
125
+ self.program.checkObjectType(self.getObject(record), ECList)
123
126
  command['target'] = record['name']
124
127
  self.add(command)
125
128
  return True
@@ -127,13 +130,8 @@ class Core(Handler):
127
130
 
128
131
  def r_append(self, command):
129
132
  value = self.textify(command['value'])
130
- target = self.getVariable(command['target'])
131
- content = target['object'].getContent()
132
- items = [] if content == None else content
133
- if not type(items) == list:
134
- RuntimeError(self.program, f'{command["target"]} is not a JSON list')
135
- items.append(value)
136
- self.putSymbolValue(target, items)
133
+ target = self.getObject(self.getVariable(command['target']))
134
+ target.append(value)
137
135
  return self.nextPC()
138
136
 
139
137
  #assert {condition} [with {message}]
@@ -193,7 +191,7 @@ class Core(Handler):
193
191
  if target['keyword'] == 'ssh':
194
192
  target['ssh'] = None
195
193
  else:
196
- self.putSymbolValue(target, ECValue(domain=self.getName(), type='boolean', content=False))
194
+ self.putSymbolValue(target, ECValue(type=bool, content=False))
197
195
  return self.nextPC()
198
196
 
199
197
  # Close a file
@@ -201,10 +199,10 @@ class Core(Handler):
201
199
  def k_close(self, command):
202
200
  if self.nextIsSymbol():
203
201
  fileRecord = self.getSymbolRecord()
204
- if fileRecord['keyword'] == 'file':
205
- command['file'] = fileRecord['name']
206
- self.add(command)
207
- return True
202
+ self.checkObjectType
203
+ command['file'] = fileRecord['name']
204
+ self.add(command)
205
+ return True
208
206
  return False
209
207
 
210
208
  def r_close(self, command):
@@ -212,6 +210,34 @@ class Core(Handler):
212
210
  fileRecord['file'].close()
213
211
  return self.nextPC()
214
212
 
213
+ # copy {variable} to {variable}
214
+ # copy {dictionary} to {dictionary}
215
+ # copy {list} to {list}
216
+ def k_copy(self, command):
217
+ if self.nextIsSymbol():
218
+ record = self.getSymbolRecord()
219
+ sourceObject = self.getObject(record)
220
+ if self.isObjectType(sourceObject, (ECVariable, ECDictionary, ECList)):
221
+ command['source'] = record['name']
222
+ self.skip('to')
223
+ if self.nextIsSymbol():
224
+ record = self.getSymbolRecord()
225
+ targetObject = self.getObject(record)
226
+ # Check that the types match
227
+ if type(sourceObject) != type(targetObject):
228
+ raise FatalError(self.compiler, 'Cannot copy - type mismatch')
229
+ command['target'] = record['name']
230
+ self.add(command)
231
+ return True
232
+ return False
233
+
234
+ def r_copy(self, command):
235
+ sourceRecord = self.getVariable(command['source'])
236
+ targetRecord = self.getVariable(command['target'])
237
+ # Copy the value (type already checked at compile time)
238
+ self.putSymbolValue(targetRecord, self.textify(sourceRecord))
239
+ return self.nextPC()
240
+
215
241
  # Create directory
216
242
  # create directory {name}
217
243
  def k_create(self, command):
@@ -236,7 +262,7 @@ class Core(Handler):
236
262
  self.compiler.debugCompile = True
237
263
  self.nextToken()
238
264
  return True
239
- elif token in ['step', 'stop', 'breakpoint', 'program', 'custom']:
265
+ elif token in ['step', 'stop', 'skip', 'breakpoint', 'program', 'custom']:
240
266
  command['mode'] = token
241
267
  self.nextToken()
242
268
  elif token == 'stack':
@@ -262,6 +288,8 @@ class Core(Handler):
262
288
  self.program.debugStep = True
263
289
  elif command['mode'] == 'stop':
264
290
  self.program.debugStep = False
291
+ elif command['mode'] == 'skip':
292
+ self.program.debugSkip = True
265
293
  elif command['mode'] == 'breakpoint':
266
294
  self.program.breakpoint = True
267
295
  elif command['mode'] == 'program':
@@ -296,8 +324,7 @@ class Core(Handler):
296
324
 
297
325
  # Delete a file or a property
298
326
  # delete file {filename}
299
- # delete property {value} of {variable}
300
- # delete element {name} of {variable}
327
+ # delete entry/item/property/element {name/number} of {variable}
301
328
  def k_delete(self, command):
302
329
  token = self.nextToken( )
303
330
  command['type'] = token
@@ -305,19 +332,21 @@ class Core(Handler):
305
332
  command['filename'] = self.nextValue()
306
333
  self.add(command)
307
334
  return True
308
- elif token in ['property', 'element']:
335
+ elif token in ('entry', 'item', 'property', 'element'):
309
336
  command['key'] = self.nextValue()
310
337
  self.skip('of')
311
338
  if self.nextIsSymbol():
312
339
  record = self.getSymbolRecord()
313
- if isinstance(self.getObject(record), ECObject):
314
- command['var'] = record['name']
315
- self.add(command)
316
- return True
317
- NoValueError(self.compiler, record)
340
+ command['variable'] = record['name']
341
+ if token == 'entry':
342
+ self.checkObjectType(self.getObject(record), ECDictionary)
343
+ elif token == 'item':
344
+ self.checkObjectType(self.getObject(record), ECList)
345
+ self.add(command)
346
+ return True
318
347
  self.warning(f'Core.delete: variable expected; got {self.getToken()}')
319
348
  else:
320
- self.warning(f'Core.delete: "file", "property" or "element" expected; got {token}')
349
+ self.warning(f'Core.delete: "file", "entry", "item", "property" or "element" expected; got {token}')
321
350
  return False
322
351
 
323
352
  def r_delete(self, command):
@@ -326,7 +355,16 @@ class Core(Handler):
326
355
  filename = self.textify(command['filename'])
327
356
  if filename != None:
328
357
  if os.path.isfile(filename): os.remove(filename)
358
+ elif type == 'entry':
359
+ key = self.textify(command['key'])
360
+ record = self.getVariable(command['variable'])
361
+ self.getObject(record).deleteEntry(key)
362
+ elif type == 'item':
363
+ key = self.textify(command['key'])
364
+ record = self.getVariable(command['variable'])
365
+ self.getObject(record).deleteItem(key)
329
366
  elif type == 'property':
367
+ raise NotImplementedError('Core.delete property not implemented yet')
330
368
  key = self.textify(command['key'])
331
369
  record = self.getVariable(command['var'])
332
370
  value = self.getSymbolValue(record)
@@ -336,7 +374,7 @@ class Core(Handler):
336
374
  self.putSymbolValue(record, value)
337
375
  elif type == 'element':
338
376
  key = self.textify(command['key'])
339
- record = self.getVariable(command['var'])
377
+ record = self.getVariable(command['variable'])
340
378
  value = self.getSymbolValue(record)
341
379
  content = value.getContent()
342
380
  if isinstance(key, int):
@@ -348,6 +386,15 @@ class Core(Handler):
348
386
  self.putSymbolValue(record, value)
349
387
  return self.nextPC()
350
388
 
389
+ # Declare a dictionary variable
390
+ def k_dictionary(self, command):
391
+ self.compiler.addValueType()
392
+ return self.compileVariable(command, 'ECDictionary')
393
+
394
+ def r_dictionary(self, command):
395
+ return self.nextPC()
396
+
397
+
351
398
  # Arithmetic divide
352
399
  # divide {variable} by {value}
353
400
  # divide {value1} by {value2} giving {variable}
@@ -392,7 +439,7 @@ class Core(Handler):
392
439
  # If value1 exists, we are adding two values and storing the result in target
393
440
  if value1 != None:
394
441
  # divide X by Y giving Z
395
- targetValue = ECValue(domain=self.getName(), type='int', content=int(value1) // int(value2))
442
+ targetValue = ECValue(type=int, content=int(value1) // int(value2))
396
443
  else:
397
444
  # divide X by Y
398
445
  targetValue = self.getSymbolValue(target)
@@ -450,9 +497,9 @@ class Core(Handler):
450
497
  return self.nextPC()
451
498
 
452
499
  # Fork to a label
500
+ # fork [to] {label}
453
501
  def k_fork(self, command):
454
- if self.peek() == 'to':
455
- self.nextToken()
502
+ self.skip('to') # Optional 'to' (core-reserved keyword, plugin-safe)
456
503
  command['fork'] = self.nextToken()
457
504
  self.add(command)
458
505
  return True
@@ -468,7 +515,7 @@ class Core(Handler):
468
515
  self.run(label)
469
516
  return next
470
517
 
471
- # get {variable) from {url} [or {command}]
518
+ # get {variable) from url {url} [or {command}]
472
519
  def k_get(self, command):
473
520
  if self.nextIsSymbol():
474
521
  record = self.getSymbolRecord()
@@ -487,7 +534,7 @@ class Core(Handler):
487
534
  self.nextToken()
488
535
  command['timeout'] = self.nextValue()
489
536
  else:
490
- timeout = ECValue(type = 'int', content = 5)
537
+ timeout = ECValue(type = int, content = 5)
491
538
  command['timeout'] = timeout
492
539
  self.processOr(command, get)
493
540
  return True
@@ -495,10 +542,10 @@ class Core(Handler):
495
542
 
496
543
  def r_get(self, command):
497
544
  global errorCode, errorReason
498
- retval = ECValue(type='str')
545
+ retval = ECValue(type=str)
499
546
  url = self.textify(command['url'])
500
547
  target = self.getVariable(command['target'])
501
- response = json.loads('{}')
548
+ response = {}
502
549
  try:
503
550
  timeout = self.textify(command['timeout'])
504
551
  response = requests.get(url, auth = ('user', 'pass'), timeout=timeout)
@@ -515,15 +562,15 @@ class Core(Handler):
515
562
  return command['or']
516
563
  else:
517
564
  RuntimeError(self.program, f'Error: {errorReason}')
518
- retval.setContent(response.text)
565
+ retval.setContent(response.text) # type: ignore
519
566
  self.program.putSymbolValue(target, retval)
520
567
  return self.nextPC()
521
568
 
522
569
  # Go to a label
570
+ # go [to] {label}
523
571
  def k_go(self, command):
524
- if self.peek() == 'to':
525
- self.nextToken()
526
- return self.k_goto(command)
572
+ self.skip('to') # Optional 'to' (core-reserved keyword, plugin-safe)
573
+ return self.k_goto(command)
527
574
 
528
575
  def k_goto(self, command):
529
576
  command['keyword'] = 'goto'
@@ -545,9 +592,9 @@ class Core(Handler):
545
592
  return command['goto']
546
593
 
547
594
  # Call a subroutine
595
+ # gosub [to] {label}
548
596
  def k_gosub(self, command):
549
- if self.peek() == 'to':
550
- self.nextToken()
597
+ self.skip('to') # Optional 'to' (core-reserved keyword, plugin-safe)
551
598
  command['gosub'] = self.nextToken()
552
599
  self.add(command)
553
600
  return True
@@ -684,7 +731,7 @@ class Core(Handler):
684
731
  # get the variable
685
732
  if self.nextIsSymbol():
686
733
  command['target'] = self.getToken()
687
- value = ECValue(domain=self.getName(), type='str', content=': ')
734
+ value = ECValue(type=str, content=': ')
688
735
  command['prompt'] = value
689
736
  if self.peek() == 'with':
690
737
  self.nextToken()
@@ -696,10 +743,18 @@ class Core(Handler):
696
743
  def r_input(self, command):
697
744
  record = self.getVariable(command['target'])
698
745
  prompt = command['prompt'].getValue()
699
- value = ECValue(domain=self.getName(), type='str', content=prompt+input(prompt))
746
+ value = ECValue(type=str, content=prompt+input(prompt))
700
747
  self.putSymbolValue(record, value)
701
748
  return self.nextPC()
702
749
 
750
+ # Declare a list variable
751
+ def k_list(self, command):
752
+ self.compiler.addValueType()
753
+ return self.compileVariable(command, 'ECList')
754
+
755
+ def r_list(self, command):
756
+ return self.nextPC()
757
+
703
758
  # 1 Load a plugin. This is done at compile time.
704
759
  # 2 Load text from a file or ssh
705
760
  def k_load(self, command):
@@ -712,7 +767,7 @@ class Core(Handler):
712
767
  return True
713
768
  elif self.isSymbol():
714
769
  record = self.getSymbolRecord()
715
- if isinstance(self.getObject(record), ECVariable):
770
+ if isinstance(self.getObject(record), (ECVariable, ECDictionary, ECList)):
716
771
  command['target'] = record['name']
717
772
  if self.nextIs('from'):
718
773
  if self.nextIsSymbol():
@@ -761,8 +816,13 @@ class Core(Handler):
761
816
  return command['or']
762
817
  else:
763
818
  RuntimeError(self.program, f'Error: {errorReason}')
764
- value = ECValue(domain=self.getName(), type='str', content=content)
765
- self.putSymbolValue(target, value)
819
+
820
+ value = ECValue(type=str, content=content)
821
+ try:
822
+ self.putSymbolValue(target, value)
823
+ except Exception as e:
824
+ print(f'Exception "{e}": Running the "or" clause')
825
+ return command['or']
766
826
  return self.nextPC()
767
827
 
768
828
  # Lock a variable
@@ -788,7 +848,7 @@ class Core(Handler):
788
848
  # Declare a module variable
789
849
  def k_module(self, command):
790
850
  self.compiler.addValueType()
791
- return self.compileVariable(command, 'ECObject')
851
+ return self.compileVariable(command, 'ECModule')
792
852
 
793
853
  def r_module(self, command):
794
854
  return self.nextPC()
@@ -837,7 +897,7 @@ class Core(Handler):
837
897
  # If value1 exists, we are adding two values and storing the result in target
838
898
  if value1 != None:
839
899
  # multiply X by Y giving Z
840
- targetValue = ECValue(domain=self.getName(), type='int', content=int(value1) * int(value2))
900
+ targetValue = ECValue(type=int, content=int(value1) * int(value2))
841
901
  else:
842
902
  # multiply X by Y
843
903
  targetValue = self.getSymbolValue(target)
@@ -870,7 +930,8 @@ class Core(Handler):
870
930
 
871
931
  # on message {action}
872
932
  def k_on(self, command):
873
- if self.nextIs('message'):
933
+ token = self.peek()
934
+ if token == 'message':
874
935
  self.nextToken()
875
936
  command['goto'] = 0
876
937
  self.add(command)
@@ -937,7 +998,7 @@ class Core(Handler):
937
998
  return self.nextPC()
938
999
  RuntimeError(self.program, f"File {path} does not exist")
939
1000
 
940
- # Dummy command for testing
1001
+ # Dummy command to hit a debugger breakpoint
941
1002
  def k_pass(self, command):
942
1003
  self.add(command)
943
1004
  return True
@@ -991,7 +1052,7 @@ class Core(Handler):
991
1052
 
992
1053
  def r_post(self, command):
993
1054
  global errorCode, errorReason
994
- retval = ECValue(domain=self.getName(), type='str', content = '')
1055
+ retval = ECValue(type=str, content = '')
995
1056
  value = self.textify(command['value'])
996
1057
  url = self.textify(command['url'])
997
1058
  try:
@@ -1061,30 +1122,29 @@ class Core(Handler):
1061
1122
  stackRecord['object'].push(value)
1062
1123
  return self.nextPC()
1063
1124
 
1064
- # put {value} into {variable}
1125
+ # put {value} into {variable/dictionary/list}
1065
1126
  def k_put(self, command):
1066
1127
  value = self.nextValue()
1067
1128
  if value != None:
1068
1129
  command['value'] = value
1130
+ valueType = value.getType()
1069
1131
  if self.nextIs('into'):
1070
1132
  if self.nextIsSymbol():
1071
1133
  record = self.getSymbolRecord()
1072
1134
  command['target'] = record['name']
1073
- self.checkObjectType(self.getObject(record), ECVariable)
1074
- command['or'] = None
1075
- self.processOr(command, self.getCodeSize())
1076
- return True
1135
+ object = self.getObject(record)
1136
+ self.checkObjectType(object, (ECVariable, ECDictionary, ECList))
1137
+ if (isinstance(object, ECVariable) and not valueType in ('dict', 'list', 'json') or
1138
+ isinstance(object, (ECDictionary, ECList))):
1139
+ command['or'] = None
1140
+ self.processOr(command, self.getCodeSize())
1141
+ return True
1077
1142
  else:
1078
1143
  FatalError(self.compiler, f'Symbol {self.getToken()} is not a variable')
1079
1144
  return False
1080
1145
 
1081
1146
  def r_put(self, command):
1082
1147
  value = self.evaluate(command['value'])
1083
- # if value == None:
1084
- # if command['or'] != None:
1085
- # return command['or']
1086
- # else:
1087
- # RuntimeError(self.program, f'Error: could not compute value')
1088
1148
  record = self.getVariable(command['target'])
1089
1149
  self.putSymbolValue(record, value)
1090
1150
  return self.nextPC()
@@ -1120,7 +1180,7 @@ class Core(Handler):
1120
1180
  file = fileRecord['file']
1121
1181
  if file.mode == 'r':
1122
1182
  content = file.readline().split('\n')[0] if line else file.read()
1123
- value = ECValue(domain=self.getName(), type='str', content=content)
1183
+ value = ECValue(type=str, content=content)
1124
1184
  self.putSymbolValue(record, value)
1125
1185
  return self.nextPC()
1126
1186
 
@@ -1128,7 +1188,8 @@ class Core(Handler):
1128
1188
  def k_release(self, command):
1129
1189
  if self.nextIs('parent'):
1130
1190
  self.add(command)
1131
- return True
1191
+ return True
1192
+ return False
1132
1193
 
1133
1194
  def r_release(self, command):
1134
1195
  self.program.releaseParent()
@@ -1157,7 +1218,7 @@ class Core(Handler):
1157
1218
  original = self.textify(command['original'])
1158
1219
  replacement = self.textify(command['replacement'])
1159
1220
  content = content.replace(original, str(replacement))
1160
- value = ECValue(domain=self.getName(), type='str', content=content)
1221
+ value = ECValue(type=str, content=content)
1161
1222
  self.putSymbolValue(templateRecord, value)
1162
1223
  return self.nextPC()
1163
1224
 
@@ -1181,6 +1242,7 @@ class Core(Handler):
1181
1242
  return True
1182
1243
 
1183
1244
  def r_return(self, command):
1245
+ self.program.debugSkip = False
1184
1246
  return self.stack.pop()
1185
1247
 
1186
1248
  # Compile and run a script
@@ -1296,7 +1358,7 @@ class Core(Handler):
1296
1358
  if self.nextIs('to'):
1297
1359
  if self.nextIsSymbol():
1298
1360
  record = self.getSymbolRecord()
1299
- if record['keyword'] == 'module':
1361
+ if self.isObjectType(record, ECModule):
1300
1362
  command['module'] = record['name']
1301
1363
  self.add(command)
1302
1364
  return True
@@ -1310,9 +1372,10 @@ class Core(Handler):
1310
1372
 
1311
1373
  # Set a value
1312
1374
  # set {variable}
1375
+ # set {variable} to {value}
1313
1376
  # set {ssh} host {host} user {user} password {password}
1314
- # set the elements of {variable} to {value}
1315
- # set element/property of {variable} to {value}
1377
+ # set the items/elements in/of {variable} to {value}
1378
+ # set item/entry/property of {variable} to {value}
1316
1379
  # set breakpoint
1317
1380
  def k_set(self, command):
1318
1381
  if self.nextIsSymbol():
@@ -1341,7 +1404,15 @@ class Core(Handler):
1341
1404
  self.add(command)
1342
1405
  return True
1343
1406
  elif isinstance(self.getObject(record), ECVariable):
1344
- command['type'] = 'set'
1407
+ self.skip('to')
1408
+ mark = self.compiler.getIndex()
1409
+ value = self.nextValue()
1410
+ if value != None:
1411
+ command['type'] = 'setValue'
1412
+ command['value'] = value
1413
+ else:
1414
+ self.rewindTo(mark)
1415
+ command['type'] = 'set'
1345
1416
  self.add(command)
1346
1417
  return True
1347
1418
  return False
@@ -1353,7 +1424,7 @@ class Core(Handler):
1353
1424
 
1354
1425
  if token == 'elements':
1355
1426
  self.nextToken()
1356
- if self.peek() == 'of':
1427
+ if self.peek() in ('in', 'of'):
1357
1428
  self.nextToken()
1358
1429
  if self.nextIsSymbol():
1359
1430
  command['name'] = self.getToken()
@@ -1369,11 +1440,14 @@ class Core(Handler):
1369
1440
  self.add(command)
1370
1441
  return True
1371
1442
 
1372
- elif token == 'property':
1373
- command['name'] = self.nextValue()
1443
+ elif token in ('entry', 'property'):
1444
+ command['key'] = self.nextValue()
1374
1445
  if self.nextIs('of'):
1375
1446
  if self.nextIsSymbol():
1376
- command['target'] = self.getSymbolRecord()['name']
1447
+ record = self.getSymbolRecord()
1448
+ if token == 'entry':
1449
+ self.checkObjectType(self.getObject(record), ECDictionary)
1450
+ command['target'] = record['name']
1377
1451
  if self.nextIs('to'):
1378
1452
  value = self.nextValue()
1379
1453
  if value == None:
@@ -1382,7 +1456,7 @@ class Core(Handler):
1382
1456
  self.add(command)
1383
1457
  return True
1384
1458
 
1385
- elif token == 'element':
1459
+ elif token == 'item':
1386
1460
  command['index'] = self.nextValue()
1387
1461
  if self.nextIs('of'):
1388
1462
  if self.nextIsSymbol():
@@ -1408,7 +1482,13 @@ class Core(Handler):
1408
1482
  cmdType = command['type']
1409
1483
  if cmdType == 'set':
1410
1484
  target = self.getVariable(command['target'])
1411
- self.putSymbolValue(target, ECValue(domain=self.getName(), type='boolean', content=True))
1485
+ self.putSymbolValue(target, ECValue(type=bool, content=True))
1486
+ return self.nextPC()
1487
+
1488
+ elif cmdType == 'setValue':
1489
+ value = self.evaluate(command['value'])
1490
+ target = self.getVariable(command['target'])
1491
+ self.putSymbolValue(target, value)
1412
1492
  return self.nextPC()
1413
1493
 
1414
1494
  elif cmdType == 'elements':
@@ -1419,19 +1499,13 @@ class Core(Handler):
1419
1499
  object.setElements(elements)
1420
1500
  return self.nextPC()
1421
1501
 
1422
- elif cmdType == 'element':
1423
- value = self.textify(command['value'])
1502
+ elif cmdType == 'item':
1424
1503
  index = self.textify(command['index'])
1425
- target = self.getVariable(command['target'])
1426
- val = self.getSymbolValue(target)
1427
- content = val.getContent()
1428
- if content == '':
1429
- content = []
1430
- # else:
1431
- # content = json.loads(content)
1432
- content[index] = value
1433
- val.setContent(content)
1434
- self.putSymbolValue(target, val)
1504
+ value = self.textify(command['value'])
1505
+ record = self.getVariable(command['target'])
1506
+ self.checkObjectType(self.getObject(record), ECList)
1507
+ variable = self.getObject(record)
1508
+ variable.setItem(index, value)
1435
1509
  return self.nextPC()
1436
1510
 
1437
1511
  elif cmdType == 'encoding':
@@ -1443,18 +1517,28 @@ class Core(Handler):
1443
1517
  os.chdir(path)
1444
1518
  return self.nextPC()
1445
1519
 
1520
+ elif cmdType == 'entry':
1521
+ key = self.textify(command['key'])
1522
+ value = self.textify(command['value'])
1523
+ record = self.getVariable(command['target'])
1524
+ self.checkObjectType(self.getObject(record), ECDictionary)
1525
+ variable = self.getObject(record)
1526
+ variable.setEntry(key, value)
1527
+ return self.nextPC()
1528
+
1446
1529
  elif cmdType == 'property':
1447
- name = self.textify(command['name'])
1530
+ key = self.textify(command['key'])
1448
1531
  value = self.evaluate(command['value'])
1449
1532
  record = self.getVariable(command['target'])
1450
1533
  variable = self.getObject(record)
1534
+ variable.setProperty(key, value)
1451
1535
  content = variable.getContent()
1452
1536
  if content == None: content = {}
1453
1537
  elif not isinstance(content, dict):
1454
1538
  raise RuntimeError(self.program, f'{record["name"]} is not a dictionary')
1455
- if isinstance(value, dict): content[name] = value
1456
- else: content[name] = self.textify(value)
1457
- variable.setContent(ECValue(domain=self.getName(), type='dict', content=content))
1539
+ if isinstance(value, dict): content[key] = value
1540
+ else: content[key] = self.textify(value)
1541
+ variable.setContent(ECValue(type='dict', content=content))
1458
1542
  return self.nextPC()
1459
1543
 
1460
1544
  elif cmdType == 'ssh':
@@ -1510,7 +1594,7 @@ class Core(Handler):
1510
1594
  record = self.getSymbolRecord()
1511
1595
  if isinstance(record['object'], ECObject):
1512
1596
  command['target'] = record['name']
1513
- value = ECValue(domain=self.getName(), type='str', content='\n')
1597
+ value = ECValue(type=str, content='\n')
1514
1598
  command['on'] = value
1515
1599
  if self.peek() == 'on':
1516
1600
  self.nextToken()
@@ -1533,7 +1617,7 @@ class Core(Handler):
1533
1617
  object.setElements(elements)
1534
1618
 
1535
1619
  for n in range(0, elements):
1536
- val = ECValue(domain=self.getName(), type='str', content=content[n])
1620
+ val = ECValue(type=str, content=content[n])
1537
1621
  object.setIndex(n)
1538
1622
  object.setValue(val)
1539
1623
  object.setIndex(0)
@@ -1602,7 +1686,7 @@ class Core(Handler):
1602
1686
  self.checkObjectType(record, ECObject)
1603
1687
  # If 'giving' comes next, this variable is the second value
1604
1688
  if self.peek() == 'giving':
1605
- v2 = ECValue(domain=self.getName(), type='symbol')
1689
+ v2 = ECValue(type='symbol')
1606
1690
  v2.setContent(record['name'])
1607
1691
  command['value2'] = v2
1608
1692
  self.nextToken()
@@ -1640,7 +1724,7 @@ class Core(Handler):
1640
1724
  # If value2 exists, we are adding two values and storing the result in target
1641
1725
  if value2 != None:
1642
1726
  # take X from Y giving Z
1643
- targetValue = ECValue(domain=self.getName(), type='int', content=int(value2) - int(value1))
1727
+ targetValue = ECValue(type=int, content=int(value2) - int(value1))
1644
1728
  else:
1645
1729
  # take X from Y
1646
1730
  targetValue = self.getSymbolValue(target)
@@ -1661,7 +1745,7 @@ class Core(Handler):
1661
1745
  def r_toggle(self, command):
1662
1746
  target = self.getVariable(command['target'])
1663
1747
  value = self.getSymbolValue(target)
1664
- val = ECValue(domain=self.getName(), type='boolean', content=not value.getContent())
1748
+ val = ECValue(type=bool, content=not value.getContent())
1665
1749
  self.putSymbolValue(target, val)
1666
1750
  self.add(command)
1667
1751
  return self.nextPC()
@@ -1679,7 +1763,7 @@ class Core(Handler):
1679
1763
  def r_trim(self, command):
1680
1764
  record = self.getVariable(command['name'])
1681
1765
  value = record['value'][record['index']]
1682
- if value.getType() == 'str':
1766
+ if value.getType() == str:
1683
1767
  content = value.getContent()
1684
1768
  value.setContent(content.strip())
1685
1769
  return self.nextPC()
@@ -1715,7 +1799,8 @@ class Core(Handler):
1715
1799
 
1716
1800
  # use plugin {class} from {source}
1717
1801
  # use graphics
1718
- # use psutil.
1802
+ # use mqtt
1803
+ # use psutil
1719
1804
  def k_use(self, command):
1720
1805
  if self.peek() == 'plugin':
1721
1806
  # Import a plugin
@@ -1730,9 +1815,15 @@ class Core(Handler):
1730
1815
  token = self.nextToken()
1731
1816
  if token == 'graphics':
1732
1817
  return self.program.useGraphics()
1818
+ if token == 'mqtt':
1819
+ return self.program.useMQTT()
1733
1820
  elif token == 'psutil':
1734
1821
  return self.program.usePSUtil()
1735
1822
  return False
1823
+
1824
+ # Unused
1825
+ def r_use(self, command):
1826
+ return self.nextPC()
1736
1827
 
1737
1828
  # Declare a general-purpose variable
1738
1829
  def k_variable(self, command):
@@ -1856,7 +1947,7 @@ class Core(Handler):
1856
1947
  #############################################################################
1857
1948
  # Compile a value in this domain
1858
1949
  def compileValue(self):
1859
- value = ECValue(domain=self.getName())
1950
+ value = ECValue()
1860
1951
  token = self.getToken()
1861
1952
  if self.isSymbol():
1862
1953
  value.setValue(type='symbol', content=token)
@@ -1879,7 +1970,7 @@ class Core(Handler):
1879
1970
  if token in ['now', 'today', 'newline', 'tab', 'empty']:
1880
1971
  return value
1881
1972
 
1882
- if token in ['stringify', 'prettify', 'json', 'lowercase', 'uppercase', 'hash', 'random', 'float', 'integer', 'encode', 'decode']:
1973
+ if token in ['stringify', 'prettify', 'json', 'lowercase', 'uppercase', 'hash', 'random', float, 'integer', 'encode', 'decode']:
1883
1974
  value.setContent(self.nextValue())
1884
1975
  return value
1885
1976
 
@@ -1893,27 +1984,25 @@ class Core(Handler):
1893
1984
  value.format = None
1894
1985
  return value
1895
1986
 
1896
- if token == 'element':
1987
+ if token == 'item':
1897
1988
  value.index = self.nextValue()
1898
1989
  if self.nextToken() == 'of':
1899
1990
  if self.nextIsSymbol():
1900
1991
  record = self.getSymbolRecord()
1901
- self.checkObjectType(record['object'], ECVariable)
1902
- value.target = ECValue(domain=self.getName(), type='symbol', content=record['name'])
1992
+ self.checkObjectType(record['object'], ECList)
1993
+ value.target = ECValue(type='symbol', content=record['name'])
1903
1994
  return value
1904
1995
  return None
1905
1996
 
1906
- if token == 'property':
1907
- value.name = self.nextValue() # type: ignore
1908
- if self.nextToken() == 'of':
1997
+ if token == 'entry':
1998
+ value.key = self.nextValue() # type: ignore
1999
+ if self.nextToken() in ('in', 'of'):
1909
2000
  if self.nextIsSymbol():
1910
2001
  record = self.getSymbolRecord()
1911
2002
  object = record['object']
1912
- self.checkObjectType(object, ECObject)
1913
- if hasattr(object, 'name'):
1914
- value.target = ECValue(domain=self.getName(), type='symbol', content=object.name) # type: ignore
1915
- return value
1916
- raise RuntimeError(self.program, f'Object {record["name"]} has no attribute "name"')
2003
+ self.checkObjectType(object, ECDictionary)
2004
+ value.target = object.name
2005
+ return value
1917
2006
  return None
1918
2007
 
1919
2008
  if token == 'arg':
@@ -1937,11 +2026,11 @@ class Core(Handler):
1937
2026
  token = self.getToken()
1938
2027
  value.setType(token)
1939
2028
 
1940
- if token == 'args':
1941
- return value
2029
+ if token in ['args', 'message', 'uuid', 'weekday']:
2030
+ return value
1942
2031
 
1943
- if token == 'elements':
1944
- if self.nextIs('of'):
2032
+ if token in ('items', 'elements'):
2033
+ if self.nextToken() in ('in', 'of'):
1945
2034
  if self.nextIsSymbol():
1946
2035
  value.name = self.getToken() # type: ignore
1947
2036
  return value
@@ -1949,8 +2038,9 @@ class Core(Handler):
1949
2038
 
1950
2039
  if token == 'keys':
1951
2040
  if self.nextIs('of'):
1952
- value.name = self.nextValue() # type: ignore
1953
- return value
2041
+ if self.nextIsSymbol():
2042
+ value.name = self.getToken() # type: ignore
2043
+ return value
1954
2044
  return None
1955
2045
 
1956
2046
  if token == 'count':
@@ -1958,7 +2048,7 @@ class Core(Handler):
1958
2048
  if self.nextIsSymbol():
1959
2049
  record = self.getSymbolRecord()
1960
2050
  object = record['object']
1961
- if isinstance(object, ECVariable):
2051
+ if isinstance(object, ECList):
1962
2052
  value.setContent(record['name'])
1963
2053
  return value
1964
2054
  return None
@@ -1970,6 +2060,7 @@ class Core(Handler):
1970
2060
  if self.peek() == 'in':
1971
2061
  value.value = None # type: ignore
1972
2062
  value.setType('indexOf')
2063
+ self.nextToken()
1973
2064
  if self.nextIsSymbol():
1974
2065
  value.target = self.getSymbolRecord()['name'] # type: ignore
1975
2066
  return value
@@ -2033,9 +2124,6 @@ class Core(Handler):
2033
2124
  value.haystack = self.nextValue() # type: ignore
2034
2125
  return value
2035
2126
 
2036
- if token == 'message':
2037
- return value
2038
-
2039
2127
  if token == 'timestamp':
2040
2128
  value.format = None # type: ignore
2041
2129
  if self.peek() == 'of':
@@ -2053,10 +2141,6 @@ class Core(Handler):
2053
2141
  return value
2054
2142
  return None
2055
2143
 
2056
- if token == 'weekday':
2057
- value.setType('weekday')
2058
- return value
2059
-
2060
2144
  if token == 'error':
2061
2145
  token = self.peek()
2062
2146
  if token == 'code':
@@ -2104,7 +2188,7 @@ class Core(Handler):
2104
2188
  def modifyValue(self, value):
2105
2189
  if self.peek() == 'modulo':
2106
2190
  self.nextToken()
2107
- mv = ECValue(domain=self.getName(), type='modulo', content=value)
2191
+ mv = ECValue(type='modulo', content=value)
2108
2192
  mv.modval = self.nextValue() # type: ignore
2109
2193
  return mv
2110
2194
 
@@ -2114,26 +2198,28 @@ class Core(Handler):
2114
2198
  # Value handlers
2115
2199
 
2116
2200
  def v_args(self, v):
2117
- return ECValue(domain=self.getName(), type='str', content=json.dumps(self.program.argv))
2201
+ return ECValue(type=str, content=json.dumps(self.program.argv))
2118
2202
 
2119
2203
  def v_arg(self, v):
2120
2204
  index = self.textify(v['index'])
2121
2205
  if index >= len(self.program.argv):
2122
2206
  RuntimeError(self.program, 'Index exceeds # of args')
2123
- return ECValue(domain=self.getName(), type='str', content=self.program.argv[index])
2207
+ return ECValue(type=str, content=self.program.argv[index])
2124
2208
 
2209
+ def v_bool(self, v):
2210
+ value = ECValue(type=bool, content=v.getContent())
2211
+
2125
2212
  def v_boolean(self, v):
2126
- value = ECValue(domain=self.getName(), type='boolean', content=v.getContent())
2213
+ return self.v_bool(v)
2127
2214
 
2128
2215
  def v_cos(self, v):
2129
2216
  angle = self.textify(v['angle'])
2130
2217
  radius = self.textify(v['radius'])
2131
- return ECValue(domain=self.getName(), type='int', content=round(math.cos(angle * 0.01745329) * radius))
2218
+ return ECValue(type=int, content=round(math.cos(angle * 0.01745329) * radius))
2132
2219
 
2133
2220
  def v_count(self, v):
2134
- content = self.textify(self.getVariable(v.getContent()))
2135
- if content == None: raise RuntimeError(self.program, 'Count: No value provided')
2136
- return ECValue(domain=self.getName(), type='int', content=len(content))
2221
+ variable = self.getObject(self.getVariable(v.getContent()))
2222
+ return ECValue(type=int, content=variable.getItemCount())
2137
2223
 
2138
2224
  def v_datime(self, v):
2139
2225
  ts = self.textify(v.timestamp)
@@ -2142,11 +2228,11 @@ class Core(Handler):
2142
2228
  fmt = '%b %d %Y %H:%M:%S'
2143
2229
  else:
2144
2230
  fmt = self.textify(fmt)
2145
- return ECValue(domain=self.getName(), type='str', content=datetime.fromtimestamp(ts/1000).strftime(fmt))
2231
+ return ECValue(type=str, content=datetime.fromtimestamp(ts/1000).strftime(fmt))
2146
2232
 
2147
2233
  def v_decode(self, v):
2148
2234
  content = self.textify(v.getContent())
2149
- value = ECValue(domain=self.getName(), type='str')
2235
+ value = ECValue(type=str)
2150
2236
  if self.encoding == 'utf-8':
2151
2237
  value.setContent(content.decode('utf-8'))
2152
2238
  elif self.encoding == 'base64':
@@ -2161,34 +2247,18 @@ class Core(Handler):
2161
2247
  value = v
2162
2248
  return value
2163
2249
 
2164
- def v_element(self, v):
2165
- index = self.textify(v.index)
2166
- targetName = v.target
2167
- target = self.getVariable(targetName.getContent())
2168
- variable = target['object']
2169
- self.checkObjectType(variable, ECObject)
2170
- content = variable.getContent()
2171
- if not type(content) == list:
2172
- RuntimeError(self.program, f'{targetName} is not a list')
2173
- if index >= len(content):
2174
- RuntimeError(self.program, f'Index out of range in {targetName}')
2175
- targetValue = content[index]
2176
- if isinstance(targetValue, ECValue):
2177
- targetValue = self.textify(targetValue)
2178
- return targetValue
2179
-
2180
2250
  def v_elements(self, v):
2181
2251
  var = self.getVariable(v.name)
2182
2252
  object = var['object']
2183
2253
  self.checkObjectType(object, ECVariable)
2184
- return ECValue(domain=self.getName(), type='int', content=object.getElements())
2254
+ return ECValue(type=int, content=object.getElements())
2185
2255
 
2186
2256
  def v_empty(self, v):
2187
- return ECValue(domain=self.getName(), type='str', content='' )
2257
+ return ECValue(type=str, content='' )
2188
2258
 
2189
2259
  def v_encode(self, v):
2190
2260
  content = self.textify(v.getContent())
2191
- value = ECValue(domain=self.getName(), type='str')
2261
+ value = ECValue(type=str)
2192
2262
  if self.encoding == 'utf-8':
2193
2263
  value.setContent(content.encode('utf-8'))
2194
2264
  elif self.encoding == 'base64':
@@ -2203,26 +2273,31 @@ class Core(Handler):
2203
2273
  value = v
2204
2274
  return value
2205
2275
 
2276
+ def v_entry(self, v):
2277
+ record = self.getVariable(v.target)
2278
+ dictionary = self.getObject(record)
2279
+ return dictionary.getEntry(self.textify(v.key))
2280
+
2206
2281
  def v_error(self, v):
2207
2282
  global errorCode, errorReason
2208
- value = ECValue(domain=self.getName())
2283
+ value = ECValue()
2209
2284
  item = v.item
2210
2285
  if item == 'errorCode':
2211
- value.setValue(type='int', content=errorCode)
2286
+ value.setValue(type=int, content=errorCode)
2212
2287
  elif item == 'errorReason':
2213
- value.setValue(type='str', content=errorReason)
2288
+ value.setValue(type=str, content=errorReason)
2214
2289
  elif item == 'sshError':
2215
2290
  record = self.getVariable(v.name)
2216
- value.setValue(type='str', content=record['error'] if 'error' in record else '')
2291
+ value.setValue(type=str, content=record['error'] if 'error' in record else '')
2217
2292
  return value
2218
2293
 
2219
2294
  def v_files(self, v):
2220
2295
  path = self.textify(v.target)
2221
- return ECValue(domain=self.getName(), type='str', content=json.dumps(os.listdir(path)))
2296
+ return ECValue(type=str, content=json.dumps(os.listdir(path)))
2222
2297
 
2223
2298
  def v_float(self, v):
2224
2299
  val = self.textify(v.getContent())
2225
- value = ECValue(domain=self.getName(), type='float')
2300
+ value = ECValue(type=float)
2226
2301
  try:
2227
2302
  value.setContent(float(val))
2228
2303
  except:
@@ -2238,130 +2313,151 @@ class Core(Handler):
2238
2313
  RuntimeError(self.program, 'Invalid "from" value')
2239
2314
  if to is not None and type(to) != int:
2240
2315
  RuntimeError(self.program, 'Invalid "to" value')
2241
- return ECValue(domain=self.getName(), type='str', content=content[start:] if to == None else content[start:to])
2316
+ return ECValue(type=str, content=content[start:] if to == None else content[start:to])
2242
2317
 
2243
2318
  def v_hash(self, v):
2244
2319
  hashval = self.textify(v.getContent())
2245
- return ECValue(domain=self.getName(), type='str', content=hashlib.sha256(hashval.encode('utf-8')).hexdigest())
2320
+ return ECValue(type=str, content=hashlib.sha256(hashval.encode('utf-8')).hexdigest())
2246
2321
 
2247
2322
  def v_index(self, v):
2248
2323
  record = self.getVariable(v.name)
2249
2324
  object = self.getObject(record)
2250
- return ECValue(domain=self.getName(), type='int', content=object.getIndex())
2325
+ return ECValue(type=int, content=object.getIndex())
2251
2326
 
2252
2327
  def v_indexOf(self, v):
2253
2328
  value = v.value
2254
2329
  if value == None:
2255
- value = self.getSymbolValue(v.variable).getContent()
2330
+ var = self.getObject(self.getVariable(v.variable))
2331
+ value = var.getContent()
2256
2332
  else:
2257
2333
  value = self.textify(value)
2258
- target = self.getVariable(v.target)
2259
- data = self.getSymbolValue(target).getContent()
2260
- try: index = data.index(value)
2261
- except: index = -1
2262
- return ECValue(domain=self.getName(), type='int', content=index)
2334
+ target = self.getObject(self.getVariable(v.target))
2335
+ if hasattr(target, 'getIndexOf'):
2336
+ index = target.getIndexOf(value)
2337
+ else:
2338
+ data = target.getContent()
2339
+ try: index = data.index(value)
2340
+ except: index = -1
2341
+ return ECValue(type=int, content=index)
2342
+
2343
+ def v_int(self, v):
2344
+ content = self.textify(v.getContent())
2345
+ if content in ('', None):
2346
+ return ECValue(type=int, content=0)
2347
+ return ECValue(type=int, content=int(content))
2263
2348
 
2264
2349
  def v_integer(self, v):
2265
- val = self.textify(v.getValue())
2266
- return ECValue(domain=self.getName(), type='int', content=int(val))
2350
+ return self.v_int(v)
2351
+
2352
+ def v_item(self, v):
2353
+ index = self.textify(v.index)
2354
+ targetName = v.target
2355
+ target = self.getVariable(targetName.getContent())
2356
+ variable = self.getObject(target)
2357
+ self.checkObjectType(variable, ECList)
2358
+ if index >= variable.getItemCount():
2359
+ RuntimeError(self.program, f'Index out of range in {targetName}')
2360
+ targetValue = variable.getItem(index)
2361
+ return targetValue
2362
+
2363
+ def v_items(self, v):
2364
+ record = self.getVariable(v.name)
2365
+ object = self.getObject(record)
2366
+ self.checkObjectType(object, ECList)
2367
+ return object.getItemCount()
2267
2368
 
2268
2369
  def v_json(self, v):
2269
2370
  item = self.textify(v.getContent())
2270
- value = ECValue(domain=self.getName())
2371
+ value = ECValue()
2271
2372
  try:
2272
2373
  v = json.loads(item)
2273
2374
  if type(v) == list: value.setType('list')
2274
2375
  elif type(v) == dict: value.setType('dict')
2275
- else: value.setType('str')
2376
+ else: value.setType(str)
2276
2377
  value.setContent(v)
2277
2378
  except:
2278
2379
  value = None
2279
2380
  return value
2280
2381
 
2281
2382
  def v_keys(self, v):
2282
- value = self.textify(v.name)
2283
- return ECValue(domain=self.getName(), type='list', content=list(value.keys())) # type: ignore
2383
+ dictionary = self.getObject(self.getVariable(v.name))
2384
+ return ECValue(type='list', content=list(dictionary.keys())) # type: ignore
2284
2385
 
2285
2386
  def v_left(self, v):
2286
2387
  content = self.textify(v.getContent())
2287
2388
  count = self.textify(v.count)
2288
- return ECValue(domain=self.getName(), type='str', content=content[0:count])
2389
+ return ECValue(type=str, content=content[0:count])
2289
2390
 
2290
2391
  def v_lengthOf(self, v):
2291
2392
  content = self.textify(v.getContent())
2292
2393
  if type(content) == str:
2293
- return ECValue(domain=self.getName(), type='int', content=len(content))
2394
+ return ECValue(type=int, content=len(content))
2294
2395
  RuntimeError(self.program, 'Value is not a string')
2295
2396
 
2296
2397
  def v_lowercase(self, v):
2297
2398
  content = self.textify(v.getValue())
2298
- return ECValue(domain=self.getName(), type='str', content=content.lower())
2399
+ return ECValue(type=str, content=content.lower())
2299
2400
 
2300
2401
  def v_message(self, v):
2301
- return ECValue(domain=self.getName(), type='str', content=self.program.message)
2402
+ return ECValue(type=str, content=self.program.message)
2302
2403
 
2303
2404
  def v_modification(self, v):
2304
2405
  fileName = self.textify(v['fileName'])
2305
2406
  ts = int(os.stat(fileName).st_mtime)
2306
- return ECValue(domain=self.getName(), type='int', content=ts)
2407
+ return ECValue(type=int, content=ts)
2307
2408
 
2308
2409
  def v_modulo(self, v):
2309
2410
  val = self.textify(v.getContent())
2310
2411
  modval = self.textify(v.modval)
2311
- return ECValue(domain=self.getName(), type='int', content=val % modval)
2412
+ return ECValue(type=int, content=val % modval)
2312
2413
 
2313
2414
  def v_newline(self, v):
2314
- return ECValue(domain=self.getName(), type='str', content='\n')
2415
+ return ECValue(type=str, content='\n')
2315
2416
 
2316
2417
  def v_now(self, v):
2317
- return ECValue(domain=self.getName(), type='int', content=int(time.time()))
2418
+ return ECValue(type=int, content=int(time.time()))
2318
2419
 
2319
2420
  def v_position(self, v):
2320
2421
  needle = self.textify(v.needle)
2321
2422
  haystack = self.textify(v.haystack)
2322
2423
  last = v.last
2323
- return ECValue(domain=self.getName(), type='int', content=haystack.rfind(needle) if last else haystack.find(needle))
2424
+ return ECValue(type=int, content=haystack.rfind(needle) if last else haystack.find(needle))
2324
2425
 
2325
2426
  def v_prettify(self, v):
2326
2427
  item = self.textify(v.getContent())
2327
- return ECValue(domain=self.getName(), type='str', content=json.dumps(item, indent=4))
2428
+ if isinstance(item, str): item = json.loads(item)
2429
+ return ECValue(type=str, content=json.dumps(item, indent=4))
2328
2430
 
2329
2431
  def v_property(self, v):
2330
2432
  propertyName = v.name
2331
2433
  propertyValue = self.textify(propertyName)
2332
2434
  targetName = v.target
2333
- targetValue = self.textify(targetName)
2334
- try:
2335
- targetObject = json.loads(targetValue)
2336
- except:
2337
- targetObject = targetValue
2338
- if type(targetObject) != dict:
2339
- RuntimeError(self.program, f'{targetName} is not a dictionary')
2340
- if not propertyValue in targetObject:
2341
- RuntimeError(self.program, f'This value does not have the property \'{propertyValue}\'')
2342
- value = targetObject[propertyValue]
2343
- if isinstance(value, ECValue):
2344
- value = self.textify(value)
2345
- return value
2435
+ target = self.getVariable(targetName.getContent())
2436
+ variable = self.getObject(target)
2437
+ return variable.getProperty(propertyValue)
2346
2438
 
2347
2439
  def v_random(self, v):
2348
2440
  limit = self.textify(v.getValue())
2349
- return ECValue(domain=self.getName(), type='int', content=random.randrange(0, limit))
2441
+ return ECValue(type=int, content=random.randrange(0, limit))
2350
2442
 
2351
2443
  def v_right(self, v):
2352
2444
  content = self.textify(v.getContent())
2353
2445
  count = self.textify(v.count)
2354
- return ECValue(domain=self.getName(), type='str', content=content[-count:])
2446
+ return ECValue(type=str, content=content[-count:])
2355
2447
 
2356
2448
  def v_sin(self, v):
2357
2449
  angle = self.textify(v.angle)
2358
2450
  radius = self.textify(v.radius)
2359
- return ECValue(domain=self.getName(), type='int', content=round(math.sin(angle * 0.01745329) * radius))
2451
+ return ECValue(type=int, content=round(math.sin(angle * 0.01745329) * radius))
2452
+
2453
+ def v_str(self, v):
2454
+ content = self.textify(v.getContent())
2455
+ return ECValue(type=str, content=str(content))
2360
2456
 
2361
2457
  def v_stringify(self, v):
2362
2458
  item = self.textify(v.getContent())
2363
- self.checkObjectType(item, (dict, list))
2364
- return ECValue(domain=self.getName(), type='str', content=json.dumps(item))
2459
+ item = json.loads(item)
2460
+ return ECValue(type=str, content=json.dumps(item))
2365
2461
 
2366
2462
  # This is used by the expression evaluator to get the value of a symbol
2367
2463
  def v_symbol(self, v):
@@ -2373,28 +2469,28 @@ class Core(Handler):
2373
2469
  elif keyword == 'variable':
2374
2470
  return self.getSymbolValue(record)
2375
2471
  elif keyword == 'ssh':
2376
- return ECValue(domain=self.getName(), type='boolean', content=True if 'ssh' in record and record['ssh'] != None else False)
2472
+ return ECValue(type=bool, content=True if 'ssh' in record and record['ssh'] != None else False)
2377
2473
  else:
2378
2474
  return None
2379
2475
 
2380
2476
  def v_system(self, v):
2381
2477
  command = self.textify(v.getContent())
2382
2478
  result = os.popen(command).read()
2383
- return ECValue(domain=self.getName(), type='str', content=result)
2479
+ return ECValue(type=str, content=result)
2384
2480
 
2385
2481
  def v_tab(self, v):
2386
- return ECValue(domain=self.getName(), type='str', content='\t')
2482
+ return ECValue(type=str, content='\t')
2387
2483
 
2388
2484
  def v_tan(self, v):
2389
2485
  angle = self.textify(v['angle'])
2390
2486
  radius = self.textify(v['radius'])
2391
- return ECValue(domain=self.getName(), type='int', content=round(math.tan(angle * 0.01745329) * radius))
2487
+ return ECValue(type=int, content=round(math.tan(angle * 0.01745329) * radius))
2392
2488
 
2393
2489
  def v_ticker(self, v):
2394
- return ECValue(domain=self.getName(), type='int', content=self.program.ticker)
2490
+ return ECValue(type=int, content=self.program.ticker)
2395
2491
 
2396
2492
  def v_timestamp(self, v):
2397
- value = ECValue(domain=self.getName(), type='int')
2493
+ value = ECValue(type=int)
2398
2494
  fmt = v.format
2399
2495
  if fmt == None:
2400
2496
  value.setContent(int(time.time()))
@@ -2407,24 +2503,24 @@ class Core(Handler):
2407
2503
  return value
2408
2504
 
2409
2505
  def v_today(self, v):
2410
- return ECValue(domain=self.getName(), type='int', content=int(datetime.combine(datetime.now().date(),datetime.min.time()).timestamp()) * 1000)
2506
+ return ECValue(type=int, content=int(datetime.combine(datetime.now().date(),datetime.min.time()).timestamp()) * 1000)
2411
2507
 
2412
2508
  def v_trim(self, v):
2413
2509
  content = v.getContent()
2414
2510
  content = self.textify(content)
2415
- return ECValue(domain=self.getName(), type='str', content=content.strip())
2511
+ return ECValue(type=str, content=content.strip())
2416
2512
 
2417
2513
  def v_type(self, v):
2418
- value = ECValue(domain=self.getName(), type='str')
2514
+ value = ECValue(type=str)
2419
2515
  val = self.textify(v['value'])
2420
2516
  if val is None:
2421
2517
  value.setContent('none')
2422
2518
  elif type(val) is str:
2423
- value.setContent('str')
2519
+ value.setContent(str)
2424
2520
  elif type(val) is int:
2425
2521
  value.setContent('numeric')
2426
2522
  elif type(val) is bool:
2427
- value.setContent('boolean')
2523
+ value.setContent(bool)
2428
2524
  elif type(val) is list:
2429
2525
  value.setContent('list')
2430
2526
  elif type(val) is dict:
@@ -2433,11 +2529,14 @@ class Core(Handler):
2433
2529
 
2434
2530
  def v_uppercase(self, v):
2435
2531
  content = self.textify(v.getContent())
2436
- return ECValue(domain=self.getName(), type='str', content=content.upper())
2532
+ return ECValue(type=str, content=content.upper())
2533
+
2534
+ def v_uuid(self, v):
2535
+ return ECValue(type=str, content=str(uuid.uuid4())[:8])
2437
2536
 
2438
2537
  def v_valueOf(self, v):
2439
2538
  v = self.textify(v.getContent())
2440
- return ECValue(domain=self.getName(), type='int', content=int(v) if v != '' else 0)
2539
+ return ECValue(type=int, content=int(v) if v != '' else 0)
2441
2540
 
2442
2541
  def v_variable(self, v):
2443
2542
  name = v.getContent()
@@ -2448,7 +2547,7 @@ class Core(Handler):
2448
2547
  return value
2449
2548
 
2450
2549
  def v_weekday(self, v):
2451
- return ECValue(domain=self.getName(), type='int', content=datetime.today().weekday())
2550
+ return ECValue(type=int, content=datetime.today().weekday())
2452
2551
 
2453
2552
  #############################################################################
2454
2553
  # Compile a condition
@@ -2494,6 +2593,10 @@ class Core(Handler):
2494
2593
  condition.negate = not condition.negate # type: ignore
2495
2594
  return condition
2496
2595
  return None
2596
+
2597
+ elif token == 'debugging':
2598
+ condition.type = 'debugging' # type: ignore
2599
+ return condition
2497
2600
 
2498
2601
  value = self.getValue()
2499
2602
  if value == None:
@@ -2505,10 +2608,15 @@ class Core(Handler):
2505
2608
 
2506
2609
  if token == 'has':
2507
2610
  self.nextToken()
2508
- if self.nextToken() == 'property':
2509
- prop = self.nextValue()
2510
- condition.type = 'hasProperty' # type: ignore
2511
- condition.property = prop # type: ignore
2611
+ token = self.nextToken()
2612
+ if token in ('entry', 'property'):
2613
+ value = self.nextValue()
2614
+ if token == 'entry':
2615
+ condition.type = 'hasEntry' # type: ignore
2616
+ condition.entry = value # type: ignore
2617
+ elif token == 'property:':
2618
+ condition.type = 'hasProperty' # type: ignore
2619
+ condition.property = value # type: ignore
2512
2620
  return condition
2513
2621
  return None
2514
2622
 
@@ -2517,10 +2625,15 @@ class Core(Handler):
2517
2625
  if self.nextIs('not'):
2518
2626
  token = self.nextToken()
2519
2627
  if token == 'have':
2520
- if self.nextToken() == 'property':
2521
- prop = self.nextValue()
2522
- condition.type = 'hasProperty' # type: ignore
2523
- condition.property = prop # type: ignore
2628
+ token = self.nextToken()
2629
+ if token in ('entry', 'property:'):
2630
+ value = self.nextValue()
2631
+ if token == 'entry':
2632
+ condition.type = 'hasEntry' # type: ignore
2633
+ condition.entry = value # type: ignore
2634
+ elif token == 'property':
2635
+ condition.type = 'hasProperty' # type: ignore
2636
+ condition.property = value # type: ignore
2524
2637
  condition.negate = not condition.negate # type: ignore
2525
2638
  return condition
2526
2639
  elif token == 'include':
@@ -2548,7 +2661,7 @@ class Core(Handler):
2548
2661
  condition.negate = True # type: ignore
2549
2662
  token = self.nextToken()
2550
2663
  condition.type = token # type: ignore
2551
- if token in ['numeric', 'string', 'boolean', 'none', 'list', 'object', 'even', 'odd', 'empty']:
2664
+ if token in ['numeric', 'string', 'bool', 'boolean', 'none', 'list', 'object', 'even', 'odd', 'empty']:
2552
2665
  return condition
2553
2666
  if token in ['greater', 'less']:
2554
2667
  if self.nextToken() == 'than':
@@ -2560,7 +2673,7 @@ class Core(Handler):
2560
2673
 
2561
2674
  if condition.value1: # type: ignore
2562
2675
  # It's a boolean if
2563
- condition.type = 'boolean' # type: ignore
2676
+ condition.type = bool # type: ignore
2564
2677
  return condition
2565
2678
 
2566
2679
  self.warning(f'Core.compileCondition: I can\'t get a conditional:')
@@ -2576,7 +2689,7 @@ class Core(Handler):
2576
2689
  #############################################################################
2577
2690
  # Condition handlers
2578
2691
 
2579
- def c_boolean(self, condition):
2692
+ def c_bool(self, condition):
2580
2693
  value = self.textify(condition.value1)
2581
2694
  if type(value) == bool:
2582
2695
  return not value if condition.negate else value
@@ -2590,12 +2703,24 @@ class Core(Handler):
2590
2703
  else:
2591
2704
  return True if condition.negate else False
2592
2705
  return False
2706
+
2707
+ def c_boolean(self, condition):
2708
+ return self.c_bool(condition)
2709
+
2710
+ def c_debugging(self, condition):
2711
+ return self.program.debugging
2593
2712
 
2594
2713
  def c_empty(self, condition):
2714
+ if condition.value1.getType() == 'symbol':
2715
+ record = self.getVariable(condition.value1.content)
2716
+ variable = self.getObject(record)
2717
+ if isinstance(variable, (ECList, ECDictionary)):
2718
+ comparison = variable.isEmpty()
2719
+ return not comparison if condition.negate else comparison
2595
2720
  value = self.textify(condition.value1)
2596
2721
  if value == None:
2597
2722
  comparison = True
2598
- elif type(value) == str or type(value) == list or type(value) == dict:
2723
+ elif isinstance(value, str):
2599
2724
  comparison = len(value) == 0
2600
2725
  else:
2601
2726
  domainName = condition.value1.domain
@@ -2623,20 +2748,17 @@ class Core(Handler):
2623
2748
  raise RuntimeError(self.program, f'Cannot compare {self.textify(condition.value1)} and {self.textify(condition.value2)}')
2624
2749
  return comparison <= 0 if condition.negate else comparison > 0
2625
2750
 
2751
+ def c_hasEntry(self, condition):
2752
+ dictionary = self.getObject(self.getVariable(condition.value1.content))
2753
+ entry = self.textify(condition.entry)
2754
+ hasEntry = dictionary.hasEntry(entry) # type: ignore
2755
+ return not hasEntry if condition.negate else hasEntry
2756
+
2626
2757
  def c_hasProperty(self, condition):
2627
- value = self.textify(condition.value1)
2758
+ record = self.getVariable(condition.value1)
2759
+ variable = self.getObject(record)
2628
2760
  prop = self.textify(condition.property)
2629
- if isinstance(value, str):
2630
- try:
2631
- jsonValue = json.loads(value)
2632
- value = jsonValue
2633
- except:
2634
- pass
2635
- try:
2636
- value[prop]
2637
- hasProp = True
2638
- except:
2639
- hasProp = False
2761
+ hasProp = variable.hasProperty(prop)
2640
2762
  return not hasProp if condition.negate else hasProp
2641
2763
 
2642
2764
  def c_includes(self, condition):