easypy-lang 2.0.1__py3-none-any.whl → 2.1.0__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):
@@ -111,3 +120,144 @@ class _DiscordImpl:
111
120
  return {"token": token}
112
121
 
113
122
  discord_bot = _DiscordImpl()
123
+
124
+ # ==================== ML (Machine Learning - Sklearn Wrapper) ====================
125
+ class _MLImpl:
126
+ def model(self, type="classifier"):
127
+ try:
128
+ from sklearn.neighbors import KNeighborsClassifier
129
+ from sklearn.linear_model import LinearRegression
130
+
131
+ if type == "classifier":
132
+ print("🔹 Created KNN Classifier")
133
+ return _SimpleModel(KNeighborsClassifier(n_neighbors=3))
134
+ elif type == "regression":
135
+ print("🔹 Created Linear Regression Model")
136
+ return _SimpleModel(LinearRegression())
137
+ except ImportError:
138
+ print("❌ Error: 'scikit-learn' not installed.")
139
+ return None
140
+
141
+ class _SimpleModel:
142
+ def __init__(self, internal):
143
+ self.model = internal
144
+ self.trained = False
145
+
146
+ def train(self, inputs, outputs):
147
+ try:
148
+ self.model.fit(inputs, outputs)
149
+ self.trained = True
150
+ print("✅ Model trained!")
151
+ except Exception as e:
152
+ print(f"❌ Training Failed: {e}")
153
+
154
+ def predict(self, input_data):
155
+ if not self.trained:
156
+ print("⚠️ Model not trained yet!")
157
+ return None
158
+ return self.model.predict([input_data])[0]
159
+
160
+ ml = _MLImpl()
161
+
162
+ # ==================== AI (Simple NLP/Chat) ====================
163
+ class _AIImpl:
164
+ def ask(self, prompt):
165
+ # Placeholder for actual LLM or API
166
+ responses = {
167
+ "hello": "Hello there! How can I help?",
168
+ "easypy": "Easypy is the easiest language ever!",
169
+ "time": f"The time is {time.strftime('%H:%M')}"
170
+ }
171
+ for k, v in responses.items():
172
+ if k in prompt.lower():
173
+ return v
174
+ return f"User asked: '{prompt}' (AI Backend not connected)"
175
+
176
+ ai = _AIImpl()
177
+
178
+ # ==================== GAME (Turtle Graphics) ====================
179
+ class _GameImpl:
180
+ def window(self, width=600, height=600, title="Easypy Game"):
181
+ import turtle
182
+ s = turtle.Screen()
183
+ s.setup(width, height)
184
+ s.title(title)
185
+ s.bgcolor("black")
186
+ return s
187
+
188
+ def player(self, shape="turtle", color="lime"):
189
+ import turtle
190
+ p = turtle.Turtle()
191
+ p.shape(shape)
192
+ p.color(color)
193
+ p.speed(0)
194
+ return p
195
+
196
+ game = _GameImpl()
197
+
198
+ # Helper for implicit 'ml_classifier' in examples often seen
199
+ def ml_classifier(): return ml.model("classifier")
200
+
201
+ # ==================== DATA (Database & Storage) ====================
202
+ class _DBImpl:
203
+ def sqlite(self, path="database.db"):
204
+ import sqlite3
205
+ try:
206
+ conn = sqlite3.connect(path)
207
+ print(f"✓ Connected to SQLite: {path}")
208
+ return _SQLiteConn(conn)
209
+ except Exception as e:
210
+ print(f"❌ Connection Failed: {e}")
211
+ return None
212
+
213
+ def save_json(self, path, data):
214
+ try:
215
+ with open(path, 'w') as f:
216
+ json.dump(data, f, indent=4)
217
+ print(f"✓ Data saved to {path}")
218
+ except Exception as e:
219
+ print(f"❌ Save JSON Failed: {e}")
220
+
221
+ def load_json(self, path):
222
+ try:
223
+ if not os.path.exists(path):
224
+ return {}
225
+ with open(path, 'r') as f:
226
+ return json.load(f)
227
+ except Exception as e:
228
+ print(f"❌ Load JSON Failed: {e}")
229
+ return None
230
+
231
+ class _SQLiteConn:
232
+ def __init__(self, conn):
233
+ self.conn = conn
234
+ self.cursor = conn.cursor()
235
+
236
+ def execute(self, query, params=()):
237
+ try:
238
+ self.cursor.execute(query, params)
239
+ self.conn.commit()
240
+ return self.cursor.fetchall()
241
+ except Exception as e:
242
+ print(f"❌ SQL Error: {e}")
243
+ return []
244
+
245
+ def close(self):
246
+ self.conn.close()
247
+
248
+ db = _DBImpl()
249
+
250
+ # ==================== DATE & TIME ====================
251
+ class _DateImpl:
252
+ def now(self):
253
+ return time.strftime("%Y-%m-%d %H:%M:%S")
254
+
255
+ def today(self):
256
+ return time.strftime("%Y-%m-%d")
257
+
258
+ def timestamp(self):
259
+ return time.time()
260
+
261
+ datetime = _DateImpl()
262
+
263
+
easypy_lang/transpiler.py CHANGED
@@ -155,17 +155,97 @@ class EasypyTranspiler:
155
155
 
156
156
  return "".join(normalized)
157
157
 
158
+ def _clean_expression(self, text):
159
+ """Helper to convert C-style syntax to Python"""
160
+ # Protect strings from replacement
161
+ # placeholders mapping: string_content -> placeholder
162
+ placeholders = {}
163
+ def replace_str(match):
164
+ key = f"__STR_{len(placeholders)}__"
165
+ placeholders[key] = match.group(0)
166
+ return key
167
+
168
+ # Temporarily replace strings with placeholders
169
+ # Regex for double and single quotes
170
+ text_safe = re.sub(r'("[^"]*"|\'[^\']*\')', replace_str, text)
171
+
172
+ # Operators
173
+ text_safe = text_safe.replace("===", "==")
174
+ text_safe = text_safe.replace("&&", " and ")
175
+ text_safe = text_safe.replace("||", " or ")
176
+
177
+ # '!' but not '!='
178
+ # Only replace if preceded by start-of-line, space, '(', '[', or '='
179
+ text_safe = re.sub(r'(?:^|(?<=[=\s(\[]))!(?!=)', 'not ', text_safe)
180
+
181
+ # Restore strings
182
+ for key, val in placeholders.items():
183
+ text_safe = text_safe.replace(key, val)
184
+
185
+ return text_safe
186
+ # but replacing them here can be safer for some edge cases.
187
+ # But let's stick to the aliases to avoid accidental string replacement.
188
+
189
+ return text
190
+
191
+ def _preprocess_line(self, line):
192
+ """Handle 'Advanced' syntax: --->, if =, implicit f-strings"""
193
+
194
+ # 1. implicit f-strings for print/log
195
+ # Detect if {var} pattern exists inside quotes
196
+ if ('print(' in line or 'log ' in line) and '{' in line and '}' in line:
197
+ # Simple heuristic: inject 'f' before quotes
198
+ # Handle double quotes
199
+ line = re.sub(r'print\(\s*"', 'print(f"', line)
200
+ line = re.sub(r'log\s+"', 'log f"', line)
201
+ # Handle single quotes
202
+ line = re.sub(r"print\(\s*'", "print(f'", line)
203
+ line = re.sub(r"log\s+'", "log f'", line)
204
+
205
+ # 2. Syntax Sugar: Arrow
206
+ if "--->" in line:
207
+ line = line.replace("--->", ":")
208
+
209
+ # 3. Syntax Sugar: if =, else if =, else =
210
+ line = re.sub(r'^(\s*)if\s*=\s*', r'\1if ', line)
211
+ line = re.sub(r'^(\s*)else\s*if\s*=\s*', r'\1elif ', line)
212
+ # Fix: else = statement needs a colon for inline usage
213
+ line = re.sub(r'^(\s*)else\s*=\s*', r'\1else: ', line)
214
+
215
+ # 4. Handle "else if" without equals
216
+ line = line.replace("else if", "elif")
217
+
218
+ # 5. Loop syntax conversion (loop N times)
219
+ # Supports both "loop 3 times:" and "loop 3 times {"
220
+ # We replace the start with pythonic loop
221
+ loop_match = re.match(r'^(\s*)loop\s+(\d+)\s+times', line)
222
+ if loop_match:
223
+ indent = loop_match.group(1)
224
+ count = loop_match.group(2)
225
+ # Replace 'loop N times' with 'for _ in range(N)'
226
+ # We leave the suffix (colon or brace) for the next step
227
+ line = re.sub(r'loop\s+\d+\s+times', f'for _ in range({count})', line, count=1)
228
+
229
+ return line
230
+
158
231
  def _process_line(self, raw_line):
159
232
  # Indentation handling (Closing Brace)
160
233
  # Handle cases like "}" or "} else {" or "}}"
234
+ closing_braces = ""
161
235
  while raw_line.startswith("}"):
236
+ ctx = self.context_stack[-1] if self.context_stack else 'block'
237
+ if ctx == 'dict':
238
+ closing_braces += "}"
239
+
162
240
  self.indent_level -= 1
163
241
  if self.indent_level < 0: self.indent_level = 0
164
242
  if self.context_stack: self.context_stack.pop()
165
243
  raw_line = raw_line[1:].strip()
166
244
 
167
- # If line was just "}", it is now empty. Return None to skip outputting a blank/garbage line.
245
+ # If line was just "}", it is now empty.
168
246
  if not raw_line:
247
+ if closing_braces:
248
+ return f"{' ' * self.indent_level}{closing_braces}"
169
249
  return None
170
250
 
171
251
  # Prepare current indentation string
@@ -175,6 +255,13 @@ class EasypyTranspiler:
175
255
  if raw_line.startswith('#') or raw_line.startswith('//'):
176
256
  return f"{indent}#{raw_line}"
177
257
 
258
+ # === PREPROCESSOR ===
259
+ # Handle "Advanced" syntax (Arrow, if=, etc)
260
+ raw_line = self._preprocess_line(raw_line)
261
+
262
+ # Apply basic expression cleanup (&& -> and)
263
+ raw_line = self._clean_expression(raw_line)
264
+
178
265
  # === TRANSLATION RULES ===
179
266
 
180
267
  # 1. Imports
@@ -183,7 +270,7 @@ class EasypyTranspiler:
183
270
  if len(parts) >= 2:
184
271
  mod = parts[1]
185
272
  # These are actually pre-loaded objects in modules_real.py
186
- preloaded_objects = ["gui", "file", "web", "system", "discord_bot", "api"]
273
+ preloaded_objects = ["gui", "file", "web", "system", "discord_bot", "api", "ml", "ai", "game", "db", "datetime"]
187
274
 
188
275
  if mod in preloaded_objects:
189
276
  return f"{indent}# Module '{mod}' is pre-loaded"
@@ -233,6 +320,18 @@ class EasypyTranspiler:
233
320
  # 3b. Async Functions
234
321
  if raw_line.startswith("async func ") and raw_line.endswith("{"):
235
322
  defn = raw_line[11:-1].strip()
323
+
324
+ # Auto-inject 'self' if inside a class
325
+ if self.context_stack and self.context_stack[-1] == 'class':
326
+ parts = defn.split("(", 1)
327
+ name = parts[0]
328
+ args = parts[1][:-1] if len(parts) > 1 else ""
329
+
330
+ if args.strip():
331
+ defn = f"{name}(self, {args})"
332
+ else:
333
+ defn = f"{name}(self)"
334
+
236
335
  self.indent_level += 1
237
336
  self.context_stack.append('func')
238
337
  return f"{indent}async def {defn}:"
@@ -249,12 +348,14 @@ class EasypyTranspiler:
249
348
  condition = raw_line[2:-1].strip()
250
349
  # Handle if(x) vs if x
251
350
  if condition.startswith("(") and condition.endswith(")"): condition = condition
351
+ condition = self._clean_expression(condition)
252
352
  self.indent_level += 1
253
353
  self.context_stack.append('block')
254
354
  return f"{indent}if {condition}:"
255
355
 
256
356
  if raw_line.startswith("elif") and raw_line.endswith("{"):
257
357
  condition = raw_line[4:-1].strip()
358
+ condition = self._clean_expression(condition)
258
359
  self.indent_level += 1
259
360
  self.context_stack.append('block')
260
361
  return f"{indent}elif {condition}:"
@@ -265,31 +366,57 @@ class EasypyTranspiler:
265
366
  return f"{indent}else:"
266
367
 
267
368
  # 6. Loops
268
- loop_match = re.match(r'loop (\d+) times\s*\{', raw_line)
269
- if loop_match:
270
- count = loop_match.group(1)
369
+ # (Handled by preprocessor now, but we keep this for block context tracking if needed)
370
+ if raw_line.startswith("for ") and " range(" in raw_line and (raw_line.endswith("{") or raw_line.endswith(":")):
271
371
  self.indent_level += 1
272
372
  self.context_stack.append('block')
273
- return f"{indent}for _ in range({count}):"
373
+ # If it ends with {, strip it for python
374
+ if raw_line.endswith("{"):
375
+ return f"{indent}{raw_line[:-1].strip()}:"
376
+ return f"{indent}{raw_line}"
274
377
 
275
- if raw_line.startswith("while") and raw_line.endswith("{"):
276
- condition = raw_line[5:-1].strip()
378
+ if raw_line.startswith("while") and (raw_line.endswith("{") or raw_line.endswith(":")):
379
+ clean_line = raw_line[:-1].strip() if raw_line.endswith("{") else raw_line[:-1].strip()
277
380
  self.indent_level += 1
278
381
  self.context_stack.append('block')
279
- return f"{indent}while {condition}:"
382
+ return f"{indent}{clean_line}:"
280
383
 
281
- if raw_line.startswith("for ") and raw_line.endswith("{"):
282
- condition = raw_line[3:-1].strip()
384
+ if raw_line.startswith("for ") and (raw_line.endswith("{") or raw_line.endswith(":")):
385
+ clean_line = raw_line[:-1].strip() if raw_line.endswith("{") else raw_line[:-1].strip()
283
386
  self.indent_level += 1
284
387
  self.context_stack.append('block')
285
- return f"{indent}for {condition}:"
388
+ return f"{indent}{clean_line}:"
286
389
 
287
390
  # 7. Generic Block
288
391
  if raw_line.endswith("{"):
289
392
  clean_line = raw_line[:-1].strip()
393
+ clean_line = self._clean_expression(clean_line)
394
+
395
+ # CRITICAL FIX: Detect Dictionary vs Code Block
396
+ # If it is an assignment (x = {), keep the brace, do strict Python dict open.
397
+ # Regex checks for "var =" or "word =" pattern at end of line
398
+ # But NOT "if x == {" (comparison)
399
+
400
+ # Also check for nested dictionary structure: "key": {
401
+ is_dict_entry = bool(re.search(r'[\w"\']+\s*:\s*$', clean_line)) # Ends with colon
402
+
403
+ is_assignment = re.search(r'[^=!<>]=[^=]', clean_line) or clean_line.endswith("=")
404
+
405
+ # Check if current context is 'dict' (nested)
406
+ in_dict_context = self.context_stack and self.context_stack[-1] == 'dict'
407
+
290
408
  self.indent_level += 1
291
- self.context_stack.append('block')
292
- return f"{indent}{clean_line}:"
409
+
410
+ if is_assignment or (in_dict_context and is_dict_entry):
411
+ # It's a dict! Keep the brace.
412
+ self.context_stack.append('dict')
413
+ return f"{indent}{clean_line} {{"
414
+ else:
415
+ # It's a code block! Use colon.
416
+ self.context_stack.append('block')
417
+ return f"{indent}{clean_line}:"
293
418
 
294
419
  # 8. Standard Line
420
+ # Apply cleanup to standard lines too (assignments, expressions)
421
+ # raw_line = self._clean_expression(raw_line) # Already done in preprocess
295
422
  return f"{indent}{raw_line}"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: easypy-lang
3
- Version: 2.0.1
3
+ Version: 2.1.0
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
@@ -66,7 +66,21 @@ docker run -it vadikgoel/easypy-lang
66
66
  - **Python Compatible**: Runs on top of the robust Python ecosystem.
67
67
  - **Zero Configuration**: No complex build tools or compilers needed.
68
68
 
69
- ## 🚀 Quick Start
69
+ ## Modules (Batteries Included)
70
+
71
+ | Module | Purpose | Example |
72
+ |--------|---------|---------|
73
+ | `gui` | Desktop Apps | `gui.create_app("My App")` |
74
+ | `ml` | Machine Learning | `ml.model("classifier")` |
75
+ | `ai` | AI Helpers | `ai.ask("Hello")` |
76
+ | `db` | Database | `db.sqlite("my.db")` |
77
+ | `game` | 2D Games | `game.window()` |
78
+ | `web` | Internet | `web.get("https://google.com")` |
79
+ | `file` | File I/O | `file.write("test.txt", "Hi")` |
80
+ | `datetime`| Date & Time | `datetime.now()` |
81
+ | `discord` | Bots | `class MyBot(discord.Client)` |
82
+
83
+ ## �🚀 Quick Start
70
84
 
71
85
  Create a file named `hello.easy` and write your first program:
72
86
 
@@ -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=hpecfv95fzr9sGaFXBn9pE4TP9qQUsbBJC2xus9htII,7909
7
+ easypy_lang/transpiler.py,sha256=UoRMVTdch4HROORP1I3DMnSdxSV3MO8kNUhhDeqTNqQ,17722
8
+ easypy_lang-2.1.0.dist-info/METADATA,sha256=x1lHmttq-DTS_LYW1IaMtXB2iyI_4w7vdQ37nXC1Qjg,3741
9
+ easypy_lang-2.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
10
+ easypy_lang-2.1.0.dist-info/entry_points.txt,sha256=mKKDQahcErG53etDO_npP9h4Oxvw4s7buWdl9R6BMX4,76
11
+ easypy_lang-2.1.0.dist-info/top_level.txt,sha256=MEflM_hPiCkdi0AemcYPHUMvUOoFQhNylmfb92x_FZw,12
12
+ easypy_lang-2.1.0.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=8EtYsjigGKjMPS_dUOaNaijM-H-uVkHa8hAeCm3pvps,11878
8
- easypy_lang-2.0.1.dist-info/METADATA,sha256=nnVzgSVa2qTgHeK7j23ADG6_2SoyrSfAm_z-7ctASLY,3180
9
- easypy_lang-2.0.1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
10
- easypy_lang-2.0.1.dist-info/entry_points.txt,sha256=mKKDQahcErG53etDO_npP9h4Oxvw4s7buWdl9R6BMX4,76
11
- easypy_lang-2.0.1.dist-info/top_level.txt,sha256=MEflM_hPiCkdi0AemcYPHUMvUOoFQhNylmfb92x_FZw,12
12
- easypy_lang-2.0.1.dist-info/RECORD,,