easypy-lang 2.0.0__py3-none-any.whl → 2.0.2__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.
@@ -9,6 +9,15 @@ import time
9
9
  import json
10
10
  import threading
11
11
 
12
+ # Global Aliases for Easypy -> Python compatibility
13
+ true = True
14
+ false = False
15
+ null = None
16
+
17
+ # Built-in String Helpers
18
+ def upper(s): return str(s).upper()
19
+ def lower(s): return str(s).lower()
20
+
12
21
  # ==================== GUI (Tkinter) ====================
13
22
  class _GUIImpl:
14
23
  def __init__(self):
easypy_lang/transpiler.py CHANGED
@@ -8,6 +8,7 @@ import re
8
8
  class EasypyTranspiler:
9
9
  def __init__(self):
10
10
  self.indent_level = 0
11
+ self.context_stack = [] # Stack to track [class, func, other]
11
12
  self.in_block = False
12
13
  self.source_map = {} # Maps generated python line -> original ep line
13
14
 
@@ -154,16 +155,82 @@ class EasypyTranspiler:
154
155
 
155
156
  return "".join(normalized)
156
157
 
158
+ def _clean_expression(self, text):
159
+ """Helper to convert C-style syntax to Python"""
160
+ # Operators
161
+ text = text.replace("===", "==")
162
+ text = text.replace("&&", " and ")
163
+ text = text.replace("||", " or ")
164
+
165
+ # '!' but not '!='
166
+ # Only replace if preceded by start-of-line, space, '(', '[', or '='
167
+ # This prevents replacing '!' inside strings like "Hello!"
168
+ text = re.sub(r'(?:^|(?<=[=\s(\[]))!(?!=)', 'not ', text)
169
+
170
+ # 'true'/'false'/'null' are handled by modules_real.py aliases at runtime,
171
+ # but replacing them here can be safer for some edge cases.
172
+ # But let's stick to the aliases to avoid accidental string replacement.
173
+
174
+ return text
175
+
176
+ def _preprocess_line(self, line):
177
+ """Handle 'Advanced' syntax: --->, if =, implicit f-strings"""
178
+
179
+ # 1. implicit f-strings for print/log
180
+ # Detect if {var} pattern exists inside quotes
181
+ if ('print(' in line or 'log ' in line) and '{' in line and '}' in line:
182
+ # Simple heuristic: inject 'f' before quotes
183
+ # Handle double quotes
184
+ line = re.sub(r'print\(\s*"', 'print(f"', line)
185
+ line = re.sub(r'log\s+"', 'log f"', line)
186
+ # Handle single quotes
187
+ line = re.sub(r"print\(\s*'", "print(f'", line)
188
+ line = re.sub(r"log\s+'", "log f'", line)
189
+
190
+ # 2. Syntax Sugar: Arrow
191
+ if "--->" in line:
192
+ line = line.replace("--->", ":")
193
+
194
+ # 3. Syntax Sugar: if =, else if =, else =
195
+ line = re.sub(r'^(\s*)if\s*=\s*', r'\1if ', line)
196
+ line = re.sub(r'^(\s*)else\s*if\s*=\s*', r'\1elif ', line)
197
+ # Fix: else = statement needs a colon for inline usage
198
+ line = re.sub(r'^(\s*)else\s*=\s*', r'\1else: ', line)
199
+
200
+ # 4. Handle "else if" without equals
201
+ line = line.replace("else if", "elif")
202
+
203
+ # 5. Loop syntax conversion (loop N times)
204
+ # Supports both "loop 3 times:" and "loop 3 times {"
205
+ # We replace the start with pythonic loop
206
+ loop_match = re.match(r'^(\s*)loop\s+(\d+)\s+times', line)
207
+ if loop_match:
208
+ indent = loop_match.group(1)
209
+ count = loop_match.group(2)
210
+ # Replace 'loop N times' with 'for _ in range(N)'
211
+ # We leave the suffix (colon or brace) for the next step
212
+ line = re.sub(r'loop\s+\d+\s+times', f'for _ in range({count})', line, count=1)
213
+
214
+ return line
215
+
157
216
  def _process_line(self, raw_line):
158
217
  # Indentation handling (Closing Brace)
159
218
  # Handle cases like "}" or "} else {" or "}}"
219
+ closing_braces = ""
160
220
  while raw_line.startswith("}"):
221
+ ctx = self.context_stack[-1] if self.context_stack else 'block'
222
+ if ctx == 'dict':
223
+ closing_braces += "}"
224
+
161
225
  self.indent_level -= 1
162
226
  if self.indent_level < 0: self.indent_level = 0
227
+ if self.context_stack: self.context_stack.pop()
163
228
  raw_line = raw_line[1:].strip()
164
229
 
165
- # If line was just "}", it is now empty. Return None to skip outputting a blank/garbage line.
230
+ # If line was just "}", it is now empty.
166
231
  if not raw_line:
232
+ if closing_braces:
233
+ return f"{' ' * self.indent_level}{closing_braces}"
167
234
  return None
168
235
 
169
236
  # Prepare current indentation string
@@ -173,6 +240,13 @@ class EasypyTranspiler:
173
240
  if raw_line.startswith('#') or raw_line.startswith('//'):
174
241
  return f"{indent}#{raw_line}"
175
242
 
243
+ # === PREPROCESSOR ===
244
+ # Handle "Advanced" syntax (Arrow, if=, etc)
245
+ raw_line = self._preprocess_line(raw_line)
246
+
247
+ # Apply basic expression cleanup (&& -> and)
248
+ raw_line = self._clean_expression(raw_line)
249
+
176
250
  # === TRANSLATION RULES ===
177
251
 
178
252
  # 1. Imports
@@ -212,19 +286,34 @@ class EasypyTranspiler:
212
286
  # 3. Functions
213
287
  if raw_line.startswith("func ") and raw_line.endswith("{"):
214
288
  defn = raw_line[5:-1].strip()
289
+
290
+ # Auto-inject 'self' if inside a class
291
+ if self.context_stack and self.context_stack[-1] == 'class':
292
+ parts = defn.split("(", 1)
293
+ name = parts[0]
294
+ args = parts[1][:-1] if len(parts) > 1 else ""
295
+
296
+ if args.strip():
297
+ defn = f"{name}(self, {args})"
298
+ else:
299
+ defn = f"{name}(self)"
300
+
215
301
  self.indent_level += 1
302
+ self.context_stack.append('func')
216
303
  return f"{indent}def {defn}:"
217
304
 
218
305
  # 3b. Async Functions
219
306
  if raw_line.startswith("async func ") and raw_line.endswith("{"):
220
307
  defn = raw_line[11:-1].strip()
221
308
  self.indent_level += 1
309
+ self.context_stack.append('func')
222
310
  return f"{indent}async def {defn}:"
223
311
 
224
312
  # 4. Classes
225
313
  if raw_line.startswith("class ") and raw_line.endswith("{"):
226
314
  defn = raw_line[6:-1].strip()
227
315
  self.indent_level += 1
316
+ self.context_stack.append('class')
228
317
  return f"{indent}class {defn}:"
229
318
 
230
319
  # 5. Logic
@@ -232,40 +321,68 @@ class EasypyTranspiler:
232
321
  condition = raw_line[2:-1].strip()
233
322
  # Handle if(x) vs if x
234
323
  if condition.startswith("(") and condition.endswith(")"): condition = condition
324
+ condition = self._clean_expression(condition)
235
325
  self.indent_level += 1
326
+ self.context_stack.append('block')
236
327
  return f"{indent}if {condition}:"
237
328
 
238
329
  if raw_line.startswith("elif") and raw_line.endswith("{"):
239
330
  condition = raw_line[4:-1].strip()
331
+ condition = self._clean_expression(condition)
240
332
  self.indent_level += 1
333
+ self.context_stack.append('block')
241
334
  return f"{indent}elif {condition}:"
242
335
 
243
336
  if raw_line.startswith("else") and raw_line.endswith("{"):
244
337
  self.indent_level += 1
338
+ self.context_stack.append('block')
245
339
  return f"{indent}else:"
246
340
 
247
341
  # 6. Loops
248
- loop_match = re.match(r'loop (\d+) times\s*\{', raw_line)
249
- if loop_match:
250
- count = loop_match.group(1)
342
+ # (Handled by preprocessor now, but we keep this for block context tracking if needed)
343
+ if raw_line.startswith("for ") and " range(" in raw_line and (raw_line.endswith("{") or raw_line.endswith(":")):
251
344
  self.indent_level += 1
252
- return f"{indent}for _ in range({count}):"
345
+ self.context_stack.append('block')
346
+ # If it ends with {, strip it for python
347
+ if raw_line.endswith("{"):
348
+ return f"{indent}{raw_line[:-1].strip()}:"
349
+ return f"{indent}{raw_line}"
253
350
 
254
- if raw_line.startswith("while") and raw_line.endswith("{"):
255
- condition = raw_line[5:-1].strip()
351
+ if raw_line.startswith("while") and (raw_line.endswith("{") or raw_line.endswith(":")):
352
+ clean_line = raw_line[:-1].strip() if raw_line.endswith("{") else raw_line[:-1].strip()
256
353
  self.indent_level += 1
257
- return f"{indent}while {condition}:"
354
+ self.context_stack.append('block')
355
+ return f"{indent}{clean_line}:"
258
356
 
259
- if raw_line.startswith("for ") and raw_line.endswith("{"):
260
- condition = raw_line[3:-1].strip()
357
+ if raw_line.startswith("for ") and (raw_line.endswith("{") or raw_line.endswith(":")):
358
+ clean_line = raw_line[:-1].strip() if raw_line.endswith("{") else raw_line[:-1].strip()
261
359
  self.indent_level += 1
262
- return f"{indent}for {condition}:"
360
+ self.context_stack.append('block')
361
+ return f"{indent}{clean_line}:"
263
362
 
264
363
  # 7. Generic Block
265
364
  if raw_line.endswith("{"):
266
365
  clean_line = raw_line[:-1].strip()
366
+ clean_line = self._clean_expression(clean_line)
367
+
368
+ # CRITICAL FIX: Detect Dictionary vs Code Block
369
+ # If it is an assignment (x = {), keep the brace, do strict Python dict open.
370
+ # Regex checks for "var =" or "word =" pattern at end of line
371
+ # But NOT "if x == {" (comparison)
372
+ # Simple heuristic: If it has "=" and not " if " or " while ", it's likely data.
373
+ is_assignment = re.search(r'[^=!<>]=[^=]', clean_line) or clean_line.endswith("=")
374
+
267
375
  self.indent_level += 1
268
- return f"{indent}{clean_line}:"
376
+ if is_assignment:
377
+ # It's a dict! Keep the brace.
378
+ self.context_stack.append('dict')
379
+ return f"{indent}{clean_line} {{"
380
+ else:
381
+ # It's a code block! Use colon.
382
+ self.context_stack.append('block')
383
+ return f"{indent}{clean_line}:"
269
384
 
270
385
  # 8. Standard Line
386
+ # Apply cleanup to standard lines too (assignments, expressions)
387
+ # raw_line = self._clean_expression(raw_line) # Already done in preprocess
271
388
  return f"{indent}{raw_line}"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: easypy-lang
3
- Version: 2.0.0
3
+ Version: 2.0.2
4
4
  Summary: A simple, powerful language for AI, ML, and apps - No coding experience needed!
5
5
  Home-page: https://github.com/VadikGoel/easypy-lang
6
6
  Author: Vadik Goel
@@ -41,12 +41,24 @@ Whether you want to build websites, analyze data, or create AI chatbots, Easypy
41
41
 
42
42
  ## 📦 Installation
43
43
 
44
- To get started, simply install the package via pip:
44
+ ### Option 1: Install via Pip (Recommended)
45
+ Simply install the package via pip:
45
46
 
46
47
  ```bash
47
48
  pip install easypy-lang
48
49
  ```
49
50
 
51
+ ### Option 2: Run via Docker (No Install Needed)
52
+ Don't want to install Python? Run Easypy directly in a container:
53
+
54
+ ```bash
55
+ # Pull the image
56
+ docker pull vadikgoel/easypy-lang
57
+
58
+ # Run interactive mode
59
+ docker run -it vadikgoel/easypy-lang
60
+ ```
61
+
50
62
  ## ✨ Features
51
63
 
52
64
  - **English-like Syntax**: Write code that reads like a story.
@@ -80,7 +92,7 @@ easypy hello.easy
80
92
 
81
93
  ## 📚 Documentation
82
94
 
83
- For full documentation, tutorials, and the interactive playground, please check the `docs/` folder included in the repository or visit our [GitHub Repository](https://github.com/VadikGoel/easypy-lang).
95
+ For full documentation, tutorials, and the interactive playground, please check the `docs/` folder included in the repository or visit our [Documentation](https://vadikgoel.github.io/easypy-lang/).
84
96
 
85
97
  ## 🤝 Contributing
86
98
 
@@ -0,0 +1,12 @@
1
+ easypy_lang/__init__.py,sha256=8RyTILsaMBOfR1mgK9hRKOvYz8lkwAUdX9HQILNR5LU,218
2
+ easypy_lang/cli.py,sha256=THGJ23wEK4sG0R-fSVGesNa0wsYp0-WIj4NK_-uWBBA,15490
3
+ easypy_lang/engine.py,sha256=mEvSKb_gcGvlb4iBtrQFFc6Pns73-Gb3HiGKWqLLTes,26608
4
+ easypy_lang/epi.py,sha256=W3FQtq__GujGofQDwBAqsbPGS-1bCyFSnriqnaP_V4A,1683
5
+ easypy_lang/modules.py,sha256=uuyrx7_GY8V64rgBPrLTksjUAB3q0V_PI3aGZySVGwQ,14403
6
+ easypy_lang/modules_real.py,sha256=IcZMcVafF3aozmPZYp5YXPoIJzh6oV-BX-28G6TDAQA,3588
7
+ easypy_lang/transpiler.py,sha256=0WJYN5PxML0y2yg7TUjDswKJ7-vvcKirxR_0Jwjj8UY,16393
8
+ easypy_lang-2.0.2.dist-info/METADATA,sha256=hfNPY9gCQe5aJyZe8X-IdCi7Uk8-ZBzI-DSRMPlANuM,3180
9
+ easypy_lang-2.0.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
10
+ easypy_lang-2.0.2.dist-info/entry_points.txt,sha256=mKKDQahcErG53etDO_npP9h4Oxvw4s7buWdl9R6BMX4,76
11
+ easypy_lang-2.0.2.dist-info/top_level.txt,sha256=MEflM_hPiCkdi0AemcYPHUMvUOoFQhNylmfb92x_FZw,12
12
+ easypy_lang-2.0.2.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.10.2)
2
+ Generator: setuptools (80.9.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,12 +0,0 @@
1
- easypy_lang/__init__.py,sha256=8RyTILsaMBOfR1mgK9hRKOvYz8lkwAUdX9HQILNR5LU,218
2
- easypy_lang/cli.py,sha256=THGJ23wEK4sG0R-fSVGesNa0wsYp0-WIj4NK_-uWBBA,15490
3
- easypy_lang/engine.py,sha256=mEvSKb_gcGvlb4iBtrQFFc6Pns73-Gb3HiGKWqLLTes,26608
4
- easypy_lang/epi.py,sha256=W3FQtq__GujGofQDwBAqsbPGS-1bCyFSnriqnaP_V4A,1683
5
- easypy_lang/modules.py,sha256=uuyrx7_GY8V64rgBPrLTksjUAB3q0V_PI3aGZySVGwQ,14403
6
- easypy_lang/modules_real.py,sha256=Zchg6xmTUoThHl45Ae1qLQPaPtXvYTSovG8LxbBvVM8,3389
7
- easypy_lang/transpiler.py,sha256=3V1p7sNi3zH3-2a1gXbP9HytfEx0CrtGWJB9IyPc4kA,10813
8
- easypy_lang-2.0.0.dist-info/METADATA,sha256=d_mFxZwyDUVCEqFpNApMBNmBu0sUPRR6RYVSZxlXjPc,2903
9
- easypy_lang-2.0.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
10
- easypy_lang-2.0.0.dist-info/entry_points.txt,sha256=mKKDQahcErG53etDO_npP9h4Oxvw4s7buWdl9R6BMX4,76
11
- easypy_lang-2.0.0.dist-info/top_level.txt,sha256=MEflM_hPiCkdi0AemcYPHUMvUOoFQhNylmfb92x_FZw,12
12
- easypy_lang-2.0.0.dist-info/RECORD,,