Skip to content

CarlosDlw/Lucis

Repository files navigation

Lucis

A compiled systems programming language that targets LLVM IR. Lucis combines the performance and control of C with modern syntax, user-defined generics, a rich standard library, and native C interoperability — with zero runtime overhead.

namespace Main;

use std::log::println;

fn main() int32 {
    println("Hello, world!");
    ret 0;
}

Table of Contents


Features

  • Native compilation — LLVM backend targeting x86-64, ARM, and more; full optimization pipeline (-O1 through -O3, -Os, -Oz, -Ofast) and Link Time Optimization (--lto)
  • C FFI — call any C function with extern, include headers with #include, pass structs by value; zero glue code
  • User-defined generics — monomorphized struct Node<T>, extend Node<T> methods, generic functions fn max<T>(T a, T b) T, and type inference from call arguments when possible
  • Built-in collectionsvec<T>, map<K, V>, set<T> with full method suites
  • Concurrencyspawn/await, Task<T>, Mutex, lock statement
  • Error handlingtry/catch/finally, throw, built-in Error type
  • Type methods — dot notation on all primitive types, structs, and arrays
  • Rich enums — unit, tuple payload, named payload, and generic enums (e.g., Result<V, E>)
  • Enum variant checksis supports type checks, variant identity checks, and variant binding like value is Result<int32, string>::Err(msg)
  • Manual memory — explicit allocation, defer cleanup, auto-cleanup for collections
  • List comprehensions[x * 2 | for int32 x in items if x > 0]
  • LSP support — semantic highlighting, hover, completions, and signature help via the included language server

Building

Dependencies:

Dependency Version
CMake 3.20+
GCC or Clang C++17 (GCC 9+ / Clang 10+)
LLVM 15+ (18+ recommended)
ANTLR4 C++ Runtime 4.13+
libclang any
yaml-cpp 0.7+
zlib, pthreads any

Install dependencies

Install the equivalent development packages for your distro. Typical package names:

  • Ubuntu/Debian: cmake, build-essential, llvm-dev, libclang-dev, libantlr4-runtime-dev, libyaml-cpp-dev, zlib1g-dev, pkg-config
  • Arch Linux: cmake, base-devel, llvm, clang, antlr4-runtime, yaml-cpp, zlib, pkgconf
  • Fedora: cmake, gcc-c++, llvm-devel, clang-devel, antlr4-cpp-runtime-devel, yaml-cpp-devel, zlib-devel, pkgconf-pkg-config

Note for Static Linkage (--static): If you plan to use the --static flag, you must also install the static library versions for system dependencies:

  • Ubuntu/Debian: zlib1g-static
  • Arch Linux: zlib-static
  • Fedora: zlib-static
  • (Ensure glibc-static is also installed on Fedora/RedHat based systems)

Configure and build (recommended)

Use the project Makefile (portable wrappers around CMake):

git clone https://github.com/CarlosDlw/Lucis.git
cd Lucis
make configure BUILD_TYPE=Debug SANITIZERS=ON
make build

The compiled binary is at build/lucis.

Useful configure variants:

# Release build without sanitizers
make configure BUILD_TYPE=Release SANITIZERS=OFF

# Force Ninja generator
make configure GENERATOR="Ninja"

# Pass extra CMake hints (custom prefixes/toolchains)
make configure CMAKE_FLAGS="-DCMAKE_PREFIX_PATH=/opt/custom"

Alternative: pure CMake

cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug -DLUCIS_ENABLE_SANITIZERS=ON
cmake --build build --parallel

Regenerate grammar manually (optional)

Grammar generation is available as a manual Make target and is not part of the normal build pipeline.

# Uses the default jar in project root
make grammar

# Or pass an explicit jar path
make grammar ANTLR_JAR=/path/to/antlr-4.13.2-complete.jar

This regenerates C++ ANTLR sources into src/generated/ from:

  • grammar/LucisLexer.g4
  • grammar/LucisParser.g4

Use this only when grammar files change or when you intentionally want to refresh generated sources.

Dependency discovery notes

  • The build system prefers pkg-config when available.
  • It also searches common system locations for ANTLR4 runtime and libclang.
  • If LLVM is installed in a non-standard prefix, pass -DLLVM_DIR=... or include it in CMAKE_PREFIX_PATH.

Troubleshooting

If CMake reports missing ANTLR4 runtime or libclang:

  1. Ensure development packages are installed (headers + libraries, not only runtime binaries).
  2. Check pkg-config visibility:
    • pkg-config --cflags --libs antlr4-runtime
    • pkg-config --cflags --libs libclang
  3. If pkg-config cannot find them, export paths:
    • export PKG_CONFIG_PATH=/your/prefix/lib/pkgconfig:$PKG_CONFIG_PATH
  4. Provide explicit CMake hints:
    • -DCMAKE_PREFIX_PATH=/your/prefix
    • -DLLVM_DIR=/your/prefix/lib/cmake/llvm

Usage

lucis init  [path]                                               Create a new project
lucis build <file> [-o <out>] [-O <lvl>] [--lto] [--emit-...]  Compile to binary
lucis run   <file> [-O <lvl>] [--lto] [-- args...]             JIT execution
lucis check <file> [-I <dir>]                                   Type-check only
lucis test  [filter] [-q]                                       Run test suite
lucis help  [command]                                           Show help
lucis helpc <lib> [symbol]                                      C library reference
# Compile to binary with LTO and size optimization
lucis build main.lc -o ./main -Oz --lto

# Emit Assembly to file
lucis build main.lc --emit-asm -o main.s

# Show LLVM IR in terminal
lucis build main.lc --emit-llvm

# Run via JIT with O3 optimization
lucis run main.lc -O3

# Type-check only
lucis check main.lc

# Run the test suite
lucis test -q

Quick Example

namespace Main;

use std::log::println;

struct Node<T> {
    T value;
}

extend Node<T> {
    fn create(T val) Node<T> {
        ret Node<T> { value: val };
    }

    fn getValue(&self) T {
        ret self.value;
    }
}

fn max<T>(T a, T b) T {
    ret a > b ? a : b;
}

fn main() int32 {
    Node<int32> n = Node::create(42);
    int32 val = n.getValue();
    int32 m = max(3, 7);
    println(val);   // 42
    println(m);     // 7
    ret 0;
}

Documentation

Getting Started

Installation Build the compiler from source
Hello World Your first Lucis program, step by step
CLI Usage Compiler flags, options, and output modes
Editor Setup Syntax highlighting and LSP tooling

Language Guide

Basics

Overview Philosophy, goals, and design principles
Syntax Semicolons, blocks, comments, identifiers
Types Integers, floats, bool, char, string, void, pointers
Variables Declaration, initialization, scope
Operators Arithmetic, comparison, logical, bitwise, precedence

Control Flow

Control Flow if/else, switch, for, while, loop, break, continue
Ranges .., ..= in loops and comprehensions

Functions

Functions Declaration, parameters, return values, variadic, function pointers

Data Structures

Arrays Fixed-size [N]T, indexing, methods, list comprehensions
Structs Definition, instantiation, field access, extend methods
Enums Definition, variants, :: access
Unions Definition, shared memory layout, generic unions
Tuples Lightweight anonymous product types

Type System

Type Aliases The type keyword
Generics Built-in and user-defined generic types, monomorphization, constraints
Expressions Ternary, null coalescing, as, is, sizeof, typeof

Modules and Namespaces

Modules use / import system and module resolution
Namespaces Namespace declaration, resolution, multi-file projects

Error Handling

Error Handling try/catch/finally, throw, Error, panic, assert

Pointers and Memory

Pointers Pointer types, &, *, ->, null, pointer arithmetic
Memory Management defer, auto-cleanup, manual allocation via std::mem

Concurrency

Concurrency spawn, await, Task<T>, Mutex, lock

Standard Library

Module Description
std::log println, print, eprint, dbg, sprintf
std::io readLine, prompt, readChar, readInt, readFloat
std::fmt lpad, rpad, center, hex, bin, commas, humanBytes
std::str split, join, parseInt, parseFloat, trim, replace
std::conv itoa, atoi, ftoa, toHex, toBinary
Vec<T> Dynamic growable array
Map<K, V> Open-addressing hash map
Set<T> Open-addressing hash set
std::fs Read, write, mkdir, list, remove
std::path join, parent, extension, normalize
std::process exec, env, pid, platform
std::os getpid, hostname, errno, signals
std::math PI, E, sqrt, sin, cos, pow, clamp
std::random randInt, randFloat, randBool
std::bits popcount, rotl, rotr, setBit, testBit
std::time now, sleep, year, formatTime
std::net TCP/UDP: tcpConnect, tcpSend, udpBind
std::crypto md5, sha256, sha512, hmacSha256, randomBytes
std::encoding Base64, URL encoding
std::compress gzipCompress, deflate, inflate
std::regex match, find, regexReplace, regexSplit
std::ascii isAlpha, isDigit, toUpper, toLower
std::hash hashString, hashInt, hashCombine, crc32
std::mem alloc, free, copy, move, zero, compare
std::thread cpuCount, threadId, yield
std::test assertEqual, assertNear, fail, skip

Advanced Topics

Compiler Internals Parser, checker, IR builder, optimizer pipeline
Memory Model Stack layout, heap, pointer semantics
Optimization LLVM pass pipeline, -o1/-o2/-o3
Extending Lucis Adding builtins and extending the compiler

FFI

FFI Overview C interop architecture
Calling C extern, #include, calling conventions
Structs & ABI Passing structs by value, layout compatibility
C Strings c"..." literals, null termination, conversions
Linking Linking against static and shared libraries

Reference

Grammar Full ANTLR4 grammar reference
Keywords Reserved keywords
Operator Precedence Full precedence table
Builtins Built-in functions and operators
Type Methods Methods available on all types
Changelog Version history

Final Notes

Lucis is actively evolving. If a build fails on your platform, open an issue with:

  • distro and version
  • compiler and CMake versions
  • full CMake configure output

This helps keep cross-distro support reliable and fast to maintain.

License

This project is licensed under the MIT License. See LICENSE.