DEV Community

COMMENTERTHE9
COMMENTERTHE9

Posted on • Originally published at cx-lang.com

Cx Dev Log — 2026-03-23

The big story today is that Cx has revamped its entire IO structure. Gone are the days of print, printn, read, and input being treated as special cases in the lexer and parser. Now, these are unified under function syntax, marking a significant shift along with the completion of Phase 1 and 2 of generic structs, introduction of string interpolation, and stdin built-ins. This update, encapsulated in PR #15, closed four critical blockers for the 0.1 release, pushing forward with a test matrix now showing 63 green tests.

Print Promoted to Function

This was the major structural change everyone is talking about. KeywordPrint and KeywordPrintInline have been removed from the lexer's vocabulary. We’ve deleted 47 lines of combinator spaghetti from the parser that were handling print statements. Now, print and printn are parsed as Expr::Call, making them akin to any other function call. printn has been duly registered as a built-in within call_semantic_func.

While the transition was largely smooth, we had to tweak the call_arg parser to better handle nested calls and method calls when they appeared as arguments. So, 47 parser lines out, 11 runtime lines in. This transformation unexpectedly resolved a longstanding issue with the t42 TypeParam-vs-Struct ambiguity, a clash existing since generics v2. It turned out that print as a keyword was creating parsing conflicts that no longer exist with it being a standard function call. As a result, we removed the expected_fail marker on t42.

String Interpolation and Stdin Built-ins

String interpolation is finally here. By utilizing the expand_interpolation method in the runtime, placeholders like {varname} within string literals are expanded when printing. Importantly, variables not recognized within these placeholders simply pass through unchanged, providing a predictable output especially during the initial development phases.

We’ve also introduced built-in functions for standard input operations. read(var) reads a line from stdin directly into a variable, and input("prompt", var) first prints a prompt before reading. This keeps the consistency with our redesigned IO surface, confirmed by test t60 covering basic read operations using this interpolation syntax.

Generic Structs Phase 1 and 2

Phase 1 ushered in type_params for Stmt::StructDef and SemanticStmt::StructDef. Our parser can now handle syntax like struct Foo { field: T }, allowing the analyzer to correctly resolve type parameters. During field analysis, current_type_params are set, while a struct_type_params registry tracks them methodically. Tests t61 and t62 validated basic generic struct definitions and instantiation using f64.

With Phase 2, AstValue::StructInstance now supports Vec type arguments. Consequently, the parser now understands Pair<t32> { ... } instantiations, and we updated all StructInstance match sites to accommodate this expanded form with three fields. Test t63 thoroughly tested explicit type argument instantiation.

However, some gaps remain, specifically type arguments in variable type annotations (p: Pair<t32>) and enforcement of generic field type checking, both marked for follow-up despite their current lack of implementation.

Dead Code Cleanup

Goodbye to unnecessary code. We’ve stripped out the enum group runtime infrastructure, including EnumRuntimeInfo, the enums field on RunTime, and super_group_handler_index. These components were purely write-only and no longer had a purpose as SemanticWhenPattern now supports all when-group matching needs. This cleanup freed 37 lines from runtime.rs and 8 from main.rs. It follows closely on the heels of our recent 790-line AST interpreter removal, further reducing the codebase and emphasizing the newfound dominance of the semantic path.

What's Next

Up next, we're eyeing Phase 3 of generic structs: tackling type arguments in variable declarations and enforcing checks on generic fields. Multi-file imports remain a tough nut to crack for 0.1, with syntax locked in but functionality yet to be implemented. Although backend IR tasks like loop and struct lowering were scheduled, they did not land; however, the IO unification has cleared a prerequisite hurdle for backend work involving calling conventions.

Our test runner and assertions (assert(cond), assert_eq(a, b), test blocks) are open blockers that demand attention, although no progress has been made there yet.

As it stands, the test matrix is solid at 63/63 with no regressions. We'll keep pushing forward.


Follow the Cx language project:

Originally published at https://cx-lang.com/blog/2026-03-23

Top comments (0)