easycoder 241211.3__py2.py3-none-any.whl → 241215.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
@@ -8,5 +8,6 @@ from .ec_handler import *
8
8
  from .ec_program import *
9
9
  from .ec_timestamp import *
10
10
  from .ec_value import *
11
+ from .ec_graphics import *
11
12
 
12
- __version__ = "241211.3"
13
+ __version__ = "241215.1"
easycoder/ec_compiler.py CHANGED
@@ -101,7 +101,7 @@ class Compiler:
101
101
 
102
102
  def showWarnings(self):
103
103
  for warning in self.warnings:
104
- print(f'Line {self.getLino() + 1}: {warning}')
104
+ print(f'Warning: Line {self.getLino() + 1}: {warning}')
105
105
 
106
106
  def getSymbolRecord(self):
107
107
  token = self.getToken()
easycoder/ec_core.py CHANGED
@@ -38,7 +38,7 @@ class Core(Handler):
38
38
  command['target'] = self.getToken()
39
39
  self.add(command)
40
40
  return True
41
- self.warning(f'core.add: Expected value holder')
41
+ self.warning(f'Core.add: Expected value holder')
42
42
  else:
43
43
  # Here we have 2 values so 'giving' must come next
44
44
  command['value2'] = self.getValue()
@@ -46,7 +46,7 @@ class Core(Handler):
46
46
  command['target'] = self.nextToken()
47
47
  self.add(command)
48
48
  return True
49
- self.warning(f'core.add: Expected "giving"')
49
+ self.warning(f'Core.add: Expected "giving"')
50
50
  return False
51
51
 
52
52
  def r_add(self, command):
@@ -90,7 +90,7 @@ class Core(Handler):
90
90
  command['target'] = symbolRecord['name']
91
91
  self.add(command)
92
92
  return True
93
- self.warning(f'Variable "{symbolRecord["name"]}" does not hold a value')
93
+ self.warning(f'Core.append: Variable "{symbolRecord["name"]}" does not hold a value')
94
94
  return False
95
95
 
96
96
  def r_append(self, command):
@@ -248,7 +248,7 @@ class Core(Handler):
248
248
  command['target'] = self.getToken()
249
249
  self.add(command)
250
250
  return True
251
- self.warning(f'Variable "{symbolRecord["name"]}" does not hold a value')
251
+ self.warning(f'Core.decrement: Variable "{symbolRecord["name"]}" does not hold a value')
252
252
  return False
253
253
 
254
254
  def r_decrement(self, command):
@@ -272,9 +272,9 @@ class Core(Handler):
272
272
  self.add(command)
273
273
  return True
274
274
  else:
275
- self.warning(f'"of" expected; got {self.getToken()}')
275
+ self.warning(f'Core.delete: "of" expected; got {self.getToken()}')
276
276
  else:
277
- self.warning(f'"file" or "property" expected; got {token}')
277
+ self.warning(f'Core.delete: "file" or "property" expected; got {token}')
278
278
  return False
279
279
 
280
280
  def r_delete(self, command):
@@ -570,7 +570,7 @@ class Core(Handler):
570
570
  command['target'] = self.getToken()
571
571
  self.add(command)
572
572
  return True
573
- self.warning(f'Variable "{symbolRecord["name"]}" does not hold a value')
573
+ self.warning(f'Core.increment: Variable "{symbolRecord["name"]}" does not hold a value')
574
574
  return False
575
575
 
576
576
  def r_increment(self, command):
@@ -729,7 +729,7 @@ class Core(Handler):
729
729
  else:
730
730
  FatalError(self.compiler, f'Variable "{self.getToken()}" is not a file')
731
731
  else:
732
- self.warning(f'core.open: Variable "{self.getToken()}" not declared')
732
+ self.warning(f'Core.open: Variable "{self.getToken()}" not declared')
733
733
  return False
734
734
 
735
735
  def r_open(self, command):
@@ -1215,7 +1215,7 @@ class Core(Handler):
1215
1215
  command['target'] = self.getToken()
1216
1216
  self.add(command)
1217
1217
  return True
1218
- self.warning(f'core.take: Expected value holder')
1218
+ self.warning(f'Core.take: Expected value holder')
1219
1219
  else:
1220
1220
  # Here we have 2 values so 'giving' must come next
1221
1221
  command['value2'] = self.getValue()
@@ -1227,7 +1227,7 @@ class Core(Handler):
1227
1227
  else:
1228
1228
  FatalError(self.program.compiler, f'\'{self.getToken()}\' is not a symbol')
1229
1229
  else:
1230
- self.warning(f'core.take: Expected "giving"')
1230
+ self.warning(f'Core.take: Expected "giving"')
1231
1231
  return False
1232
1232
 
1233
1233
  def r_take(self, command):
@@ -1402,6 +1402,8 @@ class Core(Handler):
1402
1402
  RuntimeError(self.program, f'{symbolRecord["name"]} does not hold a value')
1403
1403
  return None
1404
1404
  value = self.getSymbolValue(symbolRecord)
1405
+ if value == None:
1406
+ RuntimeError(self.program, f'{symbolRecord["name"]} has not been initialised')
1405
1407
  if mode == '+':
1406
1408
  value['content'] += 1
1407
1409
  else:
@@ -1467,7 +1469,7 @@ class Core(Handler):
1467
1469
  if symbolRecord['valueHolder']:
1468
1470
  value['target'] = symbolRecord['name']
1469
1471
  return value
1470
- self.warning(f'Token \'{self.getToken()}\' does not hold a value')
1472
+ self.warning(f'Core.compileValue: Token \'{self.getToken()}\' does not hold a value')
1471
1473
  return None
1472
1474
 
1473
1475
  if token == 'property':
@@ -1478,7 +1480,7 @@ class Core(Handler):
1478
1480
  if symbolRecord['valueHolder']:
1479
1481
  value['target'] = symbolRecord['name']
1480
1482
  return value
1481
- self.warning(f'Token \'{self.getToken()}\' does not hold a value')
1483
+ self.warning(f'Core.compileValue: Token \'{self.getToken()}\' does not hold a value')
1482
1484
  return None
1483
1485
 
1484
1486
  if token == 'arg':
@@ -1640,7 +1642,7 @@ class Core(Handler):
1640
1642
  return value
1641
1643
  return None
1642
1644
 
1643
- self.warning(f'Core: Unknown token {token}')
1645
+ self.warning(f'Core.compileValue: Unknown token "{token}"')
1644
1646
  return None
1645
1647
 
1646
1648
  #############################################################################
@@ -2115,7 +2117,7 @@ class Core(Handler):
2115
2117
  condition.type = 'boolean'
2116
2118
  return condition
2117
2119
 
2118
- self.warning(f'I can\'t get a conditional:')
2120
+ self.warning(f'Core.compileCondition: I can\'t get a conditional:')
2119
2121
  return None
2120
2122
 
2121
2123
  def isNegate(self):
@@ -0,0 +1,376 @@
1
+ from .ec_classes import FatalError, RuntimeError
2
+ from .ec_handler import Handler
3
+ from .ec_renderer import *
4
+
5
+ class Graphics(Handler):
6
+
7
+ def __init__(self, compiler):
8
+ Handler.__init__(self, compiler)
9
+
10
+ def getName(self):
11
+ return 'graphics'
12
+
13
+ #############################################################################
14
+ # Keyword handlers
15
+
16
+ def k_attach(self, command):
17
+ if self.nextIsSymbol():
18
+ record = self.getSymbolRecord()
19
+ command['name'] = record['name']
20
+ if self.nextIs('to'):
21
+ value = self.nextValue()
22
+ command['id'] = value
23
+ self.add(command)
24
+ return True
25
+
26
+ def r_attach(self, command):
27
+ target = self.getVariable(command['name'])
28
+ id = self.getRuntimeValue(command['id'])
29
+ element = getElement(id)
30
+ if element == None:
31
+ FatalError(self.program.compiler, f'There is no screen element with id \'{id}\'')
32
+ return -1
33
+ if element['type'] != target['keyword']:
34
+ FatalError(self.program.compiler, f'Mismatched element type ({element['type']} and {target['keyword']})')
35
+ self.putSymbolValue(target, {'type': 'text', 'content': id})
36
+ return self.nextPC()
37
+
38
+ def k_close(self, command):
39
+ if self.nextIs('screen'):
40
+ self.add(command)
41
+ return True
42
+ return False
43
+
44
+ def r_close(self, command):
45
+ closeScreen()
46
+ return self.nextPC()
47
+
48
+ def k_create(self, command):
49
+ if self.nextIs('screen'):
50
+ while True:
51
+ token = self.peek()
52
+ if token == 'at':
53
+ self.nextToken()
54
+ command['left'] = self.nextValue()
55
+ command['top'] = self.nextValue()
56
+ elif token == 'size':
57
+ self.nextToken()
58
+ command['width'] = self.nextValue()
59
+ command['height'] = self.nextValue()
60
+ elif token == 'fill':
61
+ self.nextToken()
62
+ command['fill'] = self.nextValue()
63
+ else:
64
+ break
65
+ self.add(command)
66
+ return True
67
+ return False
68
+
69
+ def r_create(self, command):
70
+ createScreen(command)
71
+ return self.nextPC()
72
+
73
+ def k_ellipse(self, command):
74
+ return self.compileVariable(command)
75
+
76
+ def r_ellipse(self, command):
77
+ return self.nextPC()
78
+
79
+ def k_image(self, command):
80
+ return self.compileVariable(command)
81
+
82
+ def r_image(self, command):
83
+ return self.nextPC()
84
+
85
+ def k_move(self, command):
86
+ if self.nextIsSymbol():
87
+ record = self.getSymbolRecord()
88
+ if record['keyword'] in ['rectangle', 'ellipse', 'text', 'image']:
89
+ command['name'] = record['name']
90
+ if self.nextToken() in ['by', 'to']:
91
+ command['type'] = self.getToken()
92
+ command['x'] = self.nextValue()
93
+ command['y'] = self.nextValue()
94
+ self.add(command)
95
+ return True
96
+ return False
97
+
98
+ def r_move(self, command):
99
+ target = self.getVariable(command['name'])
100
+ id = self.getSymbolValue(target)['content']
101
+ type = command['type']
102
+ x = self.getRuntimeValue(command['x'])
103
+ y = self.getRuntimeValue(command['y'])
104
+ if type == 'by':
105
+ moveElement(id, x, y)
106
+ elif type == 'to':
107
+ moveElementTo(id, x, y)
108
+ return self.nextPC()
109
+
110
+ def k_on(self, command):
111
+ token = self.nextToken()
112
+ command['type'] = token
113
+ if token == 'click':
114
+ command['event'] = token
115
+ if self.peek() == 'in':
116
+ self.nextToken()
117
+ if self.nextIs('screen'):
118
+ command['target'] = None
119
+ elif self.isSymbol():
120
+ target = self.getSymbolRecord()
121
+ command['target'] = target['name']
122
+ else:
123
+ FatalError(self.program.compiler, f'{self.getToken()} is not a screen element')
124
+ return False
125
+ command['goto'] = self.getPC() + 2
126
+ self.add(command)
127
+ self.nextToken()
128
+ pcNext = self.getPC()
129
+ cmd = {}
130
+ cmd['domain'] = 'core'
131
+ cmd['lino'] = command['lino']
132
+ cmd['keyword'] = 'gotoPC'
133
+ cmd['goto'] = 0
134
+ cmd['debug'] = False
135
+ self.addCommand(cmd)
136
+ self.compileOne()
137
+ cmd = {}
138
+ cmd['domain'] = 'core'
139
+ cmd['lino'] = command['lino']
140
+ cmd['keyword'] = 'stop'
141
+ cmd['debug'] = False
142
+ self.addCommand(cmd)
143
+ # Fixup the link
144
+ self.getCommandAt(pcNext)['goto'] = self.getPC()
145
+ return True
146
+ elif token == 'tick':
147
+ command['event'] = token
148
+ command['goto'] = self.getPC() + 2
149
+ self.add(command)
150
+ self.nextToken()
151
+ pcNext = self.getPC()
152
+ cmd = {}
153
+ cmd['domain'] = 'core'
154
+ cmd['lino'] = command['lino']
155
+ cmd['keyword'] = 'gotoPC'
156
+ cmd['goto'] = 0
157
+ cmd['debug'] = False
158
+ self.addCommand(cmd)
159
+ self.compileOne()
160
+ cmd = {}
161
+ cmd['domain'] = 'core'
162
+ cmd['lino'] = command['lino']
163
+ cmd['keyword'] = 'stop'
164
+ cmd['debug'] = False
165
+ self.addCommand(cmd)
166
+ # Fixup the link
167
+ self.getCommandAt(pcNext)['goto'] = self.getPC()
168
+ return True
169
+ return False
170
+
171
+ def r_on(self, command):
172
+ pc = command['goto']
173
+ if command['type'] == 'click':
174
+ event = command['event']
175
+ if event == 'click':
176
+ target = command['target']
177
+ if target == None:
178
+ value = 'screen'
179
+ else:
180
+ widget = self.getVariable(target)
181
+ value = widget['value'][widget['index']]
182
+ setOnClick(value['content'], lambda: self.run(pc))
183
+ elif command['type'] == 'tick':
184
+ setOnTick(lambda: self.run(pc))
185
+ return self.nextPC()
186
+
187
+ def k_rectangle(self, command):
188
+ return self.compileVariable(command)
189
+
190
+ def r_rectangle(self, command):
191
+ return self.nextPC()
192
+
193
+ def k_render(self, command):
194
+ command['value'] = self.nextValue()
195
+ command['parent'] = 'screen'
196
+ if self.peek() == 'in':
197
+ self.nextToken()
198
+ if self.nextIsSymbol():
199
+ record = self.getSymbolRecord()
200
+ type = record['type']
201
+ name = record['name']
202
+ if type in ['rectangle', 'ellipse']:
203
+ command['parent'] = record['name']
204
+ self.add(command)
205
+ return True
206
+ else:
207
+ self.warning(f'Graphics.render: {name} cannot be a parent of another element')
208
+ return False
209
+ self.add(command)
210
+ return True
211
+ FatalError(self.program.compiler, 'Nothing specified to render')
212
+ return False
213
+
214
+ def r_render(self, command):
215
+ parent = command['parent']
216
+ value = self.getRuntimeValue(command['value'])
217
+ render(value, parent)
218
+ return self.nextPC()
219
+
220
+ def k_set(self, command):
221
+ if self.peek() == 'the':
222
+ self.nextToken()
223
+ token = self.peek()
224
+ if token == 'text':
225
+ self.nextToken()
226
+ command['variant'] = 'setText'
227
+ if self.peek() == 'of':
228
+ self.nextToken()
229
+ if self.nextIsSymbol():
230
+ record = self.getSymbolRecord()
231
+ command['name'] = record['name']
232
+ if record['keyword'] != 'text':
233
+ RuntimeError(command['program'], f'Symbol type is not \'text\'')
234
+ if self.peek() == 'to':
235
+ self.nextToken()
236
+ command['value'] = self.nextValue()
237
+ self.add(command)
238
+ return True
239
+ return False
240
+ elif token == 'background':
241
+ self.nextToken()
242
+ command['variant'] = 'setBackground'
243
+ if self.peek() == 'color':
244
+ self.nextToken()
245
+ if self.peek() == 'of':
246
+ self.nextToken()
247
+ if self.nextIsSymbol():
248
+ record = self.getSymbolRecord()
249
+ command['name'] = record['name']
250
+ if not record['keyword'] in ['rectangle', 'ellipse']:
251
+ RuntimeError(command['program'], f'Symbol type is not \'rectangle\' or \'ellipse\'')
252
+ if self.peek() == 'to':
253
+ self.nextToken()
254
+ command['value'] = self.nextValue()
255
+ self.add(command)
256
+ return True
257
+ return False
258
+ return False
259
+
260
+ def r_set(self, command):
261
+ variant = command['variant']
262
+ if variant == 'setText':
263
+ variable = self.getVariable(command['name'])
264
+ element = self.getSymbolValue(variable)
265
+ value = self.getRuntimeValue(command['value'])
266
+ setText(element['content'], value)
267
+ elif variant == 'setBackground':
268
+ variable = self.getVariable(command['name'])
269
+ element = self.getSymbolValue(variable)
270
+ value = self.getRuntimeValue(command['value'])
271
+ setBackground(element['content'], value)
272
+ return self.nextPC()
273
+
274
+ def k_show(self, command):
275
+ if self.nextIs('screen'):
276
+ command['name'] = None
277
+ self.add(command)
278
+ return True
279
+ return False
280
+
281
+ def r_show(self, command):
282
+ showScreen()
283
+ return self.nextPC()
284
+
285
+ def k_spec(self, command):
286
+ return self.compileVariable(command, True)
287
+
288
+ def r_spec(self, command):
289
+ return self.nextPC()
290
+
291
+ def k_text(self, command):
292
+ return self.compileVariable(command)
293
+
294
+ def r_text(self, command):
295
+ return self.nextPC()
296
+
297
+ #############################################################################
298
+ # Compile a value in this domain
299
+ def compileValue(self):
300
+ value = {}
301
+ value['domain'] = 'graphics'
302
+ token = self.getToken()
303
+ if self.isSymbol():
304
+ value['name'] = token
305
+ symbolRecord = self.getSymbolRecord()
306
+ keyword = symbolRecord['keyword']
307
+ if keyword == 'module':
308
+ value['type'] = 'module'
309
+ return value
310
+
311
+ if symbolRecord['valueHolder'] == True or keyword == 'dictionary':
312
+ value['type'] = 'symbol'
313
+ return value
314
+ return None
315
+
316
+ if self.tokenIs('the'):
317
+ self.nextToken()
318
+ token = self.getToken()
319
+
320
+ value['type'] = token
321
+
322
+ if token == 'color':
323
+ name = self.nextToken()
324
+ value = {}
325
+ value['type'] = 'string'
326
+ value['content'] = name
327
+ return value
328
+
329
+ elif token == 'attribute':
330
+ value['attribute'] = self.nextValue()
331
+ if (self.nextIs('of')):
332
+ if (self.nextIsSymbol()):
333
+ value['name'] = self.getToken()
334
+ return value
335
+ return None
336
+
337
+ #############################################################################
338
+ # Modify a value or leave it unchanged.
339
+ def modifyValue(self, value):
340
+ return value
341
+
342
+ #############################################################################
343
+ # Value handlers
344
+
345
+ def v_symbol(self, symbolRecord):
346
+ result = {}
347
+ if symbolRecord['valueHolder']:
348
+ symbolValue = self.getSymbolValue(symbolRecord)
349
+ if symbolValue == None:
350
+ return None
351
+ result['type'] = symbolValue['type']
352
+ content = symbolValue['content']
353
+ if content == None:
354
+ return ''
355
+ result['content'] = content
356
+ return result
357
+ else:
358
+ return ''
359
+
360
+ def v_attribute(self, v):
361
+ target = self.getVariable(v['name'])
362
+ attribute = self.getRuntimeValue(v['attribute'])
363
+ name = target['value'][target['index']]['content']
364
+ value = {}
365
+ value['type'] = 'int'
366
+ value['content'] = getAttribute(name, attribute)
367
+ return value
368
+
369
+ #############################################################################
370
+ # Compile a condition
371
+ def compileCondition(self):
372
+ condition = {}
373
+ return condition
374
+
375
+ #############################################################################
376
+ # Condition handlers
easycoder/ec_program.py CHANGED
@@ -12,18 +12,22 @@ class Program:
12
12
  def __init__(self, argv):
13
13
  print(f'EasyCoder version {version("easycoder")}')
14
14
  scriptName = None
15
- if len(argv)>0:
16
- scriptName = argv[0]
17
- else:
15
+ if len(argv) == 0:
18
16
  print('No script supplied')
19
- exit();
17
+ exit()
18
+ self.classes=[Core]
19
+ for n in range(len(argv)):
20
+ arg = argv[n]
21
+ if arg == '-g':
22
+ from .ec_graphics import Graphics
23
+ self.classes.append(Graphics)
24
+ else:
25
+ scriptName = arg
20
26
 
21
- # print('Domains:',domains)
22
27
  f = open(scriptName, 'r')
23
28
  source = f.read()
24
29
  f.close()
25
30
  self.argv = argv
26
- self.classes=[Core]
27
31
  self.domains = []
28
32
  self.domainIndex = {}
29
33
  self.name = '<anon>'
@@ -40,6 +44,7 @@ class Program:
40
44
  self.processClasses()
41
45
  self.queue = deque()
42
46
 
47
+ def start(self):
43
48
  startCompile = time.time()
44
49
  self.tokenise(self.script)
45
50
  if self.compiler.compileFrom(0, []):
@@ -62,8 +67,11 @@ class Program:
62
67
  # Import a plugin
63
68
  def importPlugin(self, source):
64
69
  args=source.split(':')
70
+ if len(args)<2:
71
+ RuntimeError(None, f'Invalid plugin spec "{source}"')
65
72
  idx=args[0].rfind('/')
66
73
  if idx<0:
74
+ sys.path.append('.')
67
75
  module=args[0]
68
76
  else:
69
77
  sys.path.append(args[0][0:idx])
@@ -318,6 +326,6 @@ class Program:
318
326
  # This is the program launcher
319
327
  def Main():
320
328
  if (len(sys.argv) > 1):
321
- Program(sys.argv[1:])
329
+ Program(sys.argv[1:]).start()
322
330
  else:
323
331
  print('Syntax: easycoder <scriptname> [plugins]')
@@ -0,0 +1,330 @@
1
+ # renderer.py
2
+
3
+ import sys, json
4
+ import tkinter as tk
5
+ from PIL import *
6
+
7
+ elements = {}
8
+ zlist = []
9
+ images = {}
10
+ onTick = None
11
+
12
+ # Set the canvas
13
+ def setCanvas(c):
14
+ global canvas
15
+ canvas = c
16
+
17
+ # Get the canvas
18
+ def getCanvas():
19
+ global canvas
20
+ return canvas
21
+
22
+ def createScreen(values):
23
+ global screen, canvas, screenLeft, screenTop, running
24
+ running = True
25
+ screen = tk.Tk()
26
+ screen.title('EasyCoder')
27
+ # screen.attributes('-fullscreen', True)
28
+
29
+ # screen.overrideredirect(True)
30
+ width = values['width']['content'] if 'width' in values else 600
31
+ height = values['height']['content'] if 'height' in values else 800
32
+ screenLeft = int((screen.winfo_screenwidth() - width) / 2)
33
+ screenTop = int((screen.winfo_screenheight() - height) / 2)
34
+ if 'left' in values:
35
+ screenLeft = values['left']['content']
36
+ if 'top' in values:
37
+ screenTop = values['top']['content']
38
+
39
+ geometry = str(width) + 'x' + str(height) + '+' + str(screenLeft) + '+' + str(screenTop)
40
+ screen.geometry(geometry)
41
+
42
+ # Handle a click in the screen
43
+ def onClick(event):
44
+ global screenLeft, screenTop, zlist
45
+ x = event.x
46
+ y = event.y
47
+ # print('Clicked at : '+ str(x) +","+ str(y))
48
+ for i in range(1, len(zlist) + 1):
49
+ element = zlist[-i]
50
+ id = list(element)[0]
51
+ values = element[id]
52
+ x1 = values['left']
53
+ x2 = x1 + values['width']
54
+ y1 = values['top']
55
+ y2 = y1 + values['height']
56
+ if x >= x1 and x < x2 and y >= y1 and y < y2:
57
+ if id in elements:
58
+ element = elements[id]
59
+ if 'cb' in element:
60
+ element['cb']()
61
+ break
62
+ else:
63
+ RuntimeError(None, f'Element \'{id}\' does not exist')
64
+
65
+ screen.bind('<Button-1>', onClick)
66
+
67
+ fill = values['fill']['content'] if 'fill' in values else 'white'
68
+ canvas = tk.Canvas(master=screen, width=width, height=height, bg=fill)
69
+ canvas.place(x=0, y=0)
70
+ setCanvas(canvas)
71
+
72
+ # Close the screen
73
+ def closeScreen():
74
+ global screen
75
+ screen.destroy()
76
+
77
+ # Set up a click handler in an element
78
+ def setOnClick(id, cb):
79
+ global elements
80
+ if id in elements:
81
+ elements[id]['cb'] = cb
82
+ else:
83
+ RuntimeError(None, f'Element \'{id}\' does not exist')
84
+ return
85
+
86
+ # Set up the tick handler
87
+ def setOnTick(cb):
88
+ global onTick
89
+ onTick = cb
90
+
91
+ # Show the screen and issue tick callbacks
92
+ def showScreen():
93
+ global screen, onTick
94
+ def afterCB(screen):
95
+ if onTick != None:
96
+ onTick()
97
+ screen.after(100, lambda: afterCB(screen))
98
+ screen.after(1000, lambda: afterCB(screen))
99
+ screen.mainloop()
100
+ sys.exit()
101
+
102
+ # Render a graphic specification
103
+ def render(spec, parent):
104
+ global elements
105
+
106
+ def getValue(args, item):
107
+ if item in args:
108
+ if type(item) == int:
109
+ return item
110
+ return args[item]
111
+ return item
112
+
113
+ def renderIntoRectangle(widgetType, values, offset, args):
114
+ global zlist
115
+ left = getValue(args, values['left']) if 'left' in values else 10
116
+ top = getValue(args, values['top']) if 'top' in values else 10
117
+ left = offset['dx'] + left
118
+ top = offset['dy'] + top
119
+ width = getValue(args, values['width']) if 'width' in values else 100
120
+ height = getValue(args, values['height']) if 'height' in values else 100
121
+ right = left + width
122
+ bottom = top + height
123
+ fill = values['fill'] if 'fill' in values else None
124
+ outline = values['outline'] if 'outline' in values else None
125
+ if outline != None:
126
+ outlineWidth = getValue(args, values['outlineWidth']) if 'outlineWidth' in values else 1
127
+ else:
128
+ outlineWidth = 0
129
+ if widgetType == 'rectangle':
130
+ widgetId = getCanvas().create_rectangle(left, top, right, bottom, fill=fill, outline=outline, width=outlineWidth)
131
+ elif widgetType == 'ellipse':
132
+ widgetId = getCanvas().create_oval(left, top, right, bottom, fill=fill, outline=outline, width=outlineWidth)
133
+ else:
134
+ return f'Unknown widget type \'{widgetType}\''
135
+ if 'name' in values:
136
+ widgetName = getValue(args, values['name'])
137
+ else:
138
+ widgetName = None
139
+ widgetSpec = {
140
+ "type": widgetType,
141
+ "name": widgetName,
142
+ "id": widgetId,
143
+ "left": left,
144
+ "top": top,
145
+ "width": width,
146
+ "height": height,
147
+ "children": []
148
+ }
149
+ elements[widgetName] = widgetSpec
150
+ zlist.append({widgetName: widgetSpec})
151
+ if '#' in values:
152
+ children = values['#']
153
+ if type(children) == list:
154
+ for item in children:
155
+ if item in values:
156
+ child = values[item]
157
+ childSpec = renderWidget(child, {'dx': left, 'dy': top}, args)
158
+ widgetSpec['children'].append(childSpec['name'])
159
+ else:
160
+ child = values[children]
161
+ childSpec = renderWidget(child, {'dx': left, 'dy': top}, args)
162
+ widgetSpec['children'].append(childSpec['name'])
163
+ return widgetSpec
164
+
165
+ def renderText(values, offset, args):
166
+ left = getValue(args, values['left']) if 'left' in values else 10
167
+ top = getValue(args, values['top']) if 'top' in values else 10
168
+ left = offset['dx'] + left
169
+ top = offset['dy'] + top
170
+ width = getValue(args, values['width']) if 'width' in values else 100
171
+ height = getValue(args, values['height']) if 'height' in values else 100
172
+ right = left + width
173
+ bottom = top + height
174
+ shape = getValue(args, values['shape']) if 'shape' in values else 'rectangle'
175
+ fill = getValue(args, values['fill']) if 'fill' in values else None
176
+ outline = getValue(args, values['outline']) if 'outline' in values else None
177
+ outlineWidth = getValue(args, values['outlineWidth']) if 'outlineWidth' in values else 0 if outline == None else 1
178
+ color = getValue(args, values['color']) if 'color' in values else None
179
+ text = getValue(args, values['text']) if 'text' in values else ''
180
+ fontFace = getValue(args, values['fontFace']) if 'fontFace' in values else 'Helvetica'
181
+ fontWeight = getValue(args, values['fontWeight']) if 'fontWeight' in values else 'normal'
182
+ fontSize = round(height*2/5) if shape == 'ellipse' else round(height*3/5)
183
+ fontTop = top + height/2
184
+ if 'fontSize' in values:
185
+ fontSize = getValue(args, values['fontSize'])
186
+ fontTop = top + round(fontSize * 5 / 4)
187
+ adjust = round(fontSize/5) if shape == 'ellipse' else 0
188
+ align = getValue(args, values['align']) if 'align' in values else 'center'
189
+ if align == 'left':
190
+ xoff = round(fontSize/5)
191
+ anchor = 'w'
192
+ elif align == 'right':
193
+ xoff = width - round(fontSize/5)
194
+ anchor = 'e'
195
+ else:
196
+ xoff = width/2
197
+ anchor = 'center'
198
+ if xoff < 3:
199
+ xoff = 3
200
+ if shape == 'ellipse':
201
+ containerId = getCanvas().create_oval(left, top, right, bottom, fill=fill, outline=outline, width=outlineWidth)
202
+ else:
203
+ containerId = getCanvas().create_rectangle(left, top, right, bottom, fill=fill, outline=outline, width=outlineWidth)
204
+ textId = canvas.create_text(left + xoff, fontTop + adjust, fill=color, font=f'"{fontFace}" {fontSize} {fontWeight}', text=text, anchor=anchor)
205
+ if 'name' in values:
206
+ widgetName = getValue(args, values['name'])
207
+ else:
208
+ widgetName = None
209
+ widgetSpec = {
210
+ "type": "text",
211
+ "name": widgetName,
212
+ "id": textId,
213
+ "containerId": containerId,
214
+ "left": left,
215
+ "top": top,
216
+ "width": width,
217
+ "height": height
218
+ }
219
+ elements[widgetName] = widgetSpec
220
+ zlist.append({widgetName: widgetSpec})
221
+ return widgetSpec
222
+
223
+ def renderImage(values, offset, args):
224
+ global images
225
+ left = getValue(args, values['left']) if 'left' in values else 10
226
+ top = getValue(args, values['top']) if 'top' in values else 10
227
+ left = offset['dx'] + left
228
+ top = offset['dy'] + top
229
+ width = getValue(args, values['width']) if 'width' in values else 100
230
+ height = getValue(args, values['height']) if 'height' in values else 100
231
+ right = left + width
232
+ bottom = top + height
233
+ src = getValue(args, values['src']) if 'src' in values else None
234
+ containerId = getCanvas().create_rectangle(left, top, right, bottom, width=0)
235
+ if 'name' in values:
236
+ widgetName = values['name']
237
+ else:
238
+ widgetName = None
239
+ widgetSpec = {
240
+ "type": "image",
241
+ "nme": widgetName,
242
+ "id": containerId,
243
+ "left": left,
244
+ "top": top,
245
+ "width": width,
246
+ "height": height
247
+ }
248
+ elements[widgetName] = widgetSpec
249
+ zlist.append({widgetName: widgetSpec})
250
+ if src == None:
251
+ raise(Exception(f'No image source given for \'{id}\''))
252
+ img = (Image.open(src))
253
+ resized_image= img.resize((width, height), Image.ANTIALIAS)
254
+ new_image= ImageTk.PhotoImage(resized_image)
255
+ imageid = getCanvas().create_image(left, top, anchor='nw', image=new_image)
256
+ images[containerId] = {'id': imageid, "image": new_image}
257
+ return widgetSpec
258
+
259
+ # Create a canvas or render a widget
260
+ def renderWidget(widget, offset, args):
261
+ widgetType = widget['type']
262
+ if widgetType in ['rectangle', 'ellipse']:
263
+ return renderIntoRectangle(widgetType, widget, offset, args)
264
+ elif widgetType == 'text':
265
+ return renderText(widget, offset, args)
266
+ elif widgetType == 'image':
267
+ return renderImage(widget, offset, args)
268
+
269
+ # Render a complete specification
270
+ def renderSpec(spec, offset, args):
271
+ widgets = spec['#']
272
+ # If a list, iterate it
273
+ if type(widgets) is list:
274
+ for widget in widgets:
275
+ renderWidget(spec[widget], offset, args)
276
+ # Otherwise, process the single widget
277
+ else:
278
+ renderWidget(spec[widgets], offset, args)
279
+
280
+ # Main entry point
281
+ offset = {'dx': 0, 'dy': 0}
282
+ if parent != screen:
283
+ RuntimeError(None, 'Can\'t yet render into parent widget')
284
+
285
+ # If it'a string, process it
286
+ if type(spec) is str:
287
+ renderSpec(json.loads(spec), offset, {})
288
+
289
+ # If it's a 'dict', extract the spec and the args
290
+ if type(spec) is dict:
291
+ args = spec['args']
292
+ spec = json.loads(spec['spec'])
293
+ renderSpec(spec, offset, args)
294
+
295
+ # Get the widget whose name is given
296
+ def getElement(name):
297
+ global elements
298
+ if name in elements:
299
+ return elements[name]
300
+ else:
301
+ RuntimeError(None, f'Element \'{name}\' does not exist')
302
+
303
+ # Set the content of a text widget
304
+ def setText(name, value):
305
+ getCanvas().itemconfig(getElement(name)['id'], text=value)
306
+
307
+ # Set the background of a rectangle or ellipse widget
308
+ def setBackground(name, value):
309
+ id = getElement(name)['id']
310
+ getCanvas().itemconfig(getElement(name)['id'], fill=value)
311
+
312
+ # Move an element by an amount
313
+ def moveElement(name, dx, dy):
314
+ element = getElement(name)
315
+ getCanvas().move(element['id'], dx, dy)
316
+ element['left'] += dx
317
+ element['top'] += dy
318
+ for childName in element['children']:
319
+ element = getElement(childName)
320
+ getCanvas().move(element['id'], dx, dy)
321
+
322
+ # Move an element to a new location
323
+ def moveElementTo(name, left, top):
324
+ element = getElement(name)
325
+ moveElement(name, left - element['left'], top - element['top'])
326
+
327
+ # Get an attribute of an element
328
+ def getAttribute(name, attribute):
329
+ element = getElement(name)
330
+ return element[attribute]
@@ -1,19 +1,20 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: easycoder
3
- Version: 241211.3
4
- Summary: EasyCoder for Python
3
+ Version: 241215.1
4
+ Summary: Rapid scripting in English
5
+ Keywords: compiler,scripting,prototyping,programming,coding,python,low code,hypertalk,computer language,learn to code
5
6
  Author-email: Graham Trott <gtanyware@gmail.com>
6
7
  Description-Content-Type: text/markdown
7
8
  Classifier: License :: OSI Approved :: MIT License
8
9
  Requires-Dist: pytz
9
- Project-URL: Home, https://github.com/easycoder
10
+ Project-URL: Home, https://github.com/easycoder/easycoder-py
10
11
 
11
12
  # Introduction
12
13
  This is the Python version of **_EasyCoder_**, a high-level English-like scripting language suited for prototyping and rapid testing of ideas. It operates on the command line.
13
14
 
14
15
  The JavaScript version of **_EasyCoder_**, which provides a full set of graphical features to run in a browser, is at
15
16
 
16
- Repository: [https://github.com/easycoder/easycoder.github.io](https://github.com/easycoder/easycoder.github.io)
17
+ Repository: [https://github.com/easycoder/easycoder.github.io](https://github.com/easycoder/easycoder.github.io)
17
18
  Website: [https://easycoder.github.io](https://easycoder.github.io)
18
19
 
19
20
  ## Quick Start
@@ -58,6 +59,11 @@ Here in the repository is a folder called `scripts` containing some sample scrip
58
59
  `tests.ecs` is a test program containing many of the EasyCoder features
59
60
  `fizzbuzz.ecs` is a simple programming challenge often given at job interviews
60
61
 
62
+ ## Graphical programmming
63
+ **_EasyCoder_** is currently being extended to include a graphical programming environment. A single demo script `graphics-demo.ecs` is included in the `scripts` directory. To run it, first install the Python graphics library if it's not already present on your system. On Linux this is done with `sudo apt install python3-tk`. On Windows it's `pip install tk`. Then give the command `easycoder -g scripts/graphics-demo.ecs`.
64
+
65
+ As development progresses this demo script will be extended to include new features as they are added. **_EasyCoder_** graphics are handled by a library module, `ec_renderer` that can be used outside of the **EasyCoder_** environment, in other Python programs.
66
+
61
67
  ## EasyCoder programming reference
62
68
 
63
69
  The language comprises a general-purpose core package, which can be enhanced by plugins to provide special features on demand.
@@ -0,0 +1,16 @@
1
+ easycoder/__init__.py,sha256=aW_U1vTxPggcn2FkBFYhEwPI9qsf4KZPbV2hNZk24B4,289
2
+ easycoder/ec_classes.py,sha256=mRvIk2d-yc20u54oX8fauKeYy-z_g30Otbl-dn5O6T4,1486
3
+ easycoder/ec_compiler.py,sha256=s7M3Jl6z0X_tp-QW4p3v7ac2fhL_bf0uw1GHebqt1Ug,4638
4
+ easycoder/ec_condition.py,sha256=WSbONo4zs2sX1icOVpscZDFSCAEFmTsquoc2RGcLx_k,763
5
+ easycoder/ec_core.py,sha256=NfXNTyxmsU4_f3JTTFMUzgsfCRL9se8rdVfwqBC_1wM,76452
6
+ easycoder/ec_graphics.py,sha256=qHkZkpT5WM0Y-vHXXLoiqGGS_1qWiajKBYqS1MRSicA,12786
7
+ easycoder/ec_handler.py,sha256=WDDIz0awD3vSQZ149rgbUWsClt6zXqED8ByXQJ5p1Ds,2200
8
+ easycoder/ec_program.py,sha256=MHPHEYHoU5zUVsnkL30kyUxtFph-8naVlDGN2hHsKpw,8103
9
+ easycoder/ec_renderer.py,sha256=SiKmUg5SWjlquscZt7UwD6EFlYLZWORmzMl5FrNz7u0,12082
10
+ easycoder/ec_timestamp.py,sha256=_3QFJPzIWZ9Rzk3SQOQJ-gwmvB07pg78k23SPntoZtY,288
11
+ easycoder/ec_value.py,sha256=aOvSF6bM1iKpcLLm0zyGerxHixBkyjE3iYl1Bx6jmzU,2381
12
+ easycoder-241215.1.dist-info/entry_points.txt,sha256=JXAZbenl0TnsIft2FcGJbJ-4qoztVu2FuT8PFmWFexM,44
13
+ easycoder-241215.1.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
14
+ easycoder-241215.1.dist-info/WHEEL,sha256=ssQ84EZ5gH1pCOujd3iW7HClo_O_aDaClUbX4B8bjKY,100
15
+ easycoder-241215.1.dist-info/METADATA,sha256=J5XUm0EhpR-bLU1FizskpkVt1F4ZyHUHHmeuug71iAQ,4096
16
+ easycoder-241215.1.dist-info/RECORD,,
@@ -1,14 +0,0 @@
1
- easycoder/__init__.py,sha256=vys8r-4skG-XdH6b0L8eS4WpTpCkVdV_-RaKL1snTMA,262
2
- easycoder/ec_classes.py,sha256=mRvIk2d-yc20u54oX8fauKeYy-z_g30Otbl-dn5O6T4,1486
3
- easycoder/ec_compiler.py,sha256=1z5U92uzUdCOO5-k0VXLnU581iAhwzQga04kfdTlpMY,4629
4
- easycoder/ec_condition.py,sha256=WSbONo4zs2sX1icOVpscZDFSCAEFmTsquoc2RGcLx_k,763
5
- easycoder/ec_core.py,sha256=j4YPwERad7iKSaUpbcFG6d5dlyLETK3M-np132G6d9s,76188
6
- easycoder/ec_handler.py,sha256=WDDIz0awD3vSQZ149rgbUWsClt6zXqED8ByXQJ5p1Ds,2200
7
- easycoder/ec_program.py,sha256=U5QRxcyYycTugyu1ew8prk6NUEqnvcGuFJSY9stN8vY,7870
8
- easycoder/ec_timestamp.py,sha256=_3QFJPzIWZ9Rzk3SQOQJ-gwmvB07pg78k23SPntoZtY,288
9
- easycoder/ec_value.py,sha256=aOvSF6bM1iKpcLLm0zyGerxHixBkyjE3iYl1Bx6jmzU,2381
10
- easycoder-241211.3.dist-info/entry_points.txt,sha256=JXAZbenl0TnsIft2FcGJbJ-4qoztVu2FuT8PFmWFexM,44
11
- easycoder-241211.3.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
12
- easycoder-241211.3.dist-info/WHEEL,sha256=ssQ84EZ5gH1pCOujd3iW7HClo_O_aDaClUbX4B8bjKY,100
13
- easycoder-241211.3.dist-info/METADATA,sha256=kk6Hmmny8KtnC3gQhlpZEHYHV5zAU-8dGZc_KPKsRbM,3242
14
- easycoder-241211.3.dist-info/RECORD,,