Yuzhe's Blog

yuzhes

RedScript v3.0 Feature Deep Dive

What matters most in RedScript v3 is not the marketing line “more powerful.” It is which capabilities are truly wired through parser, typechecker, HIR/MIR, and emit, and which ones are still only present at syntax level. Clarifying that boundary is more useful than listing new keywords.

match on string: string branches without integer hacks

src/__tests__/compiler/match-string.test.ts makes this very explicit: match cmd { "help" => ... } becomes an if data ... matches "help" check against storage rs:strings, instead of mapping strings to a custom enum first. For datapack development this is practical: command names, mode names, and UI inputs can be dispatched directly. The value is not just syntax modernity; string values now participate in compilable control flow.

tuples: from “single return value” to multiple returns

Tuple is the most complete feature in v3. AST has tuple type, tuple_lit, let_destruct; tests cover (int, int) return types, return (a, b), and let (q, r) = divmod(...). More important is that lowering does not stop at syntax: MIR writes to __rf_0/__rf_1, and final emit maps these to $ret_0/$ret_1. That means tuple is not sugar; it participates in the actual call convention. For a Minecraft target with no stack and no registers, this is a very pragmatic design.

interface/trait: syntax has landed, implementation is midstream

The boundary matters here. src/ast/types.ts and src/parser/index.ts already support interface Foo { ... }; impl Display for Vec2 { ... } can be parsed and kept in HIR, and src/__tests__/struct-display.test.ts confirms that path. But I have not found a full set of typechecker constraints, trait method resolution, or complete codegen today—there are still todos in tests. So the accurate statement is: v3 has built syntax scaffolding and partial HIR plumbing for interface/trait, but it is not yet a fully usable Rust/TS-style trait system.

decorators: @singleton, @config, @watch, @deprecated, @inline

This group is the most “engineering-focused” part of v3.

@singleton is not mere sugar. It injects a global state model into structs. The typechecker synthesizes Type::get() / Type::set(...) for singleton structs; emit generates scoreboard objectives plus get/set functions for fields. This is ideal for game-wide global state.

@config is compile-time injection. In tests, compile(..., { config: { difficulty: 5 } }) directly changes output mcfunction constants. So it is build-time specialization, not runtime config loading.

@watch follows another path: the compiler generates a dispatcher that, each tick, compares a scoreboard objective’s current and previous values and fires callbacks only when the value changes. That makes scores useful as event sources.

@deprecated currently emits compile-time warnings. src/hir/deprecated.ts walks HIR call sites and emits [DeprecatedUsage] warnings without blocking compilation.

@inline has reached the optimizer. src/__tests__/optimizer/inline_fn.test.ts shows it can inline single-block functions and multi-block CFG functions while explicitly skipping recursive, macro, and external functions. For a datapack backend where a function call is mostly file dispatch, this is high-value optimization.

do-while / repeat

Both loops are more than parser recognition. src/__tests__/compiler/do-while.test.ts shows do { ... } while cond lowers in HIR as “execute body first, then while.” repeat 5 { ... } lowers to a hidden counter plus while. In other words, these are complete semantic features, not sugar. That lowering is appropriate because Minecraft ultimately needs explicit loop blocks.

const, Result, and format_string fixes

const now works at top level and also enters typechecker readonly constraints; assigning to const produces a direct error. This change is not flashy, but it significantly reduces accidental constant mutation in datapack scripts.

Result needs careful wording. The docs call it Result<T>, but current implementation in src/stdlib/result.mcrs is actually concrete enum Result { Ok(value: int), Err(code: int) }, not a generic ADT. It is sufficient for safe division and error-code propagation, but cannot be described as full generic Result<T> yet.

format_string updates are mostly compatibility and bug fixes: the typechecker keeps the legacy format_string type annotation, and the rich-text builtin parameter checks are relaxed to string or format_string. With stronger f-string tests, v3 clearly moves away from treating formatted strings as a side channel and back toward normal string semantics.

Closing

If you only read the feature list, v3 looks like a major upgrade, but from source it is a maturation of compiler convergence. The features truly end-to-end today are string match, tuples, several decorators, do-while/repeat, const, and several string/warning/optimization capabilities. interface/trait and truly generic Result<T> are still on the way. Distinguishing “stable mcfunction emit” from “just entering syntax and IR” is the right way to evaluate RedScript v3.