easycoder 251105.1__py2.py3-none-any.whl → 251215.2__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 -2
- easycoder/debugger/__init__.py +5 -0
- easycoder/debugger/ec_dbg_value_display copy.py +195 -0
- easycoder/debugger/ec_dbg_value_display.py +23 -0
- easycoder/debugger/ec_dbg_watch_list copy.py +219 -0
- easycoder/debugger/ec_dbg_watchlist.py +159 -0
- easycoder/{ec_debug.py → debugger/ec_debug.py} +349 -182
- easycoder/ec_classes.py +284 -11
- easycoder/ec_compiler.py +55 -39
- easycoder/ec_condition.py +1 -1
- easycoder/ec_core.py +846 -1007
- easycoder/ec_gclasses.py +225 -0
- easycoder/{ec_pyside.py → ec_graphics.py} +566 -431
- easycoder/ec_handler.py +16 -13
- easycoder/ec_program.py +262 -168
- easycoder/ec_psutil.py +48 -0
- easycoder/ec_value.py +29 -32
- {easycoder-251105.1.dist-info → easycoder-251215.2.dist-info}/METADATA +11 -1
- easycoder-251215.2.dist-info/RECORD +31 -0
- easycoder-251105.1.dist-info/RECORD +0 -24
- {easycoder-251105.1.dist-info → easycoder-251215.2.dist-info}/WHEEL +0 -0
- {easycoder-251105.1.dist-info → easycoder-251215.2.dist-info}/entry_points.txt +0 -0
- {easycoder-251105.1.dist-info → easycoder-251215.2.dist-info}/licenses/LICENSE +0 -0
easycoder/ec_classes.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import sys
|
|
1
|
+
import sys, paramiko
|
|
2
|
+
from typing import Optional, Any
|
|
2
3
|
|
|
3
4
|
class FatalError(BaseException):
|
|
4
5
|
def __init__(self, compiler, message):
|
|
@@ -12,7 +13,7 @@ class NoValueError(FatalError):
|
|
|
12
13
|
def __init__(self, compiler, record):
|
|
13
14
|
super().__init__(compiler, f'Variable {record["name"]} does not hold a value')
|
|
14
15
|
|
|
15
|
-
class
|
|
16
|
+
class RuntimeAssertionError:
|
|
16
17
|
def __init__(self, program, msg=None):
|
|
17
18
|
code = program.code[program.pc]
|
|
18
19
|
lino = code['lino']
|
|
@@ -22,7 +23,7 @@ class AssertionError:
|
|
|
22
23
|
print(message)
|
|
23
24
|
sys.exit()
|
|
24
25
|
|
|
25
|
-
class RuntimeError:
|
|
26
|
+
class RuntimeError(BaseException):
|
|
26
27
|
def __init__(self, program, message):
|
|
27
28
|
if program == None:
|
|
28
29
|
sys.exit(f'Runtime Error: {message}')
|
|
@@ -56,11 +57,283 @@ class Token:
|
|
|
56
57
|
def __init__(self, lino, token):
|
|
57
58
|
self.lino = lino
|
|
58
59
|
self.token = token
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
60
|
+
|
|
61
|
+
###############################################################################
|
|
62
|
+
# This is the set of generic EasyCoder objects (values and variables)
|
|
63
|
+
|
|
64
|
+
###############################################################################
|
|
65
|
+
# A multipurpose value object. Holds a single value, with domain and type information
|
|
66
|
+
class ECValue():
|
|
67
|
+
def __init__(self, domain: Optional[str] = None, type: Optional[str] = None,
|
|
68
|
+
content: Any = None, name: Optional[str] = None):
|
|
69
|
+
object.__setattr__(self, 'domain', domain)
|
|
70
|
+
object.__setattr__(self, 'type', type)
|
|
71
|
+
object.__setattr__(self, 'content', content)
|
|
72
|
+
object.__setattr__(self, 'name', name)
|
|
73
|
+
object.__setattr__(self, 'properties', {})
|
|
74
|
+
object.__setattr__(self, 'locked', False)
|
|
75
|
+
object.__setattr__(self, '_attrs', {}) # Store dynamic attributes
|
|
76
|
+
|
|
77
|
+
def __setattr__(self, name: str, value: Any) -> None:
|
|
78
|
+
"""Allow setting any attribute dynamically."""
|
|
79
|
+
if name in ('domain', 'type', 'content', 'name', 'properties', 'locked', '_attrs'):
|
|
80
|
+
object.__setattr__(self, name, value)
|
|
81
|
+
else:
|
|
82
|
+
# Store dynamic attributes in _attrs dict
|
|
83
|
+
self._attrs[name] = value
|
|
84
|
+
|
|
85
|
+
def __getattr__(self, name: str) -> Any:
|
|
86
|
+
"""Retrieve dynamic attributes or return None if not found."""
|
|
87
|
+
if name == '_attrs':
|
|
88
|
+
return object.__getattribute__(self, '_attrs')
|
|
89
|
+
return self._attrs.get(name)
|
|
90
|
+
|
|
91
|
+
def setDomain(self, domain):
|
|
92
|
+
self.domain = domain
|
|
93
|
+
|
|
94
|
+
def getDomain(self):
|
|
95
|
+
return self.domain
|
|
96
|
+
|
|
97
|
+
def setType(self, type):
|
|
98
|
+
self.type = type
|
|
99
|
+
|
|
100
|
+
def getType(self):
|
|
101
|
+
return self.type
|
|
102
|
+
|
|
103
|
+
def setContent(self, content):
|
|
104
|
+
self.content = content
|
|
105
|
+
|
|
106
|
+
def getContent(self):
|
|
107
|
+
return self.content
|
|
108
|
+
|
|
109
|
+
def setValue(self, type=None, content=None):
|
|
110
|
+
self.type = type
|
|
111
|
+
self.content = content
|
|
112
|
+
|
|
113
|
+
def setProperty(self, key, value):
|
|
114
|
+
self.properties[key] = value
|
|
115
|
+
|
|
116
|
+
def getProperty(self, key):
|
|
117
|
+
return self.properties.get(key, None)
|
|
118
|
+
|
|
119
|
+
def setName(self, name):
|
|
120
|
+
self.name = name
|
|
121
|
+
|
|
122
|
+
def getName(self):
|
|
123
|
+
return self.name
|
|
124
|
+
|
|
125
|
+
def lock(self):
|
|
126
|
+
self.locked = True
|
|
127
|
+
|
|
128
|
+
def isLocked(self):
|
|
129
|
+
return self.locked
|
|
130
|
+
|
|
131
|
+
###############################################################################
|
|
132
|
+
# The base class for all EasyCoder variable types
|
|
133
|
+
class ECObject():
|
|
134
|
+
def __init__(self):
|
|
135
|
+
self.locked: bool = False
|
|
136
|
+
self.elements: int = 0
|
|
137
|
+
self.index: Optional[int] = None
|
|
138
|
+
self.values: Optional[list] = None
|
|
139
|
+
self.name: Optional[str] = None
|
|
140
|
+
|
|
141
|
+
# Set the index for the variable
|
|
142
|
+
def setIndex(self, index: int) -> None:
|
|
143
|
+
self.index = index
|
|
144
|
+
|
|
145
|
+
# Get the index for the variable
|
|
146
|
+
def getIndex(self):
|
|
147
|
+
return self.index
|
|
148
|
+
|
|
149
|
+
# Lock the variable
|
|
150
|
+
def setLocked(self):
|
|
151
|
+
self.locked = True
|
|
152
|
+
|
|
153
|
+
# Check if the variable is locked
|
|
154
|
+
def isLocked(self):
|
|
155
|
+
return self.locked
|
|
156
|
+
|
|
157
|
+
# Set the value at the current index
|
|
158
|
+
def setValue(self, value):
|
|
159
|
+
if self.values is None:
|
|
160
|
+
self.index = 0
|
|
161
|
+
self.elements = 1
|
|
162
|
+
self.values = [None]
|
|
163
|
+
if isinstance(value, ECValue): value.setName(self.name)
|
|
164
|
+
self.values[self.index] = value # type: ignore
|
|
165
|
+
|
|
166
|
+
# Get the value at the current index
|
|
167
|
+
def getValue(self):
|
|
168
|
+
if self.values is None: return None
|
|
169
|
+
return self.values[self.index] # type: ignore
|
|
170
|
+
|
|
171
|
+
# Get all the values
|
|
172
|
+
def getValues(self):
|
|
173
|
+
return self.values
|
|
174
|
+
|
|
175
|
+
# Set the number of elements in the variable
|
|
176
|
+
def setElements(self, elements):
|
|
177
|
+
if self.elements == 0:
|
|
178
|
+
self.values = [None] * elements
|
|
179
|
+
self.elements = elements
|
|
180
|
+
self.index = 0
|
|
181
|
+
if elements == self.elements:
|
|
182
|
+
pass
|
|
183
|
+
elif elements > self.elements:
|
|
184
|
+
self.values.extend([None] * (elements - self.elements)) # pyright: ignore[reportOptionalMemberAccess]
|
|
185
|
+
else:
|
|
186
|
+
del self.values[elements:] # pyright: ignore[reportOptionalSubscript]
|
|
187
|
+
self.index = 0
|
|
188
|
+
self.elements = elements
|
|
189
|
+
|
|
190
|
+
# Get the number of elements in the variable
|
|
191
|
+
def getElements(self):
|
|
192
|
+
return self.elements
|
|
193
|
+
|
|
194
|
+
# Check if the object has a runtime value. Default is False
|
|
195
|
+
def hasRuntimeValue(self):
|
|
196
|
+
return False
|
|
197
|
+
|
|
198
|
+
# Check if the object is mutable. Default is False
|
|
199
|
+
def isMutable(self):
|
|
200
|
+
return False
|
|
201
|
+
|
|
202
|
+
# Check if the object is clearable
|
|
203
|
+
def isClearable(self):
|
|
204
|
+
return False
|
|
205
|
+
|
|
206
|
+
# Get the content of the value at the current index
|
|
207
|
+
def getContent(self):
|
|
208
|
+
if not self.hasRuntimeValue(): return None
|
|
209
|
+
v = self.getValue()
|
|
210
|
+
if v is None: return None
|
|
211
|
+
return v.getContent()
|
|
212
|
+
|
|
213
|
+
# Get the type of the value at the current index
|
|
214
|
+
def getType(self):
|
|
215
|
+
if not self.hasRuntimeValue(): return None
|
|
216
|
+
v = self.getValue()
|
|
217
|
+
if v is None: return None
|
|
218
|
+
return v.getType()
|
|
219
|
+
|
|
220
|
+
# Check if the object is empty. Default is True
|
|
221
|
+
def isEmpty(self):
|
|
222
|
+
return True
|
|
223
|
+
|
|
224
|
+
# Set the name of the object
|
|
225
|
+
def setName(self, name):
|
|
226
|
+
self.name = name
|
|
227
|
+
|
|
228
|
+
# Get the name of the object
|
|
229
|
+
def getName(self):
|
|
230
|
+
return self.name
|
|
231
|
+
|
|
232
|
+
# Check if the object can have properties
|
|
233
|
+
def hasProperties(self):
|
|
234
|
+
return False
|
|
235
|
+
|
|
236
|
+
###############################################################################
|
|
237
|
+
# A generic variable object that can hold a mutable value
|
|
238
|
+
class ECVariable(ECObject):
|
|
239
|
+
def __init__(self):
|
|
240
|
+
super().__init__()
|
|
241
|
+
self.properties = {}
|
|
242
|
+
|
|
243
|
+
# Set the content of the value at the current index
|
|
244
|
+
def setContent(self, content):
|
|
245
|
+
if self.values is None:
|
|
246
|
+
self.index = 0
|
|
247
|
+
self.elements = 1
|
|
248
|
+
self.values = [None]
|
|
249
|
+
self.values[self.index] = content # type: ignore
|
|
250
|
+
|
|
251
|
+
# Set the value to a given ECValue
|
|
252
|
+
def setValue(self, value):
|
|
253
|
+
if self.values is None:
|
|
254
|
+
self.index = 0
|
|
255
|
+
self.elements = 1
|
|
256
|
+
self.values = [None]
|
|
257
|
+
if self.index >= self.elements: raise RuntimeError(None, 'Index out of range') # type: ignore
|
|
258
|
+
self.values[self.index] = value # type: ignore
|
|
259
|
+
|
|
260
|
+
# Report if the object is clearable
|
|
261
|
+
def isClearable(self):
|
|
262
|
+
return True
|
|
263
|
+
|
|
264
|
+
# This object has a runtime value
|
|
265
|
+
def hasRuntimeValue(self):
|
|
266
|
+
return True
|
|
267
|
+
|
|
268
|
+
# This object is mutable.
|
|
269
|
+
def isMutable(self):
|
|
270
|
+
return True
|
|
271
|
+
|
|
272
|
+
# Reset the object to empty state
|
|
273
|
+
def reset(self):
|
|
274
|
+
self.setValue(ECValue())
|
|
275
|
+
|
|
276
|
+
# Check if the object can have properties
|
|
277
|
+
def hasProperties(self):
|
|
278
|
+
return True
|
|
279
|
+
|
|
280
|
+
# Set a specific property on the object
|
|
281
|
+
def setProperty(self, name, value):
|
|
282
|
+
self.properties[name] = value
|
|
283
|
+
|
|
284
|
+
# Check if the object has a specific property
|
|
285
|
+
def hasProperty(self, name):
|
|
286
|
+
return name in self.properties
|
|
287
|
+
|
|
288
|
+
# Get a specific property
|
|
289
|
+
def getProperty(self, name):
|
|
290
|
+
return self.properties[name]
|
|
291
|
+
|
|
292
|
+
###############################################################################
|
|
293
|
+
# A file variable
|
|
294
|
+
class ECFile(ECObject):
|
|
295
|
+
def __init__(self):
|
|
296
|
+
super().__init__()
|
|
297
|
+
|
|
298
|
+
###############################################################################
|
|
299
|
+
# An SSH variable
|
|
300
|
+
class ECSSH(ECObject):
|
|
301
|
+
def __init__(self):
|
|
302
|
+
super().__init__()
|
|
303
|
+
|
|
304
|
+
# Set up the SSH connection
|
|
305
|
+
def setup(self, host=None, user=None, password=None):
|
|
306
|
+
ssh = paramiko.SSHClient()
|
|
307
|
+
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
|
308
|
+
try:
|
|
309
|
+
ssh.connect(host, username=user, password=password, timeout=10) # type: ignore
|
|
310
|
+
self.setValue(ssh)
|
|
311
|
+
self.sftp = ssh.open_sftp()
|
|
312
|
+
return True
|
|
313
|
+
except:
|
|
314
|
+
return False
|
|
315
|
+
|
|
316
|
+
# Get the SFTP client
|
|
317
|
+
def getSFTP(self):
|
|
318
|
+
return self.sftp
|
|
319
|
+
|
|
320
|
+
###############################################################################
|
|
321
|
+
# A stack variable
|
|
322
|
+
class ECStack(ECObject):
|
|
323
|
+
|
|
324
|
+
def __init__(self):
|
|
325
|
+
super().__init__()
|
|
326
|
+
self.values: Optional[list[list[Any]]] = None # List of stacks, each holding any type
|
|
327
|
+
|
|
328
|
+
def push(self, item: Any) -> None:
|
|
329
|
+
if self.values is None:
|
|
330
|
+
self.index = 0
|
|
331
|
+
self.elements = 1
|
|
332
|
+
self.values = [[]]
|
|
333
|
+
assert self.index is not None # Type narrowing: index is always set when values exists
|
|
334
|
+
self.values[self.index].append(item)
|
|
335
|
+
|
|
336
|
+
def pop(self) -> Any:
|
|
337
|
+
if self.values is None or self.index is None or self.values[self.index] is None or len(self.values[self.index]) == 0:
|
|
338
|
+
return None
|
|
339
|
+
return self.values[self.index].pop()
|
easycoder/ec_compiler.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import sys
|
|
1
2
|
from .ec_classes import FatalError
|
|
2
3
|
from .ec_value import Value
|
|
3
4
|
from .ec_condition import Condition
|
|
@@ -84,7 +85,8 @@ class Compiler:
|
|
|
84
85
|
return self.program.code[pc]
|
|
85
86
|
|
|
86
87
|
# Add a command to the code list
|
|
87
|
-
def addCommand(self, command):
|
|
88
|
+
def addCommand(self, command, debug=True):
|
|
89
|
+
command['debug'] = debug
|
|
88
90
|
command['bp'] = False
|
|
89
91
|
self.code.append(command)
|
|
90
92
|
|
|
@@ -132,16 +134,16 @@ class Compiler:
|
|
|
132
134
|
print(warning)
|
|
133
135
|
|
|
134
136
|
# Get the symbol record for the current token (assumes it is a symbol name)
|
|
135
|
-
def getSymbolRecord(self):
|
|
136
|
-
|
|
137
|
-
if not
|
|
138
|
-
FatalError(self, f'Undefined symbol name "{
|
|
137
|
+
def getSymbolRecord(self, name=None):
|
|
138
|
+
if name == None: name = self.getToken()
|
|
139
|
+
if not name in self.symbols:
|
|
140
|
+
FatalError(self, f'Undefined symbol name "{name}"')
|
|
139
141
|
return None
|
|
140
|
-
symbol = self.symbols[
|
|
142
|
+
symbol = self.symbols[name]
|
|
141
143
|
if symbol == None: return None
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
return
|
|
144
|
+
record = self.code[symbol]
|
|
145
|
+
record['used'] = True
|
|
146
|
+
return record
|
|
145
147
|
|
|
146
148
|
# Add a value type
|
|
147
149
|
def addValueType(self):
|
|
@@ -150,40 +152,56 @@ class Compiler:
|
|
|
150
152
|
# Test if a given value is in the value types list
|
|
151
153
|
def hasValue(self, type):
|
|
152
154
|
return type in self.valueTypes
|
|
153
|
-
|
|
154
|
-
#
|
|
155
|
-
def
|
|
156
|
-
|
|
155
|
+
|
|
156
|
+
# Instantiate an object of the given class name
|
|
157
|
+
def instantiate(self, classname):
|
|
158
|
+
# Search through all loaded modules for the class
|
|
159
|
+
items = sys.modules.items()
|
|
160
|
+
for module_name, module in items:
|
|
161
|
+
if module is None:
|
|
162
|
+
continue
|
|
163
|
+
try:
|
|
164
|
+
if hasattr(module, classname):
|
|
165
|
+
cls = getattr(module, classname)
|
|
166
|
+
# Verify it's actually a class
|
|
167
|
+
if isinstance(cls, type):
|
|
168
|
+
# Attempt to instantiate
|
|
169
|
+
try:
|
|
170
|
+
return cls()
|
|
171
|
+
except TypeError as ex:
|
|
172
|
+
raise FatalError(self, f"Object instantiation error: {ex}")
|
|
173
|
+
except Exception:
|
|
174
|
+
continue
|
|
175
|
+
return None
|
|
157
176
|
|
|
158
177
|
# Compile a variable
|
|
159
|
-
def compileVariable(self, command,
|
|
160
|
-
return self.compileSymbol(command, self.nextToken(),
|
|
178
|
+
def compileVariable(self, command, classname):
|
|
179
|
+
return self.compileSymbol(command, self.nextToken(), classname)
|
|
161
180
|
|
|
162
181
|
# Compile a symbol
|
|
163
|
-
def compileSymbol(self, command, name,
|
|
182
|
+
def compileSymbol(self, command, name, classname):
|
|
164
183
|
try:
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
if v:
|
|
169
|
-
FatalError(self, f'Duplicate symbol name "{name}"')
|
|
170
|
-
return False
|
|
171
|
-
self.symbols[name] = self.getCodeSize()
|
|
172
|
-
command['program'] = self.program
|
|
173
|
-
command['type'] = 'symbol'
|
|
184
|
+
self.symbols[name]
|
|
185
|
+
raise FatalError(self, f'Duplicate symbol name "{name}"')
|
|
186
|
+
except: pass
|
|
174
187
|
command['name'] = name
|
|
175
|
-
command['
|
|
176
|
-
command['
|
|
177
|
-
command['value'] = [None]
|
|
188
|
+
command['classname'] = classname
|
|
189
|
+
command['program'] = self.program
|
|
178
190
|
command['used'] = False
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
191
|
+
self.symbols[name] = self.getCodeSize()
|
|
192
|
+
if classname != ':':
|
|
193
|
+
object = self.instantiate(classname)
|
|
194
|
+
command['object'] = object
|
|
195
|
+
if object != None:
|
|
196
|
+
command['type'] = 'symbol'
|
|
197
|
+
object.setName(name) # type: ignore
|
|
198
|
+
self.addCommand(command, False)
|
|
185
199
|
return True
|
|
186
200
|
|
|
201
|
+
# Compile a program label (a symbol ending with ':')
|
|
202
|
+
def compileLabel(self, command):
|
|
203
|
+
return self.compileSymbol(command, self.getToken(), ':')
|
|
204
|
+
|
|
187
205
|
# Compile the current token
|
|
188
206
|
def compileToken(self):
|
|
189
207
|
self.warnings = []
|
|
@@ -192,8 +210,8 @@ class Compiler:
|
|
|
192
210
|
if not token:
|
|
193
211
|
return False
|
|
194
212
|
if len(self.code) == 0:
|
|
195
|
-
if self.program.parent == None and self.program.
|
|
196
|
-
cmd = {'domain': 'graphics', 'keyword': 'init'
|
|
213
|
+
if self.program.parent == None and self.program.graphics:
|
|
214
|
+
cmd = {'domain': 'graphics', 'keyword': 'init'}
|
|
197
215
|
self.code.append(cmd)
|
|
198
216
|
mark = self.getIndex()
|
|
199
217
|
for domain in self.program.getDomains():
|
|
@@ -203,8 +221,6 @@ class Compiler:
|
|
|
203
221
|
command['domain'] = domain.getName()
|
|
204
222
|
command['lino'] = self.tokens[self.index].lino
|
|
205
223
|
command['keyword'] = token
|
|
206
|
-
command['type'] = None
|
|
207
|
-
command['debug'] = True
|
|
208
224
|
result = handler(command)
|
|
209
225
|
if result:
|
|
210
226
|
return result
|
|
@@ -234,7 +250,7 @@ class Compiler:
|
|
|
234
250
|
while True:
|
|
235
251
|
token = self.tokens[self.index]
|
|
236
252
|
# keyword = token.token
|
|
237
|
-
if self.debugCompile: print(self.script.lines[token.lino])
|
|
253
|
+
if self.debugCompile: print(f'{token.lino + 1}: {self.script.lines[token.lino]}')
|
|
238
254
|
# if keyword != 'else':
|
|
239
255
|
if self.compileOne() == True:
|
|
240
256
|
if self.index == len(self.tokens) - 1:
|
easycoder/ec_condition.py
CHANGED
|
@@ -16,7 +16,7 @@ class Condition:
|
|
|
16
16
|
for domain in self.compiler.program.getDomains():
|
|
17
17
|
condition = domain.compileCondition()
|
|
18
18
|
if condition != None:
|
|
19
|
-
condition.domain= domain.getName()
|
|
19
|
+
condition.domain = domain.getName()
|
|
20
20
|
return condition
|
|
21
21
|
self.rewindTo(mark)
|
|
22
22
|
return None
|