easycoder 260110.1__py2.py3-none-any.whl → 260118.3__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/ec_mqtt.py ADDED
@@ -0,0 +1,435 @@
1
+ from easycoder import Handler, ECObject, ECValue, RuntimeError
2
+ import paho.mqtt.client as mqtt
3
+ import time
4
+ import threading
5
+ import json
6
+
7
+ #############################################################################
8
+ # MQTT client class
9
+ class MQTTClient():
10
+ def __init__(self):
11
+ super().__init__()
12
+
13
+ def create(self, program, clientID, broker, port, topics):
14
+ self.program = program
15
+ self.clientID = clientID
16
+ self.broker = broker
17
+ self.port = port
18
+ self.topics = topics
19
+ self.onConnectPC = None
20
+ self.onMessagePC = None
21
+ self.timeout = False
22
+ self.messages = {}
23
+ self.chunked_messages = {} # Store incoming chunked messages {topic: {part_num: data}}
24
+ self.confirmation_lock = threading.Lock()
25
+ self.chunk_size = 1024 # Default chunk size
26
+ self.last_send_time = None # Time taken for last message transmission (seconds)
27
+ self.client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=self.clientID) # type: ignore
28
+
29
+ # Setup callbacks
30
+ self.client.on_connect = self.on_connect
31
+ self.client.on_message = self.on_message
32
+
33
+ def on_connect(self, client, userdata, flags, reason_code, properties):
34
+ print(f"Client {self.clientID} connected")
35
+ for item in self.topics:
36
+ topic = self.program.getObject(self.program.getVariable(item))
37
+ self.client.subscribe(topic.getName(), qos=topic.getQoS())
38
+ print(f"Subscribed to topic: {topic.getName()} with QoS {topic.getQoS()}")
39
+
40
+ if self.onConnectPC is not None:
41
+ self.program.run(self.onConnectPC)
42
+ self.program.flushCB()
43
+
44
+ def on_message(self, client, userdata, msg):
45
+ payload = msg.payload.decode('utf-8')
46
+ topic = msg.topic
47
+
48
+ # Check if this is a chunked message (format: "!part!<n> <total><data>")
49
+ if payload.startswith('!part!'):
50
+ # Extract: "!part!<n> <total><data>"
51
+ header_end = payload.find(' ', 6) # Find space after part number
52
+ if header_end > 6:
53
+ try:
54
+ part_num = int(payload[6:header_end]) # Extract part number
55
+ # Find next space to get total_chunks
56
+ total_end = payload.find(' ', header_end + 1)
57
+ if total_end > header_end:
58
+ total_chunks = int(payload[header_end + 1:total_end])
59
+ data = payload[total_end + 1:] # Rest is data
60
+
61
+ # Initialize chunked message storage if this is part 0
62
+ if part_num == 0:
63
+ self.chunked_messages[topic] = {}
64
+
65
+ # Store this chunk with its part number
66
+ if topic in self.chunked_messages:
67
+ self.chunked_messages[topic][part_num] = data
68
+ print(f"Received chunk {part_num}/{total_chunks - 1} on topic {topic}")
69
+ except (ValueError, IndexError):
70
+ pass
71
+ return
72
+
73
+ elif payload.startswith('!last!'):
74
+ # Final chunk: "!last!<total><data>"
75
+ try:
76
+ # Find where the total ends and data begins (first space after !last!)
77
+ space_pos = payload.find(' ', 6)
78
+ if space_pos > 6:
79
+ total_chunks = int(payload[6:space_pos])
80
+ data = payload[space_pos + 1:] # Rest is data
81
+
82
+ # Initialize topic storage if not present (single chunk case)
83
+ if topic not in self.chunked_messages:
84
+ self.chunked_messages[topic] = {}
85
+
86
+ # Store the last chunk
87
+ self.chunked_messages[topic][total_chunks - 1] = data
88
+
89
+ # Verify all chunks are present
90
+ expected_parts = set(range(total_chunks))
91
+ received_parts = set(self.chunked_messages[topic].keys())
92
+
93
+ if expected_parts == received_parts:
94
+ # All chunks received - assemble complete message
95
+ message_parts = [self.chunked_messages[topic][i] for i in sorted(self.chunked_messages[topic].keys())]
96
+ complete_message = ''.join(message_parts)
97
+ del self.chunked_messages[topic]
98
+
99
+ # Confirmation is now handled at the EasyCoder level
100
+ # print(f"All chunks received for topic {topic} ({len(complete_message)} bytes total).")
101
+ self.message = {"topic": topic, "payload": complete_message}
102
+
103
+ if self.onMessagePC is not None:
104
+ # print(f'Run from PC {self.onMessagePC}')
105
+ self.program.run(self.onMessagePC)
106
+ self.program.flushCB()
107
+ else:
108
+ missing = expected_parts - received_parts
109
+ print(f"Warning: Missing chunks {missing} for topic {topic}")
110
+ except (ValueError, IndexError):
111
+ pass
112
+ return
113
+
114
+ def getMessageTopic(self):
115
+ return self.message.topic # type: ignore
116
+
117
+ def getReceivedMessage(self):
118
+ return self.message
119
+
120
+ def onMessage(self, pc):
121
+ self.onMessagePC = pc
122
+
123
+ def sendMessage(self, topic, message, qos, chunk_size=0):
124
+ """Send a message, optionally chunked if chunk_size > 0
125
+
126
+ Stores transmission time in self.last_send_time (seconds)
127
+ """
128
+ send_start = time.time()
129
+
130
+ # Send message in chunks using selected strategy
131
+ message_len = len(message)
132
+ num_chunks = (message_len + chunk_size - 1) // chunk_size
133
+
134
+ # print(f'Sending message ({message_len} bytes) in {num_chunks} chunks of size {chunk_size} to topic {topic} with QoS {qos}')
135
+
136
+ self._send_rapid_fire(topic, message, qos, chunk_size, num_chunks)
137
+
138
+ self.last_send_time = time.time() - send_start
139
+ print(f'Message transmission complete in {self.last_send_time:.3f} seconds')
140
+
141
+ def _send_rapid_fire(self, topic, message, qos, chunk_size, num_chunks):
142
+ """Send all chunks as rapidly as possible, wait for single final confirmation"""
143
+ # print(f"Sending all {num_chunks} chunks as fast as possible...")
144
+
145
+ # Send all chunks rapidly
146
+ for i in range(num_chunks):
147
+ start = i * chunk_size
148
+ end = min(start + chunk_size, len(message))
149
+ chunk_data = message[start:end]
150
+
151
+ # Prepare chunk with header: "!part!<n> <total><data>" or "!last!<total><data>"
152
+ if i == num_chunks - 1:
153
+ chunk_msg = f"!last!{num_chunks} {chunk_data}"
154
+ else:
155
+ chunk_msg = f"!part!{i} {num_chunks} {chunk_data}"
156
+
157
+ # Send without waiting
158
+ self.client.publish(topic, chunk_msg, qos=qos)
159
+ # print(f"Sent chunk {i}/{num_chunks - 1} to topic {topic} with QoS {qos}: {chunk_msg}")
160
+ # No waiting here; confirmations are handled in EasyCoder using sender identity
161
+
162
+ # Start the MQTT client loop
163
+ def run(self):
164
+ self.client.connect(self.broker, int(self.port), 60)
165
+ self.client.loop_start()
166
+
167
+ ###############################################################################
168
+ # An MQTT topic
169
+ class ECTopic(ECObject):
170
+ def __init__(self):
171
+ super().__init__()
172
+
173
+ def getName(self):
174
+ v = self.getValue()
175
+ if v is None:
176
+ return ""
177
+ if v is None:
178
+ return ""
179
+ return v['name']
180
+
181
+ def getQoS(self):
182
+ v = self.getValue()
183
+ if v is None:
184
+ return 0
185
+ if v is None:
186
+ return 0
187
+ return int(v['qos'])
188
+
189
+ def textify(self):
190
+ v = self.getValue()
191
+ if v is None:
192
+ return ""
193
+ return f'{{"name": "{v["name"]}", "qos": {v["qos"]}}}'
194
+
195
+ ###############################################################################
196
+ ###############################################################################
197
+ # The MQTT compiler and runtime handlers
198
+ class MQTT(Handler):
199
+
200
+ def __init__(self, compiler):
201
+ Handler.__init__(self, compiler)
202
+ self.spoke = None
203
+
204
+ def getName(self):
205
+ return 'mqtt'
206
+
207
+ #############################################################################
208
+ # Keyword handlers
209
+
210
+ # init {topic} name {name} qos {qos}
211
+ def k_init(self, command):
212
+ if self.nextIsSymbol():
213
+ record = self.getSymbolRecord()
214
+ self.checkObjectType(record, ECTopic)
215
+ command['topic'] = record['name']
216
+ self.skip('name')
217
+ name =self.nextValue()
218
+ command['name'] = name
219
+ self.skip('qos')
220
+ command['qos'] = self.nextValue()
221
+ self.add(command)
222
+ return True
223
+ return False
224
+
225
+ def r_init(self, command):
226
+ record = self.getVariable(command['topic'])
227
+ topic = ECTopic()
228
+ value = {}
229
+ value['name'] = self.textify(command['name'])
230
+ value['qos'] = int(self.textify(command['qos']))
231
+ topic.setValue(value)
232
+ record['object'] = topic
233
+ return self.nextPC()
234
+
235
+ # mqtt id {clientID} broker {broker} port {port} topics {topic} [and {topic} ...]
236
+ def k_mqtt(self, command):
237
+ while True:
238
+ token = self.peek()
239
+ if token == 'id':
240
+ self.nextToken()
241
+ command['clientID'] = self.nextValue()
242
+ elif token == 'broker':
243
+ self.nextToken()
244
+ command['broker'] = self.nextValue()
245
+ elif token == 'port':
246
+ self.nextToken()
247
+ command['port'] = self.nextValue()
248
+ elif token == 'topics':
249
+ self.nextToken()
250
+ topics = []
251
+ while self.nextIsSymbol():
252
+ record = self.getSymbolRecord()
253
+ self.checkObjectType(record, ECTopic())
254
+ topics.append(record['name'])
255
+ if self.peek() == 'and': self.nextToken()
256
+ else:break
257
+ command['topics'] = topics
258
+ else:
259
+ self.add(command)
260
+ return True
261
+ return False
262
+
263
+ def r_mqtt(self, command):
264
+ if hasattr(self.program, 'mqttClient'):
265
+ raise RuntimeError(self.program, 'MQQT client already defined')
266
+ clientID = self.textify(command['clientID'])
267
+ broker = self.textify(command['broker'])
268
+ port = self.textify(command['port'])
269
+ topics = command['topics']
270
+ client = MQTTClient()
271
+ client.create(self.program, clientID, broker, port, topics)
272
+ client.run()
273
+ self.program.mqttClient = client
274
+ return self.nextPC()
275
+
276
+ # on mqtt message {action}
277
+ def k_on(self, command):
278
+ token = self.peek()
279
+ if token == 'mqtt':
280
+ self.nextToken()
281
+ event = self.nextToken()
282
+ if event in ['connect', 'message']:
283
+ command['event'] = event
284
+ self.nextToken()
285
+ command['goto'] = 0
286
+ self.add(command)
287
+ cmd = {}
288
+ cmd['domain'] = 'core'
289
+ cmd['lino'] = command['lino']
290
+ cmd['keyword'] = 'gotoPC'
291
+ cmd['goto'] = 0
292
+ cmd['debug'] = False
293
+ self.add(cmd)
294
+ # Add the action and a 'stop'
295
+ self.compileOne()
296
+ cmd = {}
297
+ cmd['domain'] = 'core'
298
+ cmd['lino'] = command['lino']
299
+ cmd['keyword'] = 'stop'
300
+ cmd['debug'] = False
301
+ self.add(cmd)
302
+ # Fixup the link
303
+ command['goto'] = self.getCodeSize()
304
+ return True
305
+ return False
306
+
307
+ def r_on(self, command):
308
+ event = command['event']
309
+ if event == 'connect':
310
+ self.program.mqttClient.onConnectPC = self.nextPC()+1
311
+ elif event == 'message':
312
+ self.program.mqttClient.onMessagePC = self.nextPC()+1
313
+ return command['goto']
314
+
315
+ # send {message} to {topic}
316
+ def k_send(self, command):
317
+ if self.nextIs('mqtt'):
318
+ command['message'] = self.nextValue()
319
+ self.skip('to')
320
+ if self.nextIsSymbol():
321
+ record = self.getSymbolRecord()
322
+ self.checkObjectType(record, MQTTClient)
323
+ command['to'] = record['name']
324
+ token = self.peek()
325
+ if token == 'with':
326
+ self.nextToken()
327
+ while True:
328
+ token = self.nextToken()
329
+ if token == 'qos':
330
+ command['qos'] = self.nextValue()
331
+ if self.peek() == 'and':
332
+ self.nextToken()
333
+ else:
334
+ break
335
+ self.add(command)
336
+ return True
337
+ return False
338
+
339
+ def r_send(self, command):
340
+ if not hasattr(self.program, 'mqttClient'):
341
+ raise RuntimeError(self.program, 'No MQTT client defined')
342
+ topic = self.getObject(self.getVariable(command['to']))
343
+ message = self.textify(command['message'])
344
+
345
+ # Validate that outgoing message is valid JSON with required 'sender' and 'action' fields
346
+ try:
347
+ payload_dict = json.loads(message)
348
+ if not isinstance(payload_dict, dict):
349
+ raise RuntimeError(self.program, f'MQTT message must be a JSON object, got {type(payload_dict).__name__}')
350
+
351
+ # sender can be a string (topic name) or a dict with 'name' and 'qos' keys
352
+ if 'sender' not in payload_dict:
353
+ raise RuntimeError(self.program, 'MQTT message must contain "sender" field')
354
+ sender_val = payload_dict.get('sender')
355
+ if isinstance(sender_val, str):
356
+ pass # sender is a string (topic name), valid
357
+ elif isinstance(sender_val, dict):
358
+ if 'name' not in sender_val or not isinstance(sender_val.get('name'), str):
359
+ raise RuntimeError(self.program, 'MQTT message "sender" dict must contain "name" field as string')
360
+ else:
361
+ raise RuntimeError(self.program, f'MQTT message "sender" must be a string or dict, got {type(sender_val).__name__}')
362
+
363
+ # action must be a string
364
+ if 'action' not in payload_dict or not isinstance(payload_dict.get('action'), str):
365
+ raise RuntimeError(self.program, 'MQTT message must contain "action" field as string')
366
+ except json.JSONDecodeError as e:
367
+ raise RuntimeError(self.program, f'MQTT message must be valid JSON: {e}')
368
+
369
+ if 'qos' in command:
370
+ qos = int(self.textify(command['qos']))
371
+ else:
372
+ qos = topic.qos if hasattr(topic, 'qos') else 1
373
+ self.program.mqttClient.sendMessage(topic.getName(), message, qos, chunk_size=1024)
374
+ if self.program.mqttClient.timeout:
375
+ return 0
376
+ return self.nextPC()
377
+
378
+ # Declare a topic variable
379
+ def k_topic(self, command):
380
+ self.compiler.addValueType()
381
+ return self.compileVariable(command, 'ECTopic')
382
+
383
+ def r_topic(self, command):
384
+ return self.nextPC()
385
+
386
+ #############################################################################
387
+ # Compile a value in this domain
388
+ def compileValue(self):
389
+ token = self.getToken()
390
+ if token == 'the':
391
+ token = self.nextToken()
392
+ if self.isSymbol():
393
+ record = self.getSymbolRecord()
394
+ object = self.getObject(record)
395
+ if isinstance(object, ECTopic):
396
+ return ECValue(domain=self.getName(), type='topic', content=record['name'])
397
+ else: return None
398
+ else:
399
+ if token == 'mqtt':
400
+ token = self.nextToken()
401
+ if token == 'message':
402
+ return ECValue(domain=self.getName(), type='mqtt', content=token)
403
+ # else:
404
+ # return self.getValue()
405
+ return None
406
+
407
+ #############################################################################
408
+ # Modify a value or leave it unchanged.
409
+ def modifyValue(self, value):
410
+ return value
411
+
412
+ #############################################################################
413
+ # Value handlers
414
+
415
+ def v_message(self, v):
416
+ return self.program.mqttClient.message
417
+
418
+ def v_mqtt(self, v):
419
+ content = v.getContent()
420
+ if content == 'message':
421
+ return self.program.mqttClient.message
422
+ return None
423
+
424
+ def v_topic(self, v):
425
+ topic = self.getObject(self.getVariable(self.textify(v.getContent())))
426
+ return f'{{"name": "{topic.getName()}", "qos": {topic.getQoS()}}}'
427
+
428
+ #############################################################################
429
+ # Compile a condition
430
+ def compileCondition(self):
431
+ condition = {}
432
+ return condition
433
+
434
+ #############################################################################
435
+ # Condition handlers
easycoder/ec_program.py CHANGED
@@ -1,6 +1,7 @@
1
- import time, sys, json
1
+ import time, sys, json, traceback
2
2
  from copy import deepcopy
3
3
  from collections import deque
4
+
4
5
  from .ec_classes import (
5
6
  Script,
6
7
  Token,
@@ -60,6 +61,7 @@ class Program:
60
61
  self.value = self.compiler.value
61
62
  self.condition = self.compiler.condition
62
63
  self.graphics = None
64
+ self.mqtt = None
63
65
  self.psutil = None
64
66
  self.useClass(Core)
65
67
  self.ticker = 0
@@ -93,7 +95,7 @@ class Program:
93
95
  f'{round((finishCompile - startCompile) * 1000)} ms')
94
96
  for name in self.symbols.keys():
95
97
  record = self.code[self.symbols[name]]
96
- if name[-1] != ':' and not 'used' in record:
98
+ if name[-1] != ':' and not record['used']:
97
99
  print(f'Variable "{name}" not used')
98
100
  else:
99
101
  print(f'Run {self.name}')
@@ -119,6 +121,15 @@ class Program:
119
121
  self.useClass(Graphics)
120
122
  return True
121
123
 
124
+ # Use the MQTT module
125
+ def useMQTT(self):
126
+ if self.mqtt == None:
127
+ print('Loading MQTT module')
128
+ from .ec_mqtt import MQTT
129
+ self.mqtt = MQTT
130
+ self.useClass(MQTT)
131
+ return True
132
+
122
133
  # Use the psutil module
123
134
  def usePSUtil(self):
124
135
  if self.psutil == None:
@@ -243,7 +254,7 @@ class Program:
243
254
  # Test if an item is a string or a number
244
255
  def getItemType(self, value):
245
256
  return int if isinstance(value, int) else str
246
-
257
+
247
258
  # Get the value of an item that may be an ECValue or a raw value. Return as an ECValue
248
259
  def getValueOf(self, item):
249
260
  value = ECValue()
@@ -261,6 +272,15 @@ class Program:
261
272
  elif varType == 'dict': value.setValue(type=dict, content=item)
262
273
  else: value.setValue(type=None, content=None)
263
274
  return value
275
+
276
+ # Get the value of an item from its domain handler
277
+ def textifyInDomain(self, value):
278
+ domainName = value.getDomain() # type: ignore
279
+ if domainName == None: domainName = 'core'
280
+ domain = self.domainIndex[domainName]
281
+ handler = domain.valueHandler(value.getType()) # type: ignore
282
+ result = handler(value) if handler else None
283
+ return result
264
284
 
265
285
  # Runtime function to evaluate an ECObject or ECValue. Returns another ECValue
266
286
  # This function may be called recursively by value handlers.
@@ -289,7 +309,7 @@ class Program:
289
309
 
290
310
  elif valType == 'symbol': # type: ignore
291
311
  # If it's a symbol, get its value
292
- record = self.getVariable(value.getContent()) # type: ignore
312
+ record = self.getVariable(value.getName()) # type: ignore
293
313
  if not 'object' in record: return None # type: ignore
294
314
  variable = self.getObject(record) # type: ignore
295
315
  result = variable.getValue() # type: ignore
@@ -316,12 +336,7 @@ class Program:
316
336
  result.setValue(type=str, content=content)
317
337
 
318
338
  else:
319
- # Call the given domain to handle a value
320
- domainName = value.getDomain() # type: ignore
321
- if domainName == None: domainName = 'core'
322
- domain = self.domainIndex[domainName]
323
- handler = domain.valueHandler(value.getType()) # type: ignore
324
- if handler: result = handler(value)
339
+ result = self.textifyInDomain(value)
325
340
 
326
341
  return result
327
342
 
@@ -334,7 +349,7 @@ class Program:
334
349
  if isinstance(value, dict):
335
350
  value = value['object']
336
351
  if isinstance(value, ECObject):
337
- value = value.getValue()
352
+ value = value.textify() # type: ignore
338
353
  if isinstance(value, ECValue): # type: ignore
339
354
  v = self.evaluate(value) # type: ignore
340
355
  else:
@@ -344,6 +359,8 @@ class Program:
344
359
  if v.getType() == 'object':
345
360
  return value.getContent() # type: ignore
346
361
  return v.getContent()
362
+ elif isinstance(v, ECObject):
363
+ return v.textify() # type: ignore
347
364
  if isinstance(v, (dict, list)):
348
365
  return json.dumps(v)
349
366
  return v
@@ -363,7 +380,6 @@ class Program:
363
380
  value = object.getValue() # type: ignore
364
381
  if value is None:
365
382
  raise NoValueRuntimeError(self, f'Symbol "{record["name"]}" has no value')
366
- # copy = deepcopy(value)
367
383
  copy = ECValue(domain=value.getDomain(),type=value.getType(),content=deepcopy(value.getContent()))
368
384
  return copy
369
385
 
@@ -478,7 +494,8 @@ class Program:
478
494
  pass # Place a breakpoint here for a debugger to catch
479
495
  self.pc = handler(command)
480
496
  except Exception as e:
481
- raise RuntimeError(self, f'Error during execution of {domainName}:{keyword}: {str(e)}')
497
+ tb = traceback.format_exc()
498
+ raise RuntimeError(self, f'Error during execution of {domainName}:{keyword}: {str(e)}\n\nTraceback:\n{tb}')
482
499
  # Deal with 'exit'
483
500
  if self.pc == -1:
484
501
  queue = deque()
easycoder/ec_value.py CHANGED
@@ -22,11 +22,11 @@ class Value:
22
22
 
23
23
  value = ECValue()
24
24
 
25
- if token == 'true':
25
+ if token.lower() == 'true':
26
26
  value.setValue(bool, True)
27
27
  return value
28
28
 
29
- if token == 'false':
29
+ if token.lower() == 'false':
30
30
  value.setValue(bool, False)
31
31
  return value
32
32
 
@@ -96,8 +96,9 @@ class Value:
96
96
  self.compiler.warning(f'ec_value.compileValue: Cannot get the value of "{token}"')
97
97
  return None
98
98
  if item.getType() == 'symbol':
99
- object = self.compiler.getSymbolRecord(item.getContent())['object']
99
+ object = self.compiler.getSymbolRecord(item.getName())['object']
100
100
  if not object.hasRuntimeValue(): return None
101
+ item.setContent(object.name)
101
102
 
102
103
  if self.peek() == 'cat':
103
104
  self.nextToken() # consume 'cat'
easycoder/pre/ec_core.py CHANGED
@@ -69,7 +69,7 @@ class Core(Handler):
69
69
  if not isinstance(self.getObject(record), ECVariable): return False
70
70
  # If 'giving' comes next, this variable is the second value
71
71
  if self.peek() == 'giving':
72
- v2 = ECValue(type='symbol', content=record['name'])
72
+ v2 = ECValue(type='symbol', name=record['name'])
73
73
  command['value2'] = v2
74
74
  self.nextToken()
75
75
  # Now get the target variable
@@ -1655,8 +1655,7 @@ class Core(Handler):
1655
1655
  self.checkObjectType(record, ECObject)
1656
1656
  # If 'giving' comes next, this variable is the second value
1657
1657
  if self.peek() == 'giving':
1658
- v2 = ECValue(type='symbol')
1659
- v2.setContent(record['name'])
1658
+ v2 = ECValue(type='symbol', name=record['name'])
1660
1659
  command['value2'] = v2
1661
1660
  self.nextToken()
1662
1661
  # Now get the target variable
@@ -1912,7 +1911,7 @@ class Core(Handler):
1912
1911
  value = ECValue()
1913
1912
  token = self.getToken()
1914
1913
  if self.isSymbol():
1915
- value.setValue(type='symbol', content=token)
1914
+ value.setValue(type='symbol', name=token)
1916
1915
  return value
1917
1916
 
1918
1917
  value.setType(token)
@@ -1946,13 +1945,13 @@ class Core(Handler):
1946
1945
  value.format = None
1947
1946
  return value
1948
1947
 
1949
- if token == 'element':
1948
+ if token == 'item':
1950
1949
  value.index = self.nextValue()
1951
1950
  if self.nextToken() == 'of':
1952
1951
  if self.nextIsSymbol():
1953
1952
  record = self.getSymbolRecord()
1954
1953
  self.checkObjectType(record['object'], ECList)
1955
- value.target = ECValue(type='symbol', content=record['name'])
1954
+ value.target = ECValue(type='symbol', name=record['name'])
1956
1955
  return value
1957
1956
  return None
1958
1957
 
@@ -2214,10 +2213,10 @@ class Core(Handler):
2214
2213
  value = v
2215
2214
  return value
2216
2215
 
2217
- def v_element(self, v):
2216
+ def v_item(self, v):
2218
2217
  index = self.textify(v.index)
2219
2218
  targetName = v.target
2220
- target = self.getVariable(targetName.getContent())
2219
+ target = self.getVariable(targetName.getName())
2221
2220
  variable = self.getObject(target)
2222
2221
  self.checkObjectType(variable, ECList)
2223
2222
  content = variable.getContent()
@@ -2656,7 +2655,7 @@ class Core(Handler):
2656
2655
 
2657
2656
  def c_empty(self, condition):
2658
2657
  if condition.value1.getType() == 'symbol':
2659
- record = self.getVariable(condition.value1.content)
2658
+ record = self.getVariable(condition.value1.name)
2660
2659
  variable = self.getObject(record)
2661
2660
  if isinstance(variable, (ECList, ECDictionary)):
2662
2661
  comparison = variable.isEmpty()
@@ -289,7 +289,7 @@ class Program:
289
289
 
290
290
  elif valType == 'symbol': # type: ignore
291
291
  # If it's a symbol, get its value
292
- record = self.getVariable(value.getContent()) # type: ignore
292
+ record = self.getVariable(value.getName()) # type: ignore
293
293
  if not 'object' in record: return None # type: ignore
294
294
  variable = self.getObject(record) # type: ignore
295
295
  result = variable.getValue() # type: ignore
easycoder/pre/ec_value.py CHANGED
@@ -96,7 +96,7 @@ class Value:
96
96
  self.compiler.warning(f'ec_value.compileValue: Cannot get the value of "{token}"')
97
97
  return None
98
98
  if item.getType() == 'symbol':
99
- object = self.compiler.getSymbolRecord(item.getContent())['object']
99
+ object = self.compiler.getSymbolRecord(item.getName())['object']
100
100
  if not object.hasRuntimeValue(): return None
101
101
 
102
102
  if self.peek() == 'cat':
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: easycoder
3
- Version: 260110.1
3
+ Version: 260118.3
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>
@@ -12,6 +12,7 @@ Requires-Dist: requests
12
12
  Requires-Dist: psutil
13
13
  Requires-Dist: paramiko
14
14
  Requires-Dist: pyside6
15
+ Requires-Dist: paho-mqtt
15
16
  Project-URL: Home, https://github.com/easycoder/easycoder-py
16
17
 
17
18
  # Introduction