myna-lang 0.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- myna_lang-0.4.0/CHANGELOG.md +29 -0
- myna_lang-0.4.0/LICENSE +21 -0
- myna_lang-0.4.0/MANIFEST.in +7 -0
- myna_lang-0.4.0/PKG-INFO +225 -0
- myna_lang-0.4.0/README.md +200 -0
- myna_lang-0.4.0/SPEC.md +193 -0
- myna_lang-0.4.0/examples/books_api.myna +77 -0
- myna_lang-0.4.0/examples/cross_target.myna +12 -0
- myna_lang-0.4.0/examples/demo.myna +33 -0
- myna_lang-0.4.0/examples/rest_api.myna +164 -0
- myna_lang-0.4.0/myna/__init__.py +43 -0
- myna_lang-0.4.0/myna/__main__.py +6 -0
- myna_lang-0.4.0/myna/ast_nodes.py +452 -0
- myna_lang-0.4.0/myna/backends/js_gen.py +767 -0
- myna_lang-0.4.0/myna/backends/python_gen.py +739 -0
- myna_lang-0.4.0/myna/bundler.py +309 -0
- myna_lang-0.4.0/myna/cli.py +268 -0
- myna_lang-0.4.0/myna/frontends/python_decompile.py +1299 -0
- myna_lang-0.4.0/myna/lexer.py +529 -0
- myna_lang-0.4.0/myna/loader.py +91 -0
- myna_lang-0.4.0/myna/parser.py +1741 -0
- myna_lang-0.4.0/myna/targets.py +39 -0
- myna_lang-0.4.0/myna_lang.egg-info/PKG-INFO +225 -0
- myna_lang-0.4.0/myna_lang.egg-info/SOURCES.txt +31 -0
- myna_lang-0.4.0/myna_lang.egg-info/dependency_links.txt +1 -0
- myna_lang-0.4.0/myna_lang.egg-info/entry_points.txt +2 -0
- myna_lang-0.4.0/myna_lang.egg-info/top_level.txt +1 -0
- myna_lang-0.4.0/pyproject.toml +38 -0
- myna_lang-0.4.0/setup.cfg +4 -0
- myna_lang-0.4.0/skills/myna/SKILL.md +103 -0
- myna_lang-0.4.0/tests/test_js.py +193 -0
- myna_lang-0.4.0/tests/test_myna.py +740 -0
- myna_lang-0.4.0/tests/test_roundtrip_corpus.py +285 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.4.0 (2026-10-24)
|
|
4
|
+
|
|
5
|
+
- `myna run` now resolves relative imports (`<.util:{f}`, `<.pkg.mod:{f}`) to sibling
|
|
6
|
+
`.myna` modules and package directories, with lazy compilation and normal
|
|
7
|
+
`sys.modules` caching. `myna compile` is still per-file, and `-t js` is single-file.
|
|
8
|
+
- Add `myna bundle input.myna -o app.py`, which embeds the relative-import graph in a
|
|
9
|
+
single standalone Python file that needs only the standard library at runtime. With
|
|
10
|
+
`--zipapp` it writes a runnable, reproducible `.pyz` archive with real module files.
|
|
11
|
+
- Lex/parse errors raised while running or importing a module now include the source
|
|
12
|
+
file path.
|
|
13
|
+
- Fix `:?` runtime checks with compound types (`[T]`, `{K:V}`, `{T}`, `Name[T]`): they
|
|
14
|
+
emitted invalid Python (`isinstance(x, [s])`) and JS (`x instanceof [s]`). They now
|
|
15
|
+
check the runtime origin type (`list`, `dict`, `set`, `Name`); `(A, B)` remains a union.
|
|
16
|
+
- Fix `pip install -e .` failing because setuptools flat-layout discovery treated
|
|
17
|
+
`skills/` as a package; the distributed packages are now declared explicitly.
|
|
18
|
+
|
|
19
|
+
## 0.3.0
|
|
20
|
+
|
|
21
|
+
- Add module-level `@target(...)` declarations and compile-time `@target(...) { ... }` blocks.
|
|
22
|
+
- Add `myna targets` to report declared target support and likely JavaScript import incompatibilities.
|
|
23
|
+
- Harden Python-to-Myna round-trips: corpus tests, lossless byte literals, f-string conversions, clearer `DecompileError` failures.
|
|
24
|
+
- Improve parser handling of tuple conditions, brace-adjacent definition sigils, and f-string error locations.
|
|
25
|
+
- Print concise language errors from the CLI instead of tracebacks.
|
|
26
|
+
|
|
27
|
+
## 0.2.0
|
|
28
|
+
|
|
29
|
+
- Initial public release of Myna with Python and JavaScript backends, Python decompilation, and token-efficiency stats.
|
myna_lang-0.4.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Joseph Hicks
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
myna_lang-0.4.0/PKG-INFO
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: myna-lang
|
|
3
|
+
Version: 0.4.0
|
|
4
|
+
Summary: Myna: An AI-friendly, token-efficient symbolic language that transpiles to/from Python and JavaScript
|
|
5
|
+
Author: Joseph Hicks
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/jhicksdev/myna
|
|
8
|
+
Project-URL: Repository, https://github.com/jhicksdev/myna
|
|
9
|
+
Project-URL: Issues, https://github.com/jhicksdev/myna/issues
|
|
10
|
+
Project-URL: Changelog, https://github.com/jhicksdev/myna/blob/main/CHANGELOG.md
|
|
11
|
+
Keywords: language,transpiler,compiler,python,javascript,llm,code-generation
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Topic :: Software Development :: Code Generators
|
|
20
|
+
Classifier: Topic :: Software Development :: Compilers
|
|
21
|
+
Requires-Python: >=3.11
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Dynamic: license-file
|
|
25
|
+
|
|
26
|
+
# Myna (`myna-lang`)
|
|
27
|
+
|
|
28
|
+
**Myna** is an ultra-dense, symbol-based, AI-native programming language designed to cut LLM token and character usage by **~50%** while transpiling 1:1 to **Python 3.12+** and **JavaScript (ES2024+)**—and decompiling existing Python code back into Myna.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Why Myna?
|
|
33
|
+
|
|
34
|
+
Coding agents spend thousands of context and output tokens on repetitive syntactic boilerplate (`def __init__(self, ...):`, `self.`, `Optional[...]`, `try ... except Exception as e:`, quoted JSON keys, and 4-space indentation blocks).
|
|
35
|
+
|
|
36
|
+
Myna replaces verbose multi-token boilerplate with **tokenizer-friendly ASCII sigils and digraphs** (`$$`, `@`, `~@`, `=>`, `??`, `!?`, `!&`, `$`, `$?`) and brace-delimited blocks (`{}`):
|
|
37
|
+
- **~46%–52% fewer LLM tokens** and **~48%–58% fewer characters** than equivalent Python (compact and ultra-compact modes).
|
|
38
|
+
- **Zero indentation fragility:** Blocks use `{ ... }` or `=>`, making partial diffs and single-line generation rock-solid for AI agents.
|
|
39
|
+
- **Bi-directional (`Python <-> Myna`) & Multi-Target (`Myna -> Python / JS`):** Compress existing Python codebases into `.myna` to fit twice as much code into an agent's context window, or let agents write `.myna` and transpile to clean Python or JavaScript.
|
|
40
|
+
- **Agent-Ready:** Ships with a compact Rosetta Stone skill (`skills/myna/SKILL.md`) that teaches any coding agent to read and write idiomatic Myna in one shot.
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Side-by-Side Comparison
|
|
45
|
+
|
|
46
|
+
### Python (21 lines, 611 chars)
|
|
47
|
+
```python
|
|
48
|
+
from dataclasses import dataclass
|
|
49
|
+
import asyncio
|
|
50
|
+
|
|
51
|
+
@dataclass
|
|
52
|
+
class Task:
|
|
53
|
+
id: int
|
|
54
|
+
title: str
|
|
55
|
+
priority: int = 1
|
|
56
|
+
done: bool = False
|
|
57
|
+
|
|
58
|
+
def to_dict(self) -> dict[str, object]:
|
|
59
|
+
return {"id": self.id, "title": self.title, "done": self.done}
|
|
60
|
+
|
|
61
|
+
async def pending(tasks: list[Task], min_p: int = 1) -> list[dict[str, object]]:
|
|
62
|
+
try:
|
|
63
|
+
await asyncio.sleep(0.01)
|
|
64
|
+
items = [t for t in tasks if not t.done and t.priority >= min_p]
|
|
65
|
+
return [t.to_dict() for t in sorted(items, key=lambda t: -t.priority)]
|
|
66
|
+
except Exception as e:
|
|
67
|
+
print(f"Error: {e}")
|
|
68
|
+
return []
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Myna (11 lines, 299 chars — 51% smaller)
|
|
72
|
+
```myna
|
|
73
|
+
<asyncio
|
|
74
|
+
$$Task(id:i,title:s,priority:i=1,done:b=F){
|
|
75
|
+
@to_dict->{s:A}=>{.id,.title,.done}
|
|
76
|
+
}
|
|
77
|
+
~@pending(tasks:[Task],min_p:i=1)->[{s:A}]{
|
|
78
|
+
!{
|
|
79
|
+
~asyncio.sleep(0.01)
|
|
80
|
+
items=[t|t<-tasks?!t.done&&t.priority>=min_p]
|
|
81
|
+
^[t.to_dict()|t<-@srt(items,key=t=>-t.priority)]
|
|
82
|
+
}!!e{p(`Error: {e}`);^[]}
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## Quickstart
|
|
89
|
+
|
|
90
|
+
Myna requires **Python 3.11+** and has **zero external dependencies**.
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
# Install from PyPI
|
|
94
|
+
pip install myna-lang # or: uv tool install myna-lang / pipx install myna-lang
|
|
95
|
+
|
|
96
|
+
# Install for local development (from a checkout)
|
|
97
|
+
pip install -e .
|
|
98
|
+
|
|
99
|
+
# Create and run a script
|
|
100
|
+
printf 'p(`hello from {1+1}`)\n' > hello.myna
|
|
101
|
+
myna run hello.myna
|
|
102
|
+
|
|
103
|
+
# The remaining examples are in a checkout of the repository.
|
|
104
|
+
# Run a Myna script directly (via Python)
|
|
105
|
+
python3 -m myna run examples/demo.myna
|
|
106
|
+
|
|
107
|
+
# Run a portable Myna script via Node.js (JavaScript target)
|
|
108
|
+
python3 -m myna run -t js examples/cross_target.myna
|
|
109
|
+
|
|
110
|
+
# Compile Myna -> Python (.py)
|
|
111
|
+
python3 -m myna compile examples/rest_api.myna -o rest_api.py
|
|
112
|
+
|
|
113
|
+
# Compile Myna -> JavaScript (.js)
|
|
114
|
+
python3 -m myna compile -t js examples/cross_target.myna -o cross_target.js
|
|
115
|
+
|
|
116
|
+
# Bundle a multi-file program into one standalone Python file
|
|
117
|
+
python3 -m myna bundle src/main.myna -o app.py
|
|
118
|
+
|
|
119
|
+
# ...or into a runnable zipapp (stdlib-only, executable)
|
|
120
|
+
python3 -m myna bundle src/main.myna --zipapp -o app.pyz
|
|
121
|
+
|
|
122
|
+
# Compress existing Python -> Myna (.myna)
|
|
123
|
+
python3 -m myna decompile script.py -o script.myna
|
|
124
|
+
|
|
125
|
+
# Ultra-compact decompilation (with local variable minification)
|
|
126
|
+
python3 -m myna decompile -m script.py -o script.min.myna
|
|
127
|
+
|
|
128
|
+
# Compare token & character efficiency
|
|
129
|
+
python3 -m myna stats examples/books_api.myna
|
|
130
|
+
|
|
131
|
+
# Report declared/inferred Python & JavaScript support
|
|
132
|
+
python3 -m myna targets examples/demo.myna
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Multi-file programs
|
|
136
|
+
|
|
137
|
+
`myna run` gives relative imports a package context, so a program can be split into
|
|
138
|
+
sibling `.myna` files:
|
|
139
|
+
|
|
140
|
+
```myna
|
|
141
|
+
# main.myna
|
|
142
|
+
<.util:{slug}
|
|
143
|
+
p(slug("Hello World"))
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
```myna
|
|
147
|
+
# util.myna
|
|
148
|
+
@slug(text:s)->s { ^ text.lower().replace(" ", "-") }
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
- `<.util:{slug}` → `util.myna` beside the entry file.
|
|
152
|
+
- `<.pkg.helper:{parse}` → `pkg/helper.myna`; `pkg` may have an `__init__.myna` or be a
|
|
153
|
+
plain directory (namespace package).
|
|
154
|
+
- Absolute imports (`<json`, `<mypkg>alias`) keep their normal Python meaning.
|
|
155
|
+
- Imported files are compiled on first import and cached by Python's import system.
|
|
156
|
+
- `myna bundle src/main.myna -o app.py` embeds the whole relative-import graph in one
|
|
157
|
+
standalone Python file. It runs with a plain `python3` and does not need `myna`
|
|
158
|
+
installed; absolute imports still come from the environment.
|
|
159
|
+
- `myna bundle src/main.myna --zipapp -o app.pyz` writes a runnable, executable zipapp
|
|
160
|
+
containing real module files (`python3 app.pyz`). It is reproducible: rebuilding the
|
|
161
|
+
same sources produces identical bytes. Without `-o`, the archive lands next to the
|
|
162
|
+
entry file as `<name>.pyz`.
|
|
163
|
+
- `myna compile` still compiles one file at a time. If you use relative imports, the
|
|
164
|
+
emitted `.py` files must be placed in a matching Python package.
|
|
165
|
+
- `myna run -t js` is single-file for now; multi-file resolution and bundling are
|
|
166
|
+
Python-target only.
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## Token & Character Efficiency Benchmarks
|
|
171
|
+
|
|
172
|
+
Measured by compiling each example to Python and running `python3 -m myna stats` on the generated file (so the Myna columns reflect the decompiler's `compact` and `-m` modes). Counts use the built-in `bpe-approx` estimator unless `tiktoken` is installed:
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
python3 -m myna compile examples/demo.myna -o /tmp/demo.py
|
|
176
|
+
python3 -m myna stats /tmp/demo.py
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Running `stats` directly on a `.myna` file instead compares the generated Python against that file as written, so its Myna counts will differ from the table.
|
|
180
|
+
|
|
181
|
+
| Example Program | Python Tokens / Chars | Myna (Compact) | Myna (Ultra-Compact `-m`) |
|
|
182
|
+
| :--- | :--- | :--- | :--- |
|
|
183
|
+
| **`examples/demo.myna`** (Async Task Pipeline) | `517` toks / `953` chars | `271` toks (**-47.6%**) / `463` chars (**-51.4%**) | **`247` toks (-52.2%)** / **`397` chars (-58.3%)** |
|
|
184
|
+
| **`examples/rest_api.myna`** (Todo HTTP REST API) | `4,105` toks / `8,011` chars | `2,183` toks (**-46.8%**) / `3,932` chars (**-50.9%**) | **`2,045` toks (-50.2%)** / **`3,573` chars (-55.4%)** |
|
|
185
|
+
| **`examples/books_api.myna`** (Library Catalog API) | `4,402` toks / `8,515` chars | `2,371` toks (**-46.1%**) / `4,381` chars (**-48.5%**) | **`2,289` toks (-48.0%)** / **`4,104` chars (-51.8%)** |
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
## Language Cheat Sheet
|
|
190
|
+
|
|
191
|
+
| Feature | Myna Syntax | Python Equivalent | JavaScript Equivalent |
|
|
192
|
+
| :--- | :--- | :--- | :--- |
|
|
193
|
+
| **Literals** | `T`, `F`, `N`, `U`, `_` | `True`, `False`, `None`, `None`, `pass` | `true`, `false`, `null`, `undefined`, `/*noop*/` |
|
|
194
|
+
| **Types** | `i`, `f`, `s`, `b`, `B`, `A`, `T?`, `[T]`, `{K:V}` | `int`, `float`, `str`, `bool`, `bytes`, `Any`, `T \| None`, `list[T]`, `dict[K, V]` | *(Type annotations stripped in JS)* |
|
|
195
|
+
| **Imports** | `<os, numpy>np` / `<mod:{a, b>c}` | `import os, numpy as np` / `from mod import a, b as c` | `import * as ...` / `import { a, b as c } from ...` |
|
|
196
|
+
| **Targets** | `@target(py)` / `@target(py) { ... }` | Python-only module / Python-only block | JS-only module / JS-only block |
|
|
197
|
+
| **Functions** | `@f(a:i, b=1)->i { ^a+b }` or `@f(a)=>a+1` | `def f(a: int, b=1) -> int: return a + b` | `function f(a, b = 1) { return a + b; }` |
|
|
198
|
+
| **Async / Gen** | `~@f` / `~expr` / `*@gen` / `^*val` | `async def` / `await` / generator / `yield` | `async function` / `await` / `function*` / `yield` |
|
|
199
|
+
| **Classes** | `$C < Base { @init(x){ .x=x } }` | `class C(Base): def __init__(self, x): self.x = x` | `class C extends Base { constructor(x) { this.x = x; } }` |
|
|
200
|
+
| **Dataclasses** | `$$User(id:i, name:s="anon")` | `@dataclass class User: ...` | `class User { constructor(id, name = "anon") ... }` |
|
|
201
|
+
| **Dict Shorthands** | `{.id, .title, =name, ok: T}` | `{"id": self.id, "title": self.title, "name": name, "ok": True}` | `({id: this.id, title: this.title, name, ok: true})` |
|
|
202
|
+
| **Key Lookup** | `d$key` / `d$?key ?? "def"` | `d["key"]` / `d.get("key") or "def"` | `d["key"]` / `d.get("key") ?? "def"` |
|
|
203
|
+
| **Branching** | `? c1 {...} :? c2 {...} : {...}` | `if c1: ... elif c2: ... else: ...` | `if (c1) {...} else if (c2) {...} else {...}` |
|
|
204
|
+
| **Pattern Match** | `?? v { 0 => {...}; x ? x>0 => {...}; _ => {...} }` | `match v: case 0: ... case x if x > 0: ... case _: ...` | Block-scoped pattern & guard dispatch |
|
|
205
|
+
| **Loops** | `% x <- 0..10 { ... }` / `%? cond { ... }` | `for x in range(10): ...` / `while cond: ...` | `for (let x = 0; x < 10; x += 1)` / `while (cond)` |
|
|
206
|
+
| **Comprehensions** | `[x*2 \| x <- items ? x > 0]` | `[x * 2 for x in items if x > 0]` | `items.filter(x => x > 0).map(x => x * 2)` |
|
|
207
|
+
| **Exceptions** | `!{ ... } !!e:ValueError { ... } !: { ... }` | `try: ... except ValueError as e: ... finally: ...` | `try { ... } catch (e) { ... } finally { ... }` |
|
|
208
|
+
| **With / Assert** | `!& f=open(p) { ... }` / `!? cond, "msg"` | `with open(p) as f:` / `assert cond, "msg"` | Scoped try/finally `.close()` / `if (!cond) throw ...` |
|
|
209
|
+
| **Built-in Aliases** | `p`, `#x`, `@j`, `@J`, `@u8`, `@u8d`, `@srt`, `@sum` | `print`, `len(x)`, `json.loads`, `json.dumps`, `.encode`, `.decode`, `sorted`, `sum` | `console.log`, `len`, `JSON.parse`, `JSON.stringify`, `TextEncoder`, `TextDecoder`, `.sort`, `.reduce` |
|
|
210
|
+
|
|
211
|
+
See [`SPEC.md`](https://github.com/jhicksdev/myna/blob/main/SPEC.md) for the complete language specification and [`skills/myna/SKILL.md`](https://github.com/jhicksdev/myna/blob/main/skills/myna/SKILL.md) for the AI agent skill file.
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
## Running Tests
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
python3 -m unittest discover -s tests -v
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## License
|
|
224
|
+
|
|
225
|
+
[MIT](https://github.com/jhicksdev/myna/blob/main/LICENSE)
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
# Myna (`myna-lang`)
|
|
2
|
+
|
|
3
|
+
**Myna** is an ultra-dense, symbol-based, AI-native programming language designed to cut LLM token and character usage by **~50%** while transpiling 1:1 to **Python 3.12+** and **JavaScript (ES2024+)**—and decompiling existing Python code back into Myna.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Why Myna?
|
|
8
|
+
|
|
9
|
+
Coding agents spend thousands of context and output tokens on repetitive syntactic boilerplate (`def __init__(self, ...):`, `self.`, `Optional[...]`, `try ... except Exception as e:`, quoted JSON keys, and 4-space indentation blocks).
|
|
10
|
+
|
|
11
|
+
Myna replaces verbose multi-token boilerplate with **tokenizer-friendly ASCII sigils and digraphs** (`$$`, `@`, `~@`, `=>`, `??`, `!?`, `!&`, `$`, `$?`) and brace-delimited blocks (`{}`):
|
|
12
|
+
- **~46%–52% fewer LLM tokens** and **~48%–58% fewer characters** than equivalent Python (compact and ultra-compact modes).
|
|
13
|
+
- **Zero indentation fragility:** Blocks use `{ ... }` or `=>`, making partial diffs and single-line generation rock-solid for AI agents.
|
|
14
|
+
- **Bi-directional (`Python <-> Myna`) & Multi-Target (`Myna -> Python / JS`):** Compress existing Python codebases into `.myna` to fit twice as much code into an agent's context window, or let agents write `.myna` and transpile to clean Python or JavaScript.
|
|
15
|
+
- **Agent-Ready:** Ships with a compact Rosetta Stone skill (`skills/myna/SKILL.md`) that teaches any coding agent to read and write idiomatic Myna in one shot.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Side-by-Side Comparison
|
|
20
|
+
|
|
21
|
+
### Python (21 lines, 611 chars)
|
|
22
|
+
```python
|
|
23
|
+
from dataclasses import dataclass
|
|
24
|
+
import asyncio
|
|
25
|
+
|
|
26
|
+
@dataclass
|
|
27
|
+
class Task:
|
|
28
|
+
id: int
|
|
29
|
+
title: str
|
|
30
|
+
priority: int = 1
|
|
31
|
+
done: bool = False
|
|
32
|
+
|
|
33
|
+
def to_dict(self) -> dict[str, object]:
|
|
34
|
+
return {"id": self.id, "title": self.title, "done": self.done}
|
|
35
|
+
|
|
36
|
+
async def pending(tasks: list[Task], min_p: int = 1) -> list[dict[str, object]]:
|
|
37
|
+
try:
|
|
38
|
+
await asyncio.sleep(0.01)
|
|
39
|
+
items = [t for t in tasks if not t.done and t.priority >= min_p]
|
|
40
|
+
return [t.to_dict() for t in sorted(items, key=lambda t: -t.priority)]
|
|
41
|
+
except Exception as e:
|
|
42
|
+
print(f"Error: {e}")
|
|
43
|
+
return []
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Myna (11 lines, 299 chars — 51% smaller)
|
|
47
|
+
```myna
|
|
48
|
+
<asyncio
|
|
49
|
+
$$Task(id:i,title:s,priority:i=1,done:b=F){
|
|
50
|
+
@to_dict->{s:A}=>{.id,.title,.done}
|
|
51
|
+
}
|
|
52
|
+
~@pending(tasks:[Task],min_p:i=1)->[{s:A}]{
|
|
53
|
+
!{
|
|
54
|
+
~asyncio.sleep(0.01)
|
|
55
|
+
items=[t|t<-tasks?!t.done&&t.priority>=min_p]
|
|
56
|
+
^[t.to_dict()|t<-@srt(items,key=t=>-t.priority)]
|
|
57
|
+
}!!e{p(`Error: {e}`);^[]}
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## Quickstart
|
|
64
|
+
|
|
65
|
+
Myna requires **Python 3.11+** and has **zero external dependencies**.
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
# Install from PyPI
|
|
69
|
+
pip install myna-lang # or: uv tool install myna-lang / pipx install myna-lang
|
|
70
|
+
|
|
71
|
+
# Install for local development (from a checkout)
|
|
72
|
+
pip install -e .
|
|
73
|
+
|
|
74
|
+
# Create and run a script
|
|
75
|
+
printf 'p(`hello from {1+1}`)\n' > hello.myna
|
|
76
|
+
myna run hello.myna
|
|
77
|
+
|
|
78
|
+
# The remaining examples are in a checkout of the repository.
|
|
79
|
+
# Run a Myna script directly (via Python)
|
|
80
|
+
python3 -m myna run examples/demo.myna
|
|
81
|
+
|
|
82
|
+
# Run a portable Myna script via Node.js (JavaScript target)
|
|
83
|
+
python3 -m myna run -t js examples/cross_target.myna
|
|
84
|
+
|
|
85
|
+
# Compile Myna -> Python (.py)
|
|
86
|
+
python3 -m myna compile examples/rest_api.myna -o rest_api.py
|
|
87
|
+
|
|
88
|
+
# Compile Myna -> JavaScript (.js)
|
|
89
|
+
python3 -m myna compile -t js examples/cross_target.myna -o cross_target.js
|
|
90
|
+
|
|
91
|
+
# Bundle a multi-file program into one standalone Python file
|
|
92
|
+
python3 -m myna bundle src/main.myna -o app.py
|
|
93
|
+
|
|
94
|
+
# ...or into a runnable zipapp (stdlib-only, executable)
|
|
95
|
+
python3 -m myna bundle src/main.myna --zipapp -o app.pyz
|
|
96
|
+
|
|
97
|
+
# Compress existing Python -> Myna (.myna)
|
|
98
|
+
python3 -m myna decompile script.py -o script.myna
|
|
99
|
+
|
|
100
|
+
# Ultra-compact decompilation (with local variable minification)
|
|
101
|
+
python3 -m myna decompile -m script.py -o script.min.myna
|
|
102
|
+
|
|
103
|
+
# Compare token & character efficiency
|
|
104
|
+
python3 -m myna stats examples/books_api.myna
|
|
105
|
+
|
|
106
|
+
# Report declared/inferred Python & JavaScript support
|
|
107
|
+
python3 -m myna targets examples/demo.myna
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Multi-file programs
|
|
111
|
+
|
|
112
|
+
`myna run` gives relative imports a package context, so a program can be split into
|
|
113
|
+
sibling `.myna` files:
|
|
114
|
+
|
|
115
|
+
```myna
|
|
116
|
+
# main.myna
|
|
117
|
+
<.util:{slug}
|
|
118
|
+
p(slug("Hello World"))
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
```myna
|
|
122
|
+
# util.myna
|
|
123
|
+
@slug(text:s)->s { ^ text.lower().replace(" ", "-") }
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
- `<.util:{slug}` → `util.myna` beside the entry file.
|
|
127
|
+
- `<.pkg.helper:{parse}` → `pkg/helper.myna`; `pkg` may have an `__init__.myna` or be a
|
|
128
|
+
plain directory (namespace package).
|
|
129
|
+
- Absolute imports (`<json`, `<mypkg>alias`) keep their normal Python meaning.
|
|
130
|
+
- Imported files are compiled on first import and cached by Python's import system.
|
|
131
|
+
- `myna bundle src/main.myna -o app.py` embeds the whole relative-import graph in one
|
|
132
|
+
standalone Python file. It runs with a plain `python3` and does not need `myna`
|
|
133
|
+
installed; absolute imports still come from the environment.
|
|
134
|
+
- `myna bundle src/main.myna --zipapp -o app.pyz` writes a runnable, executable zipapp
|
|
135
|
+
containing real module files (`python3 app.pyz`). It is reproducible: rebuilding the
|
|
136
|
+
same sources produces identical bytes. Without `-o`, the archive lands next to the
|
|
137
|
+
entry file as `<name>.pyz`.
|
|
138
|
+
- `myna compile` still compiles one file at a time. If you use relative imports, the
|
|
139
|
+
emitted `.py` files must be placed in a matching Python package.
|
|
140
|
+
- `myna run -t js` is single-file for now; multi-file resolution and bundling are
|
|
141
|
+
Python-target only.
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## Token & Character Efficiency Benchmarks
|
|
146
|
+
|
|
147
|
+
Measured by compiling each example to Python and running `python3 -m myna stats` on the generated file (so the Myna columns reflect the decompiler's `compact` and `-m` modes). Counts use the built-in `bpe-approx` estimator unless `tiktoken` is installed:
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
python3 -m myna compile examples/demo.myna -o /tmp/demo.py
|
|
151
|
+
python3 -m myna stats /tmp/demo.py
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Running `stats` directly on a `.myna` file instead compares the generated Python against that file as written, so its Myna counts will differ from the table.
|
|
155
|
+
|
|
156
|
+
| Example Program | Python Tokens / Chars | Myna (Compact) | Myna (Ultra-Compact `-m`) |
|
|
157
|
+
| :--- | :--- | :--- | :--- |
|
|
158
|
+
| **`examples/demo.myna`** (Async Task Pipeline) | `517` toks / `953` chars | `271` toks (**-47.6%**) / `463` chars (**-51.4%**) | **`247` toks (-52.2%)** / **`397` chars (-58.3%)** |
|
|
159
|
+
| **`examples/rest_api.myna`** (Todo HTTP REST API) | `4,105` toks / `8,011` chars | `2,183` toks (**-46.8%**) / `3,932` chars (**-50.9%**) | **`2,045` toks (-50.2%)** / **`3,573` chars (-55.4%)** |
|
|
160
|
+
| **`examples/books_api.myna`** (Library Catalog API) | `4,402` toks / `8,515` chars | `2,371` toks (**-46.1%**) / `4,381` chars (**-48.5%**) | **`2,289` toks (-48.0%)** / **`4,104` chars (-51.8%)** |
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## Language Cheat Sheet
|
|
165
|
+
|
|
166
|
+
| Feature | Myna Syntax | Python Equivalent | JavaScript Equivalent |
|
|
167
|
+
| :--- | :--- | :--- | :--- |
|
|
168
|
+
| **Literals** | `T`, `F`, `N`, `U`, `_` | `True`, `False`, `None`, `None`, `pass` | `true`, `false`, `null`, `undefined`, `/*noop*/` |
|
|
169
|
+
| **Types** | `i`, `f`, `s`, `b`, `B`, `A`, `T?`, `[T]`, `{K:V}` | `int`, `float`, `str`, `bool`, `bytes`, `Any`, `T \| None`, `list[T]`, `dict[K, V]` | *(Type annotations stripped in JS)* |
|
|
170
|
+
| **Imports** | `<os, numpy>np` / `<mod:{a, b>c}` | `import os, numpy as np` / `from mod import a, b as c` | `import * as ...` / `import { a, b as c } from ...` |
|
|
171
|
+
| **Targets** | `@target(py)` / `@target(py) { ... }` | Python-only module / Python-only block | JS-only module / JS-only block |
|
|
172
|
+
| **Functions** | `@f(a:i, b=1)->i { ^a+b }` or `@f(a)=>a+1` | `def f(a: int, b=1) -> int: return a + b` | `function f(a, b = 1) { return a + b; }` |
|
|
173
|
+
| **Async / Gen** | `~@f` / `~expr` / `*@gen` / `^*val` | `async def` / `await` / generator / `yield` | `async function` / `await` / `function*` / `yield` |
|
|
174
|
+
| **Classes** | `$C < Base { @init(x){ .x=x } }` | `class C(Base): def __init__(self, x): self.x = x` | `class C extends Base { constructor(x) { this.x = x; } }` |
|
|
175
|
+
| **Dataclasses** | `$$User(id:i, name:s="anon")` | `@dataclass class User: ...` | `class User { constructor(id, name = "anon") ... }` |
|
|
176
|
+
| **Dict Shorthands** | `{.id, .title, =name, ok: T}` | `{"id": self.id, "title": self.title, "name": name, "ok": True}` | `({id: this.id, title: this.title, name, ok: true})` |
|
|
177
|
+
| **Key Lookup** | `d$key` / `d$?key ?? "def"` | `d["key"]` / `d.get("key") or "def"` | `d["key"]` / `d.get("key") ?? "def"` |
|
|
178
|
+
| **Branching** | `? c1 {...} :? c2 {...} : {...}` | `if c1: ... elif c2: ... else: ...` | `if (c1) {...} else if (c2) {...} else {...}` |
|
|
179
|
+
| **Pattern Match** | `?? v { 0 => {...}; x ? x>0 => {...}; _ => {...} }` | `match v: case 0: ... case x if x > 0: ... case _: ...` | Block-scoped pattern & guard dispatch |
|
|
180
|
+
| **Loops** | `% x <- 0..10 { ... }` / `%? cond { ... }` | `for x in range(10): ...` / `while cond: ...` | `for (let x = 0; x < 10; x += 1)` / `while (cond)` |
|
|
181
|
+
| **Comprehensions** | `[x*2 \| x <- items ? x > 0]` | `[x * 2 for x in items if x > 0]` | `items.filter(x => x > 0).map(x => x * 2)` |
|
|
182
|
+
| **Exceptions** | `!{ ... } !!e:ValueError { ... } !: { ... }` | `try: ... except ValueError as e: ... finally: ...` | `try { ... } catch (e) { ... } finally { ... }` |
|
|
183
|
+
| **With / Assert** | `!& f=open(p) { ... }` / `!? cond, "msg"` | `with open(p) as f:` / `assert cond, "msg"` | Scoped try/finally `.close()` / `if (!cond) throw ...` |
|
|
184
|
+
| **Built-in Aliases** | `p`, `#x`, `@j`, `@J`, `@u8`, `@u8d`, `@srt`, `@sum` | `print`, `len(x)`, `json.loads`, `json.dumps`, `.encode`, `.decode`, `sorted`, `sum` | `console.log`, `len`, `JSON.parse`, `JSON.stringify`, `TextEncoder`, `TextDecoder`, `.sort`, `.reduce` |
|
|
185
|
+
|
|
186
|
+
See [`SPEC.md`](https://github.com/jhicksdev/myna/blob/main/SPEC.md) for the complete language specification and [`skills/myna/SKILL.md`](https://github.com/jhicksdev/myna/blob/main/skills/myna/SKILL.md) for the AI agent skill file.
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
## Running Tests
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
python3 -m unittest discover -s tests -v
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## License
|
|
199
|
+
|
|
200
|
+
[MIT](https://github.com/jhicksdev/myna/blob/main/LICENSE)
|