easycoder 250317.4__py2.py3-none-any.whl → 250403.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.

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