Map

Tig (tc-lang) — Simplest possible, usable systems language

Sources alonsovm44 (Alonso VM) ↳ show in map Markdown
title
Tig (tc-lang) — Simplest possible, usable systems language
author
alonsovm44 (Alonso VM)
fetched
2026-07-21

Tig 🦁

Simplest possible, usable systems language

Repo: https://github.com/alonsovm44/tc-lang Stars: ~40 · License: MIT (per README badge; GitHub API reports no detected SPDX license file) · Primary language: C · Version: 1.3.1s/1.3.2 · Platforms: Windows, Linux, macOS · Description: "A minimalistic ergonomic layer over C"

Disclaimer: For stage0, Tig can use AI codegen for the C core (but test thoroughly); for stage1 of self-hosting, handwritten code only. See CONTRIBUTING.md. Note: see dogfood/ folder for non-trivial examples of Tig being used.

Tig is a minimalistic systems programming language.

What's New in v1.3.2

  1. Added C compiler flags using --
  2. Added freestanding parameter to enable freestanding mode
  3. Multiple file linking support
  4. Added binary and hex literals
  5. Removed old function syntax
  6. Removed extern blocks

Project Goals

  1. Make the first usable systems language from Mexico.
  2. Explore the bare minimum of what a systems language must have to be usable, modern and ergonomic.
  3. Learn C and master Tig.
  4. Make an OS with it.
  5. Get a job.

Philosophy

Why it was made: the author wanted an ergonomic systems language capable of anything, with fewer keywords than Go, no GC and without heavy runtime overhead; as safe as possible (but not necessarily proving safety). A language one could master and teach to others, and put Mexico on the map.

Backend: for now, C11. Core Ideal: Tig has to fit in a single person's head. Small but powerful.

Core principle:

Everything that can be built with libraries has to be built with libraries. The core language is small. Explicit is better than implicit, but exceptions can be made. Flexibility over rigidity. Anything C can run, we run: anywhere C has run, we will run.

Features

  • 18 keywordsif, loop, break, defer, ret, strun, fn, use, pin, match, else, enum, async, select, throw, try, catch, raw.
  • Zero-boilerplate async — automatic runtime initialization, no manual setup.
  • Async functionsasync fn.
  • Concurrent data structures — built-in queue<T> and stack<T>.
  • Ownership transfer@ operator for safe resource transfer.
  • Select statements — multi-operation waiting with select.
  • Pin — keep variables alive across async boundaries.
  • No hidden magic — no GC, no type inference, no shadowing, no aliasing.
  • Raw pointers (->) and fat pointers (=>) with built-in slicing.
  • Manual memoryalloc() / free() with defer for cleanup.
  • Packed structs — no padding, predictable layout.
  • Rust-style errors — colored diagnostics with source lines and carets.
  • One-step compiletightc source.tc -c app transpiles and compiles in one command.
  • Inline imports@use "lib.tc" inlines another .tc file at compile time.
  • CLI argsi32 fn main: =>->i8 args { ... }.

Quick Start

make                                # build the compiler (requires make)
./tigc stdlib/io.tc -o stdlib/io.h  # compile stdlib headers (once)
./tigc samples/fizzbuzz.tc -c fizzbuzz
./fizzbuzz

How It Works

Tig is a source-to-source compiler (transpiler) written in ~4,600 lines of C. It reads .tc files and outputs portable C11.

source.tc -> [Lexer] -> [Parser] -> [AST] -> [Checker] -> [Emitter] -> output.c -> gcc/clang -> binary

Pipeline

  1. Lexer (lexer.c) — tokenizes source; tracks line/col for error reporting.
  2. Parser (parser.c) — builds AST; handles operator precedence, scope-level pin enforcement, and @use file inlining (recursive parse + splice).
  3. Emitter (emitter.c) — walks the AST and outputs C11. Most constructs are 1:1:
Tig Emitted C
=>i32 s tc_fat_i32 s (struct with .ptr + .len)
defer { free(p) } scope-exit statements emitted in reverse order before }/return
pin x nothing emitted — enforced at parse time (compile error on reassignment)
@use "lib.tc" declarations inlined directly into AST (no #include)
alloc(T, n) TC_ALLOC(T, n)calloc(n, sizeof(T))
=>->i8 args in main main(int argc, char **argv) + local fat pointer wrapping them

What it is not

Not memory safe — no borrow checker, no GC; memory management is manual (arenas planned). Provides safety features: fat pointers, self-cleaning error functions, defer statements. No optimizer, no IR, no type-inference pass, no code generation beyond string concatenation of C. Can leverage LLVM flags from gcc/clang. Output is always readable, debuggable C (inspect with tightc source.tc -o source.c).

What Tig is good for

CLI tools (tiny/no runtime overhead); embedded / bare-metal (no runtime, no allocator required, packed structs); game engine internals (manual memory, no GC pauses); learning compilers (small enough to read in an afternoon); C codebases that want better ergonomics.

Syntax highlights

Hello world:

use "stdlib/io.tc"
fn i32 main: {
    print("hello, world")
    ret 0
}
  • Variables are C-like, mutable by default; uninitialized default to 0. pin PI makes a variable immutable in the current scope.
  • Functions: fn i32 add: i32 a, i32 b { ret a + b }. Varargs via ....
  • Strunions (strun) — a struct/union spectrum ([struct]—[strun]—[union]). & marks a union element sharing memory; anonymous types provide padding/grouping. Struns can contain methods (fn void printPoint: self { ... }).
  • Pointers: -> raw (address-of/deref), => fat pointer (.ptr/.len), slicing arr[1:3], pointer combos (->->, =>->, ->=>, =>=>). Struct pointer access via .> (same as C's ->).
  • Control flow: if/else if/else, loop { ... break } (infinite), loop if (cond) { ... } (while alias), match statements with _ wildcard and = arms.
  • Memory: alloc(i32, 100) + defer free(arr) (braceless defer allowed).
  • Imports: use "lib.tc" (link precompiled .h) vs @use "lib.tc" (inline .tc at compile time).
  • C FFI: "C"{ #include "clib.h" }.
  • Error reporting: Rust-style error[E000] diagnostics with source lines/carets; tightc --error E000 prints help.

Error system

Errors are a built-in type via error. An error is a function-like unit carrying context params plus handling/cleanup code, invoked with throw. try/catch blocks handle them; catch blocks work like match (specify which errors to catch and what return value to use, _ wildcard).

error zero_division: i32 a, i32 b { printf("Error, division by zero: %d / %d", a, b) }
fn f64 divide: i32 a, i32 b {
    if(b == (f32)0){ throw zero_division(a, b) }
    return (f64)a / (f64)b
}

Note: error system may be tricky in freestanding environments (no longjmp, no debug printf).

Async

Zero-boilerplate: async fn void worker: i32 x { ... }; calling it auto-initializes the runtime (no async_init()/async_shutdown()). Async functions are always void; communicate via channels (queue<T>/stack<T> from stdlib/snq.tc). select { task1() => {...} task2() => {...} } waits on multiple operations.

Types

i8char, i16int16_t, i32int32_t, i64int64_t, u8uint8_t, u16uint16_t, u32uint32_t, u64uint64_t, f32float, f64double, voidvoid.

Compiler usage

tigc <input.tc> [-o output.c] [-c binary] [-t] -- [clang/gcc flags]
  • -o file.c — emit transpiled C (.h gets #pragma once)
  • -c binary — transpile + compile to binary (auto-detects gcc/clang)
  • -t — keep temporary files
  • (none) — print transpiled C to stdout

Hot reloading

Global hot reloading of functions, strun definitions, and enums — recompile shared libraries on-the-fly without restarting the running app. Host/DLL splitting architecture: main compiled into the host executable; all other functions/structs/enums compiled into a versioned shared library (hotlib_N.dll / hotlib_N.so), exported automatically. Host stubs check the current library version, reload on change, and execute via function pointers; old version files auto-cleaned. Flags: -H <libname> (enable hot reload), --hot (rebuild only the hot library), -t/--temp. See dogfood/HOTSWAPPING/.

Project structure

tc-lang/
  compiler/
    include/   # headers
    src/       # compiler source (C)
  stdlib/      # standard library (.tc)
  samples/     # example programs
  docs/        # language specification
  Makefile

Stdlib

Simple stdlib wrapping libc plus runtimes for stacks/queues and async functions.

Built by @alonsovm44.