easycoder 251104.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
@@ -1771,21 +1728,16 @@ class Core(Handler):
1771
1728
  return False
1772
1729
  else:
1773
1730
  token = self.nextToken()
1774
- if token in ['graphics', 'debugger']:
1775
- if not hasattr(self.program, 'usingGraphics'):
1776
- print('Loading graphics module')
1777
- from .ec_pyside import Graphics
1778
- self.program.graphics = Graphics
1779
- self.program.useClass(Graphics)
1780
- self.program.usingGraphics = True
1781
- if token == 'debugger': self.program.debugging = True
1782
- return True
1731
+ if token == 'graphics':
1732
+ return self.program.useGraphics()
1733
+ elif token == 'psutil':
1734
+ return self.program.usePSUtil()
1783
1735
  return False
1784
1736
 
1785
1737
  # Declare a general-purpose variable
1786
1738
  def k_variable(self, command):
1787
1739
  self.compiler.addValueType()
1788
- return self.compileVariable(command)
1740
+ return self.compileVariable(command, 'ECVariable')
1789
1741
 
1790
1742
  def r_variable(self, command):
1791
1743
  return self.nextPC()
@@ -1811,7 +1763,7 @@ class Core(Handler):
1811
1763
  return True
1812
1764
 
1813
1765
  def r_wait(self, command):
1814
- value = self.getRuntimeValue(command['value']) * command['multiplier']
1766
+ value = self.textify(command['value']) * command['multiplier']
1815
1767
  next = self.nextPC()
1816
1768
  threading.Timer(value/1000.0, lambda: (self.run(next))).start()
1817
1769
  return 0
@@ -1877,7 +1829,7 @@ class Core(Handler):
1877
1829
  return False
1878
1830
 
1879
1831
  def r_write(self, command):
1880
- value = self.getRuntimeValue(command['value'])
1832
+ value = self.textify(command['value'])
1881
1833
  fileRecord = self.getVariable(command['file'])
1882
1834
  file = fileRecord['file']
1883
1835
  if file.mode in ['w', 'w+', 'a', 'a+']:
@@ -1890,51 +1842,37 @@ class Core(Handler):
1890
1842
  # Support functions
1891
1843
 
1892
1844
  def incdec(self, command, mode):
1893
- symbolRecord = self.getVariable(command['target'])
1894
- if not symbolRecord['hasValue']:
1895
- NoValueRuntimeError(self.program, symbolRecord)
1896
- value = self.getSymbolValue(symbolRecord)
1897
- if value == None:
1898
- RuntimeError(self.program, f'{symbolRecord["name"]} has not been initialised')
1899
- if mode == '+':
1900
- value['content'] += 1
1901
- else:
1902
- value['content'] -= 1
1903
- 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)
1904
1854
  return self.nextPC()
1905
1855
 
1906
1856
  #############################################################################
1907
1857
  # Compile a value in this domain
1908
1858
  def compileValue(self):
1909
- value = {}
1910
- value['domain'] = self.getName()
1859
+ value = ECValue(domain=self.getName())
1911
1860
  token = self.getToken()
1912
1861
  if self.isSymbol():
1913
- value['name'] = token
1914
- symbolRecord = self.getSymbolRecord()
1915
- keyword = symbolRecord['keyword']
1916
-
1917
- if keyword == 'module':
1918
- value['type'] = 'module'
1919
- return value
1920
-
1921
- if keyword in ['ssh', 'variable']:
1922
- value['type'] = 'symbol'
1923
- return value
1924
-
1925
- return None
1862
+ value.setValue(type='symbol', content=token)
1863
+ return value
1926
1864
 
1927
- value['type'] = token
1865
+ value.setType(token)
1928
1866
 
1929
1867
  if token == 'arg':
1930
1868
  self.nextToken()
1931
- value['index'] = self.getValue()
1869
+ value.index = self.getValue()
1932
1870
  return value
1933
1871
 
1934
1872
  if token in ['cos', 'sin', 'tan']:
1935
- value['angle'] = self.nextValue()
1873
+ value.angle = self.nextValue()
1936
1874
  if self.nextToken() == 'radius':
1937
- value['radius'] = self.nextValue()
1875
+ value.radius = self.nextValue()
1938
1876
  return value
1939
1877
  return None
1940
1878
 
@@ -1942,61 +1880,62 @@ class Core(Handler):
1942
1880
  return value
1943
1881
 
1944
1882
  if token in ['stringify', 'prettify', 'json', 'lowercase', 'uppercase', 'hash', 'random', 'float', 'integer', 'encode', 'decode']:
1945
- value['content'] = self.nextValue()
1883
+ value.setContent(self.nextValue())
1946
1884
  return value
1947
1885
 
1948
1886
  if (token in ['datime', 'datetime']):
1949
- value['type'] = 'datime'
1950
- value['timestamp'] = self.nextValue()
1887
+ value.setType('datime')
1888
+ value.timestamp = self.nextValue()
1951
1889
  if self.peek() == 'format':
1952
1890
  self.nextToken()
1953
- value['format'] = self.nextValue()
1891
+ value.format = self.nextValue()
1954
1892
  else:
1955
- value['format'] = None
1893
+ value.format = None
1956
1894
  return value
1957
1895
 
1958
1896
  if token == 'element':
1959
- value['index'] = self.nextValue()
1897
+ value.index = self.nextValue()
1960
1898
  if self.nextToken() == 'of':
1961
1899
  if self.nextIsSymbol():
1962
- symbolRecord = self.getSymbolRecord()
1963
- if symbolRecord['hasValue']:
1964
- value['target'] = symbolRecord['name']
1965
- return value
1966
- 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
1967
1904
  return None
1968
1905
 
1969
1906
  if token == 'property':
1970
- value['name'] = self.nextValue()
1907
+ value.name = self.nextValue() # type: ignore
1971
1908
  if self.nextToken() == 'of':
1972
1909
  if self.nextIsSymbol():
1973
- symbolRecord = self.getSymbolRecord()
1974
- if symbolRecord['hasValue']:
1975
- 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
1976
1915
  return value
1977
- NoValueError(self.compiler, symbolRecord)
1916
+ raise RuntimeError(self.program, f'Object {record["name"]} has no attribute "name"')
1978
1917
  return None
1979
1918
 
1980
1919
  if token == 'arg':
1981
- value['content'] = self.nextValue()
1920
+ value.setContent(self.nextValue())
1982
1921
  if self.getToken() == 'of':
1983
1922
  if self.nextIsSymbol():
1984
- symbolRecord = self.getSymbolRecord()
1985
- if symbolRecord['keyword'] == 'variable':
1986
- value['target'] = symbolRecord['name']
1923
+ record = self.getSymbolRecord()
1924
+ if record['keyword'] == 'variable':
1925
+ value.target = record['name'] # type: ignore
1987
1926
  return value
1988
1927
  return None
1989
1928
 
1990
1929
  if token == 'trim':
1991
1930
  self.nextToken()
1992
- value['content'] = self.getValue()
1931
+ value.setContent(self.getValue())
1993
1932
  return value
1994
1933
 
1995
1934
  if self.getToken() == 'the':
1996
1935
  self.nextToken()
1997
1936
 
1998
1937
  token = self.getToken()
1999
- value['type'] = token
1938
+ value.setType(token)
2000
1939
 
2001
1940
  if token == 'args':
2002
1941
  return value
@@ -2004,44 +1943,46 @@ class Core(Handler):
2004
1943
  if token == 'elements':
2005
1944
  if self.nextIs('of'):
2006
1945
  if self.nextIsSymbol():
2007
- value['name'] = self.getToken()
1946
+ value.name = self.getToken() # type: ignore
2008
1947
  return value
2009
1948
  return None
2010
1949
 
2011
1950
  if token == 'keys':
2012
1951
  if self.nextIs('of'):
2013
- value['name'] = self.nextValue()
1952
+ value.name = self.nextValue() # type: ignore
2014
1953
  return value
2015
1954
  return None
2016
1955
 
2017
1956
  if token == 'count':
2018
1957
  if self.nextIs('of'):
2019
1958
  if self.nextIsSymbol():
2020
- if self.getSymbolRecord()['hasValue']:
2021
- value['name'] = self.getToken()
1959
+ record = self.getSymbolRecord()
1960
+ object = record['object']
1961
+ if isinstance(object, ECVariable):
1962
+ value.setContent(record['name'])
2022
1963
  return value
2023
1964
  return None
2024
1965
 
2025
1966
  if token == 'index':
2026
1967
  if self.nextIs('of'):
2027
1968
  if self.nextIsSymbol():
2028
- value['variable'] = self.getSymbolRecord()['name']
1969
+ value.variable = self.getSymbolRecord()['name'] # type: ignore
2029
1970
  if self.peek() == 'in':
2030
- value['value'] = None
2031
- value['type'] = 'indexOf'
1971
+ value.value = None # type: ignore
1972
+ value.setType('indexOf')
2032
1973
  if self.nextIsSymbol():
2033
- value['target'] = self.getSymbolRecord()['name']
1974
+ value.target = self.getSymbolRecord()['name'] # type: ignore
2034
1975
  return value
2035
1976
  else:
2036
- value['name'] = self.getToken()
1977
+ value.name = self.getToken() # type: ignore
2037
1978
  return value
2038
1979
  else:
2039
- value['value'] = self.getValue()
1980
+ value.value = self.getValue() # type: ignore
2040
1981
  if self.nextIs('in'):
2041
- value['variable'] = None
2042
- value['type'] = 'indexOf'
1982
+ value.variable = None # type: ignore
1983
+ value.setType('indexOf')
2043
1984
  if self.nextIsSymbol():
2044
- value['target'] = self.getSymbolRecord()['name']
1985
+ value.target = self.getSymbolRecord()['name'] # type: ignore
2045
1986
  return value
2046
1987
  return None
2047
1988
 
@@ -2049,111 +1990,108 @@ class Core(Handler):
2049
1990
  if self.nextIs('of'):
2050
1991
  v = self.nextValue()
2051
1992
  if v !=None:
2052
- value['type'] = 'valueOf'
2053
- value['content'] = v
1993
+ value.setValue(type='valueOf', content=v)
2054
1994
  return value
2055
1995
  return None
2056
1996
 
2057
1997
  if token == 'length':
2058
- value['type'] = 'lengthOf'
1998
+ value.setType('lengthOf')
2059
1999
  if self.nextIs('of'):
2060
- value['content'] = self.nextValue()
2000
+ value.setContent(self.nextValue())
2061
2001
  return value
2062
2002
  return None
2063
2003
 
2064
2004
  if token in ['left', 'right']:
2065
- value['count'] = self.nextValue()
2005
+ value.count = self.nextValue() # type: ignore
2066
2006
  if self.nextToken() == 'of':
2067
- value['content'] = self.nextValue()
2007
+ value.setContent(self.nextValue())
2068
2008
  return value
2069
2009
  return None
2070
2010
 
2011
+ # from {n} of {value}
2012
+ # from {n} to {m} of {value}
2071
2013
  if token == 'from':
2072
- value['start'] = self.nextValue()
2014
+ value.start = self.nextValue() # type: ignore
2073
2015
  if self.peek() == 'to':
2074
2016
  self.nextToken()
2075
- value['to'] = self.nextValue()
2017
+ value.to = self.nextValue() # type: ignore
2076
2018
  else:
2077
- value['to'] = None
2019
+ value.to = None # type: ignore
2078
2020
  if self.nextToken() == 'of':
2079
- value['content'] = self.nextValue()
2021
+ value.setContent(self.nextValue())
2080
2022
  return value
2081
2023
 
2024
+ # position of [the] [last] {needle} in {haystack}
2082
2025
  if token == 'position':
2083
- if self.nextIs('of'):
2084
- value['last'] = False
2085
- if self.nextIs('the'):
2086
- if self.nextIs('last'):
2087
- self.nextToken()
2088
- value['last'] = True
2089
- value['needle'] = self.getValue()
2090
- if self.nextToken() == 'in':
2091
- value['haystack'] = self.nextValue()
2092
- 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
2093
2035
 
2094
2036
  if token == 'message':
2095
2037
  return value
2096
2038
 
2097
2039
  if token == 'timestamp':
2098
- value['format'] = None
2040
+ value.format = None # type: ignore
2099
2041
  if self.peek() == 'of':
2100
2042
  self.nextToken()
2101
- value['datime'] = self.nextValue()
2043
+ value.timestamp = self.nextValue() # type: ignore
2102
2044
  if self.peek() == 'format':
2103
2045
  self.nextToken()
2104
- value['format'] = self.nextValue()
2046
+ value.format = self.nextValue() # type: ignore
2105
2047
  return value
2106
2048
 
2107
2049
  if token == 'files':
2108
2050
  token = self.nextToken()
2109
2051
  if token in ['in', 'of']:
2110
- value['target'] = self.nextValue()
2052
+ value.target = self.nextValue() # type: ignore
2111
2053
  return value
2112
2054
  return None
2113
2055
 
2114
2056
  if token == 'weekday':
2115
- value['type'] = 'weekday'
2116
- return value
2117
-
2118
- if token == 'mem' or token == 'memory':
2119
- value['type'] = 'memory'
2057
+ value.setType('weekday')
2120
2058
  return value
2121
2059
 
2122
2060
  if token == 'error':
2123
2061
  token = self.peek()
2124
2062
  if token == 'code':
2125
2063
  self.nextToken()
2126
- value['item'] = 'errorCode'
2064
+ value.item = 'errorCode' # type: ignore
2127
2065
  return value
2128
2066
  elif token == 'reason':
2129
2067
  self.nextToken()
2130
- value['item'] = 'errorReason'
2068
+ value.item = 'errorReason' # type: ignore
2131
2069
  return value
2132
2070
  elif token in ['in', 'of']:
2133
2071
  self.nextToken()
2134
2072
  if self.nextIsSymbol():
2135
2073
  record = self.getSymbolRecord()
2136
- if record['keyword'] == 'ssh':
2137
- value['item'] = 'sshError'
2138
- value['name'] = record['name']
2074
+ if isinstance(record['object'], ECSSH):
2075
+ value.item = 'sshError' # type: ignore
2076
+ value.name = record['name'] # type: ignore
2139
2077
  return value
2140
2078
  return None
2141
2079
 
2142
2080
  if token == 'type':
2143
2081
  if self.nextIs('of'):
2144
- value['value'] = self.nextValue()
2082
+ value.value = self.nextValue() # type: ignore
2145
2083
  return value
2146
2084
  return None
2147
2085
 
2148
2086
  if token == 'modification':
2149
2087
  if self.nextIs('time'):
2150
2088
  if self.nextIs('of'):
2151
- value['fileName'] = self.nextValue()
2089
+ value.fileName = self.nextValue() # type: ignore
2152
2090
  return value
2153
2091
  return None
2154
2092
 
2155
2093
  if token == 'system':
2156
- value['command'] = self.nextValue()
2094
+ value.setContent(self.nextValue())
2157
2095
  return value
2158
2096
 
2159
2097
  if token == 'ticker':
@@ -2166,12 +2104,9 @@ class Core(Handler):
2166
2104
  def modifyValue(self, value):
2167
2105
  if self.peek() == 'modulo':
2168
2106
  self.nextToken()
2169
- mv = {}
2170
- mv['domain'] = 'core'
2171
- mv['type'] = 'modulo'
2172
- mv['content'] = value
2173
- mv['modval'] = self.nextValue()
2174
- value = mv
2107
+ mv = ECValue(domain=self.getName(), type='modulo', content=value)
2108
+ mv.modval = self.nextValue() # type: ignore
2109
+ return mv
2175
2110
 
2176
2111
  return value
2177
2112
 
@@ -2179,472 +2114,353 @@ class Core(Handler):
2179
2114
  # Value handlers
2180
2115
 
2181
2116
  def v_args(self, v):
2182
- value = {}
2183
- value['type'] = 'text'
2184
- value['content'] = json.dumps(self.program.argv)
2185
- return value
2117
+ return ECValue(domain=self.getName(), type='str', content=json.dumps(self.program.argv))
2186
2118
 
2187
2119
  def v_arg(self, v):
2188
- value = {}
2189
- value['type'] = 'text'
2190
- index = self.getRuntimeValue(v['index'])
2120
+ index = self.textify(v['index'])
2191
2121
  if index >= len(self.program.argv):
2192
2122
  RuntimeError(self.program, 'Index exceeds # of args')
2193
- value['content'] = self.program.argv[index]
2194
- return value
2123
+ return ECValue(domain=self.getName(), type='str', content=self.program.argv[index])
2195
2124
 
2196
2125
  def v_boolean(self, v):
2197
- value = {}
2198
- value['type'] = 'boolean'
2199
- value['content'] = v['content']
2200
- return value
2126
+ value = ECValue(domain=self.getName(), type='boolean', content=v.getContent())
2201
2127
 
2202
2128
  def v_cos(self, v):
2203
- angle = self.getRuntimeValue(v['angle'])
2204
- radius = self.getRuntimeValue(v['radius'])
2205
- value = {}
2206
- value['type'] = 'int'
2207
- value['content'] = round(math.cos(angle * 0.01745329) * radius)
2208
- 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))
2209
2132
 
2210
2133
  def v_count(self, v):
2211
- variable = self.getVariable(v['name'])
2212
- content = variable['value'][variable['index']]['content']
2213
- value = {}
2214
- value['type'] = 'int'
2215
- value['content'] = len(content)
2216
- 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))
2217
2137
 
2218
2138
  def v_datime(self, v):
2219
- ts = self.getRuntimeValue(v['timestamp'])
2220
- fmt = v['format']
2139
+ ts = self.textify(v.timestamp)
2140
+ fmt = v.format
2221
2141
  if fmt == None:
2222
2142
  fmt = '%b %d %Y %H:%M:%S'
2223
2143
  else:
2224
- fmt = self.getRuntimeValue(fmt)
2225
- value = {}
2226
- value['type'] = 'text'
2227
- value['content'] = datetime.fromtimestamp(ts/1000).strftime(fmt)
2228
- return value
2144
+ fmt = self.textify(fmt)
2145
+ return ECValue(domain=self.getName(), type='str', content=datetime.fromtimestamp(ts/1000).strftime(fmt))
2229
2146
 
2230
2147
  def v_decode(self, v):
2231
- content = self.getRuntimeValue(v['content'])
2232
- value = {}
2233
- value['type'] = 'text'
2148
+ content = self.textify(v.getContent())
2149
+ value = ECValue(domain=self.getName(), type='str')
2234
2150
  if self.encoding == 'utf-8':
2235
- value['content'] = content.decode('utf-8')
2151
+ value.setContent(content.decode('utf-8'))
2236
2152
  elif self.encoding == 'base64':
2237
2153
  base64_bytes = content.encode('ascii')
2238
2154
  message_bytes = base64.b64decode(base64_bytes)
2239
- value['content'] = message_bytes.decode('ascii')
2155
+ value.setContent(message_bytes.decode('ascii'))
2240
2156
  elif self.encoding == 'hex':
2241
2157
  hex_bytes = content.encode('utf-8')
2242
2158
  message_bytes = binascii.unhexlify(hex_bytes)
2243
- value['content'] = message_bytes.decode('utf-8')
2159
+ value.setContent(message_bytes.decode('utf-8'))
2244
2160
  else:
2245
2161
  value = v
2246
2162
  return value
2247
2163
 
2248
2164
  def v_element(self, v):
2249
- index = self.getRuntimeValue(v['index'])
2250
- target = self.getVariable(v['target'])
2251
- val = self.getSymbolValue(target)
2252
- content = val['content']
2253
- value = {}
2254
- value['type'] = 'int' if isinstance(content, int) else 'text'
2255
- if type(content) == list:
2256
- try:
2257
- value['content'] = content[index]
2258
- return value
2259
- except:
2260
- RuntimeError(self.program, 'Index out of range')
2261
- # lino = self.program.code[self.program.pc]['lino']
2262
- 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
2263
2179
 
2264
2180
  def v_elements(self, v):
2265
- var = self.getVariable(v['name'])
2266
- value = {}
2267
- value['type'] = 'int'
2268
- value['content'] = var['elements']
2269
- 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())
2270
2185
 
2271
2186
  def v_empty(self, v):
2272
- value = {}
2273
- value['type'] = 'text'
2274
- value['content'] = ''
2275
- return value
2187
+ return ECValue(domain=self.getName(), type='str', content='' )
2276
2188
 
2277
2189
  def v_encode(self, v):
2278
- content = self.getRuntimeValue(v['content'])
2279
- value = {}
2280
- value['type'] = 'text'
2190
+ content = self.textify(v.getContent())
2191
+ value = ECValue(domain=self.getName(), type='str')
2281
2192
  if self.encoding == 'utf-8':
2282
- value['content'] = content.encode('utf-8')
2193
+ value.setContent(content.encode('utf-8'))
2283
2194
  elif self.encoding == 'base64':
2284
2195
  data_bytes = content.encode('ascii')
2285
2196
  base64_bytes = base64.b64encode(data_bytes)
2286
- value['content'] = base64_bytes.decode('ascii')
2197
+ value.setContent(base64_bytes.decode('ascii'))
2287
2198
  elif self.encoding == 'hex':
2288
2199
  data_bytes = content.encode('utf-8')
2289
2200
  hex_bytes = binascii.hexlify(data_bytes)
2290
- value['content'] = hex_bytes.decode('utf-8')
2201
+ value.setContent(hex_bytes.decode('utf-8'))
2291
2202
  else:
2292
2203
  value = v
2293
2204
  return value
2294
2205
 
2295
2206
  def v_error(self, v):
2296
2207
  global errorCode, errorReason
2297
- value = {}
2298
- if v['item'] == 'errorCode':
2299
- value['type'] = 'int'
2300
- value['content'] = errorCode
2301
- elif v['item'] == 'errorReason':
2302
- value['type'] = 'text'
2303
- value['content'] = errorReason
2304
- elif v['item'] == 'sshError':
2305
- record = self.getVariable(v['name'])
2306
- value['type'] = 'text'
2307
- 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 '')
2308
2217
  return value
2309
2218
 
2310
2219
  def v_files(self, v):
2311
- v = self.getRuntimeValue(v['target'])
2312
- value = {}
2313
- value['type'] = 'text'
2314
- value['content'] = os.listdir(v)
2315
- return value
2220
+ path = self.textify(v.target)
2221
+ return ECValue(domain=self.getName(), type='str', content=json.dumps(os.listdir(path)))
2316
2222
 
2317
2223
  def v_float(self, v):
2318
- val = self.getRuntimeValue(v['content'])
2319
- value = {}
2320
- value['type'] = 'float'
2224
+ val = self.textify(v.getContent())
2225
+ value = ECValue(domain=self.getName(), type='float')
2321
2226
  try:
2322
- value['content'] = float(val)
2227
+ value.setContent(float(val))
2323
2228
  except:
2324
2229
  RuntimeWarning(self.program, f'Value cannot be parsed as floating-point')
2325
- value['content'] = 0.0
2230
+ value.setContent(0.0)
2326
2231
  return value
2327
2232
 
2328
2233
  def v_from(self, v):
2329
- content = self.getRuntimeValue(v['content'])
2330
- start = self.getRuntimeValue(v['start'])
2331
- to = v['to']
2332
- if not to == None:
2333
- to = self.getRuntimeValue(to)
2334
- value = {}
2335
- value['type'] = 'text'
2336
- if to == None:
2337
- value['content'] = content[start:]
2338
- else:
2339
- value['content'] = content[start:to]
2340
- 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])
2341
2242
 
2342
2243
  def v_hash(self, v):
2343
- hashval = self.getRuntimeValue(v['content'])
2344
- value = {}
2345
- value['type'] = 'text'
2346
- value['content'] = hashlib.sha256(hashval.encode('utf-8')).hexdigest()
2347
- return value
2244
+ hashval = self.textify(v.getContent())
2245
+ return ECValue(domain=self.getName(), type='str', content=hashlib.sha256(hashval.encode('utf-8')).hexdigest())
2348
2246
 
2349
2247
  def v_index(self, v):
2350
- value = {}
2351
- value['type'] = 'int'
2352
- value['content'] = self.getVariable(v['name'])['index']
2353
- return value
2248
+ record = self.getVariable(v.name)
2249
+ object = self.getObject(record)
2250
+ return ECValue(domain=self.getName(), type='int', content=object.getIndex())
2354
2251
 
2355
2252
  def v_indexOf(self, v):
2356
- value = v['value']
2253
+ value = v.value
2357
2254
  if value == None:
2358
- value = self.getSymbolValue(v['variable'])['content']
2255
+ value = self.getSymbolValue(v.variable).getContent()
2359
2256
  else:
2360
- value = self.getRuntimeValue(value)
2361
- target = self.getVariable(v['target'])
2362
- data = self.getSymbolValue(target)['content']
2363
- index = -1
2364
- for n in range(0, len(data)):
2365
- if data[n] == value:
2366
- index = n
2367
- break
2368
- retval = {}
2369
- retval['type'] = 'int'
2370
- retval['content'] = index
2371
- 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)
2372
2263
 
2373
2264
  def v_integer(self, v):
2374
- val = self.getRuntimeValue(v['content'])
2375
- value = {}
2376
- value['type'] = 'int'
2377
- value['content'] = int(val)
2378
- return value
2265
+ val = self.textify(v.getValue())
2266
+ return ECValue(domain=self.getName(), type='int', content=int(val))
2379
2267
 
2380
2268
  def v_json(self, v):
2381
- item = self.getRuntimeValue(v['content'])
2382
- value = {}
2383
- value['type'] = 'object'
2269
+ item = self.textify(v.getContent())
2270
+ value = ECValue(domain=self.getName())
2384
2271
  try:
2385
- 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)
2386
2277
  except:
2387
2278
  value = None
2388
2279
  return value
2389
2280
 
2390
2281
  def v_keys(self, v):
2391
- value = {}
2392
- value['type'] = 'int'
2393
- value['content'] = list(self.getRuntimeValue(v['name']).keys())
2394
- return value
2282
+ value = self.textify(v.name)
2283
+ return ECValue(domain=self.getName(), type='list', content=list(value.keys())) # type: ignore
2395
2284
 
2396
2285
  def v_left(self, v):
2397
- content = self.getRuntimeValue(v['content'])
2398
- count = self.getRuntimeValue(v['count'])
2399
- value = {}
2400
- value['type'] = 'text'
2401
- value['content'] = content[0:count]
2402
- 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])
2403
2289
 
2404
2290
  def v_lengthOf(self, v):
2405
- content = self.getRuntimeValue(v['content'])
2291
+ content = self.textify(v.getContent())
2406
2292
  if type(content) == str:
2407
- value = {}
2408
- value['type'] = 'int'
2409
- value['content'] = len(content)
2410
- return value
2293
+ return ECValue(domain=self.getName(), type='int', content=len(content))
2411
2294
  RuntimeError(self.program, 'Value is not a string')
2412
2295
 
2413
2296
  def v_lowercase(self, v):
2414
- content = self.getRuntimeValue(v['content'])
2415
- value = {}
2416
- value['type'] = 'text'
2417
- value['content'] = content.lower()
2418
- return value
2419
-
2420
- def v_memory(self, v):
2421
- process: Process = Process(os.getpid())
2422
- megabytes: float = process.memory_info().rss / (1024 * 1024)
2423
- value = {}
2424
- value['type'] = 'float'
2425
- value['content'] = megabytes
2426
- return value
2297
+ content = self.textify(v.getValue())
2298
+ return ECValue(domain=self.getName(), type='str', content=content.lower())
2427
2299
 
2428
2300
  def v_message(self, v):
2429
- value = {}
2430
- value['type'] = 'text'
2431
- value['content'] = self.program.message
2432
- return value
2301
+ return ECValue(domain=self.getName(), type='str', content=self.program.message)
2433
2302
 
2434
2303
  def v_modification(self, v):
2435
- fileName = self.getRuntimeValue(v['fileName'])
2304
+ fileName = self.textify(v['fileName'])
2436
2305
  ts = int(os.stat(fileName).st_mtime)
2437
- value = {}
2438
- value['type'] = 'int'
2439
- value['content'] = ts
2440
- return value
2306
+ return ECValue(domain=self.getName(), type='int', content=ts)
2441
2307
 
2442
2308
  def v_modulo(self, v):
2443
- val = self.getRuntimeValue(v['content'])
2444
- modval = self.getRuntimeValue(v['modval'])
2445
- value = {}
2446
- value['type'] = 'int'
2447
- value['content'] = val % modval
2448
- 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)
2449
2312
 
2450
2313
  def v_newline(self, v):
2451
- value = {}
2452
- value['type'] = 'text'
2453
- value['content'] = '\n'
2454
- return value
2314
+ return ECValue(domain=self.getName(), type='str', content='\n')
2455
2315
 
2456
2316
  def v_now(self, v):
2457
- value = {}
2458
- value['type'] = 'int'
2459
- value['content'] = int(time.time())
2460
- return value
2317
+ return ECValue(domain=self.getName(), type='int', content=int(time.time()))
2461
2318
 
2462
2319
  def v_position(self, v):
2463
- needle = self.getRuntimeValue(v['needle'])
2464
- haystack = self.getRuntimeValue(v['haystack'])
2465
- last = v['last']
2466
- value = {}
2467
- value['type'] = 'int'
2468
- value['content'] = haystack.rfind(needle) if last else haystack.find(needle)
2469
- 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))
2470
2324
 
2471
2325
  def v_prettify(self, v):
2472
- item = self.getRuntimeValue(v['content'])
2473
- value = {}
2474
- value['type'] = 'text'
2475
- value['content'] = json.dumps(item, indent=4)
2476
- return value
2326
+ item = self.textify(v.getContent())
2327
+ return ECValue(domain=self.getName(), type='str', content=json.dumps(item, indent=4))
2477
2328
 
2478
2329
  def v_property(self, v):
2479
- propertyValue = self.getRuntimeValue(v['name'])
2480
- if 'target' in v:
2481
- targetName = v['target']
2482
- target = self.getVariable(targetName)
2483
- targetValue = self.getRuntimeValue(target)
2484
- else:
2485
- targetValue = self.getRuntimeValue(v['value'])
2330
+ propertyName = v.name
2331
+ propertyValue = self.textify(propertyName)
2332
+ targetName = v.target
2333
+ targetValue = self.textify(targetName)
2486
2334
  try:
2487
- val = targetValue[propertyValue]
2335
+ targetObject = json.loads(targetValue)
2488
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:
2489
2341
  RuntimeError(self.program, f'This value does not have the property \'{propertyValue}\'')
2490
- return None
2491
- value = {}
2492
- value['content'] = val
2493
- if isinstance(v, numbers.Number):
2494
- value['type'] = 'int'
2495
- else:
2496
- value['type'] = 'text'
2342
+ value = targetObject[propertyValue]
2343
+ if isinstance(value, ECValue):
2344
+ value = self.textify(value)
2497
2345
  return value
2498
2346
 
2499
2347
  def v_random(self, v):
2500
- limit = self.getRuntimeValue(v['content'])
2501
- value = {}
2502
- value['type'] = 'int'
2503
- value['content'] = random.randrange(0, limit)
2504
- return value
2348
+ limit = self.textify(v.getValue())
2349
+ return ECValue(domain=self.getName(), type='int', content=random.randrange(0, limit))
2505
2350
 
2506
2351
  def v_right(self, v):
2507
- content = self.getRuntimeValue(v['content'])
2508
- count = self.getRuntimeValue(v['count'])
2509
- value = {}
2510
- value['type'] = 'text'
2511
- value['content'] = content[-count:]
2512
- 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:])
2513
2355
 
2514
2356
  def v_sin(self, v):
2515
- angle = self.getRuntimeValue(v['angle'])
2516
- radius = self.getRuntimeValue(v['radius'])
2517
- value = {}
2518
- value['type'] = 'int'
2519
- value['content'] = round(math.sin(angle * 0.01745329) * radius)
2520
- 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))
2521
2360
 
2522
2361
  def v_stringify(self, v):
2523
- item = self.getRuntimeValue(v['content'])
2524
- value = {}
2525
- value['type'] = 'text'
2526
- value['content'] = json.dumps(item)
2527
- 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))
2528
2365
 
2529
2366
  # This is used by the expression evaluator to get the value of a symbol
2530
- def v_symbol(self, value):
2531
- name = value['name']
2532
- symbolRecord = self.program.getSymbolRecord(name)
2533
- keyword = symbolRecord['keyword']
2534
- if keyword == 'variable':
2535
- 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)
2536
2375
  elif keyword == 'ssh':
2537
- v = {}
2538
- v['type'] = 'boolean'
2539
- v['content'] = True if 'ssh' in symbolRecord and symbolRecord['ssh'] != None else False
2540
- return v
2376
+ return ECValue(domain=self.getName(), type='boolean', content=True if 'ssh' in record and record['ssh'] != None else False)
2541
2377
  else:
2542
2378
  return None
2543
2379
 
2544
2380
  def v_system(self, v):
2545
- command = self.getRuntimeValue(v['command'])
2381
+ command = self.textify(v.getContent())
2546
2382
  result = os.popen(command).read()
2547
- value = {}
2548
- value['type'] = 'text'
2549
- value['content'] = result
2550
- return value
2383
+ return ECValue(domain=self.getName(), type='str', content=result)
2551
2384
 
2552
2385
  def v_tab(self, v):
2553
- value = {}
2554
- value['type'] = 'text'
2555
- value['content'] = '\t'
2556
- return value
2386
+ return ECValue(domain=self.getName(), type='str', content='\t')
2557
2387
 
2558
2388
  def v_tan(self, v):
2559
- angle = self.getRuntimeValue(v['angle'])
2560
- radius = self.getRuntimeValue(v['radius'])
2561
- value = {}
2562
- value['type'] = 'int'
2563
- value['content'] = round(math.tan(angle * 0.01745329) * radius)
2564
- 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))
2565
2392
 
2566
2393
  def v_ticker(self, v):
2567
- value = {}
2568
- value['type'] = 'int'
2569
- value['content'] = self.program.ticker
2570
- return value
2394
+ return ECValue(domain=self.getName(), type='int', content=self.program.ticker)
2571
2395
 
2572
2396
  def v_timestamp(self, v):
2573
- value = {}
2574
- value['type'] = 'int'
2575
- fmt = v['format']
2397
+ value = ECValue(domain=self.getName(), type='int')
2398
+ fmt = v.format
2576
2399
  if fmt == None:
2577
- value['content'] = int(time.time())
2400
+ value.setContent(int(time.time()))
2578
2401
  else:
2579
- fmt = self.getRuntimeValue(fmt)
2580
- dt = self.getRuntimeValue(v['datime'])
2402
+ fmt = self.textify(fmt)
2403
+ dt = self.textify(v.timestamp)
2581
2404
  spec = datetime.strptime(dt, fmt)
2582
2405
  t = datetime.now().replace(hour=spec.hour, minute=spec.minute, second=spec.second, microsecond=0)
2583
- value['content'] = int(t.timestamp())
2406
+ value.setContent(int(t.timestamp()))
2584
2407
  return value
2585
2408
 
2586
2409
  def v_today(self, v):
2587
- value = {}
2588
- value['type'] = 'int'
2589
- value['content'] = int(datetime.combine(datetime.now().date(),datetime.min.time()).timestamp())*1000
2590
- return value
2410
+ return ECValue(domain=self.getName(), type='int', content=int(datetime.combine(datetime.now().date(),datetime.min.time()).timestamp()) * 1000)
2591
2411
 
2592
2412
  def v_trim(self, v):
2593
- v = self.getRuntimeValue(v['content'])
2594
- value = {}
2595
- value['type'] = 'text'
2596
- value['content'] = v.strip()
2597
- return value
2413
+ content = v.getContent()
2414
+ content = self.textify(content)
2415
+ return ECValue(domain=self.getName(), type='str', content=content.strip())
2598
2416
 
2599
2417
  def v_type(self, v):
2600
- value = {}
2601
- value['type'] = 'text'
2602
- val = self.getRuntimeValue(v['value'])
2418
+ value = ECValue(domain=self.getName(), type='str')
2419
+ val = self.textify(v['value'])
2603
2420
  if val is None:
2604
- value['content'] = 'none'
2421
+ value.setContent('none')
2605
2422
  elif type(val) is str:
2606
- value['content'] = 'text'
2423
+ value.setContent('str')
2607
2424
  elif type(val) is int:
2608
- value['content'] = 'numeric'
2425
+ value.setContent('numeric')
2609
2426
  elif type(val) is bool:
2610
- value['content'] = 'boolean'
2427
+ value.setContent('boolean')
2611
2428
  elif type(val) is list:
2612
- value['content'] = 'list'
2429
+ value.setContent('list')
2613
2430
  elif type(val) is dict:
2614
- value['content'] = 'object'
2431
+ value.setContent('dict')
2615
2432
  return value
2616
2433
 
2617
2434
  def v_uppercase(self, v):
2618
- content = self.getRuntimeValue(v['content'])
2619
- value = {}
2620
- value['type'] = 'text'
2621
- value['content'] = content.upper()
2622
- return value
2435
+ content = self.textify(v.getContent())
2436
+ return ECValue(domain=self.getName(), type='str', content=content.upper())
2623
2437
 
2624
2438
  def v_valueOf(self, v):
2625
- v = self.getRuntimeValue(v['content'])
2626
- value = {}
2627
- value['type'] = 'int'
2628
- 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()
2629
2448
  return value
2630
2449
 
2631
2450
  def v_weekday(self, v):
2632
- value = {}
2633
- value['type'] = 'int'
2634
- value['content'] = datetime.today().weekday()
2635
- return value
2451
+ return ECValue(domain=self.getName(), type='int', content=datetime.today().weekday())
2636
2452
 
2637
2453
  #############################################################################
2638
2454
  # Compile a condition
2639
2455
  def compileCondition(self):
2640
- condition = Object()
2641
- condition.negate = False
2456
+ condition = ECValue()
2457
+ condition.negate = False # type: ignore
2642
2458
 
2643
2459
  token = self.getToken()
2644
2460
 
2645
2461
  if token == 'not':
2646
- condition.type = 'not'
2647
- condition.value = self.nextValue()
2462
+ condition.type = 'not' # type: ignore
2463
+ condition.value = self.nextValue() # type: ignore
2648
2464
  return condition
2649
2465
 
2650
2466
  elif token == 'error':
@@ -2653,21 +2469,21 @@ class Core(Handler):
2653
2469
  if self.nextIsSymbol():
2654
2470
  record = self.getSymbolRecord()
2655
2471
  if record['keyword'] == 'ssh':
2656
- condition.type = 'sshError'
2657
- condition.target = record['name']
2472
+ condition.type = 'sshError' # type: ignore
2473
+ condition.target = record['name'] # type: ignore
2658
2474
  return condition
2659
2475
  return None
2660
2476
 
2661
2477
  elif token == 'file':
2662
2478
  path = self.nextValue()
2663
- condition.path = path
2664
- condition.type = 'exists'
2479
+ condition.path = path # type: ignore
2480
+ condition.type = 'exists' # type: ignore
2665
2481
  self.skip('on')
2666
2482
  if self.nextIsSymbol():
2667
2483
  record = self.getSymbolRecord()
2668
2484
  if record['keyword'] == 'ssh':
2669
- condition.type = 'sshExists'
2670
- condition.target = record['name']
2485
+ condition.type = 'sshExists' # type: ignore
2486
+ condition.target = record['name'] # type: ignore
2671
2487
  token = self.nextToken()
2672
2488
  else: token = self.getToken()
2673
2489
  if token == 'exists':
@@ -2675,7 +2491,7 @@ class Core(Handler):
2675
2491
  elif token == 'does':
2676
2492
  if self.nextIs('not'):
2677
2493
  if self.nextIs('exist'):
2678
- condition.negate = not condition.negate
2494
+ condition.negate = not condition.negate # type: ignore
2679
2495
  return condition
2680
2496
  return None
2681
2497
 
@@ -2683,16 +2499,16 @@ class Core(Handler):
2683
2499
  if value == None:
2684
2500
  return None
2685
2501
 
2686
- condition.value1 = value
2502
+ condition.value1 = value # type: ignore
2687
2503
  token = self.peek()
2688
- condition.type = token
2504
+ condition.type = token # type: ignore
2689
2505
 
2690
2506
  if token == 'has':
2691
2507
  self.nextToken()
2692
2508
  if self.nextToken() == 'property':
2693
2509
  prop = self.nextValue()
2694
- condition.type = 'hasProperty'
2695
- condition.property = prop
2510
+ condition.type = 'hasProperty' # type: ignore
2511
+ condition.property = prop # type: ignore
2696
2512
  return condition
2697
2513
  return None
2698
2514
 
@@ -2703,48 +2519,48 @@ class Core(Handler):
2703
2519
  if token == 'have':
2704
2520
  if self.nextToken() == 'property':
2705
2521
  prop = self.nextValue()
2706
- condition.type = 'hasProperty'
2707
- condition.property = prop
2708
- 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
2709
2525
  return condition
2710
2526
  elif token == 'include':
2711
2527
  value = self.nextValue()
2712
- condition.type = 'includes'
2713
- condition.value2 = value
2714
- 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
2715
2531
  return condition
2716
2532
  return None
2717
2533
 
2718
2534
  if token in ['starts', 'ends']:
2719
2535
  self.nextToken()
2720
2536
  if self.nextToken() == 'with':
2721
- condition.value2 = self.nextValue()
2537
+ condition.value2 = self.nextValue() # type: ignore
2722
2538
  return condition
2723
2539
 
2724
2540
  if token == 'includes':
2725
- condition.value2 = self.nextValue()
2541
+ condition.value2 = self.nextValue() # type: ignore
2726
2542
  return condition
2727
2543
 
2728
2544
  if token == 'is':
2729
2545
  token = self.nextToken()
2730
2546
  if self.peek() == 'not':
2731
2547
  self.nextToken()
2732
- condition.negate = True
2548
+ condition.negate = True # type: ignore
2733
2549
  token = self.nextToken()
2734
- condition.type = token
2550
+ condition.type = token # type: ignore
2735
2551
  if token in ['numeric', 'string', 'boolean', 'none', 'list', 'object', 'even', 'odd', 'empty']:
2736
2552
  return condition
2737
2553
  if token in ['greater', 'less']:
2738
2554
  if self.nextToken() == 'than':
2739
- condition.value2 = self.nextValue()
2555
+ condition.value2 = self.nextValue() # type: ignore
2740
2556
  return condition
2741
- condition.type = 'is'
2742
- condition.value2 = self.getValue()
2557
+ condition.type = 'is' # type: ignore
2558
+ condition.value2 = self.getValue() # type: ignore
2743
2559
  return condition
2744
2560
 
2745
- if condition.value1:
2561
+ if condition.value1: # type: ignore
2746
2562
  # It's a boolean if
2747
- condition.type = 'boolean'
2563
+ condition.type = 'boolean' # type: ignore
2748
2564
  return condition
2749
2565
 
2750
2566
  self.warning(f'Core.compileCondition: I can\'t get a conditional:')
@@ -2761,7 +2577,7 @@ class Core(Handler):
2761
2577
  # Condition handlers
2762
2578
 
2763
2579
  def c_boolean(self, condition):
2764
- value = self.getRuntimeValue(condition.value1)
2580
+ value = self.textify(condition.value1)
2765
2581
  if type(value) == bool:
2766
2582
  return not value if condition.negate else value
2767
2583
  elif type(value) == int:
@@ -2776,33 +2592,46 @@ class Core(Handler):
2776
2592
  return False
2777
2593
 
2778
2594
  def c_empty(self, condition):
2779
- value = self.getRuntimeValue(condition.value1)
2595
+ value = self.textify(condition.value1)
2780
2596
  if value == None:
2781
2597
  comparison = True
2782
- else:
2598
+ elif type(value) == str or type(value) == list or type(value) == dict:
2783
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))
2784
2605
  return not comparison if condition.negate else comparison
2785
2606
 
2786
2607
  def c_ends(self, condition):
2787
- value1 = self.getRuntimeValue(condition.value1)
2788
- value2 = self.getRuntimeValue(condition.value2)
2608
+ value1 = self.textify(condition.value1)
2609
+ value2 = self.textify(condition.value2)
2789
2610
  return value1.endswith(value2)
2790
2611
 
2791
2612
  def c_even(self, condition):
2792
- return self.getRuntimeValue(condition.value1) % 2 == 0
2613
+ return self.textify(condition.value1) % 2 == 0
2793
2614
 
2794
2615
  def c_exists(self, condition):
2795
- path = self.getRuntimeValue(condition.path)
2616
+ path = self.textify(condition.path)
2796
2617
  comparison = os.path.exists(path)
2797
2618
  return not comparison if condition.negate else comparison
2798
2619
 
2799
2620
  def c_greater(self, condition):
2800
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)}')
2801
2624
  return comparison <= 0 if condition.negate else comparison > 0
2802
2625
 
2803
2626
  def c_hasProperty(self, condition):
2804
- value = self.getRuntimeValue(condition.value1)
2805
- 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
2806
2635
  try:
2807
2636
  value[prop]
2808
2637
  hasProp = True
@@ -2811,40 +2640,43 @@ class Core(Handler):
2811
2640
  return not hasProp if condition.negate else hasProp
2812
2641
 
2813
2642
  def c_includes(self, condition):
2814
- value1 = self.getRuntimeValue(condition.value1)
2815
- value2 = self.getRuntimeValue(condition.value2)
2643
+ value1 = self.textify(condition.value1)
2644
+ value2 = self.textify(condition.value2)
2816
2645
  includes = value2 in value1
2817
2646
  return not includes if condition.negate else includes
2818
2647
 
2819
2648
  def c_is(self, condition):
2820
2649
  comparison = self.program.compare(condition.value1, condition.value2)
2650
+ if comparison == None: comparison = 1
2821
2651
  return comparison != 0 if condition.negate else comparison == 0
2822
2652
 
2823
2653
  def c_less(self, condition):
2824
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)}')
2825
2657
  return comparison >= 0 if condition.negate else comparison < 0
2826
2658
 
2827
2659
  def c_list(self, condition):
2828
- comparison = type(self.getRuntimeValue(condition.value1)) is list
2660
+ comparison = type(self.textify(condition.value1)) is list
2829
2661
  return not comparison if condition.negate else comparison
2830
2662
 
2831
2663
  def c_numeric(self, condition):
2832
- comparison = type(self.getRuntimeValue(condition.value1)) is int
2664
+ comparison = type(self.textify(condition.value1)) is int
2833
2665
  return not comparison if condition.negate else comparison
2834
2666
 
2835
2667
  def c_none(self, condition):
2836
- comparison = self.getRuntimeValue(condition.value1) is None
2668
+ comparison = self.textify(condition.value1) is None
2837
2669
  return not comparison if condition.negate else comparison
2838
2670
 
2839
2671
  def c_not(self, condition):
2840
- return not self.getRuntimeValue(condition.value)
2672
+ return not self.textify(condition.value)
2841
2673
 
2842
2674
  def c_object(self, condition):
2843
- comparison = type(self.getRuntimeValue(condition.value1)) is dict
2675
+ comparison = type(self.textify(condition.value1)) is dict
2844
2676
  return not comparison if condition.negate else comparison
2845
2677
 
2846
2678
  def c_odd(self, condition):
2847
- return self.getRuntimeValue(condition.value1) % 2 == 1
2679
+ return self.textify(condition.value1) % 2 == 1
2848
2680
 
2849
2681
  def c_sshError(self, condition):
2850
2682
  target = self.getVariable(condition.target)
@@ -2854,7 +2686,7 @@ class Core(Handler):
2854
2686
  return not test if condition.negate else test
2855
2687
 
2856
2688
  def c_sshExists(self, condition):
2857
- path = self.getRuntimeValue(condition.path)
2689
+ path = self.textify(condition.path)
2858
2690
  ssh = self.getVariable(condition.target)
2859
2691
  sftp = ssh['sftp']
2860
2692
  try:
@@ -2865,10 +2697,10 @@ class Core(Handler):
2865
2697
  return not comparison if condition.negate else comparison
2866
2698
 
2867
2699
  def c_starts(self, condition):
2868
- value1 = self.getRuntimeValue(condition.value1)
2869
- value2 = self.getRuntimeValue(condition.value2)
2700
+ value1 = self.textify(condition.value1)
2701
+ value2 = self.textify(condition.value2)
2870
2702
  return value1.startswith(value2)
2871
2703
 
2872
2704
  def c_string(self, condition):
2873
- comparison = type(self.getRuntimeValue(condition.value1)) is str
2705
+ comparison = type(self.textify(condition.value1)) is str
2874
2706
  return not comparison if condition.negate else comparison