Imported from AserJoker/cubec (
.codebuddy/skills/cubec-project/SKILL.md). Install upstream withnpx skills add AserJoker/cubec --skill cubec-project. Copyright stays with the author.
Cubec Compiler Project
Overview
Cubec is a C-like programming language compiler frontend, written in C11. It implements a complete lexer (tokenizer), parser, semantic analysis engine, and comptime compile-time evaluator. The project uses a hand-crafted "C-style OOP" pattern with virtual tables (type_t) for lifecycle management, unified memory management via allocator_t, and built-in memory leak detection.
Directory Structure
cubec/
├── CMakeLists.txt # Build configuration
├── include/ # Public headers
│ ├── core/ # Core data structure library
│ │ ├── allocator.h # Memory allocator
│ │ ├── error.h # Error handling (TRY/THROW/CATCH macros)
│ │ ├── icu_data.h # ICU common data initialization
│ │ ├── list.h # Doubly-linked list
│ │ ├── location.h # Source location info
│ │ ├── map.h # Hash map (switches to red-black tree at >=16 entries)
│ │ ├── node.h # AST node base class
│ │ ├── position.h # Line/column position
│ │ ├── rbtree.h # Red-black tree (uint64_t keys)
│ │ ├── string.h # Dynamic string
│ │ ├── token.h # Token base class
│ │ ├── type.h # Type system (virtual table)
│ │ └── vec.h # Dynamic array (vector)
│ └── cubec/ # Language frontend module
│ ├── declaration.h # Declaration base class (abstract)
│ ├── declaration_array.h # Array declaration ([ <expr> ] <type>)
│ ├── declaration_pointer.h # Pointer declaration (* [const] [volatile] <type>)
│ ├── declaration_slice.h # Slice declaration ([] [const] [volatile] <type>)
│ ├── declaration_variable.h # Variable declarator (<identifier> [: <type>] = <expression>)
│ ├── function_argument.h # Function parameter node (<identifier> [: <type>])
│ ├── function_capture.h # Function capture node (<identifier>)
│ ├── statement.h # Statement dispatcher (read_statement)
│ ├── statement_block.h # Block statement node ({ <statements> })
│ ├── statement_declaration.h # Declaration statement ([export|extern|builtin|comptime] var <declarator> ;)
│ ├── statement_empty.h # Empty statement node
│ ├── statement_expression.h # Expression statement node (<expression>;)
│ ├── statement_function.h # Function declaration ([export] [inline] func | [extern] func | [builtin] func | [comptime] func name[params](args) [: type] { body } | ;)
│ ├── statement_import.h # Import statement node (import <name> [as <alias>] from "<path>";)
│ ├── statement_return.h # Return statement (return [expr];)
│ ├── expression.h # Expression AST node
│ ├── expression_assignment.h # Assignment expression (a = b, a += b, etc.)
│ ├── expression_binary.h # Binary/prefix-unary expression (left/right/opt)
│ ├── expression_call.h # Function-call expression callee(args)
│ ├── expression_comma.h # Comma expression (a, b, c) — right-associative
│ ├── expression_function.h # Function expression (func [name] |captures| [generic](params): type { body } | ;)
│ ├── expression_generic_instantiation.h # Generic instantiation expr[a,b]
│ ├── expression_group.h # Grouped expression ( expr )
│ ├── expression_initialize_field.h # Initialize field (.field = value)
│ ├── expression_initialize_list.h # Initialize list (.<type>{items} or .{items})
│ ├── expression_member.h # Member-access expression (host.field, instance member only)
│ ├── expression_namespace_access.h # Namespace access (host::field)
│ ├── expression_postfix_unary.h # Postfix unary: value.* (deref), value.& (addr)
│ ├── expression_typeof.h # Typeof expression (typeof(<expression>), compile-time type computation)
│ ├── expression_sizeof.h # Sizeof expression (sizeof(<expression>), compile-time size computation)
│ ├── expression_alignof.h # Alignof expression (alignof(<expression>), compile-time alignment computation)
│ ├── expression_slice.h # Slice expression (host[start:length])
│ ├── expression_spread.h # Spread expression (...expr)
│ ├── expression_ternary.h # Ternary/conditional expression (cond ? consequent : alternate)
│ ├── expression_type_qualifier.h # Type qualifier expression (const/volatile <type>, has is_const + is_volatile flags)
│ ├── expression_type_function.h # Function type expression (func(params) -> type)
│ ├── literal.h # Literal AST node (abstract)
│ ├── literal_char.h # Character literal
│ ├── literal_identifier.h# Identifier literal
│ ├── literal_numeric.h # Numeric literal (with type suffixes)
│ ├── literal_string.h # String literal
│ ├── literal_undefined.h # Undefined literal (TDZ initializer)
│ ├── node.h # AST node kind enum (71 node kinds)
│ ├── program.h # Top-level program node
│ ├── generic_param.h # Generic parameter node (T, T extends U, N: u64, ...T)
│ ├── statement_block.h # Block statement node ({ <statements> })
│ ├── statement_declaration_type.h # Type alias declaration node
│ ├── statement_empty.h # Empty statement node
│ ├── statement_expression.h # Expression statement node (<expression>;)
│ ├── statement_function.h # Function declaration ([export] [inline] func | [extern] func | [builtin] func | [comptime] func name[params](args) [: type] { body } | ;)
│ ├── statement_import.h # Import statement node (import <name> [as <alias>] from "<path>";)
│ ├── statement.h # Statement dispatcher (read_statement)
│ └── token.h # Token kind enum + lexer interface
├── src/ # Source files (mirrors include/ structure)
│ ├── main.c # Entry point (stub - initializes ICU + allocator)
│ ├── icu_data.c # ICU common data (generated at build time)
│ ├── core/ # Core data structure implementations
│ ├── cubec/ # Lexer + parser implementations
│ └── engine/ # Semantic analysis + comptime evaluator
│ ├── checker.c # Checker lifecycle (create/dispose + pass orchestration)
│ ├── checker_collect.c # Pass 1: symbol collection
│ ├── checker_check_stmt.c # Pass 2+3: type checking + queue-driven body check
│ ├── checker_evaluate.c # Pass 2: comptime evaluation + type resolution
│ ├── comptime_eval.c # Evaluator lifecycle (create/dispose)
│ ├── comptime_eval_expr.c # Expression evaluation
│ ├── comptime_eval_stmt.c # Statement execution
│ └── comptime_alloc.c # Virtual memory (comptime allocator)
├── test/ # Tests (1611 test cases, Google Test + C++20)
│ ├── main.cpp # Test entry point
│ ├── common/test_common.h # RAII test allocator helper
│ ├── core/ # Tests for core data structures
│ ├── cubec/ # Tests for lexer/parser
│ └── engine/ # Tests for semantic analysis + comptime evaluator
└── demo/
└── index.cubec # Sample source file
Core Architecture: type_t + allocator_t
type_t (Virtual Table)
Every "class" has a global type_t instance describing its lifecycle:
struct _type_t {
const size_t size; // Object size
const char *name; // Type name (for debug/leak reporting)
type_init_fn_t init; // Constructor
type_dispose_fn_t dispose; // Destructor
type_clone_fn_t clone; // Deep copy
type_move_fn_t move; // Move (transfer ownership)
};
Each module exports extern type_t g_xxx_type with registered lifecycle functions.
allocator_t
- Wraps
malloc/free(injectable custom allocator) - All allocated memory tracked in a doubly-linked list of
alloc_chunk_t(records size, type pointer, unique ID) - OOM policy: Allocation failure →
abort()crash. Callers who need graceful degradation should implement it in their customalloc_fn, not by checking return values. - Key APIs:
allocator_alloc(allocator, size)— Allocate raw zero-initialized memory; never returns NULL (aborts on OOM). Returns NULL only whensize == 0.allocator_create(allocator, type, arg)— Create typed object (callstype->init); never returns NULL (aborts on OOM)allocator_free(allocator, &data)— Free memory (auto-callstype->dispose) and set pointer to NULL. NULL-safe on both the wrapper and the pointed-to pointer. Implemented as a macro wrapping_allocator_free_impl(self, (void **)(ptr))to handle C'sT**→void**type incompatibility. Pass the address of your pointer:allocator_free(a, &ptr).value_get_type(data)/value_get_id(data)— Introspectionvalue_clone(allocator, data)/value_move(allocator, data)— Clone/move
create_allocatoraborts on OOM;delete_allocatoris NULL-safe — reports all unfreed memory
Inheritance Pattern
C struct nesting simulates single inheritance:
node_t (core/node.h)
└── cubec_expression_t (cubec/expression.h)
├── cubec_declaration_t (cubec/declaration.h) # Declaration base class (abstract)
│ ├── cubec_declaration_array_t (cubec/declaration_array.h) # [ <expr> ] <type>
│ ├── cubec_declaration_pointer_t (cubec/declaration_pointer.h) # * [const] [volatile] <type>
│ └── cubec_declaration_slice_t (cubec/declaration_slice.h) # [] [const] [volatile] <type>
├── cubec_expression_binary_t (cubec/expression_binary.h)
├── cubec_expression_call_t (cubec/expression_call.h)
├── cubec_expression_function_t (cubec/expression_function.h) # func [name] |captures| [generic](params): type { body } | ;
├── cubec_expression_generic_instantiation_t (cubec/expression_generic_instantiation.h)
├── cubec_expression_group_t (cubec/expression_group.h)
├── cubec_initialize_field_t (cubec/initialize_field.h) # .field = value
├── cubec_expression_initialize_list_t (cubec/expression_initialize_list.h) # .<type>{items}
├── cubec_expression_member_t (cubec/expression_member.h)
├── cubec_expression_namespace_access_t (cubec/expression_namespace_access.h) # host::field
├── cubec_expression_postfix_unary_t (cubec/expression_postfix_unary.h) # value.*, value.&
├── cubec_expression_slice_t (cubec/expression_slice.h)
├── cubec_expression_spread_t (cubec/expression_spread.h)
├── cubec_expression_type_qualifier_t (cubec/expression_type_qualifier.h) # const/volatile <type> (is_const + is_volatile flags)
├── cubec_expression_type_function_t (cubec/expression_type_function.h) # func(params) -> type
├── cubec_expression_typeof_t (cubec/expression_typeof.h) # typeof(<expression>)
├── cubec_expression_sizeof_t (cubec/expression_sizeof.h) # sizeof(<expression>)
├── cubec_expression_alignof_t (cubec/expression_alignof.h) # alignof(<expression>)
└── cubec_literal_t (cubec/literal.h)
├── cubec_literal_char_t
├── cubec_literal_identifier_t
├── cubec_literal_numeric_t
├── cubec_literal_string_t
└── cubec_literal_undefined_t
└── cubec_function_argument_t (cubec/function_argument.h) # func param (identifier [: type])
└── cubec_function_capture_t (cubec/function_capture.h) # func capture (identifier)
└── cubec_statement_block_t
└── cubec_statement_empty_t
└── cubec_statement_expression_t
└── cubec_statement_function_t (cubec/statement_function.h) # func declaration
└── cubec_statement_return_t (cubec/statement_return.h) # return [expr];
└── cubec_program_node_t
Subclasses embed the parent via a super field and call parent's type->init during initialization.
node_t (core/node.h)
└── cubec_expression_t (cubec/expression.h)
├── cubec_declaration_t (cubec/declaration.h) # Declaration base class (abstract)
│ ├── cubec_declaration_array_t (cubec/declaration_array.h) # [ <expr> ] <type>
│ ├── cubec_declaration_pointer_t (cubec/declaration_pointer.h) # * [const] [volatile] <type>
│ └── cubec_declaration_slice_t (cubec/declaration_slice.h) # [] [const] [volatile] <type>
├── cubec_expression_assignment_t (cubec/expression_assignment.h)
├── cubec_expression_binary_t (cubec/expression_binary.h)
├── cubec_expression_call_t (cubec/expression_call.h)
├── cubec_expression_comma_t (cubec/expression_comma.h)
├── cubec_expression_function_t (cubec/expression_function.h) # func [name] |captures| [generic](params): type { body } | ;
├── cubec_expression_generic_instantiation_t (cubec/expression_generic_instantiation.h)
├── cubec_expression_group_t (cubec/expression_group.h)
├── cubec_initialize_field_t (cubec/initialize_field.h) # .field = value
├── cubec_expression_initialize_list_t (cubec/expression_initialize_list.h) # .<type>{items}
├── cubec_expression_member_t (cubec/expression_member.h)
├── cubec_expression_namespace_access_t (cubec/expression_namespace_access.h) # host::field
├── cubec_expression_postfix_unary_t (cubec/expression_postfix_unary.h) # value.*, value.&
├── cubec_expression_slice_t (cubec/expression_slice.h)
├── cubec_expression_spread_t (cubec/expression_spread.h)
├── cubec_expression_ternary_t (cubec/expression_ternary.h)
├── cubec_expression_type_qualifier_t (cubec/expression_type_qualifier.h) # const/volatile <type> (is_const + is_volatile flags)
├── cubec_expression_type_function_t (cubec/expression_type_function.h) # func(params) -> type
├── cubec_expression_typeof_t (cubec/expression_typeof.h) # typeof(<expression>)
├── cubec_expression_sizeof_t (cubec/expression_sizeof.h) # sizeof(<expression>)
├── cubec_expression_alignof_t (cubec/expression_alignof.h) # alignof(<expression>)
└── cubec_literal_t (cubec/literal.h)
├── cubec_literal_char_t
├── cubec_literal_identifier_t
├── cubec_literal_numeric_t
├── cubec_literal_string_t
└── cubec_literal_undefined_t
└── cubec_function_argument_t (cubec/function_argument.h) # func param (identifier [: type])
└── cubec_function_capture_t (cubec/function_capture.h) # func capture (identifier)
└── cubec_statement_empty_t
└── cubec_statement_expression_t
└── cubec_statement_function_t (cubec/statement_function.h) # func declaration
└── cubec_statement_return_t (cubec/statement_return.h) # return [expr];
└── cubec_program_node_t
Core Data Structures
vec_t — Dynamic Array
- Like
std::vector<void*> - Operations: push, pop, get, set, insert, remove, resize
- Capacity starts at 0, first resize → 8, then doubles
- Iterator operations:
vec_iter_get(O(1)),vec_iter_set(O(1)),vec_iter_remove(O(n) — shifts left, iterator stays at same index),vec_iter_next(O(1)) - Iterator model:
vec_iter_firstpositions at index 0;vec_iter_nextreturns current data then advances;vec_iter_getreads without advancing;vec_iter_removeremoves current element, subsequent elements shift left, iterator stays at same index (now pointing to the next element) - Supports
auto_disposemode
list_t — Doubly-Linked List
- Standard doubly-linked list with head/tail pointers
- Indexed insert:
list_insert(idx, data)— O(n), traverse from nearer end - Iterator operations (O(1)):
list_iter_get/list_iter_set/list_iter_remove— direct node access, preferred API pattern - Queue/stack:
push/pop(tail),unshift/shift(head) - Iterator model:
list_iter_firstpositions at the first element;list_iter_nextreturns current data then advances;list_iter_getreads without advancing;list_iter_removedeletes current and advances to next - No indexed read/write/delete — all random access must go through the iterator
- Supports clone/move,
auto_disposemode
rbtree_t — Red-Black Tree
- Complete RB-tree with uint64_t keys
- Standard operations: insert, find, remove, clear
- Left/right rotation, insert fixup, delete fixup all implemented
- In-order traversal iterator
rbtree_iter_t - Supports auto_dispose
map_t — Dictionary/Map
- Implemented as two
vec_t(keys + values) + index - Small scale (< 16 entries): hash table (16 buckets, chaining)
- Large scale (>= 16 entries): auto-converts to red-black tree index (irreversible)
map_remove: uses iterator-basedremove_entry_from_bucket(single-pass O(n) traversal) for hash bucket removal- Uses
value_get_id(key)as hash key - Provides
map_iter_t
string_t — Dynamic String
- Like
std::string, null-terminated - Initial capacity 1, doubles on expansion
- Operations: get, set, concat, nconcat (fixed-length)
- Clone allocates new buffer matching source capacity
- Move transfers data pointer, leaves source with empty 8-byte buffer
position_t / location_t
position_t: line, column, offset pointerlocation_t: filename + begin/end positionslocation_get: extracts source text between begin/end offsets viamemcpy+ explicit\0terminationlocation_is: compares location text to a string viastrncmpplus length check (str[length] == '\0') to prevent prefix-only matches (e.g., single-charbwould previously match keywordbreak)
token_t / node_t (Base Classes)
token_t: allocator, kind (uint32_t), locationnode_t: allocator, kind (uint32_t), location, parent pointer
Error Handling System
- Uses
thread_localglobal error pointerg_error err_tcontains: error message (1024 bytes, formatted viavsnprintf) + call stack (up to 64 frames)error_push(): NULL-safe — no-op ifg_error == NULL(safeguard against calling before any error thrown)error_to_string(): formats error to readable string with call stack, usesPRIuPTRfor portability- Rust-style macros:
THROW(ret, fmt, ...)— Throw error and returnTRY(ret, expr)— Execute expr, propagate error on failureCATCH_ERROR(expr, onerror)— Execute expr, run onerror on failureTRY_LOCAL/TRY_VOID_LOCAL/THROW_LOCAL— Jump toonerror:label
- Uses GCC extensions:
__auto_type, statement expressions({...})— requires-std=gnu11(CMake default)
Lexer (src/cubec/token.c, ~675 lines)
Token Types (9 kinds)
WHITESPACE,EOF,COMMENT(//),MULTILINE_COMMENT(/* */)IDENTIFIER,NUMERIC,SYMBOL,KEYWORD,STRING,CHAR
Note: ... (ellipsis/spread) is tokenized as a SYMBOL with text "...", relying on the symbols table's longest-match ordering (placed among the 3-character symbols: &&=, ||=, ...).
Key Functions
read_unicode— UTF-8 decoder (1-4 bytes), returns Unicode codepointread_symbol_token— Longest match ordered by token length: 3-char (&&=,||=,...), 2-char (==,!=,>>,<<,+=, ...), 1-char (=,!,+, ...). The...operator is placed among the 3-character symbols so it matches before the single..read_whitespace_token— Uses ICUu_isWhitespaceread_comment_token— Single-line//read_multiline_comment_token— Multi-line/* */(no nesting)read_numeric_token— Decimal, hex0x, octal0o, binary0b, float, scientific notatione/Eread_string_token— Escape sequences:\n,\t,\r,\\,\',\",\0,\xHH,\u{...}read_char_token— Character literal, same escape supportread_identifier_token— Uses ICUu_isIDStart/u_isIDPart, also detects keywordsread_token— Tries all token types in priority orderresolve_token_list— Complete lexer entry point, returns token vector
Keywords (38 total)
as, alignof, break, builtin, case, comptime, const, continue, defer, do, else, enum, export, extends, extern, for, foreach, from, func, if, import, in, inline, interface, is, of, pub, return, sizeof, struct, switch, test, type, typeof, union, using, var, volatile, while
Known Issue
Whitespace tokens are sometimes incorrectly marked as SYMBOL (documented as "bug" in tests).
Complete Token Pipeline
源码文件 (const char *source)
│
▼ 阶段 1: 词法分析
resolve_token_list() ──────────────────► vec_t tokens
│ (src/cubec/token.c) (token 仅存位置指针 offset,
│ 逐字符分类: 不复制文本)
│ ├─ EOF / WHITESPACE / COMMENT / MULTILINE_COMMENT
│ ├─ IDENTIFIER (UCD: u_isIDStart/u_isIDPart)
│ ├─ KEYWORD (40个: break, case, comptime, const, ...)
│ ├─ NUMERIC (十进制/十六进制/八进制/二进制/浮点/科学计数法)
│ ├─ SYMBOL (最长匹配: 3字符 > 2字符 > 1字符)
│ ├─ STRING (支持转义 \n \t \xHH \u{...})
│ └─ CHAR (支持转义, 同 STRING)
│
▼ 阶段 2: 语法分析
read_program_node() ──────────────────► AST (cubec_program_node_t)
│ (src/cubec/program.c)
│ skip_whitespace → 循环 read_statement
│
└── 表达式解析子流程 (Precedence Climbing):
read_expression = read_expression_type
└── read_expression_ternary()
├── condition: read_expression_binary()
│ ├── read_unary() ← 前缀一元: ! + - ~
│ │ ├── read_expression_prefix() → 递归 read_unary
│ │ └── read_value() →
│ │ ├── read_atom() 基础值
│ │ │ ├─ read_expression_initialize_list() .<type>{items}
│ │ │ ├─ read_expression_typeof() typeof(expr) 编译期类型计算
│ │ │ ├─ read_expression_sizeof() sizeof(expr) 编译期大小计算
│ │ │ ├─ read_expression_alignof() alignof(expr) 编译期对齐计算
│ │ │ ├─ read_expression_type_function() func(i32) -> type
│ │ │ ├─ read_expression_function() func |caps| (params): type { body }
│ │ │ ├─ read_expression_group() (...)
│ │ │ ├─ read_expression_type_qualifier() const/volatile <type>
│ │ │ ├─ read_declaration_pointer() * [const] [volatile] <type>
│ │ │ ├─ read_declaration_slice() [] [const] [volatile] <type>
│ │ │ ├─ read_declaration_array() [expr] [const] [volatile] <type>
│ │ │ ├─ read_literal_string() "..."
│ │ │ ├─ read_literal_numeric() 42, 0xFF, 3.14e5
│ │ │ ├─ read_literal_undefined() undefined
│ │ │ ├─ read_literal_identifier() foo
│ │ │ └─ read_literal_char() 'a'
│ │ │
│ │ └── Postfix 链循环 ──────
│ │ ├─ read_expression_call() callee(args)
│ │ ├─ read_expression_generic_instantiation() callee[a,b]
│ │ ├─ read_expression_postfix_unary() value.* (解引用), value.& (取地址), value.? (try/unwrap)
│ │ ├─ read_expression_member() host.field (实例成员访问)
│ │ ├─ read_expression_namespace_access() host::field (类型成员访问/命名空间导航)
│ │ └─ (spread 不入 postfix 链, 由调用方显式调用)
│ │
│ └── read_binary_rhs() ← 中缀二元, 10级优先级
│ 1: || 2: && 3: | 4: ^ 5: &
│ 6: == != extends 7: < > <= >= 8: << >> 9: + - 10: * / %
│
├── consequent: read_expression() ← 递归
└── alternate: read_expression() ← 递归
Parsing Pipeline
Expression parsing follows a precedence-climbing architecture:
read_expression # Entry point (currently delegates to binary)
└── read_expression_binary # Binary precedence climbing
├── read_unary (static helper) # Prefix unary chain OR value
│ ├── read_expression_prefix → recursive read_unary
│ └── read_value → read_atom → postfix loop
└── read_binary_rhs (static) # RHS: precedence-climbing recursion
read_atom
├── read_expression_initialize_list # .<type>{items} or .{items}
├── read_expression_typeof # typeof(<expression>)
├── read_expression_sizeof # sizeof(<expression>)
├── read_expression_alignof # alignof(<expression>)
├── read_expression_type_function # func(i32) -> type (function type, no named params)
├── read_expression_function # func|caps|(params): type { body } (function expression)
├── read_expression_group # ( expr )
├── read_expression_type_qualifier # const/volatile <type>
├── read_declaration_pointer # * [const] [volatile] <type>
├── read_declaration_slice # [] [const] [volatile] <type>
├── read_declaration_array # [ <expr> ] <type>
├── read_literal_string
├── read_literal_numeric
├── read_literal_undefined
├── read_literal_identifier
└── read_literal_char
# Standalone expression parsers (not part of main precedence chain):
read_expression_assignment # Assignment (=, +=, -=, etc.)
read_expression_comma # Comma (a, b, c) — right-associative
read_expression_ternary # Ternary (a ? b : c)
read_expression_spread # Spread (...expr)
# Postfix operators (called from read_value loop):
read_expression_call # callee(args)
read_expression_slice # host[start:length] — MUST be before generic (uses lookahead for ':')
read_expression_generic_instantiation # callee[args] — tried after slice to handle non-slice brackets
read_expression_postfix_unary # value.* (deref), value.& (addr), value.? (try/unwrap) — MUST be before member (uses '.' token)
read_expression_member # host.field (实例成员访问,. 仅用于对象/变量)
read_expression_namespace_access # host::field (类型成员访问/命名空间导航,:: 用于类型级,. 用于实例级)
- read_expression → delegates to
read_expression_ternary(identical toread_expression_type) - read_expression_ternary → calls
read_expression_binaryfor condition, then parses? consequent : alternate - read_expression_binary → calls
read_unaryfor LHS, then entersread_binary_rhsprecedence climbing loop - read_unary → tries
read_expression_prefixfirst; if that returns NULL, falls back toread_value - Design rule: all
read_xxxentry points assume first token is ready for parsing (caller skips whitespace/comments before invoking)
Implemented Modules
read_expression_assignment(expression_assignment.c) — Parses assignment expressions: simple assignment (a = b) and compound assignment (+=,-=,*=,/=,%=,&=,|=,^=,<<=,>>=). Left operand must be an lvalue (identifier, member access, dereference, or subscript). Right operand parsed viaread_expression_ternary. Returnscubec_expression_assignment_twrapping the target and value. When no assignment operator is found, returns NULL gracefully.read_expression_comma(expression_comma.c) — Parses comma expressions (a, b, c). Right-associative:a, b, cparses ascomma(a, comma(b, c)). Left operand triesread_expression_assignmentfirst, then falls back toread_expression_ternary. After consuming a comma, recursively calls itself for the right operand; if that returns NULL, falls back toread_expression_ternary. When no comma is found after the left operand, returns the left operand directly (passthrough behavior).read_expression_binary(expression_binary.c) — Full precedence-climbing binary expression parser. Supports 10 precedence levels:\|\|<&&<\|<^<&<== != extends<< > <= >=<<< >><+ -<* / %. Theextendskeyword is a binary operator at the same precedence level as==and!=(level 6), handled separately inget_binary_precedence()via keyword check. Note: assignment (=,+=, etc.) and comma (,) are NOT part of this binary precedence table — they are parsed as separate expression types. Callsread_unaryfor operands, usesread_binary_rhsfor right-recursive precedence climbing.read_expression_prefix(expression_binary.c) — Parses prefix unary operators (!,+,-,~); right operand parsed via recursiveread_unary(NOTread_expression, which prevents incorrect binding like-42 * 3being parsed as-(42*3)); supports chained!!x,--n; returnscubec_expression_binary_twithleft=NULL; does NOT callskip_whitespaceat entryread_expression_postfix_unary(expression_postfix_unary.c) — Parses postfix unary operatorsvalue.*(dereference),value.&(address-of), andvalue.?(try/unwrap). Composed of separate.and&/*/?tokens combined by the parser. Uses lookahead for:to distinguish from slice expressions. Must be called beforeread_expression_membersince both use the.token. Returnscubec_expression_postfix_unary_twithoptset to".*",".&", or".?".read_value(expression.c) — Atom + recursive postfix loop. Callsread_atomfirst, then in while loop: callsskip_whitespace, then tries postfix operators in order:read_expression_callforcallee(args),read_expression_sliceforhost[start:length],read_expression_generic_instantiationforcallee[args],read_expression_postfix_unaryforvalue.*,value.&, andvalue.?, thenread_expression_memberfor.fieldaccess. Slice is tried before generic instantiation using lookahead for:to distinguisharr[0:10](slice) fromarr[0](generic). Postfix unary is tried before member since both start with..read_expression_call(expression_call.c) — Parses C-style function callcallee(arg1, arg2, ...). Called fromread_valueas a postfix operator withcalleealready parsed. Each argument first triesread_expression_spread(supporting...exprincluding pack expansion...args), then falls back toread_expression. Returns NULL if next token is not(; THROW errors on malformed arguments (trailing comma, unclosed paren). Supports chained callsfoo()()and mix with member:obj.method(),foo().field. Ownership:argumentsvec created withauto_dispose=truein parser;initdirectly takes the pointer (no copy), ownership fully transferred to node.read_expression_generic_instantiation(expression_generic_instantiation.c) — Parses generic instantiationcallee[arg1, arg2, ...]. Uses[and]as delimiters. Arguments parsed viaread_expression, each first triesread_expression_spread(supporting...exprin generic args). Returns NULL if next token is not[; THROW errors on malformed arguments (trailing comma, unclosed bracket). Supports chained instantiationsfn[a][b]and mixing with calls and member access:fn[a](),fn[a].field,foo()[a]. Single-arg formobj[0]is syntactically ambiguous with member access — disambiguation deferred to semantic analysis.read_expression_slice(expression_slice.c) — Parses slice expressionhost[start:length]. Called fromread_valueas a postfix operator withhostalready parsed. Format:host[start:length]wherestartandlengthare both optional (at least:must be present). If[doesn't follow, returns NULL gracefully. If[](empty brackets), throws error. Usesread_expressionfor parsing start/length expressions. Supports chained slicesarr[1:2][0:1]and mixing with calls and member access:arr[0:1].field,getArr()[1:]. Node fields:host,start,length(start/length may be NULL if omitted).read_atom(expression.c) — Parses in order:read_expression_initialize_list→read_expression_typeof→read_expression_sizeof→read_expression_alignof→read_expression_type_function→read_expression_function→read_expression_group→read_expression_type_qualifier→read_declaration_pointer→read_declaration_slice→read_declaration_array→read_literal_string→read_literal_numeric→read_literal_identifier→read_literal_char. Type and value expressions share a unified parsing path:read_expression_typedelegates directly toread_expression. Composite types (pointer/slice/array/qualifier/function type) greedily consume their inner type expression, including ternary. Use grouping()to prevent greedy consumption.read_expression_group(expression_group.c) — Parses parenthesized expression( expr ). Returnscubec_expression_group_twrapping the inner expression. Tried first inread_atomso(a + b)is parsed as a group wrapping a binary expression.read_expression_initialize_list(expression_initialize_list.c) — Parses initialize list expression.<type>{<items>}or.{<items>}. Called fromread_atomas a primary expression (tried beforeread_expression_group). Checks for.at current position, then looks ahead:.+{→ anonymous (type=NULL),.+ type expression +{→ typed. Type is parsed viaread_expression_type(supports member access, generic instantiation, pointer, etc.). Items are comma-separated and must be homogeneous: either allinitialize_field(.name = value) or all positional expressions — mixing is an error. First item determines mode: triesread_initialize_fieldfirst; if that fails, falls back toread_expression. In field mode, non-field items cause error; in positional mode, field-like items cause error. Disambiguation:.{.Test{}}is positional (.Test{}is a nested initialize_list expression),.{.Test=123}is field mode (.Test=123has=). THROW errors on: trailing comma, unclosed}, mixed field/positional items. Returnscubec_expression_initialize_list_twithtype(nullable node_t),items(vec_t with auto_dispose=true),is_field(bool). Supports postfix chaining:.Vec{1,2}.field, and binary context:1 + .Vec{1,2}.read_initialize_field(expression_initialize_field.c) — Parses initialize field expression.identifier = expression. Used insideread_expression_initialize_listto parse individual field items. Checks for.followed by identifier followed by=. Returns NULL if the.+ identifier +=pattern is not matched (not an error — allows caller to try positional expression parsing). Returnscubec_initialize_field_twithfield(cubec_literal_identifier_t) andvalue(node_t).read_generic_params(generic_param.c) — Parses generic parameter list[param1, param2, ...]. Supports four forms: simple (T), constrained (T extends Numeric), value generic (N: u64), and rest param (...Args). Rest param is detected by checking for...symbol before reading identifier; if detected,is_restis set totrue. Constraint and value types are parsed viaread_expression_type(greedy — consumes ternary). Parameters are comma-separated within[]. Pack params must be last; only one pack param allowed. Returns a vec ofcubec_generic_param_t. Ownership: params vec is created withauto_dispose=true; caller takes ownership.read_expression_spread(expression_spread.c) — Parses spread operator...<expr>. Returnscubec_expression_spread_twrapping the spread value. Standalone function — NOT called fromread_atom/read_value/read_expression. Designed to be explicitly invoked by callers that support spread syntax (e.g., function arguments, struct initializers). Usesread_expressionfor the value so...a + bspreads the entire binary expressiona + b.read_expression_ternary(expression_ternary.c) — Parses ternary/conditional expressioncondition ? consequent : alternate. Uses precedence climbing viaread_expression_binaryfor the condition. Falls back gracefully if?is not found (returns condition as-is). Recursively callsread_expressionfor consequent and alternate to handle nested ternaries naturally. Full lifecycle: init/dispose/clone/move. Node fields:condition,consequent,alternate.read_literal_char— Character literal AST noderead_literal_undefined— Undefined literal AST node (undefined), used as TDZ initializer in var declarationsread_literal_identifier— Identifier AST noderead_literal_numeric— Numeric AST node, auto-detects int/float, supports type suffixes (i8-i64,u8-u64,f16-f64)read_literal_string— String AST node, supports auto-concatenation of adjacent stringsread_statement_empty— Empty statement (;)read_statement_block(statement_block.c) — Block statement ({ <statements> }). Parses a sequence of statements enclosed in curly braces, creating a new scope. The block may be empty ({}). Returnscubec_statement_block_twith astatementsvec field. Returns NULL if current token is not{. THROW errors on unclosed brace or unexpected token in block.read_statement_expression(statement_expression.c) — Expression statement (<expression>;). Parses an expression viaread_expression, then expects a mandatory trailing semicolon. Missing semicolon is a parse error. Returnscubec_statement_expression_twithexpressionfield. Returns NULL ifread_expressionreturns NULL (no expression to form a statement).read_statement(statement.c) — Statement dispatcher. Tries each statement parser in order:read_statement_blockfirst (has distinguishing prefix{), thenread_statement_declaration(has distinguishing prefixvar), thenread_statement_declaration_type(has distinguishing prefixtype), thenread_statement_function(has distinguishing prefixfunc/export/inline/extern), thenread_statement_import(has distinguishing prefiximport), thenread_statement_return(has distinguishing prefixreturn), thenread_statement_empty(has distinguishing prefix;), thenread_statement_expressionas the fallback (no distinguishing prefix — any expression can start it, so it must be tried last). UsesTRY_LOCAL(onerror, ...)to catch errors from sub-parsers; on error, returns NULL. Returns the first successful parse, or NULL if no statement matches.read_statement_declaration(statement_declaration.c) — Declaration statement ([export|extern|builtin|comptime|using] var <declarator> ;). Parses a single variable declarator:<identifier> [: <type>] [= <expression>]. Modifiers:export(exported from module),extern(external linkage, no initializer, requires type annotation),builtin(compiler-provided, no initializer, requires type annotation),comptime(compile-time evaluated, requires initializer, mutually exclusive withexternandbuiltin),using(RAII declaration, auto-defer__dispose__at scope exit, mutually exclusive withextern,builtin, andcomptime, not allowed at module scope, cannot useundefined).exportandbuiltinare orthogonal (can combine).exportandcomptimeare orthogonal (can combine).exportandusingare orthogonal (can combine).externis mutually exclusive withexport,builtin,comptime, andusing.builtinandcomptimeare mutually exclusive.usingis mutually exclusive withbuiltin,comptime, andextern. Extern/builtin declarations must not have= expression. Comptime declarations must have= expression. Using declarations must have= expression(notundefined). Returnscubec_statement_declaration_twithis_export,is_extern,is_builtin,is_comptime,is_using(bools) anddeclarator(singledeclaration_variable_tnode) fields.read_statement_declaration_type(statement_declaration_type.c) — Type alias declaration ([export|builtin] type Name[<generic_params>] [= <type_expression>];). Modifiers:export(exported from module, orthogonal with builtin),builtin(compiler-provided, no= type_expressionbody). For builtin types,type_valueis NULL. Returnscubec_statement_declaration_type_twithis_export,is_builtin(bools),name(identifier),params(vec ofcubec_generic_param_t, may be NULL), andtype_value(type expression, NULL for builtin) fields.read_statement_import(statement_import.c) — Import statement (import <module_name> [as <alias>] from "<path>";). Parses module import with optionalasalias. Returnscubec_statement_import_twithmodule_name(identifier node),alias(optional identifier node, NULL if noas), andpath(string literal node) fields. Returns NULL if current token is notimport. THROW errors on missing module name, missingfromkeyword, missing path, or missing semicolon.read_function_argument(function_argument.c) — Parses a single function parameter:[...]<identifier> [: <type>]. Supports...prefix for pack expansion parameters (...args: Args). The identifier is parsed viaread_literal_identifier. The optional type annotation is parsed viaread_expression_type. Returnscubec_function_argument_twithidentifier,type(nullable), andis_rest(bool) fields. Returns NULL if current token is not...or an identifier.read_statement_function(statement_function.c) — Parses function declaration statement. Delegates toread_expression_functionfor the actualfuncparsing, then validates the result: statement functions must have a name, cannot have captures, and C-style variadic...is only allowed in extern functions. Handles modifier parsing (export/inline/extern/builtin/comptime) and mutual exclusion checks (export+extern,inline+extern,builtin+extern,comptime+extern,comptime+builtin) before delegation. Comptime functions must have a body. Afterread_expression_functionreturns, extracts fields from the expression function node (transferring ownership) and creates acubec_statement_function_tnode. Returnscubec_statement_function_twithis_export,is_inline,is_extern,is_builtin,is_comptime,is_c_variadic(bools),name,generic_params(nullable vec),arguments(vec with auto_dispose=true),return_type(nullable),body(nullable) fields. Returns NULL if current token is not a function declaration prefix.read_expression_function(expression_function.c) — Universal func parser that handles both anonymous and named function expressions:func [|<captures>| | <name>] [<generic_params>] (<params>) [-> <return_type>] { <body> } | ;. Afterfunckeyword, detects|/||(capture list) or identifier (function name) or falls through to[/((anonymous, no captures, no name). Name is nullable (present for named functions, NULL for anonymous). Capture list is optional:||(empty, tokenized as single||by lexer, captures remains NULL),|x, y|(non-empty), or omitted entirely. Each capture is identifier-only. Generic params, function params, and return type follow standard rules. Parameter list supports C-style variadic...(stored inis_c_variadic, validity checked by caller). Body: named functions allow;(body=NULL), anonymous functions require{ body }. Returnscubec_expression_function_twithname(nullable),captures(nullable vec ofcubec_function_capture_t),generic_params(nullable vec),arguments(vec with auto_dispose=true),return_type(nullable),body(nullable),is_c_variadic(bool) fields. Returns NULL if current token is notfunckeyword. Supports postfix: immediate callfunc |x| (a: i32): i32 { return x + a; }(42), member accessfunc || ():Vec[i32] { }.field, assignmentvar f = func |x| () { };.read_function_capture(function_capture.c) — Parses a single capture item:<identifier>. Captures are identifier-only. Returnscubec_function_capture_twithidentifierfield. Returns NULL if current token is not an identifier.read_statement_return(statement_return.c) — Parses return statement:return [<expression>] ;. Expression is optional (barereturn;hasexpression = NULL). Expression parsed viaread_expression. Returnscubec_statement_return_twithexpression(nullable) field. Returns NULL if current token is notreturnkeyword.read_declaration_variable(declaration_variable.c) — Variable declarator (<identifier> [: <type>] = <expression>). Parses a single variable declarator with optional type annotation and required initializer expression. The type is parsed viaread_expression_type. Returnscubec_declaration_variable_twithidentifier,type(nullable), andexpressionfields.read_program_node— Top-level entry, parses statements using a whitelist approach:statement_import(import),statement_declaration([export|extern|builtin|comptime] var),statement_declaration_type([export|builtin] type),statement_function([export] [inline] func|[extern] func|[builtin] func|[comptime] func),statement_empty(;).usingis NOT allowed at program level (no defer semantics).statement_expressionis NOT supported at program level (only within blocks). UsesTRY_LOCAL(onerror, ...)to catch errors from sub-parsers.read_expression_type(expression.c) — Now identical toread_expression. Type and value expressions share a unified parsing path. Composite types (pointer/slice/array/qualifier/function type) greedily consume their inner type expression, including ternary:*a ? b : c→pointer(ternary(a, b, c)),func(i32) -> A ? B : C→func(i32) -> ternary(A, B, C). Use grouping to prevent greedy consumption:(*a) ? b : c→ternary(pointer(a), b, c).typeofcan now be used as pointer/slice/array base type:*typeof(x),[]typeof(x). Namespace access (::) binds tighter than type constructors:*std::vec::Vec→*(std::vec::Vec). Type constraints (extends/==/!=) are binary operators parsed byread_expression_binary.read_expression_type_function(expression_type_function.c) — Parses function type expressionsfunc(<type_list>) -> <return_type>. Parameters are type-only (no names), unlike function definitions which usename: type. Return type uses->(not:) to distinguish from function definitions. Parameters and return type are parsed viaread_expression_type(greedy — consumes ternary/constraint). Greedy behavior:func(i32) -> A ? B : C→func(i32) -> ternary(A, B, C)(return type greedily consumes ternary). Use grouping for the alternative:(func(i32) -> A) ? B : C. Supports: no paramsfunc() -> i32, multiple paramsfunc(i32, i32) -> void, C-style variadicfunc(i32, ...) -> void, pack expansionfunc(...Args) -> R, complex typesfunc(*i32) -> *i32, nested function typesfunc(func(i32) -> i32) -> void. Can be wrapped by pointer/slice/const/volatile:*func(i32) -> i32(pointer to function),const func(i32) -> i32. Returnscubec_expression_type_function_twithparameters(vec of node_t type expressions, auto_dispose),return_type(nullable node_t),is_c_variadic(bool) fields. Returns NULL if current token is notfunckeyword or(does not followfunc, or if named parameter pattern detected (identifier:→ function expression).read_expression_type_qualifier(expression_type_qualifier.c) — Parses const/volatile type qualifier expressions. Merged from the former separateconstandvolatileparsers into a single node withis_volatileflag. A standalone prefix type modifier at the same level as pointer/slice/array. The underlying type is parsed viaread_expression_type(greedy — consumes ternary). Placed before pointer inread_atomso thatconst * i32parses asconst(pointer(*i32))rather than*consumingconstas a qualifier. Greedy consumption:const a ? b : c→const(ternary(a, b, c)). Supports nesting:const const i32. Can combine:const volatile i32→const(volatile(i32)),volatile const i32→volatile(const(i32)). Use grouping to prevent greedy:(const a) ? b : c→ternary(const(a), b, c). Returnscubec_expression_type_qualifier_twithtypeandis_volatile(bool) fields. Returns NULL if the current token is not the keywordconstorvolatile.read_expression_typeof(expression_typeof.c) — Parses compile-time type computation expressiontypeof(<expression>). Available in both type expression context (viaread_type_expression_primary) and value expression context (viaread_atom). In type expressions,typeof(x)can be used as pointer/slice/array base type:*typeof(x),[]typeof(x). In value expressions,typeof(x)is an atom that supports full postfix chaining:.field(member access),::method(namespace access),[i32](generic instantiation),()(call). The inner expression is parsed viaread_expression. Returnscubec_expression_typeof_twithexpressionfield. Returns NULL if the current token is not thetypeofkeyword. THROW errors on missing(, missing), or missing inner expression.
Not Yet Implemented
All statement and declaration types have parser implementations, including comptime blocks/if/for.
Semantic Analysis Engine (src/engine/)
Architecture
13-file split: checker.c (lifecycle + pass orchestration), checker_collect.c (Pass 1: symbol collection), checker_check_stmt.c (Pass 2: type checking), checker_check_expr.c (Pass 2: expression type checking), checker_check_expr_helpers.c (expression helper functions), checker_type_util.c (type utilities: instantiation, substitution, unification), type_unify.c (generic type inference), checker_evaluate.c (Pass 2: comptime evaluation + type resolution), flow_state.c (control flow analysis: unreachable detection, return completeness, TDZ flow propagation), builtin.c (builtin registry mechanism only), builtin_debug.c (assert), builtin_panic.c (panic), builtin_collection.c (length), builtin_tuple.c (getTupleItem/setTupleItem), builtin_cast.c (cast), comptime_eval.c (evaluator lifecycle), comptime_eval_expr.c (expression evaluation), comptime_eval_stmt.c (statement execution), comptime_alloc.c (virtual memory). All functions ≤ 50 lines. Design doc: docs/semantic-design.md.
Type System (semantic_type_t)
Two-layer representation: semantic_type_t wraps AST type nodes with semantic info. Structural equivalence for type comparison. Pointer decay rules. Built-in types: builtin_i8builtin_u64, builtin_f16builtin_f64, builtin_bool, builtin_void, etc. Type layout computation via type_layout_compute.
const/volatile qualifier: TYPE_QUALIFIER has is_const and is_volatile flags. *const T → POINTER(QUALIFIER(const, T)) (pointer to const T, C: const T*). const *T → QUALIFIER(const, POINTER(T)) (const pointer, C: T* const). Utility functions: semantic_type_is_const(), semantic_type_is_volatile(), semantic_type_strip_qualifier(). Const enforcement: assignment to const lvalue errors, member/deref const propagation, is_mutable set based on !semantic_type_is_const(). Implicit conversion allows T → const T but not const T → T. Comptime eval enforces const but ignores volatile.
Tuple type (TYPE_TUPLE): Native type kind for tuples. Syntax: <i32, f64> (angle brackets in type context). _type_impl.tuple contains element_types (vec of semantic_type_t) and fields (pre-computed _0, _1, ... symbol vec). Structural equivalence: two tuples are equal iff element types are pairwise equal. Tuple→Array implicit conversion: allowed if each element can be implicitly converted to the array element type and lengths match. T extends <?> generic constraint means T must be TYPE_TUPLE. < disambiguation: in type context < starts a tuple; in expression context < is comparison. Tuple fields are NOT directly accessible via ._0, ._1 — must use getTupleItem[N](tuple) builtin function.
Parameter pack type: TYPE_GENERIC_PACK represents a variadic pack type, containing expanded_types (vec of semantic_type_t). Used when a generic parameter is declared with ... prefix. Pack types expand to zero or more concrete types during instantiation. The comptime value layer uses COMPTIME_VALUE_PACK with an elements vec to represent pack values at compile time.
Opaque type (TYPE_OPAQUE): A type-erased pointer-like type analogous to C's void*. Layout: size = sizeof(void*), alignment = alignof(void*). Any pointer or slice type can implicitly convert to opaque. Opaque cannot implicitly convert to any other type. Registered as builtin_opaque in the checker. Not directly constructible via literals; typically obtained from pointer-to-opaque conversion.
Anonymous initialize_list type inference: Initialize lists without an explicit type (.{...}) are context-independent semantic units that infer their type from their contents:
- Named fields
.{.x=1, .y=2}→ anonymousstruct { x: i32, y: i32 } - Positional fields
.{1, 2.0}→ tuple<i32, f64> - Empty
.{}→ empty struct - Tuple→Array implicit conversion: if all tuple elements can implicitly convert to the target array element type,
<e1, e2, ...>converts to[N]T - Struct-like→struct-like: anonymous struct can implicitly convert to named struct or generic instance if field names and types match pairwise
Implicit conversion rules (semantic_type_can_implicit_convert):
- Same type → true
- Qualifier strip:
T → const T,T → volatile T,T → const volatile T(recursive) - Pointer conversion: only qualifier addition on pointee (
*T → *const T), pointee types must be structurally equivalent - nil → pointer/slice/interface
- Integer widening
- Float widening
- Tuple → array (element size+alignment layout-compatible)
- Pointer/slice → opaque
- Struct-like → struct-like (field name + type matching, supports TYPE_STRUCT/UNION/CUNION/GENERIC_INSTANCE)
NOT allowed implicitly (per design doc): int→float, []T→*T, [N]T→[]T. These require explicit cast[]().
Array/slice decay (semantic_type_can_decay): returns false — no implicit array-to-slice or array-to-pointer decay.
Explicit cast rules (semantic_type_can_explicit_cast):
All implicit conversions plus:
- Numeric: float→int (truncation), int→float, int narrowing, float narrowing, bool↔int, enum↔int, char↔int
- Pointer: opaque→pointer, pointer→int, *Small→*Big downcast (struct pointer, prefix field match)
- Container: array→tuple (element size+alignment layout-compatible)
The checker validates explicit casts in _check_expr_call after generic instantiation of the cast builtin.
Generic type inference (type_unify.c): _infer_type_args_from_call infers type arguments from call arguments using _type_unify (structural unification). Generic param names are passed as const char* strings (not symbol pointers, since generic params aren't in scope). _type_unify handles TYPE_GENERIC_INSTANCE elastic matching when expected type_args contain a PACK parameter. The generic_params vec in _infer_type_args_from_call must contain name strings directly, not symbol pointers from scope_lookup (which returns NULL for generic param names).
Symbol Table (scope_t)
Chain-of-responsibility scope model. scope_lookup returns SYMBOL_NAME_KNOWN for imported values (cannot be used at compile time). TDZ (temporal dead zone) multi-pass checking. Symbols have is_builtin flag set when builtin declaration passes validation against the builtin table. undefined literal: CUBEC_NODE_LITERAL_UNDEFINED is a valid initializer for var declarations with explicit type annotation; the variable enters SYMBOL_TDZ state. Non-extern/non-builtin var declarations require an initializer (var x: i32; is an error). Using undefined as a standalone expression is an error.
Builtin Registry (builtin_table_t)
Dynamic registry (builtin.h/builtin.c) mapping names to builtin_entry (name + type + eval_call callback). No enum dispatch IDs — each builtin entry carries an eval_call function pointer for comptime evaluation. builtin.c contains only the mechanism (table create/dispose/register/lookup); builtin_table_init_defaults() dispatches to per-module init functions. Builtin function declarations must come from standard library source code (using builtin func syntax), NOT auto-registered to global_scope by the compiler. This ensures proper module affiliation and version decoupling. Builtin declarations go through normal checker flow (type resolution, generic param handling) then are validated against the table: unknown builtin → error, signature mismatch → error, match → sym->is_builtin = true. Comptime eval uses callee_sym->is_builtin + eval_call callback instead of hardcoded name checks or switch/case dispatch.
Module split:
builtin_debug.c/h—assert(type creation +builtin_assert_evalcallback). assert is only allowed inside test blocks; calling it elsewhere is an error. On failure, assert reports the error but does NOT abort the block — subsequent statements continue executing (test-local failure).builtin_panic.c/h—panic(msg: str): void(type creation +builtin_panic_evalcallback). panic is unrecoverable — it propagatesCOMPTIME_VALUE_FATALwhich triggersCOMPTIME_SIGNAL_FATAL, setsctx->fatal_error, and aborts all further compilation. No subsequent declarations or statements are evaluated.builtin_collection.c/h—length(type creation +builtin_length_evalcallback)builtin_tuple.c/h—getTupleItem,setTupleItem(type creation +builtin_get_eval/builtin_set_evalcallbacks)builtin_cast.c/h—cast[T,K](expr:K):T(type creation +builtin_cast_evalcallback)builtin_union.c/h—unionIs[T,K](obj:K):bool(type creation +builtin_unionis_evalcallback)builtin_dispatch.c/h— deleted (callbacks moved to respective modules)
Tuple is a native type (TYPE_TUPLE), not a builtin. Syntax: <i32, f64> (angle brackets in type context). Fields are _0, _1, etc. See Type System section for details.
getTupleItem[N: u64, ...Args](tuple: <...Args>): Args[N] — Returns the Nth element of a tuple. N is a value generic parameter (TYPE_GENERIC_VALUE). Type checking resolves the return type by looking up the Nth field of the concrete tuple type. Normal generic instantiation flow handles type checking and substitution; eval_call callback (builtin_get_eval) handles comptime execution.
setTupleItem[N: u64, ...Args](tuple: <...Args>, value: Args[N]): void — Sets the Nth element of a tuple. Value type must match the Nth field type. Same as getTupleItem: normal flow for type checking, eval_call for comptime.
castT,K:T — Explicit type cast. The checker validates via semantic_type_can_explicit_cast after generic instantiation. Comptime eval (builtin_cast_eval) performs the actual value conversion. Supported casts: numeric (float→int truncation, int narrowing, float narrowing, bool↔int, enum↔int, char↔int), pointer (opaque→pointer, pointer→int, *Small→*Big downcast), container (array→tuple with layout-compatible elements).
unionIsT,K:bool — Check if a tagged union's active variant matches type T. K must be a
Truncated - read the full file at https://github.com/AserJoker/cubec/blob/3a4556201f0b15310879cb070606aabcd423c93e/.codebuddy/skills/cubec-project/SKILL.md.