diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index c6cfe89..0000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,51 +0,0 @@ -# Contributing to ForeignThon - -## Project structure -foreignthon/ -├── packages/ -│ ├── foreignthon/ # core engine + fpy CLI -│ │ ├── src/foreignthon/ -│ │ │ ├── cli.py # fpy commands -│ │ │ ├── transpiler.py # tokenizer-based transpiler -│ │ │ ├── pack.py # language pack loader -│ │ │ └── errors.py # bilingual error hook -│ │ └── tests/ -│ └── langs/ -│ └── es/ # Spanish language pack -│ └── src/foreignthon_es/es.json - -## Setting up - -```bash -python -m venv .venv && source .venv/bin/activate -pip install -e "packages/foreignthon[dev]" -pip install -e packages/langs/es -``` - -## Running tests - -```bash -pytest packages/foreignthon/tests/ -v -``` - -## Adding a new language pack - -1. Copy `packages/langs/es/` to `packages/langs//` -2. Rename `foreignthon_es` → `foreignthon_` throughout -3. Fill in `.json` following the same schema as `es.json` -4. Validate it: `fpy pack packages/langs//src/foreignthon_/.json` -5. Add tests if the language has tricky characters or edge cases -6. Open a PR or publish independently as `foreignthon-` on PyPI - -## Language pack schema - -Every pack must have these top-level keys: -`meta`, `keywords`, `builtins`, `exceptions`, `error_messages`, `stdlib` - -See `packages/langs/es/src/foreignthon_es/es.json` as the reference. - -## Code style - -```bash -ruff check packages/foreignthon/src -``` diff --git a/README.md b/README.md index c05d196..c549a98 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,8 @@ -DME.md << 'EOF' # ForeignThon -Write Python in any human language. ForeignThon transpiles `.es.py`, `.ta.py` (and more) files into standard Python — keywords, builtins, exceptions, all of it. +Write Python in any human language. + +ForeignThon transpiles `.es.py`, `.ta.py` (and more) into standard Python. Keywords, builtins, exceptions — all translated. Errors come back in your language too. ```python # hola.es.py @@ -19,7 +20,7 @@ fpy run hola.es.py # Hola, mundo 2! ``` -## Installation +## Install ```bash pip install foreignthon @@ -27,62 +28,13 @@ pip install foreignthon-es # Spanish pip install foreignthon-ta # Tamil ``` -## Usage +## Docs -```bash -fpy run script.es.py # transpile and run -fpy compile script.es.py # output a .compiled.py file -fpy check script.es.py # validate without running -fpy pack mylang.json # validate a language pack -``` - -### Language override - -```python -# foreignthon: es -# ^ overrides the file extension -``` - -Or via CLI flag: - -```bash -fpy run script.py --lang es -``` - -## Errors - -Errors are shown in your language first, English below: -[ES] ErrorDeDivisionCero: Error: división por cero -[EN] ZeroDivisionError: division by zero - -## Language Packs - -A language pack is a JSON file + a tiny Python wrapper published as `foreignthon-xx` on PyPI. -See `packages/langs/es/` for the reference implementation. - -The JSON covers: -- **keywords** — `si → if`, `para → for`, `definir → def` … -- **builtins** — `imprimir → print`, `rango → range` … -- **exceptions** — `ErrorDeValor → ValueError` … -- **error_messages** — bilingual error output -- **stdlib** — `matematicas → math` … - -Validate your pack before publishing: - -```bash -fpy pack mylang.json -``` - -## Development - -```bash -git clone -cd foreignthon -python -m venv .venv && source .venv/bin/activate -pip install -e "packages/foreignthon[dev]" -pip install -e packages/langs/es -pytest packages/foreignthon/tests/ -v -``` +- [Getting Started](docs/getting-started.md) +- [Language Packs](docs/language-packs.md) +- [Postfix Syntax](docs/postfix-syntax.md) +- [Contributing](docs/contributing.md) +- [Releasing](docs/releasing.md) ## License diff --git a/docs/contributing.md b/docs/contributing.md new file mode 100644 index 0000000..636d426 --- /dev/null +++ b/docs/contributing.md @@ -0,0 +1,63 @@ +# Contributing + +## Project structure +``` +foreignthon/ +├── packages/ +│ ├── foreignthon/ # core engine + fpy CLI +│ │ ├── src/foreignthon/ +│ │ │ ├── cli.py # fpy commands +│ │ │ ├── transpiler.py # tokenizer-based transpiler + @@ pre-pass +│ │ │ ├── pack.py # language pack loader + entry point discovery +│ │ │ └── errors.py # bilingual error hook +│ │ └── tests/ +│ └── langs/ +│ ├── es/ # foreignthon-es (Spanish) +│ └── ta/ # foreignthon-ta (Tamil) +├── docs/ +└── .gitea/workflows/ +├── ci.yml # runs tests + lint on every push +└── publish.yml # builds + publishes to PyPI on git tag +``` + +## How the transpiler works + +1. **Pre-pass** — scans for `@@keyword` postfix syntax and rewrites lines to prefix form +2. **Tokenizer** — uses Python's `tokenize` module to swap `NAME` tokens. Strings and comments are never touched. +3. **Runner** — compiles with the original filename so tracebacks point to your `.es.py` file, not a temp file + +## Setup + +```bash +python -m venv .venv && source .venv/bin/activate +pip install -e "packages/foreignthon[dev]" +pip install -e packages/langs/es +pip install -e packages/langs/ta +``` + +## Running tests + +```bash +pytest packages/foreignthon/tests/ -v +``` + +## Linting + +```bash +ruff check packages/foreignthon/src +``` + +## Adding a language pack + +See [language-packs.md](language-packs.md) for the full guide. + +## CI + +Every push to `main` runs tests and lint (non-blocking). Releases are triggered by pushing a git tag — see [releasing.md](releasing.md). + +## Submitting changes + +1. Fork the repo +2. Create a branch +3. Make your changes + add tests if relevant +4. Open a pull request against `main` diff --git a/docs/getting-started.md b/docs/getting-started.md new file mode 100644 index 0000000..8b5823d --- /dev/null +++ b/docs/getting-started.md @@ -0,0 +1,80 @@ +# Getting Started + +## Installation + +```bash +pip install foreignthon +pip install foreignthon-es # add Spanish +pip install foreignthon-ta # add Tamil +``` + +For CLI use across projects, prefer pipx: + +```bash +pipx install foreignthon +``` + +## Writing a file + +Name your file `script..py` — the extension tells ForeignThon which language pack to use. + +```python +# script.es.py +definir sumar(a, b): + retornar a + b + +para i en rango(5): + imprimir(sumar(i, 1)) +``` + +## Running + +```bash +fpy run script.es.py # transpile and run +fpy compile script.es.py # output a .compiled.py file +fpy check script.es.py # validate without running +``` + +## Overriding the language + +Via shebang comment at the top of the file: + +```python +# foreignthon: es +``` + +Or via CLI flag: + +```bash +fpy run script.py --lang es +``` + +## Errors + +Errors are shown in your language first, English below: +[ES] ErrorDeDivisionCero: Error: división por cero +[EN] ZeroDivisionError: division by zero +File "script.es.py", line 3 + +## Variable names + +Variable names are optional — you can use English or your language freely: + +```python +# both work fine in the same file +definir calculate(anchura, altura): + area = anchura * altura + retornar area +``` + +## Local dev setup + +```bash +git clone +cd foreignthon +python -m venv .venv && source .venv/bin/activate +pip install -e "packages/foreignthon[dev]" +pip install -e packages/langs/es +pip install -e packages/langs/ta +pytest packages/foreignthon/tests/ -v +``` diff --git a/docs/language-packs.md b/docs/language-packs.md new file mode 100644 index 0000000..6170c2b --- /dev/null +++ b/docs/language-packs.md @@ -0,0 +1,69 @@ +# Language Packs + +A language pack is a JSON file that maps foreign tokens to Python equivalents, published as `foreignthon-` on PyPI. + +## Available packs + +| Package | Language | Install | +|---|---|---| +| `foreignthon-es` | Spanish | `pip install foreignthon-es` | +| `foreignthon-ta` | Tamil | `pip install foreignthon-ta` | + +## JSON schema + +Every pack must have these top-level keys: + +```json +{ + "meta": { + "name": "Spanish", + "native_name": "Español", + "code": "es", + "version": "0.1.0", + "authors": [] + }, + "keywords": { "si": "if", "para": "for", ... }, + "builtins": { "imprimir": "print", "rango": "range", ... }, + "exceptions": { "ErrorDeValor": "ValueError", ... }, + "error_messages":{ "ValueError": "Error de valor", ... }, + "stdlib": { "matematicas": "math", ... } +} +``` + +- **keywords** — Python reserved words (`if`, `for`, `def`, `class` …) +- **builtins** — built-in functions (`print`, `range`, `len` …) +- **exceptions** — built-in exception names (`ValueError`, `TypeError` …) +- **error_messages** — translations for bilingual error output +- **stdlib** — common standard library module names (`math`, `sys` …) + +Third-party library names (numpy, pandas etc.) are out of scope. + +## Creating a pack + +1. Copy `packages/langs/es/` to `packages/langs//` +2. Rename every `foreignthon_es` → `foreignthon_` +3. Fill in `.json` following the schema above +4. Validate: `fpy pack packages/langs//src/foreignthon_/.json` +5. Install locally: `pip install -e packages/langs/` +6. Test: `fpy run myscript..py` + +## Publishing a pack + +```bash +python -m build packages/langs/ +twine upload packages/langs//dist/* +``` + +Anyone can publish an independent `foreignthon-` pack — you don't need to be a core maintainer. + +## How discovery works + +Packs register themselves via Python entry points: + +```toml +# in the pack's pyproject.toml +[project.entry-points."foreignthon.langs"] +es = "foreignthon_es" +``` + +The core finds all installed packs automatically — no config needed. diff --git a/docs/postfix-syntax.md b/docs/postfix-syntax.md new file mode 100644 index 0000000..ef042d3 --- /dev/null +++ b/docs/postfix-syntax.md @@ -0,0 +1,49 @@ +# Postfix Syntax + +Some languages (like Tamil) are grammatically SOV — the condition comes before the keyword, not after. ForeignThon supports this with the `@@` operator. + +## How it works + +```python +# Standard prefix (works in every language) +si x > 0: + imprimir(x) + +# Postfix with @@ +x > 0 @@si: + imprimir(x) +``` + +Both produce identical Python: `if x > 0:`. The `@@` means "take whatever is to my left, put the keyword first". + +## Rules + +- `@@` only rewrites the line it appears on — nothing else changes +- Indentation rules are identical to normal Python +- Prefix and postfix can be mixed freely in the same file +- Works for any keyword in any language pack + +## Examples + +```python +# if / else +x > 0 @@si: + imprimir(x) +sino: + pasar + +# while +contador < 10 @@mientras: + contador += 1 + +# inside a function — indentation unchanged +definir comprobar(x): + x > 0 @@si: + imprimir("positivo") + sino: + imprimir("negativo") +``` + +## Why @@ + +`@@` is not valid Python syntax so it never conflicts with existing code. Single `@` is used for decorators and matrix multiplication, so it was ruled out.