Compare commits
2 Commits
4436ddb8f3
...
bd53d1c0f5
| Author | SHA1 | Date | |
|---|---|---|---|
| bd53d1c0f5 | |||
| ff9f0e1e14 |
@@ -1 +1,95 @@
|
|||||||
# Custom Packs
|
## Custom Packs
|
||||||
|
|
||||||
|
ForeignThon lets you extend or override any installed language pack locally — no PyPI account, no new package required. You can also scaffold a completely new language from scratch.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Local override
|
||||||
|
|
||||||
|
Create a `custom.json` in your project root with only the keys you want to change:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"builtins": {
|
||||||
|
"show": "print"
|
||||||
|
},
|
||||||
|
"keywords": {
|
||||||
|
"when": "if"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Then reference it in `.foreignthon.toml`:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[foreignthon]
|
||||||
|
lang = "es"
|
||||||
|
custom_pack = "custom.json"
|
||||||
|
```
|
||||||
|
|
||||||
|
Custom keys are merged on top of the installed pack. Installed pack keys are preserved — only the keys you define in `custom.json` are overridden.
|
||||||
|
|
||||||
|
!!! tip
|
||||||
|
ForeignThon walks up the directory tree to find `.foreignthon.toml`, so you can place it at the project root and run `fpy` from any subdirectory.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Scaffold a new language
|
||||||
|
|
||||||
|
If no pack exists for your language yet:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
fpy new myproject --lang custom
|
||||||
|
```
|
||||||
|
|
||||||
|
You will be prompted for:
|
||||||
|
|
||||||
|
- Language code (e.g. `ru`, `fr`, `ar`)
|
||||||
|
- English name (e.g. `Russian`)
|
||||||
|
- Native name (e.g. `Русский`)
|
||||||
|
|
||||||
|
This generates a `custom.json` based on the official template — every Python keyword, builtin, exception, and stdlib module is listed with the English value as a placeholder. Replace the **keys** with your language's words.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"keywords": {
|
||||||
|
"if": "if", ← replace the key, keep the value
|
||||||
|
"for": "for",
|
||||||
|
"def": "def",
|
||||||
|
...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The `.foreignthon.toml` is automatically wired to use this file.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Pack schema
|
||||||
|
|
||||||
|
A standalone pack must have these top-level sections:
|
||||||
|
|
||||||
|
| Section | Purpose |
|
||||||
|
|---|---|
|
||||||
|
| `meta` | Name, code, version, authors |
|
||||||
|
| `keywords` | Python reserved words |
|
||||||
|
| `builtins` | Built-in functions |
|
||||||
|
| `exceptions` | Built-in exception classes |
|
||||||
|
| `error_messages` | Translations for bilingual error output |
|
||||||
|
| `stdlib` | Common standard library module names |
|
||||||
|
| `postfix_keywords` | English keywords to rewrite in `--postfix` output |
|
||||||
|
|
||||||
|
Validate your pack at any time:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
fpy pack custom.json
|
||||||
|
# ✓ Pack 'Russian' is valid.
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Publishing
|
||||||
|
|
||||||
|
Once your `custom.json` is complete and working, you can turn it into a proper `foreignthon-xx` package on PyPI so others can install it with `pip install foreignthon-xx`.
|
||||||
|
|
||||||
|
See [Contributing → Language Packs](contributing/language-packs.md) for the full guide. Custom Packs
|
||||||
|
|||||||
@@ -1 +1,87 @@
|
|||||||
# Postfix Syntax
|
## Postfix Syntax
|
||||||
|
|
||||||
|
Some languages are SOV — subject-object-verb — meaning the condition naturally comes before the keyword rather than after it. ForeignThon supports this with the `@@` operator.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## The problem
|
||||||
|
|
||||||
|
In English-order Python, the keyword always comes first:
|
||||||
|
|
||||||
|
```python
|
||||||
|
if condition:
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
In many languages, the natural order is the opposite — the condition is stated first, then the action. Forcing English word order on these languages makes the code feel unnatural.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## The solution
|
||||||
|
|
||||||
|
The `@@` operator lets you put any keyword after its expression:
|
||||||
|
|
||||||
|
```
|
||||||
|
condition @@keyword:
|
||||||
|
body
|
||||||
|
```
|
||||||
|
|
||||||
|
This is equivalent to:
|
||||||
|
|
||||||
|
```
|
||||||
|
keyword condition:
|
||||||
|
body
|
||||||
|
```
|
||||||
|
|
||||||
|
Both produce identical compiled Python. `@@` is purely a source-level syntax — it is processed before tokenization and never appears in the output.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Rules
|
||||||
|
|
||||||
|
- `@@` rewrites only the line it appears on — nothing else changes
|
||||||
|
- Indentation follows standard Python rules, unchanged
|
||||||
|
- Prefix and postfix can be mixed freely in the same file
|
||||||
|
- Works for any keyword in any language pack
|
||||||
|
- `@@` is not valid Python syntax, so it never conflicts with existing code
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Supported constructs
|
||||||
|
|
||||||
|
| Construct | Prefix | Postfix |
|
||||||
|
|---|---|---|
|
||||||
|
| if | `keyword condition:` | `condition @@keyword:` |
|
||||||
|
| elif | `keyword condition:` | `condition @@keyword:` |
|
||||||
|
| while | `keyword condition:` | `condition @@keyword:` |
|
||||||
|
| def | `keyword name(args):` | `name(args) @@keyword:` |
|
||||||
|
| class | `keyword Name:` | `Name @@keyword:` |
|
||||||
|
| for | `keyword var in iter:` | `var @@in_kw iter @@for_kw:` |
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
`for` loops with postfix require two `@@` operators and can be complex. Most users keep `for` in prefix style.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Decompile with postfix
|
||||||
|
|
||||||
|
When converting Python back to a foreign language, pass `--postfix`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
fpy decompile script.py --lang <code> --postfix
|
||||||
|
```
|
||||||
|
|
||||||
|
Which keywords get rewritten is controlled by the `postfix_keywords` list in the language pack JSON. A language that uses SVO order sets this to `[]` — postfix output is never forced on languages that don't need it.
|
||||||
|
|
||||||
|
```json
|
||||||
|
"postfix_keywords": ["if", "elif", "while", "def", "class"]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Input vs output
|
||||||
|
|
||||||
|
| Direction | Mechanism | Controlled by |
|
||||||
|
|---|---|---|
|
||||||
|
| Input (writing `.xx.py`) | `@@` in source | Always available for any keyword |
|
||||||
|
| Output (`fpy decompile --postfix`) | Pack's `postfix_keywords` | Language pack author | Postfix Syntax
|
||||||
|
|||||||
Reference in New Issue
Block a user