easycoder 251105.1__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 (51) hide show
  1. easycoder/__init__.py +6 -3
  2. easycoder/debugger/__init__.py +5 -0
  3. easycoder/debugger/ec_dbg_value_display copy.py +195 -0
  4. easycoder/debugger/ec_dbg_value_display.py +24 -0
  5. easycoder/debugger/ec_dbg_watch_list copy.py +219 -0
  6. easycoder/debugger/ec_dbg_watchlist.py +293 -0
  7. easycoder/debugger/ec_debug.py +1025 -0
  8. easycoder/ec_classes.py +487 -11
  9. easycoder/ec_compiler.py +81 -44
  10. easycoder/ec_condition.py +1 -1
  11. easycoder/ec_core.py +1042 -1081
  12. easycoder/ec_gclasses.py +236 -0
  13. easycoder/ec_graphics.py +1683 -0
  14. easycoder/ec_handler.py +18 -14
  15. easycoder/ec_mqtt.py +248 -0
  16. easycoder/ec_program.py +297 -168
  17. easycoder/ec_psutil.py +48 -0
  18. easycoder/ec_value.py +65 -47
  19. easycoder/pre/README.md +3 -0
  20. easycoder/pre/__init__.py +17 -0
  21. easycoder/pre/debugger/__init__.py +5 -0
  22. easycoder/pre/debugger/ec_dbg_value_display copy.py +195 -0
  23. easycoder/pre/debugger/ec_dbg_value_display.py +24 -0
  24. easycoder/pre/debugger/ec_dbg_watch_list copy.py +219 -0
  25. easycoder/pre/debugger/ec_dbg_watchlist.py +293 -0
  26. easycoder/{ec_debug.py → pre/debugger/ec_debug.py} +418 -185
  27. easycoder/pre/ec_border.py +67 -0
  28. easycoder/pre/ec_classes.py +470 -0
  29. easycoder/pre/ec_compiler.py +291 -0
  30. easycoder/pre/ec_condition.py +27 -0
  31. easycoder/pre/ec_core.py +2772 -0
  32. easycoder/pre/ec_gclasses.py +230 -0
  33. easycoder/{ec_pyside.py → pre/ec_graphics.py} +583 -433
  34. easycoder/pre/ec_handler.py +79 -0
  35. easycoder/pre/ec_keyboard.py +439 -0
  36. easycoder/pre/ec_program.py +557 -0
  37. easycoder/pre/ec_psutil.py +48 -0
  38. easycoder/pre/ec_timestamp.py +11 -0
  39. easycoder/pre/ec_value.py +124 -0
  40. easycoder/pre/icons/close.png +0 -0
  41. easycoder/pre/icons/exit.png +0 -0
  42. easycoder/pre/icons/run.png +0 -0
  43. easycoder/pre/icons/step.png +0 -0
  44. easycoder/pre/icons/stop.png +0 -0
  45. easycoder/pre/icons/tick.png +0 -0
  46. {easycoder-251105.1.dist-info → easycoder-260111.1.dist-info}/METADATA +11 -1
  47. easycoder-260111.1.dist-info/RECORD +59 -0
  48. easycoder-251105.1.dist-info/RECORD +0 -24
  49. {easycoder-251105.1.dist-info → easycoder-260111.1.dist-info}/WHEEL +0 -0
  50. {easycoder-251105.1.dist-info → easycoder-260111.1.dist-info}/entry_points.txt +0 -0
  51. {easycoder-251105.1.dist-info → easycoder-260111.1.dist-info}/licenses/LICENSE +0 -0
easycoder/ec_core.py CHANGED
@@ -1,16 +1,31 @@
1
1
  import json, math, hashlib, threading, os, subprocess, time
2
- import numbers, base64, binascii, random, requests, paramiko
2
+ import base64, binascii, random, requests, paramiko, uuid
3
3
  from copy import deepcopy
4
- from psutil import Process
5
4
  from datetime import datetime
6
- from .ec_classes import FatalError, RuntimeWarning, RuntimeError, AssertionError, NoValueError, NoValueRuntimeError, Object
5
+ from .ec_classes import (
6
+ FatalError,
7
+ RuntimeWarning,
8
+ RuntimeError,
9
+ RuntimeAssertionError,
10
+ NoValueError,
11
+ NoValueRuntimeError,
12
+ ECObject,
13
+ ECVariable,
14
+ ECDictionary,
15
+ ECList,
16
+ ECFile,
17
+ ECStack,
18
+ ECSSH,
19
+ ECValue,
20
+ ECModule
21
+ )
22
+
7
23
  from .ec_handler import Handler
8
- from .ec_timestamp import getTimestamp
9
24
 
10
25
  class Core(Handler):
11
26
 
12
27
  def __init__(self, compiler):
13
- Handler.__init__(self, compiler)
28
+ super().__init__(compiler)
14
29
  self.encoding = 'utf-8'
15
30
 
16
31
  def getName(self):
@@ -43,91 +58,80 @@ class Core(Handler):
43
58
  # Keyword handlers
44
59
 
45
60
  # Arithmetic add
46
- # add {value} to {variable}[ giving {variable}]}
61
+ # add {value} to {variable}
62
+ # add {value1} to {value2} giving {variable}
47
63
  def k_add(self, command):
48
64
  # Get the (first) value
49
65
  command['value1'] = self.nextValue()
50
- if self.nextToken() == 'to':
51
- if self.nextIsSymbol():
52
- symbolRecord = self.getSymbolRecord()
53
- if symbolRecord['hasValue']:
54
- if self.peek() == 'giving':
55
- # This variable must be treated as a second value
56
- command['value2'] = self.getValue()
57
- self.nextToken()
58
- command['target'] = self.nextToken()
59
- self.add(command)
60
- return True
61
- else:
62
- # Here the variable is the target
63
- command['target'] = self.getToken()
64
- self.add(command)
65
- return True
66
- self.warning(f'Core.add: Expected value holder')
66
+ if command['value1'] == None: return False
67
+ self.skip('to')
68
+ if self.nextIsSymbol():
69
+ record = self.getSymbolRecord()
70
+ if not isinstance(self.getObject(record), ECVariable): return False
71
+ # If 'giving' comes next, this variable is the second value
72
+ if self.peek() == 'giving':
73
+ v2 = ECValue(type='symbol', content=record['name'])
74
+ command['value2'] = v2
75
+ self.nextToken()
76
+ # Now get the target variable
77
+ if self.nextIsSymbol():
78
+ record = self.getSymbolRecord()
79
+ self.checkObjectType(record, ECVariable)
80
+ command['target'] = record['name']
81
+ self.add(command)
82
+ return True
67
83
  else:
68
- # Here we have 2 values so 'giving' must come next
69
- command['value2'] = self.getValue()
70
- if self.nextToken() == 'giving':
71
- command['target'] = self.nextToken()
84
+ # Here the variable is the target
85
+ command['target'] = record['name']
86
+ if self.getObject(record).isMutable():
72
87
  self.add(command)
73
88
  return True
74
- self.warning(f'Core.add: Expected "giving"')
89
+ else:
90
+ # Here we have 2 values so 'giving' must come next
91
+ command['value2'] = self.getValue()
92
+ if self.nextToken() == 'giving':
93
+ if self.nextIsSymbol():
94
+ record = self.getSymbolRecord()
95
+ self.checkObjectType(record, ECVariable)
96
+ command['target'] = record['name']
97
+ self.add(command)
98
+ return True
99
+ # raise FatalError(self.compiler, 'Cannot add values: target variable expected')
75
100
  return False
76
101
 
77
102
  def r_add(self, command):
78
- value1 = command['value1']
79
- try:
80
- value2 = command['value2']
81
- except:
82
- value2 = None
103
+ value1 = self.textify(command['value1'])
104
+ value2 = self.textify(command['value2']) if 'value2' in command else None
83
105
  target = self.getVariable(command['target'])
84
- if not target['hasValue']:
85
- self.variableDoesNotHoldAValueError(target['name'])
86
- targetValue = self.getSymbolValue(target)
87
- if targetValue == None:
88
- targetValue = {}
89
- targetValue['content'] = 0
90
- targetValue['type'] = 'int'
91
- if value2:
92
- v1 = int(self.getRuntimeValue(value1))
93
- v2 = int(self.getRuntimeValue(value2))
94
- targetValue['content'] = v1 + v2
106
+ # Check that the target variable is mutable. If not, it's not an arithmetic add
107
+ # If value2 exists, we are adding two values and storing the result in target
108
+ if value2 != None:
109
+ # add X to Y giving Z
110
+ targetValue = ECValue(type=int, content=int(value1) + int(value2))
95
111
  else:
96
- # if targetValue['type'] != 'int' and targetValue['content'] != None:
97
- # self.nonNumericValueError()
98
- v = self.getRuntimeValue(targetValue)
99
- v = int(v)
100
- v1 = int(self.getRuntimeValue(value1))
101
- if v1 == None:
102
- v1 = 0
103
- targetValue['content'] = v + v1
112
+ # add X to Y
113
+ targetValue = self.getSymbolValue(target)
114
+ targetValue.setContent(int(targetValue.getContent()) + int(value1))
104
115
  self.putSymbolValue(target, targetValue)
105
116
  return self.nextPC()
106
117
 
107
- # Append a value to an array
108
- # append {value} to {array}
118
+ # Append a value to an list
119
+ # append {value} to {list}
109
120
  def k_append(self, command):
110
121
  command['value'] = self.nextValue()
111
122
  if self.nextIs('to'):
112
123
  if self.nextIsSymbol():
113
- symbolRecord = self.getSymbolRecord()
114
- if symbolRecord['hasValue']:
115
- command['target'] = symbolRecord['name']
116
- self.add(command)
117
- return True
118
- self.warning(f'Core.append: Variable {symbolRecord["name"]} does not hold a value')
124
+ record = self.getSymbolRecord()
125
+ self.program.checkObjectType(self.getObject(record), ECList)
126
+ command['target'] = record['name']
127
+ self.add(command)
128
+ return True
119
129
  return False
120
130
 
121
131
  def r_append(self, command):
122
- value = self.getRuntimeValue(command['value'])
123
- target = self.getVariable(command['target'])
124
- val = self.getSymbolValue(target)
125
- content = val['content']
126
- if content == '':
127
- content = []
128
- content.append(value)
129
- val['content'] = content
130
- self.putSymbolValue(target, val)
132
+ value = self.textify(command['value'])
133
+ target = self.getObject(self.getVariable(command['target']))
134
+ target.append(value)
131
135
  return self.nextPC()
132
136
 
133
137
  #assert {condition} [with {message}]
@@ -145,7 +149,7 @@ class Core(Handler):
145
149
  test = self.program.condition.testCondition(command['test'])
146
150
  if test:
147
151
  return self.nextPC()
148
- AssertionError(self.program, self.getRuntimeValue(command['with']))
152
+ RuntimeAssertionError(self.program, self.textify(command['with']))
149
153
 
150
154
  # Begin a block
151
155
  def k_begin(self, command):
@@ -162,23 +166,32 @@ class Core(Handler):
162
166
 
163
167
  # clear {variable}
164
168
  def k_clear(self, command):
165
- if self.nextIsSymbol():
166
- target = self.getSymbolRecord()
167
- command['target'] = target['name']
168
- if target['hasValue'] or target['keyword'] == 'ssh':
169
+ token = self.nextToken()
170
+ if token == 'breakpoint':
171
+ command['breakpoint'] = True
172
+ self.add(command)
173
+ return True
174
+ elif self.isSymbol():
175
+ record = self.getSymbolRecord()
176
+ command['target'] = record['name']
177
+ object = self.getObject(record)
178
+ if isinstance(object, ECSSH):
179
+ self.add(command)
180
+ return True
181
+ if isinstance(object, ECVariable):
169
182
  self.add(command)
170
183
  return True
171
184
  return False
172
185
 
173
186
  def r_clear(self, command):
174
- target = self.getVariable(command['target'])
175
- if target['keyword'] == 'ssh':
176
- target['ssh'] = None
187
+ if 'breakpoint' in command:
188
+ self.program.breakpoint = False
177
189
  else:
178
- val = {}
179
- val['type'] = 'boolean'
180
- val['content'] = False
181
- self.putSymbolValue(target, val)
190
+ target = self.getVariable(command['target'])
191
+ if target['keyword'] == 'ssh':
192
+ target['ssh'] = None
193
+ else:
194
+ self.putSymbolValue(target, ECValue(type=bool, content=False))
182
195
  return self.nextPC()
183
196
 
184
197
  # Close a file
@@ -186,10 +199,10 @@ class Core(Handler):
186
199
  def k_close(self, command):
187
200
  if self.nextIsSymbol():
188
201
  fileRecord = self.getSymbolRecord()
189
- if fileRecord['keyword'] == 'file':
190
- command['file'] = fileRecord['name']
191
- self.add(command)
192
- return True
202
+ self.checkObjectType
203
+ command['file'] = fileRecord['name']
204
+ self.add(command)
205
+ return True
193
206
  return False
194
207
 
195
208
  def r_close(self, command):
@@ -197,7 +210,35 @@ class Core(Handler):
197
210
  fileRecord['file'].close()
198
211
  return self.nextPC()
199
212
 
200
- #Create directory
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
+
241
+ # Create directory
201
242
  # create directory {name}
202
243
  def k_create(self, command):
203
244
  if self.nextIs('directory'):
@@ -209,7 +250,7 @@ class Core(Handler):
209
250
 
210
251
  def r_create(self, command):
211
252
  if command['item'] == 'directory':
212
- path = self.getRuntimeValue(command['path'])
253
+ path = self.textify(command['path'])
213
254
  if not os.path.exists(path):
214
255
  os.makedirs(path)
215
256
  return self.nextPC()
@@ -221,7 +262,7 @@ class Core(Handler):
221
262
  self.compiler.debugCompile = True
222
263
  self.nextToken()
223
264
  return True
224
- elif token in ['step', 'stop', 'program', 'custom']:
265
+ elif token in ['step', 'stop', 'skip', 'breakpoint', 'program', 'custom']:
225
266
  command['mode'] = token
226
267
  self.nextToken()
227
268
  elif token == 'stack':
@@ -247,18 +288,22 @@ class Core(Handler):
247
288
  self.program.debugStep = True
248
289
  elif command['mode'] == 'stop':
249
290
  self.program.debugStep = False
291
+ elif command['mode'] == 'skip':
292
+ self.program.debugSkip = True
293
+ elif command['mode'] == 'breakpoint':
294
+ self.program.breakpoint = True
250
295
  elif command['mode'] == 'program':
251
296
  for item in self.code:
252
297
  print(json.dumps(item, indent = 2))
253
298
  elif command['mode'] == 'stack':
254
299
  stackRecord = self.getVariable(command['stack'])
255
300
  value = self.getSymbolValue(stackRecord)
256
- print(f'{self.getRuntimeValue(command["as"])}:',json.dumps(self.getSymbolValue(stackRecord), indent = 2))
301
+ print(f'{self.textify(command["as"])}:',json.dumps(self.getSymbolValue(stackRecord), indent = 2))
257
302
  elif command['mode'] == 'custom':
258
303
  # Custom debugging code goes in here
259
304
  record = self.getVariable('Script')
260
305
  print('(Debug) Script:',record)
261
- value = self.getRuntimeValue(record)
306
+ value = self.textify(record)
262
307
  print('(Debug) Value:',value)
263
308
  pass
264
309
  return self.nextPC()
@@ -267,12 +312,11 @@ class Core(Handler):
267
312
  # decrement {variable}
268
313
  def k_decrement(self, command):
269
314
  if self.nextIsSymbol():
270
- symbolRecord = self.getSymbolRecord()
271
- if symbolRecord['hasValue']:
272
- command['target'] = self.getToken()
273
- self.add(command)
274
- return True
275
- self.warning(f'Core.decrement: Variable {symbolRecord["name"]} does not hold a value')
315
+ record = self.getSymbolRecord()
316
+ self.checkObjectType(self.getObject(record), ECVariable)
317
+ command['target'] = record['name']
318
+ self.add(command)
319
+ return True
276
320
  return False
277
321
 
278
322
  def r_decrement(self, command):
@@ -280,8 +324,7 @@ class Core(Handler):
280
324
 
281
325
  # Delete a file or a property
282
326
  # delete file {filename}
283
- # delete property {value} of {variable}
284
- # delete element {name} of {variable}
327
+ # delete entry/item/property/element {name/number} of {variable}
285
328
  def k_delete(self, command):
286
329
  token = self.nextToken( )
287
330
  command['type'] = token
@@ -289,94 +332,119 @@ class Core(Handler):
289
332
  command['filename'] = self.nextValue()
290
333
  self.add(command)
291
334
  return True
292
- elif token in ['property', 'element']:
335
+ elif token in ('entry', 'item', 'property', 'element'):
293
336
  command['key'] = self.nextValue()
294
337
  self.skip('of')
295
338
  if self.nextIsSymbol():
296
339
  record = self.getSymbolRecord()
297
- if record['hasValue']:
298
- command['var'] = record['name']
299
- self.add(command)
300
- return True
301
- 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
302
347
  self.warning(f'Core.delete: variable expected; got {self.getToken()}')
303
348
  else:
304
- 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}')
305
350
  return False
306
351
 
307
352
  def r_delete(self, command):
308
353
  type = command['type']
309
354
  if type == 'file':
310
- filename = self.getRuntimeValue(command['filename'])
355
+ filename = self.textify(command['filename'])
311
356
  if filename != None:
312
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)
313
366
  elif type == 'property':
314
- key = self.getRuntimeValue(command['key'])
315
- symbolRecord = self.getVariable(command['var'])
316
- value = self.getSymbolValue(symbolRecord)
317
- content = value['content']
367
+ raise NotImplementedError('Core.delete property not implemented yet')
368
+ key = self.textify(command['key'])
369
+ record = self.getVariable(command['var'])
370
+ value = self.getSymbolValue(record)
371
+ content = value.getContent()
318
372
  content.pop(key, None)
319
- value['content'] = content
320
- self.putSymbolValue(symbolRecord, value)
373
+ value.setContent(content)
374
+ self.putSymbolValue(record, value)
321
375
  elif type == 'element':
322
- key = self.getRuntimeValue(command['key'])
323
- symbolRecord = self.getVariable(command['var'])
324
- value = self.getSymbolValue(symbolRecord)
325
- content = value['content']
326
- if key >= 0 and key < len(content): del(content[key])
376
+ key = self.textify(command['key'])
377
+ record = self.getVariable(command['variable'])
378
+ value = self.getSymbolValue(record)
379
+ content = value.getContent()
380
+ if isinstance(key, int):
381
+ if key >= 0 and key < len(content): del(content[key])
382
+ elif isinstance(key, str):
383
+ if key in content: content.remove(key)
327
384
  else: RuntimeError(self.program, f'Index {key} out of range')
328
- value['content'] = content
329
- self.putSymbolValue(symbolRecord, value)
385
+ value.setContent(content)
386
+ self.putSymbolValue(record, value)
387
+ return self.nextPC()
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):
330
395
  return self.nextPC()
331
396
 
332
- # Arithmetic division
333
- # divide {variable} by {value}[ giving {variable}]}
397
+
398
+ # Arithmetic divide
399
+ # divide {variable} by {value}
400
+ # divide {value1} by {value2} giving {variable}
334
401
  def k_divide(self, command):
335
- # Get the (first) value
336
- command['value1'] = self.nextValue()
337
- if self.nextToken() == 'by':
338
- command['value2'] = self.nextValue()
339
- if self.peek() == 'giving':
340
- self.nextToken()
341
- if (self.nextIsSymbol()):
342
- command['target'] = self.getToken()
343
- self.add(command)
344
- return True
345
- FatalError(self.compiler, 'Symbol expected')
346
- else:
347
- # First value must be a variable
348
- if command['value1']['type'] == 'symbol':
349
- command['target'] = command['value1']['name']
350
- self.add(command)
351
- return True
352
- FatalError(self.compiler, 'First value must be a variable')
402
+ # Get the (first) item. If it's a symbol, it may be the target variable
403
+ if self.nextIsSymbol():
404
+ record = self.getSymbolRecord()
405
+ self.checkObjectType(record, ECVariable)
406
+ # Hold onto the variable and its value
407
+ variable1 = record['name']
408
+ value1 = self.getValue()
409
+ else:
410
+ # Here we have a value
411
+ value1 = self.getValue()
412
+ variable1 = None
413
+ self.skip('by')
414
+ command['value2'] = self.nextValue()
415
+ # if 'giving' comes next, the target is the next value
416
+ if self.peek() == 'giving':
417
+ self.nextToken()
418
+ if self.nextIsSymbol():
419
+ record = self.getSymbolRecord()
420
+ self.checkObjectType(record, ECVariable)
421
+ command['target'] = record['name']
422
+ command['value1'] = value1
423
+ self.add(command)
424
+ return True
425
+ else:
426
+ # Here the first variable is the target
427
+ if variable1 != None:
428
+ command['target'] = variable1
429
+ self.add(command)
430
+ return True
353
431
  return False
354
432
 
355
433
  def r_divide(self, command):
356
- value1 = command['value1']
357
- try:
358
- value2 = command['value2']
359
- except:
360
- value2 = None
434
+ value1 = self.textify(command['value1']) if 'value1' in command else None
435
+ value2 = self.textify(command['value2'])
361
436
  target = self.getVariable(command['target'])
362
- if not target['hasValue']:
363
- self.variableDoesNotHoldAValueError(target['name'])
364
- return None
365
- value = self.getSymbolValue(target)
366
- if value == None:
367
- value = {}
368
- value['type'] = 'int'
369
- if value2:
370
- v1 = int(self.getRuntimeValue(value1))
371
- v2 = int(self.getRuntimeValue(value2))
372
- value['content'] = int(v1/v2)
437
+ # Check that the target variable can hold a value
438
+ self.checkObjectType(target, ECVariable)
439
+ # If value1 exists, we are adding two values and storing the result in target
440
+ if value1 != None:
441
+ # divide X by Y giving Z
442
+ targetValue = ECValue(type=int, content=int(value1) // int(value2))
373
443
  else:
374
- if value['type'] != 'int' and value['content'] != None:
375
- self.nonNumericValueError(self.compiler, command['lino'])
376
- v = int(self.getRuntimeValue(value))
377
- v1 = int(self.getRuntimeValue(value1))
378
- value['content'] = int(v/v1)
379
- self.putSymbolValue(target, value)
444
+ # divide X by Y
445
+ targetValue = self.getSymbolValue(target)
446
+ targetValue.setContent(int(targetValue.getContent()) // int(value2))
447
+ self.putSymbolValue(target, targetValue)
380
448
  return self.nextPC()
381
449
 
382
450
  # download [binary] {url} to {path}
@@ -393,8 +461,8 @@ class Core(Handler):
393
461
 
394
462
  def r_download(self, command):
395
463
  binary = command['binary']
396
- url = self.getRuntimeValue(command['url'])
397
- path = self.getRuntimeValue(command['path'])
464
+ url = self.textify(command['url'])
465
+ path = self.textify(command['path'])
398
466
  mode = 'wb' if binary else 'w'
399
467
  response = requests.get(url, stream=True)
400
468
  with open(path, mode) as f:
@@ -402,14 +470,6 @@ class Core(Handler):
402
470
  if chunk: f.write(chunk)
403
471
  return self.nextPC()
404
472
 
405
- # Dummy command for testing
406
- def k_dummy(self, command):
407
- self.add(command)
408
- return True
409
-
410
- def r_dummy(self, command):
411
- return self.nextPC()
412
-
413
473
  # Match a begin
414
474
  def k_end(self, command):
415
475
  self.add(command)
@@ -430,15 +490,16 @@ class Core(Handler):
430
490
 
431
491
  # Declare a file variable
432
492
  def k_file(self, command):
433
- return self.compileVariable(command)
493
+ self.compiler.addValueType()
494
+ return self.compileVariable(command, 'ECFile')
434
495
 
435
496
  def r_file(self, command):
436
497
  return self.nextPC()
437
498
 
438
499
  # Fork to a label
500
+ # fork [to] {label}
439
501
  def k_fork(self, command):
440
- if self.peek() == 'to':
441
- self.nextToken()
502
+ self.skip('to') # Optional 'to' (core-reserved keyword, plugin-safe)
442
503
  command['fork'] = self.nextToken()
443
504
  self.add(command)
444
505
  return True
@@ -454,14 +515,14 @@ class Core(Handler):
454
515
  self.run(label)
455
516
  return next
456
517
 
457
- # get {variable) from {url} [or {command}]
518
+ # get {variable) from url {url} [or {command}]
458
519
  def k_get(self, command):
459
520
  if self.nextIsSymbol():
460
- symbolRecord = self.getSymbolRecord()
461
- if symbolRecord['hasValue']:
521
+ record = self.getSymbolRecord()
522
+ if isinstance(self.getObject(record), ECObject):
462
523
  command['target'] = self.getToken()
463
524
  else:
464
- NoValueError(self.compiler, symbolRecord)
525
+ NoValueError(self.compiler, record)
465
526
  if self.nextIs('from'):
466
527
  if self.nextIs('url'):
467
528
  url = self.nextValue()
@@ -473,9 +534,7 @@ class Core(Handler):
473
534
  self.nextToken()
474
535
  command['timeout'] = self.nextValue()
475
536
  else:
476
- timeout = {}
477
- timeout['type'] = 'int'
478
- timeout['content'] = 5
537
+ timeout = ECValue(type = int, content = 5)
479
538
  command['timeout'] = timeout
480
539
  self.processOr(command, get)
481
540
  return True
@@ -483,14 +542,12 @@ class Core(Handler):
483
542
 
484
543
  def r_get(self, command):
485
544
  global errorCode, errorReason
486
- retval = {}
487
- retval['type'] = 'text'
488
- retval['numeric'] = False
489
- url = self.getRuntimeValue(command['url'])
545
+ retval = ECValue(type=str)
546
+ url = self.textify(command['url'])
490
547
  target = self.getVariable(command['target'])
491
- response = json.loads('{}')
548
+ response = {}
492
549
  try:
493
- timeout = self.getRuntimeValue(command['timeout'])
550
+ timeout = self.textify(command['timeout'])
494
551
  response = requests.get(url, auth = ('user', 'pass'), timeout=timeout)
495
552
  if response.status_code >= 400:
496
553
  errorCode = response.status_code
@@ -505,15 +562,15 @@ class Core(Handler):
505
562
  return command['or']
506
563
  else:
507
564
  RuntimeError(self.program, f'Error: {errorReason}')
508
- retval['content'] = response.text
565
+ retval.setContent(response.text) # type: ignore
509
566
  self.program.putSymbolValue(target, retval)
510
567
  return self.nextPC()
511
568
 
512
569
  # Go to a label
570
+ # go [to] {label}
513
571
  def k_go(self, command):
514
- if self.peek() == 'to':
515
- self.nextToken()
516
- return self.k_goto(command)
572
+ self.skip('to') # Optional 'to' (core-reserved keyword, plugin-safe)
573
+ return self.k_goto(command)
517
574
 
518
575
  def k_goto(self, command):
519
576
  command['keyword'] = 'goto'
@@ -535,9 +592,9 @@ class Core(Handler):
535
592
  return command['goto']
536
593
 
537
594
  # Call a subroutine
595
+ # gosub [to] {label}
538
596
  def k_gosub(self, command):
539
- if self.peek() == 'to':
540
- self.nextToken()
597
+ self.skip('to') # Optional 'to' (core-reserved keyword, plugin-safe)
541
598
  command['gosub'] = self.nextToken()
542
599
  self.add(command)
543
600
  return True
@@ -599,55 +656,51 @@ class Core(Handler):
599
656
 
600
657
  # Import one or more variables
601
658
  def k_import(self, command):
659
+ self.add(command)
602
660
  imports = []
603
661
  while True:
604
- keyword = self.nextToken()
605
- name = self.nextToken()
606
- item = [keyword, name]
607
- imports.append(item)
608
- self.symbols[name] = self.getCodeSize()
609
- variable = {}
610
- variable['domain'] = None
611
- variable['name'] = name
612
- variable['keyword'] = keyword
613
- variable['import'] = None
614
- variable['used'] = False
615
- variable['hasValue'] = True if keyword == 'variable' else False
616
- self.add(variable)
662
+ vartype = self.nextToken()
663
+ for domain in self.program.getDomains():
664
+ handler = domain.keywordHandler(vartype)
665
+ if handler != None:
666
+ variable = {}
667
+ if not handler(variable):
668
+ raise RuntimeError(self.program, f'Failed to handle variable type "{vartype}"')
669
+ imports.append(variable)
617
670
  if self.peek() != 'and':
618
671
  break
619
672
  self.nextToken()
620
- command['imports'] = json.dumps(imports)
621
- self.add(command)
673
+ command['imports'] = imports
622
674
  return True
623
675
 
624
676
  def r_import(self, command):
625
677
  exports = self.program.exports
626
- imports = json.loads(command['imports'])
678
+ imports = command['imports']
627
679
  if len(imports) < len(exports):
628
680
  RuntimeError(self.program, 'Too few imports')
629
681
  elif len(imports) > len(exports):
630
682
  RuntimeError(self.program, 'Too many imports')
631
683
  for n in range(0, len(imports)):
632
684
  exportRecord = exports[n]
633
- exportKeyword = exportRecord['keyword']
634
- name = imports[n][1]
635
- symbolRecord = self.program.getSymbolRecord(name)
636
- symbolKeyword = symbolRecord['keyword']
637
- if symbolKeyword != exportKeyword:
638
- RuntimeError(self.program, f'Import {n} ({symbolKeyword}) does not match export {n} ({exportKeyword})')
639
- symbolRecord['import'] = exportRecord
685
+ importRecord = imports[n]
686
+ if importRecord['classname'] != exportRecord['classname']:
687
+ raise RuntimeError(self.program, f'Import {n} does not match export (wrong type)')
688
+ name = importRecord['name']
689
+ importRecord.clear()
690
+ importRecord['name'] = name
691
+ importRecord['domain'] = exportRecord['domain']
692
+ importRecord['keyword'] = exportRecord['keyword']
693
+ importRecord['import'] = exportRecord
640
694
  return self.nextPC()
641
695
 
642
696
  # Increment a variable
643
697
  def k_increment(self, command):
644
698
  if self.nextIsSymbol():
645
- symbolRecord = self.getSymbolRecord()
646
- if symbolRecord['hasValue']:
647
- command['target'] = self.getToken()
648
- self.add(command)
649
- return True
650
- self.warning(f'Core.increment: Variable {symbolRecord["name"]} does not hold a value')
699
+ record = self.getSymbolRecord()
700
+ self.checkObjectType(self.getObject(record), ECVariable)
701
+ command['target'] = record['name']
702
+ self.add(command)
703
+ return True
651
704
  return False
652
705
 
653
706
  def r_increment(self, command):
@@ -667,43 +720,18 @@ class Core(Handler):
667
720
  return False
668
721
 
669
722
  def r_index(self, command):
670
- symbolRecord = self.getVariable(command['target'])
671
- symbolRecord['index'] = self.getRuntimeValue(command['value'])
723
+ value = self.textify(command['value'])
724
+ record = self.getVariable(command['target'])
725
+ self.getObject(record).setIndex(value)
672
726
  return self.nextPC()
673
727
 
674
- # Initialise a stack, array or object
675
- def k_init(self, command):
676
- # get the variable
677
- if self.nextIsSymbol():
678
- symbolRecord = self.getSymbolRecord()
679
- keyword = symbolRecord['keyword']
680
- if keyword in ['stack','array', 'object']:
681
- command['keyword'] = keyword
682
- command['target'] = symbolRecord['name']
683
- return True
684
- return False
685
-
686
- def r_init(self, command):
687
- symbolRecord = self.getVariable(command['target'])
688
- keyword = command['keyword']
689
- if keyword in ['stack', 'array']:
690
- self.putSymbolValue(symbolRecord, json.loads('[]'))
691
- elif keyword == 'object':
692
- self.putSymbolValue(symbolRecord, json.loads('{}'))
693
- else:
694
- RuntimeError(self.program, f"Inappropriate variable type '{keyword}'")
695
- return self.nextPC()
696
-
697
- # Inout a value from the terminal
728
+ # Input a value from the terminal
698
729
  # input {variable} [with {prompt}]
699
730
  def k_input(self, command):
700
731
  # get the variable
701
732
  if self.nextIsSymbol():
702
733
  command['target'] = self.getToken()
703
- value = {}
704
- value['type'] = 'text'
705
- value['numeric'] = 'false'
706
- value['content'] = ': '
734
+ value = ECValue(type=str, content=': ')
707
735
  command['prompt'] = value
708
736
  if self.peek() == 'with':
709
737
  self.nextToken()
@@ -713,13 +741,18 @@ class Core(Handler):
713
741
  return False
714
742
 
715
743
  def r_input(self, command):
716
- symbolRecord = self.getVariable(command['target'])
717
- prompt = command['prompt']['content']
718
- value = {}
719
- value['type'] = 'text'
720
- value['numeric'] = False
721
- value['content'] = prompt+input(prompt)
722
- self.putSymbolValue(symbolRecord, value)
744
+ record = self.getVariable(command['target'])
745
+ prompt = command['prompt'].getValue()
746
+ value = ECValue(type=str, content=prompt+input(prompt))
747
+ self.putSymbolValue(record, value)
748
+ return self.nextPC()
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):
723
756
  return self.nextPC()
724
757
 
725
758
  # 1 Load a plugin. This is done at compile time.
@@ -733,9 +766,9 @@ class Core(Handler):
733
766
  self.program.importPlugin(f'{source}:{clazz}')
734
767
  return True
735
768
  elif self.isSymbol():
736
- symbolRecord = self.getSymbolRecord()
737
- if symbolRecord['hasValue']:
738
- command['target'] = symbolRecord['name']
769
+ record = self.getSymbolRecord()
770
+ if isinstance(self.getObject(record), (ECVariable, ECDictionary, ECList)):
771
+ command['target'] = record['name']
739
772
  if self.nextIs('from'):
740
773
  if self.nextIsSymbol():
741
774
  record = self.getSymbolRecord()
@@ -759,7 +792,7 @@ class Core(Handler):
759
792
  target = self.getVariable(command['target'])
760
793
  if 'ssh' in command:
761
794
  ssh = self.getVariable(command['ssh'])
762
- path = self.getRuntimeValue(command['path'])
795
+ path = self.textify(command['path'])
763
796
  sftp = ssh['sftp']
764
797
  try:
765
798
  with sftp.open(path, 'r') as remote_file: content = remote_file.read().decode()
@@ -771,13 +804,9 @@ class Core(Handler):
771
804
  else:
772
805
  RuntimeError(self.program, f'Error: {errorReason}')
773
806
  else:
774
- filename = self.getRuntimeValue(command['file'])
807
+ filename = self.textify(command['file'])
775
808
  try:
776
809
  with open(filename) as f: content = f.read()
777
- try:
778
- if filename.endswith('.json'): content = json.loads(content)
779
- except:
780
- errorReason = 'Bad or null JSON string'
781
810
  except:
782
811
  errorReason = f'Unable to read from {filename}'
783
812
 
@@ -787,17 +816,20 @@ class Core(Handler):
787
816
  return command['or']
788
817
  else:
789
818
  RuntimeError(self.program, f'Error: {errorReason}')
790
- value = {}
791
- value['type'] = 'text'
792
- value['content'] = content
793
- 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']
794
826
  return self.nextPC()
795
827
 
796
828
  # Lock a variable
797
829
  def k_lock(self, command):
798
830
  if self.nextIsSymbol():
799
- symbolRecord = self.getSymbolRecord()
800
- command['target'] = symbolRecord['name']
831
+ record = self.getSymbolRecord()
832
+ command['target'] = record['name']
801
833
  self.add(command)
802
834
  return True
803
835
  return False
@@ -815,88 +847,91 @@ class Core(Handler):
815
847
 
816
848
  # Declare a module variable
817
849
  def k_module(self, command):
818
- return self.compileVariable(command)
850
+ self.compiler.addValueType()
851
+ return self.compileVariable(command, 'ECModule')
819
852
 
820
853
  def r_module(self, command):
821
854
  return self.nextPC()
822
855
 
823
856
  # Arithmetic multiply
824
- # multiply {variable} by {value}[ giving {variable}]}
857
+ # multiply {variable} by {value}
858
+ # multiply {value1} by {value2} giving {variable}
825
859
  def k_multiply(self, command):
826
- # Get the (first) value
827
- command['value1'] = self.nextValue()
828
- if self.nextToken() == 'by':
829
- command['value2'] = self.nextValue()
830
- if self.peek() == 'giving':
831
- self.nextToken()
832
- if (self.nextIsSymbol()):
833
- command['target'] = self.getToken()
834
- self.add(command)
835
- return True
836
- FatalError(self.compiler, 'Symbol expected')
837
- else:
838
- # First value must be a variable
839
- if command['value1']['type'] == 'symbol':
840
- command['target'] = command['value1']['name']
841
- self.add(command)
842
- return True
843
- FatalError(self.compiler, 'First value must be a variable')
860
+ # Get the (first) item. If it's a symbol, it may be the target variable
861
+ if self.nextIsSymbol():
862
+ record = self.getSymbolRecord()
863
+ self.checkObjectType(record, ECVariable)
864
+ # Hold onto the variable and its value
865
+ variable1 = record['name']
866
+ value1 = self.getValue()
867
+ else:
868
+ # Here we have a value
869
+ value1 = self.getValue()
870
+ variable1 = None
871
+ self.skip('by')
872
+ command['value2'] = self.nextValue()
873
+ # if 'giving' comes next, the target is the next value
874
+ if self.peek() == 'giving':
875
+ self.nextToken()
876
+ if self.nextIsSymbol():
877
+ record = self.getSymbolRecord()
878
+ self.checkObjectType(record, ECVariable)
879
+ command['target'] = record['name']
880
+ command['value1'] = value1
881
+ self.add(command)
882
+ return True
883
+ else:
884
+ # Here the first variable is the target
885
+ if variable1 != None:
886
+ command['target'] = variable1
887
+ self.add(command)
888
+ return True
844
889
  return False
845
890
 
846
891
  def r_multiply(self, command):
847
- value1 = command['value1']
848
- try:
849
- value2 = command['value2']
850
- except:
851
- value2 = None
892
+ value1 = self.textify(command['value1']) if 'value1' in command else None
893
+ value2 = self.textify(command['value2'])
852
894
  target = self.getVariable(command['target'])
853
- if not target['hasValue']:
854
- self.variableDoesNotHoldAValueError(target['name'])
855
- return None
856
- value = self.getSymbolValue(target)
857
- if value == None:
858
- value = {}
859
- value['type'] = 'int'
860
- if value2:
861
- v1 = int(self.getRuntimeValue(value1))
862
- v2 = int(self.getRuntimeValue(value2))
863
- value['content'] = v1*v2
895
+ # Check that the target variable can hold a value
896
+ self.checkObjectType(target, ECVariable)
897
+ # If value1 exists, we are adding two values and storing the result in target
898
+ if value1 != None:
899
+ # multiply X by Y giving Z
900
+ targetValue = ECValue(type=int, content=int(value1) * int(value2))
864
901
  else:
865
- if value['type'] != 'int' and value['content'] != None:
866
- self.nonNumericValueError()
867
- return None
868
- v = int(self.getRuntimeValue(value))
869
- v1 = int(self.getRuntimeValue(value1))
870
- value['content'] = v*v1
871
- self.putSymbolValue(target, value)
902
+ # multiply X by Y
903
+ targetValue = self.getSymbolValue(target)
904
+ targetValue.setContent(int(targetValue.getContent()) * int(value2))
905
+ self.putSymbolValue(target, targetValue)
872
906
  return self.nextPC()
873
907
 
874
908
  # Negate a variable
875
909
  def k_negate(self, command):
876
910
  if self.nextIsSymbol():
877
- symbolRecord = self.getSymbolRecord()
878
- if symbolRecord['hasValue']:
911
+ record = self.getSymbolRecord()
912
+ if record['hasValue']:
879
913
  command['target'] = self.getToken()
880
914
  self.add(command)
881
915
  return True
882
- self.warning(f'Core.negate: Variable {symbolRecord["name"]} does not hold a value')
916
+ self.warning(f'Core.negate: Variable {record["name"]} does not hold a value')
883
917
  return False
884
918
 
885
919
  def r_negate(self, command):
886
- symbolRecord = self.getVariable(command['target'])
887
- if not symbolRecord['hasValue']:
888
- NoValueRuntimeError(self.program, symbolRecord)
920
+ record = self.getVariable(command['target'])
921
+ if not record['hasValue']:
922
+ NoValueRuntimeError(self.program, record)
889
923
  return None
890
- value = self.getSymbolValue(symbolRecord)
924
+ value = self.getSymbolValue(record)
891
925
  if value == None:
892
- RuntimeError(self.program, f'{symbolRecord["name"]} has not been initialised')
893
- value['content'] *= -1
894
- self.putSymbolValue(symbolRecord, value)
926
+ RuntimeError(self.program, f'{record["name"]} has not been initialised')
927
+ value.setContent(value.getContent() * -1)
928
+ self.putSymbolValue(record, value)
895
929
  return self.nextPC()
896
930
 
897
931
  # on message {action}
898
932
  def k_on(self, command):
899
- if self.nextIs('message'):
933
+ token = self.peek()
934
+ if token == 'message':
900
935
  self.nextToken()
901
936
  command['goto'] = 0
902
937
  self.add(command)
@@ -928,10 +963,10 @@ class Core(Handler):
928
963
  # open {file} for reading/writing/appending
929
964
  def k_open(self, command):
930
965
  if self.nextIsSymbol():
931
- symbolRecord = self.getSymbolRecord()
932
- command['target'] = symbolRecord['name']
966
+ record = self.getSymbolRecord()
967
+ command['target'] = record['name']
933
968
  command['path'] = self.nextValue()
934
- if symbolRecord['keyword'] == 'file':
969
+ if record['keyword'] == 'file':
935
970
  if self.peek() == 'for':
936
971
  self.nextToken()
937
972
  token = self.nextToken()
@@ -956,39 +991,43 @@ class Core(Handler):
956
991
  return False
957
992
 
958
993
  def r_open(self, command):
959
- symbolRecord = self.getVariable(command['target'])
960
- path = self.getRuntimeValue(command['path'])
994
+ record = self.getVariable(command['target'])
995
+ path = self.textify(command['path'])
961
996
  if command['mode'] == 'r' and os.path.exists(path) or command['mode'] != 'r':
962
- symbolRecord['file'] = open(path, command['mode'])
997
+ record['file'] = open(path, command['mode'])
963
998
  return self.nextPC()
964
999
  RuntimeError(self.program, f"File {path} does not exist")
965
1000
 
1001
+ # Dummy command to hit a debugger breakpoint
1002
+ def k_pass(self, command):
1003
+ self.add(command)
1004
+ return True
1005
+
1006
+ def r_pass(self, command):
1007
+ return self.nextPC()
1008
+
966
1009
  # Pop a value from a stack
967
1010
  # pop {variable} from {stack}
968
1011
  def k_pop(self, command):
969
1012
  if (self.nextIsSymbol()):
970
- symbolRecord = self.getSymbolRecord()
971
- command['target'] = symbolRecord['name']
1013
+ record = self.getSymbolRecord()
1014
+ self.checkObjectType(record, ECObject)
1015
+ command['target'] = record['name']
972
1016
  if self.peek() == 'from':
973
1017
  self.nextToken()
974
1018
  if self.nextIsSymbol():
975
- command['from'] = self.getToken()
1019
+ record = self.getSymbolRecord()
1020
+ self.checkObjectType(record, ECStack)
1021
+ command['from'] = record['name']
976
1022
  self.add(command)
977
1023
  return True
978
1024
  return False
979
1025
 
980
1026
  def r_pop(self, command):
981
- symbolRecord = self.getVariable(command['target'])
982
- if not symbolRecord['hasValue']:
983
- NoValueRuntimeError(self.program, symbolRecord)
1027
+ record = self.getVariable(command['target'])
984
1028
  stackRecord = self.getVariable(command['from'])
985
- stack = self.getSymbolValue(stackRecord)
986
- v = stack.pop()
987
- self.putSymbolValue(stackRecord, stack)
988
- value = {}
989
- value['type'] = 'int' if type(v) == int else 'text'
990
- value['content'] = v
991
- self.putSymbolValue(symbolRecord, value)
1029
+ value = stackRecord['object'].pop()
1030
+ self.putSymbolValue(record, value)
992
1031
  return self.nextPC()
993
1032
 
994
1033
  # Perform an HTTP POST
@@ -1013,14 +1052,12 @@ class Core(Handler):
1013
1052
 
1014
1053
  def r_post(self, command):
1015
1054
  global errorCode, errorReason
1016
- retval = {}
1017
- retval['type'] = 'text'
1018
- retval['numeric'] = False
1019
- value = self.getRuntimeValue(command['value'])
1020
- url = self.getRuntimeValue(command['url'])
1055
+ retval = ECValue(type=str, content = '')
1056
+ value = self.textify(command['value'])
1057
+ url = self.textify(command['url'])
1021
1058
  try:
1022
1059
  response = requests.post(url, value, timeout=5)
1023
- retval['content'] = response.text
1060
+ retval.setContent(response.text) # type: ignore
1024
1061
  if response.status_code >= 400:
1025
1062
  errorCode = response.status_code
1026
1063
  errorReason = response.reason
@@ -1052,7 +1089,7 @@ class Core(Handler):
1052
1089
  return False
1053
1090
 
1054
1091
  def r_print(self, command):
1055
- value = self.getRuntimeValue(command['value'])
1092
+ value = self.textify(command['value'])
1056
1093
  program = command['program']
1057
1094
  code = program.code[program.pc]
1058
1095
  lino = str(code['lino'] + 1)
@@ -1073,38 +1110,32 @@ class Core(Handler):
1073
1110
  if peekValue in ['onto', 'to']:
1074
1111
  self.nextToken()
1075
1112
  if self.nextIsSymbol():
1076
- symbolRecord = self.getSymbolRecord()
1077
- command['to'] = symbolRecord['name']
1113
+ record = self.getSymbolRecord()
1114
+ command['to'] = record['name']
1078
1115
  self.add(command)
1079
1116
  return True
1080
1117
  return False
1081
1118
 
1082
1119
  def r_push(self, command):
1083
- value = deepcopy(self.getRuntimeValue(command['value']))
1120
+ value = deepcopy(self.evaluate(command['value']))
1084
1121
  stackRecord = self.getVariable(command['to'])
1085
- if stackRecord['keyword'] != 'stack':
1086
- RuntimeError(self.program, f'{stackRecord["name"]} is not a stack')
1087
- return -1
1088
- stack = stackRecord['value'][stackRecord['index']]
1089
- if stack == None:
1090
- stack = [value]
1091
- else:
1092
- stack.append(value)
1093
- self.putSymbolValue(stackRecord, stack)
1122
+ stackRecord['object'].push(value)
1094
1123
  return self.nextPC()
1095
1124
 
1096
- # put {value} into {variable}
1125
+ # put {value} into {variable/dictionary/list}
1097
1126
  def k_put(self, command):
1098
1127
  value = self.nextValue()
1099
1128
  if value != None:
1100
1129
  command['value'] = value
1130
+ valueType = value.getType()
1101
1131
  if self.nextIs('into'):
1102
1132
  if self.nextIsSymbol():
1103
- symbolRecord = self.getSymbolRecord()
1104
- command['target'] = symbolRecord['name']
1105
- if 'hasValue' in symbolRecord and symbolRecord['hasValue'] == False:
1106
- FatalError(self.compiler, f'Symbol {symbolRecord["name"]} is not a value holder')
1107
- else:
1133
+ record = self.getSymbolRecord()
1134
+ command['target'] = record['name']
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))):
1108
1139
  command['or'] = None
1109
1140
  self.processOr(command, self.getCodeSize())
1110
1141
  return True
@@ -1114,16 +1145,8 @@ class Core(Handler):
1114
1145
 
1115
1146
  def r_put(self, command):
1116
1147
  value = self.evaluate(command['value'])
1117
- if value == None:
1118
- if command['or'] != None:
1119
- return command['or']
1120
- else:
1121
- RuntimeError(self.program, f'Error: could not compute value')
1122
- symbolRecord = self.getVariable(command['target'])
1123
- if not symbolRecord['hasValue']:
1124
- NoValueRuntimeError(self.program, symbolRecord)
1125
- return -1
1126
- self.putSymbolValue(symbolRecord, value)
1148
+ record = self.getVariable(command['target'])
1149
+ self.putSymbolValue(record, value)
1127
1150
  return self.nextPC()
1128
1151
 
1129
1152
  # Read from a file
@@ -1135,41 +1158,38 @@ class Core(Handler):
1135
1158
  else:
1136
1159
  command['line'] = False
1137
1160
  if self.nextIsSymbol():
1138
- symbolRecord = self.getSymbolRecord()
1139
- if symbolRecord['hasValue']:
1140
- if self.peek() == 'from':
1141
- self.nextToken()
1142
- if self.nextIsSymbol():
1143
- fileRecord = self.getSymbolRecord()
1144
- if fileRecord['keyword'] == 'file':
1145
- command['target'] = symbolRecord['name']
1146
- command['file'] = fileRecord['name']
1147
- self.add(command)
1148
- return True
1149
- FatalError(self.compiler, f'Symbol "{symbolRecord["name"]}" is not a value holder')
1161
+ record = self.getSymbolRecord()
1162
+ self.checkObjectType(self.getObject(record), ECVariable)
1163
+ if self.peek() == 'from':
1164
+ self.nextToken()
1165
+ if self.nextIsSymbol():
1166
+ fileRecord = self.getSymbolRecord()
1167
+ self.checkObjectType(fileRecord['object'], ECFile)
1168
+ command['target'] = record['name']
1169
+ command['file'] = fileRecord['name']
1170
+ self.add(command)
1171
+ return True
1150
1172
  return False
1151
1173
  FatalError(self.compiler, f'Symbol "{self.getToken()}" has not been declared')
1152
1174
  return False
1153
1175
 
1154
1176
  def r_read(self, command):
1155
- symbolRecord = self.getVariable(command['target'])
1177
+ record = self.getVariable(command['target'])
1156
1178
  fileRecord = self.getVariable(command['file'])
1157
1179
  line = command['line']
1158
1180
  file = fileRecord['file']
1159
1181
  if file.mode == 'r':
1160
- value = {}
1161
1182
  content = file.readline().split('\n')[0] if line else file.read()
1162
- value['type'] = 'text'
1163
- value['numeric'] = False
1164
- value['content'] = content
1165
- self.putSymbolValue(symbolRecord, value)
1183
+ value = ECValue(type=str, content=content)
1184
+ self.putSymbolValue(record, value)
1166
1185
  return self.nextPC()
1167
1186
 
1168
1187
  # Release the parent script
1169
1188
  def k_release(self, command):
1170
1189
  if self.nextIs('parent'):
1171
1190
  self.add(command)
1172
- return True
1191
+ return True
1192
+ return False
1173
1193
 
1174
1194
  def r_release(self, command):
1175
1195
  self.program.releaseParent()
@@ -1194,23 +1214,35 @@ class Core(Handler):
1194
1214
 
1195
1215
  def r_replace(self, command):
1196
1216
  templateRecord = self.getVariable(command['target'])
1197
- content = self.getSymbolValue(templateRecord)['content']
1198
- original = self.getRuntimeValue(command['original'])
1199
- replacement = self.getRuntimeValue(command['replacement'])
1217
+ content = self.getSymbolValue(templateRecord).getContent()
1218
+ original = self.textify(command['original'])
1219
+ replacement = self.textify(command['replacement'])
1200
1220
  content = content.replace(original, str(replacement))
1201
- value = {}
1202
- value['type'] = 'text'
1203
- value['numeric'] = False
1204
- value['content'] = content
1221
+ value = ECValue(type=str, content=content)
1205
1222
  self.putSymbolValue(templateRecord, value)
1206
1223
  return self.nextPC()
1207
1224
 
1225
+ # Reset a variable
1226
+ def k_reset(self, command):
1227
+ if self.nextIsSymbol():
1228
+ record = self.getSymbolRecord()
1229
+ command['target'] = record['name']
1230
+ self.add(command)
1231
+ return True
1232
+ return False
1233
+
1234
+ def r_reset(self, command):
1235
+ record = self.getVariable(command['target'])
1236
+ self.getObject(record).reset()
1237
+ return self.nextPC()
1238
+
1208
1239
  # Return from subroutine
1209
1240
  def k_return(self, command):
1210
1241
  self.add(command)
1211
1242
  return True
1212
1243
 
1213
1244
  def r_return(self, command):
1245
+ self.program.debugSkip = False
1214
1246
  return self.stack.pop()
1215
1247
 
1216
1248
  # Compile and run a script
@@ -1246,15 +1278,15 @@ class Core(Handler):
1246
1278
 
1247
1279
  def r_run(self, command):
1248
1280
  module = self.getVariable(command['module'])
1249
- path = self.getRuntimeValue(command['path'])
1281
+ path = self.textify(command['path'])
1250
1282
  exports = json.loads(command['exports'])
1251
1283
  for n in range(0, len(exports)):
1252
1284
  exports[n] = self.getVariable(exports[n])
1253
1285
  module['path'] = path
1254
- parent = Object()
1255
- parent.program = self.program
1256
- parent.pc = self.nextPC()
1257
- parent.waiting = True
1286
+ parent = ECValue()
1287
+ parent.program = self.program # type: ignore
1288
+ parent.pc = self.nextPC() # type: ignore
1289
+ parent.waiting = True # type: ignore
1258
1290
  p = self.program.__class__
1259
1291
  p(path).start(parent, module, exports)
1260
1292
  return 0
@@ -1268,7 +1300,6 @@ class Core(Handler):
1268
1300
  if record['keyword'] == 'ssh':
1269
1301
  command['ssh'] = record['name']
1270
1302
  command['path'] = self.nextValue()
1271
- self.add(command)
1272
1303
  else:
1273
1304
  command['file'] = self.getValue()
1274
1305
  else:
@@ -1280,10 +1311,10 @@ class Core(Handler):
1280
1311
 
1281
1312
  def r_save(self, command):
1282
1313
  errorReason = None
1283
- content = self.getRuntimeValue(command['content'])
1314
+ content = self.textify(command['content'])
1284
1315
  if 'ssh' in command:
1285
1316
  ssh = self.getVariable(command['ssh'])
1286
- path = self.getRuntimeValue(command['path'])
1317
+ path = self.textify(command['path'])
1287
1318
  sftp = ssh['sftp']
1288
1319
  if path.endswith('.json'): content = json.dumps(content)
1289
1320
  try:
@@ -1296,12 +1327,17 @@ class Core(Handler):
1296
1327
  else:
1297
1328
  RuntimeError(self.program, f'Error: {errorReason}')
1298
1329
  else:
1299
- filename = self.getRuntimeValue(command['file'])
1300
- if filename.endswith('.json'): content = json.dumps(content)
1330
+ filename = self.textify(command['file'])
1301
1331
  try:
1332
+ if content == None:
1333
+ content = ''
1334
+ elif isinstance(content, dict) or isinstance(content, list):
1335
+ content = json.dumps(content)
1336
+ elif not isinstance(content, str):
1337
+ content = self.textify(content)
1302
1338
  with open(filename, 'w') as f: f.write(content)
1303
- except:
1304
- errorReason = f'Unable to write to {filename}'
1339
+ except Exception as e:
1340
+ errorReason = f'Unable to write to {filename}: {str(e)}'
1305
1341
 
1306
1342
  if errorReason:
1307
1343
  if command['or'] != None:
@@ -1322,32 +1358,30 @@ class Core(Handler):
1322
1358
  if self.nextIs('to'):
1323
1359
  if self.nextIsSymbol():
1324
1360
  record = self.getSymbolRecord()
1325
- if record['keyword'] == 'module':
1361
+ if self.isObjectType(record, ECModule):
1326
1362
  command['module'] = record['name']
1327
1363
  self.add(command)
1328
1364
  return True
1329
1365
  return False
1330
1366
 
1331
1367
  def r_send(self, command):
1332
- message = self.getRuntimeValue(command['message'])
1368
+ message = self.textify(command['message'])
1333
1369
  module = self.getVariable(command['module'])
1334
1370
  module['child'].handleMessage(message)
1335
1371
  return self.nextPC()
1336
1372
 
1337
1373
  # Set a value
1338
1374
  # set {variable}
1375
+ # set {variable} to {value}
1339
1376
  # set {ssh} host {host} user {user} password {password}
1340
- # set the elements of {variable} to {value}
1341
- # 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}
1379
+ # set breakpoint
1342
1380
  def k_set(self, command):
1343
1381
  if self.nextIsSymbol():
1344
1382
  record = self.getSymbolRecord()
1345
1383
  command['target'] = record['name']
1346
- if record['hasValue']:
1347
- command['type'] = 'set'
1348
- self.add(command)
1349
- return True
1350
- elif record['keyword'] == 'ssh':
1384
+ if record['keyword'] == 'ssh':
1351
1385
  host = None
1352
1386
  user = None
1353
1387
  password = None
@@ -1369,7 +1403,18 @@ class Core(Handler):
1369
1403
  command['type'] = 'ssh'
1370
1404
  self.add(command)
1371
1405
  return True
1372
-
1406
+ elif isinstance(self.getObject(record), ECVariable):
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'
1416
+ self.add(command)
1417
+ return True
1373
1418
  return False
1374
1419
 
1375
1420
  token = self.getToken()
@@ -1379,7 +1424,7 @@ class Core(Handler):
1379
1424
 
1380
1425
  if token == 'elements':
1381
1426
  self.nextToken()
1382
- if self.peek() == 'of':
1427
+ if self.peek() in ('in', 'of'):
1383
1428
  self.nextToken()
1384
1429
  if self.nextIsSymbol():
1385
1430
  command['name'] = self.getToken()
@@ -1395,11 +1440,14 @@ class Core(Handler):
1395
1440
  self.add(command)
1396
1441
  return True
1397
1442
 
1398
- elif token == 'property':
1399
- command['name'] = self.nextValue()
1443
+ elif token in ('entry', 'property'):
1444
+ command['key'] = self.nextValue()
1400
1445
  if self.nextIs('of'):
1401
1446
  if self.nextIsSymbol():
1402
- 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']
1403
1451
  if self.nextIs('to'):
1404
1452
  value = self.nextValue()
1405
1453
  if value == None:
@@ -1408,7 +1456,7 @@ class Core(Handler):
1408
1456
  self.add(command)
1409
1457
  return True
1410
1458
 
1411
- elif token == 'element':
1459
+ elif token == 'item':
1412
1460
  command['index'] = self.nextValue()
1413
1461
  if self.nextIs('of'):
1414
1462
  if self.nextIsSymbol():
@@ -1422,6 +1470,11 @@ class Core(Handler):
1422
1470
  command['path'] = self.nextValue()
1423
1471
  self.add(command)
1424
1472
  return True
1473
+
1474
+ elif token == 'breakpoint':
1475
+ command['breakpoint'] = True
1476
+ self.add(command)
1477
+ return True
1425
1478
 
1426
1479
  return False
1427
1480
 
@@ -1429,81 +1482,70 @@ class Core(Handler):
1429
1482
  cmdType = command['type']
1430
1483
  if cmdType == 'set':
1431
1484
  target = self.getVariable(command['target'])
1432
- val = {}
1433
- val['type'] = 'boolean'
1434
- val['content'] = True
1435
- self.putSymbolValue(target, val)
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)
1436
1492
  return self.nextPC()
1437
1493
 
1438
1494
  elif cmdType == 'elements':
1439
- symbolRecord = self.getVariable(command['name'])
1440
- elements = self.getRuntimeValue(command['elements'])
1441
- currentElements = symbolRecord['elements']
1442
- currentValue = symbolRecord['value']
1443
- if currentValue == None:
1444
- currentValue = [None]
1445
- newValue = [None] * elements
1446
- if elements > currentElements:
1447
- for index, value in enumerate(currentValue):
1448
- newValue[index] = value
1449
- elif elements < currentElements:
1450
- for index, value in enumerate(currentValue):
1451
- if index < elements:
1452
- newValue[index] = value
1453
- symbolRecord['elements'] = elements
1454
- symbolRecord['value'] = newValue
1455
- symbolRecord['index'] = 0
1495
+ record = self.getVariable(command['name'])
1496
+ elements = self.textify(command['elements'])
1497
+ object = self.getObject(record)
1498
+ self.checkObjectType(object, ECObject)
1499
+ object.setElements(elements)
1456
1500
  return self.nextPC()
1457
1501
 
1458
- elif cmdType == 'element':
1459
- value = self.getRuntimeValue(command['value'])
1460
- index = self.getRuntimeValue(command['index'])
1461
- target = self.getVariable(command['target'])
1462
- val = self.getSymbolValue(target)
1463
- content = val['content']
1464
- if content == '':
1465
- content = []
1466
- # else:
1467
- # content = json.loads(content)
1468
- content[index] = value
1469
- val['content'] = content
1470
- self.putSymbolValue(target, val)
1502
+ elif cmdType == 'item':
1503
+ index = self.textify(command['index'])
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)
1471
1509
  return self.nextPC()
1472
1510
 
1473
1511
  elif cmdType == 'encoding':
1474
- self.encoding = self.getRuntimeValue(command['encoding'])
1512
+ self.encoding = self.textify(command['encoding'])
1475
1513
  return self.nextPC()
1476
1514
 
1477
1515
  elif cmdType == 'path':
1478
- path = self.getRuntimeValue(command['path'])
1516
+ path = self.textify(command['path'])
1479
1517
  os.chdir(path)
1480
1518
  return self.nextPC()
1481
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
+
1482
1529
  elif cmdType == 'property':
1483
- value = self.getRuntimeValue(command['value'])
1484
- name = self.getRuntimeValue(command['name'])
1485
- target = command['target']
1486
- targetVariable = self.getVariable(target)
1487
- val = self.getSymbolValue(targetVariable)
1488
- try:
1489
- content = val['content']
1490
- except:
1491
- RuntimeError(self.program, f'{target} is not an object')
1492
- if content == '':
1493
- content = {}
1494
- try:
1495
- content[name] = value
1496
- except:
1497
- RuntimeError(self.program, f'{target} is not an object')
1498
- val['content'] = content
1499
- self.putSymbolValue(targetVariable, val)
1530
+ key = self.textify(command['key'])
1531
+ value = self.evaluate(command['value'])
1532
+ record = self.getVariable(command['target'])
1533
+ variable = self.getObject(record)
1534
+ variable.setProperty(key, value)
1535
+ content = variable.getContent()
1536
+ if content == None: content = {}
1537
+ elif not isinstance(content, dict):
1538
+ raise RuntimeError(self.program, f'{record["name"]} is not a dictionary')
1539
+ if isinstance(value, dict): content[key] = value
1540
+ else: content[key] = self.textify(value)
1541
+ variable.setContent(ECValue(type='dict', content=content))
1500
1542
  return self.nextPC()
1501
1543
 
1502
1544
  elif cmdType == 'ssh':
1503
1545
  target = self.getVariable(command['target'])
1504
- host = self.getRuntimeValue(command['host'])
1505
- user = self.getRuntimeValue(command['user'])
1506
- password = self.getRuntimeValue(command['password'])
1546
+ host = self.textify(command['host'])
1547
+ user = self.textify(command['user'])
1548
+ password = self.textify(command['password'])
1507
1549
  ssh = paramiko.SSHClient()
1508
1550
  target['ssh'] = ssh
1509
1551
  ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
@@ -1513,50 +1555,51 @@ class Core(Handler):
1513
1555
  except:
1514
1556
  target['error'] = f'Unable to connect to {host} (timeout)'
1515
1557
  return self.nextPC()
1558
+
1559
+ elif cmdType == 'breakpoint':
1560
+ self.program.breakpoint = True
1561
+ return self.nextPC()
1516
1562
 
1517
- # Shuffle a list
1563
+ # Shuffle a JSON list
1518
1564
  def k_shuffle(self, command):
1519
1565
  if self.nextIsSymbol():
1520
- symbolRecord = self.getSymbolRecord()
1521
- if symbolRecord['hasValue']:
1566
+ record = self.getSymbolRecord()
1567
+ if record['hasValue']:
1522
1568
  command['target'] = self.getToken()
1523
1569
  self.add(command)
1524
1570
  return True
1525
- self.warning(f'Core.negate: Variable {symbolRecord["name"]} does not hold a value')
1571
+ self.warning(f'Core.negate: Variable {record["name"]} does not hold a value')
1526
1572
  return False
1527
1573
 
1528
1574
  def r_shuffle(self, command):
1529
- symbolRecord = self.getVariable(command['target'])
1530
- if not symbolRecord['hasValue']:
1531
- NoValueRuntimeError(self.program, symbolRecord)
1575
+ record = self.getVariable(command['target'])
1576
+ if not record['hasValue']:
1577
+ NoValueRuntimeError(self.program, record)
1532
1578
  return None
1533
- value = self.getSymbolValue(symbolRecord)
1579
+ value = self.getSymbolValue(record)
1534
1580
  if value == None:
1535
- RuntimeError(self.program, f'{symbolRecord["name"]} has not been initialised')
1536
- content = value['content']
1581
+ RuntimeError(self.program, f'{record["name"]} has not been initialised')
1582
+ content = value.getContent()
1537
1583
  if isinstance(content, list):
1538
1584
  random.shuffle(content)
1539
- value['content'] = content
1540
- self.putSymbolValue(symbolRecord, value)
1585
+ value.setContent(content)
1586
+ self.putSymbolValue(record, value)
1541
1587
  return self.nextPC()
1542
- RuntimeError(self.program, f'{symbolRecord["name"]} is not a list')
1588
+ RuntimeError(self.program, f'{record["name"]} is not a list')
1543
1589
 
1544
1590
  # Split a string into a variable with several elements
1545
1591
  # split {variable} on {value}
1546
1592
  def k_split(self, command):
1547
1593
  if self.nextIsSymbol():
1548
- symbolRecord = self.getSymbolRecord()
1549
- if symbolRecord['hasValue']:
1550
- command['target'] = symbolRecord['name']
1551
- value = {}
1552
- value['type'] = 'text'
1553
- value['numeric'] = 'false'
1554
- value['content'] = '\n'
1594
+ record = self.getSymbolRecord()
1595
+ if isinstance(record['object'], ECObject):
1596
+ command['target'] = record['name']
1597
+ value = ECValue(type=str, content='\n')
1555
1598
  command['on'] = value
1556
1599
  if self.peek() == 'on':
1557
1600
  self.nextToken()
1558
1601
  if self.peek() == 'tab':
1559
- value['content'] = '\t'
1602
+ value.setContent('\t')
1560
1603
  self.nextToken()
1561
1604
  else:
1562
1605
  command['on'] = self.nextValue()
@@ -1568,30 +1611,31 @@ class Core(Handler):
1568
1611
  def r_split(self, command):
1569
1612
  target = self.getVariable(command['target'])
1570
1613
  value = self.getSymbolValue(target)
1571
- content = value['content'].split(self.getRuntimeValue(command['on']))
1614
+ content = value.getContent().split(self.textify(command['on']))
1572
1615
  elements = len(content)
1573
- target['elements'] = elements
1574
- target['index'] = 0
1575
- target['value'] = [None] * elements
1576
-
1577
- for index, item in enumerate(content):
1578
- element = {}
1579
- element['type'] = 'text'
1580
- element['numeric'] = 'false'
1581
- element['content'] = item
1582
- target['value'][index] = element
1616
+ object = target['object']
1617
+ object.setElements(elements)
1618
+
1619
+ for n in range(0, elements):
1620
+ val = ECValue(type=str, content=content[n])
1621
+ object.setIndex(n)
1622
+ object.setValue(val)
1623
+ object.setIndex(0)
1583
1624
 
1584
1625
  return self.nextPC()
1585
1626
 
1627
+ # Declare an SSH connection variable
1586
1628
  def k_ssh(self, command):
1587
- return self.compileVariable(command)
1629
+ self.compiler.addValueType()
1630
+ return self.compileVariable(command, 'ECSSH')
1588
1631
 
1589
1632
  def r_ssh(self, command):
1590
1633
  return self.nextPC()
1591
1634
 
1592
1635
  # Declare a stack variable
1593
1636
  def k_stack(self, command):
1594
- return self.compileVariable(command)
1637
+ self.compiler.addValueType()
1638
+ return self.compileVariable(command, 'ECStack')
1595
1639
 
1596
1640
  def r_stack(self, command):
1597
1641
  return self.nextPC()
@@ -1622,7 +1666,7 @@ class Core(Handler):
1622
1666
  return False
1623
1667
 
1624
1668
  def r_system(self, command):
1625
- value = self.getRuntimeValue(command['value'])
1669
+ value = self.textify(command['value'])
1626
1670
  if value != None:
1627
1671
  if command['background']:
1628
1672
  subprocess.Popen(["sh",value,"&"])
@@ -1631,82 +1675,77 @@ class Core(Handler):
1631
1675
  return self.nextPC()
1632
1676
 
1633
1677
  # Arithmetic subtraction
1634
- # take {value} from {variable}[ giving {variable}]}
1678
+ # take {value} from {variable}
1679
+ # take {value1} from {value2} giving {variable}
1635
1680
  def k_take(self, command):
1636
1681
  # Get the (first) value
1637
1682
  command['value1'] = self.nextValue()
1638
- if self.nextToken() == 'from':
1639
- if self.nextIsSymbol():
1640
- symbolRecord = self.getSymbolRecord()
1641
- if symbolRecord['hasValue']:
1642
- if self.peek() == 'giving':
1643
- # This variable must be treated as a second value
1644
- command['value2'] = self.getValue()
1645
- self.nextToken()
1646
- command['target'] = self.nextToken()
1647
- self.add(command)
1648
- return True
1649
- else:
1650
- # Here the variable is the target
1651
- command['target'] = self.getToken()
1652
- self.add(command)
1653
- return True
1654
- self.warning(f'Core.take: Expected value holder')
1683
+ self.skip('from')
1684
+ if self.nextIsSymbol():
1685
+ record = self.getSymbolRecord()
1686
+ self.checkObjectType(record, ECObject)
1687
+ # If 'giving' comes next, this variable is the second value
1688
+ if self.peek() == 'giving':
1689
+ v2 = ECValue(type='symbol')
1690
+ v2.setContent(record['name'])
1691
+ command['value2'] = v2
1692
+ self.nextToken()
1693
+ # Now get the target variable
1694
+ if self.nextIsSymbol():
1695
+ record = self.getSymbolRecord()
1696
+ self.checkObjectType(record, ECVariable)
1697
+ command['target'] = record['name']
1698
+ self.add(command)
1699
+ return True
1655
1700
  else:
1656
- # Here we have 2 values so 'giving' must come next
1657
- command['value2'] = self.getValue()
1658
- if self.nextToken() == 'giving':
1659
- if (self.nextIsSymbol()):
1660
- command['target'] = self.getToken()
1661
- self.add(command)
1662
- return True
1663
- else:
1664
- FatalError(self.compiler, f'\'{self.getToken()}\' is not a symbol')
1665
- else:
1666
- self.warning(f'Core.take: Expected "giving"')
1701
+ # Here the variable is the target
1702
+ command['target'] = record['name']
1703
+ self.add(command)
1704
+ return True
1705
+ else:
1706
+ # Here we have 2 values so 'giving' must come next
1707
+ command['value2'] = self.getValue()
1708
+ if self.nextToken() == 'giving':
1709
+ if self.nextIsSymbol():
1710
+ record = self.getSymbolRecord()
1711
+ self.checkObjectType(record, ECVariable)
1712
+ command['target'] = record['name']
1713
+ self.add(command)
1714
+ return True
1715
+ raise FatalError(self.compiler, 'Cannot subtract values: target variable expected')
1667
1716
  return False
1668
1717
 
1669
1718
  def r_take(self, command):
1670
- value1 = command['value1']
1671
- try:
1672
- value2 = command['value2']
1673
- except:
1674
- value2 = None
1719
+ value1 = self.textify(command['value1'])
1720
+ value2 = self.textify(command['value2']) if 'value2' in command else None
1675
1721
  target = self.getVariable(command['target'])
1676
- if not target['hasValue']:
1677
- self.variableDoesNotHoldAValueError(target['name'])
1678
- return None
1679
- value = self.getSymbolValue(target)
1680
- if value == None:
1681
- value = {}
1682
- value['type'] = 'int'
1683
- if value2:
1684
- v1 = int(self.getRuntimeValue(value1))
1685
- v2 = int(self.getRuntimeValue(value2))
1686
- value['content'] = v2-v1
1722
+ # Check that the target variable can hold a value
1723
+ self.checkObjectType(target, ECVariable)
1724
+ # If value2 exists, we are adding two values and storing the result in target
1725
+ if value2 != None:
1726
+ # take X from Y giving Z
1727
+ targetValue = ECValue(type=int, content=int(value2) - int(value1))
1687
1728
  else:
1688
- v = int(self.getRuntimeValue(value))
1689
- v1 = int(self.getRuntimeValue(value1))
1690
- value['content'] = v-v1
1691
- self.putSymbolValue(target, value)
1729
+ # take X from Y
1730
+ targetValue = self.getSymbolValue(target)
1731
+ targetValue.setContent(int(targetValue.getContent()) - int(value1))
1732
+ self.putSymbolValue(target, targetValue)
1692
1733
  return self.nextPC()
1693
1734
 
1694
1735
  # Toggle a boolean value
1695
1736
  def k_toggle(self, command):
1696
1737
  if self.nextIsSymbol():
1697
1738
  target = self.getSymbolRecord()
1698
- if target['hasValue']:
1699
- command['target'] = target['name']
1700
- self.add(command)
1701
- return True
1739
+ self.checkObjectType(target, ECVariable)
1740
+ command['target'] = target['name']
1741
+ self.add(command)
1742
+ return True
1702
1743
  return False
1703
1744
 
1704
1745
  def r_toggle(self, command):
1705
1746
  target = self.getVariable(command['target'])
1706
1747
  value = self.getSymbolValue(target)
1707
- val = {}
1708
- val['type'] = 'boolean'
1709
- val['content'] = not value['content']
1748
+ val = ECValue(type=bool, content=not value.getContent())
1710
1749
  self.putSymbolValue(target, val)
1711
1750
  self.add(command)
1712
1751
  return self.nextPC()
@@ -1724,9 +1763,9 @@ class Core(Handler):
1724
1763
  def r_trim(self, command):
1725
1764
  record = self.getVariable(command['name'])
1726
1765
  value = record['value'][record['index']]
1727
- if value['type'] == 'text':
1728
- content = value['content']
1729
- value['content'] = content.strip()
1766
+ if value.getType() == str:
1767
+ content = value.getContent()
1768
+ value.setContent(content.strip())
1730
1769
  return self.nextPC()
1731
1770
 
1732
1771
  # Truncate a file
@@ -1747,8 +1786,8 @@ class Core(Handler):
1747
1786
  # Unlock a variable
1748
1787
  def k_unlock(self, command):
1749
1788
  if self.nextIsSymbol():
1750
- symbolRecord = self.getSymbolRecord()
1751
- command['target'] = symbolRecord['name']
1789
+ record = self.getSymbolRecord()
1790
+ command['target'] = record['name']
1752
1791
  self.add(command)
1753
1792
  return True
1754
1793
  return False
@@ -1758,7 +1797,10 @@ class Core(Handler):
1758
1797
  target['locked'] = False
1759
1798
  return self.nextPC()
1760
1799
 
1761
- # Use a plugin module
1800
+ # use plugin {class} from {source}
1801
+ # use graphics
1802
+ # use mqtt
1803
+ # use psutil
1762
1804
  def k_use(self, command):
1763
1805
  if self.peek() == 'plugin':
1764
1806
  # Import a plugin
@@ -1773,12 +1815,20 @@ class Core(Handler):
1773
1815
  token = self.nextToken()
1774
1816
  if token == 'graphics':
1775
1817
  return self.program.useGraphics()
1818
+ if token == 'mqtt':
1819
+ return self.program.useMQTT()
1820
+ elif token == 'psutil':
1821
+ return self.program.usePSUtil()
1776
1822
  return False
1823
+
1824
+ # Unused
1825
+ def r_use(self, command):
1826
+ return self.nextPC()
1777
1827
 
1778
1828
  # Declare a general-purpose variable
1779
1829
  def k_variable(self, command):
1780
1830
  self.compiler.addValueType()
1781
- return self.compileVariable(command)
1831
+ return self.compileVariable(command, 'ECVariable')
1782
1832
 
1783
1833
  def r_variable(self, command):
1784
1834
  return self.nextPC()
@@ -1804,7 +1854,7 @@ class Core(Handler):
1804
1854
  return True
1805
1855
 
1806
1856
  def r_wait(self, command):
1807
- value = self.getRuntimeValue(command['value']) * command['multiplier']
1857
+ value = self.textify(command['value']) * command['multiplier']
1808
1858
  next = self.nextPC()
1809
1859
  threading.Timer(value/1000.0, lambda: (self.run(next))).start()
1810
1860
  return 0
@@ -1870,7 +1920,7 @@ class Core(Handler):
1870
1920
  return False
1871
1921
 
1872
1922
  def r_write(self, command):
1873
- value = self.getRuntimeValue(command['value'])
1923
+ value = self.textify(command['value'])
1874
1924
  fileRecord = self.getVariable(command['file'])
1875
1925
  file = fileRecord['file']
1876
1926
  if file.mode in ['w', 'w+', 'a', 'a+']:
@@ -1883,158 +1933,147 @@ class Core(Handler):
1883
1933
  # Support functions
1884
1934
 
1885
1935
  def incdec(self, command, mode):
1886
- symbolRecord = self.getVariable(command['target'])
1887
- if not symbolRecord['hasValue']:
1888
- NoValueRuntimeError(self.program, symbolRecord)
1889
- value = self.getSymbolValue(symbolRecord)
1890
- if value == None:
1891
- RuntimeError(self.program, f'{symbolRecord["name"]} has not been initialised')
1892
- if mode == '+':
1893
- value['content'] += 1
1894
- else:
1895
- value['content'] -= 1
1896
- self.putSymbolValue(symbolRecord, value)
1936
+ record = self.getVariable(command['target'])
1937
+ self.checkObjectType(record['object'], ECVariable)
1938
+ value = self.getSymbolValue(record)
1939
+ content = value.getContent()
1940
+ if not isinstance(content, int):
1941
+ RuntimeError(self.program, f'Variable {record["name"]} does not hold an integer')
1942
+ if mode == '+': value.setContent(content + 1)
1943
+ else: value.setContent(content - 1)
1944
+ self.putSymbolValue(record, value)
1897
1945
  return self.nextPC()
1898
1946
 
1899
1947
  #############################################################################
1900
1948
  # Compile a value in this domain
1901
1949
  def compileValue(self):
1902
- value = {}
1903
- value['domain'] = self.getName()
1950
+ value = ECValue()
1904
1951
  token = self.getToken()
1905
1952
  if self.isSymbol():
1906
- value['name'] = token
1907
- symbolRecord = self.getSymbolRecord()
1908
- keyword = symbolRecord['keyword']
1909
-
1910
- if keyword == 'module':
1911
- value['type'] = 'module'
1912
- return value
1913
-
1914
- if keyword in ['ssh', 'variable']:
1915
- value['type'] = 'symbol'
1916
- return value
1917
-
1918
- return None
1953
+ value.setValue(type='symbol', content=token)
1954
+ return value
1919
1955
 
1920
- value['type'] = token
1956
+ value.setType(token)
1921
1957
 
1922
1958
  if token == 'arg':
1923
1959
  self.nextToken()
1924
- value['index'] = self.getValue()
1960
+ value.index = self.getValue()
1925
1961
  return value
1926
1962
 
1927
1963
  if token in ['cos', 'sin', 'tan']:
1928
- value['angle'] = self.nextValue()
1964
+ value.angle = self.nextValue()
1929
1965
  if self.nextToken() == 'radius':
1930
- value['radius'] = self.nextValue()
1966
+ value.radius = self.nextValue()
1931
1967
  return value
1932
1968
  return None
1933
1969
 
1934
1970
  if token in ['now', 'today', 'newline', 'tab', 'empty']:
1935
1971
  return value
1936
1972
 
1937
- if token in ['stringify', 'prettify', 'json', 'lowercase', 'uppercase', 'hash', 'random', 'float', 'integer', 'encode', 'decode']:
1938
- value['content'] = self.nextValue()
1973
+ if token in ['stringify', 'prettify', 'json', 'lowercase', 'uppercase', 'hash', 'random', float, 'integer', 'encode', 'decode']:
1974
+ value.setContent(self.nextValue())
1939
1975
  return value
1940
1976
 
1941
1977
  if (token in ['datime', 'datetime']):
1942
- value['type'] = 'datime'
1943
- value['timestamp'] = self.nextValue()
1978
+ value.setType('datime')
1979
+ value.timestamp = self.nextValue()
1944
1980
  if self.peek() == 'format':
1945
1981
  self.nextToken()
1946
- value['format'] = self.nextValue()
1982
+ value.format = self.nextValue()
1947
1983
  else:
1948
- value['format'] = None
1984
+ value.format = None
1949
1985
  return value
1950
1986
 
1951
- if token == 'element':
1952
- value['index'] = self.nextValue()
1987
+ if token == 'item':
1988
+ value.index = self.nextValue()
1953
1989
  if self.nextToken() == 'of':
1954
1990
  if self.nextIsSymbol():
1955
- symbolRecord = self.getSymbolRecord()
1956
- if symbolRecord['hasValue']:
1957
- value['target'] = symbolRecord['name']
1958
- return value
1959
- self.warning(f'Core.compileValue: Token {symbolRecord["name"]} does not hold a value')
1991
+ record = self.getSymbolRecord()
1992
+ self.checkObjectType(record['object'], ECList)
1993
+ value.target = ECValue(type='symbol', content=record['name'])
1994
+ return value
1960
1995
  return None
1961
1996
 
1962
- if token == 'property':
1963
- value['name'] = self.nextValue()
1964
- if self.nextToken() == 'of':
1997
+ if token == 'entry':
1998
+ value.key = self.nextValue() # type: ignore
1999
+ if self.nextToken() in ('in', 'of'):
1965
2000
  if self.nextIsSymbol():
1966
- symbolRecord = self.getSymbolRecord()
1967
- if symbolRecord['hasValue']:
1968
- value['target'] = symbolRecord['name']
1969
- return value
1970
- NoValueError(self.compiler, symbolRecord)
2001
+ record = self.getSymbolRecord()
2002
+ object = record['object']
2003
+ self.checkObjectType(object, ECDictionary)
2004
+ value.target = object.name
2005
+ return value
1971
2006
  return None
1972
2007
 
1973
2008
  if token == 'arg':
1974
- value['content'] = self.nextValue()
2009
+ value.setContent(self.nextValue())
1975
2010
  if self.getToken() == 'of':
1976
2011
  if self.nextIsSymbol():
1977
- symbolRecord = self.getSymbolRecord()
1978
- if symbolRecord['keyword'] == 'variable':
1979
- value['target'] = symbolRecord['name']
2012
+ record = self.getSymbolRecord()
2013
+ if record['keyword'] == 'variable':
2014
+ value.target = record['name'] # type: ignore
1980
2015
  return value
1981
2016
  return None
1982
2017
 
1983
2018
  if token == 'trim':
1984
2019
  self.nextToken()
1985
- value['content'] = self.getValue()
2020
+ value.setContent(self.getValue())
1986
2021
  return value
1987
2022
 
1988
2023
  if self.getToken() == 'the':
1989
2024
  self.nextToken()
1990
2025
 
1991
2026
  token = self.getToken()
1992
- value['type'] = token
2027
+ value.setType(token)
1993
2028
 
1994
- if token == 'args':
1995
- return value
2029
+ if token in ['args', 'message', 'uuid', 'weekday']:
2030
+ return value
1996
2031
 
1997
- if token == 'elements':
1998
- if self.nextIs('of'):
2032
+ if token in ('items', 'elements'):
2033
+ if self.nextToken() in ('in', 'of'):
1999
2034
  if self.nextIsSymbol():
2000
- value['name'] = self.getToken()
2035
+ value.name = self.getToken() # type: ignore
2001
2036
  return value
2002
2037
  return None
2003
2038
 
2004
2039
  if token == 'keys':
2005
2040
  if self.nextIs('of'):
2006
- value['name'] = self.nextValue()
2007
- return value
2041
+ if self.nextIsSymbol():
2042
+ value.name = self.getToken() # type: ignore
2043
+ return value
2008
2044
  return None
2009
2045
 
2010
2046
  if token == 'count':
2011
2047
  if self.nextIs('of'):
2012
2048
  if self.nextIsSymbol():
2013
- if self.getSymbolRecord()['hasValue']:
2014
- value['name'] = self.getToken()
2049
+ record = self.getSymbolRecord()
2050
+ object = record['object']
2051
+ if isinstance(object, ECList):
2052
+ value.setContent(record['name'])
2015
2053
  return value
2016
2054
  return None
2017
2055
 
2018
2056
  if token == 'index':
2019
2057
  if self.nextIs('of'):
2020
2058
  if self.nextIsSymbol():
2021
- value['variable'] = self.getSymbolRecord()['name']
2059
+ value.variable = self.getSymbolRecord()['name'] # type: ignore
2022
2060
  if self.peek() == 'in':
2023
- value['value'] = None
2024
- value['type'] = 'indexOf'
2061
+ value.value = None # type: ignore
2062
+ value.setType('indexOf')
2063
+ self.nextToken()
2025
2064
  if self.nextIsSymbol():
2026
- value['target'] = self.getSymbolRecord()['name']
2065
+ value.target = self.getSymbolRecord()['name'] # type: ignore
2027
2066
  return value
2028
2067
  else:
2029
- value['name'] = self.getToken()
2068
+ value.name = self.getToken() # type: ignore
2030
2069
  return value
2031
2070
  else:
2032
- value['value'] = self.getValue()
2071
+ value.value = self.getValue() # type: ignore
2033
2072
  if self.nextIs('in'):
2034
- value['variable'] = None
2035
- value['type'] = 'indexOf'
2073
+ value.variable = None # type: ignore
2074
+ value.setType('indexOf')
2036
2075
  if self.nextIsSymbol():
2037
- value['target'] = self.getSymbolRecord()['name']
2076
+ value.target = self.getSymbolRecord()['name'] # type: ignore
2038
2077
  return value
2039
2078
  return None
2040
2079
 
@@ -2042,111 +2081,101 @@ class Core(Handler):
2042
2081
  if self.nextIs('of'):
2043
2082
  v = self.nextValue()
2044
2083
  if v !=None:
2045
- value['type'] = 'valueOf'
2046
- value['content'] = v
2084
+ value.setValue(type='valueOf', content=v)
2047
2085
  return value
2048
2086
  return None
2049
2087
 
2050
2088
  if token == 'length':
2051
- value['type'] = 'lengthOf'
2089
+ value.setType('lengthOf')
2052
2090
  if self.nextIs('of'):
2053
- value['content'] = self.nextValue()
2091
+ value.setContent(self.nextValue())
2054
2092
  return value
2055
2093
  return None
2056
2094
 
2057
2095
  if token in ['left', 'right']:
2058
- value['count'] = self.nextValue()
2096
+ value.count = self.nextValue() # type: ignore
2059
2097
  if self.nextToken() == 'of':
2060
- value['content'] = self.nextValue()
2098
+ value.setContent(self.nextValue())
2061
2099
  return value
2062
2100
  return None
2063
2101
 
2102
+ # from {n} of {value}
2103
+ # from {n} to {m} of {value}
2064
2104
  if token == 'from':
2065
- value['start'] = self.nextValue()
2105
+ value.start = self.nextValue() # type: ignore
2066
2106
  if self.peek() == 'to':
2067
2107
  self.nextToken()
2068
- value['to'] = self.nextValue()
2108
+ value.to = self.nextValue() # type: ignore
2069
2109
  else:
2070
- value['to'] = None
2110
+ value.to = None # type: ignore
2071
2111
  if self.nextToken() == 'of':
2072
- value['content'] = self.nextValue()
2112
+ value.setContent(self.nextValue())
2073
2113
  return value
2074
2114
 
2115
+ # position of [the] [last] {needle} in {haystack}
2075
2116
  if token == 'position':
2076
- if self.nextIs('of'):
2077
- value['last'] = False
2078
- if self.nextIs('the'):
2079
- if self.nextIs('last'):
2080
- self.nextToken()
2081
- value['last'] = True
2082
- value['needle'] = self.getValue()
2083
- if self.nextToken() == 'in':
2084
- value['haystack'] = self.nextValue()
2085
- return value
2086
-
2087
- if token == 'message':
2117
+ self.skip('of')
2118
+ self.skip('the')
2119
+ if self.peek() == 'last':
2120
+ value.last = True # type: ignore
2121
+ self.nextToken()
2122
+ value.needle = self.nextValue() # type: ignore
2123
+ self.skip('in')
2124
+ value.haystack = self.nextValue() # type: ignore
2088
2125
  return value
2089
2126
 
2090
2127
  if token == 'timestamp':
2091
- value['format'] = None
2128
+ value.format = None # type: ignore
2092
2129
  if self.peek() == 'of':
2093
2130
  self.nextToken()
2094
- value['datime'] = self.nextValue()
2131
+ value.timestamp = self.nextValue() # type: ignore
2095
2132
  if self.peek() == 'format':
2096
2133
  self.nextToken()
2097
- value['format'] = self.nextValue()
2134
+ value.format = self.nextValue() # type: ignore
2098
2135
  return value
2099
2136
 
2100
2137
  if token == 'files':
2101
2138
  token = self.nextToken()
2102
2139
  if token in ['in', 'of']:
2103
- value['target'] = self.nextValue()
2140
+ value.target = self.nextValue() # type: ignore
2104
2141
  return value
2105
2142
  return None
2106
2143
 
2107
- if token == 'weekday':
2108
- value['type'] = 'weekday'
2109
- return value
2110
-
2111
- if token == 'mem' or token == 'memory':
2112
- value['type'] = 'memory'
2113
- return value
2114
-
2115
2144
  if token == 'error':
2116
2145
  token = self.peek()
2117
2146
  if token == 'code':
2118
2147
  self.nextToken()
2119
- value['item'] = 'errorCode'
2148
+ value.item = 'errorCode' # type: ignore
2120
2149
  return value
2121
2150
  elif token == 'reason':
2122
2151
  self.nextToken()
2123
- value['item'] = 'errorReason'
2152
+ value.item = 'errorReason' # type: ignore
2124
2153
  return value
2125
2154
  elif token in ['in', 'of']:
2126
2155
  self.nextToken()
2127
2156
  if self.nextIsSymbol():
2128
2157
  record = self.getSymbolRecord()
2129
- if record['keyword'] == 'ssh':
2130
- value['item'] = 'sshError'
2131
- value['name'] = record['name']
2158
+ if isinstance(record['object'], ECSSH):
2159
+ value.item = 'sshError' # type: ignore
2160
+ value.name = record['name'] # type: ignore
2132
2161
  return value
2133
2162
  return None
2134
2163
 
2135
2164
  if token == 'type':
2136
2165
  if self.nextIs('of'):
2137
- value['value'] = self.nextValue()
2166
+ value.value = self.nextValue() # type: ignore
2138
2167
  return value
2139
2168
  return None
2140
2169
 
2141
2170
  if token == 'modification':
2142
2171
  if self.nextIs('time'):
2143
2172
  if self.nextIs('of'):
2144
- value['fileName'] = self.nextValue()
2173
+ value.fileName = self.nextValue() # type: ignore
2145
2174
  return value
2146
2175
  return None
2147
2176
 
2148
2177
  if token == 'system':
2149
- value['command'] = self.nextValue()
2178
+ value.setContent(self.nextValue())
2150
2179
  return value
2151
2180
 
2152
2181
  if token == 'ticker':
@@ -2159,12 +2188,9 @@ class Core(Handler):
2159
2188
  def modifyValue(self, value):
2160
2189
  if self.peek() == 'modulo':
2161
2190
  self.nextToken()
2162
- mv = {}
2163
- mv['domain'] = 'core'
2164
- mv['type'] = 'modulo'
2165
- mv['content'] = value
2166
- mv['modval'] = self.nextValue()
2167
- value = mv
2191
+ mv = ECValue(type='modulo', content=value)
2192
+ mv.modval = self.nextValue() # type: ignore
2193
+ return mv
2168
2194
 
2169
2195
  return value
2170
2196
 
@@ -2172,472 +2198,368 @@ class Core(Handler):
2172
2198
  # Value handlers
2173
2199
 
2174
2200
  def v_args(self, v):
2175
- value = {}
2176
- value['type'] = 'text'
2177
- value['content'] = json.dumps(self.program.argv)
2178
- return value
2201
+ return ECValue(type=str, content=json.dumps(self.program.argv))
2179
2202
 
2180
2203
  def v_arg(self, v):
2181
- value = {}
2182
- value['type'] = 'text'
2183
- index = self.getRuntimeValue(v['index'])
2204
+ index = self.textify(v['index'])
2184
2205
  if index >= len(self.program.argv):
2185
2206
  RuntimeError(self.program, 'Index exceeds # of args')
2186
- value['content'] = self.program.argv[index]
2187
- return value
2207
+ return ECValue(type=str, content=self.program.argv[index])
2188
2208
 
2209
+ def v_bool(self, v):
2210
+ value = ECValue(type=bool, content=v.getContent())
2211
+
2189
2212
  def v_boolean(self, v):
2190
- value = {}
2191
- value['type'] = 'boolean'
2192
- value['content'] = v['content']
2193
- return value
2213
+ return self.v_bool(v)
2194
2214
 
2195
2215
  def v_cos(self, v):
2196
- angle = self.getRuntimeValue(v['angle'])
2197
- radius = self.getRuntimeValue(v['radius'])
2198
- value = {}
2199
- value['type'] = 'int'
2200
- value['content'] = round(math.cos(angle * 0.01745329) * radius)
2201
- return value
2216
+ angle = self.textify(v['angle'])
2217
+ radius = self.textify(v['radius'])
2218
+ return ECValue(type=int, content=round(math.cos(angle * 0.01745329) * radius))
2202
2219
 
2203
2220
  def v_count(self, v):
2204
- variable = self.getVariable(v['name'])
2205
- content = variable['value'][variable['index']]['content']
2206
- value = {}
2207
- value['type'] = 'int'
2208
- value['content'] = len(content)
2209
- return value
2221
+ variable = self.getObject(self.getVariable(v.getContent()))
2222
+ return ECValue(type=int, content=variable.getItemCount())
2210
2223
 
2211
2224
  def v_datime(self, v):
2212
- ts = self.getRuntimeValue(v['timestamp'])
2213
- fmt = v['format']
2225
+ ts = self.textify(v.timestamp)
2226
+ fmt = v.format
2214
2227
  if fmt == None:
2215
2228
  fmt = '%b %d %Y %H:%M:%S'
2216
2229
  else:
2217
- fmt = self.getRuntimeValue(fmt)
2218
- value = {}
2219
- value['type'] = 'text'
2220
- value['content'] = datetime.fromtimestamp(ts/1000).strftime(fmt)
2221
- return value
2230
+ fmt = self.textify(fmt)
2231
+ return ECValue(type=str, content=datetime.fromtimestamp(ts/1000).strftime(fmt))
2222
2232
 
2223
2233
  def v_decode(self, v):
2224
- content = self.getRuntimeValue(v['content'])
2225
- value = {}
2226
- value['type'] = 'text'
2234
+ content = self.textify(v.getContent())
2235
+ value = ECValue(type=str)
2227
2236
  if self.encoding == 'utf-8':
2228
- value['content'] = content.decode('utf-8')
2237
+ value.setContent(content.decode('utf-8'))
2229
2238
  elif self.encoding == 'base64':
2230
2239
  base64_bytes = content.encode('ascii')
2231
2240
  message_bytes = base64.b64decode(base64_bytes)
2232
- value['content'] = message_bytes.decode('ascii')
2241
+ value.setContent(message_bytes.decode('ascii'))
2233
2242
  elif self.encoding == 'hex':
2234
2243
  hex_bytes = content.encode('utf-8')
2235
2244
  message_bytes = binascii.unhexlify(hex_bytes)
2236
- value['content'] = message_bytes.decode('utf-8')
2245
+ value.setContent(message_bytes.decode('utf-8'))
2237
2246
  else:
2238
2247
  value = v
2239
2248
  return value
2240
2249
 
2241
- def v_element(self, v):
2242
- index = self.getRuntimeValue(v['index'])
2243
- target = self.getVariable(v['target'])
2244
- val = self.getSymbolValue(target)
2245
- content = val['content']
2246
- value = {}
2247
- value['type'] = 'int' if isinstance(content, int) else 'text'
2248
- if type(content) == list:
2249
- try:
2250
- value['content'] = content[index]
2251
- return value
2252
- except:
2253
- RuntimeError(self.program, 'Index out of range')
2254
- # lino = self.program.code[self.program.pc]['lino']
2255
- RuntimeError(self.program, 'Item is not a list')
2256
-
2257
2250
  def v_elements(self, v):
2258
- var = self.getVariable(v['name'])
2259
- value = {}
2260
- value['type'] = 'int'
2261
- value['content'] = var['elements']
2262
- return value
2251
+ var = self.getVariable(v.name)
2252
+ object = var['object']
2253
+ self.checkObjectType(object, ECVariable)
2254
+ return ECValue(type=int, content=object.getElements())
2263
2255
 
2264
2256
  def v_empty(self, v):
2265
- value = {}
2266
- value['type'] = 'text'
2267
- value['content'] = ''
2268
- return value
2257
+ return ECValue(type=str, content='' )
2269
2258
 
2270
2259
  def v_encode(self, v):
2271
- content = self.getRuntimeValue(v['content'])
2272
- value = {}
2273
- value['type'] = 'text'
2260
+ content = self.textify(v.getContent())
2261
+ value = ECValue(type=str)
2274
2262
  if self.encoding == 'utf-8':
2275
- value['content'] = content.encode('utf-8')
2263
+ value.setContent(content.encode('utf-8'))
2276
2264
  elif self.encoding == 'base64':
2277
2265
  data_bytes = content.encode('ascii')
2278
2266
  base64_bytes = base64.b64encode(data_bytes)
2279
- value['content'] = base64_bytes.decode('ascii')
2267
+ value.setContent(base64_bytes.decode('ascii'))
2280
2268
  elif self.encoding == 'hex':
2281
2269
  data_bytes = content.encode('utf-8')
2282
2270
  hex_bytes = binascii.hexlify(data_bytes)
2283
- value['content'] = hex_bytes.decode('utf-8')
2271
+ value.setContent(hex_bytes.decode('utf-8'))
2284
2272
  else:
2285
2273
  value = v
2286
2274
  return value
2287
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
+
2288
2281
  def v_error(self, v):
2289
2282
  global errorCode, errorReason
2290
- value = {}
2291
- if v['item'] == 'errorCode':
2292
- value['type'] = 'int'
2293
- value['content'] = errorCode
2294
- elif v['item'] == 'errorReason':
2295
- value['type'] = 'text'
2296
- value['content'] = errorReason
2297
- elif v['item'] == 'sshError':
2298
- record = self.getVariable(v['name'])
2299
- value['type'] = 'text'
2300
- value['content'] = record['error'] if 'error' in record else ''
2283
+ value = ECValue()
2284
+ item = v.item
2285
+ if item == 'errorCode':
2286
+ value.setValue(type=int, content=errorCode)
2287
+ elif item == 'errorReason':
2288
+ value.setValue(type=str, content=errorReason)
2289
+ elif item == 'sshError':
2290
+ record = self.getVariable(v.name)
2291
+ value.setValue(type=str, content=record['error'] if 'error' in record else '')
2301
2292
  return value
2302
2293
 
2303
2294
  def v_files(self, v):
2304
- v = self.getRuntimeValue(v['target'])
2305
- value = {}
2306
- value['type'] = 'text'
2307
- value['content'] = os.listdir(v)
2308
- return value
2295
+ path = self.textify(v.target)
2296
+ return ECValue(type=str, content=json.dumps(os.listdir(path)))
2309
2297
 
2310
2298
  def v_float(self, v):
2311
- val = self.getRuntimeValue(v['content'])
2312
- value = {}
2313
- value['type'] = 'float'
2299
+ val = self.textify(v.getContent())
2300
+ value = ECValue(type=float)
2314
2301
  try:
2315
- value['content'] = float(val)
2302
+ value.setContent(float(val))
2316
2303
  except:
2317
2304
  RuntimeWarning(self.program, f'Value cannot be parsed as floating-point')
2318
- value['content'] = 0.0
2305
+ value.setContent(0.0)
2319
2306
  return value
2320
2307
 
2321
2308
  def v_from(self, v):
2322
- content = self.getRuntimeValue(v['content'])
2323
- start = self.getRuntimeValue(v['start'])
2324
- to = v['to']
2325
- if not to == None:
2326
- to = self.getRuntimeValue(to)
2327
- value = {}
2328
- value['type'] = 'text'
2329
- if to == None:
2330
- value['content'] = content[start:]
2331
- else:
2332
- value['content'] = content[start:to]
2333
- return value
2309
+ content = self.textify(v.getContent())
2310
+ start = self.textify(v.start)
2311
+ to = self.textify(v.to)
2312
+ if start is not None and type(start) != int:
2313
+ RuntimeError(self.program, 'Invalid "from" value')
2314
+ if to is not None and type(to) != int:
2315
+ RuntimeError(self.program, 'Invalid "to" value')
2316
+ return ECValue(type=str, content=content[start:] if to == None else content[start:to])
2334
2317
 
2335
2318
  def v_hash(self, v):
2336
- hashval = self.getRuntimeValue(v['content'])
2337
- value = {}
2338
- value['type'] = 'text'
2339
- value['content'] = hashlib.sha256(hashval.encode('utf-8')).hexdigest()
2340
- return value
2319
+ hashval = self.textify(v.getContent())
2320
+ return ECValue(type=str, content=hashlib.sha256(hashval.encode('utf-8')).hexdigest())
2341
2321
 
2342
2322
  def v_index(self, v):
2343
- value = {}
2344
- value['type'] = 'int'
2345
- value['content'] = self.getVariable(v['name'])['index']
2346
- return value
2323
+ record = self.getVariable(v.name)
2324
+ object = self.getObject(record)
2325
+ return ECValue(type=int, content=object.getIndex())
2347
2326
 
2348
2327
  def v_indexOf(self, v):
2349
- value = v['value']
2328
+ value = v.value
2350
2329
  if value == None:
2351
- value = self.getSymbolValue(v['variable'])['content']
2330
+ var = self.getObject(self.getVariable(v.variable))
2331
+ value = var.getContent()
2352
2332
  else:
2353
- value = self.getRuntimeValue(value)
2354
- target = self.getVariable(v['target'])
2355
- data = self.getSymbolValue(target)['content']
2356
- index = -1
2357
- for n in range(0, len(data)):
2358
- if data[n] == value:
2359
- index = n
2360
- break
2361
- retval = {}
2362
- retval['type'] = 'int'
2363
- retval['content'] = index
2364
- return retval
2333
+ value = self.textify(value)
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))
2365
2348
 
2366
2349
  def v_integer(self, v):
2367
- val = self.getRuntimeValue(v['content'])
2368
- value = {}
2369
- value['type'] = 'int'
2370
- value['content'] = int(val)
2371
- return value
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()
2372
2368
 
2373
2369
  def v_json(self, v):
2374
- item = self.getRuntimeValue(v['content'])
2375
- value = {}
2376
- value['type'] = 'object'
2370
+ item = self.textify(v.getContent())
2371
+ value = ECValue()
2377
2372
  try:
2378
- value['content'] = json.loads(item)
2373
+ v = json.loads(item)
2374
+ if type(v) == list: value.setType('list')
2375
+ elif type(v) == dict: value.setType('dict')
2376
+ else: value.setType(str)
2377
+ value.setContent(v)
2379
2378
  except:
2380
2379
  value = None
2381
2380
  return value
2382
2381
 
2383
2382
  def v_keys(self, v):
2384
- value = {}
2385
- value['type'] = 'int'
2386
- value['content'] = list(self.getRuntimeValue(v['name']).keys())
2387
- return value
2383
+ dictionary = self.getObject(self.getVariable(v.name))
2384
+ return ECValue(type='list', content=list(dictionary.keys())) # type: ignore
2388
2385
 
2389
2386
  def v_left(self, v):
2390
- content = self.getRuntimeValue(v['content'])
2391
- count = self.getRuntimeValue(v['count'])
2392
- value = {}
2393
- value['type'] = 'text'
2394
- value['content'] = content[0:count]
2395
- return value
2387
+ content = self.textify(v.getContent())
2388
+ count = self.textify(v.count)
2389
+ return ECValue(type=str, content=content[0:count])
2396
2390
 
2397
2391
  def v_lengthOf(self, v):
2398
- content = self.getRuntimeValue(v['content'])
2392
+ content = self.textify(v.getContent())
2399
2393
  if type(content) == str:
2400
- value = {}
2401
- value['type'] = 'int'
2402
- value['content'] = len(content)
2403
- return value
2394
+ return ECValue(type=int, content=len(content))
2404
2395
  RuntimeError(self.program, 'Value is not a string')
2405
2396
 
2406
2397
  def v_lowercase(self, v):
2407
- content = self.getRuntimeValue(v['content'])
2408
- value = {}
2409
- value['type'] = 'text'
2410
- value['content'] = content.lower()
2411
- return value
2412
-
2413
- def v_memory(self, v):
2414
- process: Process = Process(os.getpid())
2415
- megabytes: float = process.memory_info().rss / (1024 * 1024)
2416
- value = {}
2417
- value['type'] = 'float'
2418
- value['content'] = megabytes
2419
- return value
2398
+ content = self.textify(v.getValue())
2399
+ return ECValue(type=str, content=content.lower())
2420
2400
 
2421
2401
  def v_message(self, v):
2422
- value = {}
2423
- value['type'] = 'text'
2424
- value['content'] = self.program.message
2425
- return value
2402
+ return ECValue(type=str, content=self.program.message)
2426
2403
 
2427
2404
  def v_modification(self, v):
2428
- fileName = self.getRuntimeValue(v['fileName'])
2405
+ fileName = self.textify(v['fileName'])
2429
2406
  ts = int(os.stat(fileName).st_mtime)
2430
- value = {}
2431
- value['type'] = 'int'
2432
- value['content'] = ts
2433
- return value
2407
+ return ECValue(type=int, content=ts)
2434
2408
 
2435
2409
  def v_modulo(self, v):
2436
- val = self.getRuntimeValue(v['content'])
2437
- modval = self.getRuntimeValue(v['modval'])
2438
- value = {}
2439
- value['type'] = 'int'
2440
- value['content'] = val % modval
2441
- return value
2410
+ val = self.textify(v.getContent())
2411
+ modval = self.textify(v.modval)
2412
+ return ECValue(type=int, content=val % modval)
2442
2413
 
2443
2414
  def v_newline(self, v):
2444
- value = {}
2445
- value['type'] = 'text'
2446
- value['content'] = '\n'
2447
- return value
2415
+ return ECValue(type=str, content='\n')
2448
2416
 
2449
2417
  def v_now(self, v):
2450
- value = {}
2451
- value['type'] = 'int'
2452
- value['content'] = int(time.time())
2453
- return value
2418
+ return ECValue(type=int, content=int(time.time()))
2454
2419
 
2455
2420
  def v_position(self, v):
2456
- needle = self.getRuntimeValue(v['needle'])
2457
- haystack = self.getRuntimeValue(v['haystack'])
2458
- last = v['last']
2459
- value = {}
2460
- value['type'] = 'int'
2461
- value['content'] = haystack.rfind(needle) if last else haystack.find(needle)
2462
- return value
2421
+ needle = self.textify(v.needle)
2422
+ haystack = self.textify(v.haystack)
2423
+ last = v.last
2424
+ return ECValue(type=int, content=haystack.rfind(needle) if last else haystack.find(needle))
2463
2425
 
2464
2426
  def v_prettify(self, v):
2465
- item = self.getRuntimeValue(v['content'])
2466
- value = {}
2467
- value['type'] = 'text'
2468
- value['content'] = json.dumps(item, indent=4)
2469
- return value
2427
+ item = self.textify(v.getContent())
2428
+ if isinstance(item, str): item = json.loads(item)
2429
+ return ECValue(type=str, content=json.dumps(item, indent=4))
2470
2430
 
2471
2431
  def v_property(self, v):
2472
- propertyValue = self.getRuntimeValue(v['name'])
2473
- if 'target' in v:
2474
- targetName = v['target']
2475
- target = self.getVariable(targetName)
2476
- targetValue = self.getRuntimeValue(target)
2477
- else:
2478
- targetValue = self.getRuntimeValue(v['value'])
2479
- try:
2480
- val = targetValue[propertyValue]
2481
- except:
2482
- RuntimeError(self.program, f'This value does not have the property \'{propertyValue}\'')
2483
- return None
2484
- value = {}
2485
- value['content'] = val
2486
- if isinstance(v, numbers.Number):
2487
- value['type'] = 'int'
2488
- else:
2489
- value['type'] = 'text'
2490
- return value
2432
+ propertyName = v.name
2433
+ propertyValue = self.textify(propertyName)
2434
+ targetName = v.target
2435
+ target = self.getVariable(targetName.getContent())
2436
+ variable = self.getObject(target)
2437
+ return variable.getProperty(propertyValue)
2491
2438
 
2492
2439
  def v_random(self, v):
2493
- limit = self.getRuntimeValue(v['content'])
2494
- value = {}
2495
- value['type'] = 'int'
2496
- value['content'] = random.randrange(0, limit)
2497
- return value
2440
+ limit = self.textify(v.getValue())
2441
+ return ECValue(type=int, content=random.randrange(0, limit))
2498
2442
 
2499
2443
  def v_right(self, v):
2500
- content = self.getRuntimeValue(v['content'])
2501
- count = self.getRuntimeValue(v['count'])
2502
- value = {}
2503
- value['type'] = 'text'
2504
- value['content'] = content[-count:]
2505
- return value
2444
+ content = self.textify(v.getContent())
2445
+ count = self.textify(v.count)
2446
+ return ECValue(type=str, content=content[-count:])
2506
2447
 
2507
2448
  def v_sin(self, v):
2508
- angle = self.getRuntimeValue(v['angle'])
2509
- radius = self.getRuntimeValue(v['radius'])
2510
- value = {}
2511
- value['type'] = 'int'
2512
- value['content'] = round(math.sin(angle * 0.01745329) * radius)
2513
- return value
2449
+ angle = self.textify(v.angle)
2450
+ radius = self.textify(v.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))
2514
2456
 
2515
2457
  def v_stringify(self, v):
2516
- item = self.getRuntimeValue(v['content'])
2517
- value = {}
2518
- value['type'] = 'text'
2519
- value['content'] = json.dumps(item)
2520
- return value
2458
+ item = self.textify(v.getContent())
2459
+ item = json.loads(item)
2460
+ return ECValue(type=str, content=json.dumps(item))
2521
2461
 
2522
2462
  # This is used by the expression evaluator to get the value of a symbol
2523
- def v_symbol(self, value):
2524
- name = value['name']
2525
- symbolRecord = self.program.getSymbolRecord(name)
2526
- keyword = symbolRecord['keyword']
2527
- if keyword == 'variable':
2528
- return self.getSymbolValue(symbolRecord)
2463
+ def v_symbol(self, v):
2464
+ name = v.name
2465
+ record = self.program.getSymbolRecord(name)
2466
+ keyword = record['keyword']
2467
+ if keyword == 'object':
2468
+ return record['object'].getValue()
2469
+ elif keyword == 'variable':
2470
+ return self.getSymbolValue(record)
2529
2471
  elif keyword == 'ssh':
2530
- v = {}
2531
- v['type'] = 'boolean'
2532
- v['content'] = True if 'ssh' in symbolRecord and symbolRecord['ssh'] != None else False
2533
- return v
2472
+ return ECValue(type=bool, content=True if 'ssh' in record and record['ssh'] != None else False)
2534
2473
  else:
2535
2474
  return None
2536
2475
 
2537
2476
  def v_system(self, v):
2538
- command = self.getRuntimeValue(v['command'])
2477
+ command = self.textify(v.getContent())
2539
2478
  result = os.popen(command).read()
2540
- value = {}
2541
- value['type'] = 'text'
2542
- value['content'] = result
2543
- return value
2479
+ return ECValue(type=str, content=result)
2544
2480
 
2545
2481
  def v_tab(self, v):
2546
- value = {}
2547
- value['type'] = 'text'
2548
- value['content'] = '\t'
2549
- return value
2482
+ return ECValue(type=str, content='\t')
2550
2483
 
2551
2484
  def v_tan(self, v):
2552
- angle = self.getRuntimeValue(v['angle'])
2553
- radius = self.getRuntimeValue(v['radius'])
2554
- value = {}
2555
- value['type'] = 'int'
2556
- value['content'] = round(math.tan(angle * 0.01745329) * radius)
2557
- return value
2485
+ angle = self.textify(v['angle'])
2486
+ radius = self.textify(v['radius'])
2487
+ return ECValue(type=int, content=round(math.tan(angle * 0.01745329) * radius))
2558
2488
 
2559
2489
  def v_ticker(self, v):
2560
- value = {}
2561
- value['type'] = 'int'
2562
- value['content'] = self.program.ticker
2563
- return value
2490
+ return ECValue(type=int, content=self.program.ticker)
2564
2491
 
2565
2492
  def v_timestamp(self, v):
2566
- value = {}
2567
- value['type'] = 'int'
2568
- fmt = v['format']
2493
+ value = ECValue(type=int)
2494
+ fmt = v.format
2569
2495
  if fmt == None:
2570
- value['content'] = int(time.time())
2496
+ value.setContent(int(time.time()))
2571
2497
  else:
2572
- fmt = self.getRuntimeValue(fmt)
2573
- dt = self.getRuntimeValue(v['datime'])
2498
+ fmt = self.textify(fmt)
2499
+ dt = self.textify(v.timestamp)
2574
2500
  spec = datetime.strptime(dt, fmt)
2575
2501
  t = datetime.now().replace(hour=spec.hour, minute=spec.minute, second=spec.second, microsecond=0)
2576
- value['content'] = int(t.timestamp())
2502
+ value.setContent(int(t.timestamp()))
2577
2503
  return value
2578
2504
 
2579
2505
  def v_today(self, v):
2580
- value = {}
2581
- value['type'] = 'int'
2582
- value['content'] = int(datetime.combine(datetime.now().date(),datetime.min.time()).timestamp())*1000
2583
- return value
2506
+ return ECValue(type=int, content=int(datetime.combine(datetime.now().date(),datetime.min.time()).timestamp()) * 1000)
2584
2507
 
2585
2508
  def v_trim(self, v):
2586
- v = self.getRuntimeValue(v['content'])
2587
- value = {}
2588
- value['type'] = 'text'
2589
- value['content'] = v.strip()
2590
- return value
2509
+ content = v.getContent()
2510
+ content = self.textify(content)
2511
+ return ECValue(type=str, content=content.strip())
2591
2512
 
2592
2513
  def v_type(self, v):
2593
- value = {}
2594
- value['type'] = 'text'
2595
- val = self.getRuntimeValue(v['value'])
2514
+ value = ECValue(type=str)
2515
+ val = self.textify(v['value'])
2596
2516
  if val is None:
2597
- value['content'] = 'none'
2517
+ value.setContent('none')
2598
2518
  elif type(val) is str:
2599
- value['content'] = 'text'
2519
+ value.setContent(str)
2600
2520
  elif type(val) is int:
2601
- value['content'] = 'numeric'
2521
+ value.setContent('numeric')
2602
2522
  elif type(val) is bool:
2603
- value['content'] = 'boolean'
2523
+ value.setContent(bool)
2604
2524
  elif type(val) is list:
2605
- value['content'] = 'list'
2525
+ value.setContent('list')
2606
2526
  elif type(val) is dict:
2607
- value['content'] = 'object'
2527
+ value.setContent('dict')
2608
2528
  return value
2609
2529
 
2610
2530
  def v_uppercase(self, v):
2611
- content = self.getRuntimeValue(v['content'])
2612
- value = {}
2613
- value['type'] = 'text'
2614
- value['content'] = content.upper()
2615
- return value
2531
+ content = self.textify(v.getContent())
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])
2616
2536
 
2617
2537
  def v_valueOf(self, v):
2618
- v = self.getRuntimeValue(v['content'])
2619
- value = {}
2620
- value['type'] = 'int'
2621
- value['content'] = int(v) if v != '' else 0
2538
+ v = self.textify(v.getContent())
2539
+ return ECValue(type=int, content=int(v) if v != '' else 0)
2540
+
2541
+ def v_variable(self, v):
2542
+ name = v.getContent()
2543
+ record = self.program.getSymbolRecord(name)
2544
+ variable = record['object']
2545
+ self.checkObjectType(variable, ECVariable)
2546
+ value = variable.getValue()
2622
2547
  return value
2623
2548
 
2624
2549
  def v_weekday(self, v):
2625
- value = {}
2626
- value['type'] = 'int'
2627
- value['content'] = datetime.today().weekday()
2628
- return value
2550
+ return ECValue(type=int, content=datetime.today().weekday())
2629
2551
 
2630
2552
  #############################################################################
2631
2553
  # Compile a condition
2632
2554
  def compileCondition(self):
2633
- condition = Object()
2634
- condition.negate = False
2555
+ condition = ECValue()
2556
+ condition.negate = False # type: ignore
2635
2557
 
2636
2558
  token = self.getToken()
2637
2559
 
2638
2560
  if token == 'not':
2639
- condition.type = 'not'
2640
- condition.value = self.nextValue()
2561
+ condition.type = 'not' # type: ignore
2562
+ condition.value = self.nextValue() # type: ignore
2641
2563
  return condition
2642
2564
 
2643
2565
  elif token == 'error':
@@ -2646,21 +2568,21 @@ class Core(Handler):
2646
2568
  if self.nextIsSymbol():
2647
2569
  record = self.getSymbolRecord()
2648
2570
  if record['keyword'] == 'ssh':
2649
- condition.type = 'sshError'
2650
- condition.target = record['name']
2571
+ condition.type = 'sshError' # type: ignore
2572
+ condition.target = record['name'] # type: ignore
2651
2573
  return condition
2652
2574
  return None
2653
2575
 
2654
2576
  elif token == 'file':
2655
2577
  path = self.nextValue()
2656
- condition.path = path
2657
- condition.type = 'exists'
2578
+ condition.path = path # type: ignore
2579
+ condition.type = 'exists' # type: ignore
2658
2580
  self.skip('on')
2659
2581
  if self.nextIsSymbol():
2660
2582
  record = self.getSymbolRecord()
2661
2583
  if record['keyword'] == 'ssh':
2662
- condition.type = 'sshExists'
2663
- condition.target = record['name']
2584
+ condition.type = 'sshExists' # type: ignore
2585
+ condition.target = record['name'] # type: ignore
2664
2586
  token = self.nextToken()
2665
2587
  else: token = self.getToken()
2666
2588
  if token == 'exists':
@@ -2668,24 +2590,33 @@ class Core(Handler):
2668
2590
  elif token == 'does':
2669
2591
  if self.nextIs('not'):
2670
2592
  if self.nextIs('exist'):
2671
- condition.negate = not condition.negate
2593
+ condition.negate = not condition.negate # type: ignore
2672
2594
  return condition
2673
2595
  return None
2596
+
2597
+ elif token == 'debugging':
2598
+ condition.type = 'debugging' # type: ignore
2599
+ return condition
2674
2600
 
2675
2601
  value = self.getValue()
2676
2602
  if value == None:
2677
2603
  return None
2678
2604
 
2679
- condition.value1 = value
2605
+ condition.value1 = value # type: ignore
2680
2606
  token = self.peek()
2681
- condition.type = token
2607
+ condition.type = token # type: ignore
2682
2608
 
2683
2609
  if token == 'has':
2684
2610
  self.nextToken()
2685
- if self.nextToken() == 'property':
2686
- prop = self.nextValue()
2687
- condition.type = 'hasProperty'
2688
- condition.property = prop
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
2689
2620
  return condition
2690
2621
  return None
2691
2622
 
@@ -2694,50 +2625,55 @@ class Core(Handler):
2694
2625
  if self.nextIs('not'):
2695
2626
  token = self.nextToken()
2696
2627
  if token == 'have':
2697
- if self.nextToken() == 'property':
2698
- prop = self.nextValue()
2699
- condition.type = 'hasProperty'
2700
- condition.property = prop
2701
- condition.negate = not condition.negate
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
2637
+ condition.negate = not condition.negate # type: ignore
2702
2638
  return condition
2703
2639
  elif token == 'include':
2704
2640
  value = self.nextValue()
2705
- condition.type = 'includes'
2706
- condition.value2 = value
2707
- condition.negate = not condition.negate
2641
+ condition.type = 'includes' # type: ignore
2642
+ condition.value2 = value # type: ignore
2643
+ condition.negate = not condition.negate # type: ignore
2708
2644
  return condition
2709
2645
  return None
2710
2646
 
2711
2647
  if token in ['starts', 'ends']:
2712
2648
  self.nextToken()
2713
2649
  if self.nextToken() == 'with':
2714
- condition.value2 = self.nextValue()
2650
+ condition.value2 = self.nextValue() # type: ignore
2715
2651
  return condition
2716
2652
 
2717
2653
  if token == 'includes':
2718
- condition.value2 = self.nextValue()
2654
+ condition.value2 = self.nextValue() # type: ignore
2719
2655
  return condition
2720
2656
 
2721
2657
  if token == 'is':
2722
2658
  token = self.nextToken()
2723
2659
  if self.peek() == 'not':
2724
2660
  self.nextToken()
2725
- condition.negate = True
2661
+ condition.negate = True # type: ignore
2726
2662
  token = self.nextToken()
2727
- condition.type = token
2728
- if token in ['numeric', 'string', 'boolean', 'none', 'list', 'object', 'even', 'odd', 'empty']:
2663
+ condition.type = token # type: ignore
2664
+ if token in ['numeric', 'string', 'bool', 'boolean', 'none', 'list', 'object', 'even', 'odd', 'empty']:
2729
2665
  return condition
2730
2666
  if token in ['greater', 'less']:
2731
2667
  if self.nextToken() == 'than':
2732
- condition.value2 = self.nextValue()
2668
+ condition.value2 = self.nextValue() # type: ignore
2733
2669
  return condition
2734
- condition.type = 'is'
2735
- condition.value2 = self.getValue()
2670
+ condition.type = 'is' # type: ignore
2671
+ condition.value2 = self.getValue() # type: ignore
2736
2672
  return condition
2737
2673
 
2738
- if condition.value1:
2674
+ if condition.value1: # type: ignore
2739
2675
  # It's a boolean if
2740
- condition.type = 'boolean'
2676
+ condition.type = bool # type: ignore
2741
2677
  return condition
2742
2678
 
2743
2679
  self.warning(f'Core.compileCondition: I can\'t get a conditional:')
@@ -2753,8 +2689,8 @@ class Core(Handler):
2753
2689
  #############################################################################
2754
2690
  # Condition handlers
2755
2691
 
2756
- def c_boolean(self, condition):
2757
- value = self.getRuntimeValue(condition.value1)
2692
+ def c_bool(self, condition):
2693
+ value = self.textify(condition.value1)
2758
2694
  if type(value) == bool:
2759
2695
  return not value if condition.negate else value
2760
2696
  elif type(value) == int:
@@ -2767,77 +2703,102 @@ class Core(Handler):
2767
2703
  else:
2768
2704
  return True if condition.negate else False
2769
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
2770
2712
 
2771
2713
  def c_empty(self, condition):
2772
- value = self.getRuntimeValue(condition.value1)
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
2720
+ value = self.textify(condition.value1)
2773
2721
  if value == None:
2774
2722
  comparison = True
2775
- else:
2723
+ elif isinstance(value, str):
2776
2724
  comparison = len(value) == 0
2725
+ else:
2726
+ domainName = condition.value1.domain
2727
+ domain = self.program.domainIndex[domainName] # type: ignore
2728
+ handler = domain.valueHandler('empty') # type: ignore
2729
+ if handler: comparison = self.textify(handler(condition.value1))
2777
2730
  return not comparison if condition.negate else comparison
2778
2731
 
2779
2732
  def c_ends(self, condition):
2780
- value1 = self.getRuntimeValue(condition.value1)
2781
- value2 = self.getRuntimeValue(condition.value2)
2733
+ value1 = self.textify(condition.value1)
2734
+ value2 = self.textify(condition.value2)
2782
2735
  return value1.endswith(value2)
2783
2736
 
2784
2737
  def c_even(self, condition):
2785
- return self.getRuntimeValue(condition.value1) % 2 == 0
2738
+ return self.textify(condition.value1) % 2 == 0
2786
2739
 
2787
2740
  def c_exists(self, condition):
2788
- path = self.getRuntimeValue(condition.path)
2741
+ path = self.textify(condition.path)
2789
2742
  comparison = os.path.exists(path)
2790
2743
  return not comparison if condition.negate else comparison
2791
2744
 
2792
2745
  def c_greater(self, condition):
2793
2746
  comparison = self.program.compare(condition.value1, condition.value2)
2747
+ if comparison == None:
2748
+ raise RuntimeError(self.program, f'Cannot compare {self.textify(condition.value1)} and {self.textify(condition.value2)}')
2794
2749
  return comparison <= 0 if condition.negate else comparison > 0
2795
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
+
2796
2757
  def c_hasProperty(self, condition):
2797
- value = self.getRuntimeValue(condition.value1)
2798
- prop = self.getRuntimeValue(condition.property)
2799
- try:
2800
- value[prop]
2801
- hasProp = True
2802
- except:
2803
- hasProp = False
2758
+ record = self.getVariable(condition.value1)
2759
+ variable = self.getObject(record)
2760
+ prop = self.textify(condition.property)
2761
+ hasProp = variable.hasProperty(prop)
2804
2762
  return not hasProp if condition.negate else hasProp
2805
2763
 
2806
2764
  def c_includes(self, condition):
2807
- value1 = self.getRuntimeValue(condition.value1)
2808
- value2 = self.getRuntimeValue(condition.value2)
2765
+ value1 = self.textify(condition.value1)
2766
+ value2 = self.textify(condition.value2)
2809
2767
  includes = value2 in value1
2810
2768
  return not includes if condition.negate else includes
2811
2769
 
2812
2770
  def c_is(self, condition):
2813
2771
  comparison = self.program.compare(condition.value1, condition.value2)
2772
+ if comparison == None: comparison = 1
2814
2773
  return comparison != 0 if condition.negate else comparison == 0
2815
2774
 
2816
2775
  def c_less(self, condition):
2817
2776
  comparison = self.program.compare(condition.value1, condition.value2)
2777
+ if comparison == None:
2778
+ raise RuntimeError(self.program, f'Cannot compare {self.textify(condition.value1)} and {self.textify(condition.value2)}')
2818
2779
  return comparison >= 0 if condition.negate else comparison < 0
2819
2780
 
2820
2781
  def c_list(self, condition):
2821
- comparison = type(self.getRuntimeValue(condition.value1)) is list
2782
+ comparison = type(self.textify(condition.value1)) is list
2822
2783
  return not comparison if condition.negate else comparison
2823
2784
 
2824
2785
  def c_numeric(self, condition):
2825
- comparison = type(self.getRuntimeValue(condition.value1)) is int
2786
+ comparison = type(self.textify(condition.value1)) is int
2826
2787
  return not comparison if condition.negate else comparison
2827
2788
 
2828
2789
  def c_none(self, condition):
2829
- comparison = self.getRuntimeValue(condition.value1) is None
2790
+ comparison = self.textify(condition.value1) is None
2830
2791
  return not comparison if condition.negate else comparison
2831
2792
 
2832
2793
  def c_not(self, condition):
2833
- return not self.getRuntimeValue(condition.value)
2794
+ return not self.textify(condition.value)
2834
2795
 
2835
2796
  def c_object(self, condition):
2836
- comparison = type(self.getRuntimeValue(condition.value1)) is dict
2797
+ comparison = type(self.textify(condition.value1)) is dict
2837
2798
  return not comparison if condition.negate else comparison
2838
2799
 
2839
2800
  def c_odd(self, condition):
2840
- return self.getRuntimeValue(condition.value1) % 2 == 1
2801
+ return self.textify(condition.value1) % 2 == 1
2841
2802
 
2842
2803
  def c_sshError(self, condition):
2843
2804
  target = self.getVariable(condition.target)
@@ -2847,7 +2808,7 @@ class Core(Handler):
2847
2808
  return not test if condition.negate else test
2848
2809
 
2849
2810
  def c_sshExists(self, condition):
2850
- path = self.getRuntimeValue(condition.path)
2811
+ path = self.textify(condition.path)
2851
2812
  ssh = self.getVariable(condition.target)
2852
2813
  sftp = ssh['sftp']
2853
2814
  try:
@@ -2858,10 +2819,10 @@ class Core(Handler):
2858
2819
  return not comparison if condition.negate else comparison
2859
2820
 
2860
2821
  def c_starts(self, condition):
2861
- value1 = self.getRuntimeValue(condition.value1)
2862
- value2 = self.getRuntimeValue(condition.value2)
2822
+ value1 = self.textify(condition.value1)
2823
+ value2 = self.textify(condition.value2)
2863
2824
  return value1.startswith(value2)
2864
2825
 
2865
2826
  def c_string(self, condition):
2866
- comparison = type(self.getRuntimeValue(condition.value1)) is str
2827
+ comparison = type(self.textify(condition.value1)) is str
2867
2828
  return not comparison if condition.negate else comparison