move docs to docs/ folder, rewrite readme and contributing
This commit is contained in:
@@ -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/<code>/`
|
|
||||||
2. Rename `foreignthon_es` → `foreignthon_<code>` throughout
|
|
||||||
3. Fill in `<code>.json` following the same schema as `es.json`
|
|
||||||
4. Validate it: `fpy pack packages/langs/<code>/src/foreignthon_<code>/<code>.json`
|
|
||||||
5. Add tests if the language has tricky characters or edge cases
|
|
||||||
6. Open a PR or publish independently as `foreignthon-<code>` 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
|
|
||||||
```
|
|
||||||
68
README.md
68
README.md
@@ -1,7 +1,8 @@
|
|||||||
DME.md << 'EOF'
|
|
||||||
# ForeignThon
|
# 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
|
```python
|
||||||
# hola.es.py
|
# hola.es.py
|
||||||
@@ -19,7 +20,7 @@ fpy run hola.es.py
|
|||||||
# Hola, mundo 2!
|
# Hola, mundo 2!
|
||||||
```
|
```
|
||||||
|
|
||||||
## Installation
|
## Install
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pip install foreignthon
|
pip install foreignthon
|
||||||
@@ -27,62 +28,13 @@ pip install foreignthon-es # Spanish
|
|||||||
pip install foreignthon-ta # Tamil
|
pip install foreignthon-ta # Tamil
|
||||||
```
|
```
|
||||||
|
|
||||||
## Usage
|
## Docs
|
||||||
|
|
||||||
```bash
|
- [Getting Started](docs/getting-started.md)
|
||||||
fpy run script.es.py # transpile and run
|
- [Language Packs](docs/language-packs.md)
|
||||||
fpy compile script.es.py # output a .compiled.py file
|
- [Postfix Syntax](docs/postfix-syntax.md)
|
||||||
fpy check script.es.py # validate without running
|
- [Contributing](docs/contributing.md)
|
||||||
fpy pack mylang.json # validate a language pack
|
- [Releasing](docs/releasing.md)
|
||||||
```
|
|
||||||
|
|
||||||
### 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 <your-repo>
|
|
||||||
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
|
|
||||||
```
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|||||||
63
docs/contributing.md
Normal file
63
docs/contributing.md
Normal file
@@ -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`
|
||||||
80
docs/getting-started.md
Normal file
80
docs/getting-started.md
Normal file
@@ -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.<lang>.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 <repo>
|
||||||
|
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
|
||||||
|
```
|
||||||
69
docs/language-packs.md
Normal file
69
docs/language-packs.md
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
# Language Packs
|
||||||
|
|
||||||
|
A language pack is a JSON file that maps foreign tokens to Python equivalents, published as `foreignthon-<code>` 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/<code>/`
|
||||||
|
2. Rename every `foreignthon_es` → `foreignthon_<code>`
|
||||||
|
3. Fill in `<code>.json` following the schema above
|
||||||
|
4. Validate: `fpy pack packages/langs/<code>/src/foreignthon_<code>/<code>.json`
|
||||||
|
5. Install locally: `pip install -e packages/langs/<code>`
|
||||||
|
6. Test: `fpy run myscript.<code>.py`
|
||||||
|
|
||||||
|
## Publishing a pack
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python -m build packages/langs/<code>
|
||||||
|
twine upload packages/langs/<code>/dist/*
|
||||||
|
```
|
||||||
|
|
||||||
|
Anyone can publish an independent `foreignthon-<code>` 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.
|
||||||
49
docs/postfix-syntax.md
Normal file
49
docs/postfix-syntax.md
Normal file
@@ -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.
|
||||||
Reference in New Issue
Block a user