added custom functionality and working with new toml as well, and update cli functions

This commit is contained in:
2026-05-17 18:30:32 -05:00
parent 5f820ab1ae
commit d2fef3b7de
2 changed files with 61 additions and 15 deletions

View File

@@ -56,3 +56,6 @@ ignore = ["E501"]
[tool.ruff.lint.per-file-ignores] [tool.ruff.lint.per-file-ignores]
"pack.py" = ["E501"] "pack.py" = ["E501"]
[tool.hatch.build.targets.wheel.force-include]
"src/foreignthon/template.json" = "foreignthon/template.json"

View File

@@ -12,18 +12,16 @@ from .transpiler import run_transpiled, transpile_file
CONTEXT_SETTINGS = dict(help_option_names=["-h", "--help"]) CONTEXT_SETTINGS = dict(help_option_names=["-h", "--help"])
def _load_effective_pack(project: Path, lang: str) -> dict: def _load_effective_pack(project: Path, lang: str) -> dict:
""" """
Load installed pack then merge local custom.json on top if configured. Load pack for a file. Priority:
Walks up from project to find .foreignthon.toml. 1. custom_pack in .foreignthon.toml — no installed pack needed
2. installed pack via entry points
""" """
import json import json
from .pack import load_pack from .pack import load_pack
pack = load_pack(lang) # Walk up to find .foreignthon.toml
# Walk up directories to find .foreignthon.toml
search = project if project.is_dir() else project.parent search = project if project.is_dir() else project.parent
toml_path = None toml_path = None
for parent in [search, *search.parents]: for parent in [search, *search.parents]:
@@ -32,20 +30,29 @@ def _load_effective_pack(project: Path, lang: str) -> dict:
toml_path = candidate toml_path = candidate
break break
# Check for custom_pack first — if found, load it directly
# and merge on top of template (no installed pack required)
if toml_path: if toml_path:
for line in toml_path.read_text(encoding="utf-8").splitlines(): for line in toml_path.read_text(encoding="utf-8").splitlines():
if line.strip().startswith("custom_pack"): if line.strip().startswith("custom_pack") and not line.strip().startswith("#"):
custom_path = toml_path.parent / line.split("=", 1)[1].strip().strip('"').strip("'") custom_path = toml_path.parent / line.split("=", 1)[1].strip().strip('"').strip("'")
if custom_path.exists(): if custom_path.exists():
custom = json.loads(custom_path.read_text(encoding="utf-8")) custom = json.loads(custom_path.read_text(encoding="utf-8"))
# If custom pack has meta with a code, it's a standalone pack
if custom.get("meta", {}).get("code"):
return custom
# Otherwise it's an override — merge on top of installed pack
pack = load_pack(lang)
for section in ("keywords", "builtins", "exceptions", "stdlib", "error_messages"): for section in ("keywords", "builtins", "exceptions", "stdlib", "error_messages"):
if section in custom: if section in custom:
pack[section] = {**pack.get(section, {}), **custom[section]} pack[section] = {**pack.get(section, {}), **custom[section]}
if "postfix_keywords" in custom: if "postfix_keywords" in custom:
pack["postfix_keywords"] = custom["postfix_keywords"] pack["postfix_keywords"] = custom["postfix_keywords"]
return pack
break break
return pack # Fall back to installed pack
return load_pack(lang)
def _pick(mapping: dict, english: str, fallback: str) -> str: def _pick(mapping: dict, english: str, fallback: str) -> str:
@@ -104,21 +111,41 @@ def new(name: str, lang: str, no_git: bool):
click.echo("✗ Current directory is not empty.", err=True) click.echo("✗ Current directory is not empty.", err=True)
raise SystemExit(1) raise SystemExit(1)
try: is_custom = lang == "custom"
pack = _load_effective_pack(project, lang)
except PackNotFoundError: if is_custom:
click.echo(f"Language pack '{lang}' not installed.", err=True) lang_code = click.prompt("Language code (e.g. ru, fr, de)")
click.echo(f" Run: pip install foreignthon-{lang}", err=True) lang_name_en = click.prompt("Language name in English (e.g. Russian)")
raise SystemExit(1) lang_name_native = click.prompt("Language name in its own script (e.g. Русский)")
pack = _make_scaffold_pack(lang_code, lang_name_en, lang_name_native)
lang = lang_code
else:
try:
pack = _load_effective_pack(project, lang)
except PackNotFoundError:
click.echo(f"✗ Language pack '{lang}' not installed.", err=True)
click.echo(f" Run: pip install foreignthon-{lang}", err=True)
raise SystemExit(1)
lang_name = pack["meta"]["native_name"] lang_name = pack["meta"]["native_name"]
if is_custom:
import json
(project / "custom.json").write_text(
json.dumps(pack, ensure_ascii=False, indent=2),
encoding="utf-8",
)
toml_custom_line = 'custom_pack = "custom.json"'
click.echo(" Created custom.json — fill in your translations!")
else:
toml_custom_line = '# custom_pack = "custom.json"'
(project / ".foreignthon.toml").write_text( (project / ".foreignthon.toml").write_text(
f'[foreignthon]\n' f'[foreignthon]\n'
f'lang = "{lang}"\n' f'lang = "{lang}"\n'
f'\n' f'\n'
f'# Optional: path to a local JSON that overrides pack keywords\n' f'# Optional: path to a local JSON that overrides pack keywords\n'
f'# custom_pack = "custom.json"\n', f'{toml_custom_line}\n',
encoding="utf-8", encoding="utf-8",
) )
@@ -301,3 +328,19 @@ def decompile(file: Path, lang: str, postfix: bool, output: str | None):
out_path.parent.mkdir(parents=True, exist_ok=True) out_path.parent.mkdir(parents=True, exist_ok=True)
out_path.write_text(result, encoding="utf-8") out_path.write_text(result, encoding="utf-8")
click.echo(f"Decompiled: {out_path}") click.echo(f"Decompiled: {out_path}")
def _make_scaffold_pack(lang_code: str, lang_name: str, native_name: str) -> dict:
"""
Load template.json — single source of truth for all pack keys.
To add new keywords/builtins, edit template.json only.
"""
import json
from importlib.resources import files
template_path = files("foreignthon") / "template.json"
pack = json.loads(template_path.read_text(encoding="utf-8"))
pack["meta"]["name"] = lang_name
pack["meta"]["native_name"] = native_name
pack["meta"]["code"] = lang_code
return pack