skyforge-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.
- skyforge_lang-1.0.0/PKG-INFO +25 -0
- skyforge_lang-1.0.0/README.md +12 -0
- skyforge_lang-1.0.0/pyproject.toml +25 -0
- skyforge_lang-1.0.0/setup.cfg +4 -0
- skyforge_lang-1.0.0/skyforge/__init__.py +13 -0
- skyforge_lang-1.0.0/skyforge/cli.py +442 -0
- skyforge_lang-1.0.0/skyforge/core.py +3280 -0
- skyforge_lang-1.0.0/skyforge/errors.py +127 -0
- skyforge_lang-1.0.0/skyforge/launcher.py +973 -0
- skyforge_lang-1.0.0/skyforge/orm.py +228 -0
- skyforge_lang-1.0.0/skyforge/repl.py +177 -0
- skyforge_lang-1.0.0/skyforge/stdlib.py +186 -0
- skyforge_lang-1.0.0/skyforge/testing.py +135 -0
- skyforge_lang-1.0.0/skyforge_lang.egg-info/PKG-INFO +25 -0
- skyforge_lang-1.0.0/skyforge_lang.egg-info/SOURCES.txt +16 -0
- skyforge_lang-1.0.0/skyforge_lang.egg-info/dependency_links.txt +1 -0
- skyforge_lang-1.0.0/skyforge_lang.egg-info/entry_points.txt +3 -0
- skyforge_lang-1.0.0/skyforge_lang.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: skyforge-lang
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: SkyForge - русскоязычный язык программирования
|
|
5
|
+
Author: SkyForge
|
|
6
|
+
License: MIT
|
|
7
|
+
Keywords: language,russian,interpreter,skyforge
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Natural Language :: Russian
|
|
10
|
+
Classifier: Topic :: Software Development :: Interpreters
|
|
11
|
+
Requires-Python: >=3.9
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
|
|
14
|
+
# SkyForge
|
|
15
|
+
|
|
16
|
+
Русскоязычный язык программирования.
|
|
17
|
+
|
|
18
|
+
## Установка (dev)
|
|
19
|
+
|
|
20
|
+
pip install -e .
|
|
21
|
+
|
|
22
|
+
## Запуск
|
|
23
|
+
|
|
24
|
+
SkyForge GAZUY examples/test.skf
|
|
25
|
+
SkyForge check examples/test.skf
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "skyforge-lang" # было "skyforge"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "SkyForge - русскоязычный язык программирования"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [{ name = "SkyForge" }]
|
|
13
|
+
keywords = ["language", "russian", "interpreter", "skyforge"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Programming Language :: Python :: 3",
|
|
16
|
+
"Natural Language :: Russian",
|
|
17
|
+
"Topic :: Software Development :: Interpreters",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
[project.scripts]
|
|
21
|
+
SkyForge = "skyforge.cli:main"
|
|
22
|
+
skyforge = "skyforge.cli:main"
|
|
23
|
+
|
|
24
|
+
[tool.setuptools]
|
|
25
|
+
packages = ["skyforge"]
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from .core import (
|
|
2
|
+
Lexer, Parser, Interpreter,
|
|
3
|
+
run_source, run_file,
|
|
4
|
+
Token, Program, Environment, Function,
|
|
5
|
+
)
|
|
6
|
+
|
|
7
|
+
__version__ = "1.0.0"
|
|
8
|
+
__all__ = [
|
|
9
|
+
"Lexer", "Parser", "Interpreter",
|
|
10
|
+
"run_source", "run_file",
|
|
11
|
+
"Token", "Program", "Environment", "Function",
|
|
12
|
+
"__version__",
|
|
13
|
+
]
|
|
@@ -0,0 +1,442 @@
|
|
|
1
|
+
# skyforge/cli.py
|
|
2
|
+
# SkyForge — командный интерфейс
|
|
3
|
+
# Команды: SkyForge GAZUY file.skf | SkyForge check file.skf | SkyForge gui
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
import sys
|
|
7
|
+
import argparse
|
|
8
|
+
import signal
|
|
9
|
+
import traceback
|
|
10
|
+
|
|
11
|
+
from .core import Lexer, Parser, Interpreter
|
|
12
|
+
from . import errors as E
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
BANNER = r"""
|
|
16
|
+
███████╗██╗ ██╗██╗ ██╗███████╗ ██████╗ ██████╗ ██████╗ ███████╗
|
|
17
|
+
██╔════╝██║ ██╔╝╚██╗ ██╔╝██╔════╝██╔═══██╗██╔══██╗██╔════╝ ██╔════╝
|
|
18
|
+
███████╗█████╔╝ ╚████╔╝ █████╗ ██║ ██║██████╔╝██║ ███╗█████╗
|
|
19
|
+
╚════██║██╔═██╗ ╚██╔╝ ██╔══╝ ██║ ██║██╔══██╗██║ ██║██╔══╝
|
|
20
|
+
███████║██║ ██╗ ██║ ██║ ╚██████╔╝██║ ██║╚██████╔╝███████╗
|
|
21
|
+
╚══════╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝
|
|
22
|
+
русскоязычный язык программирования
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
# =========================================================
|
|
27
|
+
# GAZUY — запуск .skf-файла
|
|
28
|
+
# =========================================================
|
|
29
|
+
|
|
30
|
+
def cmd_gazuy(args) -> int:
|
|
31
|
+
import time as _time
|
|
32
|
+
|
|
33
|
+
path = args.file
|
|
34
|
+
if not os.path.isfile(path):
|
|
35
|
+
print(f"❌ Файл не найден: {path}", file=sys.stderr)
|
|
36
|
+
return 1
|
|
37
|
+
|
|
38
|
+
if args.port is not None:
|
|
39
|
+
os.environ["PORT"] = str(args.port)
|
|
40
|
+
if args.host is not None:
|
|
41
|
+
os.environ["SKYFORGE_HOST"] = args.host
|
|
42
|
+
|
|
43
|
+
os.environ.setdefault("PYTHONIOENCODING", "utf-8")
|
|
44
|
+
os.environ.setdefault("PYTHONUTF8", "1")
|
|
45
|
+
|
|
46
|
+
print(BANNER, flush=True)
|
|
47
|
+
print(f"🚀 GAZUY! Файл: {path}", flush=True)
|
|
48
|
+
if os.environ.get("PORT"):
|
|
49
|
+
print(f" PORT = {os.environ['PORT']}", flush=True)
|
|
50
|
+
if os.environ.get("SKYFORGE_HOST"):
|
|
51
|
+
print(f" HOST = {os.environ['SKYFORGE_HOST']}", flush=True)
|
|
52
|
+
print("-" * 66, flush=True)
|
|
53
|
+
|
|
54
|
+
try:
|
|
55
|
+
with open(path, encoding="utf-8") as f:
|
|
56
|
+
source = f.read()
|
|
57
|
+
except Exception as e:
|
|
58
|
+
print(f"❌ Не могу прочитать файл: {e}", file=sys.stderr)
|
|
59
|
+
return 1
|
|
60
|
+
|
|
61
|
+
try:
|
|
62
|
+
tokens = Lexer(source).tokenize()
|
|
63
|
+
program = Parser(tokens).parse()
|
|
64
|
+
except SyntaxError as e:
|
|
65
|
+
print(f"❌ Синтаксическая ошибка: {e}", file=sys.stderr)
|
|
66
|
+
return 2
|
|
67
|
+
except Exception as e:
|
|
68
|
+
print(f"❌ Ошибка разбора: {e}", file=sys.stderr)
|
|
69
|
+
traceback.print_exc()
|
|
70
|
+
return 2
|
|
71
|
+
|
|
72
|
+
if args.check_only:
|
|
73
|
+
print("✅ Синтаксис в порядке.", flush=True)
|
|
74
|
+
return 0
|
|
75
|
+
|
|
76
|
+
interp = Interpreter()
|
|
77
|
+
interp.current_file = os.path.abspath(path)
|
|
78
|
+
|
|
79
|
+
# SIGTERM — для контейнеров (Render/Docker).
|
|
80
|
+
# SIGINT НЕ перехватываем — пусть Python бросит KeyboardInterrupt,
|
|
81
|
+
# это самый надёжный способ остановки.
|
|
82
|
+
def _on_sigterm(signum, _frame):
|
|
83
|
+
print(f"\n⚠️ SIGTERM — останавливаю...", flush=True)
|
|
84
|
+
try:
|
|
85
|
+
interp._web_server_stop()
|
|
86
|
+
except Exception:
|
|
87
|
+
pass
|
|
88
|
+
# даём shutdown время
|
|
89
|
+
for _ in range(20):
|
|
90
|
+
if not interp.has_active_servers():
|
|
91
|
+
break
|
|
92
|
+
_time.sleep(0.1)
|
|
93
|
+
os._exit(0)
|
|
94
|
+
|
|
95
|
+
try:
|
|
96
|
+
signal.signal(signal.SIGTERM, _on_sigterm)
|
|
97
|
+
except (ValueError, AttributeError):
|
|
98
|
+
pass
|
|
99
|
+
|
|
100
|
+
try:
|
|
101
|
+
interp.run(program)
|
|
102
|
+
except SystemExit:
|
|
103
|
+
raise
|
|
104
|
+
except KeyboardInterrupt:
|
|
105
|
+
print("\n⏹️ Прервано пользователем.", flush=True)
|
|
106
|
+
try:
|
|
107
|
+
interp._web_server_stop()
|
|
108
|
+
except Exception:
|
|
109
|
+
pass
|
|
110
|
+
return 0
|
|
111
|
+
except Exception:
|
|
112
|
+
print("❌ Ошибка выполнения:", file=sys.stderr)
|
|
113
|
+
traceback.print_exc()
|
|
114
|
+
return 3
|
|
115
|
+
|
|
116
|
+
if interp.has_active_servers():
|
|
117
|
+
print("🟢 Сервер(ы) работают. Ctrl+C для остановки.\n", flush=True)
|
|
118
|
+
interp._web_server_wait()
|
|
119
|
+
|
|
120
|
+
else:
|
|
121
|
+
print("\n✅ Скрипт завершён.", flush=True)
|
|
122
|
+
|
|
123
|
+
return 0
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
# =========================================================
|
|
127
|
+
# check — только проверка синтаксиса
|
|
128
|
+
# =========================================================
|
|
129
|
+
|
|
130
|
+
def cmd_test(args) -> int:
|
|
131
|
+
"""Запуск тестов из .skf файла."""
|
|
132
|
+
path = args.file
|
|
133
|
+
if not os.path.isfile(path):
|
|
134
|
+
print(f"❌ Файл не найден: {path}", file=sys.stderr)
|
|
135
|
+
return 1
|
|
136
|
+
|
|
137
|
+
try:
|
|
138
|
+
with open(path, encoding="utf-8") as f:
|
|
139
|
+
source = f.read()
|
|
140
|
+
except Exception as e:
|
|
141
|
+
print(f"❌ Не могу прочитать: {e}", file=sys.stderr)
|
|
142
|
+
return 1
|
|
143
|
+
|
|
144
|
+
try:
|
|
145
|
+
tokens = Lexer(source).tokenize()
|
|
146
|
+
program = Parser(tokens).parse()
|
|
147
|
+
except SyntaxError as e:
|
|
148
|
+
print(E.format_syntax_error(e, source=source, filename=path), file=sys.stderr)
|
|
149
|
+
return 2
|
|
150
|
+
|
|
151
|
+
interp = Interpreter()
|
|
152
|
+
interp.current_file = os.path.abspath(path)
|
|
153
|
+
|
|
154
|
+
try:
|
|
155
|
+
interp.run(program)
|
|
156
|
+
except SystemExit:
|
|
157
|
+
raise
|
|
158
|
+
except Exception as err:
|
|
159
|
+
print(E.format_runtime_error(err, filename=path), file=sys.stderr)
|
|
160
|
+
return 3
|
|
161
|
+
|
|
162
|
+
# Запускаем накопленные тесты
|
|
163
|
+
runner = interp.globals.vars.get("запустить_тесты")
|
|
164
|
+
if runner is None:
|
|
165
|
+
print("⚠️ Не найдено функции 'запустить_тесты'. Добавь её в файл.")
|
|
166
|
+
return 1
|
|
167
|
+
|
|
168
|
+
ok = interp.call_function(runner, [])
|
|
169
|
+
return 0 if ok else 1
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def cmd_web(args) -> int:
|
|
173
|
+
"""Запуск веб-приложения SkyForge."""
|
|
174
|
+
path = args.file
|
|
175
|
+
if not os.path.isfile(path):
|
|
176
|
+
print(f"❌ Файл не найден: {path}", file=sys.stderr)
|
|
177
|
+
return 1
|
|
178
|
+
|
|
179
|
+
if args.port is not None:
|
|
180
|
+
os.environ["PORT"] = str(args.port)
|
|
181
|
+
if args.host is not None:
|
|
182
|
+
os.environ["SKYFORGE_HOST"] = args.host
|
|
183
|
+
|
|
184
|
+
print(BANNER, flush=True)
|
|
185
|
+
print(f"🌐 WEB! Файл: {path}", flush=True)
|
|
186
|
+
if os.environ.get("PORT"):
|
|
187
|
+
print(f" PORT = {os.environ['PORT']}", flush=True)
|
|
188
|
+
if os.environ.get("SKYFORGE_HOST"):
|
|
189
|
+
print(f" HOST = {os.environ['SKYFORGE_HOST']}", flush=True)
|
|
190
|
+
print("-" * 66, flush=True)
|
|
191
|
+
|
|
192
|
+
try:
|
|
193
|
+
with open(path, encoding="utf-8") as f:
|
|
194
|
+
source = f.read()
|
|
195
|
+
except Exception as e:
|
|
196
|
+
print(f"❌ Не могу прочитать файл: {e}", file=sys.stderr)
|
|
197
|
+
return 1
|
|
198
|
+
|
|
199
|
+
try:
|
|
200
|
+
tokens = Lexer(source).tokenize()
|
|
201
|
+
program = Parser(tokens).parse()
|
|
202
|
+
except SyntaxError as e:
|
|
203
|
+
print(E.format_syntax_error(e, source=source, filename=path), file=sys.stderr)
|
|
204
|
+
return 2
|
|
205
|
+
|
|
206
|
+
interp = Interpreter()
|
|
207
|
+
interp.current_file = os.path.abspath(path)
|
|
208
|
+
try:
|
|
209
|
+
interp.run(program)
|
|
210
|
+
except KeyboardInterrupt:
|
|
211
|
+
print("\n⏹ Прервано.", flush=True)
|
|
212
|
+
return 0
|
|
213
|
+
except SystemExit:
|
|
214
|
+
raise
|
|
215
|
+
except Exception as err:
|
|
216
|
+
print(E.format_runtime_error(err, filename=path), file=sys.stderr)
|
|
217
|
+
return 3
|
|
218
|
+
return 0
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def cmd_repl(args) -> int:
|
|
222
|
+
from .repl import main as repl_main
|
|
223
|
+
return repl_main() or 0
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def cmd_check(args) -> int:
|
|
227
|
+
path = args.file
|
|
228
|
+
if not os.path.isfile(path):
|
|
229
|
+
print(f"❌ Файл не найден: {path}", file=sys.stderr)
|
|
230
|
+
return 1
|
|
231
|
+
|
|
232
|
+
try:
|
|
233
|
+
with open(path, encoding="utf-8") as f:
|
|
234
|
+
src = f.read()
|
|
235
|
+
except Exception as e:
|
|
236
|
+
print(f"❌ Не могу прочитать файл: {e}", file=sys.stderr)
|
|
237
|
+
return 1
|
|
238
|
+
|
|
239
|
+
try:
|
|
240
|
+
tokens = Lexer(src).tokenize()
|
|
241
|
+
Parser(tokens).parse()
|
|
242
|
+
print(f"✅ {path}: синтаксис в порядке")
|
|
243
|
+
return 0
|
|
244
|
+
except SyntaxError as e:
|
|
245
|
+
print(E.format_syntax_error(e, source=src, filename=path), file=sys.stderr)
|
|
246
|
+
return 2
|
|
247
|
+
except Exception as e:
|
|
248
|
+
print(E.format_runtime_error(e, filename=path), file=sys.stderr)
|
|
249
|
+
return 1
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
# =========================================================
|
|
253
|
+
# run — запуск без баннера (короткий вывод)
|
|
254
|
+
# =========================================================
|
|
255
|
+
|
|
256
|
+
def cmd_run(args) -> int:
|
|
257
|
+
path = args.file
|
|
258
|
+
if not os.path.isfile(path):
|
|
259
|
+
print(f"❌ Файл не найден: {path}", file=sys.stderr)
|
|
260
|
+
return 1
|
|
261
|
+
|
|
262
|
+
if args.port is not None:
|
|
263
|
+
os.environ["PORT"] = str(args.port)
|
|
264
|
+
if args.host is not None:
|
|
265
|
+
os.environ["SKYFORGE_HOST"] = args.host
|
|
266
|
+
|
|
267
|
+
try:
|
|
268
|
+
with open(path, encoding="utf-8") as f:
|
|
269
|
+
src = f.read()
|
|
270
|
+
except Exception as e:
|
|
271
|
+
print(f"❌ Не могу прочитать файл: {e}", file=sys.stderr)
|
|
272
|
+
return 1
|
|
273
|
+
|
|
274
|
+
try:
|
|
275
|
+
tokens = Lexer(src).tokenize()
|
|
276
|
+
program = Parser(tokens).parse()
|
|
277
|
+
except SyntaxError as e:
|
|
278
|
+
print(f"❌ Синтаксическая ошибка: {e}", file=sys.stderr)
|
|
279
|
+
return 2
|
|
280
|
+
|
|
281
|
+
interp = Interpreter()
|
|
282
|
+
|
|
283
|
+
def _shutdown(signum, _frame):
|
|
284
|
+
try:
|
|
285
|
+
interp._web_server_stop()
|
|
286
|
+
except Exception:
|
|
287
|
+
pass
|
|
288
|
+
sys.exit(0)
|
|
289
|
+
|
|
290
|
+
try:
|
|
291
|
+
signal.signal(signal.SIGTERM, _shutdown)
|
|
292
|
+
signal.signal(signal.SIGINT, _shutdown)
|
|
293
|
+
except (ValueError, AttributeError):
|
|
294
|
+
pass
|
|
295
|
+
|
|
296
|
+
try:
|
|
297
|
+
interp.run(program)
|
|
298
|
+
except SystemExit:
|
|
299
|
+
raise
|
|
300
|
+
except Exception:
|
|
301
|
+
traceback.print_exc()
|
|
302
|
+
return 3
|
|
303
|
+
|
|
304
|
+
if interp.has_active_servers():
|
|
305
|
+
try:
|
|
306
|
+
interp._web_server_wait()
|
|
307
|
+
except KeyboardInterrupt:
|
|
308
|
+
interp._web_server_stop()
|
|
309
|
+
|
|
310
|
+
return 0
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
# =========================================================
|
|
314
|
+
# gui — опциональный графический лаунчер
|
|
315
|
+
# =========================================================
|
|
316
|
+
|
|
317
|
+
def cmd_gui(args) -> int:
|
|
318
|
+
try:
|
|
319
|
+
from .launcher import main as gui_main
|
|
320
|
+
except ImportError as e:
|
|
321
|
+
print(f"❌ GUI недоступен: {e}", file=sys.stderr)
|
|
322
|
+
print(" Убедитесь, что файл skyforge/launcher.py присутствует.", file=sys.stderr)
|
|
323
|
+
return 1
|
|
324
|
+
|
|
325
|
+
# Подменяем argv, чтобы launcher.main() увидел файл
|
|
326
|
+
argv_backup = sys.argv[:]
|
|
327
|
+
try:
|
|
328
|
+
sys.argv = ["SkyForge-gui"] + ([args.file] if args.file else [])
|
|
329
|
+
gui_main()
|
|
330
|
+
finally:
|
|
331
|
+
sys.argv = argv_backup
|
|
332
|
+
return 0
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
# =========================================================
|
|
336
|
+
# version / info
|
|
337
|
+
# =========================================================
|
|
338
|
+
|
|
339
|
+
def cmd_version(args) -> int:
|
|
340
|
+
from . import __version__
|
|
341
|
+
print(f"SkyForge {__version__}")
|
|
342
|
+
print(f"Python: {sys.version.split()[0]}")
|
|
343
|
+
print(f"Платформа: {sys.platform}")
|
|
344
|
+
print(f"Файл пакета: {os.path.dirname(os.path.abspath(__file__))}")
|
|
345
|
+
return 0
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
# =========================================================
|
|
349
|
+
# Парсер аргументов
|
|
350
|
+
# =========================================================
|
|
351
|
+
|
|
352
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
353
|
+
p = argparse.ArgumentParser(
|
|
354
|
+
prog="SkyForge",
|
|
355
|
+
description="SkyForge — русскоязычный язык программирования",
|
|
356
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
357
|
+
epilog=(
|
|
358
|
+
"Примеры:\n"
|
|
359
|
+
" SkyForge GAZUY app.skf # запустить файл\n"
|
|
360
|
+
" SkyForge GAZUY app.skf --port 9000 # свой порт\n"
|
|
361
|
+
" SkyForge check app.skf # проверить синтаксис\n"
|
|
362
|
+
" SkyForge gui app.skf # открыть GUI\n"
|
|
363
|
+
),
|
|
364
|
+
)
|
|
365
|
+
p.add_argument("--version", action="version", version="SkyForge 1.0.0")
|
|
366
|
+
|
|
367
|
+
sub = p.add_subparsers(dest="cmd", metavar="КОМАНДА")
|
|
368
|
+
|
|
369
|
+
# --- GAZUY ---
|
|
370
|
+
g = sub.add_parser(
|
|
371
|
+
"GAZUY",
|
|
372
|
+
aliases=["gazuy", "ГАЗУЙ", "газуй"],
|
|
373
|
+
help="Запустить .skf-файл (двигатель деплоя)",
|
|
374
|
+
)
|
|
375
|
+
g.add_argument("file", help="Путь к .skf-файлу")
|
|
376
|
+
g.add_argument("--port", type=int, default=None, help="Переопределить $PORT")
|
|
377
|
+
g.add_argument("--host", default=None, help="Переопределить $SKYFORGE_HOST")
|
|
378
|
+
g.add_argument("--check", dest="check_only", action="store_true",
|
|
379
|
+
help="Только проверить синтаксис, не запускать")
|
|
380
|
+
g.set_defaults(func=cmd_gazuy)
|
|
381
|
+
|
|
382
|
+
# --- check ---
|
|
383
|
+
c = sub.add_parser("check", help="Проверить синтаксис .skf-файла")
|
|
384
|
+
c.add_argument("file", help="Путь к .skf-файлу")
|
|
385
|
+
c.set_defaults(func=cmd_check)
|
|
386
|
+
|
|
387
|
+
# --- run (без баннера) ---
|
|
388
|
+
r = sub.add_parser("run", help="Запустить .skf-файл без баннера")
|
|
389
|
+
r.add_argument("file", help="Путь к .skf-файлу")
|
|
390
|
+
r.add_argument("--port", type=int, default=None, help="Переопределить $PORT")
|
|
391
|
+
r.add_argument("--host", default=None, help="Переопределить $SKYFORGE_HOST")
|
|
392
|
+
r.set_defaults(func=cmd_run)
|
|
393
|
+
|
|
394
|
+
# --- test ---
|
|
395
|
+
tp = sub.add_parser("test", help="Запустить тесты из .skf файла")
|
|
396
|
+
tp.add_argument("file", help="Путь к файлу с тестами")
|
|
397
|
+
tp.set_defaults(func=cmd_test)
|
|
398
|
+
|
|
399
|
+
# --- web ---
|
|
400
|
+
wp = sub.add_parser("web", help="Запустить веб-приложение SkyForge")
|
|
401
|
+
wp.add_argument("file", help="Путь к .skf файлу")
|
|
402
|
+
wp.add_argument("--port", type=int, default=None, help="Порт (или $PORT)")
|
|
403
|
+
wp.add_argument("--host", default=None, help="Хост (или $SKYFORGE_HOST)")
|
|
404
|
+
wp.set_defaults(func=cmd_web)
|
|
405
|
+
|
|
406
|
+
# --- repl ---
|
|
407
|
+
rp = sub.add_parser("repl", help="Интерактивная консоль SkyForge")
|
|
408
|
+
rp.set_defaults(func=cmd_repl)
|
|
409
|
+
|
|
410
|
+
# --- gui ---
|
|
411
|
+
u = sub.add_parser("gui", help="Открыть графический лаунчер")
|
|
412
|
+
u.add_argument("file", nargs="?", default=None, help="Файл для автозагрузки")
|
|
413
|
+
u.set_defaults(func=cmd_gui)
|
|
414
|
+
|
|
415
|
+
# --- version ---
|
|
416
|
+
v = sub.add_parser("version", help="Показать версию и окружение")
|
|
417
|
+
v.set_defaults(func=cmd_version)
|
|
418
|
+
|
|
419
|
+
return p
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
# =========================================================
|
|
423
|
+
# main
|
|
424
|
+
# =========================================================
|
|
425
|
+
|
|
426
|
+
def main(argv=None) -> int:
|
|
427
|
+
parser = build_parser()
|
|
428
|
+
args = parser.parse_args(argv)
|
|
429
|
+
|
|
430
|
+
if not args.cmd:
|
|
431
|
+
parser.print_help()
|
|
432
|
+
return 0
|
|
433
|
+
|
|
434
|
+
try:
|
|
435
|
+
return args.func(args) or 0
|
|
436
|
+
except KeyboardInterrupt:
|
|
437
|
+
print("\n⚠️ Прервано.", file=sys.stderr)
|
|
438
|
+
return 130
|
|
439
|
+
|
|
440
|
+
|
|
441
|
+
if __name__ == "__main__":
|
|
442
|
+
sys.exit(main())
|