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.
- easycoder/__init__.py +4 -3
- easycoder/debugger/ec_dbg_value_display copy.py +1 -1
- easycoder/debugger/ec_dbg_value_display.py +12 -11
- easycoder/debugger/ec_dbg_watchlist.py +146 -12
- easycoder/debugger/ec_debug.py +85 -8
- easycoder/ec_classes.py +228 -25
- easycoder/ec_compiler.py +29 -8
- easycoder/ec_core.py +364 -242
- easycoder/ec_gclasses.py +20 -9
- easycoder/ec_graphics.py +42 -26
- easycoder/ec_handler.py +4 -3
- easycoder/ec_mqtt.py +248 -0
- easycoder/ec_program.py +63 -28
- easycoder/ec_psutil.py +1 -1
- easycoder/ec_value.py +57 -36
- easycoder/pre/README.md +3 -0
- easycoder/pre/__init__.py +17 -0
- easycoder/pre/debugger/__init__.py +5 -0
- easycoder/pre/debugger/ec_dbg_value_display copy.py +195 -0
- easycoder/pre/debugger/ec_dbg_value_display.py +24 -0
- easycoder/pre/debugger/ec_dbg_watch_list copy.py +219 -0
- easycoder/pre/debugger/ec_dbg_watchlist.py +293 -0
- easycoder/pre/debugger/ec_debug.py +1014 -0
- easycoder/pre/ec_border.py +67 -0
- easycoder/pre/ec_classes.py +470 -0
- easycoder/pre/ec_compiler.py +291 -0
- easycoder/pre/ec_condition.py +27 -0
- easycoder/pre/ec_core.py +2772 -0
- easycoder/pre/ec_gclasses.py +230 -0
- easycoder/pre/ec_graphics.py +1682 -0
- easycoder/pre/ec_handler.py +79 -0
- easycoder/pre/ec_keyboard.py +439 -0
- easycoder/pre/ec_program.py +557 -0
- easycoder/pre/ec_psutil.py +48 -0
- easycoder/pre/ec_timestamp.py +11 -0
- easycoder/pre/ec_value.py +124 -0
- easycoder/pre/icons/close.png +0 -0
- easycoder/pre/icons/exit.png +0 -0
- easycoder/pre/icons/run.png +0 -0
- easycoder/pre/icons/step.png +0 -0
- easycoder/pre/icons/stop.png +0 -0
- easycoder/pre/icons/tick.png +0 -0
- {easycoder-251215.2.dist-info → easycoder-260111.1.dist-info}/METADATA +1 -1
- easycoder-260111.1.dist-info/RECORD +59 -0
- easycoder-251215.2.dist-info/RECORD +0 -31
- {easycoder-251215.2.dist-info → easycoder-260111.1.dist-info}/WHEEL +0 -0
- {easycoder-251215.2.dist-info → easycoder-260111.1.dist-info}/entry_points.txt +0 -0
- {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
|
|
68
|
-
|
|
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
|
|
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
|
-
#
|
|
233
|
-
def
|
|
234
|
-
|
|
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
|
|
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
|
-
|
|
277
|
-
|
|
278
|
-
|
|
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
|
-
#
|
|
281
|
-
def
|
|
282
|
-
|
|
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
|
-
|
|
285
|
-
|
|
286
|
-
|
|
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
|
-
#
|
|
289
|
-
def
|
|
290
|
-
return self.
|
|
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:
|
|
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.
|
|
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
|
-
|
|
185
|
-
|
|
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
|