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