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 CHANGED
@@ -10,4 +10,4 @@ from .ec_program import *
10
10
  from .ec_timestamp import *
11
11
  from .ec_value import *
12
12
 
13
- __version__ = "250103.2"
13
+ __version__ = "250105.1"
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
- sys.exit()
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
- clazz = self.nextToken()
560
- if self.nextIs('from'):
561
- source = self.nextToken()
562
- self.program.importPlugin(f'{source}:{clazz}')
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
- return False
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 variable from a file
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
- if self.nextIsSymbol():
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'No such variable: "{self.getToken()}"')
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 = self.program.flush
177
- self.windowSpec.finish = self.program.finish
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 in ['ellipse', 'rectangle']:
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 in ['ellipse', 'rectangle', 'text', 'image']:
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'] in ['ellipse', 'rectangle', 'text', 'image']:
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'] in ['ellipse', 'rectangle']:
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[0]
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.quit = False
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
- return
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.compiler.program, f'Unknown symbol \'{name}\'')
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.compiler.program, f'Variable "{name}" has no value')
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, encoding='UTF-8'):
196
- return value.encode(encoding)
218
+ def encode(self, value):
219
+ return value
197
220
 
198
- def decode(self, value, encoding='UTF-8'):
199
- return value.decode(encoding)
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 finish(self):
260
- self.quit = True
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
- while len(self.queue):
265
- self.pc = self.queue.popleft()
266
- while True:
267
- if self.quit:
268
- return
269
- command = self.code[self.pc]
270
- domainName = command['domain']
271
- if domainName == None:
272
- self.pc += 1
273
- else:
274
- keyword = command['keyword']
275
- if self.debugStep and command['debug']:
276
- lino = command['lino'] + 1
277
- line = self.script.lines[command['lino']].strip()
278
- print(f'{self.name}: Line {lino}: {domainName}:{keyword}: {line}')
279
- domain = self.domainIndex[domainName]
280
- handler = domain.runHandler(keyword)
281
- if handler:
282
- command = self.code[self.pc]
283
- command['program'] = self
284
- self.pc = handler(command)
285
- try:
286
- if self.pc == 0 or self.pc >= len(self.code):
287
- break
288
- except:
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
- def setExternalControl(self):
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
- length = len(self.queue)
297
- self.queue.append(pc)
298
- if not self.externalControl:
299
- if length == 0:
300
- return self.flush()
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
@@ -51,7 +51,7 @@ class Value:
51
51
  if item != None:
52
52
  return item
53
53
  self.compiler.rewindTo(mark)
54
- FatalError(self.compiler, f'I don\'t understand \'{token}\'')
54
+ self.compiler.warning(f'I don\'t understand \'{token}\'')
55
55
  return None
56
56
 
57
57
  def compileValue(self):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: easycoder
3
- Version: 250103.2
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 its runtime acts as a fairly thin wrapper around Python functions, giving good performance for general applications.
14
-
15
- The JavaScript version of **_EasyCoder_**, which provides a full set of graphical features to run in a browser, is at
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,,