easycoder 250103.2__py2.py3-none-any.whl → 250105.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 +1 -1
- easycoder/ec_compiler.py +3 -0
- easycoder/ec_core.py +203 -17
- easycoder/ec_graphics.py +40 -12
- easycoder/ec_program.py +98 -51
- easycoder/ec_renderer.py +3 -1
- easycoder/ec_value.py +1 -1
- {easycoder-250103.2.dist-info → easycoder-250105.1.dist-info}/METADATA +19 -5
- easycoder-250105.1.dist-info/RECORD +19 -0
- easycoder-250103.2.dist-info/RECORD +0 -19
- {easycoder-250103.2.dist-info → easycoder-250105.1.dist-info}/LICENSE +0 -0
- {easycoder-250103.2.dist-info → easycoder-250105.1.dist-info}/WHEEL +0 -0
- {easycoder-250103.2.dist-info → easycoder-250105.1.dist-info}/entry_points.txt +0 -0
easycoder/__init__.py
CHANGED
easycoder/ec_compiler.py
CHANGED
|
@@ -128,6 +128,7 @@ class Compiler:
|
|
|
128
128
|
FatalError(self, f'Duplicate symbol name "{name}"')
|
|
129
129
|
return False
|
|
130
130
|
self.symbols[name] = self.getPC()
|
|
131
|
+
command['program'] = self.program
|
|
131
132
|
command['type'] = 'symbol'
|
|
132
133
|
command['valueHolder'] = valueHolder
|
|
133
134
|
command['name'] = name
|
|
@@ -136,6 +137,8 @@ class Compiler:
|
|
|
136
137
|
command['value'] = [None]
|
|
137
138
|
command['used'] = False
|
|
138
139
|
command['debug'] = False
|
|
140
|
+
command['import'] = None
|
|
141
|
+
command['locked'] = False
|
|
139
142
|
self.addCommand(command)
|
|
140
143
|
return True
|
|
141
144
|
|
easycoder/ec_core.py
CHANGED
|
@@ -367,8 +367,7 @@ class Core(Handler):
|
|
|
367
367
|
return True
|
|
368
368
|
|
|
369
369
|
def r_exit(self, command):
|
|
370
|
-
|
|
371
|
-
return 0
|
|
370
|
+
return -1
|
|
372
371
|
|
|
373
372
|
# Declare a file variable
|
|
374
373
|
def k_file(self, command):
|
|
@@ -462,7 +461,7 @@ class Core(Handler):
|
|
|
462
461
|
else:
|
|
463
462
|
RuntimeError(self.program, f'Error: {errorReason}')
|
|
464
463
|
retval['content'] = response.text
|
|
465
|
-
self.program.putSymbolValue(target, retval)
|
|
464
|
+
self.program.putSymbolValue(target, retval)
|
|
466
465
|
return self.nextPC()
|
|
467
466
|
|
|
468
467
|
# Go to a label
|
|
@@ -553,15 +552,55 @@ class Core(Handler):
|
|
|
553
552
|
self.program.pc += 1
|
|
554
553
|
return self.program.pc
|
|
555
554
|
|
|
556
|
-
# Import a plugin. This is done at compile time.
|
|
557
|
-
# import {class} from {source}
|
|
558
555
|
def k_import(self, command):
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
self.
|
|
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)
|
|
563
585
|
return True
|
|
564
|
-
|
|
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()
|
|
565
604
|
|
|
566
605
|
# Increment a variable
|
|
567
606
|
def k_increment(self, command):
|
|
@@ -646,9 +685,17 @@ class Core(Handler):
|
|
|
646
685
|
self.putSymbolValue(symbolRecord, value)
|
|
647
686
|
return self.nextPC()
|
|
648
687
|
|
|
649
|
-
# Load a
|
|
688
|
+
# 1 Load a plugin. This is done at compile time.
|
|
689
|
+
# 2 Load text from a file
|
|
650
690
|
def k_load(self, command):
|
|
651
|
-
|
|
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():
|
|
652
699
|
command['target'] = self.getToken()
|
|
653
700
|
if self.nextIs('from'):
|
|
654
701
|
command['file'] = self.nextValue()
|
|
@@ -668,6 +715,27 @@ class Core(Handler):
|
|
|
668
715
|
self.putSymbolValue(target, value)
|
|
669
716
|
return self.nextPC()
|
|
670
717
|
|
|
718
|
+
# Lock a variable
|
|
719
|
+
def k_lock(self, command):
|
|
720
|
+
if self.nextIsSymbol():
|
|
721
|
+
symbolRecord = self.getSymbolRecord()
|
|
722
|
+
command['target'] = symbolRecord['name']
|
|
723
|
+
self.add(command)
|
|
724
|
+
return True
|
|
725
|
+
return False
|
|
726
|
+
|
|
727
|
+
def r_lock(self, command):
|
|
728
|
+
target = self.getVariable(command['target'])
|
|
729
|
+
target['locked'] = True
|
|
730
|
+
return self.nextPC()
|
|
731
|
+
|
|
732
|
+
# Declare a module variable
|
|
733
|
+
def k_module(self, command):
|
|
734
|
+
return self.compileVariable(command)
|
|
735
|
+
|
|
736
|
+
def r_module(self, command):
|
|
737
|
+
return self.nextPC()
|
|
738
|
+
|
|
671
739
|
# Arithmetic multiply
|
|
672
740
|
# multiply {variable} by {value}[ giving {variable}]}
|
|
673
741
|
def k_multiply(self, command):
|
|
@@ -749,6 +817,36 @@ class Core(Handler):
|
|
|
749
817
|
def r_object(self, command):
|
|
750
818
|
return self.nextPC()
|
|
751
819
|
|
|
820
|
+
# on message {action}
|
|
821
|
+
def k_on(self, command):
|
|
822
|
+
if self.nextIs('message'):
|
|
823
|
+
self.nextToken()
|
|
824
|
+
command['goto'] = 0
|
|
825
|
+
self.add(command)
|
|
826
|
+
cmd = {}
|
|
827
|
+
cmd['domain'] = 'core'
|
|
828
|
+
cmd['lino'] = command['lino']
|
|
829
|
+
cmd['keyword'] = 'gotoPC'
|
|
830
|
+
cmd['goto'] = 0
|
|
831
|
+
cmd['debug'] = False
|
|
832
|
+
self.addCommand(cmd)
|
|
833
|
+
# Add the action and a 'stop'
|
|
834
|
+
self.compileOne()
|
|
835
|
+
cmd = {}
|
|
836
|
+
cmd['domain'] = 'core'
|
|
837
|
+
cmd['lino'] = command['lino']
|
|
838
|
+
cmd['keyword'] = 'stop'
|
|
839
|
+
cmd['debug'] = False
|
|
840
|
+
self.addCommand(cmd)
|
|
841
|
+
# Fixup the link
|
|
842
|
+
command['goto'] = self.getPC()
|
|
843
|
+
return True
|
|
844
|
+
return False
|
|
845
|
+
|
|
846
|
+
def r_on(self, command):
|
|
847
|
+
self.program.onMessage(self.nextPC()+1)
|
|
848
|
+
return command['goto']
|
|
849
|
+
|
|
752
850
|
# Open a file
|
|
753
851
|
# open {file} for reading/writing/appending
|
|
754
852
|
def k_open(self, command):
|
|
@@ -941,13 +1039,13 @@ class Core(Handler):
|
|
|
941
1039
|
if self.nextIsSymbol():
|
|
942
1040
|
symbolRecord = self.getSymbolRecord()
|
|
943
1041
|
command['target'] = symbolRecord['name']
|
|
944
|
-
if symbolRecord['valueHolder']:
|
|
1042
|
+
if 'valueholder' in symbolRecord and symbolRecord['valueHolder'] == False:
|
|
1043
|
+
FatalError(self.program.compiler, f'Symbol {symbolRecord["name"]} is not a value holder')
|
|
1044
|
+
else:
|
|
945
1045
|
self.add(command)
|
|
946
1046
|
return True
|
|
947
|
-
else:
|
|
948
|
-
FatalError(self.program.compiler, f'Symbol {symbolRecord["name"]} is not a value holder')
|
|
949
1047
|
else:
|
|
950
|
-
FatalError(self.program.compiler, f'
|
|
1048
|
+
FatalError(self.program.compiler, f'Symbol {self.getToken()} is not a variable')
|
|
951
1049
|
return False
|
|
952
1050
|
|
|
953
1051
|
def r_put(self, command):
|
|
@@ -1030,6 +1128,16 @@ class Core(Handler):
|
|
|
1030
1128
|
self.putSymbolValue(templateRecord, value)
|
|
1031
1129
|
return self.nextPC()
|
|
1032
1130
|
|
|
1131
|
+
# Release the parent script
|
|
1132
|
+
def k_release(self, command):
|
|
1133
|
+
if self.nextIs('parent'):
|
|
1134
|
+
self.add(command)
|
|
1135
|
+
return True
|
|
1136
|
+
|
|
1137
|
+
def r_release(self, command):
|
|
1138
|
+
self.program.releaseParent()
|
|
1139
|
+
return self.nextPC()
|
|
1140
|
+
|
|
1033
1141
|
# Return from subroutine
|
|
1034
1142
|
def k_return(self, command):
|
|
1035
1143
|
self.add(command)
|
|
@@ -1038,6 +1146,47 @@ class Core(Handler):
|
|
|
1038
1146
|
def r_return(self, command):
|
|
1039
1147
|
return self.stack.pop()
|
|
1040
1148
|
|
|
1149
|
+
# Compile and run a script
|
|
1150
|
+
def k_run(self, command):
|
|
1151
|
+
try:
|
|
1152
|
+
command['path'] = self.nextValue()
|
|
1153
|
+
except Exception as e:
|
|
1154
|
+
self.warning(f'Core.run: Path expected')
|
|
1155
|
+
return False
|
|
1156
|
+
if self.nextIs('as'):
|
|
1157
|
+
if self.nextIsSymbol():
|
|
1158
|
+
record = self.getSymbolRecord()
|
|
1159
|
+
if record['keyword'] == 'module':
|
|
1160
|
+
command['module'] = record['name']
|
|
1161
|
+
exports = []
|
|
1162
|
+
if self.nextIs('with'):
|
|
1163
|
+
while True:
|
|
1164
|
+
name = self.nextToken()
|
|
1165
|
+
record = self.getSymbolRecord()
|
|
1166
|
+
exports.append(name)
|
|
1167
|
+
if self.peek() != 'and':
|
|
1168
|
+
break
|
|
1169
|
+
self.nextToken()
|
|
1170
|
+
command['exports'] = json.dumps(exports)
|
|
1171
|
+
self.add(command)
|
|
1172
|
+
return True
|
|
1173
|
+
return False
|
|
1174
|
+
|
|
1175
|
+
def r_run(self, command):
|
|
1176
|
+
module = self.getVariable(command['module'])
|
|
1177
|
+
path = self.getRuntimeValue(command['path'])
|
|
1178
|
+
exports = json.loads(command['exports'])
|
|
1179
|
+
for n in range(0, len(exports)):
|
|
1180
|
+
exports[n] = self.getVariable(exports[n])
|
|
1181
|
+
module['path'] = path
|
|
1182
|
+
parent = Object()
|
|
1183
|
+
parent.program = self.program
|
|
1184
|
+
parent.pc = self.nextPC()
|
|
1185
|
+
parent.waiting = True
|
|
1186
|
+
p = self.program.__class__
|
|
1187
|
+
p(path).start(parent, module, exports)
|
|
1188
|
+
return 0
|
|
1189
|
+
|
|
1041
1190
|
# Provide a name for the script
|
|
1042
1191
|
def k_script(self, command):
|
|
1043
1192
|
self.program.name = self.nextToken()
|
|
@@ -1060,6 +1209,24 @@ class Core(Handler):
|
|
|
1060
1209
|
f.close()
|
|
1061
1210
|
return self.nextPC()
|
|
1062
1211
|
|
|
1212
|
+
# Send a message to a module
|
|
1213
|
+
def k_send(self, command):
|
|
1214
|
+
command['message'] = self.nextValue()
|
|
1215
|
+
if self.nextIs('to'):
|
|
1216
|
+
if self.nextIsSymbol():
|
|
1217
|
+
record = self.getSymbolRecord()
|
|
1218
|
+
if record['keyword'] == 'module':
|
|
1219
|
+
command['module'] = record['name']
|
|
1220
|
+
self.add(command)
|
|
1221
|
+
return True
|
|
1222
|
+
return False
|
|
1223
|
+
|
|
1224
|
+
def r_send(self, command):
|
|
1225
|
+
message = self.getRuntimeValue(command['message'])
|
|
1226
|
+
module = self.getVariable(command['module'])
|
|
1227
|
+
module['child'].handleMessage(message)
|
|
1228
|
+
return self.nextPC()
|
|
1229
|
+
|
|
1063
1230
|
# Set a value
|
|
1064
1231
|
# set {variable}
|
|
1065
1232
|
# set the elements of {variable} to {value}
|
|
@@ -1364,6 +1531,20 @@ class Core(Handler):
|
|
|
1364
1531
|
fileRecord['file'].truncate()
|
|
1365
1532
|
return self.nextPC()
|
|
1366
1533
|
|
|
1534
|
+
# Unlock a variable
|
|
1535
|
+
def k_unlock(self, command):
|
|
1536
|
+
if self.nextIsSymbol():
|
|
1537
|
+
symbolRecord = self.getSymbolRecord()
|
|
1538
|
+
command['target'] = symbolRecord['name']
|
|
1539
|
+
self.add(command)
|
|
1540
|
+
return True
|
|
1541
|
+
return False
|
|
1542
|
+
|
|
1543
|
+
def r_unlock(self, command):
|
|
1544
|
+
target = self.getVariable(command['target'])
|
|
1545
|
+
target['locked'] = False
|
|
1546
|
+
return self.nextPC()
|
|
1547
|
+
|
|
1367
1548
|
# Declare a general-purpose variable
|
|
1368
1549
|
def k_variable(self, command):
|
|
1369
1550
|
return self.compileVariable(command, True)
|
|
@@ -1669,7 +1850,6 @@ class Core(Handler):
|
|
|
1669
1850
|
return value
|
|
1670
1851
|
|
|
1671
1852
|
if token == 'message':
|
|
1672
|
-
self.nextToken()
|
|
1673
1853
|
return value
|
|
1674
1854
|
|
|
1675
1855
|
if token == 'timestamp':
|
|
@@ -1975,6 +2155,12 @@ class Core(Handler):
|
|
|
1975
2155
|
value['content'] = megabytes
|
|
1976
2156
|
return value
|
|
1977
2157
|
|
|
2158
|
+
def v_message(self, v):
|
|
2159
|
+
value = {}
|
|
2160
|
+
value['type'] = 'text'
|
|
2161
|
+
value['content'] = self.program.message
|
|
2162
|
+
return value
|
|
2163
|
+
|
|
1978
2164
|
def v_modification(self, v):
|
|
1979
2165
|
fileName = self.getRuntimeValue(v['fileName'])
|
|
1980
2166
|
ts = int(os.stat(fileName).st_mtime)
|
easycoder/ec_graphics.py
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import sys, threading, json
|
|
1
2
|
from .ec_classes import FatalError, RuntimeError, Object
|
|
2
3
|
from .ec_handler import Handler
|
|
3
4
|
from .ec_screenspec import ScreenSpec
|
|
4
5
|
from .ec_renderer import Renderer
|
|
6
|
+
from .ec_program import flush
|
|
5
7
|
|
|
6
8
|
class Graphics(Handler):
|
|
7
9
|
|
|
@@ -173,8 +175,8 @@ class Graphics(Handler):
|
|
|
173
175
|
if type == 'window':
|
|
174
176
|
self.windowSpec = Object()
|
|
175
177
|
self.windowSpec.title = command['title']['content']
|
|
176
|
-
self.windowSpec.flush =
|
|
177
|
-
self.windowSpec.
|
|
178
|
+
self.windowSpec.flush = flush
|
|
179
|
+
self.windowSpec.kill = self.program.kill
|
|
178
180
|
self.windowSpec.pos = (self.getRuntimeValue(command['pos'][0]), self.getRuntimeValue(command['pos'][1]))
|
|
179
181
|
self.windowSpec.size = (self.getRuntimeValue(command['size'][0]), self.getRuntimeValue(command['size'][1]))
|
|
180
182
|
self.windowSpec.fill = (self.getRuntimeValue(command['fill'][0])/255, self.getRuntimeValue(command['fill'][1])/255, self.getRuntimeValue(command['fill'][2])/255)
|
|
@@ -219,7 +221,7 @@ class Graphics(Handler):
|
|
|
219
221
|
if self.nextIsSymbol():
|
|
220
222
|
record = self.getSymbolRecord()
|
|
221
223
|
type = record['keyword']
|
|
222
|
-
if type
|
|
224
|
+
if self.isGraphicType(type):
|
|
223
225
|
command['target'] = record['id']
|
|
224
226
|
token = self.nextToken()
|
|
225
227
|
if token == 'to':
|
|
@@ -283,7 +285,7 @@ class Graphics(Handler):
|
|
|
283
285
|
if command['type'] == 'tap':
|
|
284
286
|
record = self.getVariable(command['target'])
|
|
285
287
|
keyword = record['keyword']
|
|
286
|
-
if keyword
|
|
288
|
+
if self.isGraphicType(keyword):
|
|
287
289
|
id = record['value'][record['index']]['content']
|
|
288
290
|
self.ui.setOnClick(id, lambda: self.run(pc))
|
|
289
291
|
else:
|
|
@@ -296,12 +298,6 @@ class Graphics(Handler):
|
|
|
296
298
|
def r_rectangle(self, command):
|
|
297
299
|
return self.nextPC()
|
|
298
300
|
|
|
299
|
-
def k_text(self, command):
|
|
300
|
-
return self.compileVariable(command)
|
|
301
|
-
|
|
302
|
-
def r_text(self, command):
|
|
303
|
-
return self.nextPC()
|
|
304
|
-
|
|
305
301
|
# render {spec}
|
|
306
302
|
def k_render(self, command):
|
|
307
303
|
command['spec'] = self.nextValue()
|
|
@@ -332,6 +328,7 @@ class Graphics(Handler):
|
|
|
332
328
|
def r_run(self, command):
|
|
333
329
|
self.renderer = Renderer()
|
|
334
330
|
self.renderer.init(self.windowSpec)
|
|
331
|
+
self.ui = self.renderer.getUI()
|
|
335
332
|
self.program.setExternalControl()
|
|
336
333
|
self.program.run(self.nextPC())
|
|
337
334
|
self.renderer.run()
|
|
@@ -343,7 +340,7 @@ class Graphics(Handler):
|
|
|
343
340
|
if self.nextIs('of'):
|
|
344
341
|
if self.nextIsSymbol():
|
|
345
342
|
record = self.getSymbolRecord()
|
|
346
|
-
if record['keyword']
|
|
343
|
+
if self.isGraphicType(record['keyword']):
|
|
347
344
|
command['target'] = record['name']
|
|
348
345
|
if self.nextIs('to'):
|
|
349
346
|
command['value'] = self.nextValue()
|
|
@@ -363,6 +360,12 @@ class Graphics(Handler):
|
|
|
363
360
|
self.ui.setAttribute(id, attribute, value)
|
|
364
361
|
return self.nextPC()
|
|
365
362
|
|
|
363
|
+
def k_text(self, command):
|
|
364
|
+
return self.compileVariable(command)
|
|
365
|
+
|
|
366
|
+
def r_text(self, command):
|
|
367
|
+
return self.nextPC()
|
|
368
|
+
|
|
366
369
|
#############################################################################
|
|
367
370
|
# Modify a value or leave it unchanged.
|
|
368
371
|
def modifyValue(self, value):
|
|
@@ -373,6 +376,16 @@ class Graphics(Handler):
|
|
|
373
376
|
def compileValue(self):
|
|
374
377
|
value = {}
|
|
375
378
|
value['domain'] = self.getName()
|
|
379
|
+
token = self.getToken()
|
|
380
|
+
if self.isSymbol():
|
|
381
|
+
value['name'] = token
|
|
382
|
+
symbolRecord = self.getSymbolRecord()
|
|
383
|
+
keyword = symbolRecord['keyword']
|
|
384
|
+
if keyword == 'graphic':
|
|
385
|
+
value['type'] = 'symbol'
|
|
386
|
+
return value
|
|
387
|
+
return None
|
|
388
|
+
|
|
376
389
|
if self.tokenIs('the'):
|
|
377
390
|
self.nextToken()
|
|
378
391
|
kwd = self.getToken()
|
|
@@ -382,7 +395,7 @@ class Graphics(Handler):
|
|
|
382
395
|
if self.nextIs('of'):
|
|
383
396
|
if self.nextIsSymbol():
|
|
384
397
|
record = self.getSymbolRecord()
|
|
385
|
-
if record['keyword']
|
|
398
|
+
if self.isGraphicType(record['keyword']):
|
|
386
399
|
value['attribute'] = attribute
|
|
387
400
|
value['target'] = record['name']
|
|
388
401
|
return value
|
|
@@ -393,6 +406,12 @@ class Graphics(Handler):
|
|
|
393
406
|
return value
|
|
394
407
|
return None
|
|
395
408
|
|
|
409
|
+
#############################################################################
|
|
410
|
+
# Test if a graphic type
|
|
411
|
+
|
|
412
|
+
def isGraphicType(self, type):
|
|
413
|
+
return type in ['ellipse', 'rectangle', 'text', 'image']
|
|
414
|
+
|
|
396
415
|
#############################################################################
|
|
397
416
|
# Value handlers
|
|
398
417
|
|
|
@@ -409,6 +428,15 @@ class Graphics(Handler):
|
|
|
409
428
|
except Exception as e:
|
|
410
429
|
RuntimeError(self.program, e)
|
|
411
430
|
|
|
431
|
+
# This is used by the expression evaluator to get the value of a symbol
|
|
432
|
+
def v_symbol(self, symbolRecord):
|
|
433
|
+
result = {}
|
|
434
|
+
if symbolRecord['keyword'] == 'graphic':
|
|
435
|
+
symbolValue = self.getSymbolValue(symbolRecord)
|
|
436
|
+
return symbolValue
|
|
437
|
+
else:
|
|
438
|
+
return None
|
|
439
|
+
|
|
412
440
|
def v_window(self, v):
|
|
413
441
|
try:
|
|
414
442
|
attribute = v['attribute']
|
easycoder/ec_program.py
CHANGED
|
@@ -1,21 +1,29 @@
|
|
|
1
1
|
import time, json, sys
|
|
2
2
|
from copy import deepcopy
|
|
3
3
|
from collections import deque
|
|
4
|
-
from .ec_classes import Script, Token, FatalError, RuntimeError
|
|
4
|
+
from .ec_classes import Script, Token, FatalError, RuntimeError, Object
|
|
5
5
|
from .ec_compiler import Compiler
|
|
6
6
|
from .ec_core import Core
|
|
7
7
|
import importlib
|
|
8
8
|
from importlib.metadata import version
|
|
9
9
|
|
|
10
|
+
# Flush the queue
|
|
11
|
+
def flush():
|
|
12
|
+
global queue
|
|
13
|
+
while len(queue):
|
|
14
|
+
item = queue.popleft()
|
|
15
|
+
item.program.flush(item.pc)
|
|
16
|
+
|
|
10
17
|
class Program:
|
|
11
18
|
|
|
12
19
|
def __init__(self, argv):
|
|
20
|
+
global queue
|
|
13
21
|
print(f'EasyCoder version {version("easycoder")}')
|
|
14
22
|
if len(argv) == 0:
|
|
15
23
|
print('No script supplied')
|
|
16
24
|
exit()
|
|
17
25
|
self.classes=[Core]
|
|
18
|
-
scriptName = argv
|
|
26
|
+
scriptName = argv
|
|
19
27
|
if scriptName.endswith('.ecg'):
|
|
20
28
|
from .ec_graphics import Graphics
|
|
21
29
|
self.classes.append(Graphics)
|
|
@@ -23,6 +31,8 @@ class Program:
|
|
|
23
31
|
f = open(scriptName, 'r')
|
|
24
32
|
source = f.read()
|
|
25
33
|
f.close()
|
|
34
|
+
queue = deque()
|
|
35
|
+
self.argv = argv
|
|
26
36
|
self.domains = []
|
|
27
37
|
self.domainIndex = {}
|
|
28
38
|
self.name = '<anon>'
|
|
@@ -37,11 +47,14 @@ class Program:
|
|
|
37
47
|
self.value = self.compiler.value
|
|
38
48
|
self.condition = self.compiler.condition
|
|
39
49
|
self.processClasses()
|
|
40
|
-
self.queue = deque()
|
|
41
50
|
self.externalControl = False
|
|
42
|
-
self.
|
|
51
|
+
self.running = True
|
|
43
52
|
|
|
44
|
-
def start(self):
|
|
53
|
+
def start(self, parent=None, module = None, exports=[]):
|
|
54
|
+
self.parent = parent
|
|
55
|
+
self.exports = exports
|
|
56
|
+
if module != None:
|
|
57
|
+
module['child'] = self
|
|
45
58
|
startCompile = time.time()
|
|
46
59
|
self.tokenise(self.script)
|
|
47
60
|
if self.compiler.compileFrom(0, []):
|
|
@@ -59,7 +72,15 @@ class Program:
|
|
|
59
72
|
self.run(0)
|
|
60
73
|
else:
|
|
61
74
|
self.compiler.showWarnings()
|
|
62
|
-
|
|
75
|
+
|
|
76
|
+
# If this is the main script and there's no graphics, run a main loop
|
|
77
|
+
if parent == None and self.externalControl == False:
|
|
78
|
+
while True:
|
|
79
|
+
if self.running == True:
|
|
80
|
+
flush()
|
|
81
|
+
time.sleep(0.1)
|
|
82
|
+
else:
|
|
83
|
+
break
|
|
63
84
|
|
|
64
85
|
# Import a plugin
|
|
65
86
|
def importPlugin(self, source):
|
|
@@ -98,11 +119,11 @@ class Program:
|
|
|
98
119
|
def getSymbolRecord(self, name):
|
|
99
120
|
try:
|
|
100
121
|
target = self.code[self.symbols[name]]
|
|
122
|
+
if target['import'] != None:
|
|
123
|
+
target = target['import']
|
|
124
|
+
return target
|
|
101
125
|
except:
|
|
102
|
-
RuntimeError(self
|
|
103
|
-
return None
|
|
104
|
-
|
|
105
|
-
return target
|
|
126
|
+
RuntimeError(self, f'Unknown symbol \'{name}\'')
|
|
106
127
|
|
|
107
128
|
def doValue(self, value):
|
|
108
129
|
if value == None:
|
|
@@ -129,7 +150,7 @@ class Program:
|
|
|
129
150
|
name = value['name']
|
|
130
151
|
symbolRecord = self.getSymbolRecord(name)
|
|
131
152
|
if symbolRecord['value'] == [None]:
|
|
132
|
-
RuntimeWarning(self.
|
|
153
|
+
RuntimeWarning(self.program, f'Variable "{name}" has no value')
|
|
133
154
|
return None
|
|
134
155
|
handler = self.domainIndex[symbolRecord['domain']].valueHandler('symbol')
|
|
135
156
|
result = handler(symbolRecord)
|
|
@@ -184,6 +205,8 @@ class Program:
|
|
|
184
205
|
return copy
|
|
185
206
|
|
|
186
207
|
def putSymbolValue(self, symbolRecord, value):
|
|
208
|
+
if symbolRecord['locked']:
|
|
209
|
+
RuntimeError(self, f'Symbol \'{symbolRecord['name']}\' is locked')
|
|
187
210
|
if symbolRecord['value'] == None or symbolRecord['value'] == []:
|
|
188
211
|
symbolRecord['value'] = [value]
|
|
189
212
|
else:
|
|
@@ -192,11 +215,11 @@ class Program:
|
|
|
192
215
|
index = 0
|
|
193
216
|
symbolRecord['value'][index] = value
|
|
194
217
|
|
|
195
|
-
def encode(self, value
|
|
196
|
-
return value
|
|
218
|
+
def encode(self, value):
|
|
219
|
+
return value
|
|
197
220
|
|
|
198
|
-
def decode(self, value
|
|
199
|
-
return value
|
|
221
|
+
def decode(self, value):
|
|
222
|
+
return value
|
|
200
223
|
|
|
201
224
|
# Tokenise the script
|
|
202
225
|
def tokenise(self, script):
|
|
@@ -256,48 +279,56 @@ class Program:
|
|
|
256
279
|
token = ''
|
|
257
280
|
return
|
|
258
281
|
|
|
259
|
-
def
|
|
260
|
-
self.
|
|
282
|
+
def releaseParent(self):
|
|
283
|
+
if self.parent.waiting and self.parent.program.running:
|
|
284
|
+
self.parent.waiting = False
|
|
285
|
+
self.parent.program.run(self.parent.pc)
|
|
261
286
|
|
|
262
287
|
# Flush the queue
|
|
263
|
-
def flush(self):
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
288
|
+
def flush(self, pc):
|
|
289
|
+
global queue
|
|
290
|
+
self.pc = pc
|
|
291
|
+
while True:
|
|
292
|
+
command = self.code[self.pc]
|
|
293
|
+
domainName = command['domain']
|
|
294
|
+
if domainName == None:
|
|
295
|
+
self.pc += 1
|
|
296
|
+
else:
|
|
297
|
+
keyword = command['keyword']
|
|
298
|
+
if self.debugStep and command['debug']:
|
|
299
|
+
lino = command['lino'] + 1
|
|
300
|
+
line = self.script.lines[command['lino']].strip()
|
|
301
|
+
print(f'{self.name}: Line {lino}: {domainName}:{keyword}: {line}')
|
|
302
|
+
domain = self.domainIndex[domainName]
|
|
303
|
+
handler = domain.runHandler(keyword)
|
|
304
|
+
if handler:
|
|
305
|
+
command = self.code[self.pc]
|
|
306
|
+
command['program'] = self
|
|
307
|
+
self.pc = handler(command)
|
|
308
|
+
# Deal with 'exit'
|
|
309
|
+
if self.pc == -1:
|
|
310
|
+
queue = deque()
|
|
311
|
+
if self.parent != None:
|
|
312
|
+
self.releaseParent()
|
|
313
|
+
else:
|
|
314
|
+
self.running = False
|
|
289
315
|
break
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
self.externalControl = True
|
|
316
|
+
elif self.pc == None or self.pc == 0 or self.pc >= len(self.code):
|
|
317
|
+
break
|
|
293
318
|
|
|
294
319
|
# Run the script
|
|
295
320
|
def run(self, pc):
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
321
|
+
global queue
|
|
322
|
+
item = Object()
|
|
323
|
+
item.program = self
|
|
324
|
+
item.pc = pc
|
|
325
|
+
queue.append(item)
|
|
326
|
+
|
|
327
|
+
def kill(self):
|
|
328
|
+
self.running = False
|
|
329
|
+
|
|
330
|
+
def setExternalControl(self):
|
|
331
|
+
self.externalControl = True
|
|
301
332
|
|
|
302
333
|
def nonNumericValueError(self):
|
|
303
334
|
FatalError(self.compiler, 'Non-numeric value')
|
|
@@ -343,3 +374,19 @@ class Program:
|
|
|
343
374
|
if v1 < v2:
|
|
344
375
|
return -1
|
|
345
376
|
return 0
|
|
377
|
+
|
|
378
|
+
# Set up a message handler
|
|
379
|
+
def onMessage(self, pc):
|
|
380
|
+
self.onMessagePC = pc
|
|
381
|
+
|
|
382
|
+
# Handle a message from our parent program
|
|
383
|
+
def handleMessage(self, message):
|
|
384
|
+
self.message = message
|
|
385
|
+
self.run(self.onMessagePC)
|
|
386
|
+
|
|
387
|
+
# This is the program launcher
|
|
388
|
+
def Main():
|
|
389
|
+
if (len(sys.argv) > 1):
|
|
390
|
+
Program(sys.argv[1]).start()
|
|
391
|
+
else:
|
|
392
|
+
print('Syntax: easycoder <scriptname> [plugins]')
|
easycoder/ec_renderer.py
CHANGED
|
@@ -225,6 +225,7 @@ class Renderer(App):
|
|
|
225
225
|
|
|
226
226
|
def request_close(self):
|
|
227
227
|
print('close window')
|
|
228
|
+
self.kill()
|
|
228
229
|
Window.close()
|
|
229
230
|
|
|
230
231
|
def flushQueue(self, dt):
|
|
@@ -232,12 +233,13 @@ class Renderer(App):
|
|
|
232
233
|
|
|
233
234
|
def build(self):
|
|
234
235
|
Clock.schedule_interval(self.flushQueue, 0.01)
|
|
235
|
-
self.ui = UI()
|
|
236
236
|
return self.ui
|
|
237
237
|
|
|
238
238
|
def init(self, spec):
|
|
239
|
+
self.ui = UI()
|
|
239
240
|
self.title = spec.title
|
|
240
241
|
self.flush = spec.flush
|
|
242
|
+
self.kill = spec.kill
|
|
241
243
|
Window.size = spec.size
|
|
242
244
|
Window.left = spec.pos[0]
|
|
243
245
|
Window.top = spec.pos[1]
|
easycoder/ec_value.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: easycoder
|
|
3
|
-
Version:
|
|
3
|
+
Version: 250105.1
|
|
4
4
|
Summary: Rapid scripting in English
|
|
5
5
|
Keywords: compiler,scripting,prototyping,programming,coding,python,low code,hypertalk,computer language,learn to code
|
|
6
6
|
Author-email: Graham Trott <gtanyware@gmail.com>
|
|
@@ -10,12 +10,13 @@ Requires-Dist: pytz
|
|
|
10
10
|
Project-URL: Home, https://github.com/easycoder/easycoder-py
|
|
11
11
|
|
|
12
12
|
# Introduction
|
|
13
|
-
**_EasyCoder_** is a high-level English-like scripting language suited for prototyping and rapid testing of ideas. It operates on the command line and a graphics module is under construction. This version of the language is written in Python and
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
**_EasyCoder_** is a high-level English-like scripting language suited for prototyping and rapid testing of ideas. It operates on the command line and a graphics module is under construction. This version of the language is written in Python and it acts as a fairly thin wrapper around Python functions, giving fast compilation and good runtime performance for general applications.
|
|
14
|
+
<hr>
|
|
15
|
+
For the JavaScript version of **_EasyCoder_**, which provides a full set of graphical features to run in a browser, please visit
|
|
16
16
|
|
|
17
17
|
Repository: [https://github.com/easycoder/easycoder.github.io](https://github.com/easycoder/easycoder.github.io)
|
|
18
18
|
Website: [https://easycoder.github.io](https://easycoder.github.io)
|
|
19
|
+
<hr>
|
|
19
20
|
|
|
20
21
|
## Quick Start
|
|
21
22
|
Install **_EasyCoder_** in your Python environment:
|
|
@@ -28,7 +29,7 @@ print `Hello, world!`
|
|
|
28
29
|
```
|
|
29
30
|
This is traditionally the first program to be written in virtually any language. To run it, use `easycoder hello.ecs`.
|
|
30
31
|
|
|
31
|
-
The output will look like this:
|
|
32
|
+
The output will look like this (the version number will differ):
|
|
32
33
|
|
|
33
34
|
```
|
|
34
35
|
EasyCoder version 250101.1
|
|
@@ -72,6 +73,19 @@ A couple of demo graphical scripts are included in the `scripts` directory:
|
|
|
72
73
|
|
|
73
74
|
**_EasyCoder_** graphics are handled by a library module, `ec_renderer` that can be used outside of the **_EasyCoder_** environment, in other Python programs.
|
|
74
75
|
|
|
76
|
+
## Significant features
|
|
77
|
+
|
|
78
|
+
- English-like syntax based on vocabulary rather than structure. Scripts can be read as English
|
|
79
|
+
- Comprehensive feature set
|
|
80
|
+
- Runs directly from source scripts, using a fast compiler to create efficient intermediate runtime code that is run immediately
|
|
81
|
+
- Low memory requirements
|
|
82
|
+
- Minimim dependency on other 3rd-party packages
|
|
83
|
+
- Built-in co-operative multitasking
|
|
84
|
+
- Dynamic loading of scripts on demand
|
|
85
|
+
- The language can be extended seamlessly using plugin function modules
|
|
86
|
+
- Plays well with any Python code
|
|
87
|
+
- Fully Open Source
|
|
88
|
+
|
|
75
89
|
## Programming reference
|
|
76
90
|
|
|
77
91
|
**_EasyCoder_** comprises a set of modules to handle tokenisation, compilation and runtime control. Syntax and grammar are defined by [packages](doc/README.md), of which there are currently two; the [core](doc/core/README.md) package, which implements a comprehensive set of command-line programming features, and and the [graphics](doc/graphics/README.md) package, which adds graphical features in a windowing environment.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
easycoder/README.md,sha256=PYqOc_SkIGiFbyCNs90y7JqoqWe4aO1xYIW-6bOnFKU,573
|
|
2
|
+
easycoder/__init__.py,sha256=owNA7S9z8wlR3KRxJytrzPU-s4ngA04Xq8_EUinKwK8,283
|
|
3
|
+
easycoder/ec.py,sha256=Nj5PRl8GsKjfGJKq0FOM1a7FeK3cN68CoIFg8lswQEg,221
|
|
4
|
+
easycoder/ec_classes.py,sha256=xnWBNak8oKydkFoxHLlq9wo3lIsB3aMnTDrqbtCfoWo,1512
|
|
5
|
+
easycoder/ec_compiler.py,sha256=_xBh2L47mz_WOMYdruMYSHyyz2wfh7Hf28q559AWVSU,4781
|
|
6
|
+
easycoder/ec_condition.py,sha256=WSbONo4zs2sX1icOVpscZDFSCAEFmTsquoc2RGcLx_k,763
|
|
7
|
+
easycoder/ec_core.py,sha256=5woFkLx9divJomakFyeexmhqJeEPI1hUlzyB3MlAruI,86359
|
|
8
|
+
easycoder/ec_graphics.py,sha256=ylkdEOo04g8aOeY9xB1V9mf3aZWkoIUt-caXivuVje4,16427
|
|
9
|
+
easycoder/ec_handler.py,sha256=IJvxcrJJSR53d6DS_8H5qPHKhp9y5-GV4WXAjhZxu_o,2250
|
|
10
|
+
easycoder/ec_program.py,sha256=sdVPc1c8urLn5icov-NVyNkc78UR930ipDDONlZTge8,9831
|
|
11
|
+
easycoder/ec_renderer.py,sha256=oZ56OyJojQq-2ZPlVMFOrEyG_vj91D4yv9Nd5m92RQA,8031
|
|
12
|
+
easycoder/ec_screenspec.py,sha256=TeXgccfYoE--r7Rf9t9drV1V3fU-p-iBnZwtjHzIh8M,2524
|
|
13
|
+
easycoder/ec_timestamp.py,sha256=_3QFJPzIWZ9Rzk3SQOQJ-gwmvB07pg78k23SPntoZtY,288
|
|
14
|
+
easycoder/ec_value.py,sha256=KkSIxAum-bnb_qnaRgS1UiVUg_-khVPQrrEPiSXjQs4,2382
|
|
15
|
+
easycoder-250105.1.dist-info/entry_points.txt,sha256=JXAZbenl0TnsIft2FcGJbJ-4qoztVu2FuT8PFmWFexM,44
|
|
16
|
+
easycoder-250105.1.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
17
|
+
easycoder-250105.1.dist-info/WHEEL,sha256=ssQ84EZ5gH1pCOujd3iW7HClo_O_aDaClUbX4B8bjKY,100
|
|
18
|
+
easycoder-250105.1.dist-info/METADATA,sha256=hm-vw2r4-nH5eaa8ouO0Qpdx9nyeNFK60iXwS41GDxg,5817
|
|
19
|
+
easycoder-250105.1.dist-info/RECORD,,
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
easycoder/README.md,sha256=PYqOc_SkIGiFbyCNs90y7JqoqWe4aO1xYIW-6bOnFKU,573
|
|
2
|
-
easycoder/__init__.py,sha256=JT7KWbiZJzKjiWqUFbTt4PaWE-WxWQcMEBvq4al6KL0,283
|
|
3
|
-
easycoder/ec.py,sha256=Nj5PRl8GsKjfGJKq0FOM1a7FeK3cN68CoIFg8lswQEg,221
|
|
4
|
-
easycoder/ec_classes.py,sha256=xnWBNak8oKydkFoxHLlq9wo3lIsB3aMnTDrqbtCfoWo,1512
|
|
5
|
-
easycoder/ec_compiler.py,sha256=2r6Nk7px9UMYqIpYc6dAbYOAFu-CoWPy-iqlsed49Lo,4690
|
|
6
|
-
easycoder/ec_condition.py,sha256=WSbONo4zs2sX1icOVpscZDFSCAEFmTsquoc2RGcLx_k,763
|
|
7
|
-
easycoder/ec_core.py,sha256=rglL7s3f1Ewzc0wjUPZxoY8__DJscxjZU7-Pd6OCIZQ,79904
|
|
8
|
-
easycoder/ec_graphics.py,sha256=o70BdQ-Y3uIo5nheQYwJUmM3gYVerKD9_5arQ8JTP-Y,15556
|
|
9
|
-
easycoder/ec_handler.py,sha256=IJvxcrJJSR53d6DS_8H5qPHKhp9y5-GV4WXAjhZxu_o,2250
|
|
10
|
-
easycoder/ec_program.py,sha256=TUKQo56vURtx_UO7avIJZhz0EeOe2lNxQQz8EOOnCZ4,8685
|
|
11
|
-
easycoder/ec_renderer.py,sha256=ejVFemHGuFBwGA__6VfZQZeZMnw4Ilvf_y9I34k04LM,7981
|
|
12
|
-
easycoder/ec_screenspec.py,sha256=TeXgccfYoE--r7Rf9t9drV1V3fU-p-iBnZwtjHzIh8M,2524
|
|
13
|
-
easycoder/ec_timestamp.py,sha256=_3QFJPzIWZ9Rzk3SQOQJ-gwmvB07pg78k23SPntoZtY,288
|
|
14
|
-
easycoder/ec_value.py,sha256=XIBtGhcCgh1abrzAn-Wy4l_xH_3cTWakMVIiSlBAZjM,2386
|
|
15
|
-
easycoder-250103.2.dist-info/entry_points.txt,sha256=JXAZbenl0TnsIft2FcGJbJ-4qoztVu2FuT8PFmWFexM,44
|
|
16
|
-
easycoder-250103.2.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
17
|
-
easycoder-250103.2.dist-info/WHEEL,sha256=ssQ84EZ5gH1pCOujd3iW7HClo_O_aDaClUbX4B8bjKY,100
|
|
18
|
-
easycoder-250103.2.dist-info/METADATA,sha256=UyIqZzD6XVImMRxZloXqyE_bUKKJRGCMztjXaaIPBTc,5178
|
|
19
|
-
easycoder-250103.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|