Metadata-Version: 2.4
Name: smartpython
Version: 0.1.0
Summary: Python with braces instead of significant indentation -- transpiles .spy to .py, line for line.
Author: Xaliphostes
License-Expression: MIT
Project-URL: Homepage, https://github.com/xaliphostes/SmartPython
Project-URL: Repository, https://github.com/xaliphostes/SmartPython
Project-URL: Issues, https://github.com/xaliphostes/SmartPython/issues
Keywords: python,transpiler,preprocessor,braces,syntax
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Compilers
Classifier: Topic :: Software Development :: Pre-processors
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: dev
Requires-Dist: pytest>=7; extra == "dev"
Dynamic: license-file

# SmartPython

Python with braces instead of significant indentation. Write `.spy`, get `.py` — **line for line**, so every traceback, breakpoint and coverage report still points at the file you actually wrote. Think TypeScript → JavaScript, but the only thing that changes is how blocks are delimited.

A VS code is provided for files *.spy + a command to run a spy file + a command to transform spy -> py

```python
# hello.spy
def greet(names) {
    for name in names {
        if name {
            print(f"hello {name}")
        } else {
            print("hello, whoever you are")
        }
    }
}
```

```console
$ spy run hello.spy
$ spy build hello.spy     # writes hello.py
```

## Install

```console
$ pip install -e .
```

Requires Python 3.9+. Without installing, `python -m smartpython` works the same as `spy`.

## The language

It *is* Python. Every rule you know still applies — the transpiler only rewrites block delimiters:

| SmartPython | Python |
| --- | --- |
| `if x { y() }` | `if x: y()` |
| `if x {` … `}` | `if x:` + indented body |
| `} else {` | `else:` |
| `def f() { }` | `def f(): pass` |

Indentation inside braces is yours to choose; the generated Python is indented with four spaces (`--indent N` to change it). A trailing `:` on a header is an error — braces replace it, they don't accompany it.

Both brace styles work:

```python
def read(path)
{
    with open(path) as fh { return fh.read() }
}
```

Semicolons are optional and pass straight through, exactly as in Python.

### Braces that aren't blocks

Dict displays, set displays, comprehensions, f-string fields and `match` patterns all use `{}` too. The scanner tells them apart by looking at the token in front of the brace: a brace after a *value* opens a block, a brace after an *operator* or keyword opens a display. So this parses the way you'd expect:

```python
if value in {1, 2, 3} {     # first brace: a set. second: the block.
    return "small"
}

match record {
    case {"kind": kind} { return kind }
    case _ { return None }
}
```

Braces inside strings, f-strings (including nested quotes, `{{`/`}}` escapes and format specs) and comments are never touched.

## Commands

```console
spy build PATH...        # transpile to .py next to the source
    -o OUT               #   output file, or directory when building several
    --stdout             #   print instead of writing
    --bare               #   omit the "generated by" footer
    -f, --force          #   overwrite a hand-written .py
spy run FILE.spy [ARGS]  # transpile and execute, tracebacks mapped back
spy check PATH...        # transpile and compile, write nothing
```

`build` and `check` accept directories and recurse into them. `-` reads source from stdin; pair it with `--filename NAME` so errors name the real file:

```console
$ spy check - --filename src/app.spy < src/app.spy
```

## Editor support

A VS Code extension lives in [`editors/vscode`](editors/vscode/) — highlighting, live diagnostics as you type, a side-by-side preview of the generated Python, and build/run commands. No build step; symlink it into `~/.vscode/extensions` and reload. See its [README](editors/vscode/README.md).

`run` installs an import hook, so a `.spy` file can `import` other `.spy` modules sitting next to it. To use that hook in a normal Python process:

```python
import smartpython.importer as spy_hook
spy_hook.install()
import my_spy_module          # loads my_spy_module.spy
```

## Line-for-line output

The rewrites are chosen so the line count never changes for idiomatic input:

```
if x {          ->  if x:              the brace becomes the colon
    y()         ->      y()
}               ->                     the closing brace becomes a blank line
} else {        ->  else:              dedent and re-open in place
def f() { }     ->  def f(): pass      short blocks stay inline
def f() {       ->  def f():           an empty body borrows the '}' line
}               ->      pass
```

A few styles can't be preserved — a nested block written inline, or code after a closing brace on the same line. Those expand, and `Result.line_map` records the real mapping; `spy run` uses it to rewrite tracebacks.

## Library API

```python
from smartpython import transpile, transpile_file, SpyError

result = transpile(source, filename="thing.spy")
result.code        # the Python source
result.identity    # True when lines map 1:1
result.line_map    # generated line -> .spy line
result.source_line(42)
```

`SpyError` carries `filename`, `line`, `col` and `text`, and renders a caret diagram via `.pretty()`.

## What it deliberately does not do

No type checking, no new operators, no `//` comments, no `&&`/`||`. SmartPython is exactly Python with a different block delimiter — anything else would make the generated code stop looking like the code you wrote.

## Development

```console
$ python -m pytest
$ spy run examples/tour.spy      # a tour of the syntax and the tricky cases
```

## Known limits

* A statement whose first token is a bare set/dict display, sitting where an Allman-style `{` could go, is ambiguous; the brace wins.
* Generated files pick up a blank line wherever a `}` stood. That is the price of the 1:1 line mapping.
* Continuation lines inside brackets keep their original indentation rather than being re-indented.


## License
[MIT](./LICENSE)

## Author
[Xaliphostes](https://github.com/xaliphostes)

