easycoder 250118.1__py2.py3-none-any.whl → 251103.4__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/__init__.py +4 -1
- easycoder/close.png +0 -0
- easycoder/ec_border.py +63 -0
- easycoder/ec_classes.py +10 -6
- easycoder/ec_compiler.py +48 -19
- easycoder/ec_condition.py +2 -1
- easycoder/ec_core.py +632 -281
- easycoder/ec_handler.py +4 -2
- easycoder/ec_keyboard.py +439 -0
- easycoder/ec_program.py +43 -38
- easycoder/ec_pyside.py +1545 -0
- easycoder/ec_timestamp.py +1 -1
- easycoder/tick.png +0 -0
- {easycoder-250118.1.dist-info → easycoder-251103.4.dist-info}/METADATA +30 -30
- easycoder-251103.4.dist-info/RECORD +19 -0
- {easycoder-250118.1.dist-info → easycoder-251103.4.dist-info}/WHEEL +1 -1
- easycoder/README.md +0 -6
- easycoder/ec_graphics.py +0 -314
- easycoder/ec_gutils.py +0 -84
- easycoder-250118.1.dist-info/RECORD +0 -17
- {easycoder-250118.1.dist-info → easycoder-251103.4.dist-info}/entry_points.txt +0 -0
- {easycoder-250118.1.dist-info → easycoder-251103.4.dist-info/licenses}/LICENSE +0 -0
easycoder/ec_core.py
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import json, math, hashlib, threading, os, subprocess,
|
|
1
|
+
import json, math, hashlib, threading, os, subprocess, time
|
|
2
|
+
import numbers, base64, binascii, random, requests, paramiko
|
|
3
|
+
from copy import deepcopy
|
|
2
4
|
from psutil import Process
|
|
3
|
-
from datetime import datetime
|
|
4
|
-
from
|
|
5
|
-
from .ec_classes import FatalError, RuntimeWarning, RuntimeError, AssertionError, Condition
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
from .ec_classes import FatalError, RuntimeWarning, RuntimeError, AssertionError, NoValueError, NoValueRuntimeError, Object
|
|
6
7
|
from .ec_handler import Handler
|
|
7
8
|
from .ec_timestamp import getTimestamp
|
|
8
9
|
|
|
@@ -14,6 +15,29 @@ class Core(Handler):
|
|
|
14
15
|
|
|
15
16
|
def getName(self):
|
|
16
17
|
return 'core'
|
|
18
|
+
|
|
19
|
+
def noSymbolWarning(self):
|
|
20
|
+
self.warning(f'Symbol "{self.getToken()}" not found')
|
|
21
|
+
|
|
22
|
+
def processOr(self, command, orHere):
|
|
23
|
+
self.add(command)
|
|
24
|
+
if self.peek() == 'or':
|
|
25
|
+
self.nextToken()
|
|
26
|
+
self.nextToken()
|
|
27
|
+
# Add a 'goto' to skip the 'or'
|
|
28
|
+
cmd = {}
|
|
29
|
+
cmd['lino'] = command['lino']
|
|
30
|
+
cmd['domain'] = 'core'
|
|
31
|
+
cmd['keyword'] = 'gotoPC'
|
|
32
|
+
cmd['goto'] = 0
|
|
33
|
+
cmd['debug'] = False
|
|
34
|
+
skip = self.getPC()
|
|
35
|
+
self.add(cmd)
|
|
36
|
+
# Process the 'or'
|
|
37
|
+
self.getCommandAt(orHere)['or'] = self.getPC()
|
|
38
|
+
self.compileOne()
|
|
39
|
+
# Fixup the skip
|
|
40
|
+
self.getCommandAt(skip)['goto'] = self.getPC()
|
|
17
41
|
|
|
18
42
|
#############################################################################
|
|
19
43
|
# Keyword handlers
|
|
@@ -26,7 +50,7 @@ class Core(Handler):
|
|
|
26
50
|
if self.nextToken() == 'to':
|
|
27
51
|
if self.nextIsSymbol():
|
|
28
52
|
symbolRecord = self.getSymbolRecord()
|
|
29
|
-
if symbolRecord['
|
|
53
|
+
if symbolRecord['hasValue']:
|
|
30
54
|
if self.peek() == 'giving':
|
|
31
55
|
# This variable must be treated as a second value
|
|
32
56
|
command['value2'] = self.getValue()
|
|
@@ -57,7 +81,7 @@ class Core(Handler):
|
|
|
57
81
|
except:
|
|
58
82
|
value2 = None
|
|
59
83
|
target = self.getVariable(command['target'])
|
|
60
|
-
if not target['
|
|
84
|
+
if not target['hasValue']:
|
|
61
85
|
self.variableDoesNotHoldAValueError(target['name'])
|
|
62
86
|
targetValue = self.getSymbolValue(target)
|
|
63
87
|
if targetValue == None:
|
|
@@ -87,11 +111,11 @@ class Core(Handler):
|
|
|
87
111
|
if self.nextIs('to'):
|
|
88
112
|
if self.nextIsSymbol():
|
|
89
113
|
symbolRecord = self.getSymbolRecord()
|
|
90
|
-
if symbolRecord['
|
|
114
|
+
if symbolRecord['hasValue']:
|
|
91
115
|
command['target'] = symbolRecord['name']
|
|
92
116
|
self.add(command)
|
|
93
117
|
return True
|
|
94
|
-
self.warning(f'Core.append: Variable
|
|
118
|
+
self.warning(f'Core.append: Variable {symbolRecord["name"]} does not hold a value')
|
|
95
119
|
return False
|
|
96
120
|
|
|
97
121
|
def r_append(self, command):
|
|
@@ -106,14 +130,6 @@ class Core(Handler):
|
|
|
106
130
|
self.putSymbolValue(target, val)
|
|
107
131
|
return self.nextPC()
|
|
108
132
|
|
|
109
|
-
# Define an array
|
|
110
|
-
def k_array(self, command):
|
|
111
|
-
return self.compileVariable(command)
|
|
112
|
-
|
|
113
|
-
def r_array(self, command):
|
|
114
|
-
return self.nextPC()
|
|
115
|
-
|
|
116
|
-
# Assertion
|
|
117
133
|
#assert {condition} [with {message}]
|
|
118
134
|
def k_assert(self, command):
|
|
119
135
|
command['test'] = self.nextCondition()
|
|
@@ -122,7 +138,7 @@ class Core(Handler):
|
|
|
122
138
|
command['with'] = self.nextValue()
|
|
123
139
|
else:
|
|
124
140
|
command['with'] = None
|
|
125
|
-
self.
|
|
141
|
+
self.add(command)
|
|
126
142
|
return True
|
|
127
143
|
|
|
128
144
|
def r_assert(self, command):
|
|
@@ -139,29 +155,30 @@ class Core(Handler):
|
|
|
139
155
|
cmd['keyword'] = 'end'
|
|
140
156
|
cmd['debug'] = True
|
|
141
157
|
cmd['lino'] = command['lino']
|
|
142
|
-
self.
|
|
158
|
+
self.add(cmd)
|
|
143
159
|
return self.nextPC()
|
|
144
160
|
else:
|
|
145
161
|
return self.compileFromHere(['end'])
|
|
146
162
|
|
|
147
|
-
# Clear (set False)
|
|
148
163
|
# clear {variable}
|
|
149
164
|
def k_clear(self, command):
|
|
150
165
|
if self.nextIsSymbol():
|
|
151
166
|
target = self.getSymbolRecord()
|
|
152
|
-
|
|
153
|
-
|
|
167
|
+
command['target'] = target['name']
|
|
168
|
+
if target['hasValue'] or target['keyword'] == 'ssh':
|
|
154
169
|
self.add(command)
|
|
155
170
|
return True
|
|
156
171
|
return False
|
|
157
172
|
|
|
158
173
|
def r_clear(self, command):
|
|
159
174
|
target = self.getVariable(command['target'])
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
175
|
+
if target['keyword'] == 'ssh':
|
|
176
|
+
target['ssh'] = None
|
|
177
|
+
else:
|
|
178
|
+
val = {}
|
|
179
|
+
val['type'] = 'boolean'
|
|
180
|
+
val['content'] = False
|
|
181
|
+
self.putSymbolValue(target, val)
|
|
165
182
|
return self.nextPC()
|
|
166
183
|
|
|
167
184
|
# Close a file
|
|
@@ -200,7 +217,11 @@ class Core(Handler):
|
|
|
200
217
|
# Debug the script
|
|
201
218
|
def k_debug(self, command):
|
|
202
219
|
token = self.peek()
|
|
203
|
-
if token
|
|
220
|
+
if token == 'compile':
|
|
221
|
+
self.compiler.debugCompile = True
|
|
222
|
+
self.nextToken()
|
|
223
|
+
return True
|
|
224
|
+
elif token in ['step', 'stop', 'program', 'custom']:
|
|
204
225
|
command['mode'] = token
|
|
205
226
|
self.nextToken()
|
|
206
227
|
elif token == 'stack':
|
|
@@ -220,7 +241,9 @@ class Core(Handler):
|
|
|
220
241
|
return True
|
|
221
242
|
|
|
222
243
|
def r_debug(self, command):
|
|
223
|
-
if command['mode'] == '
|
|
244
|
+
if command['mode'] == 'compile':
|
|
245
|
+
self.program.debugStep = True
|
|
246
|
+
elif command['mode'] == 'step':
|
|
224
247
|
self.program.debugStep = True
|
|
225
248
|
elif command['mode'] == 'stop':
|
|
226
249
|
self.program.debugStep = False
|
|
@@ -245,46 +268,48 @@ class Core(Handler):
|
|
|
245
268
|
def k_decrement(self, command):
|
|
246
269
|
if self.nextIsSymbol():
|
|
247
270
|
symbolRecord = self.getSymbolRecord()
|
|
248
|
-
if symbolRecord['
|
|
271
|
+
if symbolRecord['hasValue']:
|
|
249
272
|
command['target'] = self.getToken()
|
|
250
273
|
self.add(command)
|
|
251
274
|
return True
|
|
252
|
-
self.warning(f'Core.decrement: Variable
|
|
275
|
+
self.warning(f'Core.decrement: Variable {symbolRecord["name"]} does not hold a value')
|
|
253
276
|
return False
|
|
254
277
|
|
|
255
278
|
def r_decrement(self, command):
|
|
256
279
|
return self.incdec(command, '-')
|
|
257
280
|
|
|
258
281
|
# Delete a file or a property
|
|
259
|
-
# delete {filename}
|
|
282
|
+
# delete file {filename}
|
|
260
283
|
# delete property {value} of {variable}
|
|
284
|
+
# delete element {name} of {variable}
|
|
261
285
|
def k_delete(self, command):
|
|
262
286
|
token = self.nextToken( )
|
|
287
|
+
command['type'] = token
|
|
263
288
|
if token == 'file':
|
|
264
|
-
command['type'] = 'file'
|
|
265
289
|
command['filename'] = self.nextValue()
|
|
266
290
|
self.add(command)
|
|
267
291
|
return True
|
|
268
|
-
elif token
|
|
269
|
-
command['key'] = self.nextValue()
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
292
|
+
elif token in ['property', 'element']:
|
|
293
|
+
command['key'] = self.nextValue()
|
|
294
|
+
self.skip('of')
|
|
295
|
+
if self.nextIsSymbol():
|
|
296
|
+
record = self.getSymbolRecord()
|
|
297
|
+
if record['hasValue']:
|
|
298
|
+
command['var'] = record['name']
|
|
299
|
+
self.add(command)
|
|
300
|
+
return True
|
|
301
|
+
NoValueError(self.compiler, record)
|
|
302
|
+
self.warning(f'Core.delete: variable expected; got {self.getToken()}')
|
|
277
303
|
else:
|
|
278
|
-
self.warning(f'Core.delete: "file" or "
|
|
304
|
+
self.warning(f'Core.delete: "file", "property" or "element" expected; got {token}')
|
|
279
305
|
return False
|
|
280
306
|
|
|
281
307
|
def r_delete(self, command):
|
|
282
308
|
type = command['type']
|
|
283
309
|
if type == 'file':
|
|
284
310
|
filename = self.getRuntimeValue(command['filename'])
|
|
285
|
-
if
|
|
286
|
-
|
|
287
|
-
os.remove(filename)
|
|
311
|
+
if filename != None:
|
|
312
|
+
if os.path.isfile(filename): os.remove(filename)
|
|
288
313
|
elif type == 'property':
|
|
289
314
|
key = self.getRuntimeValue(command['key'])
|
|
290
315
|
symbolRecord = self.getVariable(command['var'])
|
|
@@ -293,6 +318,15 @@ class Core(Handler):
|
|
|
293
318
|
content.pop(key, None)
|
|
294
319
|
value['content'] = content
|
|
295
320
|
self.putSymbolValue(symbolRecord, value)
|
|
321
|
+
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])
|
|
327
|
+
else: RuntimeError(self.program, f'Index {key} out of range')
|
|
328
|
+
value['content'] = content
|
|
329
|
+
self.putSymbolValue(symbolRecord, value)
|
|
296
330
|
return self.nextPC()
|
|
297
331
|
|
|
298
332
|
# Arithmetic division
|
|
@@ -308,7 +342,7 @@ class Core(Handler):
|
|
|
308
342
|
command['target'] = self.getToken()
|
|
309
343
|
self.add(command)
|
|
310
344
|
return True
|
|
311
|
-
FatalError(self.
|
|
345
|
+
FatalError(self.compiler, 'Symbol expected')
|
|
312
346
|
else:
|
|
313
347
|
# First value must be a variable
|
|
314
348
|
if command['value1']['type'] == 'symbol':
|
|
@@ -325,7 +359,7 @@ class Core(Handler):
|
|
|
325
359
|
except:
|
|
326
360
|
value2 = None
|
|
327
361
|
target = self.getVariable(command['target'])
|
|
328
|
-
if not target['
|
|
362
|
+
if not target['hasValue']:
|
|
329
363
|
self.variableDoesNotHoldAValueError(target['name'])
|
|
330
364
|
return None
|
|
331
365
|
value = self.getSymbolValue(target)
|
|
@@ -345,6 +379,29 @@ class Core(Handler):
|
|
|
345
379
|
self.putSymbolValue(target, value)
|
|
346
380
|
return self.nextPC()
|
|
347
381
|
|
|
382
|
+
# download [binary] {url} to {path}
|
|
383
|
+
def k_download(self, command):
|
|
384
|
+
if self.nextIs('binary'):
|
|
385
|
+
command['binary'] = True
|
|
386
|
+
self.nextToken()
|
|
387
|
+
else: command['binary'] = False
|
|
388
|
+
command['url'] = self.getValue()
|
|
389
|
+
self.skip('to')
|
|
390
|
+
command['path'] = self.nextValue()
|
|
391
|
+
self.add(command)
|
|
392
|
+
return True
|
|
393
|
+
|
|
394
|
+
def r_download(self, command):
|
|
395
|
+
binary = command['binary']
|
|
396
|
+
url = self.getRuntimeValue(command['url'])
|
|
397
|
+
path = self.getRuntimeValue(command['path'])
|
|
398
|
+
mode = 'wb' if binary else 'w'
|
|
399
|
+
response = requests.get(url, stream=True)
|
|
400
|
+
with open(path, mode) as f:
|
|
401
|
+
for chunk in response.iter_content(chunk_size=8192):
|
|
402
|
+
if chunk: f.write(chunk)
|
|
403
|
+
return self.nextPC()
|
|
404
|
+
|
|
348
405
|
# Dummy command for testing
|
|
349
406
|
def k_dummy(self, command):
|
|
350
407
|
self.add(command)
|
|
@@ -367,11 +424,13 @@ class Core(Handler):
|
|
|
367
424
|
return True
|
|
368
425
|
|
|
369
426
|
def r_exit(self, command):
|
|
427
|
+
if self.program.parent == None and self.program.graphics != None:
|
|
428
|
+
self.program.graphics.force_exit(None)
|
|
370
429
|
return -1
|
|
371
430
|
|
|
372
431
|
# Declare a file variable
|
|
373
432
|
def k_file(self, command):
|
|
374
|
-
return self.compileVariable(command
|
|
433
|
+
return self.compileVariable(command)
|
|
375
434
|
|
|
376
435
|
def r_file(self, command):
|
|
377
436
|
return self.nextPC()
|
|
@@ -395,46 +454,32 @@ class Core(Handler):
|
|
|
395
454
|
self.run(label)
|
|
396
455
|
return next
|
|
397
456
|
|
|
398
|
-
# Issue a REST GET request
|
|
399
457
|
# get {variable) from {url} [or {command}]
|
|
400
458
|
def k_get(self, command):
|
|
401
459
|
if self.nextIsSymbol():
|
|
402
460
|
symbolRecord = self.getSymbolRecord()
|
|
403
|
-
if symbolRecord['
|
|
461
|
+
if symbolRecord['hasValue']:
|
|
404
462
|
command['target'] = self.getToken()
|
|
405
463
|
else:
|
|
406
|
-
|
|
464
|
+
NoValueError(self.compiler, symbolRecord)
|
|
407
465
|
if self.nextIs('from'):
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
cmd['lino'] = command['lino']
|
|
426
|
-
cmd['domain'] = 'core'
|
|
427
|
-
cmd['keyword'] = 'gotoPC'
|
|
428
|
-
cmd['goto'] = 0
|
|
429
|
-
cmd['debug'] = False
|
|
430
|
-
skip = self.getPC()
|
|
431
|
-
self.addCommand(cmd)
|
|
432
|
-
# Process the 'or'
|
|
433
|
-
self.getCommandAt(get)['or'] = self.getPC()
|
|
434
|
-
self.compileOne()
|
|
435
|
-
# Fixup the skip
|
|
436
|
-
self.getCommandAt(skip)['goto'] = self.getPC()
|
|
437
|
-
return True
|
|
466
|
+
if self.nextIs('url'):
|
|
467
|
+
url = self.nextValue()
|
|
468
|
+
if url != None:
|
|
469
|
+
command['url'] = url
|
|
470
|
+
command['or'] = None
|
|
471
|
+
get = self.getPC()
|
|
472
|
+
if self.peek() == 'timeout':
|
|
473
|
+
self.nextToken()
|
|
474
|
+
command['timeout'] = self.nextValue()
|
|
475
|
+
else:
|
|
476
|
+
timeout = {}
|
|
477
|
+
timeout['type'] = 'int'
|
|
478
|
+
timeout['content'] = 5
|
|
479
|
+
command['timeout'] = timeout
|
|
480
|
+
self.processOr(command, get)
|
|
481
|
+
return True
|
|
482
|
+
return False
|
|
438
483
|
|
|
439
484
|
def r_get(self, command):
|
|
440
485
|
global errorCode, errorReason
|
|
@@ -499,17 +544,17 @@ class Core(Handler):
|
|
|
499
544
|
|
|
500
545
|
def r_gosub(self, command):
|
|
501
546
|
label = command['gosub'] + ':'
|
|
502
|
-
|
|
503
|
-
|
|
547
|
+
if label in self.symbols:
|
|
548
|
+
address = self.symbols[label]
|
|
504
549
|
self.stack.append(self.nextPC())
|
|
505
550
|
return address
|
|
506
|
-
RuntimeError(self.program, f'There is no label "{label
|
|
551
|
+
RuntimeError(self.program, f'There is no label "{label}"')
|
|
507
552
|
return None
|
|
508
553
|
|
|
509
554
|
# if <condition> <action> [else <action>]
|
|
510
555
|
def k_if(self, command):
|
|
511
556
|
command['condition'] = self.nextCondition()
|
|
512
|
-
self.
|
|
557
|
+
self.add(command)
|
|
513
558
|
self.nextToken()
|
|
514
559
|
pcElse = self.getPC()
|
|
515
560
|
cmd = {}
|
|
@@ -518,7 +563,7 @@ class Core(Handler):
|
|
|
518
563
|
cmd['keyword'] = 'gotoPC'
|
|
519
564
|
cmd['goto'] = 0
|
|
520
565
|
cmd['debug'] = False
|
|
521
|
-
self.
|
|
566
|
+
self.add(cmd)
|
|
522
567
|
# Get the 'then' code
|
|
523
568
|
self.compileOne()
|
|
524
569
|
if self.peek() == 'else':
|
|
@@ -531,7 +576,7 @@ class Core(Handler):
|
|
|
531
576
|
cmd['keyword'] = 'gotoPC'
|
|
532
577
|
cmd['goto'] = 0
|
|
533
578
|
cmd['debug'] = False
|
|
534
|
-
self.
|
|
579
|
+
self.add(cmd)
|
|
535
580
|
# Fixup the link to the 'else' branch
|
|
536
581
|
self.getCommandAt(pcElse)['goto'] = self.getPC()
|
|
537
582
|
# Process the 'else' branch
|
|
@@ -552,37 +597,29 @@ class Core(Handler):
|
|
|
552
597
|
self.program.pc += 1
|
|
553
598
|
return self.program.pc
|
|
554
599
|
|
|
600
|
+
# Import one or more variables
|
|
555
601
|
def k_import(self, command):
|
|
556
|
-
|
|
557
|
-
|
|
602
|
+
imports = []
|
|
603
|
+
while True:
|
|
604
|
+
keyword = self.nextToken()
|
|
605
|
+
name = self.nextToken()
|
|
606
|
+
item = [keyword, name]
|
|
607
|
+
imports.append(item)
|
|
608
|
+
self.symbols[name] = self.getPC()
|
|
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)
|
|
617
|
+
if self.peek() != 'and':
|
|
618
|
+
break
|
|
558
619
|
self.nextToken()
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
self.program.importPlugin(f'{source}:{clazz}')
|
|
563
|
-
return True
|
|
564
|
-
return False
|
|
565
|
-
else:
|
|
566
|
-
# Import one or more variables
|
|
567
|
-
imports = []
|
|
568
|
-
while True:
|
|
569
|
-
keyword = self.nextToken()
|
|
570
|
-
name = self.nextToken()
|
|
571
|
-
item = [keyword, name]
|
|
572
|
-
imports.append(item)
|
|
573
|
-
self.symbols[name] = self.getPC()
|
|
574
|
-
variable = {}
|
|
575
|
-
variable['domain'] = None
|
|
576
|
-
variable['name'] = name
|
|
577
|
-
variable['keyword'] = keyword
|
|
578
|
-
variable['import'] = None
|
|
579
|
-
self.addCommand(variable)
|
|
580
|
-
if self.peek() != 'and':
|
|
581
|
-
break
|
|
582
|
-
self.nextToken()
|
|
583
|
-
command['imports'] = json.dumps(imports)
|
|
584
|
-
self.add(command)
|
|
585
|
-
return True
|
|
620
|
+
command['imports'] = json.dumps(imports)
|
|
621
|
+
self.add(command)
|
|
622
|
+
return True
|
|
586
623
|
|
|
587
624
|
def r_import(self, command):
|
|
588
625
|
exports = self.program.exports
|
|
@@ -606,11 +643,11 @@ class Core(Handler):
|
|
|
606
643
|
def k_increment(self, command):
|
|
607
644
|
if self.nextIsSymbol():
|
|
608
645
|
symbolRecord = self.getSymbolRecord()
|
|
609
|
-
if symbolRecord['
|
|
646
|
+
if symbolRecord['hasValue']:
|
|
610
647
|
command['target'] = self.getToken()
|
|
611
648
|
self.add(command)
|
|
612
649
|
return True
|
|
613
|
-
self.warning(f'Core.increment: Variable
|
|
650
|
+
self.warning(f'Core.increment: Variable {symbolRecord["name"]} does not hold a value')
|
|
614
651
|
return False
|
|
615
652
|
|
|
616
653
|
def r_increment(self, command):
|
|
@@ -686,7 +723,7 @@ class Core(Handler):
|
|
|
686
723
|
return self.nextPC()
|
|
687
724
|
|
|
688
725
|
# 1 Load a plugin. This is done at compile time.
|
|
689
|
-
# 2 Load text from a file
|
|
726
|
+
# 2 Load text from a file or ssh
|
|
690
727
|
def k_load(self, command):
|
|
691
728
|
self.nextToken()
|
|
692
729
|
if self.tokenIs('plugin'):
|
|
@@ -696,19 +733,60 @@ class Core(Handler):
|
|
|
696
733
|
self.program.importPlugin(f'{source}:{clazz}')
|
|
697
734
|
return True
|
|
698
735
|
elif self.isSymbol():
|
|
699
|
-
|
|
700
|
-
if
|
|
701
|
-
command['
|
|
702
|
-
self.
|
|
703
|
-
|
|
736
|
+
symbolRecord = self.getSymbolRecord()
|
|
737
|
+
if symbolRecord['hasValue']:
|
|
738
|
+
command['target'] = symbolRecord['name']
|
|
739
|
+
if self.nextIs('from'):
|
|
740
|
+
if self.nextIsSymbol():
|
|
741
|
+
record = self.getSymbolRecord()
|
|
742
|
+
if record['keyword'] == 'ssh':
|
|
743
|
+
command['ssh'] = record['name']
|
|
744
|
+
command['path'] = self.nextValue()
|
|
745
|
+
else:
|
|
746
|
+
command['file'] = self.getValue()
|
|
747
|
+
else:
|
|
748
|
+
command['file'] = self.getValue()
|
|
749
|
+
command['or'] = None
|
|
750
|
+
load = self.getPC()
|
|
751
|
+
self.processOr(command, load)
|
|
752
|
+
return True
|
|
753
|
+
else:
|
|
754
|
+
FatalError(self.compiler, f'I don\'t understand \'{self.getToken()}\'')
|
|
704
755
|
return False
|
|
705
756
|
|
|
706
757
|
def r_load(self, command):
|
|
758
|
+
errorReason = None
|
|
707
759
|
target = self.getVariable(command['target'])
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
760
|
+
if 'ssh' in command:
|
|
761
|
+
ssh = self.getVariable(command['ssh'])
|
|
762
|
+
path = self.getRuntimeValue(command['path'])
|
|
763
|
+
sftp = ssh['sftp']
|
|
764
|
+
try:
|
|
765
|
+
with sftp.open(path, 'r') as remote_file: content = remote_file.read().decode()
|
|
766
|
+
except:
|
|
767
|
+
errorReason = f'Unable to read from {path}'
|
|
768
|
+
if command['or'] != None:
|
|
769
|
+
print(f'Exception "{errorReason}": Running the "or" clause')
|
|
770
|
+
return command['or']
|
|
771
|
+
else:
|
|
772
|
+
RuntimeError(self.program, f'Error: {errorReason}')
|
|
773
|
+
else:
|
|
774
|
+
filename = self.getRuntimeValue(command['file'])
|
|
775
|
+
try:
|
|
776
|
+
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
|
+
except:
|
|
782
|
+
errorReason = f'Unable to read from {filename}'
|
|
783
|
+
|
|
784
|
+
if errorReason:
|
|
785
|
+
if command['or'] != None:
|
|
786
|
+
print(f'Exception "{errorReason}": Running the "or" clause')
|
|
787
|
+
return command['or']
|
|
788
|
+
else:
|
|
789
|
+
RuntimeError(self.program, f'Error: {errorReason}')
|
|
712
790
|
value = {}
|
|
713
791
|
value['type'] = 'text'
|
|
714
792
|
value['content'] = content
|
|
@@ -729,6 +807,12 @@ class Core(Handler):
|
|
|
729
807
|
target['locked'] = True
|
|
730
808
|
return self.nextPC()
|
|
731
809
|
|
|
810
|
+
# Log a message
|
|
811
|
+
def k_log(self, command):
|
|
812
|
+
command['log'] = True
|
|
813
|
+
command['keyword'] = 'print'
|
|
814
|
+
return self.k_print(command)
|
|
815
|
+
|
|
732
816
|
# Declare a module variable
|
|
733
817
|
def k_module(self, command):
|
|
734
818
|
return self.compileVariable(command)
|
|
@@ -749,14 +833,14 @@ class Core(Handler):
|
|
|
749
833
|
command['target'] = self.getToken()
|
|
750
834
|
self.add(command)
|
|
751
835
|
return True
|
|
752
|
-
FatalError(self.
|
|
836
|
+
FatalError(self.compiler, 'Symbol expected')
|
|
753
837
|
else:
|
|
754
838
|
# First value must be a variable
|
|
755
839
|
if command['value1']['type'] == 'symbol':
|
|
756
840
|
command['target'] = command['value1']['name']
|
|
757
841
|
self.add(command)
|
|
758
842
|
return True
|
|
759
|
-
FatalError(self.
|
|
843
|
+
FatalError(self.compiler, 'First value must be a variable')
|
|
760
844
|
return False
|
|
761
845
|
|
|
762
846
|
def r_multiply(self, command):
|
|
@@ -766,7 +850,7 @@ class Core(Handler):
|
|
|
766
850
|
except:
|
|
767
851
|
value2 = None
|
|
768
852
|
target = self.getVariable(command['target'])
|
|
769
|
-
if not target['
|
|
853
|
+
if not target['hasValue']:
|
|
770
854
|
self.variableDoesNotHoldAValueError(target['name'])
|
|
771
855
|
return None
|
|
772
856
|
value = self.getSymbolValue(target)
|
|
@@ -791,17 +875,17 @@ class Core(Handler):
|
|
|
791
875
|
def k_negate(self, command):
|
|
792
876
|
if self.nextIsSymbol():
|
|
793
877
|
symbolRecord = self.getSymbolRecord()
|
|
794
|
-
if symbolRecord['
|
|
878
|
+
if symbolRecord['hasValue']:
|
|
795
879
|
command['target'] = self.getToken()
|
|
796
880
|
self.add(command)
|
|
797
881
|
return True
|
|
798
|
-
self.warning(f'Core.negate: Variable
|
|
882
|
+
self.warning(f'Core.negate: Variable {symbolRecord["name"]} does not hold a value')
|
|
799
883
|
return False
|
|
800
884
|
|
|
801
885
|
def r_negate(self, command):
|
|
802
886
|
symbolRecord = self.getVariable(command['target'])
|
|
803
|
-
if not symbolRecord['
|
|
804
|
-
|
|
887
|
+
if not symbolRecord['hasValue']:
|
|
888
|
+
NoValueRuntimeError(self.program, symbolRecord)
|
|
805
889
|
return None
|
|
806
890
|
value = self.getSymbolValue(symbolRecord)
|
|
807
891
|
if value == None:
|
|
@@ -810,13 +894,6 @@ class Core(Handler):
|
|
|
810
894
|
self.putSymbolValue(symbolRecord, value)
|
|
811
895
|
return self.nextPC()
|
|
812
896
|
|
|
813
|
-
# Define an object variable
|
|
814
|
-
def k_object(self, command):
|
|
815
|
-
return self.compileVariable(command)
|
|
816
|
-
|
|
817
|
-
def r_object(self, command):
|
|
818
|
-
return self.nextPC()
|
|
819
|
-
|
|
820
897
|
# on message {action}
|
|
821
898
|
def k_on(self, command):
|
|
822
899
|
if self.nextIs('message'):
|
|
@@ -829,7 +906,7 @@ class Core(Handler):
|
|
|
829
906
|
cmd['keyword'] = 'gotoPC'
|
|
830
907
|
cmd['goto'] = 0
|
|
831
908
|
cmd['debug'] = False
|
|
832
|
-
self.
|
|
909
|
+
self.add(cmd)
|
|
833
910
|
# Add the action and a 'stop'
|
|
834
911
|
self.compileOne()
|
|
835
912
|
cmd = {}
|
|
@@ -837,7 +914,7 @@ class Core(Handler):
|
|
|
837
914
|
cmd['lino'] = command['lino']
|
|
838
915
|
cmd['keyword'] = 'stop'
|
|
839
916
|
cmd['debug'] = False
|
|
840
|
-
self.
|
|
917
|
+
self.add(cmd)
|
|
841
918
|
# Fixup the link
|
|
842
919
|
command['goto'] = self.getPC()
|
|
843
920
|
return True
|
|
@@ -865,7 +942,7 @@ class Core(Handler):
|
|
|
865
942
|
elif token == 'writing':
|
|
866
943
|
mode = 'w'
|
|
867
944
|
else:
|
|
868
|
-
FatalError(self.
|
|
945
|
+
FatalError(self.compiler, 'Unknown file open mode {self.getToken()}')
|
|
869
946
|
return False
|
|
870
947
|
command['mode'] = mode
|
|
871
948
|
else:
|
|
@@ -898,15 +975,15 @@ class Core(Handler):
|
|
|
898
975
|
command['from'] = self.getToken()
|
|
899
976
|
self.add(command)
|
|
900
977
|
return True
|
|
901
|
-
return False
|
|
978
|
+
return False
|
|
902
979
|
|
|
903
980
|
def r_pop(self, command):
|
|
904
981
|
symbolRecord = self.getVariable(command['target'])
|
|
905
|
-
if not symbolRecord['
|
|
906
|
-
|
|
982
|
+
if not symbolRecord['hasValue']:
|
|
983
|
+
NoValueRuntimeError(self.program, symbolRecord)
|
|
907
984
|
stackRecord = self.getVariable(command['from'])
|
|
908
985
|
stack = self.getSymbolValue(stackRecord)
|
|
909
|
-
v = stack.pop()
|
|
986
|
+
v = stack.pop()
|
|
910
987
|
self.putSymbolValue(stackRecord, stack)
|
|
911
988
|
value = {}
|
|
912
989
|
value['type'] = 'int' if type(v) == int else 'text'
|
|
@@ -931,24 +1008,7 @@ class Core(Handler):
|
|
|
931
1008
|
command['result'] = None
|
|
932
1009
|
command['or'] = None
|
|
933
1010
|
post = self.getPC()
|
|
934
|
-
self.
|
|
935
|
-
if self.peek() == 'or':
|
|
936
|
-
self.nextToken()
|
|
937
|
-
self.nextToken()
|
|
938
|
-
# Add a 'goto' to skip the 'or'
|
|
939
|
-
cmd = {}
|
|
940
|
-
cmd['lino'] = command['lino']
|
|
941
|
-
cmd['domain'] = 'core'
|
|
942
|
-
cmd['keyword'] = 'gotoPC'
|
|
943
|
-
cmd['goto'] = 0
|
|
944
|
-
cmd['debug'] = False
|
|
945
|
-
skip = self.getPC()
|
|
946
|
-
self.addCommand(cmd)
|
|
947
|
-
# Process the 'or'
|
|
948
|
-
self.getCommandAt(post)['or'] = self.getPC()
|
|
949
|
-
self.compileOne()
|
|
950
|
-
# Fixup the skip
|
|
951
|
-
self.getCommandAt(skip)['goto'] = self.getPC()
|
|
1011
|
+
self.processOr(command, post)
|
|
952
1012
|
return True
|
|
953
1013
|
|
|
954
1014
|
def r_post(self, command):
|
|
@@ -988,18 +1048,20 @@ class Core(Handler):
|
|
|
988
1048
|
command['value'] = value
|
|
989
1049
|
self.add(command)
|
|
990
1050
|
return True
|
|
991
|
-
FatalError(self.
|
|
1051
|
+
FatalError(self.compiler, 'I can\'t print this value')
|
|
992
1052
|
return False
|
|
993
1053
|
|
|
994
1054
|
def r_print(self, command):
|
|
995
1055
|
value = self.getRuntimeValue(command['value'])
|
|
996
1056
|
program = command['program']
|
|
997
1057
|
code = program.code[program.pc]
|
|
998
|
-
lino = code['lino'] + 1
|
|
999
|
-
|
|
1000
|
-
|
|
1058
|
+
lino = str(code['lino'] + 1)
|
|
1059
|
+
# while len(lino) < 5: lino = f' {lino}'
|
|
1060
|
+
if value == None: value = '<empty>'
|
|
1061
|
+
if 'log' in command:
|
|
1062
|
+
print(f'{datetime.now().time()}:{self.program.name}:{lino}->{value}')
|
|
1001
1063
|
else:
|
|
1002
|
-
print(
|
|
1064
|
+
print(value)
|
|
1003
1065
|
return self.nextPC()
|
|
1004
1066
|
|
|
1005
1067
|
# Push a value onto a stack
|
|
@@ -1018,7 +1080,7 @@ class Core(Handler):
|
|
|
1018
1080
|
return False
|
|
1019
1081
|
|
|
1020
1082
|
def r_push(self, command):
|
|
1021
|
-
value = self.getRuntimeValue(command['value'])
|
|
1083
|
+
value = deepcopy(self.getRuntimeValue(command['value']))
|
|
1022
1084
|
stackRecord = self.getVariable(command['to'])
|
|
1023
1085
|
if stackRecord['keyword'] != 'stack':
|
|
1024
1086
|
RuntimeError(self.program, f'{stackRecord["name"]} is not a stack')
|
|
@@ -1031,30 +1093,35 @@ class Core(Handler):
|
|
|
1031
1093
|
self.putSymbolValue(stackRecord, stack)
|
|
1032
1094
|
return self.nextPC()
|
|
1033
1095
|
|
|
1034
|
-
# Put a value into a variable
|
|
1035
1096
|
# put {value} into {variable}
|
|
1036
1097
|
def k_put(self, command):
|
|
1037
|
-
|
|
1038
|
-
if
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1098
|
+
value = self.nextValue()
|
|
1099
|
+
if value != None:
|
|
1100
|
+
command['value'] = value
|
|
1101
|
+
if self.nextIs('into'):
|
|
1102
|
+
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.getPC())
|
|
1110
|
+
return True
|
|
1044
1111
|
else:
|
|
1045
|
-
self.
|
|
1046
|
-
return True
|
|
1047
|
-
else:
|
|
1048
|
-
FatalError(self.program.compiler, f'Symbol {self.getToken()} is not a variable')
|
|
1112
|
+
FatalError(self.compiler, f'Symbol {self.getToken()} is not a variable')
|
|
1049
1113
|
return False
|
|
1050
1114
|
|
|
1051
1115
|
def r_put(self, command):
|
|
1052
1116
|
value = self.evaluate(command['value'])
|
|
1053
1117
|
if value == None:
|
|
1054
|
-
|
|
1118
|
+
if command['or'] != None:
|
|
1119
|
+
return command['or']
|
|
1120
|
+
else:
|
|
1121
|
+
RuntimeError(self.program, f'Error: could not compute value')
|
|
1055
1122
|
symbolRecord = self.getVariable(command['target'])
|
|
1056
|
-
if not symbolRecord['
|
|
1057
|
-
|
|
1123
|
+
if not symbolRecord['hasValue']:
|
|
1124
|
+
NoValueRuntimeError(self.program, symbolRecord)
|
|
1058
1125
|
return -1
|
|
1059
1126
|
self.putSymbolValue(symbolRecord, value)
|
|
1060
1127
|
return self.nextPC()
|
|
@@ -1069,7 +1136,7 @@ class Core(Handler):
|
|
|
1069
1136
|
command['line'] = False
|
|
1070
1137
|
if self.nextIsSymbol():
|
|
1071
1138
|
symbolRecord = self.getSymbolRecord()
|
|
1072
|
-
if symbolRecord['
|
|
1139
|
+
if symbolRecord['hasValue']:
|
|
1073
1140
|
if self.peek() == 'from':
|
|
1074
1141
|
self.nextToken()
|
|
1075
1142
|
if self.nextIsSymbol():
|
|
@@ -1079,9 +1146,9 @@ class Core(Handler):
|
|
|
1079
1146
|
command['file'] = fileRecord['name']
|
|
1080
1147
|
self.add(command)
|
|
1081
1148
|
return True
|
|
1082
|
-
FatalError(self.
|
|
1149
|
+
FatalError(self.compiler, f'Symbol "{symbolRecord["name"]}" is not a value holder')
|
|
1083
1150
|
return False
|
|
1084
|
-
FatalError(self.
|
|
1151
|
+
FatalError(self.compiler, f'Symbol "{self.getToken()}" has not been declared')
|
|
1085
1152
|
return False
|
|
1086
1153
|
|
|
1087
1154
|
def r_read(self, command):
|
|
@@ -1098,6 +1165,16 @@ class Core(Handler):
|
|
|
1098
1165
|
self.putSymbolValue(symbolRecord, value)
|
|
1099
1166
|
return self.nextPC()
|
|
1100
1167
|
|
|
1168
|
+
# Release the parent script
|
|
1169
|
+
def k_release(self, command):
|
|
1170
|
+
if self.nextIs('parent'):
|
|
1171
|
+
self.add(command)
|
|
1172
|
+
return True
|
|
1173
|
+
|
|
1174
|
+
def r_release(self, command):
|
|
1175
|
+
self.program.releaseParent()
|
|
1176
|
+
return self.nextPC()
|
|
1177
|
+
|
|
1101
1178
|
# Replace a substring
|
|
1102
1179
|
#replace {value} with {value} in {variable}
|
|
1103
1180
|
def k_replace(self, command):
|
|
@@ -1128,16 +1205,6 @@ class Core(Handler):
|
|
|
1128
1205
|
self.putSymbolValue(templateRecord, value)
|
|
1129
1206
|
return self.nextPC()
|
|
1130
1207
|
|
|
1131
|
-
# Release the parent script
|
|
1132
|
-
def k_release(self, command):
|
|
1133
|
-
if self.nextIs('parent'):
|
|
1134
|
-
self.add(command)
|
|
1135
|
-
return True
|
|
1136
|
-
|
|
1137
|
-
def r_release(self, command):
|
|
1138
|
-
self.program.releaseParent()
|
|
1139
|
-
return self.nextPC()
|
|
1140
|
-
|
|
1141
1208
|
# Return from subroutine
|
|
1142
1209
|
def k_return(self, command):
|
|
1143
1210
|
self.add(command)
|
|
@@ -1147,6 +1214,7 @@ class Core(Handler):
|
|
|
1147
1214
|
return self.stack.pop()
|
|
1148
1215
|
|
|
1149
1216
|
# Compile and run a script
|
|
1217
|
+
# run {path} [as {module}] [with {variable} [and {variable}...]]
|
|
1150
1218
|
def k_run(self, command):
|
|
1151
1219
|
try:
|
|
1152
1220
|
command['path'] = self.nextValue()
|
|
@@ -1157,20 +1225,24 @@ class Core(Handler):
|
|
|
1157
1225
|
if self.nextIsSymbol():
|
|
1158
1226
|
record = self.getSymbolRecord()
|
|
1159
1227
|
if record['keyword'] == 'module':
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1228
|
+
name = record['name']
|
|
1229
|
+
command['module'] = name
|
|
1230
|
+
else: FatalError(self.compiler, f'Symbol \'name\' is not a module')
|
|
1231
|
+
else: FatalError(self.compiler, 'Module name expected after \'as\'')
|
|
1232
|
+
else: FatalError(self.compiler, '\'as {module name}\' expected')
|
|
1233
|
+
exports = []
|
|
1234
|
+
if self.peek() == 'with':
|
|
1235
|
+
self.nextToken()
|
|
1236
|
+
while True:
|
|
1237
|
+
name = self.nextToken()
|
|
1238
|
+
record = self.getSymbolRecord()
|
|
1239
|
+
exports.append(name)
|
|
1240
|
+
if self.peek() != 'and':
|
|
1241
|
+
break
|
|
1242
|
+
self.nextToken()
|
|
1243
|
+
command['exports'] = json.dumps(exports)
|
|
1244
|
+
self.add(command)
|
|
1245
|
+
return True
|
|
1174
1246
|
|
|
1175
1247
|
def r_run(self, command):
|
|
1176
1248
|
module = self.getVariable(command['module'])
|
|
@@ -1187,28 +1259,63 @@ class Core(Handler):
|
|
|
1187
1259
|
p(path).start(parent, module, exports)
|
|
1188
1260
|
return 0
|
|
1189
1261
|
|
|
1190
|
-
# Provide a name for the script
|
|
1191
|
-
def k_script(self, command):
|
|
1192
|
-
self.program.name = self.nextToken()
|
|
1193
|
-
return True
|
|
1194
|
-
|
|
1195
1262
|
# Save a value to a file
|
|
1196
1263
|
def k_save(self, command):
|
|
1197
1264
|
command['content'] = self.nextValue()
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
self.
|
|
1201
|
-
|
|
1202
|
-
|
|
1265
|
+
self.skip('to')
|
|
1266
|
+
if self.nextIsSymbol():
|
|
1267
|
+
record = self.getSymbolRecord()
|
|
1268
|
+
if record['keyword'] == 'ssh':
|
|
1269
|
+
command['ssh'] = record['name']
|
|
1270
|
+
command['path'] = self.nextValue()
|
|
1271
|
+
self.add(command)
|
|
1272
|
+
else:
|
|
1273
|
+
command['file'] = self.getValue()
|
|
1274
|
+
else:
|
|
1275
|
+
command['file'] = self.getValue()
|
|
1276
|
+
command['or'] = None
|
|
1277
|
+
save = self.getPC()
|
|
1278
|
+
self.processOr(command, save)
|
|
1279
|
+
return True
|
|
1203
1280
|
|
|
1204
1281
|
def r_save(self, command):
|
|
1282
|
+
errorReason = None
|
|
1205
1283
|
content = self.getRuntimeValue(command['content'])
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1284
|
+
if 'ssh' in command:
|
|
1285
|
+
ssh = self.getVariable(command['ssh'])
|
|
1286
|
+
path = self.getRuntimeValue(command['path'])
|
|
1287
|
+
sftp = ssh['sftp']
|
|
1288
|
+
if path.endswith('.json'): content = json.dumps(content)
|
|
1289
|
+
try:
|
|
1290
|
+
with sftp.open(path, 'w') as remote_file: remote_file.write(content)
|
|
1291
|
+
except:
|
|
1292
|
+
errorReason = 'Unable to write to {path}'
|
|
1293
|
+
if command['or'] != None:
|
|
1294
|
+
print(f'Exception "{errorReason}": Running the "or" clause')
|
|
1295
|
+
return command['or']
|
|
1296
|
+
else:
|
|
1297
|
+
RuntimeError(self.program, f'Error: {errorReason}')
|
|
1298
|
+
else:
|
|
1299
|
+
filename = self.getRuntimeValue(command['file'])
|
|
1300
|
+
if filename.endswith('.json'): content = json.dumps(content)
|
|
1301
|
+
try:
|
|
1302
|
+
with open(filename, 'w') as f: f.write(content)
|
|
1303
|
+
except:
|
|
1304
|
+
errorReason = f'Unable to write to {filename}'
|
|
1305
|
+
|
|
1306
|
+
if errorReason:
|
|
1307
|
+
if command['or'] != None:
|
|
1308
|
+
print(f'Exception "{errorReason}": Running the "or" clause')
|
|
1309
|
+
return command['or']
|
|
1310
|
+
else:
|
|
1311
|
+
RuntimeError(self.program, f'Error: {errorReason}')
|
|
1210
1312
|
return self.nextPC()
|
|
1211
1313
|
|
|
1314
|
+
# Provide a name for the script
|
|
1315
|
+
def k_script(self, command):
|
|
1316
|
+
self.program.name = self.nextToken()
|
|
1317
|
+
return True
|
|
1318
|
+
|
|
1212
1319
|
# Send a message to a module
|
|
1213
1320
|
def k_send(self, command):
|
|
1214
1321
|
command['message'] = self.nextValue()
|
|
@@ -1229,16 +1336,40 @@ class Core(Handler):
|
|
|
1229
1336
|
|
|
1230
1337
|
# Set a value
|
|
1231
1338
|
# set {variable}
|
|
1339
|
+
# set {ssh} host {host} user {user} password {password}
|
|
1232
1340
|
# set the elements of {variable} to {value}
|
|
1233
1341
|
# set element/property of {variable} to {value}
|
|
1234
1342
|
def k_set(self, command):
|
|
1235
1343
|
if self.nextIsSymbol():
|
|
1236
|
-
|
|
1237
|
-
|
|
1344
|
+
record = self.getSymbolRecord()
|
|
1345
|
+
command['target'] = record['name']
|
|
1346
|
+
if record['hasValue']:
|
|
1238
1347
|
command['type'] = 'set'
|
|
1239
|
-
command['target'] = target['name']
|
|
1240
1348
|
self.add(command)
|
|
1241
1349
|
return True
|
|
1350
|
+
elif record['keyword'] == 'ssh':
|
|
1351
|
+
host = None
|
|
1352
|
+
user = None
|
|
1353
|
+
password = None
|
|
1354
|
+
while True:
|
|
1355
|
+
token = self.peek()
|
|
1356
|
+
if token == 'host':
|
|
1357
|
+
self.nextToken()
|
|
1358
|
+
host = self.nextValue()
|
|
1359
|
+
elif token == 'user':
|
|
1360
|
+
self.nextToken()
|
|
1361
|
+
user = self.nextValue()
|
|
1362
|
+
elif token == 'password':
|
|
1363
|
+
self.nextToken()
|
|
1364
|
+
password = self.nextValue()
|
|
1365
|
+
else: break
|
|
1366
|
+
command['host'] = host
|
|
1367
|
+
command['user'] = user
|
|
1368
|
+
command['password'] = password
|
|
1369
|
+
command['type'] = 'ssh'
|
|
1370
|
+
self.add(command)
|
|
1371
|
+
return True
|
|
1372
|
+
|
|
1242
1373
|
return False
|
|
1243
1374
|
|
|
1244
1375
|
token = self.getToken()
|
|
@@ -1270,7 +1401,10 @@ class Core(Handler):
|
|
|
1270
1401
|
if self.nextIsSymbol():
|
|
1271
1402
|
command['target'] = self.getSymbolRecord()['name']
|
|
1272
1403
|
if self.nextIs('to'):
|
|
1273
|
-
|
|
1404
|
+
value = self.nextValue()
|
|
1405
|
+
if value == None:
|
|
1406
|
+
FatalError(self.compiler, 'Unable to get a value')
|
|
1407
|
+
command['value'] = value
|
|
1274
1408
|
self.add(command)
|
|
1275
1409
|
return True
|
|
1276
1410
|
|
|
@@ -1283,6 +1417,12 @@ class Core(Handler):
|
|
|
1283
1417
|
command['value'] = self.nextValue()
|
|
1284
1418
|
self.add(command)
|
|
1285
1419
|
return True
|
|
1420
|
+
|
|
1421
|
+
elif token == 'path':
|
|
1422
|
+
command['path'] = self.nextValue()
|
|
1423
|
+
self.add(command)
|
|
1424
|
+
return True
|
|
1425
|
+
|
|
1286
1426
|
return False
|
|
1287
1427
|
|
|
1288
1428
|
def r_set(self, command):
|
|
@@ -1312,6 +1452,7 @@ class Core(Handler):
|
|
|
1312
1452
|
newValue[index] = value
|
|
1313
1453
|
symbolRecord['elements'] = elements
|
|
1314
1454
|
symbolRecord['value'] = newValue
|
|
1455
|
+
symbolRecord['index'] = 0
|
|
1315
1456
|
return self.nextPC()
|
|
1316
1457
|
|
|
1317
1458
|
elif cmdType == 'element':
|
|
@@ -1333,6 +1474,11 @@ class Core(Handler):
|
|
|
1333
1474
|
self.encoding = self.getRuntimeValue(command['encoding'])
|
|
1334
1475
|
return self.nextPC()
|
|
1335
1476
|
|
|
1477
|
+
elif cmdType == 'path':
|
|
1478
|
+
path = self.getRuntimeValue(command['path'])
|
|
1479
|
+
os.chdir(path)
|
|
1480
|
+
return self.nextPC()
|
|
1481
|
+
|
|
1336
1482
|
elif cmdType == 'property':
|
|
1337
1483
|
value = self.getRuntimeValue(command['value'])
|
|
1338
1484
|
name = self.getRuntimeValue(command['name'])
|
|
@@ -1352,13 +1498,55 @@ class Core(Handler):
|
|
|
1352
1498
|
val['content'] = content
|
|
1353
1499
|
self.putSymbolValue(targetVariable, val)
|
|
1354
1500
|
return self.nextPC()
|
|
1501
|
+
|
|
1502
|
+
elif cmdType == 'ssh':
|
|
1503
|
+
target = self.getVariable(command['target'])
|
|
1504
|
+
host = self.getRuntimeValue(command['host'])
|
|
1505
|
+
user = self.getRuntimeValue(command['user'])
|
|
1506
|
+
password = self.getRuntimeValue(command['password'])
|
|
1507
|
+
ssh = paramiko.SSHClient()
|
|
1508
|
+
target['ssh'] = ssh
|
|
1509
|
+
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
|
1510
|
+
try:
|
|
1511
|
+
ssh.connect(host, username=user, password=password, timeout=10)
|
|
1512
|
+
target['sftp'] = ssh.open_sftp()
|
|
1513
|
+
except:
|
|
1514
|
+
target['error'] = f'Unable to connect to {host} (timeout)'
|
|
1515
|
+
return self.nextPC()
|
|
1516
|
+
|
|
1517
|
+
# Shuffle a list
|
|
1518
|
+
def k_shuffle(self, command):
|
|
1519
|
+
if self.nextIsSymbol():
|
|
1520
|
+
symbolRecord = self.getSymbolRecord()
|
|
1521
|
+
if symbolRecord['hasValue']:
|
|
1522
|
+
command['target'] = self.getToken()
|
|
1523
|
+
self.add(command)
|
|
1524
|
+
return True
|
|
1525
|
+
self.warning(f'Core.negate: Variable {symbolRecord["name"]} does not hold a value')
|
|
1526
|
+
return False
|
|
1527
|
+
|
|
1528
|
+
def r_shuffle(self, command):
|
|
1529
|
+
symbolRecord = self.getVariable(command['target'])
|
|
1530
|
+
if not symbolRecord['hasValue']:
|
|
1531
|
+
NoValueRuntimeError(self.program, symbolRecord)
|
|
1532
|
+
return None
|
|
1533
|
+
value = self.getSymbolValue(symbolRecord)
|
|
1534
|
+
if value == None:
|
|
1535
|
+
RuntimeError(self.program, f'{symbolRecord["name"]} has not been initialised')
|
|
1536
|
+
content = value['content']
|
|
1537
|
+
if isinstance(content, list):
|
|
1538
|
+
random.shuffle(content)
|
|
1539
|
+
value['content'] = content
|
|
1540
|
+
self.putSymbolValue(symbolRecord, value)
|
|
1541
|
+
return self.nextPC()
|
|
1542
|
+
RuntimeError(self.program, f'{symbolRecord["name"]} is not a list')
|
|
1355
1543
|
|
|
1356
1544
|
# Split a string into a variable with several elements
|
|
1357
1545
|
# split {variable} on {value}
|
|
1358
1546
|
def k_split(self, command):
|
|
1359
1547
|
if self.nextIsSymbol():
|
|
1360
1548
|
symbolRecord = self.getSymbolRecord()
|
|
1361
|
-
if symbolRecord['
|
|
1549
|
+
if symbolRecord['hasValue']:
|
|
1362
1550
|
command['target'] = symbolRecord['name']
|
|
1363
1551
|
value = {}
|
|
1364
1552
|
value['type'] = 'text'
|
|
@@ -1374,6 +1562,7 @@ class Core(Handler):
|
|
|
1374
1562
|
command['on'] = self.nextValue()
|
|
1375
1563
|
self.add(command)
|
|
1376
1564
|
return True
|
|
1565
|
+
else: self.noSymbolWarning()
|
|
1377
1566
|
return False
|
|
1378
1567
|
|
|
1379
1568
|
def r_split(self, command):
|
|
@@ -1382,6 +1571,7 @@ class Core(Handler):
|
|
|
1382
1571
|
content = value['content'].split(self.getRuntimeValue(command['on']))
|
|
1383
1572
|
elements = len(content)
|
|
1384
1573
|
target['elements'] = elements
|
|
1574
|
+
target['index'] = 0
|
|
1385
1575
|
target['value'] = [None] * elements
|
|
1386
1576
|
|
|
1387
1577
|
for index, item in enumerate(content):
|
|
@@ -1393,6 +1583,12 @@ class Core(Handler):
|
|
|
1393
1583
|
|
|
1394
1584
|
return self.nextPC()
|
|
1395
1585
|
|
|
1586
|
+
def k_ssh(self, command):
|
|
1587
|
+
return self.compileVariable(command)
|
|
1588
|
+
|
|
1589
|
+
def r_ssh(self, command):
|
|
1590
|
+
return self.nextPC()
|
|
1591
|
+
|
|
1396
1592
|
# Declare a stack variable
|
|
1397
1593
|
def k_stack(self, command):
|
|
1398
1594
|
return self.compileVariable(command)
|
|
@@ -1422,7 +1618,7 @@ class Core(Handler):
|
|
|
1422
1618
|
command['background'] = background
|
|
1423
1619
|
self.add(command)
|
|
1424
1620
|
return True
|
|
1425
|
-
FatalError(self.
|
|
1621
|
+
FatalError(self.compiler, 'I can\'t give this command')
|
|
1426
1622
|
return False
|
|
1427
1623
|
|
|
1428
1624
|
def r_system(self, command):
|
|
@@ -1442,7 +1638,7 @@ class Core(Handler):
|
|
|
1442
1638
|
if self.nextToken() == 'from':
|
|
1443
1639
|
if self.nextIsSymbol():
|
|
1444
1640
|
symbolRecord = self.getSymbolRecord()
|
|
1445
|
-
if symbolRecord['
|
|
1641
|
+
if symbolRecord['hasValue']:
|
|
1446
1642
|
if self.peek() == 'giving':
|
|
1447
1643
|
# This variable must be treated as a second value
|
|
1448
1644
|
command['value2'] = self.getValue()
|
|
@@ -1465,7 +1661,7 @@ class Core(Handler):
|
|
|
1465
1661
|
self.add(command)
|
|
1466
1662
|
return True
|
|
1467
1663
|
else:
|
|
1468
|
-
FatalError(self.
|
|
1664
|
+
FatalError(self.compiler, f'\'{self.getToken()}\' is not a symbol')
|
|
1469
1665
|
else:
|
|
1470
1666
|
self.warning(f'Core.take: Expected "giving"')
|
|
1471
1667
|
return False
|
|
@@ -1477,7 +1673,7 @@ class Core(Handler):
|
|
|
1477
1673
|
except:
|
|
1478
1674
|
value2 = None
|
|
1479
1675
|
target = self.getVariable(command['target'])
|
|
1480
|
-
if not target['
|
|
1676
|
+
if not target['hasValue']:
|
|
1481
1677
|
self.variableDoesNotHoldAValueError(target['name'])
|
|
1482
1678
|
return None
|
|
1483
1679
|
value = self.getSymbolValue(target)
|
|
@@ -1499,7 +1695,7 @@ class Core(Handler):
|
|
|
1499
1695
|
def k_toggle(self, command):
|
|
1500
1696
|
if self.nextIsSymbol():
|
|
1501
1697
|
target = self.getSymbolRecord()
|
|
1502
|
-
if target['
|
|
1698
|
+
if target['hasValue']:
|
|
1503
1699
|
command['target'] = target['name']
|
|
1504
1700
|
self.add(command)
|
|
1505
1701
|
return True
|
|
@@ -1515,6 +1711,24 @@ class Core(Handler):
|
|
|
1515
1711
|
self.add(command)
|
|
1516
1712
|
return self.nextPC()
|
|
1517
1713
|
|
|
1714
|
+
# Trim whitespace from a variable
|
|
1715
|
+
def k_trim(self, command):
|
|
1716
|
+
if self.nextIsSymbol():
|
|
1717
|
+
record = self.getSymbolRecord()
|
|
1718
|
+
if record['hasValue']:
|
|
1719
|
+
command['name'] = record['name']
|
|
1720
|
+
self.add(command)
|
|
1721
|
+
return True
|
|
1722
|
+
return False
|
|
1723
|
+
|
|
1724
|
+
def r_trim(self, command):
|
|
1725
|
+
record = self.getVariable(command['name'])
|
|
1726
|
+
value = record['value'][record['index']]
|
|
1727
|
+
if value['type'] == 'text':
|
|
1728
|
+
content = value['content']
|
|
1729
|
+
value['content'] = content.strip()
|
|
1730
|
+
return self.nextPC()
|
|
1731
|
+
|
|
1518
1732
|
# Truncate a file
|
|
1519
1733
|
def k_truncate(self, command):
|
|
1520
1734
|
if self.nextIsSymbol():
|
|
@@ -1544,9 +1758,34 @@ class Core(Handler):
|
|
|
1544
1758
|
target['locked'] = False
|
|
1545
1759
|
return self.nextPC()
|
|
1546
1760
|
|
|
1761
|
+
# Use a plugin module
|
|
1762
|
+
def k_use(self, command):
|
|
1763
|
+
if self.peek() == 'plugin':
|
|
1764
|
+
# Import a plugin
|
|
1765
|
+
self.nextToken()
|
|
1766
|
+
clazz = self.nextToken()
|
|
1767
|
+
if self.nextIs('from'):
|
|
1768
|
+
source = self.nextToken()
|
|
1769
|
+
self.program.importPlugin(f'{source}:{clazz}')
|
|
1770
|
+
return True
|
|
1771
|
+
return False
|
|
1772
|
+
else:
|
|
1773
|
+
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
|
|
1783
|
+
return False
|
|
1784
|
+
|
|
1547
1785
|
# Declare a general-purpose variable
|
|
1548
1786
|
def k_variable(self, command):
|
|
1549
|
-
|
|
1787
|
+
self.compiler.addValueType()
|
|
1788
|
+
return self.compileVariable(command)
|
|
1550
1789
|
|
|
1551
1790
|
def r_variable(self, command):
|
|
1552
1791
|
return self.nextPC()
|
|
@@ -1585,7 +1824,7 @@ class Core(Handler):
|
|
|
1585
1824
|
# token = self.getToken()
|
|
1586
1825
|
command['condition'] = code
|
|
1587
1826
|
test = self.getPC()
|
|
1588
|
-
self.
|
|
1827
|
+
self.add(command)
|
|
1589
1828
|
# Set up a goto for when the test fails
|
|
1590
1829
|
fail = self.getPC()
|
|
1591
1830
|
cmd = {}
|
|
@@ -1594,7 +1833,7 @@ class Core(Handler):
|
|
|
1594
1833
|
cmd['keyword'] = 'gotoPC'
|
|
1595
1834
|
cmd['goto'] = 0
|
|
1596
1835
|
cmd['debug'] = False
|
|
1597
|
-
self.
|
|
1836
|
+
self.add(cmd)
|
|
1598
1837
|
# Do the body of the while
|
|
1599
1838
|
self.nextToken()
|
|
1600
1839
|
if self.compileOne() == False:
|
|
@@ -1606,7 +1845,7 @@ class Core(Handler):
|
|
|
1606
1845
|
cmd['keyword'] = 'gotoPC'
|
|
1607
1846
|
cmd['goto'] = test
|
|
1608
1847
|
cmd['debug'] = False
|
|
1609
|
-
self.
|
|
1848
|
+
self.add(cmd)
|
|
1610
1849
|
# Fixup the 'goto' on completion
|
|
1611
1850
|
self.getCommandAt(fail)['goto'] = self.getPC()
|
|
1612
1851
|
return True
|
|
@@ -1652,9 +1891,8 @@ class Core(Handler):
|
|
|
1652
1891
|
|
|
1653
1892
|
def incdec(self, command, mode):
|
|
1654
1893
|
symbolRecord = self.getVariable(command['target'])
|
|
1655
|
-
if not symbolRecord['
|
|
1656
|
-
|
|
1657
|
-
return None
|
|
1894
|
+
if not symbolRecord['hasValue']:
|
|
1895
|
+
NoValueRuntimeError(self.program, symbolRecord)
|
|
1658
1896
|
value = self.getSymbolValue(symbolRecord)
|
|
1659
1897
|
if value == None:
|
|
1660
1898
|
RuntimeError(self.program, f'{symbolRecord["name"]} has not been initialised')
|
|
@@ -1675,13 +1913,15 @@ class Core(Handler):
|
|
|
1675
1913
|
value['name'] = token
|
|
1676
1914
|
symbolRecord = self.getSymbolRecord()
|
|
1677
1915
|
keyword = symbolRecord['keyword']
|
|
1916
|
+
|
|
1678
1917
|
if keyword == 'module':
|
|
1679
1918
|
value['type'] = 'module'
|
|
1680
1919
|
return value
|
|
1681
1920
|
|
|
1682
|
-
if keyword
|
|
1921
|
+
if keyword in ['ssh', 'variable']:
|
|
1683
1922
|
value['type'] = 'symbol'
|
|
1684
1923
|
return value
|
|
1924
|
+
|
|
1685
1925
|
return None
|
|
1686
1926
|
|
|
1687
1927
|
value['type'] = token
|
|
@@ -1701,7 +1941,7 @@ class Core(Handler):
|
|
|
1701
1941
|
if token in ['now', 'today', 'newline', 'tab', 'empty']:
|
|
1702
1942
|
return value
|
|
1703
1943
|
|
|
1704
|
-
if token in ['stringify', 'json', 'lowercase', 'uppercase', 'hash', 'random', 'float', 'integer', 'encode', 'decode']:
|
|
1944
|
+
if token in ['stringify', 'prettify', 'json', 'lowercase', 'uppercase', 'hash', 'random', 'float', 'integer', 'encode', 'decode']:
|
|
1705
1945
|
value['content'] = self.nextValue()
|
|
1706
1946
|
return value
|
|
1707
1947
|
|
|
@@ -1720,10 +1960,10 @@ class Core(Handler):
|
|
|
1720
1960
|
if self.nextToken() == 'of':
|
|
1721
1961
|
if self.nextIsSymbol():
|
|
1722
1962
|
symbolRecord = self.getSymbolRecord()
|
|
1723
|
-
if symbolRecord['
|
|
1963
|
+
if symbolRecord['hasValue']:
|
|
1724
1964
|
value['target'] = symbolRecord['name']
|
|
1725
1965
|
return value
|
|
1726
|
-
self.warning(f'Core.compileValue: Token
|
|
1966
|
+
self.warning(f'Core.compileValue: Token {symbolRecord["name"]} does not hold a value')
|
|
1727
1967
|
return None
|
|
1728
1968
|
|
|
1729
1969
|
if token == 'property':
|
|
@@ -1731,13 +1971,10 @@ class Core(Handler):
|
|
|
1731
1971
|
if self.nextToken() == 'of':
|
|
1732
1972
|
if self.nextIsSymbol():
|
|
1733
1973
|
symbolRecord = self.getSymbolRecord()
|
|
1734
|
-
if symbolRecord['
|
|
1974
|
+
if symbolRecord['hasValue']:
|
|
1735
1975
|
value['target'] = symbolRecord['name']
|
|
1736
1976
|
return value
|
|
1737
|
-
|
|
1738
|
-
value['value'] = self.getValue()
|
|
1739
|
-
return value
|
|
1740
|
-
self.warning(f'Core.compileValue: Token \'{self.getToken()}\' does not hold a value')
|
|
1977
|
+
NoValueError(self.compiler, symbolRecord)
|
|
1741
1978
|
return None
|
|
1742
1979
|
|
|
1743
1980
|
if token == 'arg':
|
|
@@ -1780,8 +2017,9 @@ class Core(Handler):
|
|
|
1780
2017
|
if token == 'count':
|
|
1781
2018
|
if self.nextIs('of'):
|
|
1782
2019
|
if self.nextIsSymbol():
|
|
1783
|
-
|
|
1784
|
-
|
|
2020
|
+
if self.getSymbolRecord()['hasValue']:
|
|
2021
|
+
value['name'] = self.getToken()
|
|
2022
|
+
return value
|
|
1785
2023
|
return None
|
|
1786
2024
|
|
|
1787
2025
|
if token == 'index':
|
|
@@ -1808,10 +2046,12 @@ class Core(Handler):
|
|
|
1808
2046
|
return None
|
|
1809
2047
|
|
|
1810
2048
|
if token == 'value':
|
|
1811
|
-
value['type'] = 'valueOf'
|
|
1812
2049
|
if self.nextIs('of'):
|
|
1813
|
-
|
|
1814
|
-
|
|
2050
|
+
v = self.nextValue()
|
|
2051
|
+
if v !=None:
|
|
2052
|
+
value['type'] = 'valueOf'
|
|
2053
|
+
value['content'] = v
|
|
2054
|
+
return value
|
|
1815
2055
|
return None
|
|
1816
2056
|
|
|
1817
2057
|
if token == 'length':
|
|
@@ -1880,14 +2120,24 @@ class Core(Handler):
|
|
|
1880
2120
|
return value
|
|
1881
2121
|
|
|
1882
2122
|
if token == 'error':
|
|
1883
|
-
|
|
2123
|
+
token = self.peek()
|
|
2124
|
+
if token == 'code':
|
|
1884
2125
|
self.nextToken()
|
|
1885
2126
|
value['item'] = 'errorCode'
|
|
1886
2127
|
return value
|
|
1887
|
-
|
|
2128
|
+
elif token == 'reason':
|
|
1888
2129
|
self.nextToken()
|
|
1889
2130
|
value['item'] = 'errorReason'
|
|
1890
2131
|
return value
|
|
2132
|
+
elif token in ['in', 'of']:
|
|
2133
|
+
self.nextToken()
|
|
2134
|
+
if self.nextIsSymbol():
|
|
2135
|
+
record = self.getSymbolRecord()
|
|
2136
|
+
if record['keyword'] == 'ssh':
|
|
2137
|
+
value['item'] = 'sshError'
|
|
2138
|
+
value['name'] = record['name']
|
|
2139
|
+
return value
|
|
2140
|
+
return None
|
|
1891
2141
|
|
|
1892
2142
|
if token == 'type':
|
|
1893
2143
|
if self.nextIs('of'):
|
|
@@ -1906,6 +2156,9 @@ class Core(Handler):
|
|
|
1906
2156
|
value['command'] = self.nextValue()
|
|
1907
2157
|
return value
|
|
1908
2158
|
|
|
2159
|
+
if token == 'ticker':
|
|
2160
|
+
return value
|
|
2161
|
+
|
|
1909
2162
|
return None
|
|
1910
2163
|
|
|
1911
2164
|
#############################################################################
|
|
@@ -1984,6 +2237,10 @@ class Core(Handler):
|
|
|
1984
2237
|
base64_bytes = content.encode('ascii')
|
|
1985
2238
|
message_bytes = base64.b64decode(base64_bytes)
|
|
1986
2239
|
value['content'] = message_bytes.decode('ascii')
|
|
2240
|
+
elif self.encoding == 'hex':
|
|
2241
|
+
hex_bytes = content.encode('utf-8')
|
|
2242
|
+
message_bytes = binascii.unhexlify(hex_bytes)
|
|
2243
|
+
value['content'] = message_bytes.decode('utf-8')
|
|
1987
2244
|
else:
|
|
1988
2245
|
value = v
|
|
1989
2246
|
return value
|
|
@@ -2027,6 +2284,10 @@ class Core(Handler):
|
|
|
2027
2284
|
data_bytes = content.encode('ascii')
|
|
2028
2285
|
base64_bytes = base64.b64encode(data_bytes)
|
|
2029
2286
|
value['content'] = base64_bytes.decode('ascii')
|
|
2287
|
+
elif self.encoding == 'hex':
|
|
2288
|
+
data_bytes = content.encode('utf-8')
|
|
2289
|
+
hex_bytes = binascii.hexlify(data_bytes)
|
|
2290
|
+
value['content'] = hex_bytes.decode('utf-8')
|
|
2030
2291
|
else:
|
|
2031
2292
|
value = v
|
|
2032
2293
|
return value
|
|
@@ -2040,6 +2301,10 @@ class Core(Handler):
|
|
|
2040
2301
|
elif v['item'] == 'errorReason':
|
|
2041
2302
|
value['type'] = 'text'
|
|
2042
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 ''
|
|
2043
2308
|
return value
|
|
2044
2309
|
|
|
2045
2310
|
def v_files(self, v):
|
|
@@ -2119,7 +2384,7 @@ class Core(Handler):
|
|
|
2119
2384
|
try:
|
|
2120
2385
|
value['content'] = json.loads(item)
|
|
2121
2386
|
except:
|
|
2122
|
-
|
|
2387
|
+
value = None
|
|
2123
2388
|
return value
|
|
2124
2389
|
|
|
2125
2390
|
def v_keys(self, v):
|
|
@@ -2191,7 +2456,7 @@ class Core(Handler):
|
|
|
2191
2456
|
def v_now(self, v):
|
|
2192
2457
|
value = {}
|
|
2193
2458
|
value['type'] = 'int'
|
|
2194
|
-
value['content'] =
|
|
2459
|
+
value['content'] = int(time.time())
|
|
2195
2460
|
return value
|
|
2196
2461
|
|
|
2197
2462
|
def v_position(self, v):
|
|
@@ -2203,6 +2468,13 @@ class Core(Handler):
|
|
|
2203
2468
|
value['content'] = haystack.rfind(needle) if last else haystack.find(needle)
|
|
2204
2469
|
return value
|
|
2205
2470
|
|
|
2471
|
+
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
|
|
2477
|
+
|
|
2206
2478
|
def v_property(self, v):
|
|
2207
2479
|
propertyValue = self.getRuntimeValue(v['name'])
|
|
2208
2480
|
if 'target' in v:
|
|
@@ -2228,7 +2500,7 @@ class Core(Handler):
|
|
|
2228
2500
|
limit = self.getRuntimeValue(v['content'])
|
|
2229
2501
|
value = {}
|
|
2230
2502
|
value['type'] = 'int'
|
|
2231
|
-
value['content'] = randrange(0, limit)
|
|
2503
|
+
value['content'] = random.randrange(0, limit)
|
|
2232
2504
|
return value
|
|
2233
2505
|
|
|
2234
2506
|
def v_right(self, v):
|
|
@@ -2255,9 +2527,17 @@ class Core(Handler):
|
|
|
2255
2527
|
return value
|
|
2256
2528
|
|
|
2257
2529
|
# This is used by the expression evaluator to get the value of a symbol
|
|
2258
|
-
def v_symbol(self,
|
|
2259
|
-
|
|
2530
|
+
def v_symbol(self, value):
|
|
2531
|
+
name = value['name']
|
|
2532
|
+
symbolRecord = self.program.getSymbolRecord(name)
|
|
2533
|
+
keyword = symbolRecord['keyword']
|
|
2534
|
+
if keyword == 'variable':
|
|
2260
2535
|
return self.getSymbolValue(symbolRecord)
|
|
2536
|
+
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
|
|
2261
2541
|
else:
|
|
2262
2542
|
return None
|
|
2263
2543
|
|
|
@@ -2283,6 +2563,12 @@ class Core(Handler):
|
|
|
2283
2563
|
value['content'] = round(math.tan(angle * 0.01745329) * radius)
|
|
2284
2564
|
return value
|
|
2285
2565
|
|
|
2566
|
+
def v_ticker(self, v):
|
|
2567
|
+
value = {}
|
|
2568
|
+
value['type'] = 'int'
|
|
2569
|
+
value['content'] = self.program.ticker
|
|
2570
|
+
return value
|
|
2571
|
+
|
|
2286
2572
|
def v_timestamp(self, v):
|
|
2287
2573
|
value = {}
|
|
2288
2574
|
value['type'] = 'int'
|
|
@@ -2339,7 +2625,7 @@ class Core(Handler):
|
|
|
2339
2625
|
v = self.getRuntimeValue(v['content'])
|
|
2340
2626
|
value = {}
|
|
2341
2627
|
value['type'] = 'int'
|
|
2342
|
-
value['content'] = int(v)
|
|
2628
|
+
value['content'] = int(v) if v != '' else 0
|
|
2343
2629
|
return value
|
|
2344
2630
|
|
|
2345
2631
|
def v_weekday(self, v):
|
|
@@ -2351,19 +2637,46 @@ class Core(Handler):
|
|
|
2351
2637
|
#############################################################################
|
|
2352
2638
|
# Compile a condition
|
|
2353
2639
|
def compileCondition(self):
|
|
2354
|
-
condition =
|
|
2355
|
-
|
|
2640
|
+
condition = Object()
|
|
2641
|
+
condition.negate = False
|
|
2642
|
+
|
|
2643
|
+
token = self.getToken()
|
|
2644
|
+
|
|
2645
|
+
if token == 'not':
|
|
2356
2646
|
condition.type = 'not'
|
|
2357
2647
|
condition.value = self.nextValue()
|
|
2358
2648
|
return condition
|
|
2359
2649
|
|
|
2360
|
-
|
|
2650
|
+
elif token == 'error':
|
|
2651
|
+
self.nextToken()
|
|
2652
|
+
self.skip('in')
|
|
2653
|
+
if self.nextIsSymbol():
|
|
2654
|
+
record = self.getSymbolRecord()
|
|
2655
|
+
if record['keyword'] == 'ssh':
|
|
2656
|
+
condition.type = 'sshError'
|
|
2657
|
+
condition.target = record['name']
|
|
2658
|
+
return condition
|
|
2659
|
+
return None
|
|
2660
|
+
|
|
2661
|
+
elif token == 'file':
|
|
2361
2662
|
path = self.nextValue()
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2663
|
+
condition.path = path
|
|
2664
|
+
condition.type = 'exists'
|
|
2665
|
+
self.skip('on')
|
|
2666
|
+
if self.nextIsSymbol():
|
|
2667
|
+
record = self.getSymbolRecord()
|
|
2668
|
+
if record['keyword'] == 'ssh':
|
|
2669
|
+
condition.type = 'sshExists'
|
|
2670
|
+
condition.target = record['name']
|
|
2671
|
+
token = self.nextToken()
|
|
2672
|
+
else: token = self.getToken()
|
|
2673
|
+
if token == 'exists':
|
|
2366
2674
|
return condition
|
|
2675
|
+
elif token == 'does':
|
|
2676
|
+
if self.nextIs('not'):
|
|
2677
|
+
if self.nextIs('exist'):
|
|
2678
|
+
condition.negate = not condition.negate
|
|
2679
|
+
return condition
|
|
2367
2680
|
return None
|
|
2368
2681
|
|
|
2369
2682
|
value = self.getValue()
|
|
@@ -2383,6 +2696,25 @@ class Core(Handler):
|
|
|
2383
2696
|
return condition
|
|
2384
2697
|
return None
|
|
2385
2698
|
|
|
2699
|
+
if token == 'does':
|
|
2700
|
+
self.nextToken()
|
|
2701
|
+
if self.nextIs('not'):
|
|
2702
|
+
token = self.nextToken()
|
|
2703
|
+
if token == 'have':
|
|
2704
|
+
if self.nextToken() == 'property':
|
|
2705
|
+
prop = self.nextValue()
|
|
2706
|
+
condition.type = 'hasProperty'
|
|
2707
|
+
condition.property = prop
|
|
2708
|
+
condition.negate = not condition.negate
|
|
2709
|
+
return condition
|
|
2710
|
+
elif token == 'include':
|
|
2711
|
+
value = self.nextValue()
|
|
2712
|
+
condition.type = 'includes'
|
|
2713
|
+
condition.value2 = value
|
|
2714
|
+
condition.negate = not condition.negate
|
|
2715
|
+
return condition
|
|
2716
|
+
return None
|
|
2717
|
+
|
|
2386
2718
|
if token in ['starts', 'ends']:
|
|
2387
2719
|
self.nextToken()
|
|
2388
2720
|
if self.nextToken() == 'with':
|
|
@@ -2409,7 +2741,7 @@ class Core(Handler):
|
|
|
2409
2741
|
condition.type = 'is'
|
|
2410
2742
|
condition.value2 = self.getValue()
|
|
2411
2743
|
return condition
|
|
2412
|
-
|
|
2744
|
+
|
|
2413
2745
|
if condition.value1:
|
|
2414
2746
|
# It's a boolean if
|
|
2415
2747
|
condition.type = 'boolean'
|
|
@@ -2461,7 +2793,8 @@ class Core(Handler):
|
|
|
2461
2793
|
|
|
2462
2794
|
def c_exists(self, condition):
|
|
2463
2795
|
path = self.getRuntimeValue(condition.path)
|
|
2464
|
-
|
|
2796
|
+
comparison = os.path.exists(path)
|
|
2797
|
+
return not comparison if condition.negate else comparison
|
|
2465
2798
|
|
|
2466
2799
|
def c_greater(self, condition):
|
|
2467
2800
|
comparison = self.program.compare(condition.value1, condition.value2)
|
|
@@ -2475,12 +2808,13 @@ class Core(Handler):
|
|
|
2475
2808
|
hasProp = True
|
|
2476
2809
|
except:
|
|
2477
2810
|
hasProp = False
|
|
2478
|
-
return hasProp
|
|
2811
|
+
return not hasProp if condition.negate else hasProp
|
|
2479
2812
|
|
|
2480
2813
|
def c_includes(self, condition):
|
|
2481
2814
|
value1 = self.getRuntimeValue(condition.value1)
|
|
2482
2815
|
value2 = self.getRuntimeValue(condition.value2)
|
|
2483
|
-
|
|
2816
|
+
includes = value2 in value1
|
|
2817
|
+
return not includes if condition.negate else includes
|
|
2484
2818
|
|
|
2485
2819
|
def c_is(self, condition):
|
|
2486
2820
|
comparison = self.program.compare(condition.value1, condition.value2)
|
|
@@ -2503,7 +2837,7 @@ class Core(Handler):
|
|
|
2503
2837
|
return not comparison if condition.negate else comparison
|
|
2504
2838
|
|
|
2505
2839
|
def c_not(self, condition):
|
|
2506
|
-
return not self.getRuntimeValue(condition.
|
|
2840
|
+
return not self.getRuntimeValue(condition.value)
|
|
2507
2841
|
|
|
2508
2842
|
def c_object(self, condition):
|
|
2509
2843
|
comparison = type(self.getRuntimeValue(condition.value1)) is dict
|
|
@@ -2511,6 +2845,24 @@ class Core(Handler):
|
|
|
2511
2845
|
|
|
2512
2846
|
def c_odd(self, condition):
|
|
2513
2847
|
return self.getRuntimeValue(condition.value1) % 2 == 1
|
|
2848
|
+
|
|
2849
|
+
def c_sshError(self, condition):
|
|
2850
|
+
target = self.getVariable(condition.target)
|
|
2851
|
+
errormsg = target['error'] if 'error' in target else None
|
|
2852
|
+
condition.errormsg = errormsg
|
|
2853
|
+
test = errormsg != None
|
|
2854
|
+
return not test if condition.negate else test
|
|
2855
|
+
|
|
2856
|
+
def c_sshExists(self, condition):
|
|
2857
|
+
path = self.getRuntimeValue(condition.path)
|
|
2858
|
+
ssh = self.getVariable(condition.target)
|
|
2859
|
+
sftp = ssh['sftp']
|
|
2860
|
+
try:
|
|
2861
|
+
with sftp.open(path, 'r') as remote_file: remote_file.read().decode()
|
|
2862
|
+
comparison = True
|
|
2863
|
+
except:
|
|
2864
|
+
comparison = False
|
|
2865
|
+
return not comparison if condition.negate else comparison
|
|
2514
2866
|
|
|
2515
2867
|
def c_starts(self, condition):
|
|
2516
2868
|
value1 = self.getRuntimeValue(condition.value1)
|
|
@@ -2520,4 +2872,3 @@ class Core(Handler):
|
|
|
2520
2872
|
def c_string(self, condition):
|
|
2521
2873
|
comparison = type(self.getRuntimeValue(condition.value1)) is str
|
|
2522
2874
|
return not comparison if condition.negate else comparison
|
|
2523
|
-
# xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|