Skip to content

the-pro-coder/trace-lang

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Trace

The programming language that knows why.

Trace is a compiled, statically-typed, general-purpose programming language built around one radical idea: tracelity is a first-class feature.

Every value in Trace can carry a lightweight chain that records where it came from, why it has its current value, and what computations produced it. When something goes wrong, you don't add print statements and re-run — you ask the value itself.

let tracel result: tracel int = compute(user_input, db_record);
why result;
// → let result at line 3
//   ← compute() returned 0
//     ← db_record was null
//       ← query timed out at 14:03:22

Table of Contents

  1. Why Trace?
  2. Quick Start
  3. Building the Compiler
  4. Language Reference
  5. Compiler Flags
  6. Architecture
  7. Roadmap

Why Trace?

Modern software has a tracelity problem. Every major pain point in production systems reduces to the same question: why is this value wrong?

Problem Traditional answer Trace answer
Debugging Add print statements, re-run why x; — the value explains itself
Distributed failures Grep through logs Tracel chain spans service boundaries
AI/ML wrong outputs More logging frameworks Chain propagates through every transformation
Data pipeline errors Stack traces show where, not why why result == null;
Security auditing External audit trails Chain is structural, in the binary

Trace makes tracelity structural — part of the type system and the compiled binary — not bolted on through logging libraries or tracing tools.


Quick Start

// hello.tr
fn main() -> void {
    println("Hello from Trace!");

    let tracel x: tracel int = 42;
    let tracel y: tracel int = x + 10;

    why y;   // prints: why y was 52, tracing back to x = 42
}

Compile and run:

./tracec hello.tr -o hello.ll
clang hello.ll runtime/trace_rt.c -o hello
./hello

Building the Compiler

Requirements

  • GCC or Clang (C11) — to build the trc compiler itself
  • Clang — to link generated .ll output into a native binary
  • Make (Linux/macOS) or PowerShell (Windows)

Platform note: trc generates LLVM IR targeting x86_64 Linux. The generated .ll files can be compiled and run on Linux and Windows (via MinGW/Clang). Native Windows target support is planned for v0.2.

Build

Linux / macOS:

cd trace
make

Windows (PowerShell):

.\build.ps1

Both produce the trc (or trc.exe) compiler binary.

Run the examples

Linux / macOS:

make run-hello   # compiles and runs examples/hello.tr
make run-demo    # compiles and runs examples/causal_demo.tr
make run-calc    # compiles and runs examples/calculator.tr

Windows:

.\build.ps1 run-hello
.\build.ps1 run-demo
.\build.ps1 run-calc

Debug passes

./trc --lex       examples/hello.tr   # print all tokens
./trc --parse     examples/hello.tr   # parse and print AST
./trc --typecheck examples/hello.tr   # type-check only

Language Reference

Types

Type Description LLVM IR type
int 64-bit signed integer i64
float 64-bit IEEE 754 double double
bool Boolean (true / false) i1
string UTF-8 string i8*
void No value (functions only) void
tracel T Tracel wrapper around any type T { T, %TracelNode* }
StructName User-defined struct %StructName

Variables

let name: type = expression;

Variables are block-scoped and must be explicitly typed. Initialization is optional.

let x: int = 10;
let y: float = 3.14;
let flag: bool = true;
let greeting: string = "hello";
let unset: int;          // declared but uninitialized

Reassignment uses =:

x = x + 1;
x += 5;      // shorthand
x -= 2;

Tracel Values

The core feature of Trace. A tracel value tracks its entire history — every assignment, function call, and computation that produced it.

Declaring a tracel variable

let tracel score: tracel int = 0;
// equivalent:
let score: tracel int = 0;

The tracel keyword can appear before let or as part of the type. Both are equivalent.

Propagation

Tracelity propagates automatically through tracel functions and operations:

fn compute(x: tracel int) -> tracel int {
    return x * 2;
}

let tracel a: tracel int = 10;
let tracel b: tracel int = compute(a);
// b's chain: compute() called with a=10, a was 10 at line N

Querying tracelity: why

why x;              // print the full tracel chain of x
why x == 0;         // explain why x equals 0
why result != null; // explain why result is not null

why is a statement that prints the tracel chain to stdout at runtime. It is a no-op in release builds (when the chain is stripped).

Annotating tracelity: cause

Mark a block of code with a semantic label that gets embedded in the chain:

cause "reading from database" {
    let tracel record: tracel int = db_fetch(user_id);
    // record's chain includes: "reading from database"
}

Tracing: trace

Print a value and its current chain label inline, without interrupting execution:

let tracel result: tracel int = heavy_compute(input);
trace result;
// prints: [trace] 42  ← heavy_compute() at line 12

Functions

fn function_name(param: type, param2: type) -> return_type {
    // body
    return value;
}

Tracel functions

A function marked (or whose signature uses) tracel types automatically participates in chain propagation:

tracel fn fetch_user(id: tracel int) -> tracel int {
    // all values computed here are tagged with this function's identity
    return id + 1;
}

Non-tracel functions can still receive and return tracel values — the chain is preserved.

Examples

fn add(a: int, b: int) -> int {
    return a + b;
}

fn greet(name: string) -> void {
    print("Hello, ");
    println(name);
}

fn factorial(n: int) -> int {
    if n <= 1 {
        return 1;
    }
    return n * factorial(n - 1);
}

Control Flow

If / else

if condition {
    // then
} else if other_condition {
    // else-if
} else {
    // else
}

Condition must be bool. Parentheses are not required.

While loop

while condition {
    // body
}

For-in loop

for item in collection {
    // item is an int index for now; full iterator protocol coming in v0.2
}

Blocks

Any { ... } creates a new scope:

{
    let x: int = 5;
    // x is only visible here
}

Structs

struct Point {
    x: int,
    y: int,
}

struct User {
    id: tracel int,    // tracel field — tracks where this ID came from
    name: string,
    score: tracel float,
}

Access fields with .:

let p: Point = Point { x: 10, y: 20 };
println(p.x);

Note: Struct literal initialization syntax is coming in v0.2. Currently structs are declared and their fields accessed via the . operator.


Built-in Functions

Function Signature Description
print (any) -> void Print a value without newline
println (any) -> void Print a value with newline
read () -> string Read a line from stdin
len (string) -> int Length of a string
str (int) -> string Convert int to string
int (string) -> int Parse string to int
float (string) -> float Parse string to float

Tracel Keywords — Full Reference

Keyword Form Description
tracel type modifier Marks a type or variable as tracelity-tracked
why statement Print tracel chain: why x; or why x == val;
cause block annotation Label a scope: cause "label" { ... }
trace expression Print value + chain inline: trace expr

Compiler Flags

tracec [options] <source.tr>

Options:
  -o <file>      Output LLVM IR file (default: out.ll)
  --lex          Print all tokens and exit
  --parse        Parse only — report success or errors
  --typecheck    Type-check only — report errors
  --help         Show this help message

Compilation pipeline

source.tr
    │
    ▼ Lexer         (lexer.c)      → Token stream
    │
    ▼ Parser        (parser.c)     → AST
    │
    ▼ Type Checker  (typechecker.c)→ Annotated AST
    │
    ▼ Code Gen      (codegen.c)    → LLVM IR (.ll)
    │
    ▼ clang/llc                    → Native binary

Architecture

trace/
├── src/
│   ├── main.c          — CLI driver: argument parsing, pipeline orchestration
│   ├── lexer.h/.c      — Tokenizer: source text → token stream
│   ├── ast.h/.c        — AST node definitions and memory management
│   ├── parser.h/.c     — Recursive descent parser: tokens → AST
│   ├── typechecker.h/.c— Static type checker with scope chain
│   ├── codegen.h/.c    — LLVM IR text emitter
│   └── tracel.h        — TracelNode struct shared by compiler and runtime
├── runtime/
│   └── trace_rt.c      — Runtime: tracel chains, why-printing, I/O built-ins
├── examples/
│   ├── hello.tr        — Hello World
│   └── tracel_demo.tr  — Full tracel features demo
├── Makefile
└── README.md

Tracel values in LLVM IR

A tracel int compiles to:

%TracelNode = type { i8*, i32, i32, %TracelNode* }
%tracel_int  = type { i64, %TracelNode* }

Every assignment to a tracel variable:

  1. Stores the new value in field 0
  2. Calls @trace_rt_node_new to allocate a new chain node with the source location
  3. Stores the node pointer in field 1

why x; compiles to:

  1. GEP to extract the chain pointer from the tracel struct
  2. Call @trace_rt_why(chain) which walks and prints the linked list

In release builds, tracel structs are replaced with their plain inner types and all chain operations are elided — zero overhead.


Roadmap

v0.1 (current)

  • Lexer with full token set
  • Recursive descent parser
  • Static type checker with scope chain
  • LLVM IR code generator
  • Tracel type (tracel T) with chain tracking
  • why, cause, trace keywords
  • Runtime: TracelNode, why-printing, I/O built-ins
  • if/else, while, for-in, let, return
  • Built-in functions: print, println, read, len

v0.2

  • Struct literal initialization syntax
  • Arrays and slices
  • String interpolation: "hello {name}"
  • tracel chains spanning function call boundaries with full stack capture
  • why x == val deep equality explanation
  • Release mode: strip all tracel chains (zero overhead)
  • Error type with automatic tracel tagging

v0.3

  • Imports and multi-file compilation
  • Generics / parametric types
  • Pattern matching (match expression)
  • Distributed tracel chains (chain serialization for RPC/message passing)
  • REPL

v1.0

  • Self-hosted compiler (Trace written in Trace)
  • Standard library
  • Package manager

License

MIT — build freely, ship tracelly.


Trace — because "it crashed" is never the full story.

About

A compiled language where every value knows why

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors