cli file
This commit is contained in:
@@ -0,0 +1,111 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import runpy
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import click
|
||||||
|
|
||||||
|
from . import __version__
|
||||||
|
from .errors import activate
|
||||||
|
from .transpiler import transpile_file
|
||||||
|
|
||||||
|
|
||||||
|
@click.group()
|
||||||
|
@click.version_option(__version__, prog_name="fpy")
|
||||||
|
def main():
|
||||||
|
"""ForeignThon — write Python in any language."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@main.command()
|
||||||
|
@click.argument("file", type=click.Path(exists=True, path_type=Path))
|
||||||
|
@click.option("--lang", "-l", default=None, help="Override language code (e.g. es, ta)")
|
||||||
|
@click.option("--keep", is_flag=True, help="Keep the compiled .py file alongside the source")
|
||||||
|
def run(file: Path, lang: str | None, keep: bool):
|
||||||
|
"""Transpile and run a foreign-language Python file."""
|
||||||
|
if lang:
|
||||||
|
# Inject shebang override so transpiler picks it up
|
||||||
|
source = file.read_text(encoding="utf-8")
|
||||||
|
if not source.startswith("# foreignthon:"):
|
||||||
|
source = f"# foreignthon: {lang}\n" + source
|
||||||
|
file.write_text(source, encoding="utf-8")
|
||||||
|
|
||||||
|
transpiled = transpile_file(file)
|
||||||
|
|
||||||
|
# Detect lang for error hook
|
||||||
|
detected_lang = lang or _lang_from_file(file)
|
||||||
|
activate(detected_lang)
|
||||||
|
|
||||||
|
if keep:
|
||||||
|
out_path = file.with_suffix("").with_suffix(".compiled.py")
|
||||||
|
out_path.write_text(transpiled, encoding="utf-8")
|
||||||
|
click.echo(f"Compiled: {out_path}")
|
||||||
|
|
||||||
|
# Write to a temp file and run it — runpy keeps __file__ correct
|
||||||
|
with tempfile.NamedTemporaryFile(
|
||||||
|
suffix=".py", mode="w", encoding="utf-8", delete=False
|
||||||
|
) as tmp:
|
||||||
|
tmp.write(transpiled)
|
||||||
|
tmp_path = tmp.name
|
||||||
|
|
||||||
|
try:
|
||||||
|
runpy.run_path(tmp_path, run_name="__main__")
|
||||||
|
finally:
|
||||||
|
Path(tmp_path).unlink(missing_ok=True)
|
||||||
|
|
||||||
|
|
||||||
|
@main.command()
|
||||||
|
@click.argument("file", type=click.Path(exists=True, path_type=Path))
|
||||||
|
@click.option("--output", "-o", default=None, help="Output path (default: same dir as source)")
|
||||||
|
def compile(file: Path, output: str | None):
|
||||||
|
"""Transpile a file to standard Python without running it."""
|
||||||
|
transpiled = transpile_file(file)
|
||||||
|
|
||||||
|
out_path = Path(output) if output else file.with_suffix("").with_suffix(".compiled.py")
|
||||||
|
out_path.write_text(transpiled, encoding="utf-8")
|
||||||
|
click.echo(f"Compiled: {out_path}")
|
||||||
|
|
||||||
|
|
||||||
|
@main.command()
|
||||||
|
@click.argument("file", type=click.Path(exists=True, path_type=Path))
|
||||||
|
def check(file: Path):
|
||||||
|
"""Validate a foreign-language file without running it."""
|
||||||
|
import ast
|
||||||
|
|
||||||
|
try:
|
||||||
|
transpiled = transpile_file(file)
|
||||||
|
ast.parse(transpiled)
|
||||||
|
click.echo(f"✓ {file.name} looks good.")
|
||||||
|
except SyntaxError as e:
|
||||||
|
click.echo(f"✗ Syntax error: {e}", err=True)
|
||||||
|
sys.exit(1)
|
||||||
|
except Exception as e:
|
||||||
|
click.echo(f"✗ {e}", err=True)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
@main.command("pack")
|
||||||
|
@click.argument("json_file", type=click.Path(exists=True, path_type=Path))
|
||||||
|
def validate_pack(json_file: Path):
|
||||||
|
"""Validate a language pack JSON file."""
|
||||||
|
import json
|
||||||
|
from .pack import REQUIRED_SECTIONS
|
||||||
|
|
||||||
|
with open(json_file, encoding="utf-8") as f:
|
||||||
|
data = json.load(f)
|
||||||
|
|
||||||
|
missing = REQUIRED_SECTIONS - data.keys()
|
||||||
|
if missing:
|
||||||
|
click.echo(f"✗ Missing sections: {missing}", err=True)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
click.echo(f"✓ Pack '{data['meta']['name']}' is valid.")
|
||||||
|
|
||||||
|
|
||||||
|
def _lang_from_file(path: Path) -> str:
|
||||||
|
suffixes = path.suffixes
|
||||||
|
if len(suffixes) >= 2 and suffixes[-1] == ".py":
|
||||||
|
return suffixes[-2].lstrip(".")
|
||||||
|
return "en"
|
||||||
|
|||||||
Reference in New Issue
Block a user