Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
df67140
A0 Add OS conditions to cmake presets
skaravos Nov 11, 2025
1ca7ae3
A0 Fix cmake 4.0+ warning in VersionInfoTarget
skaravos Nov 11, 2025
dfb61d4
A0 Remove hardcoded build jobs from presets
skaravos Nov 11, 2025
127d188
A0 Always store *.sh scripts with LF line endings
skaravos Feb 24, 2026
06aaa70
A0 Add CMake presets for 'clang-windows'
skaravos Feb 24, 2026
77e7bda
A0 Change windows 'msvc' presets to VS 2026
skaravos Feb 25, 2026
73f359f
A0 Add .claude/ to .gitignore
skaravos Mar 7, 2026
6ed513c
A0 Add 'CL=/MP' to CMake MSVC presets
skaravos Mar 7, 2026
659be6f
A0 Fix unubilium cmake target on windwows
skaravos Mar 7, 2026
66a3b6f
A0 Add basic CLAUDE.md
skaravos Mar 7, 2026
389989f
A0 Small cmake fixes
skaravos Mar 7, 2026
a516ace
A0 Fix libmd compilation on MSVC
skaravos Mar 7, 2026
081aa57
A0 Add include/keywords.h to .gitignore
skaravos Mar 7, 2026
512c490
A0 Tweak CMake style
skaravos Mar 7, 2026
67cdfbf
A0 Use `_tgt_` cmake vars for project-defined targets
skaravos Mar 7, 2026
40f09df
A0 Fix dockerfile for latest ubuntu
skaravos Mar 7, 2026
d52c4e4
A0 Update default docker cmake version => 4.2.3
skaravos Mar 7, 2026
87e9fb3
A0 Fix cmake error
skaravos Mar 7, 2026
51e4838
A0 Use unbuffered sed in build-in-docker.sh
skaravos Mar 7, 2026
aeafc0a
A0 Allow colored cmake output from build-in-docker
skaravos Mar 7, 2026
5b426e7
A0 Remove build-in-ubuntu16 and ubuntu18
skaravos Mar 7, 2026
a4b6fba
A0 Use ninja in build-in-docker.sh
skaravos Mar 7, 2026
88b38f6
A0 Set explicit --parallel in build-in-docker
skaravos Mar 7, 2026
0a3ad32
A0 Fix install rule for '*.ty' files
skaravos Mar 7, 2026
c7a065c
A0 Bump cmake minimum to 3.25
skaravos Mar 7, 2026
f7015c4
A0 Use LINK_LIBRARY:WHOLE_ARCHIVE genex
skaravos Mar 7, 2026
084cac1
A0 Use 'ENABLE_EXPORTS' instead of '-rdynamic'
skaravos Mar 7, 2026
66224c6
A0 Move vendored libs into 'vendored' subdir
skaravos Mar 7, 2026
1cc71ab
A0 Fix Makefile w/ 'vendored' subdir
skaravos Mar 7, 2026
7c1cdae
A0 Add LuaJIT to .gitignore
skaravos Mar 7, 2026
455cd92
A0 Add note about 'LuaJIT' to CLAUDE.md
skaravos Mar 7, 2026
eec3153
A0 Add 'luajit' to vcpkg.json
skaravos Mar 8, 2026
46d182c
A0 Add 'nsync' to vcpkg.json, remove vendored copy
skaravos Mar 8, 2026
833c56a
This and that
marchelzo Mar 8, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
*.ty linguist-language=TypeScript
*.sh text eol=LF
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,18 @@ _*
# ides
.vscode
.vs
# Makefile garbage
obj/
# vcpkg
vcpkg_installed
vcpkg
*.log
*.err
# autogenerated file(s)
include/keywords.h
src/jit_x64.h
LuaJIT
# agents
.agents/*
.claude/*
*.local.json
153 changes: 153 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

**Ty** is an interpreted, gradually-typed programming language implementation written in C (C23
standard). It includes a bytecode VM, optional JIT compiler (DynASM-based), garbage collector,
coroutine system, and a standard library written in Ty itself.

## Build System

### CMake (primary, cross-platform)

Dependencies are managed via vcpkg.
The vcpkg root is expected at `_vcpkg/` (set in `CMakePresets.json`).
The `./scripts/setup-vcpkg.sh` script can be used to perform the initial clone of vcpkg into
the `_vcpkg` subdirectory.

```bash
# Configure
cmake --preset <preset-name>

# Build
cmake --build --preset <preset-name>
```

**Available presets** (see [CMakePresets.json](CMakePresets.json)):
- `msvc2026` / `msvc2026-debug` — Windows, Visual Studio 2026, MSVC
- `clang-windows` / `clang-windows-debug` — Windows, Clang (Ninja)
- `gcc` / `gcc-ninja` — Linux, GCC
- `clang-osx` — macOS, Apple Clang

Build output goes to `_build/<preset-name>/`. Binaries produced: `ty`, `tyls`, optionally `typrof`.

### Makefile (Unix/Linux/macOS only)

```bash
make # Default optimized build (-Og -g3)
make RELEASE=1 # Optimized release (-O3, LTO, mcpu=native)
make DEBUG=1 # Debug with ASAN
make TDEBUG=1 # Debug with ThreadSanitizer
make tyls # Build language server
make install # Install to /usr/local/bin and ~/.ty/
make clean
```

**Makefile flags** (combine freely):
- `NO_JIT=1` — Disable JIT
- `LOG=1` — Enable internal logging (disabled by default via `-DTY_NO_LOG`)
- `NO_NSYNC=1` — Disable nsync synchronization
- `JEMALLOC=1` — Use jemalloc instead of mimalloc
- `LTO=1` — Force link-time optimization
- `PROFILE_TYPES=1` — Enable type system profiling

**Pre-build requirements** (Makefile):
- `gperf` generates [include/keywords.h](include/keywords.h) from
[src/keywords.gperf](src/keywords.gperf)
- `luajit` + DynASM generates `src/jit_x64.h` or `src/jit_arm64.h` from `.dasc` files
- `LuaJIT` project must be cloned from `https://github.com/LuaJIT/LuaJIT` into the
project root at `<project-root>/LuaJIT` so that the build can find `LuaJIT/dynasm/dynasm.lua`

## Running Tests

Tests require a built `ty` executable in the project root (or on PATH):

```bash
make test # Runs: ./ty test.ty

# Run a single test file
./ty --test tests/array.ty

# Run the full test suite
./ty test.ty
```

The test runner [test.ty](test.ty) is written in Ty itself. It runs test files from
[tests/](tests/) in parallel with a 5-second timeout per test.

## Architecture

### Compilation Pipeline

```
Source text
→ Lexer (lex.c / token.c)
→ Parser + AST (parse.c)
→ Type checker + Compiler → Bytecode (compiler.c, types.c)
→ VM execution (vm.c)
└─ optional JIT (jit.c + jit_x64.dasc / jit_arm64.dasc)
```

### Core Source Modules ([src/](src/))

| File | Role |
|----------------|---------------------------------------------------------------|
| `compiler.c` | Type checking, compilation, constant folding (~20K LOC) |
| `types.c` | Type system: inference, constraints, refinements (~12K LOC) |
| `vm.c` | Bytecode interpreter, exception handling, coroutines (~11K LOC) |
| `functions.c` | Built-in functions and standard library glue (~10K LOC) |
| `parse.c` | Parser, AST construction (~7.5K LOC) |
| `jit.c` | JIT driver; arch-specific code in `.dasc` files (~7K LOC) |
| `gc.c` | Naive STW mark-and-sweep GC |
| `ffi.c` | Foreign function interface via libffi |
| `value.c` | Core `Value` type (32-byte tagged union) |
| `scope.c` | Symbol tables and lexical scoping |
| `class.c` | Class system with inheritance and traits |

### Value Representation

All Ty values are a packed 32-byte `Value` struct (defined in [include/ty.h](include/ty.h)) with a
type byte, 16-bit tag, and a union payload. This is the central type used throughout the VM and
compiler.

### Executables

- **ty** — main interpreter (entry: `ty.c`)
- **tyls** — language server (entry: `tyls.c`; compiled with `-DTY_LS`)
- **typrof** — profiler (entry: `ty.c`; compiled with `-DTY_PROFILER`)

### Vendored Libraries

| Library | Location | Purpose |
|------------|---------------------------------------|------------------------------------------|
| libco | [libco/](libco/) | Coroutines (lightweight cooperative threads) |
| dtoa | [dtoa/](dtoa/) | Swift's double-to-string conversion |
| libmd | [libmd/](libmd/) | MD5/SHA hash functions |
| unibilium | [unibilium/](unibilium/) | Terminal capability database |
| nsync | [nsync/](nsync/) | Google synchronization primitives (optional) |
| DynASM | [LuaJIT/dynasm/](LuaJIT/dynasm/) | JIT code generation DSL |

### vcpkg Dependencies

`libffi`, `sqlite3`, `utf8proc`, `pcre2`, `xxHash`, `mimalloc`

### Standard Library

~50 Ty modules in [lib/](lib/) — installed to `~/.ty/`.
Key modules: `prelude.ty`, `io.ty`, `os.ty`, `ffi.ty`, `http.ty`, `json.ty`, `regex.ty`,
`sqlite.ty`, `term.ty`.

## Platform Notes

- **Linux/macOS**
- Primary development platforms
- CMake and Makefile are both supported

- **Windows**
- Currently unsupported, does not compile on Windows.
- Uses CMake + MSVC or clang-cl
- Required file: `ioctl_constants.h` is created as an empty stub
- Required file: `keywords.h` must be created on a platform with access to gperf and copied
into `include/keywords.h`
126 changes: 51 additions & 75 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.21)
cmake_minimum_required(VERSION 3.25)

project(ty VERSION 0.1 LANGUAGES C)

Expand Down Expand Up @@ -28,6 +28,9 @@ find_package(utf8proc CONFIG REQUIRED)
find_package(pcre2 CONFIG REQUIRED)
# defines target(s): xxHash::xxhash
find_package(xxHash CONFIG REQUIRED)
# defines target(s): nsync
find_package(nsync CONFIG REQUIRED)
add_library(nsync::nsync ALIAS nsync) #<-- sadly nsync doesnt do this for us
# defines target(s): mimalloc, mimalloc-static
find_package(mimalloc CONFIG REQUIRED)
set_target_properties(mimalloc-static PROPERTIES
Expand All @@ -49,6 +52,12 @@ if(_lto_supported)
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
endif()

# ---
# vendored libraries
# ---

add_subdirectory(vendored)

# ---
# generate ioctl constants
# ---
Expand Down Expand Up @@ -148,13 +157,20 @@ endif()
set(_file_keywords_h ${_autogen_dir}/include/keywords.h)
set(_gperf_input ${PROJECT_SOURCE_DIR}/src/keywords.gperf)

find_program(GPERF gperf)
if(GPERF)
set(_tgt_keywords_h "KeywordsH")
add_library(${_tgt_keywords_h} INTERFACE)
target_include_directories(${_tgt_keywords_h}
INTERFACE
${_autogen_dir}/include
)

find_program(GPERF_EXECUTABLE gperf)
if(GPERF_EXECUTABLE)
add_custom_command(
OUTPUT
${_file_keywords_h}
COMMAND
${GPERF} "${_gperf_input}" > "${_file_keywords_h}.tmp"
${GPERF_EXECUTABLE} "${_gperf_input}" > "${_file_keywords_h}.tmp"
COMMAND
${CMAKE_COMMAND} -E copy_if_different
"${_file_keywords_h}.tmp"
Expand All @@ -173,65 +189,31 @@ else()
message(WARNING "gperf not found -- keywords.h will not be regenerated")
endif()

# ---
# target: libco
# ---
add_library(libco)
target_sources(libco PRIVATE libco/libco.c)
target_compile_definitions(libco PRIVATE LIBCO_MP)

# ---
# target: dtoa
# ---
add_library(dtoa)
target_sources(dtoa PRIVATE dtoa/SwiftDtoa.c)

# ---
# target: libmd::md
# ---
add_subdirectory(libmd)
# libmd uses C_VISIBILITY_PRESET hidden which breaks ThinLTO symbol resolution
set_target_properties(md PROPERTIES INTERPROCEDURAL_OPTIMIZATION FALSE)

# ---
# target: nsync
# ---
if (USE_NSYNC)
add_subdirectory(nsync)
endif()

# ---
# target: unibilium
# ---
add_subdirectory(unibilium)

# ---
# DynASM JIT code generation
# ---

find_program(LUAJIT luajit)
if(LUAJIT)
if(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64|arm64|ARM64")
set(_jit_dasc ${PROJECT_SOURCE_DIR}/src/jit_arm64.dasc)
set(_jit_hdr ${PROJECT_SOURCE_DIR}/src/jit_arm64.h)
else()
set(_jit_dasc ${PROJECT_SOURCE_DIR}/src/jit_x64.dasc)
set(_jit_hdr ${PROJECT_SOURCE_DIR}/src/jit_x64.h)
endif()

add_custom_command(
OUTPUT ${_jit_hdr}
COMMAND ${LUAJIT} ${PROJECT_SOURCE_DIR}/LuaJIT/dynasm/dynasm.lua
-o ${_jit_hdr} ${_jit_dasc}
DEPENDS ${_jit_dasc}
COMMENT "Generating JIT emission header from ${_jit_dasc}"
VERBATIM
)
add_custom_target(GenerateJitHeader DEPENDS ${_jit_hdr})
find_program(LUAJIT_EXECUTABLE luajit REQUIRED
HINTS "${VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/tools/luajit"
)
if(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64|arm64|ARM64")
set(_jit_dasc ${PROJECT_SOURCE_DIR}/src/jit_arm64.dasc)
set(_jit_hdr ${PROJECT_SOURCE_DIR}/src/jit_arm64.h)
else()
message(WARNING "luajit not found -- JIT headers will not be regenerated")
set(_jit_dasc ${PROJECT_SOURCE_DIR}/src/jit_x64.dasc)
set(_jit_hdr ${PROJECT_SOURCE_DIR}/src/jit_x64.h)
endif()

add_custom_command(
OUTPUT ${_jit_hdr}
COMMAND ${LUAJIT_EXECUTABLE} ${PROJECT_SOURCE_DIR}/LuaJIT/dynasm/dynasm.lua
-o ${_jit_hdr} ${_jit_dasc}
DEPENDS ${_jit_dasc}
COMMENT "Generating JIT emission header from ${_jit_dasc}"
VERBATIM
)
add_custom_target(GenerateJitHeader DEPENDS ${_jit_hdr})

# ---
# target: ty
# ---
Expand Down Expand Up @@ -340,15 +322,18 @@ target_link_libraries(${_tgt_ty_interface}
${_tgt_ty_interface_version_info}
${_tgt_ioctlconstants_h}
${_tgt_errnoconstants_h}
${_tgt_keywords_h}
vendored::libco
vendored::libmd
vendored::dtoa
$<LINK_LIBRARY:WHOLE_ARCHIVE,vendored::unibilium>
$<$<BOOL:${USE_NSYNC}>:nsync::nsync>
Threads::Threads # pthreads (or equivalent)
libco
mimalloc-static
xxHash::xxhash
utf8proc::utf8proc
PCRE2::8BIT
unofficial::libffi::libffi
libmd::md
dtoa
unofficial::sqlite3::sqlite3
$<$<NOT:$<BOOL:${WIN32}>>:${CMAKE_DL_LIBS}>
$<$<NOT:$<BOOL:${WIN32}>>:m>
Expand All @@ -360,23 +345,9 @@ if(USE_NSYNC)
target_link_libraries(${_tgt_ty_interface} INTERFACE nsync)
endif()

if(APPLE)
target_link_libraries(${_tgt_ty_interface} INTERFACE
-Wl,-force_load,$<TARGET_FILE:unibilium>
)
target_link_options(${_tgt_ty_interface} INTERFACE -Wl,-export_dynamic)
else()
target_link_libraries(${_tgt_ty_interface} INTERFACE
-Wl,--whole-archive unibilium -Wl,--no-whole-archive
)
target_link_options(${_tgt_ty_interface} INTERFACE -rdynamic)
endif()

target_include_directories(${_tgt_ty_interface}
INTERFACE
${PROJECT_SOURCE_DIR}/include
${PROJECT_SOURCE_DIR}/libco
${PROJECT_SOURCE_DIR}/dtoa
${PROJECT_SOURCE_DIR}/src
)

Expand Down Expand Up @@ -420,6 +391,11 @@ if(BUILD_PROFILER)
target_link_libraries(${_tgt_ty_profiler} PRIVATE ${_tgt_ty_interface})
endif()

# export symbols (essentially adds the equivalent of: -rdynamic during link)
foreach(_tgt IN ITEMS ${_tgt_ty} ${_tgt_tyls} ${_tgt_typrof})
set_target_properties(${_tgt} PROPERTIES ENABLE_EXPORTS ON)
endforeach()

# ---
# install rules
# ---
Expand All @@ -430,10 +406,10 @@ install(TARGETS ${_tgt_ty} ${_tgt_ty_profiler} ${_tgt_ty_ls}
DESTINATION ${CMAKE_INSTALL_BINDIR}
)

file(GLOB _ty_modules LIST_DIRECTORIES false ${PROJECT_SOURCE_DIR}/lib/*.ty)
install(FILES ${_ty_modules}
install(DIRECTORY ${PROJECT_SOURCE_DIR}/lib/
COMPONENT InstallComponentTy
DESTINATION ${CMAKE_INSTALL_LIBDIR}/ty
FILES_MATCHING PATTERN "*.ty"
PERMISSIONS
OWNER_READ OWNER_WRITE # 6
GROUP_READ # 4
Expand Down
Loading