Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ ENV/
.codex
.claude
.agents
PLAN.md

# tmp
pytest-*
Expand Down
64 changes: 62 additions & 2 deletions .releaserc.json
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,62 @@
}
],
"countMatches": true
},
{
"files": ["packages/aix/pyproject.toml"],
"from": "version = \".*\" # semantic-release",
"to": "version = \"${nextRelease.version}\" # semantic-release",
"results": [
{
"file": "packages/aix/pyproject.toml",
"hasChanged": true,
"numMatches": 1,
"numReplacements": 1
}
],
"countMatches": true
},
{
"files": ["packages/aix/pyproject.toml"],
"from": " \"astx == .*\", # semantic-release",
"to": " \"astx == ${nextRelease.version}\", # semantic-release",
"results": [
{
"file": "packages/aix/pyproject.toml",
"hasChanged": true,
"numMatches": 1,
"numReplacements": 1
}
],
"countMatches": true
},
{
"files": ["packages/aix/pyproject.toml"],
"from": " \"pyirx == .*\", # semantic-release",
"to": " \"pyirx == ${nextRelease.version}\", # semantic-release",
"results": [
{
"file": "packages/aix/pyproject.toml",
"hasChanged": true,
"numMatches": 1,
"numReplacements": 1
}
],
"countMatches": true
},
{
"files": ["packages/aix/src/aix/__init__.py"],
"from": "return \".*\" # semantic-release",
"to": "return \"${nextRelease.version}\" # semantic-release",
"results": [
{
"file": "packages/aix/src/aix/__init__.py",
"hasChanged": true,
"numMatches": 1,
"numReplacements": 1
}
],
"countMatches": true
}
]
}
Expand Down Expand Up @@ -174,7 +230,9 @@
"packages/irx/dist/*.whl",
"packages/irx/dist/*.tar.gz",
"packages/arx/dist/*.whl",
"packages/arx/dist/*.tar.gz"
"packages/arx/dist/*.tar.gz",
"packages/aix/dist/*.whl",
"packages/aix/dist/*.tar.gz"
]
}
],
Expand All @@ -190,7 +248,9 @@
"packages/irx/pyproject.toml",
"packages/irx/src/irx/__init__.py",
"packages/arx/pyproject.toml",
"packages/arx/src/arx/__init__.py"
"packages/arx/src/arx/__init__.py",
"packages/aix/pyproject.toml",
"packages/aix/src/aix/__init__.py"
],
"message": "chore(release): ${nextRelease.version}"
}
Expand Down
43 changes: 43 additions & 0 deletions packages/aix/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# AIX

AIX is an experimental AI-oriented symbolic programming language frontend in the
Arx ecosystem.

- PyPI distribution: `airx`
- Python import: `aix`
- CLI command: `aix`
- Source extension: `.aix`

## Usage

```bash
pip install airx
aix --help
aix --show-tokens examples/fib.aix
aix --show-ast examples/fib.aix
```

## MVP syntax

```aix
∴ fib ⟦ n:ℕ ⟧ → ℕ
⊢ n ≤ 1 ⇒ n
⊢ fib⟦n - 1⟧ + fib⟦n - 2⟧
```

Core forms:

- `∴` defines a function or constant.
- `⟦...⟧` is used for parameters and calls.
- `→` marks the return type.
- `⊢ expr` returns from a function.
- `⊢ cond ⇒ expr` emits an if-return branch.
- `⌁ name:T ≔ expr` creates a local binding.
- `⟣ expr` emits through the existing `print` builtin.
- `∎` ends a block; `{...}` and `;` support compact inline blocks.
- `κ⟦...⟧` metadata blocks are parsed and ignored in the MVP.
- Comments start with `⍝`.

Reserved APL-inspired operators such as `⍴`, `⍳`, `¨`, `↑`, `↓`, `⍋`, and `⍒`
are tokenized but intentionally rejected until backend support exists.
5 changes: 5 additions & 0 deletions packages/aix/docs/apl-inspired-operators.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Reserved APL-inspired operators

AIX reserves symbols such as `⍴`, `⍳`, `¨`, `∘`, `↑`, `↓`, `⍋`, `⍒`, `∊`, and
`∪`. The lexer recognizes these symbols, but the parser raises a clear
unsupported-feature error in the MVP.
4 changes: 4 additions & 0 deletions packages/aix/docs/examples.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# AIX examples

See `packages/aix/examples` for `hello.aix`, `fib.aix`, `compact_fib.aix`,
`bindings.aix`, and `metadata.aix`.
11 changes: 11 additions & 0 deletions packages/aix/docs/grammar.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# AIX MVP grammar

```ebnf
program ::= item* EOF ;
item ::= metadata_block? definition ;
definition ::= "∴" identifier "⟦" parameter_list? "⟧" "→" type block ;
block ::= statement* "∎" | "{" statement_list? "}" ;
statement ::= "⊢" expression | "⊢" expression "⇒" expression
| "⌁" identifier (":" type)? "≔" expression
| "⟣" expression | expression ;
```
11 changes: 11 additions & 0 deletions packages/aix/docs/syntax.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# AIX syntax

AIX uses symbolic, indentation-insensitive blocks. Pretty layout is encouraged,
but `∎` and `{...}` are the source of truth for block boundaries.

```aix
∴ main ⟦⟧ → ∅
⌁ x:ℕ ≔ 41
⟣ x + 1
```
4 changes: 4 additions & 0 deletions packages/aix/examples/bindings.aix
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
∴ main ⟦⟧ → ∅
⌁ answer:ℕ ≔ 42
⟣ answer
1 change: 1 addition & 0 deletions packages/aix/examples/compact_fib.aix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
∴fib⟦n:ℕ⟧→ℕ{⊢n≤1⇒n;⊢fib⟦n-1⟧+fib⟦n-2⟧}
15 changes: 15 additions & 0 deletions packages/aix/examples/fib.aix
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
κ⟦
ι: fib.recursive.v1,
π: true,
τ: ℕ → ℕ,
χ: recursion

∴ fib ⟦ n:ℕ ⟧ → ℕ
⊢ n ≤ 1 ⇒ n
⊢ fib⟦n - 1⟧ + fib⟦n - 2⟧

∴ main ⟦⟧ → ∅
⟣ fib⟦10⟧
3 changes: 3 additions & 0 deletions packages/aix/examples/hello.aix
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
∴ main ⟦⟧ → ∅
⟣ "hello from aix"
8 changes: 8 additions & 0 deletions packages/aix/examples/metadata.aix
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
κ⟦
ι: hello.v1,
χ: example

∴ main ⟦⟧ → ∅
⟣ "metadata parsed"
122 changes: 122 additions & 0 deletions packages/aix/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
[project]
name = "airx"
version = "0.1.0" # semantic-release
description = "AIX is an AI-oriented symbolic programming language frontend"
readme = "README.md"
authors = [
{name = "Ivan Ogasawara", email = "ivan.ogasawara@gmail.com"}
]
license = "Apache-2.0"
requires-python = ">=3.10,<4"
dependencies = [
"pyyaml >=4",
"astx == 1.23.1", # semantic-release
"pyirx == 1.23.1", # semantic-release
"jsonschema (>=4.0.0)",
"packaging >=23",
"types-pyyaml (>=6.0.12.20250516)",
"tomli >=2.0.0 ; python_version < \"3.11\""
]

[project.scripts]
"aix" = "aix.__main__:app"

[build-system]
requires = ["poetry-core>=2"]
build-backend = "poetry.core.masonry.api"

[tool.poetry]
include = [
"src/aix",
"src/aix/builtins/**/*.aix",
"src/aix/builtins/**/*.x",
"src/aix/schema/*.json",
"src/aix/py.typed",
"src/aix/lexer/syntax.json",
"src/aix/stdlib/**/*.aix",
"src/aix/stdlib/**/*.x",
]
exclude = [
".git/*",
".env*",
]
packages = [
{include = "aix", from="src"},
]

[tool.pytest.ini_options]
testpaths = [
"tests/python",
]
filterwarnings = [
"error::RuntimeWarning",
"error::typeguard.TypeHintWarning",
]

[tool.bandit]
exclude_dirs = ["tests"]
targets = "./"

[tool.vulture]
exclude = ["tests"]
ignore_decorators = ["abc.abstractmethod"]
ignore_names = []
make_whitelist = true
min_confidence = 80
paths = ["./"]
sort_by_size = true
verbose = false

[tool.ruff]
target-version = "py310"
line-length = 79
force-exclude = true
src = ["./"]
exclude = [
'docs',
]
fix = true

[tool.ruff.lint]
ignore = [
"F811",
"PLR0911",
"PLR0912",
"PLR0913",
"RUF012",
"PLR0915",
"PLR2004",
"PLC0415",
]
select = [
"E",
"F",
"YTT",
"PL",
"RUF",
"I001",
]
extend-select = [
"UP006",
"UP007",
]

[tool.ruff.lint.pydocstyle]
convention = "numpy"

[tool.ruff.lint.isort]
lines-between-types = 1

[tool.ruff.format]
quote-style = "double"

[tool.mypy]
python_version = "3.10"
check_untyped_defs = true
strict = true
ignore_missing_imports = true
warn_unused_ignores = true
warn_redundant_casts = true
warn_unused_configs = true
show_error_codes = true
exclude = ["scripts/"]
26 changes: 26 additions & 0 deletions packages/aix/src/aix/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
"""
title: AIX package metadata.
"""

from importlib import metadata as importlib_metadata

_DISTRIBUTION_NAME = "airx"


def get_version() -> str:
"""
title: Return the installed package version.
returns:
type: str
"""
try:
return importlib_metadata.version(_DISTRIBUTION_NAME)
except importlib_metadata.PackageNotFoundError: # pragma: no cover
return "0.1.0" # semantic-release


version: str = get_version()

__author__: str = "Ivan Ogasawara"
__email__: str = "ivan.ogasawara@gmail.com"
__version__: str = version
8 changes: 8 additions & 0 deletions packages/aix/src/aix/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
"""
title: Entrypoint module, in case you use `python -m aix`.
"""

from aix.cli import app

if __name__ == "__main__":
app()
Loading
Loading