Fast, modern object-oriented programming language that compiles to C
Perfect for cross-platform development, embedded systems, and high-performance applications
AmLang is designed for developers who want the productivity of modern languages with the performance and portability of C. Whether you're building embedded applications, cross-platform tools, or high-performance systems, AmLang gives you:
- Object-oriented programming with classes, inheritance, and interfaces
- Built-in unit testing with comprehensive mocking framework
- Memory management with automatic reference counting
- Concurrency support with built-in threading
- Clean syntax inspired by Kotlin and C#
- Compile anywhere, run everywhere - generates portable C code
- Cross-platform builds for Linux, macOS, AmigaOS, Morphos and more.
- Native C interop for seamless library integration
# Auto-detects best version for your system
curl -fsSL https://raw.githubusercontent.com/anderskjeldsen/am-lang-compiler/master/scripts/install-amlc.sh | bashDownload the latest native binary from GitHub Releases:
- Linux x64:
amlc-linux-[version].tar.gz - macOS x64:
amlc-mac-[version].tar.gz - macOS ARM64:
amlc-mac-arm64-[version].tar.gz - Universal JAR:
amlc-[version].jar(requires Java 21+)
# Download and extract (example for Linux, current release is v0.12.0)
wget https://github.com/anderskjeldsen/am-lang-compiler/releases/latest/download/amlc-linux-0.12.0.tar.gz
tar -xzf amlc-linux-0.12.0.tar.gz
chmod +x amlc-linux
# Verify installation
./amlc-linux --versionCreate hello.aml:
namespace HelloWorld {
class Program {
fun main() {
"Hello, AmLang!".println()
}
}
}
mkdir my-project && cd my-project
amlc new # Creates package.yml and src/ directoryamlc build # Compiles to C and builds executable
amlc run # Builds and runs your programnamespace Graphics {
#require opengl
class Renderer {
fun initialize() {
OpenGL.initContext()
}
}
#require directx
class Renderer {
fun initialize() {
DirectX.createDevice()
}
}
}
// package.yml configures which features to use
// dependencies:
// - id: graphics-lib
// features: [opengl] # Use OpenGL on Linux/macOS
namespace Physics {
class Particle {
private var mass: Double = 9.109e-31 // Electron mass (kg)
private var charge: Double = -1.602e-19 // Elementary charge (C)
fun kineticEnergy(velocity: Float): Double {
var v = velocity.toDouble()
return 0.5 * mass * v * v
}
}
}
namespace MyApp {
import Am.Lang
import Am.Ui
class MainWindow {
static fun main() {
var w = Window.openWindow(20S, 20S, 300US, 200US, null, null)
var panel = new Panel()
panel.setDefaultBorder()
panel.setDefaultPadding()
panel.setMargin(w.getScaledX(2S), w.getScaledY(2S))
var vStack = new VStack(w.getScaledY(2S))
panel.setChild(vStack)
var button = new Button("Click Me!", (v) => {
"Button clicked!".println()
return true
})
button.setup((v) => {
v.setDefaultPadding()
v.growX = 255UB
v.growY = 0UB
})
vStack.addChild(button)
w.setRootView(panel)
w.layout()
w.requestRepaint()
while(w.isOpen()) {
w.handleInput()
}
}
}
}
// tests/DatabaseTest.aml
namespace MyApp.Tests {
class DatabaseTest {
test testUserRepository() {
mock Database {
fun findUser(id: Int): User {
return new User(id, "Test User")
}
}
var repo = new UserRepository()
var user = repo.getUser(123)
if (user.name != "Test User") {
throw new Exception("Mock failed!")
}
}
}
}
id: my-awesome-app
version: 1.0
type: application
dependencies:
- id: am-lang-core
realm: github
type: git-repo
tag: latest
url: https://github.com/anderskjeldsen/am-lang-core.git
- id: am-ui
realm: github
type: git-repo
tag: latest
url: https://github.com/anderskjeldsen/am-ui.git
platforms:
- id: libc
abstract: true
- id: linux-x64
extends: libc
gccCommand: gcc
- id: amigaos
extends: libc
gccCommand: m68k-amigaos-gcc
buildTargets:
- id: linux-x64
platform: linux-x64
- id: amigaos
platform: amigaosamlc new # Initialize new project
amlc build # Build project
amlc run # Build and run
amlc test # Run unit tests
amlc lint # Check code style (v0.7.0)
amlc docs # Generate API documentation (v0.7.0)
amlc clean # Clean build artifacts
amlc # Show help when no valid command givenAmLang includes a comprehensive built-in testing framework:
class CalculatorTest {
test testAddition() {
var calc = new Calculator()
var result = calc.add(5, 3)
if (result != 8) {
throw new Exception("Addition failed!")
}
}
}
class ServiceTest {
test testWithComplexMock() {
mock Database {
fun query(sql: String): ResultSet {
// Mock implementation
return mockResultSet()
}
}
scope {
mock Logger {
fun log(message: String) {
// Override logging in this scope
}
}
// Test code with both mocks active
}
// Logger mock automatically restored here
}
}
Performance benchmark summary from examples/performance_test/ReadMe.md.
Workload used for all runs:
- Iterations:
1000 - Count per iteration:
100000 - Modulo:
7(skip wheni % modulo == 0) - Total points processed:
100000000
| Language | handlePoints (array) |
handlePoints2 (no array) |
|---|---|---|
Rust (rustc -C opt-level=2) |
476 ms | 331 ms |
| C (pure, gcc -O3) | 986 ms | 846 ms |
| AmLang | 986 ms | 819 ms |
| Go | 1290 ms | 867 ms |
| Java | 1632 ms | 881 ms |
C# (struct Point) |
1946 ms | 826 ms |
| Python | 52151 ms | 34041 ms |
These results were measured on a Dell XPS 15 (2018) with an Intel Core i7 CPU, NVIDIA GTX 1050-class GPU, 32 GB RAM, and 1 TB SSD.
Notes:
handlePoints: create all points in an array, then sum in a second pass.handlePoints2: create point and sum immediately (no array).- The modulo branch (
i % modulo == 0) is intentional: it helps prevent trivial constant-folding/dead-code style optimizations so compilers still emit realistic machine code for the loop workload. - Full benchmark details:
examples/performance_test/ReadMe.md.
- โ Linux (x64, ARM64, PowerPC) - Full support with native binaries
- โ macOS (x64, ARM64) - Intel and Apple Silicon support
- โ AmigaOS 3.x - Classic Amiga cross-compilation
- โ MorphOS - Modern Amiga-compatible systems
- Native binaries: No runtime dependencies
- JAR version: Java 21+ required
For AmigaOS development, AmLang provides a complete Docker-based cross-compilation environment with the Amiga GCC toolchain and AmiSSL support:
# From the project root
cd docker/amiga-gcc
./build.sh
# Or alternatively, build directly from project root:
docker build -f docker/amiga-gcc/Dockerfile -t amiga-gcc .# Interactive development environment
docker run -it amiga-gcc
# Mount your project for cross-compilation
docker run -it -v $(pwd):/workspace amiga-gcc
# Compile AmLang project for AmigaOS
amlc build . -bt amigaos_docker- Object parameters and
thisare no longer retained by the callee โ the call site already owns its arguments for longer than the call lasts. Saves two refcount ops per object argument per call, and two global-lock round trips under thread-safe ARC for a cross-thread receiver. - AmLang source needs no changes. Native C that stashes an object pointer beyond the call must now retain it explicitly.
- Mark a function deprecated and warn at every call site:
#obsolete 'use readAll() instead'. Covers instance, static and extension functions; warnings are de-duplicated.
- The compiler synthesises a class mapping
id -> versionfor every non-test package in the binary, backingAm.Lang.Runtime.getPackages().
- A hex literal with the top bit set now folds to a negative number of the target type:
0x80000000is-2147483648as anInt. Width- and suffix-aware.
42.toString()compiles. Identity conversions are deliberately excluded โas Intstays the preferred, faster spelling.
- Reassigning a parameter leaked its value and over-released the caller's reference (a latent use-after-free); now bracketed correctly.
- Loop-head temporaries leaked one wrapper per iteration (the chunk-streaming leak).
inline funreturning from inside a loop leaked every temp in the enclosing blocks; exceptions from an inlined body now unwind through the caller's block cascade.
Full release notes: release-notes/RELEASE_NOTES_v0.12.0.md.
- Bare
Tis now nullable by default;T!marks a type non-null. Assigning a nullable to a non-null slot inserts an implicit!!conversion. - Opt back into pre-0.11.0 behavior with
legacyObjectNullabilityinpackage.yml'scompilerFlags, or per class with#legacyObjectNullabilityfor a gradual file-by-file migration.
- Cross-thread reference counting via wrapper aobjects. Reads and writes from a foreign thread transparently redirect via
__unwrap()โ near-free when no wrappers exist (~one load + branch total). - AmLang source needs no changes. Hand-written native C in your package must
__unwrap()before any data deref of an aobject that might have crossed threads.
newcodegen null-checks the allocation and throws a preallocatedAm.Lang.OutOfMemoryExceptionsingleton on failure โ catchable with an ordinarytry/catch. Before v0.11.0, allocation failure meant a SIGSEGV in the constructor call.
- Implicit
SubIface โ SuperIfaceconversion when the source transitively extends the target โ drops the "declare every super-iface on the class" workaround. - Multi-parent interfaces (
interface X : A, B) resolve inherited functions at call sites. is SomeInterfacefinally works (previously always returnedfalse).
- Cross-compile in one container, run tests in another. Landed with the workspace's
amlang-amiberry:latestimage so AmigaOS m68k tests run end-to-end under Amiberry โ no local AmigaOS install needed.
- SSH + rsync analog of
dockerBuild. Cross-compile on a remote host, pull the binaries back.
#runOnExit,#runOnStartup, and the new#onNativeTearDownaccept an optional integer priority:#runOnExit(1000). Hooks fire in ascending order across all classes.
Full release notes: release-notes/RELEASE_NOTES_v0.11.0.md.
- New
inlinemodifier expands calls at the C call site โ zero-overhead abstractions and full GCC constant-folding through the expansion.
- Declares which platforms need their own per-platform native stub. Lets one native class share a
libcimplementation across linux/macos while providing custom AmigaOS/MorphOS implementations side-by-side.
- Runs as part of object construction, after the primary constructor and body-declared property defaults. Composes through inheritance โ each class's
initruns in base-to-derived order.
==/!=on struct values compare fields recursively instead of pointer identity. Nested structs handled correctly.
Full release notes: release-notes/RELEASE_NOTES_v0.10.0.md.
- Struct declarations and initialization have been improved.
- Nested structs are better supported in everyday usage.
- Better struct-related diagnostics and validation behavior.
- Struct variables are handled as struct pointers at runtime.
- Passing a struct to a function passes the pointer (shared struct data).
- Returning a struct creates a copy.
- Storing a struct in an array creates a copy of the struct value.
- Reading a struct from an array currently depends on usage.
- Direct element member access (for example
arr[i].x) works on the array element reference. - Assigning an element to a struct variable (for example
var p = arr[i]) creates a copy. - Planned for v0.10.0: make copy-vs-reference explicit for struct reads (for example use
*arr[i]for copy), and report compile errors when a reference/value mismatch is ambiguous.
- Struct initializer named fields are more reliable and better validated.
- Type-handling improvements reduce edge-case compile failures.
- Better behavior in complex call/type scenarios.
- Fixes across overload resolution, expression ordering, static call correctness, and array modification behavior.
- Improved primitive-vs-
nullhandling andreturn-statement edge cases. - Improved release workflow and publish diagnostics.
- Added intuitive iteration syntax:
each(item in collection) { ... }. - Existing syntax
each(collection, item)remains fully supported.
- Improved direct invocation of function-pointer properties (for example:
this.callback()). - Improved C code generation reliability and diagnostics for function-pointer usage.
- Improved memory handling and type validation for callback-style patterns.
- Improved direct invocation of anonymous functions stored in properties.
- Improved behavior for patterns like
this.operation(a, b).
- Feature toggles with
#requiredirectives for cross-platform development. - Float/Double support with scientific notation (for example
1.23e-4F). - Built-in linting via
amlc lint. - API documentation generation via
amlc docs.
mockkeyword for overriding class behavior in testsscopemanagement for nested mocks with automatic cleanup- Full integration with existing unit testing framework
Explore real-world projects in the examples/ directory:
- Hello World - Basic program structure
- File Browser - GUI application with native file access
- Image Browser - Graphics and image processing
- Unit Testing - Comprehensive testing examples with mocks