Arx is a new programming language that uses the power of LLVM to provide multi-architecture machine target code generation. Arx aims to provide native list and tensor abstractions with a builtin runtime backed internally by IRx.
Arx is a prototype that should replace the current Arx compiler in c++.
- Free software: Apache Software License 2.0
- Documentation: https://arxlang.org
If you want more information about ArxLang, please check the original project in c++: https://github.com/arxlang/arx
This repository contains the lockstep-released Arx ecosystem packages:
packages/astx:astxpackages/irx:pyirxpackages/arx:arxlang
Root Poetry installs these packages as editable path dependencies for local
development. The package sources should still depend on released package names
and versions; local checkout wiring belongs in the root pyproject.toml.
Releases are intentionally lockstep. A semantic-release run updates one shared
version across the root project and all three package projects, then
scripts/build.sh and scripts/publish.sh build and publish astx, pyirx,
and arxlang together.
Common local tasks:
makim arx.unittests
makim astx.unittests
makim irx.unittests
makim all.lint
makim docs.buildArx supports explicit executable link modes:
arx program.x --link-mode auto # default, use toolchain default
arx program.x --link-mode pie # force PIE executable
arx program.x --link-mode no-pie # force non-PIE executableIf you hit an error like:
relocation R_X86_64_32 ... can not be used when making a PIE object
use:
arx program.x --link-mode no-pieThis typically happens on environments where the linker defaults to PIE while objects were not compiled in a PIE-compatible mode.
Arx now exposes list[...] and tensor[...] as distinct public collection
forms:
fn pick(grid: tensor[i32, 2, 2]) -> i32:
return grid[1, 0]
fn main() -> i32:
var grid: tensor[i32, 2, 2] = [[1, 2], [3, 4]]
var ids: list[i32] = [5, 6, 7, 8]
return pick(grid) + ids[2]
Use:
list[T]for generic collection valuestensor[T, N]andtensor[T, D0, D1, ..., DN]for fixed-shape tensorstensor[T, ...]for runtime-shaped tensor parameters
Tensor details stay user-facing in terms of element types, shape, dimensions,
and indexing. In this phase, variable, field, and return tensor annotations must
declare at least one static shape dimension. IRx owns the Arrow C++ backed
Tensor runtime and lowering. Current tensor element types are fixed-width
numeric types: i8, i16, i32, i64, f32, and f64. For fixed-shape
tensors, ... in documentation is descriptive notation for additional integer
dimensions, not the runtime-shaped marker. Runtime-shaped tensor parameters can
be passed through function boundaries, but indexed access requires static-shape
annotations until dynamic tensor indexing lands in IRx.
Arx uses Tensor for homogeneous N-dimensional data and reserves Array for
one-dimensional Arrow-style data where the language exposes it. Future
heterogeneous dataframe or table support will use a separate surface type.
Arx also ships a bundled pure-Arx standard library under the reserved stdlib
namespace:
import math from stdlib
fn main() -> i32:
return math.square(4)
Compiler-provided builtins stay separate from stdlib. Builtin sources live in
packages/arx/src/arx/builtins/*.x, are bundled inside the installed arx
package, and are resolved by dedicated compiler logic instead of user-project
module lookup. Those bundled builtin modules are internal compiler assets, not a
public stdlib-style import namespace. User code does not import builtins;
builtin functions such as range(...) are available automatically.
fn main() -> none:
print(range(0, 4)[2])
The first builtin module is generators. Its current MVP exposes
range(start, stop[, step]) -> list[i32], while future overloads and
yield-backed generator semantics will grow in the same area. Positive steps
count up, negative steps count down, and step == 0 raises an assertion
failure. For-in loops can iterate over list-valued expressions such as
range(...), list literals, and list variables. Ambient builtin names such as
range are injected only when not shadowed, so a local function or import with
the same name overrides the builtin in that module.
Arx now supports fatal assertion statements in the language surface:
fn test_add() -> none:
assert 1 + 1 == 2
assert 2 + 2 == 4, "2 + 2 should be 4"
You can run compiled tests with the new arx test subcommand:
arx test
arx test packages/arx/tests/arx/test_math.x --list
arx test -k square
arx test -x
arx test --keep-artifacts
arx test --exclude "packages/arx/tests/arx/slow_*.x"By default the runner searches tests/ for files matching test_*.x, discovers
zero-argument test_* functions that return none, and executes each test in
its own compiled subprocess. Test identifiers use the cwd-relative path of the
source file (without the .x suffix) joined to the function name via ::, for
example packages/arx/tests/arx/test_math::test_square, so same-named files in
parallel directories stay distinct.
You can override discovery from .arxproject.toml:
[tests]
paths = ["tests", "integration"]
exclude = ["tests/experimental_*.x"]
file_pattern = "test_*.x"
function_pattern = "test_*"CLI flags always win over [tests] settings. In v1, shared top-level support is
intentionally narrow: imports, extern declarations, class declarations, and
helper functions are preserved, while module-scope variable declarations and
other top-level executable code are not supported yet.