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