epy-lang 1.0.0__tar.gz

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.
epy_lang-1.0.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Sk Wasim
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21
+ DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,94 @@
1
+ Metadata-Version: 2.4
2
+ Name: epy-lang
3
+ Version: 1.0.0
4
+ Summary: The English-to-AST Python Language Compiler
5
+ Author: skwasimin
6
+ License: MIT License
7
+
8
+ Copyright (c) 2026 Sk Wasim
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26
+ DEALINGS IN THE SOFTWARE.
27
+
28
+ Project-URL: Homepage, https://github.com/skwasimin/epy
29
+ Project-URL: Repository, https://github.com/skwasimin/epy
30
+ Project-URL: Issues, https://github.com/skwasimin/epy/issues
31
+ Classifier: Programming Language :: Python :: 3
32
+ Classifier: Programming Language :: Python :: 3.9
33
+ Classifier: Programming Language :: Python :: 3.10
34
+ Classifier: Programming Language :: Python :: 3.11
35
+ Classifier: Programming Language :: Python :: 3.12
36
+ Classifier: Operating System :: OS Independent
37
+ Classifier: Topic :: Software Development :: Compilers
38
+ Requires-Python: >=3.9
39
+ Description-Content-Type: text/markdown
40
+ License-File: LICENSE
41
+ Dynamic: license-file
42
+
43
+ # EPY: The English-to-AST Python Language
44
+
45
+ [![CI](https://github.com/skwasimin/epy/actions/workflows/run.yml/badge.svg)](https://github.com/skwasimin/epy/actions/workflows/run.yml)
46
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
47
+ [![Python: 3.9+](https://img.shields.io/badge/python-3.9+-blue.svg)](https://www.python.org/downloads/)
48
+
49
+ EPY is an enterprise-ready, plain-English programming syntax that compiles directly into Python's native Abstract Syntax Tree (AST).
50
+
51
+ It removes whitespace-sensitive indent traps and esoteric operator boilerplate, delivering 100% compliant, readable Python code with zero runtime overhead.
52
+
53
+ ```epy
54
+ define class MicroServer:
55
+ define method init with port:
56
+ set self's port to port
57
+ end method
58
+
59
+ define async function start:
60
+ show "Server bound to port " + convert self's port to text
61
+ while true:
62
+ await handle_traffic()
63
+ end while
64
+ end function
65
+ end class
66
+ ```
67
+
68
+ ## Quickstart
69
+
70
+ **Local Installation:**
71
+ ```bash
72
+ git clone https://github.com/skwasimin/epy.git
73
+ cd epy
74
+ pip install -e .
75
+ ```
76
+
77
+ Compile an `.epy` file into a `.py` file:
78
+ ```bash
79
+ epy app.epy
80
+ ```
81
+
82
+ Compile and run immediately:
83
+ ```bash
84
+ epy app.epy --run
85
+ ```
86
+
87
+ ## Language Reference
88
+ See [SYNTAX.md](SYNTAX.md) for full grammatical rules and examples.
89
+
90
+ ## Contributing
91
+ See [CONTRIBUTING.md](CONTRIBUTING.md) and [ARCHITECTURE.md](ARCHITECTURE.md) for code submission guidelines.
92
+
93
+ ## License
94
+ MIT License.
@@ -0,0 +1,52 @@
1
+ # EPY: The English-to-AST Python Language
2
+
3
+ [![CI](https://github.com/skwasimin/epy/actions/workflows/run.yml/badge.svg)](https://github.com/skwasimin/epy/actions/workflows/run.yml)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+ [![Python: 3.9+](https://img.shields.io/badge/python-3.9+-blue.svg)](https://www.python.org/downloads/)
6
+
7
+ EPY is an enterprise-ready, plain-English programming syntax that compiles directly into Python's native Abstract Syntax Tree (AST).
8
+
9
+ It removes whitespace-sensitive indent traps and esoteric operator boilerplate, delivering 100% compliant, readable Python code with zero runtime overhead.
10
+
11
+ ```epy
12
+ define class MicroServer:
13
+ define method init with port:
14
+ set self's port to port
15
+ end method
16
+
17
+ define async function start:
18
+ show "Server bound to port " + convert self's port to text
19
+ while true:
20
+ await handle_traffic()
21
+ end while
22
+ end function
23
+ end class
24
+ ```
25
+
26
+ ## Quickstart
27
+
28
+ **Local Installation:**
29
+ ```bash
30
+ git clone https://github.com/skwasimin/epy.git
31
+ cd epy
32
+ pip install -e .
33
+ ```
34
+
35
+ Compile an `.epy` file into a `.py` file:
36
+ ```bash
37
+ epy app.epy
38
+ ```
39
+
40
+ Compile and run immediately:
41
+ ```bash
42
+ epy app.epy --run
43
+ ```
44
+
45
+ ## Language Reference
46
+ See [SYNTAX.md](SYNTAX.md) for full grammatical rules and examples.
47
+
48
+ ## Contributing
49
+ See [CONTRIBUTING.md](CONTRIBUTING.md) and [ARCHITECTURE.md](ARCHITECTURE.md) for code submission guidelines.
50
+
51
+ ## License
52
+ MIT License.
@@ -0,0 +1,94 @@
1
+ Metadata-Version: 2.4
2
+ Name: epy-lang
3
+ Version: 1.0.0
4
+ Summary: The English-to-AST Python Language Compiler
5
+ Author: skwasimin
6
+ License: MIT License
7
+
8
+ Copyright (c) 2026 Sk Wasim
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26
+ DEALINGS IN THE SOFTWARE.
27
+
28
+ Project-URL: Homepage, https://github.com/skwasimin/epy
29
+ Project-URL: Repository, https://github.com/skwasimin/epy
30
+ Project-URL: Issues, https://github.com/skwasimin/epy/issues
31
+ Classifier: Programming Language :: Python :: 3
32
+ Classifier: Programming Language :: Python :: 3.9
33
+ Classifier: Programming Language :: Python :: 3.10
34
+ Classifier: Programming Language :: Python :: 3.11
35
+ Classifier: Programming Language :: Python :: 3.12
36
+ Classifier: Operating System :: OS Independent
37
+ Classifier: Topic :: Software Development :: Compilers
38
+ Requires-Python: >=3.9
39
+ Description-Content-Type: text/markdown
40
+ License-File: LICENSE
41
+ Dynamic: license-file
42
+
43
+ # EPY: The English-to-AST Python Language
44
+
45
+ [![CI](https://github.com/skwasimin/epy/actions/workflows/run.yml/badge.svg)](https://github.com/skwasimin/epy/actions/workflows/run.yml)
46
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
47
+ [![Python: 3.9+](https://img.shields.io/badge/python-3.9+-blue.svg)](https://www.python.org/downloads/)
48
+
49
+ EPY is an enterprise-ready, plain-English programming syntax that compiles directly into Python's native Abstract Syntax Tree (AST).
50
+
51
+ It removes whitespace-sensitive indent traps and esoteric operator boilerplate, delivering 100% compliant, readable Python code with zero runtime overhead.
52
+
53
+ ```epy
54
+ define class MicroServer:
55
+ define method init with port:
56
+ set self's port to port
57
+ end method
58
+
59
+ define async function start:
60
+ show "Server bound to port " + convert self's port to text
61
+ while true:
62
+ await handle_traffic()
63
+ end while
64
+ end function
65
+ end class
66
+ ```
67
+
68
+ ## Quickstart
69
+
70
+ **Local Installation:**
71
+ ```bash
72
+ git clone https://github.com/skwasimin/epy.git
73
+ cd epy
74
+ pip install -e .
75
+ ```
76
+
77
+ Compile an `.epy` file into a `.py` file:
78
+ ```bash
79
+ epy app.epy
80
+ ```
81
+
82
+ Compile and run immediately:
83
+ ```bash
84
+ epy app.epy --run
85
+ ```
86
+
87
+ ## Language Reference
88
+ See [SYNTAX.md](SYNTAX.md) for full grammatical rules and examples.
89
+
90
+ ## Contributing
91
+ See [CONTRIBUTING.md](CONTRIBUTING.md) and [ARCHITECTURE.md](ARCHITECTURE.md) for code submission guidelines.
92
+
93
+ ## License
94
+ MIT License.
@@ -0,0 +1,9 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ transpiler.py
5
+ epy_lang.egg-info/PKG-INFO
6
+ epy_lang.egg-info/SOURCES.txt
7
+ epy_lang.egg-info/dependency_links.txt
8
+ epy_lang.egg-info/entry_points.txt
9
+ epy_lang.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ epy = transpiler:main
@@ -0,0 +1 @@
1
+ transpiler
@@ -0,0 +1,33 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "epy-lang"
7
+ version = "1.0.0"
8
+ description = "The English-to-AST Python Language Compiler"
9
+ readme = "README.md"
10
+ authors = [{ name = "skwasimin" }]
11
+ license = { file = "LICENSE" }
12
+ classifiers = [
13
+ "Programming Language :: Python :: 3",
14
+ "Programming Language :: Python :: 3.9",
15
+ "Programming Language :: Python :: 3.10",
16
+ "Programming Language :: Python :: 3.11",
17
+ "Programming Language :: Python :: 3.12",
18
+ "Operating System :: OS Independent",
19
+ "Topic :: Software Development :: Compilers"
20
+ ]
21
+ requires-python = ">=3.9"
22
+
23
+ [project.scripts]
24
+ epy = "transpiler:main"
25
+
26
+ [project.urls]
27
+ Homepage = "https://github.com/skwasimin/epy"
28
+ Repository = "https://github.com/skwasimin/epy"
29
+ Issues = "https://github.com/skwasimin/epy/issues"
30
+
31
+ [tool.setuptools]
32
+ py-modules = ["transpiler"]
33
+
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,330 @@
1
+ #!/usr/bin/env python3
2
+ import ast
3
+ import re
4
+ import sys
5
+ import subprocess
6
+ from pathlib import Path
7
+
8
+ class EpySyntaxError(Exception): pass
9
+
10
+ QUOTE_RE = re.compile(r'("[^"]*"|\'[^\']*\')')
11
+
12
+ def mask_quotes(line: str):
13
+ quotes = []
14
+ def _stash(m):
15
+ quotes.append(m.group(0))
16
+ return f"__Q{len(quotes) - 1}__"
17
+ return QUOTE_RE.sub(_stash, line), quotes
18
+
19
+ def unmask_quotes(line: str, quotes: list):
20
+ for i, q in enumerate(quotes):
21
+ line = line.replace(f"__Q{i}__", q)
22
+ return line
23
+
24
+ PHRASE_REPLACEMENTS = [
25
+ (r"\blength of (\w+)\b", r"len(\1)"),
26
+ (r"\buppercase of (\w+)\b", r"\1.upper()"),
27
+ (r"\blowercase of (\w+)\b", r"\1.lower()"),
28
+ (r"\bitem at index (\w+) in (\w+)\b", r"\2[\1]"),
29
+ (r"\bvalue of (\w+) in (\w+)\b", r'\2["\1"]'),
30
+ (r"\bconvert (\w+) to text\b", r"str(\1)"),
31
+ (r"\bconvert (\w+) to a number\b", r"float(\1)"),
32
+ (r"\bthe error\b", "e"),
33
+ (r"\bthe remainder of (.+?) divided by (.+?)\b", r"(\1 % \2)"),
34
+ (r"\b(.+?) to the power of (.+?)\b", r"(\1 ** \2)"),
35
+ (r"\b(.+?) divided by (.+?) rounding down\b", r"(\1 // \2)"),
36
+ (r"\b(.+?) divided by (.+?)\b", r"(\1 / \2)"),
37
+ (r"\bthe sum of (\w+)\b", r"sum(\1)"),
38
+ (r"\bthe maximum of (\w+)\b", r"max(\1)"),
39
+ (r"\bthe minimum of (\w+)\b", r"min(\1)"),
40
+ (r"\bthe average of (\w+)\b", r"(sum(\1) / len(\1))"),
41
+ (r"\bthe type of (\w+)\b", r"type(\1)"),
42
+ (r"\bround (\w+) to (\w+) decimal places\b", r"round(\1, \2)"),
43
+ (r"\bround (\w+)\b", r"round(\1)"),
44
+ (r"\bis not equal to\b", "!="),
45
+ (r"\bis equal to\b", "=="),
46
+ (r"\bis greater than or equal to\b", ">="),
47
+ (r"\bis less than or equal to\b", "<="),
48
+ (r"\bis at least\b", ">="),
49
+ (r"\bis at most\b", "<="),
50
+ (r"\bis greater than\b", ">"),
51
+ (r"\bis less than\b", "<"),
52
+ (r"\bis not in\b", "not in"),
53
+ (r"\bis in\b", "in"),
54
+ (r"\btrue\b", "True"),
55
+ (r"\bfalse\b", "False"),
56
+ (r"\bnothing\b", "None"),
57
+ (r"(\w+)'s (\w+)", r"\1.\2")
58
+ ]
59
+
60
+ def translate_expr(expr: str) -> str:
61
+ masked, quotes = mask_quotes(expr)
62
+ for pattern, repl in PHRASE_REPLACEMENTS:
63
+ masked = re.sub(pattern, repl, masked, flags=re.IGNORECASE)
64
+ return unmask_quotes(masked, quotes)
65
+
66
+ def make_expr_node(expr: str) -> ast.expr:
67
+ try: return ast.parse(translate_expr(expr.strip()), mode='eval').body
68
+ except SyntaxError as e: raise EpySyntaxError(f"Cannot parse expression '{expr}': {e}")
69
+
70
+ def make_stmt_node(stmt: str) -> ast.stmt:
71
+ try:
72
+ parsed = ast.parse(translate_expr(stmt.strip()), mode='exec')
73
+ return parsed.body[0] if parsed.body else ast.Pass()
74
+ except SyntaxError as e: raise EpySyntaxError(f"Cannot parse statement '{stmt}': {e}")
75
+
76
+ def parse_dict_pairs(text: str) -> tuple:
77
+ keys, values = [], []
78
+ for p in [x.strip() for x in text.split(",") if x.strip()]:
79
+ m = re.match(r"(\w+) as (.+)", p, re.IGNORECASE)
80
+ if m:
81
+ keys.append(ast.Constant(value=m.group(1)))
82
+ values.append(make_expr_node(m.group(2)))
83
+ else: raise EpySyntaxError(f"Invalid dict element '{p}'")
84
+ return keys, values
85
+
86
+ def build_ast(source_text: str) -> ast.Module:
87
+ root = ast.Module(body=[], type_ignores=[])
88
+ stack, block_types, try_stack, if_stack, pending_dec = [root.body], ["module"], [], [], []
89
+
90
+ for line_no, raw_line in enumerate(source_text.splitlines(), start=1):
91
+ line = raw_line.strip()
92
+ if not line or line.startswith("#"): continue
93
+
94
+ try:
95
+ m_deco = re.match(r"^apply decorator (.+)$", line, re.IGNORECASE)
96
+ if m_deco: pending_dec.append(make_expr_node(m_deco.group(1))); continue
97
+
98
+ m_imp = re.match(r"^use library ([\w\.]+)(?: as (\w+))?$", line, re.IGNORECASE)
99
+ if m_imp: stack[-1].append(ast.Import(names=[ast.alias(name=m_imp.group(1), asname=m_imp.group(2))])); continue
100
+
101
+ m_from = re.match(r"^from library ([\w\.]+) use (.+)$", line, re.IGNORECASE)
102
+ if m_from:
103
+ aliases = [ast.alias(name=n.strip(), asname=None) for n in m_from.group(2).split(",") if n.strip()]
104
+ stack[-1].append(ast.ImportFrom(module=m_from.group(1), names=aliases, level=0))
105
+ continue
106
+
107
+ m_cls = re.match(r"^define class (\w+)(?: based on (\w+))?:$", line, re.IGNORECASE)
108
+ if m_cls:
109
+ bases = [ast.Name(id=m_cls.group(2), ctx=ast.Load())] if m_cls.group(2) else []
110
+ node = ast.ClassDef(name=m_cls.group(1), bases=bases, keywords=[], body=[], decorator_list=pending_dec, type_params=[])
111
+ pending_dec = []; stack[-1].append(node); stack.append(node.body); block_types.append("class"); continue
112
+
113
+ m_func = re.match(r"^define (async function|function|method) (\w+)(?: with (.+))?:$", line, re.IGNORECASE)
114
+ if m_func:
115
+ kind, f_name, f_args = m_func.group(1).lower(), m_func.group(2), m_func.group(3) or ""
116
+ if f_name.lower() == "init": f_name = "__init__"
117
+ arg_names = [a.strip() for a in f_args.split(",") if a.strip()]
118
+ if kind == "method": arg_names.insert(0, "self")
119
+ args_node = ast.arguments(posonlyargs=[], args=[ast.arg(arg=a) for a in arg_names], kwonlyargs=[], kw_defaults=[], defaults=[])
120
+ node_cls = ast.AsyncFunctionDef if "async" in kind else ast.FunctionDef
121
+ node = node_cls(name=f_name, args=args_node, body=[], decorator_list=pending_dec, returns=None, type_params=[])
122
+ pending_dec = []; stack[-1].append(node); stack.append(node.body); block_types.append(kind); continue
123
+
124
+ m_if = re.match(r"^if (.+):$", line, re.IGNORECASE)
125
+ if m_if:
126
+ node = ast.If(test=make_expr_node(m_if.group(1)), body=[], orelse=[])
127
+ stack[-1].append(node); stack.append(node.body); block_types.append("if"); if_stack.append(node); continue
128
+
129
+ m_elif = re.match(r"^otherwise if (.+):$", line, re.IGNORECASE)
130
+ if m_elif:
131
+ node = ast.If(test=make_expr_node(m_elif.group(1)), body=[], orelse=[])
132
+ if_stack[-1].orelse.append(node); stack.pop(); stack.append(node.body); if_stack[-1] = node; continue
133
+
134
+ m_else = re.match(r"^else:$", line, re.IGNORECASE)
135
+ if m_else: stack.pop(); stack.append(if_stack[-1].orelse); continue
136
+
137
+ m_rep = re.match(r"^repeat (.+) times:$", line, re.IGNORECASE)
138
+ if m_rep:
139
+ range_call = ast.Call(func=ast.Name(id="range", ctx=ast.Load()), args=[make_expr_node(m_rep.group(1))], keywords=[])
140
+ node = ast.For(target=ast.Name(id="_", ctx=ast.Store()), iter=range_call, body=[], orelse=[])
141
+ stack[-1].append(node); stack.append(node.body); block_types.append("repeat"); continue
142
+
143
+ m_while = re.match(r"^while (.+):$", line, re.IGNORECASE)
144
+ if m_while:
145
+ node = ast.While(test=make_expr_node(m_while.group(1)), body=[], orelse=[])
146
+ stack[-1].append(node); stack.append(node.body); block_types.append("while"); continue
147
+
148
+ m_for = re.match(r"^for each (\w+) in (.+):$", line, re.IGNORECASE)
149
+ if m_for:
150
+ node = ast.For(target=ast.Name(id=m_for.group(1), ctx=ast.Store()), iter=make_expr_node(m_for.group(2)), body=[], orelse=[])
151
+ stack[-1].append(node); stack.append(node.body); block_types.append("for"); continue
152
+
153
+ m_file = re.match(r"^open file (.+) for (reading|writing|appending) as (\w+):$", line, re.IGNORECASE)
154
+ if m_file:
155
+ mode_map = {"reading": "r", "writing": "w", "appending": "a"}
156
+ open_call = ast.Call(func=ast.Name(id="open", ctx=ast.Load()), args=[make_expr_node(m_file.group(1)), ast.Constant(value=mode_map[m_file.group(2).lower()])], keywords=[])
157
+ node = ast.With(items=[ast.withitem(context_expr=open_call, optional_vars=ast.Name(id=m_file.group(3), ctx=ast.Store()))], body=[])
158
+ stack[-1].append(node); stack.append(node.body); block_types.append("file"); continue
159
+
160
+ if re.match(r"^try:$", line, re.IGNORECASE):
161
+ node = ast.Try(body=[], handlers=[], orelse=[], finalbody=[])
162
+ stack[-1].append(node); stack.append(node.body); block_types.append("try"); try_stack.append(node); continue
163
+
164
+ m_except = re.match(r"^if that fails(?: with (\w+))?:$", line, re.IGNORECASE)
165
+ if m_except:
166
+ exc_type = ast.Name(id=m_except.group(1), ctx=ast.Load()) if m_except.group(1) else None
167
+ handler = ast.ExceptHandler(type=exc_type, name="e", body=[])
168
+ try_stack[-1].handlers.append(handler); stack.pop(); stack.append(handler.body); block_types[-1] = "except"; continue
169
+
170
+ if re.match(r"^finally:$", line, re.IGNORECASE):
171
+ stack.pop(); stack.append(try_stack[-1].finalbody); block_types[-1] = "finally"; continue
172
+
173
+ m_end = re.match(r"^end (if|while|repeat|for|function|async function|method|class|try|file)$", line, re.IGNORECASE)
174
+ if m_end:
175
+ expected = m_end.group(1).lower()
176
+ stack.pop(); current = block_types.pop()
177
+ if expected == "try" and current in ["except", "finally"]: try_stack.pop()
178
+ if expected == "if": if_stack.pop()
179
+ continue
180
+
181
+ m_await_store = re.match(r"^await (.+) and store in (\w+)$", line, re.IGNORECASE)
182
+ if m_await_store:
183
+ node = ast.Assign(targets=[ast.Name(id=m_await_store.group(2), ctx=ast.Store())], value=ast.Await(value=make_expr_node(m_await_store.group(1))))
184
+ stack[-1].append(node); continue
185
+
186
+ m_await_call = re.match(r"^await (.+)$", line, re.IGNORECASE)
187
+ if m_await_call:
188
+ stack[-1].append(ast.Expr(value=ast.Await(value=make_expr_node(m_await_call.group(1))))); continue
189
+
190
+ m_yield = re.match(r"^yield (.+)$", line, re.IGNORECASE)
191
+ if m_yield: stack[-1].append(ast.Expr(value=ast.Yield(value=make_expr_node(m_yield.group(1))))); continue
192
+
193
+ m_ret = re.match(r"^return (.+)$", line, re.IGNORECASE)
194
+ if m_ret: stack[-1].append(ast.Return(value=make_expr_node(m_ret.group(1)))); continue
195
+
196
+ if re.match(r"^return$", line, re.IGNORECASE): stack[-1].append(ast.Return(value=None)); continue
197
+
198
+ # --- Modifications & Attributes (RESTORED FIX) ---
199
+ m_inc = re.match(r"^increase (.+) by (.+)$", line, re.IGNORECASE)
200
+ if m_inc:
201
+ node = ast.AugAssign(target=make_expr_node(m_inc.group(1)), op=ast.Add(), value=make_expr_node(m_inc.group(2)))
202
+ stack[-1].append(node); continue
203
+
204
+ m_dec = re.match(r"^decrease (.+) by (.+)$", line, re.IGNORECASE)
205
+ if m_dec:
206
+ node = ast.AugAssign(target=make_expr_node(m_dec.group(1)), op=ast.Sub(), value=make_expr_node(m_dec.group(2)))
207
+ stack[-1].append(node); continue
208
+
209
+ m_set_val = re.match(r"^set value of (\w+) in (\w+) to (.+)$", line, re.IGNORECASE)
210
+ if m_set_val:
211
+ node = ast.Assign(
212
+ targets=[ast.Subscript(value=ast.Name(id=m_set_val.group(2), ctx=ast.Load()), slice=ast.Constant(value=m_set_val.group(1)), ctx=ast.Store())],
213
+ value=make_expr_node(m_set_val.group(3))
214
+ )
215
+ stack[-1].append(node); continue
216
+
217
+ m_set_attr = re.match(r"^set (\w+)'s (\w+) to (.+)$", line, re.IGNORECASE)
218
+ if m_set_attr:
219
+ node = ast.Assign(
220
+ targets=[ast.Attribute(value=ast.Name(id=m_set_attr.group(1), ctx=ast.Load()), attr=m_set_attr.group(2), ctx=ast.Store())],
221
+ value=make_expr_node(m_set_attr.group(3))
222
+ )
223
+ stack[-1].append(node); continue
224
+
225
+ # --- Collections (RESTORED FIX) ---
226
+ m_list = re.match(r"^create list (\w+) with (.+)$", line, re.IGNORECASE)
227
+ if m_list:
228
+ elts = [make_expr_node(e.strip()) for e in m_list.group(2).split(",") if e.strip()]
229
+ node = ast.Assign(targets=[ast.Name(id=m_list.group(1), ctx=ast.Store())], value=ast.List(elts=elts, ctx=ast.Load()))
230
+ stack[-1].append(node); continue
231
+
232
+ m_list_empty = re.match(r"^create empty list (\w+)$", line, re.IGNORECASE)
233
+ if m_list_empty:
234
+ node = ast.Assign(targets=[ast.Name(id=m_list_empty.group(1), ctx=ast.Store())], value=ast.List(elts=[], ctx=ast.Load()))
235
+ stack[-1].append(node); continue
236
+
237
+ m_dict = re.match(r"^create dictionary (\w+) with (.+)$", line, re.IGNORECASE)
238
+ if m_dict:
239
+ keys, vals = parse_dict_pairs(m_dict.group(2))
240
+ node = ast.Assign(targets=[ast.Name(id=m_dict.group(1), ctx=ast.Store())], value=ast.Dict(keys=keys, values=vals))
241
+ stack[-1].append(node); continue
242
+
243
+ m_add = re.match(r"^add (.+) to (\w+)$", line, re.IGNORECASE)
244
+ if m_add:
245
+ node = ast.Expr(value=ast.Call(func=ast.Attribute(value=ast.Name(id=m_add.group(2), ctx=ast.Load()), attr="append", ctx=ast.Load()), args=[make_expr_node(m_add.group(1))], keywords=[]))
246
+ stack[-1].append(node); continue
247
+
248
+ m_rem = re.match(r"^remove (.+) from (\w+)$", line, re.IGNORECASE)
249
+ if m_rem:
250
+ node = ast.Expr(value=ast.Call(func=ast.Attribute(value=ast.Name(id=m_rem.group(2), ctx=ast.Load()), attr="remove", ctx=ast.Load()), args=[make_expr_node(m_rem.group(1))], keywords=[]))
251
+ stack[-1].append(node); continue
252
+
253
+ m_sort = re.match(r"^sort (\w+)$", line, re.IGNORECASE)
254
+ if m_sort:
255
+ node = ast.Expr(value=ast.Call(func=ast.Attribute(value=ast.Name(id=m_sort.group(1), ctx=ast.Load()), attr="sort", ctx=ast.Load()), args=[], keywords=[]))
256
+ stack[-1].append(node); continue
257
+
258
+ # --- Standard Operations ---
259
+ m_store_multi = re.match(r"^store (.+) in ((?:\w+\s*,\s*)+\w+)$", line, re.IGNORECASE)
260
+ if m_store_multi:
261
+ targets = [ast.Name(id=t.strip(), ctx=ast.Store()) for t in m_store_multi.group(2).split(",")]
262
+ stack[-1].append(ast.Assign(targets=[ast.Tuple(elts=targets, ctx=ast.Store())], value=make_expr_node(m_store_multi.group(1)))); continue
263
+
264
+ m_store = re.match(r"^store (.+) in (\w+)$", line, re.IGNORECASE)
265
+ if m_store: stack[-1].append(ast.Assign(targets=[ast.Name(id=m_store.group(2), ctx=ast.Store())], value=make_expr_node(m_store.group(1)))); continue
266
+
267
+ m_let = re.match(r"^let (\w+) be (.+)$", line, re.IGNORECASE)
268
+ if m_let: stack[-1].append(ast.Assign(targets=[ast.Name(id=m_let.group(1), ctx=ast.Store())], value=make_expr_node(m_let.group(2)))); continue
269
+
270
+ m_new = re.match(r"^create (\w+) as new (\w+)(?: with (.+))?$", line, re.IGNORECASE)
271
+ if m_new:
272
+ args = [make_expr_node(a.strip()) for a in m_new.group(3).split(",") if a.strip()] if m_new.group(3) else []
273
+ node = ast.Assign(targets=[ast.Name(id=m_new.group(1), ctx=ast.Store())], value=ast.Call(func=ast.Name(id=m_new.group(2), ctx=ast.Load()), args=args, keywords=[]))
274
+ stack[-1].append(node); continue
275
+
276
+ m_show = re.match(r"^show (.+)$", line, re.IGNORECASE)
277
+ if m_show:
278
+ stack[-1].append(ast.Expr(value=ast.Call(func=ast.Name(id="print", ctx=ast.Load()), args=[make_expr_node(m_show.group(1))], keywords=[]))); continue
279
+
280
+ m_meth_store = re.match(r"^call (\w+)'s (\w+)(?: with (.+))? and store in (\w+)$", line, re.IGNORECASE)
281
+ if m_meth_store:
282
+ args = [make_expr_node(a.strip()) for a in m_meth_store.group(3).split(",") if a.strip()] if m_meth_store.group(3) else []
283
+ call = ast.Call(func=ast.Attribute(value=ast.Name(id=m_meth_store.group(1), ctx=ast.Load()), attr=m_meth_store.group(2), ctx=ast.Load()), args=args, keywords=[])
284
+ stack[-1].append(ast.Assign(targets=[ast.Name(id=m_meth_store.group(4), ctx=ast.Store())], value=call)); continue
285
+
286
+ m_meth = re.match(r"^call (\w+)'s (\w+)(?: with (.+))?$", line, re.IGNORECASE)
287
+ if m_meth:
288
+ args = [make_expr_node(a.strip()) for a in m_meth.group(3).split(",") if a.strip()] if m_meth.group(3) else []
289
+ call = ast.Call(func=ast.Attribute(value=ast.Name(id=m_meth.group(1), ctx=ast.Load()), attr=m_meth.group(2), ctx=ast.Load()), args=args, keywords=[])
290
+ stack[-1].append(ast.Expr(value=call)); continue
291
+
292
+ m_fn_store = re.match(r"^call ([\w\.]+)(?: with (.+))? and store in (\w+)$", line, re.IGNORECASE)
293
+ if m_fn_store:
294
+ args_code = translate_expr(m_fn_store.group(2) or "")
295
+ stack[-1].append(ast.Assign(targets=[ast.Name(id=m_fn_store.group(3), ctx=ast.Store())], value=make_expr_node(f"{m_fn_store.group(1)}({args_code})"))); continue
296
+
297
+ m_fn = re.match(r"^call ([\w\.]+)(?: with (.+))?$", line, re.IGNORECASE)
298
+ if m_fn:
299
+ args_code = translate_expr(m_fn.group(2) or "")
300
+ stack[-1].append(ast.Expr(value=make_expr_node(f"{m_fn.group(1)}({args_code})"))); continue
301
+
302
+ stack[-1].append(make_stmt_node(line))
303
+
304
+ except Exception as e: raise EpySyntaxError(f"Error on line {line_no} ('{line}'): {str(e)}")
305
+
306
+ if len(stack) > 1: raise EpySyntaxError(f"Unclosed block detected: Missing 'end {block_types[-1]}'.")
307
+ ast.fix_missing_locations(root)
308
+ return root
309
+
310
+ def compile_source(source_text: str) -> str:
311
+ return ast.unparse(build_ast(source_text))
312
+
313
+ def main():
314
+ if len(sys.argv) < 2:
315
+ print("EPY Compiler v1.0.0\nUsage: epy <file.epy> [--run]")
316
+ sys.exit(1)
317
+
318
+ src_path = Path(sys.argv[1])
319
+ py_path = src_path.with_suffix(".py")
320
+ try: py_code = compile_source(src_path.read_text(encoding="utf-8"))
321
+ except EpySyntaxError as e: print(f"Syntax Error: {e}"); sys.exit(1)
322
+
323
+ py_path.write_text(py_code, encoding="utf-8")
324
+ print(f"Compiled successfully: {src_path} -> {py_path}")
325
+
326
+ if "--run" in sys.argv:
327
+ subprocess.run([sys.executable, str(py_path)])
328
+
329
+ if __name__ == "__main__": main()
330
+