easycoder 251215.2__py2.py3-none-any.whl → 260111.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.
Files changed (48) hide show
  1. easycoder/__init__.py +4 -3
  2. easycoder/debugger/ec_dbg_value_display copy.py +1 -1
  3. easycoder/debugger/ec_dbg_value_display.py +12 -11
  4. easycoder/debugger/ec_dbg_watchlist.py +146 -12
  5. easycoder/debugger/ec_debug.py +85 -8
  6. easycoder/ec_classes.py +228 -25
  7. easycoder/ec_compiler.py +29 -8
  8. easycoder/ec_core.py +364 -242
  9. easycoder/ec_gclasses.py +20 -9
  10. easycoder/ec_graphics.py +42 -26
  11. easycoder/ec_handler.py +4 -3
  12. easycoder/ec_mqtt.py +248 -0
  13. easycoder/ec_program.py +63 -28
  14. easycoder/ec_psutil.py +1 -1
  15. easycoder/ec_value.py +57 -36
  16. easycoder/pre/README.md +3 -0
  17. easycoder/pre/__init__.py +17 -0
  18. easycoder/pre/debugger/__init__.py +5 -0
  19. easycoder/pre/debugger/ec_dbg_value_display copy.py +195 -0
  20. easycoder/pre/debugger/ec_dbg_value_display.py +24 -0
  21. easycoder/pre/debugger/ec_dbg_watch_list copy.py +219 -0
  22. easycoder/pre/debugger/ec_dbg_watchlist.py +293 -0
  23. easycoder/pre/debugger/ec_debug.py +1014 -0
  24. easycoder/pre/ec_border.py +67 -0
  25. easycoder/pre/ec_classes.py +470 -0
  26. easycoder/pre/ec_compiler.py +291 -0
  27. easycoder/pre/ec_condition.py +27 -0
  28. easycoder/pre/ec_core.py +2772 -0
  29. easycoder/pre/ec_gclasses.py +230 -0
  30. easycoder/pre/ec_graphics.py +1682 -0
  31. easycoder/pre/ec_handler.py +79 -0
  32. easycoder/pre/ec_keyboard.py +439 -0
  33. easycoder/pre/ec_program.py +557 -0
  34. easycoder/pre/ec_psutil.py +48 -0
  35. easycoder/pre/ec_timestamp.py +11 -0
  36. easycoder/pre/ec_value.py +124 -0
  37. easycoder/pre/icons/close.png +0 -0
  38. easycoder/pre/icons/exit.png +0 -0
  39. easycoder/pre/icons/run.png +0 -0
  40. easycoder/pre/icons/step.png +0 -0
  41. easycoder/pre/icons/stop.png +0 -0
  42. easycoder/pre/icons/tick.png +0 -0
  43. {easycoder-251215.2.dist-info → easycoder-260111.1.dist-info}/METADATA +1 -1
  44. easycoder-260111.1.dist-info/RECORD +59 -0
  45. easycoder-251215.2.dist-info/RECORD +0 -31
  46. {easycoder-251215.2.dist-info → easycoder-260111.1.dist-info}/WHEEL +0 -0
  47. {easycoder-251215.2.dist-info → easycoder-260111.1.dist-info}/entry_points.txt +0 -0
  48. {easycoder-251215.2.dist-info → easycoder-260111.1.dist-info}/licenses/LICENSE +0 -0
easycoder/ec_classes.py CHANGED
@@ -1,5 +1,37 @@
1
- import sys, paramiko
2
- from typing import Optional, Any
1
+ import sys, paramiko, json
2
+ from typing import Optional, Any, Union
3
+
4
+ ###############################################################################
5
+ # Type normalization: support both Python types and string type names
6
+ def normalize_type(t: Union[type, str, None]) -> Optional[str]:
7
+ """Convert a type to its string representation. Supports both Python types and string names."""
8
+ if t is None:
9
+ return None
10
+ if isinstance(t, str):
11
+ return t
12
+ # Map Python types to string names
13
+ type_map = {
14
+ str: 'str',
15
+ int: 'int',
16
+ float: 'float',
17
+ bool: 'bool',
18
+ dict: 'dict',
19
+ list: 'list',
20
+ object: 'object',
21
+ }
22
+ return type_map.get(t, str(t) if t else None)
23
+
24
+ def types_equal(t1: Union[type, str, None], t2: Union[type, str, None]) -> bool:
25
+ """Compare two types, normalizing both to strings first."""
26
+ return normalize_type(t1) == normalize_type(t2)
27
+
28
+ def type_in(t: Union[type, str, None], types: tuple) -> bool:
29
+ """Check if a type is in a tuple of types, normalizing both."""
30
+ normalized = normalize_type(t)
31
+ for typ in types:
32
+ if normalize_type(typ) == normalized:
33
+ return True
34
+ return False
3
35
 
4
36
  class FatalError(BaseException):
5
37
  def __init__(self, compiler, message):
@@ -64,8 +96,8 @@ class Token:
64
96
  ###############################################################################
65
97
  # A multipurpose value object. Holds a single value, with domain and type information
66
98
  class ECValue():
67
- def __init__(self, domain: Optional[str] = None, type: Optional[str] = None,
68
- content: Any = None, name: Optional[str] = None):
99
+ def __init__(self, domain = 'core', type = None, content: Any = None, name = None):
100
+ if type == None: type = str
69
101
  object.__setattr__(self, 'domain', domain)
70
102
  object.__setattr__(self, 'type', type)
71
103
  object.__setattr__(self, 'content', content)
@@ -95,10 +127,10 @@ class ECValue():
95
127
  return self.domain
96
128
 
97
129
  def setType(self, type):
98
- self.type = type
130
+ self.type = normalize_type(type)
99
131
 
100
132
  def getType(self):
101
- return self.type
133
+ return normalize_type(self.type)
102
134
 
103
135
  def setContent(self, content):
104
136
  self.content = content
@@ -137,6 +169,7 @@ class ECObject():
137
169
  self.index: Optional[int] = None
138
170
  self.values: Optional[list] = None
139
171
  self.name: Optional[str] = None
172
+ self.properties = {}
140
173
 
141
174
  # Set the index for the variable
142
175
  def setIndex(self, index: int) -> None:
@@ -208,7 +241,7 @@ class ECObject():
208
241
  if not self.hasRuntimeValue(): return None
209
242
  v = self.getValue()
210
243
  if v is None: return None
211
- return v.getContent()
244
+ return v
212
245
 
213
246
  # Get the type of the value at the current index
214
247
  def getType(self):
@@ -229,16 +262,23 @@ class ECObject():
229
262
  def getName(self):
230
263
  return self.name
231
264
 
232
- # Check if the object can have properties
233
- def hasProperties(self):
234
- return False
265
+ # Set a specific property on the object
266
+ def setProperty(self, name, value):
267
+ self.properties[name] = value
268
+
269
+ # Check if the object has a specific property
270
+ def hasProperty(self, name):
271
+ return name in self.properties
272
+
273
+ # Get a specific property
274
+ def getProperty(self, name):
275
+ return self.properties[name]
235
276
 
236
277
  ###############################################################################
237
278
  # A generic variable object that can hold a mutable value
238
- class ECVariable(ECObject):
279
+ class ECValueHolder(ECObject):
239
280
  def __init__(self):
240
281
  super().__init__()
241
- self.properties = {}
242
282
 
243
283
  # Set the content of the value at the current index
244
284
  def setContent(self, content):
@@ -271,23 +311,180 @@ class ECVariable(ECObject):
271
311
 
272
312
  # Reset the object to empty state
273
313
  def reset(self):
274
- self.setValue(ECValue())
314
+ self.setValue(ECValue(content=None))
315
+
316
+ ###############################################################################
317
+ # A string or int variable
318
+ class ECVariable(ECValueHolder):
319
+ def __init__(self):
320
+ super().__init__()
321
+
322
+ # Reset the object to an empty string
323
+ def reset(self):
324
+ self.setValue(ECValue(type=str, content=''))
325
+
326
+ # Set the value to a given ECValue
327
+ def setValue(self, value):
328
+ val_type = value.getType()
329
+ if type_in(val_type, ('dict', 'list')):
330
+ value.setContent(json.dumps(value.getContent()))
331
+ elif not type_in(val_type, (str, int, float, bool, None)):
332
+ raise RuntimeError(None, 'ECVariable can only hold str, int, float, or bool values') # type: ignore
333
+ super().setValue(value)
334
+
335
+ ###############################################################################
336
+ # A dictionary variable
337
+ class ECDictionary(ECValueHolder):
338
+ def __init__(self):
339
+ super().__init__()
340
+ self.reset()
341
+
342
+ # Reset the object to empty state
343
+ def reset(self):
344
+ self.setValue(ECValue(content={}))
345
+
346
+ # Set the value to an ECValue
347
+ def setValue(self, value):
348
+ varType = value.getType()
349
+ if type_in(varType, (str, 'dict')):
350
+ content = value.getContent()
351
+ if types_equal(varType, str):
352
+ try:
353
+ if content in ('', {}, None): content = {}
354
+ else: content = json.loads(content)
355
+ except:
356
+ raise RuntimeError(None, 'ECDictionary string value is not valid JSON') # type: ignore
357
+ elif varType == None:
358
+ content = {}
359
+ else:
360
+ raise RuntimeError(None, 'ECDictionary can only hold dict values or None') # type: ignore
361
+ super().setValue(content)
275
362
 
276
- # Check if the object can have properties
277
- def hasProperties(self):
278
- return True
363
+ def getValue(self):
364
+ return super().getValue()
365
+
366
+ # Set an entry in the dictionary
367
+ def setEntry(self, key, value):
368
+ content = self.getValue()
369
+ if content is None:
370
+ return
371
+ if isinstance(value, str):
372
+ try:
373
+ value = json.loads(value)
374
+ except Exception:
375
+ pass
376
+ content[key] = value # type: ignore
377
+
378
+ # Test if an entry exists in the dictionary
379
+ def hasEntry(self, key):
380
+ content = self.getValue()
381
+ if content is None:
382
+ return False
383
+ return key in content
279
384
 
280
- # Set a specific property on the object
281
- def setProperty(self, name, value):
282
- self.properties[name] = value
385
+ # Get an entry from the dictionary
386
+ def getEntry(self, key):
387
+ content = self.getValue()
388
+ if content is None:
389
+ return None
390
+ return content.get(key, None)
391
+
392
+ # Delete an entry from the dictionary
393
+ def deleteEntry(self, key):
394
+ content = self.getValue()
395
+ if content is None:
396
+ return
397
+ if key in content:
398
+ del content[key]
399
+
400
+ # Get the keys of the dictionary
401
+ def keys(self):
402
+ content = self.getValue()
403
+ if content is None:
404
+ return []
405
+ return list(content.keys())
406
+
407
+ # Check if the dictionary is empty
408
+ def isEmpty(self):
409
+ return len(self.keys()) == 0
410
+
411
+ ###############################################################################
412
+ # A list variable
413
+ class ECList(ECValueHolder):
414
+ def __init__(self):
415
+ super().__init__()
416
+ self.reset()
417
+
418
+ # Reset the object to empty state
419
+ def reset(self):
420
+ self.setValue(ECValue(content=[]))
421
+
422
+ # Set the value to an ECValue
423
+ def setValue(self, value):
424
+ content = value.getContent()
425
+ if content in ('', None): content = []
426
+ else:
427
+ try:
428
+ content = json.loads(content) # type: ignore
429
+ except:
430
+ pass
431
+ super().setValue(content)
283
432
 
284
- # Check if the object has a specific property
285
- def hasProperty(self, name):
286
- return name in self.properties
433
+ def getValue(self):
434
+ return super().getValue()
435
+
436
+ # Append an item to the list
437
+ def append(self, item):
438
+ content = self.getContent()
439
+ if content is None:
440
+ return
441
+ if isinstance(item, str):
442
+ try:
443
+ item = json.loads(item)
444
+ except Exception:
445
+ pass
446
+ content.append(item) # type: ignore
447
+ self.setContent(content)
448
+
449
+ # Set an item in the list
450
+ def setItem(self, index, value):
451
+ content = self.getValue()
452
+ if content is None:
453
+ return
454
+ if isinstance(value, str):
455
+ try:
456
+ value = json.loads(value)
457
+ except Exception:
458
+ pass
459
+ content[index] = value # type: ignore
460
+
461
+ # Return the number of items in the list
462
+ def getItemCount(self):
463
+ content = self.getContent()
464
+ if content is None:
465
+ return 0
466
+ return len(content)
467
+
468
+ # Get an item from the list
469
+ def getItem(self, index):
470
+ content = self.getContent()
471
+ if content is None:
472
+ return None
473
+ return content[index]
287
474
 
288
- # Get a specific property
289
- def getProperty(self, name):
290
- return self.properties[name]
475
+ # Check if the list is empty
476
+ def isEmpty(self):
477
+ return self.getItemCount() == 0
478
+
479
+ # Delete an item from the list
480
+ def deleteItem(self, index):
481
+ content = self.getValue()
482
+ if content is None:
483
+ return
484
+ if index < 0 or index >= len(content):
485
+ return
486
+ del content[index]
487
+ self.setContent(content)
291
488
 
292
489
  ###############################################################################
293
490
  # A file variable
@@ -295,6 +492,12 @@ class ECFile(ECObject):
295
492
  def __init__(self):
296
493
  super().__init__()
297
494
 
495
+ ###############################################################################
496
+ # A module variable
497
+ class ECModule(ECObject):
498
+ def __init__(self):
499
+ super().__init__()
500
+
298
501
  ###############################################################################
299
502
  # An SSH variable
300
503
  class ECSSH(ECObject):
easycoder/ec_compiler.py CHANGED
@@ -56,10 +56,12 @@ class Compiler:
56
56
 
57
57
  # Get a value
58
58
  def getValue(self):
59
+ self.program.ensureNotRunning()
59
60
  return self.value.compileValue()
60
61
 
61
62
  # Get the next value
62
63
  def nextValue(self):
64
+ self.program.ensureNotRunning()
63
65
  self.index += 1
64
66
  return self.value.compileValue()
65
67
 
@@ -112,7 +114,19 @@ class Compiler:
112
114
  if next == item:
113
115
  self.nextToken()
114
116
  return
115
- elif next == token: self.nextToken()
117
+ elif next == token:
118
+ self.nextToken()
119
+
120
+ # Skip common articles (optional syntactic noise for readability/disambiguation)
121
+ # Consumes leading articles ('the', 'a', 'an') at the next position
122
+ def skipArticles(self):
123
+ # Consume leading articles at next position(s) — like skip() but for multiple
124
+ while True:
125
+ next_tok = self.peek()
126
+ if next_tok in ['the', 'a', 'an']:
127
+ self.nextToken()
128
+ else:
129
+ break
116
130
 
117
131
  # Rewind to a given position in the code list
118
132
  def rewindTo(self, index):
@@ -135,6 +149,7 @@ class Compiler:
135
149
 
136
150
  # Get the symbol record for the current token (assumes it is a symbol name)
137
151
  def getSymbolRecord(self, name=None):
152
+ self.program.ensureNotRunning()
138
153
  if name == None: name = self.getToken()
139
154
  if not name in self.symbols:
140
155
  FatalError(self, f'Undefined symbol name "{name}"')
@@ -147,7 +162,15 @@ class Compiler:
147
162
 
148
163
  # Add a value type
149
164
  def addValueType(self):
150
- self.valueTypes[self.getToken()] = True
165
+ name = self.peek()
166
+ record = None
167
+ try:
168
+ record = self.symbols[name]
169
+ except:
170
+ pass
171
+ if record != None:
172
+ raise FatalError(self, f'Duplicate symbol name "{name}"')
173
+ self.valueTypes[name] = True
151
174
 
152
175
  # Test if a given value is in the value types list
153
176
  def hasValue(self, type):
@@ -180,10 +203,10 @@ class Compiler:
180
203
 
181
204
  # Compile a symbol
182
205
  def compileSymbol(self, command, name, classname):
183
- try:
184
- self.symbols[name]
185
- raise FatalError(self, f'Duplicate symbol name "{name}"')
186
- except: pass
206
+ # try:
207
+ # self.symbols[name]
208
+ # raise FatalError(self, f'Duplicate symbol name "{name}"')
209
+ # except: pass
187
210
  command['name'] = name
188
211
  command['classname'] = classname
189
212
  command['program'] = self.program
@@ -249,9 +272,7 @@ class Compiler:
249
272
  self.index = index
250
273
  while True:
251
274
  token = self.tokens[self.index]
252
- # keyword = token.token
253
275
  if self.debugCompile: print(f'{token.lino + 1}: {self.script.lines[token.lino]}')
254
- # if keyword != 'else':
255
276
  if self.compileOne() == True:
256
277
  if self.index == len(self.tokens) - 1:
257
278
  return True