Latest commit

History

200 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Blu: A language extending Lua with a fast Rust runtime

Blu is a fast, embeddable Lua/Luau superset language and runtime written in Rust. It is built for deeply extensible native applications. Its default blu dialect pragmatically unifies and extends Luau and modern Lua; explicit compatibility dialects preserve the exact semantics of each upstream language version where those semantics conflict.

Borg Agent embedding, package authority, and customization boundaries are documented in docs/borg-embedding.md. Blu owns the language runtime; the Borg host owns extension admission and capabilities.

Blu starts from Luau's optimized language and VM design, but it is not confined to Roblox's sandboxed Luau surface. Blu supports explicit Luau and Lua compatibility modes alongside a first-class systems profile with io, os, packages, filesystem, network, and process capabilities.

Blu is currently under active compatibility development. The initial milestone is a safe Luau-bytecode loader and interpreter continuously checked against a pinned upstream Luau revision. Lua 5.1–5.5 source profiles, standard libraries, and versioned C API bridges follow behind the same differential conformance gates.

The current Luau-backed Blu path compiles source in-process and covers scalar and table operations, numeric and generic loops, closures and mutable upvalues, variadic and multiple-return calls, globals and imports, Rust native functions, protected calls, pairs/ipairs/next, string method dispatch, core metamethods, an initial standard library, and host-configured cached require. The owned Blu/Luau frontend also lowers value-selecting if ... then ... elseif ... else ... expressions with short-circuit branches; Lua profiles reject that Luau syntax explicitly. The contextual _VERSION global reports Blu, Luau, or the selected Lua 5.1Lua 5.5 profile and remains explicitly overridable by the guest or embedder. The math slice includes shared truncating math.fmod; unlike floor-based %, its remainder preserves the dividend's sign, with modern integer preservation and zero-divisor errors. Shared math.pow provides the library form of floating exponentiation, while math.frexp and math.ldexp split and compose binary exponents with profile-appropriate exponent subtypes and conversion. The legacy sinh/cosh/tanh/log10/atan2 names remain available in Blu, Luau, and Lua 5.1–5.4 profiles and fail explicitly in Lua 5.5, which removed them. math.atan follows the legacy one-argument Luau/Lua 5.1–5.2 contract or modern Blu/Lua 5.3–5.5 atan2(y, x) contract according to the active profile; math.asin and math.acos follow the shared numeric contract. Profile-aware math.floor and math.ceil and the integral result of two-result math.modf preserve number-only legacy behavior or return modern exact integers when representable. math.abs preserves modern integer inputs, while math.log explicitly distinguishes Lua 5.1's ignored extra arguments from modern base selection. math.min and math.max preserve the selected modern subtype and upstream NaN ordering, using exact mixed integer/number comparisons across i64. Modern-profile math.type, math.tointeger, and math.ult provide numeric subtype introspection, exact integral conversion, and unsigned comparison. Profile-aware math.random and math.randomseed provide a deterministic, non-cryptographic generator with explicit legacy coercion, result-subtype, zero-bound, seed-arity, and seed-return contracts; seeded reproducibility is guaranteed, but upstream implementations' exact random sequences are not. Blu/Luau profiles also expose math.clamp, math.sign, and math.round with the pinned Luau edge behavior, plus numeric classification and math.lerp/math.map interpolation helpers and deterministic math.noise. The bit32 library provides variadic AND/OR/XOR, NOT, shifts, rotates, field extraction, and field replacement in Blu, Luau, Lua 5.2, and Lua 5.3 profiles, with explicit profile-specific input conversion and result subtypes. Legacy table.getn and table.maxn are also profile-gated to the pinned versions that retain them, while Blu exposes both for migration. The Lua 5.1 table.foreach and table.foreachi callbacks are available in the Lua 5.1 and Blu profiles; callback results short-circuit iteration, and owned callbacks can yield and resume without restarting iteration. pairs honors profile-available __pairs handlers, including resumable owned handlers. The legacy gcinfo memory counter is likewise available only in Blu, Luau, and Lua 5.1 profiles. Core tonumber conversion preserves profile subtypes, hexadecimal integer and floating strings, and the explicit-base grammar and overflow behavior of each profile. Byte-oriented string.find supports literal searches, relative starts, empty needles, nil misses, explicit plain mode, basic anchors, wildcard bytes, portable byte classes, class negation, and escaped punctuation under a work limit. The %g graph class follows modern profiles while Lua 5.1 preserves its literal escape semantics. Bracket sets support byte ranges, classes, and negation. Unimplemented Lua-pattern syntax fails structurally instead of being treated as literal text. All four Lua repetition suffixes are bounded: greedy */+/? and minimal -. string.find and string.match return bounded nested substring and position captures from the same byte-pattern engine, including %1 through %9 backreferences to completed substring captures and bounded %bxy nested byte pairs. Zero-width %f[set] byte frontiers share the bracket-set engine. string.gmatch returns a stateful function iterator with the same bounded pattern and capture behavior, including correct empty-match progress. string.gsub adds bounded string, number, direct-table, and function replacement, %0, %1%9, %%, empty-match progress, replacement counts, and profile-specific Lua 5.1 escape handling. Synchronous table-__index replacement handlers are included. Owned coroutine callbacks can now yield once per match and resume with explicit operation state and GC roots; other native library callbacks remain pending. Blu/Luau string.split produces bounded byte-string arrays with Luau-compatible default, empty-separator, consecutive-separator, and empty-field behavior. Blu/Luau table.create and table.find provide bounded preallocation/fill and raw array search with profile-typed result indices. table.clear retains allocation while removing entries, and table.clone performs a bounded shallow copy with unprotected metatable preservation. table.freeze/table.isfrozen enforce shallow immutability through every heap mutation path; clones of frozen tables remain mutable. coroutine.running and coroutine.isyieldable dispatch on the executing artifact profile, including Lua 5.1 main-thread nil and Luau main-thread yieldability. The base library exposes GC-safe collectgarbage("collect") and accounted-heap collectgarbage("count"); other version-specific commands fail explicitly. table.sort provides bounded, exact default ordering for uniform number and byte-string sequences, plus custom comparator callbacks and metamethod ordering. Owned comparators and ordering metamethods can yield and resume with explicit sort state and GC roots. Overlap-safe bounded table.move is available for Blu, Luau, and Lua 5.3–5.5, with explicit rejection in Lua 5.1–5.2 profiles. Ordinary bytecode calls use a bounded explicit VM frame stack; saved callers remain GC roots. Initial generational coroutine threads implement create/resume/yield/status/wrap/running/isyieldable/close, including nested calls, resume arguments, successful protected-call suspension, resumed pcall/xpcall error unwinding, yielding error handlers, and GC-traced continuations. Owned BluV1 coroutine entry closures also suspend, resume repeatedly, and retain their frames through collection; native library operations that invoke yielding callbacks still need operation-specific continuations. Owned sole-call returns replace the current closure frame, providing proper tail recursion independently of the configured ordinary call-depth limit. Portable V1 package envelopes provide bounded canonical decoding, SHA-256 identity, explicit dialect and authority requirements, and an opaque validated bytecode payload. The public engine currently executes only dialect-matched packages without imports when their authority profile and exact capability requirements are covered by the host policy; service linking still fails explicitly until its host bindings exist. The default legacy engine selects blu; --!dialect directives are checked against the configured engine. The public Engine::execute_owned_source entry point now exposes the bounded Blu-owned baseline for all seven profiles, including Lua 5.1–5.5, while the legacy bytecode path still rejects those profiles. This is meaningful execution coverage, not yet a claim of complete Luau, Lua, or Blu compatibility.

The first Blu-owned frontend substrate is also present: blu-syntax performs bounded byte-oriented lexing and parses the initial local/assignment-list/return arithmetic slice, including nil, boolean, shared decimal integers plus digit-bearing fraction/exponent forms (1.5, .25, 2e3, 4.5e-2), and quoted or long-bracket byte-string literals. Quoted strings implement the shared escapes plus explicit profile rules for byte, Unicode, whitespace, and line-continuation escapes; long strings use profile-specific newline handling plus semicolon separators, grouping parentheses, and profile-neutral +/-/*///%/^ plus right-associative .., into a spanned arena AST with explicit profile reconciliation. Unary not follows common Lua truthiness and produces a boolean under every profile; unary - preserves integers in Blu and Lua 5.3–5.5 and negates numbers in the other profiles. Unary # measures byte strings, returning an integer for Blu and Lua 5.3–5.5 and a number elsewhere, and executes raw or resumable metamethod-aware table length. Exponentiation follows the shared right-associative precedence above unary operators and always produces a number. Blu and Lua 5.3–5.5 source profiles support 64-bit integer &, |, binary and unary ~, <<, and >>, including Lua precedence and reversed negative shifts. Luau and Lua 5.1–5.2 reject this syntax during lexing. Blu and Luau additionally support +=, -=, *=, /=, //=, %=, ^=, and ..= statements. Indexed receivers and keys and the previous value are evaluated exactly once before the right-hand expression. Hexadecimal integers are accepted in every profile. Blu and Lua 5.3–5.5 use wrapping 64-bit integer representation; Luau and Lua 5.1–5.2 use numbers. Blu fitting decimal integers and Blu-only binary integers are likewise exact 64-bit values. Internal numeric separators are accepted only by Blu and Luau. Binary integers are also available only in Blu and Luau. Hexadecimal floats and their profile matrix are supported: exponent-only forms work in Blu and Lua 5.1–5.5, fractional forms work in Blu and Lua 5.2–5.5, and Luau rejects both. Arithmetic accepts whitespace-trimmed decimal and hexadecimal numeric strings under every profile. Blu and Lua 5.4–5.5 preserve exact parsed integers; Luau and Lua 5.1–5.3 produce numbers from string operands. Other profile-specific numeral extensions remain explicitly unsupported. Quoted strings support decimal byte escapes in every profile. Two-digit hexadecimal byte escapes are available in Blu, Luau, and Lua 5.2–5.5; malformed or out-of-range byte escapes are rejected structurally. Those same profiles support \z, which removes every following ASCII whitespace byte, including line breaks. Lua 5.1 rejects it explicitly. Every profile supports backslash line continuation; LF, CRLF, and CR source line endings normalize to one LF byte in the resulting string. Unicode escapes are byte-oriented and explicitly versioned: Blu, Luau, and Lua 5.3–5.5 accept \u{...} through 0x10ffff; Blu and Lua 5.4–5.5 additionally accept the upstream extended UTF-8 range through 0x7fffffff. Lua 5.1/5.2 reject the syntax. Trailing-dot forms such as 1. and 1.e2 are accepted; as in the pinned runtimes, 1..2 is malformed and must be spaced before future concatenation. blu_compiler::owned::OwnedCompiler resolves and lowers that slice into canonical BluV1 artifacts without native linkage or fallback; the same explicit-profile API is available from the public facade as blu_lang::frontend. Engine::execute_owned_compilation directly executes the single-prototype scalar baseline slice for every declared profile without the Luau compiler or bytecode translator. It revalidates the consumed artifact under caller-supplied execution limits. Canonical register moves preserve arbitrary scalar return lists without numeric coercion. BluV1 floor division executes with Luau number semantics and Blu/Lua 5.3–5.5 integer-preserving modern semantics. The owned path also directly executes profile-neutral ==, ~=, <, <=, >, and >=; ordered comparisons accept only compatible numbers or byte strings, while equality between unlike scalar types is false. Mixed integer/number comparisons remain exact across the full 64-bit range instead of first converting integers to f64; NaN is unequal and unordered. Operand-returning and and or use validated forward branches and preserve short-circuit evaluation in every profile. Structured if/elseif/else blocks execute through the same validated forward control flow, retain branch-local lexical scope, and support path-terminating returns. Shared do/end blocks provide explicit lexical scope without adding a control-flow branch; unreachable statements after an unconditional nested return are not emitted. Block-scoped while loops add separately feature-gated backward branches; validation records the target's definite-initialization state, and runtime execution remains subject to the VM instruction limit. Blu and Lua 5.2–5.5 also support owned ::label:: declarations and validated same-scope goto branches. Cross-scope jumps remain rejected until explicit upvalue-closing control flow is available. Embedders can also request persistent cooperative interruption from another thread through Vm::interrupt_handle or Engine::interrupt_handle; both execution engines stop with a structured error at an instruction boundary. Absolute wall-clock deadlines use the same safe boundary and remain distinct from deterministic instruction fuel. Native callbacks can query the active artifact profile and cooperatively poll both conditions through the public VM API. Coroutine task states have a separate live-count limit that collects unreachable tasks before rejecting growth. Shared break statements are structurally restricted to loop bodies and patch only the innermost loop's exit, including through nested conditional blocks. continue is an explicit Blu/Luau extension that restarts the innermost loop; Lua 5.1–5.5 profiles reject it during lexing. Profile-neutral repeat/until loops execute their body at least once and retain body locals through the trailing condition. In Blu and Luau, continue in a repeat loop transfers to that trailing condition. Shared numeric for loops snapshot their controls once, use the profile's numeric representation for the implicit positive unit step, and scope the index to the loop. Explicit, provably nonzero numeric-literal steps support both directions. Literal zero follows the pinned Lua 5.1–5.3 and Luau non-positive classification; Blu remains unassigned and Lua 5.4–5.5 reject zero explicitly. Dynamic steps execute for Luau and Lua 5.1–5.3 with single-evaluation snapshots and runtime direction selection; Blu and Lua 5.4–5.5 reject them until their possible zero case is executable. Generic for executes iterator/state/control triples in every owned profile, including bounded final-call adjustment, lexical result variables, and nil-only termination. Lua 5.4–5.5 also preserve the fourth to-be-closed control and close it on normal loop exit and break. Lua 5.4/5.5 <const> and <close> local attributes are parsed; const writes are rejected and close locals run their __close handler on normal scope exit, break, return, goto, and protected errors, including reverse-order cleanup and resumable yielding handlers. Full finalizer/GC and abandoned-coroutine semantics remain incomplete. Canonical BluV1 global loads and stores connect the owned frontend to the VM's embedding registry. Unknown scalar reads produce nil, and scalar writes persist in the VM; Lua 5.2–5.5 owned chunks additionally use a rooted default environment synchronized with that registry, while explicit _ENV tables are captured through nested closures. Identifier assignment lists can mix locals, captures, and globals while preserving simultaneous assignment. Owned Lua 5.2–5.5 source execution installs an environment-aware load: string chunks return callable closures rooted to the supplied fourth-argument table (or the default environment), and Engine::load_owned_source exposes the same closure primitive to embedders. Complete environment-rebinding APIs remain unsupported. The Lua 5.1 owned path additionally supports string loadstring plus function-targeted getfenv/setfenv; stack-level environment rebinding remains unsupported; owned load accepts bounded string-producing reader functions, while yielding readers, binary chunks, and exact mode-string behavior remain unsupported. The owned frontend also supports bounded table constructors with sequential array, identifier-keyed, and bracket-keyed fields, plus bracket and dot-name reads and single-target writes. These execute directly through the generational heap, return nil for absent keys, and retain active registers as GC roots during allocation and table growth. Mixed identifier/index/field assignment lists snapshot every target and right-hand side before committing writes. Table and method reads follow __index table chains or resumably invoke closure/native handlers; writes likewise follow __newindex chains or handlers. Binary arithmetic dispatches __add, __sub, __mul, __div, __mod, __pow, and dialect-gated __idiv through the same bounded continuation path; unary negation likewise dispatches __unm. Final-field vararg and call MULTRET expansion are implemented. Concatenation invokes left-then-right __concat handlers through a resumable continuation when string/number coercion is unavailable. Comparison continuations preserve profile-specific handler selection and Lua 5.5's removal of reversed-__lt fallback. Operator event values may themselves be bounded callable-table chains; their final Blu closures remain on the explicit continuation stack. Unary # measures raw table sequences in Lua 5.1. Other profiles resumably invoke a present table __len closure or native handler and otherwise use the raw sequence length. Bounded postfix calls evaluate the callee and fixed scalar arguments left-to-right and dispatch through the VM's existing closure/native/table-call path. Scalar contexts produce the first result or nil; a final call in a local or identifier assignment list requests the remaining bounded result count, truncating excess results and padding missing results with nil. Call statements support side-effecting APIs such as print. A final call or method call in a return statement forwards every result. Sole Blu closure calls replace the current frame; preceding fixed return values remain in a GC-rooted bounded continuation and are prepended after the call completes. Callable tables resolve bounded __call chains, prepend every table receiver, and enter Blu closure handlers through those same continuations. Final call arguments expand every result (target(prefix, producer())) through a GC-rooted continuation; non-final calls still collapse to one value, and nested forwarding preserves left-to-right evaluation. Remaining resumable callbacks stay explicit later work. Owned variadic functions support scalar and fixed-width ... reads with nil padding, dynamic return forwarding, and dynamic final call arguments, and final table-constructor expansion, including fixed prefixes and method receivers; active and suspended varargs remain GC roots. Final calls in table constructors expand every result through a GC-rooted resumable table-fill continuation for both Blu closures and native functions. The older Engine::execute source path continues to use the pinned Luau compatibility compiler while the owned grammar and executor are expanded.

Repository layout

  • blu-lang: public facade crate for embedding Blu.
  • blu-core: dependency-free semantic profiles, source identities, byte spans, and diagnostics.
  • blu-syntax: bounded byte lexer and initial parser/AST for the Blu-owned frontend.
  • blu-compiler: safe-Rust BluV1 compiler slice, with an opt-in legacy Luau compiler adapter.
  • blu-bytecode: bounded BluV1 artifacts plus versioned Luau decoding and loading.
  • blu-package: bounded canonical package envelopes and artifact validation.
  • blu-runtime: values, heap, interpreter, interruption, and Rust host API.
  • blu-conformance: differential execution against pinned Luau and Lua runtimes.
  • .upstream/luau: ignored checkout created by just upstream.

The Rust core, syntax, bytecode, package, runtime, facade, and conformance crates forbid unsafe Rust at the crate level. blu-compiler builds its owned compiler without native dependencies by default. Its opt-in legacy-luau feature contains the isolated boundary for the pinned upstream Luau C++ compiler; a noexcept shim translates native exceptions and owns allocation/deallocation across that boundary. The current blu-lang facade and conformance runner enable that compatibility feature explicitly.

Development

just upstream
just test
just conformance

See NOTICE.md for upstream attribution and UPSTREAM.toml for compatibility revisions. The intended compatibility and authority model is defined in docs/language-contract.md. The explicit profile backlog is tracked in docs/dialect-matrix.md, and the Blu-owned frontend decision is recorded in ADR 0002.

Rust applications should depend on the blu-lang crate. The bare blu name on crates.io belongs to an unrelated project.

use blu_lang::{Engine,Value};let values = Engine::default().execute("return 20 + 22").expect("valid Blu source");assert_eq!(values, vec![Value::Number(42.0)]);

Intended embedders

The core runtime is application-neutral and does not depend on Borg. It is intended for:

  • extensible terminal and desktop applications;
  • game engines and simulation tools;
  • command-line automation;
  • servers and edge runtimes;
  • build, workflow, and configuration systems;
  • editors and developer tools;
  • agent and orchestration platforms.

Application adapters such as a future blu-borg crate belong outside the core runtime and use the same embedding API available to third-party applications.

License

Blu is free and open-source software, available under the MIT License.

About

Lua / Luau superset with a fast Rust runtime

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all \u003cpre\u003e\u003ccode\u003e blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks"); } } catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); } })(); (function(){ try { var __m = "github.com"; var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Latest commit

History

200 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Blu: A language extending Lua with a fast Rust runtime

Blu is a fast, embeddable Lua/Luau superset language and runtime written in Rust. It is built for deeply extensible native applications. Its default blu dialect pragmatically unifies and extends Luau and modern Lua; explicit compatibility dialects preserve the exact semantics of each upstream language version where those semantics conflict.

Borg Agent embedding, package authority, and customization boundaries are documented in docs/borg-embedding.md. Blu owns the language runtime; the Borg host owns extension admission and capabilities.

Blu starts from Luau's optimized language and VM design, but it is not confined to Roblox's sandboxed Luau surface. Blu supports explicit Luau and Lua compatibility modes alongside a first-class systems profile with io, os, packages, filesystem, network, and process capabilities.

Blu is currently under active compatibility development. The initial milestone is a safe Luau-bytecode loader and interpreter continuously checked against a pinned upstream Luau revision. Lua 5.1–5.5 source profiles, standard libraries, and versioned C API bridges follow behind the same differential conformance gates.

The current Luau-backed Blu path compiles source in-process and covers scalar and table operations, numeric and generic loops, closures and mutable upvalues, variadic and multiple-return calls, globals and imports, Rust native functions, protected calls, pairs/ipairs/next, string method dispatch, core metamethods, an initial standard library, and host-configured cached require. The owned Blu/Luau frontend also lowers value-selecting if ... then ... elseif ... else ... expressions with short-circuit branches; Lua profiles reject that Luau syntax explicitly. The contextual _VERSION global reports Blu, Luau, or the selected Lua 5.1Lua 5.5 profile and remains explicitly overridable by the guest or embedder. The math slice includes shared truncating math.fmod; unlike floor-based %, its remainder preserves the dividend's sign, with modern integer preservation and zero-divisor errors. Shared math.pow provides the library form of floating exponentiation, while math.frexp and math.ldexp split and compose binary exponents with profile-appropriate exponent subtypes and conversion. The legacy sinh/cosh/tanh/log10/atan2 names remain available in Blu, Luau, and Lua 5.1–5.4 profiles and fail explicitly in Lua 5.5, which removed them. math.atan follows the legacy one-argument Luau/Lua 5.1–5.2 contract or modern Blu/Lua 5.3–5.5 atan2(y, x) contract according to the active profile; math.asin and math.acos follow the shared numeric contract. Profile-aware math.floor and math.ceil and the integral result of two-result math.modf preserve number-only legacy behavior or return modern exact integers when representable. math.abs preserves modern integer inputs, while math.log explicitly distinguishes Lua 5.1's ignored extra arguments from modern base selection. math.min and math.max preserve the selected modern subtype and upstream NaN ordering, using exact mixed integer/number comparisons across i64. Modern-profile math.type, math.tointeger, and math.ult provide numeric subtype introspection, exact integral conversion, and unsigned comparison. Profile-aware math.random and math.randomseed provide a deterministic, non-cryptographic generator with explicit legacy coercion, result-subtype, zero-bound, seed-arity, and seed-return contracts; seeded reproducibility is guaranteed, but upstream implementations' exact random sequences are not. Blu/Luau profiles also expose math.clamp, math.sign, and math.round with the pinned Luau edge behavior, plus numeric classification and math.lerp/math.map interpolation helpers and deterministic math.noise. The bit32 library provides variadic AND/OR/XOR, NOT, shifts, rotates, field extraction, and field replacement in Blu, Luau, Lua 5.2, and Lua 5.3 profiles, with explicit profile-specific input conversion and result subtypes. Legacy table.getn and table.maxn are also profile-gated to the pinned versions that retain them, while Blu exposes both for migration. The Lua 5.1 table.foreach and table.foreachi callbacks are available in the Lua 5.1 and Blu profiles; callback results short-circuit iteration, and owned callbacks can yield and resume without restarting iteration. pairs honors profile-available __pairs handlers, including resumable owned handlers. The legacy gcinfo memory counter is likewise available only in Blu, Luau, and Lua 5.1 profiles. Core tonumber conversion preserves profile subtypes, hexadecimal integer and floating strings, and the explicit-base grammar and overflow behavior of each profile. Byte-oriented string.find supports literal searches, relative starts, empty needles, nil misses, explicit plain mode, basic anchors, wildcard bytes, portable byte classes, class negation, and escaped punctuation under a work limit. The %g graph class follows modern profiles while Lua 5.1 preserves its literal escape semantics. Bracket sets support byte ranges, classes, and negation. Unimplemented Lua-pattern syntax fails structurally instead of being treated as literal text. All four Lua repetition suffixes are bounded: greedy */+/? and minimal -. string.find and string.match return bounded nested substring and position captures from the same byte-pattern engine, including %1 through %9 backreferences to completed substring captures and bounded %bxy nested byte pairs. Zero-width %f[set] byte frontiers share the bracket-set engine. string.gmatch returns a stateful function iterator with the same bounded pattern and capture behavior, including correct empty-match progress. string.gsub adds bounded string, number, direct-table, and function replacement, %0, %1%9, %%, empty-match progress, replacement counts, and profile-specific Lua 5.1 escape handling. Synchronous table-__index replacement handlers are included. Owned coroutine callbacks can now yield once per match and resume with explicit operation state and GC roots; other native library callbacks remain pending. Blu/Luau string.split produces bounded byte-string arrays with Luau-compatible default, empty-separator, consecutive-separator, and empty-field behavior. Blu/Luau table.create and table.find provide bounded preallocation/fill and raw array search with profile-typed result indices. table.clear retains allocation while removing entries, and table.clone performs a bounded shallow copy with unprotected metatable preservation. table.freeze/table.isfrozen enforce shallow immutability through every heap mutation path; clones of frozen tables remain mutable. coroutine.running and coroutine.isyieldable dispatch on the executing artifact profile, including Lua 5.1 main-thread nil and Luau main-thread yieldability. The base library exposes GC-safe collectgarbage("collect") and accounted-heap collectgarbage("count"); other version-specific commands fail explicitly. table.sort provides bounded, exact default ordering for uniform number and byte-string sequences, plus custom comparator callbacks and metamethod ordering. Owned comparators and ordering metamethods can yield and resume with explicit sort state and GC roots. Overlap-safe bounded table.move is available for Blu, Luau, and Lua 5.3–5.5, with explicit rejection in Lua 5.1–5.2 profiles. Ordinary bytecode calls use a bounded explicit VM frame stack; saved callers remain GC roots. Initial generational coroutine threads implement create/resume/yield/status/wrap/running/isyieldable/close, including nested calls, resume arguments, successful protected-call suspension, resumed pcall/xpcall error unwinding, yielding error handlers, and GC-traced continuations. Owned BluV1 coroutine entry closures also suspend, resume repeatedly, and retain their frames through collection; native library operations that invoke yielding callbacks still need operation-specific continuations. Owned sole-call returns replace the current closure frame, providing proper tail recursion independently of the configured ordinary call-depth limit. Portable V1 package envelopes provide bounded canonical decoding, SHA-256 identity, explicit dialect and authority requirements, and an opaque validated bytecode payload. The public engine currently executes only dialect-matched packages without imports when their authority profile and exact capability requirements are covered by the host policy; service linking still fails explicitly until its host bindings exist. The default legacy engine selects blu; --!dialect directives are checked against the configured engine. The public Engine::execute_owned_source entry point now exposes the bounded Blu-owned baseline for all seven profiles, including Lua 5.1–5.5, while the legacy bytecode path still rejects those profiles. This is meaningful execution coverage, not yet a claim of complete Luau, Lua, or Blu compatibility.

The first Blu-owned frontend substrate is also present: blu-syntax performs bounded byte-oriented lexing and parses the initial local/assignment-list/return arithmetic slice, including nil, boolean, shared decimal integers plus digit-bearing fraction/exponent forms (1.5, .25, 2e3, 4.5e-2), and quoted or long-bracket byte-string literals. Quoted strings implement the shared escapes plus explicit profile rules for byte, Unicode, whitespace, and line-continuation escapes; long strings use profile-specific newline handling plus semicolon separators, grouping parentheses, and profile-neutral +/-/*///%/^ plus right-associative .., into a spanned arena AST with explicit profile reconciliation. Unary not follows common Lua truthiness and produces a boolean under every profile; unary - preserves integers in Blu and Lua 5.3–5.5 and negates numbers in the other profiles. Unary # measures byte strings, returning an integer for Blu and Lua 5.3–5.5 and a number elsewhere, and executes raw or resumable metamethod-aware table length. Exponentiation follows the shared right-associative precedence above unary operators and always produces a number. Blu and Lua 5.3–5.5 source profiles support 64-bit integer &, |, binary and unary ~, <<, and >>, including Lua precedence and reversed negative shifts. Luau and Lua 5.1–5.2 reject this syntax during lexing. Blu and Luau additionally support +=, -=, *=, /=, //=, %=, ^=, and ..= statements. Indexed receivers and keys and the previous value are evaluated exactly once before the right-hand expression. Hexadecimal integers are accepted in every profile. Blu and Lua 5.3–5.5 use wrapping 64-bit integer representation; Luau and Lua 5.1–5.2 use numbers. Blu fitting decimal integers and Blu-only binary integers are likewise exact 64-bit values. Internal numeric separators are accepted only by Blu and Luau. Binary integers are also available only in Blu and Luau. Hexadecimal floats and their profile matrix are supported: exponent-only forms work in Blu and Lua 5.1–5.5, fractional forms work in Blu and Lua 5.2–5.5, and Luau rejects both. Arithmetic accepts whitespace-trimmed decimal and hexadecimal numeric strings under every profile. Blu and Lua 5.4–5.5 preserve exact parsed integers; Luau and Lua 5.1–5.3 produce numbers from string operands. Other profile-specific numeral extensions remain explicitly unsupported. Quoted strings support decimal byte escapes in every profile. Two-digit hexadecimal byte escapes are available in Blu, Luau, and Lua 5.2–5.5; malformed or out-of-range byte escapes are rejected structurally. Those same profiles support \z, which removes every following ASCII whitespace byte, including line breaks. Lua 5.1 rejects it explicitly. Every profile supports backslash line continuation; LF, CRLF, and CR source line endings normalize to one LF byte in the resulting string. Unicode escapes are byte-oriented and explicitly versioned: Blu, Luau, and Lua 5.3–5.5 accept \u{...} through 0x10ffff; Blu and Lua 5.4–5.5 additionally accept the upstream extended UTF-8 range through 0x7fffffff. Lua 5.1/5.2 reject the syntax. Trailing-dot forms such as 1. and 1.e2 are accepted; as in the pinned runtimes, 1..2 is malformed and must be spaced before future concatenation. blu_compiler::owned::OwnedCompiler resolves and lowers that slice into canonical BluV1 artifacts without native linkage or fallback; the same explicit-profile API is available from the public facade as blu_lang::frontend. Engine::execute_owned_compilation directly executes the single-prototype scalar baseline slice for every declared profile without the Luau compiler or bytecode translator. It revalidates the consumed artifact under caller-supplied execution limits. Canonical register moves preserve arbitrary scalar return lists without numeric coercion. BluV1 floor division executes with Luau number semantics and Blu/Lua 5.3–5.5 integer-preserving modern semantics. The owned path also directly executes profile-neutral ==, ~=, <, <=, >, and >=; ordered comparisons accept only compatible numbers or byte strings, while equality between unlike scalar types is false. Mixed integer/number comparisons remain exact across the full 64-bit range instead of first converting integers to f64; NaN is unequal and unordered. Operand-returning and and or use validated forward branches and preserve short-circuit evaluation in every profile. Structured if/elseif/else blocks execute through the same validated forward control flow, retain branch-local lexical scope, and support path-terminating returns. Shared do/end blocks provide explicit lexical scope without adding a control-flow branch; unreachable statements after an unconditional nested return are not emitted. Block-scoped while loops add separately feature-gated backward branches; validation records the target's definite-initialization state, and runtime execution remains subject to the VM instruction limit. Blu and Lua 5.2–5.5 also support owned ::label:: declarations and validated same-scope goto branches. Cross-scope jumps remain rejected until explicit upvalue-closing control flow is available. Embedders can also request persistent cooperative interruption from another thread through Vm::interrupt_handle or Engine::interrupt_handle; both execution engines stop with a structured error at an instruction boundary. Absolute wall-clock deadlines use the same safe boundary and remain distinct from deterministic instruction fuel. Native callbacks can query the active artifact profile and cooperatively poll both conditions through the public VM API. Coroutine task states have a separate live-count limit that collects unreachable tasks before rejecting growth. Shared break statements are structurally restricted to loop bodies and patch only the innermost loop's exit, including through nested conditional blocks. continue is an explicit Blu/Luau extension that restarts the innermost loop; Lua 5.1–5.5 profiles reject it during lexing. Profile-neutral repeat/until loops execute their body at least once and retain body locals through the trailing condition. In Blu and Luau, continue in a repeat loop transfers to that trailing condition. Shared numeric for loops snapshot their controls once, use the profile's numeric representation for the implicit positive unit step, and scope the index to the loop. Explicit, provably nonzero numeric-literal steps support both directions. Literal zero follows the pinned Lua 5.1–5.3 and Luau non-positive classification; Blu remains unassigned and Lua 5.4–5.5 reject zero explicitly. Dynamic steps execute for Luau and Lua 5.1–5.3 with single-evaluation snapshots and runtime direction selection; Blu and Lua 5.4–5.5 reject them until their possible zero case is executable. Generic for executes iterator/state/control triples in every owned profile, including bounded final-call adjustment, lexical result variables, and nil-only termination. Lua 5.4–5.5 also preserve the fourth to-be-closed control and close it on normal loop exit and break. Lua 5.4/5.5 <const> and <close> local attributes are parsed; const writes are rejected and close locals run their __close handler on normal scope exit, break, return, goto, and protected errors, including reverse-order cleanup and resumable yielding handlers. Full finalizer/GC and abandoned-coroutine semantics remain incomplete. Canonical BluV1 global loads and stores connect the owned frontend to the VM's embedding registry. Unknown scalar reads produce nil, and scalar writes persist in the VM; Lua 5.2–5.5 owned chunks additionally use a rooted default environment synchronized with that registry, while explicit _ENV tables are captured through nested closures. Identifier assignment lists can mix locals, captures, and globals while preserving simultaneous assignment. Owned Lua 5.2–5.5 source execution installs an environment-aware load: string chunks return callable closures rooted to the supplied fourth-argument table (or the default environment), and Engine::load_owned_source exposes the same closure primitive to embedders. Complete environment-rebinding APIs remain unsupported. The Lua 5.1 owned path additionally supports string loadstring plus function-targeted getfenv/setfenv; stack-level environment rebinding remains unsupported; owned load accepts bounded string-producing reader functions, while yielding readers, binary chunks, and exact mode-string behavior remain unsupported. The owned frontend also supports bounded table constructors with sequential array, identifier-keyed, and bracket-keyed fields, plus bracket and dot-name reads and single-target writes. These execute directly through the generational heap, return nil for absent keys, and retain active registers as GC roots during allocation and table growth. Mixed identifier/index/field assignment lists snapshot every target and right-hand side before committing writes. Table and method reads follow __index table chains or resumably invoke closure/native handlers; writes likewise follow __newindex chains or handlers. Binary arithmetic dispatches __add, __sub, __mul, __div, __mod, __pow, and dialect-gated __idiv through the same bounded continuation path; unary negation likewise dispatches __unm. Final-field vararg and call MULTRET expansion are implemented. Concatenation invokes left-then-right __concat handlers through a resumable continuation when string/number coercion is unavailable. Comparison continuations preserve profile-specific handler selection and Lua 5.5's removal of reversed-__lt fallback. Operator event values may themselves be bounded callable-table chains; their final Blu closures remain on the explicit continuation stack. Unary # measures raw table sequences in Lua 5.1. Other profiles resumably invoke a present table __len closure or native handler and otherwise use the raw sequence length. Bounded postfix calls evaluate the callee and fixed scalar arguments left-to-right and dispatch through the VM's existing closure/native/table-call path. Scalar contexts produce the first result or nil; a final call in a local or identifier assignment list requests the remaining bounded result count, truncating excess results and padding missing results with nil. Call statements support side-effecting APIs such as print. A final call or method call in a return statement forwards every result. Sole Blu closure calls replace the current frame; preceding fixed return values remain in a GC-rooted bounded continuation and are prepended after the call completes. Callable tables resolve bounded __call chains, prepend every table receiver, and enter Blu closure handlers through those same continuations. Final call arguments expand every result (target(prefix, producer())) through a GC-rooted continuation; non-final calls still collapse to one value, and nested forwarding preserves left-to-right evaluation. Remaining resumable callbacks stay explicit later work. Owned variadic functions support scalar and fixed-width ... reads with nil padding, dynamic return forwarding, and dynamic final call arguments, and final table-constructor expansion, including fixed prefixes and method receivers; active and suspended varargs remain GC roots. Final calls in table constructors expand every result through a GC-rooted resumable table-fill continuation for both Blu closures and native functions. The older Engine::execute source path continues to use the pinned Luau compatibility compiler while the owned grammar and executor are expanded.

Repository layout

  • blu-lang: public facade crate for embedding Blu.
  • blu-core: dependency-free semantic profiles, source identities, byte spans, and diagnostics.
  • blu-syntax: bounded byte lexer and initial parser/AST for the Blu-owned frontend.
  • blu-compiler: safe-Rust BluV1 compiler slice, with an opt-in legacy Luau compiler adapter.
  • blu-bytecode: bounded BluV1 artifacts plus versioned Luau decoding and loading.
  • blu-package: bounded canonical package envelopes and artifact validation.
  • blu-runtime: values, heap, interpreter, interruption, and Rust host API.
  • blu-conformance: differential execution against pinned Luau and Lua runtimes.
  • .upstream/luau: ignored checkout created by just upstream.

The Rust core, syntax, bytecode, package, runtime, facade, and conformance crates forbid unsafe Rust at the crate level. blu-compiler builds its owned compiler without native dependencies by default. Its opt-in legacy-luau feature contains the isolated boundary for the pinned upstream Luau C++ compiler; a noexcept shim translates native exceptions and owns allocation/deallocation across that boundary. The current blu-lang facade and conformance runner enable that compatibility feature explicitly.

Development

just upstream
just test
just conformance

See NOTICE.md for upstream attribution and UPSTREAM.toml for compatibility revisions. The intended compatibility and authority model is defined in docs/language-contract.md. The explicit profile backlog is tracked in docs/dialect-matrix.md, and the Blu-owned frontend decision is recorded in ADR 0002.

Rust applications should depend on the blu-lang crate. The bare blu name on crates.io belongs to an unrelated project.

use blu_lang::{Engine,Value};let values = Engine::default().execute("return 20 + 22").expect("valid Blu source");assert_eq!(values, vec![Value::Number(42.0)]);

Intended embedders

The core runtime is application-neutral and does not depend on Borg. It is intended for:

  • extensible terminal and desktop applications;
  • game engines and simulation tools;
  • command-line automation;
  • servers and edge runtimes;
  • build, workflow, and configuration systems;
  • editors and developer tools;
  • agent and orchestration platforms.

Application adapters such as a future blu-borg crate belong outside the core runtime and use the same embedding API available to third-party applications.

License

Blu is free and open-source software, available under the MIT License.

About

Lua / Luau superset with a fast Rust runtime

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

200 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Blu: A language extending Lua with a fast Rust runtime

Blu is a fast, embeddable Lua/Luau superset language and runtime written in Rust. It is built for deeply extensible native applications. Its default blu dialect pragmatically unifies and extends Luau and modern Lua; explicit compatibility dialects preserve the exact semantics of each upstream language version where those semantics conflict.

Borg Agent embedding, package authority, and customization boundaries are documented in docs/borg-embedding.md. Blu owns the language runtime; the Borg host owns extension admission and capabilities.

Blu starts from Luau's optimized language and VM design, but it is not confined to Roblox's sandboxed Luau surface. Blu supports explicit Luau and Lua compatibility modes alongside a first-class systems profile with io, os, packages, filesystem, network, and process capabilities.

Blu is currently under active compatibility development. The initial milestone is a safe Luau-bytecode loader and interpreter continuously checked against a pinned upstream Luau revision. Lua 5.1–5.5 source profiles, standard libraries, and versioned C API bridges follow behind the same differential conformance gates.

The current Luau-backed Blu path compiles source in-process and covers scalar and table operations, numeric and generic loops, closures and mutable upvalues, variadic and multiple-return calls, globals and imports, Rust native functions, protected calls, pairs/ipairs/next, string method dispatch, core metamethods, an initial standard library, and host-configured cached require. The owned Blu/Luau frontend also lowers value-selecting if ... then ... elseif ... else ... expressions with short-circuit branches; Lua profiles reject that Luau syntax explicitly. The contextual _VERSION global reports Blu, Luau, or the selected Lua 5.1Lua 5.5 profile and remains explicitly overridable by the guest or embedder. The math slice includes shared truncating math.fmod; unlike floor-based %, its remainder preserves the dividend's sign, with modern integer preservation and zero-divisor errors. Shared math.pow provides the library form of floating exponentiation, while math.frexp and math.ldexp split and compose binary exponents with profile-appropriate exponent subtypes and conversion. The legacy sinh/cosh/tanh/log10/atan2 names remain available in Blu, Luau, and Lua 5.1–5.4 profiles and fail explicitly in Lua 5.5, which removed them. math.atan follows the legacy one-argument Luau/Lua 5.1–5.2 contract or modern Blu/Lua 5.3–5.5 atan2(y, x) contract according to the active profile; math.asin and math.acos follow the shared numeric contract. Profile-aware math.floor and math.ceil and the integral result of two-result math.modf preserve number-only legacy behavior or return modern exact integers when representable. math.abs preserves modern integer inputs, while math.log explicitly distinguishes Lua 5.1's ignored extra arguments from modern base selection. math.min and math.max preserve the selected modern subtype and upstream NaN ordering, using exact mixed integer/number comparisons across i64. Modern-profile math.type, math.tointeger, and math.ult provide numeric subtype introspection, exact integral conversion, and unsigned comparison. Profile-aware math.random and math.randomseed provide a deterministic, non-cryptographic generator with explicit legacy coercion, result-subtype, zero-bound, seed-arity, and seed-return contracts; seeded reproducibility is guaranteed, but upstream implementations' exact random sequences are not. Blu/Luau profiles also expose math.clamp, math.sign, and math.round with the pinned Luau edge behavior, plus numeric classification and math.lerp/math.map interpolation helpers and deterministic math.noise. The bit32 library provides variadic AND/OR/XOR, NOT, shifts, rotates, field extraction, and field replacement in Blu, Luau, Lua 5.2, and Lua 5.3 profiles, with explicit profile-specific input conversion and result subtypes. Legacy table.getn and table.maxn are also profile-gated to the pinned versions that retain them, while Blu exposes both for migration. The Lua 5.1 table.foreach and table.foreachi callbacks are available in the Lua 5.1 and Blu profiles; callback results short-circuit iteration, and owned callbacks can yield and resume without restarting iteration. pairs honors profile-available __pairs handlers, including resumable owned handlers. The legacy gcinfo memory counter is likewise available only in Blu, Luau, and Lua 5.1 profiles. Core tonumber conversion preserves profile subtypes, hexadecimal integer and floating strings, and the explicit-base grammar and overflow behavior of each profile. Byte-oriented string.find supports literal searches, relative starts, empty needles, nil misses, explicit plain mode, basic anchors, wildcard bytes, portable byte classes, class negation, and escaped punctuation under a work limit. The %g graph class follows modern profiles while Lua 5.1 preserves its literal escape semantics. Bracket sets support byte ranges, classes, and negation. Unimplemented Lua-pattern syntax fails structurally instead of being treated as literal text. All four Lua repetition suffixes are bounded: greedy */+/? and minimal -. string.find and string.match return bounded nested substring and position captures from the same byte-pattern engine, including %1 through %9 backreferences to completed substring captures and bounded %bxy nested byte pairs. Zero-width %f[set] byte frontiers share the bracket-set engine. string.gmatch returns a stateful function iterator with the same bounded pattern and capture behavior, including correct empty-match progress. string.gsub adds bounded string, number, direct-table, and function replacement, %0, %1%9, %%, empty-match progress, replacement counts, and profile-specific Lua 5.1 escape handling. Synchronous table-__index replacement handlers are included. Owned coroutine callbacks can now yield once per match and resume with explicit operation state and GC roots; other native library callbacks remain pending. Blu/Luau string.split produces bounded byte-string arrays with Luau-compatible default, empty-separator, consecutive-separator, and empty-field behavior. Blu/Luau table.create and table.find provide bounded preallocation/fill and raw array search with profile-typed result indices. table.clear retains allocation while removing entries, and table.clone performs a bounded shallow copy with unprotected metatable preservation. table.freeze/table.isfrozen enforce shallow immutability through every heap mutation path; clones of frozen tables remain mutable. coroutine.running and coroutine.isyieldable dispatch on the executing artifact profile, including Lua 5.1 main-thread nil and Luau main-thread yieldability. The base library exposes GC-safe collectgarbage("collect") and accounted-heap collectgarbage("count"); other version-specific commands fail explicitly. table.sort provides bounded, exact default ordering for uniform number and byte-string sequences, plus custom comparator callbacks and metamethod ordering. Owned comparators and ordering metamethods can yield and resume with explicit sort state and GC roots. Overlap-safe bounded table.move is available for Blu, Luau, and Lua 5.3–5.5, with explicit rejection in Lua 5.1–5.2 profiles. Ordinary bytecode calls use a bounded explicit VM frame stack; saved callers remain GC roots. Initial generational coroutine threads implement create/resume/yield/status/wrap/running/isyieldable/close, including nested calls, resume arguments, successful protected-call suspension, resumed pcall/xpcall error unwinding, yielding error handlers, and GC-traced continuations. Owned BluV1 coroutine entry closures also suspend, resume repeatedly, and retain their frames through collection; native library operations that invoke yielding callbacks still need operation-specific continuations. Owned sole-call returns replace the current closure frame, providing proper tail recursion independently of the configured ordinary call-depth limit. Portable V1 package envelopes provide bounded canonical decoding, SHA-256 identity, explicit dialect and authority requirements, and an opaque validated bytecode payload. The public engine currently executes only dialect-matched packages without imports when their authority profile and exact capability requirements are covered by the host policy; service linking still fails explicitly until its host bindings exist. The default legacy engine selects blu; --!dialect directives are checked against the configured engine. The public Engine::execute_owned_source entry point now exposes the bounded Blu-owned baseline for all seven profiles, including Lua 5.1–5.5, while the legacy bytecode path still rejects those profiles. This is meaningful execution coverage, not yet a claim of complete Luau, Lua, or Blu compatibility.

The first Blu-owned frontend substrate is also present: blu-syntax performs bounded byte-oriented lexing and parses the initial local/assignment-list/return arithmetic slice, including nil, boolean, shared decimal integers plus digit-bearing fraction/exponent forms (1.5, .25, 2e3, 4.5e-2), and quoted or long-bracket byte-string literals. Quoted strings implement the shared escapes plus explicit profile rules for byte, Unicode, whitespace, and line-continuation escapes; long strings use profile-specific newline handling plus semicolon separators, grouping parentheses, and profile-neutral +/-/*///%/^ plus right-associative .., into a spanned arena AST with explicit profile reconciliation. Unary not follows common Lua truthiness and produces a boolean under every profile; unary - preserves integers in Blu and Lua 5.3–5.5 and negates numbers in the other profiles. Unary # measures byte strings, returning an integer for Blu and Lua 5.3–5.5 and a number elsewhere, and executes raw or resumable metamethod-aware table length. Exponentiation follows the shared right-associative precedence above unary operators and always produces a number. Blu and Lua 5.3–5.5 source profiles support 64-bit integer &, |, binary and unary ~, <<, and >>, including Lua precedence and reversed negative shifts. Luau and Lua 5.1–5.2 reject this syntax during lexing. Blu and Luau additionally support +=, -=, *=, /=, //=, %=, ^=, and ..= statements. Indexed receivers and keys and the previous value are evaluated exactly once before the right-hand expression. Hexadecimal integers are accepted in every profile. Blu and Lua 5.3–5.5 use wrapping 64-bit integer representation; Luau and Lua 5.1–5.2 use numbers. Blu fitting decimal integers and Blu-only binary integers are likewise exact 64-bit values. Internal numeric separators are accepted only by Blu and Luau. Binary integers are also available only in Blu and Luau. Hexadecimal floats and their profile matrix are supported: exponent-only forms work in Blu and Lua 5.1–5.5, fractional forms work in Blu and Lua 5.2–5.5, and Luau rejects both. Arithmetic accepts whitespace-trimmed decimal and hexadecimal numeric strings under every profile. Blu and Lua 5.4–5.5 preserve exact parsed integers; Luau and Lua 5.1–5.3 produce numbers from string operands. Other profile-specific numeral extensions remain explicitly unsupported. Quoted strings support decimal byte escapes in every profile. Two-digit hexadecimal byte escapes are available in Blu, Luau, and Lua 5.2–5.5; malformed or out-of-range byte escapes are rejected structurally. Those same profiles support \z, which removes every following ASCII whitespace byte, including line breaks. Lua 5.1 rejects it explicitly. Every profile supports backslash line continuation; LF, CRLF, and CR source line endings normalize to one LF byte in the resulting string. Unicode escapes are byte-oriented and explicitly versioned: Blu, Luau, and Lua 5.3–5.5 accept \u{...} through 0x10ffff; Blu and Lua 5.4–5.5 additionally accept the upstream extended UTF-8 range through 0x7fffffff. Lua 5.1/5.2 reject the syntax. Trailing-dot forms such as 1. and 1.e2 are accepted; as in the pinned runtimes, 1..2 is malformed and must be spaced before future concatenation. blu_compiler::owned::OwnedCompiler resolves and lowers that slice into canonical BluV1 artifacts without native linkage or fallback; the same explicit-profile API is available from the public facade as blu_lang::frontend. Engine::execute_owned_compilation directly executes the single-prototype scalar baseline slice for every declared profile without the Luau compiler or bytecode translator. It revalidates the consumed artifact under caller-supplied execution limits. Canonical register moves preserve arbitrary scalar return lists without numeric coercion. BluV1 floor division executes with Luau number semantics and Blu/Lua 5.3–5.5 integer-preserving modern semantics. The owned path also directly executes profile-neutral ==, ~=, <, <=, >, and >=; ordered comparisons accept only compatible numbers or byte strings, while equality between unlike scalar types is false. Mixed integer/number comparisons remain exact across the full 64-bit range instead of first converting integers to f64; NaN is unequal and unordered. Operand-returning and and or use validated forward branches and preserve short-circuit evaluation in every profile. Structured if/elseif/else blocks execute through the same validated forward control flow, retain branch-local lexical scope, and support path-terminating returns. Shared do/end blocks provide explicit lexical scope without adding a control-flow branch; unreachable statements after an unconditional nested return are not emitted. Block-scoped while loops add separately feature-gated backward branches; validation records the target's definite-initialization state, and runtime execution remains subject to the VM instruction limit. Blu and Lua 5.2–5.5 also support owned ::label:: declarations and validated same-scope goto branches. Cross-scope jumps remain rejected until explicit upvalue-closing control flow is available. Embedders can also request persistent cooperative interruption from another thread through Vm::interrupt_handle or Engine::interrupt_handle; both execution engines stop with a structured error at an instruction boundary. Absolute wall-clock deadlines use the same safe boundary and remain distinct from deterministic instruction fuel. Native callbacks can query the active artifact profile and cooperatively poll both conditions through the public VM API. Coroutine task states have a separate live-count limit that collects unreachable tasks before rejecting growth. Shared break statements are structurally restricted to loop bodies and patch only the innermost loop's exit, including through nested conditional blocks. continue is an explicit Blu/Luau extension that restarts the innermost loop; Lua 5.1–5.5 profiles reject it during lexing. Profile-neutral repeat/until loops execute their body at least once and retain body locals through the trailing condition. In Blu and Luau, continue in a repeat loop transfers to that trailing condition. Shared numeric for loops snapshot their controls once, use the profile's numeric representation for the implicit positive unit step, and scope the index to the loop. Explicit, provably nonzero numeric-literal steps support both directions. Literal zero follows the pinned Lua 5.1–5.3 and Luau non-positive classification; Blu remains unassigned and Lua 5.4–5.5 reject zero explicitly. Dynamic steps execute for Luau and Lua 5.1–5.3 with single-evaluation snapshots and runtime direction selection; Blu and Lua 5.4–5.5 reject them until their possible zero case is executable. Generic for executes iterator/state/control triples in every owned profile, including bounded final-call adjustment, lexical result variables, and nil-only termination. Lua 5.4–5.5 also preserve the fourth to-be-closed control and close it on normal loop exit and break. Lua 5.4/5.5 <const> and <close> local attributes are parsed; const writes are rejected and close locals run their __close handler on normal scope exit, break, return, goto, and protected errors, including reverse-order cleanup and resumable yielding handlers. Full finalizer/GC and abandoned-coroutine semantics remain incomplete. Canonical BluV1 global loads and stores connect the owned frontend to the VM's embedding registry. Unknown scalar reads produce nil, and scalar writes persist in the VM; Lua 5.2–5.5 owned chunks additionally use a rooted default environment synchronized with that registry, while explicit _ENV tables are captured through nested closures. Identifier assignment lists can mix locals, captures, and globals while preserving simultaneous assignment. Owned Lua 5.2–5.5 source execution installs an environment-aware load: string chunks return callable closures rooted to the supplied fourth-argument table (or the default environment), and Engine::load_owned_source exposes the same closure primitive to embedders. Complete environment-rebinding APIs remain unsupported. The Lua 5.1 owned path additionally supports string loadstring plus function-targeted getfenv/setfenv; stack-level environment rebinding remains unsupported; owned load accepts bounded string-producing reader functions, while yielding readers, binary chunks, and exact mode-string behavior remain unsupported. The owned frontend also supports bounded table constructors with sequential array, identifier-keyed, and bracket-keyed fields, plus bracket and dot-name reads and single-target writes. These execute directly through the generational heap, return nil for absent keys, and retain active registers as GC roots during allocation and table growth. Mixed identifier/index/field assignment lists snapshot every target and right-hand side before committing writes. Table and method reads follow __index table chains or resumably invoke closure/native handlers; writes likewise follow __newindex chains or handlers. Binary arithmetic dispatches __add, __sub, __mul, __div, __mod, __pow, and dialect-gated __idiv through the same bounded continuation path; unary negation likewise dispatches __unm. Final-field vararg and call MULTRET expansion are implemented. Concatenation invokes left-then-right __concat handlers through a resumable continuation when string/number coercion is unavailable. Comparison continuations preserve profile-specific handler selection and Lua 5.5's removal of reversed-__lt fallback. Operator event values may themselves be bounded callable-table chains; their final Blu closures remain on the explicit continuation stack. Unary # measures raw table sequences in Lua 5.1. Other profiles resumably invoke a present table __len closure or native handler and otherwise use the raw sequence length. Bounded postfix calls evaluate the callee and fixed scalar arguments left-to-right and dispatch through the VM's existing closure/native/table-call path. Scalar contexts produce the first result or nil; a final call in a local or identifier assignment list requests the remaining bounded result count, truncating excess results and padding missing results with nil. Call statements support side-effecting APIs such as print. A final call or method call in a return statement forwards every result. Sole Blu closure calls replace the current frame; preceding fixed return values remain in a GC-rooted bounded continuation and are prepended after the call completes. Callable tables resolve bounded __call chains, prepend every table receiver, and enter Blu closure handlers through those same continuations. Final call arguments expand every result (target(prefix, producer())) through a GC-rooted continuation; non-final calls still collapse to one value, and nested forwarding preserves left-to-right evaluation. Remaining resumable callbacks stay explicit later work. Owned variadic functions support scalar and fixed-width ... reads with nil padding, dynamic return forwarding, and dynamic final call arguments, and final table-constructor expansion, including fixed prefixes and method receivers; active and suspended varargs remain GC roots. Final calls in table constructors expand every result through a GC-rooted resumable table-fill continuation for both Blu closures and native functions. The older Engine::execute source path continues to use the pinned Luau compatibility compiler while the owned grammar and executor are expanded.

Repository layout

  • blu-lang: public facade crate for embedding Blu.
  • blu-core: dependency-free semantic profiles, source identities, byte spans, and diagnostics.
  • blu-syntax: bounded byte lexer and initial parser/AST for the Blu-owned frontend.
  • blu-compiler: safe-Rust BluV1 compiler slice, with an opt-in legacy Luau compiler adapter.
  • blu-bytecode: bounded BluV1 artifacts plus versioned Luau decoding and loading.
  • blu-package: bounded canonical package envelopes and artifact validation.
  • blu-runtime: values, heap, interpreter, interruption, and Rust host API.
  • blu-conformance: differential execution against pinned Luau and Lua runtimes.
  • .upstream/luau: ignored checkout created by just upstream.

The Rust core, syntax, bytecode, package, runtime, facade, and conformance crates forbid unsafe Rust at the crate level. blu-compiler builds its owned compiler without native dependencies by default. Its opt-in legacy-luau feature contains the isolated boundary for the pinned upstream Luau C++ compiler; a noexcept shim translates native exceptions and owns allocation/deallocation across that boundary. The current blu-lang facade and conformance runner enable that compatibility feature explicitly.

Development

just upstream
just test
just conformance

See NOTICE.md for upstream attribution and UPSTREAM.toml for compatibility revisions. The intended compatibility and authority model is defined in docs/language-contract.md. The explicit profile backlog is tracked in docs/dialect-matrix.md, and the Blu-owned frontend decision is recorded in ADR 0002.

Rust applications should depend on the blu-lang crate. The bare blu name on crates.io belongs to an unrelated project.

use blu_lang::{Engine,Value};let values = Engine::default().execute("return 20 + 22").expect("valid Blu source");assert_eq!(values, vec![Value::Number(42.0)]);

Intended embedders

The core runtime is application-neutral and does not depend on Borg. It is intended for:

  • extensible terminal and desktop applications;
  • game engines and simulation tools;
  • command-line automation;
  • servers and edge runtimes;
  • build, workflow, and configuration systems;
  • editors and developer tools;
  • agent and orchestration platforms.

Application adapters such as a future blu-borg crate belong outside the core runtime and use the same embedding API available to third-party applications.

License

Blu is free and open-source software, available under the MIT License.

About

Lua / Luau superset with a fast Rust runtime

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length \u003e 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

200 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Blu: A language extending Lua with a fast Rust runtime

Blu is a fast, embeddable Lua/Luau superset language and runtime written in Rust. It is built for deeply extensible native applications. Its default blu dialect pragmatically unifies and extends Luau and modern Lua; explicit compatibility dialects preserve the exact semantics of each upstream language version where those semantics conflict.

Borg Agent embedding, package authority, and customization boundaries are documented in docs/borg-embedding.md. Blu owns the language runtime; the Borg host owns extension admission and capabilities.

Blu starts from Luau's optimized language and VM design, but it is not confined to Roblox's sandboxed Luau surface. Blu supports explicit Luau and Lua compatibility modes alongside a first-class systems profile with io, os, packages, filesystem, network, and process capabilities.

Blu is currently under active compatibility development. The initial milestone is a safe Luau-bytecode loader and interpreter continuously checked against a pinned upstream Luau revision. Lua 5.1–5.5 source profiles, standard libraries, and versioned C API bridges follow behind the same differential conformance gates.

The current Luau-backed Blu path compiles source in-process and covers scalar and table operations, numeric and generic loops, closures and mutable upvalues, variadic and multiple-return calls, globals and imports, Rust native functions, protected calls, pairs/ipairs/next, string method dispatch, core metamethods, an initial standard library, and host-configured cached require. The owned Blu/Luau frontend also lowers value-selecting if ... then ... elseif ... else ... expressions with short-circuit branches; Lua profiles reject that Luau syntax explicitly. The contextual _VERSION global reports Blu, Luau, or the selected Lua 5.1Lua 5.5 profile and remains explicitly overridable by the guest or embedder. The math slice includes shared truncating math.fmod; unlike floor-based %, its remainder preserves the dividend's sign, with modern integer preservation and zero-divisor errors. Shared math.pow provides the library form of floating exponentiation, while math.frexp and math.ldexp split and compose binary exponents with profile-appropriate exponent subtypes and conversion. The legacy sinh/cosh/tanh/log10/atan2 names remain available in Blu, Luau, and Lua 5.1–5.4 profiles and fail explicitly in Lua 5.5, which removed them. math.atan follows the legacy one-argument Luau/Lua 5.1–5.2 contract or modern Blu/Lua 5.3–5.5 atan2(y, x) contract according to the active profile; math.asin and math.acos follow the shared numeric contract. Profile-aware math.floor and math.ceil and the integral result of two-result math.modf preserve number-only legacy behavior or return modern exact integers when representable. math.abs preserves modern integer inputs, while math.log explicitly distinguishes Lua 5.1's ignored extra arguments from modern base selection. math.min and math.max preserve the selected modern subtype and upstream NaN ordering, using exact mixed integer/number comparisons across i64. Modern-profile math.type, math.tointeger, and math.ult provide numeric subtype introspection, exact integral conversion, and unsigned comparison. Profile-aware math.random and math.randomseed provide a deterministic, non-cryptographic generator with explicit legacy coercion, result-subtype, zero-bound, seed-arity, and seed-return contracts; seeded reproducibility is guaranteed, but upstream implementations' exact random sequences are not. Blu/Luau profiles also expose math.clamp, math.sign, and math.round with the pinned Luau edge behavior, plus numeric classification and math.lerp/math.map interpolation helpers and deterministic math.noise. The bit32 library provides variadic AND/OR/XOR, NOT, shifts, rotates, field extraction, and field replacement in Blu, Luau, Lua 5.2, and Lua 5.3 profiles, with explicit profile-specific input conversion and result subtypes. Legacy table.getn and table.maxn are also profile-gated to the pinned versions that retain them, while Blu exposes both for migration. The Lua 5.1 table.foreach and table.foreachi callbacks are available in the Lua 5.1 and Blu profiles; callback results short-circuit iteration, and owned callbacks can yield and resume without restarting iteration. pairs honors profile-available __pairs handlers, including resumable owned handlers. The legacy gcinfo memory counter is likewise available only in Blu, Luau, and Lua 5.1 profiles. Core tonumber conversion preserves profile subtypes, hexadecimal integer and floating strings, and the explicit-base grammar and overflow behavior of each profile. Byte-oriented string.find supports literal searches, relative starts, empty needles, nil misses, explicit plain mode, basic anchors, wildcard bytes, portable byte classes, class negation, and escaped punctuation under a work limit. The %g graph class follows modern profiles while Lua 5.1 preserves its literal escape semantics. Bracket sets support byte ranges, classes, and negation. Unimplemented Lua-pattern syntax fails structurally instead of being treated as literal text. All four Lua repetition suffixes are bounded: greedy */+/? and minimal -. string.find and string.match return bounded nested substring and position captures from the same byte-pattern engine, including %1 through %9 backreferences to completed substring captures and bounded %bxy nested byte pairs. Zero-width %f[set] byte frontiers share the bracket-set engine. string.gmatch returns a stateful function iterator with the same bounded pattern and capture behavior, including correct empty-match progress. string.gsub adds bounded string, number, direct-table, and function replacement, %0, %1%9, %%, empty-match progress, replacement counts, and profile-specific Lua 5.1 escape handling. Synchronous table-__index replacement handlers are included. Owned coroutine callbacks can now yield once per match and resume with explicit operation state and GC roots; other native library callbacks remain pending. Blu/Luau string.split produces bounded byte-string arrays with Luau-compatible default, empty-separator, consecutive-separator, and empty-field behavior. Blu/Luau table.create and table.find provide bounded preallocation/fill and raw array search with profile-typed result indices. table.clear retains allocation while removing entries, and table.clone performs a bounded shallow copy with unprotected metatable preservation. table.freeze/table.isfrozen enforce shallow immutability through every heap mutation path; clones of frozen tables remain mutable. coroutine.running and coroutine.isyieldable dispatch on the executing artifact profile, including Lua 5.1 main-thread nil and Luau main-thread yieldability. The base library exposes GC-safe collectgarbage("collect") and accounted-heap collectgarbage("count"); other version-specific commands fail explicitly. table.sort provides bounded, exact default ordering for uniform number and byte-string sequences, plus custom comparator callbacks and metamethod ordering. Owned comparators and ordering metamethods can yield and resume with explicit sort state and GC roots. Overlap-safe bounded table.move is available for Blu, Luau, and Lua 5.3–5.5, with explicit rejection in Lua 5.1–5.2 profiles. Ordinary bytecode calls use a bounded explicit VM frame stack; saved callers remain GC roots. Initial generational coroutine threads implement create/resume/yield/status/wrap/running/isyieldable/close, including nested calls, resume arguments, successful protected-call suspension, resumed pcall/xpcall error unwinding, yielding error handlers, and GC-traced continuations. Owned BluV1 coroutine entry closures also suspend, resume repeatedly, and retain their frames through collection; native library operations that invoke yielding callbacks still need operation-specific continuations. Owned sole-call returns replace the current closure frame, providing proper tail recursion independently of the configured ordinary call-depth limit. Portable V1 package envelopes provide bounded canonical decoding, SHA-256 identity, explicit dialect and authority requirements, and an opaque validated bytecode payload. The public engine currently executes only dialect-matched packages without imports when their authority profile and exact capability requirements are covered by the host policy; service linking still fails explicitly until its host bindings exist. The default legacy engine selects blu; --!dialect directives are checked against the configured engine. The public Engine::execute_owned_source entry point now exposes the bounded Blu-owned baseline for all seven profiles, including Lua 5.1–5.5, while the legacy bytecode path still rejects those profiles. This is meaningful execution coverage, not yet a claim of complete Luau, Lua, or Blu compatibility.

The first Blu-owned frontend substrate is also present: blu-syntax performs bounded byte-oriented lexing and parses the initial local/assignment-list/return arithmetic slice, including nil, boolean, shared decimal integers plus digit-bearing fraction/exponent forms (1.5, .25, 2e3, 4.5e-2), and quoted or long-bracket byte-string literals. Quoted strings implement the shared escapes plus explicit profile rules for byte, Unicode, whitespace, and line-continuation escapes; long strings use profile-specific newline handling plus semicolon separators, grouping parentheses, and profile-neutral +/-/*///%/^ plus right-associative .., into a spanned arena AST with explicit profile reconciliation. Unary not follows common Lua truthiness and produces a boolean under every profile; unary - preserves integers in Blu and Lua 5.3–5.5 and negates numbers in the other profiles. Unary # measures byte strings, returning an integer for Blu and Lua 5.3–5.5 and a number elsewhere, and executes raw or resumable metamethod-aware table length. Exponentiation follows the shared right-associative precedence above unary operators and always produces a number. Blu and Lua 5.3–5.5 source profiles support 64-bit integer &, |, binary and unary ~, <<, and >>, including Lua precedence and reversed negative shifts. Luau and Lua 5.1–5.2 reject this syntax during lexing. Blu and Luau additionally support +=, -=, *=, /=, //=, %=, ^=, and ..= statements. Indexed receivers and keys and the previous value are evaluated exactly once before the right-hand expression. Hexadecimal integers are accepted in every profile. Blu and Lua 5.3–5.5 use wrapping 64-bit integer representation; Luau and Lua 5.1–5.2 use numbers. Blu fitting decimal integers and Blu-only binary integers are likewise exact 64-bit values. Internal numeric separators are accepted only by Blu and Luau. Binary integers are also available only in Blu and Luau. Hexadecimal floats and their profile matrix are supported: exponent-only forms work in Blu and Lua 5.1–5.5, fractional forms work in Blu and Lua 5.2–5.5, and Luau rejects both. Arithmetic accepts whitespace-trimmed decimal and hexadecimal numeric strings under every profile. Blu and Lua 5.4–5.5 preserve exact parsed integers; Luau and Lua 5.1–5.3 produce numbers from string operands. Other profile-specific numeral extensions remain explicitly unsupported. Quoted strings support decimal byte escapes in every profile. Two-digit hexadecimal byte escapes are available in Blu, Luau, and Lua 5.2–5.5; malformed or out-of-range byte escapes are rejected structurally. Those same profiles support \z, which removes every following ASCII whitespace byte, including line breaks. Lua 5.1 rejects it explicitly. Every profile supports backslash line continuation; LF, CRLF, and CR source line endings normalize to one LF byte in the resulting string. Unicode escapes are byte-oriented and explicitly versioned: Blu, Luau, and Lua 5.3–5.5 accept \u{...} through 0x10ffff; Blu and Lua 5.4–5.5 additionally accept the upstream extended UTF-8 range through 0x7fffffff. Lua 5.1/5.2 reject the syntax. Trailing-dot forms such as 1. and 1.e2 are accepted; as in the pinned runtimes, 1..2 is malformed and must be spaced before future concatenation. blu_compiler::owned::OwnedCompiler resolves and lowers that slice into canonical BluV1 artifacts without native linkage or fallback; the same explicit-profile API is available from the public facade as blu_lang::frontend. Engine::execute_owned_compilation directly executes the single-prototype scalar baseline slice for every declared profile without the Luau compiler or bytecode translator. It revalidates the consumed artifact under caller-supplied execution limits. Canonical register moves preserve arbitrary scalar return lists without numeric coercion. BluV1 floor division executes with Luau number semantics and Blu/Lua 5.3–5.5 integer-preserving modern semantics. The owned path also directly executes profile-neutral ==, ~=, <, <=, >, and >=; ordered comparisons accept only compatible numbers or byte strings, while equality between unlike scalar types is false. Mixed integer/number comparisons remain exact across the full 64-bit range instead of first converting integers to f64; NaN is unequal and unordered. Operand-returning and and or use validated forward branches and preserve short-circuit evaluation in every profile. Structured if/elseif/else blocks execute through the same validated forward control flow, retain branch-local lexical scope, and support path-terminating returns. Shared do/end blocks provide explicit lexical scope without adding a control-flow branch; unreachable statements after an unconditional nested return are not emitted. Block-scoped while loops add separately feature-gated backward branches; validation records the target's definite-initialization state, and runtime execution remains subject to the VM instruction limit. Blu and Lua 5.2–5.5 also support owned ::label:: declarations and validated same-scope goto branches. Cross-scope jumps remain rejected until explicit upvalue-closing control flow is available. Embedders can also request persistent cooperative interruption from another thread through Vm::interrupt_handle or Engine::interrupt_handle; both execution engines stop with a structured error at an instruction boundary. Absolute wall-clock deadlines use the same safe boundary and remain distinct from deterministic instruction fuel. Native callbacks can query the active artifact profile and cooperatively poll both conditions through the public VM API. Coroutine task states have a separate live-count limit that collects unreachable tasks before rejecting growth. Shared break statements are structurally restricted to loop bodies and patch only the innermost loop's exit, including through nested conditional blocks. continue is an explicit Blu/Luau extension that restarts the innermost loop; Lua 5.1–5.5 profiles reject it during lexing. Profile-neutral repeat/until loops execute their body at least once and retain body locals through the trailing condition. In Blu and Luau, continue in a repeat loop transfers to that trailing condition. Shared numeric for loops snapshot their controls once, use the profile's numeric representation for the implicit positive unit step, and scope the index to the loop. Explicit, provably nonzero numeric-literal steps support both directions. Literal zero follows the pinned Lua 5.1–5.3 and Luau non-positive classification; Blu remains unassigned and Lua 5.4–5.5 reject zero explicitly. Dynamic steps execute for Luau and Lua 5.1–5.3 with single-evaluation snapshots and runtime direction selection; Blu and Lua 5.4–5.5 reject them until their possible zero case is executable. Generic for executes iterator/state/control triples in every owned profile, including bounded final-call adjustment, lexical result variables, and nil-only termination. Lua 5.4–5.5 also preserve the fourth to-be-closed control and close it on normal loop exit and break. Lua 5.4/5.5 <const> and <close> local attributes are parsed; const writes are rejected and close locals run their __close handler on normal scope exit, break, return, goto, and protected errors, including reverse-order cleanup and resumable yielding handlers. Full finalizer/GC and abandoned-coroutine semantics remain incomplete. Canonical BluV1 global loads and stores connect the owned frontend to the VM's embedding registry. Unknown scalar reads produce nil, and scalar writes persist in the VM; Lua 5.2–5.5 owned chunks additionally use a rooted default environment synchronized with that registry, while explicit _ENV tables are captured through nested closures. Identifier assignment lists can mix locals, captures, and globals while preserving simultaneous assignment. Owned Lua 5.2–5.5 source execution installs an environment-aware load: string chunks return callable closures rooted to the supplied fourth-argument table (or the default environment), and Engine::load_owned_source exposes the same closure primitive to embedders. Complete environment-rebinding APIs remain unsupported. The Lua 5.1 owned path additionally supports string loadstring plus function-targeted getfenv/setfenv; stack-level environment rebinding remains unsupported; owned load accepts bounded string-producing reader functions, while yielding readers, binary chunks, and exact mode-string behavior remain unsupported. The owned frontend also supports bounded table constructors with sequential array, identifier-keyed, and bracket-keyed fields, plus bracket and dot-name reads and single-target writes. These execute directly through the generational heap, return nil for absent keys, and retain active registers as GC roots during allocation and table growth. Mixed identifier/index/field assignment lists snapshot every target and right-hand side before committing writes. Table and method reads follow __index table chains or resumably invoke closure/native handlers; writes likewise follow __newindex chains or handlers. Binary arithmetic dispatches __add, __sub, __mul, __div, __mod, __pow, and dialect-gated __idiv through the same bounded continuation path; unary negation likewise dispatches __unm. Final-field vararg and call MULTRET expansion are implemented. Concatenation invokes left-then-right __concat handlers through a resumable continuation when string/number coercion is unavailable. Comparison continuations preserve profile-specific handler selection and Lua 5.5's removal of reversed-__lt fallback. Operator event values may themselves be bounded callable-table chains; their final Blu closures remain on the explicit continuation stack. Unary # measures raw table sequences in Lua 5.1. Other profiles resumably invoke a present table __len closure or native handler and otherwise use the raw sequence length. Bounded postfix calls evaluate the callee and fixed scalar arguments left-to-right and dispatch through the VM's existing closure/native/table-call path. Scalar contexts produce the first result or nil; a final call in a local or identifier assignment list requests the remaining bounded result count, truncating excess results and padding missing results with nil. Call statements support side-effecting APIs such as print. A final call or method call in a return statement forwards every result. Sole Blu closure calls replace the current frame; preceding fixed return values remain in a GC-rooted bounded continuation and are prepended after the call completes. Callable tables resolve bounded __call chains, prepend every table receiver, and enter Blu closure handlers through those same continuations. Final call arguments expand every result (target(prefix, producer())) through a GC-rooted continuation; non-final calls still collapse to one value, and nested forwarding preserves left-to-right evaluation. Remaining resumable callbacks stay explicit later work. Owned variadic functions support scalar and fixed-width ... reads with nil padding, dynamic return forwarding, and dynamic final call arguments, and final table-constructor expansion, including fixed prefixes and method receivers; active and suspended varargs remain GC roots. Final calls in table constructors expand every result through a GC-rooted resumable table-fill continuation for both Blu closures and native functions. The older Engine::execute source path continues to use the pinned Luau compatibility compiler while the owned grammar and executor are expanded.

Repository layout

  • blu-lang: public facade crate for embedding Blu.
  • blu-core: dependency-free semantic profiles, source identities, byte spans, and diagnostics.
  • blu-syntax: bounded byte lexer and initial parser/AST for the Blu-owned frontend.
  • blu-compiler: safe-Rust BluV1 compiler slice, with an opt-in legacy Luau compiler adapter.
  • blu-bytecode: bounded BluV1 artifacts plus versioned Luau decoding and loading.
  • blu-package: bounded canonical package envelopes and artifact validation.
  • blu-runtime: values, heap, interpreter, interruption, and Rust host API.
  • blu-conformance: differential execution against pinned Luau and Lua runtimes.
  • .upstream/luau: ignored checkout created by just upstream.

The Rust core, syntax, bytecode, package, runtime, facade, and conformance crates forbid unsafe Rust at the crate level. blu-compiler builds its owned compiler without native dependencies by default. Its opt-in legacy-luau feature contains the isolated boundary for the pinned upstream Luau C++ compiler; a noexcept shim translates native exceptions and owns allocation/deallocation across that boundary. The current blu-lang facade and conformance runner enable that compatibility feature explicitly.

Development

just upstream
just test
just conformance

See NOTICE.md for upstream attribution and UPSTREAM.toml for compatibility revisions. The intended compatibility and authority model is defined in docs/language-contract.md. The explicit profile backlog is tracked in docs/dialect-matrix.md, and the Blu-owned frontend decision is recorded in ADR 0002.

Rust applications should depend on the blu-lang crate. The bare blu name on crates.io belongs to an unrelated project.

use blu_lang::{Engine,Value};let values = Engine::default().execute("return 20 + 22").expect("valid Blu source");assert_eq!(values, vec![Value::Number(42.0)]);

Intended embedders

The core runtime is application-neutral and does not depend on Borg. It is intended for:

  • extensible terminal and desktop applications;
  • game engines and simulation tools;
  • command-line automation;
  • servers and edge runtimes;
  • build, workflow, and configuration systems;
  • editors and developer tools;
  • agent and orchestration platforms.

Application adapters such as a future blu-borg crate belong outside the core runtime and use the same embedding API available to third-party applications.

License

Blu is free and open-source software, available under the MIT License.

About

Lua / Luau superset with a fast Rust runtime

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Latest commit

History

200 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Blu: A language extending Lua with a fast Rust runtime

Blu is a fast, embeddable Lua/Luau superset language and runtime written in Rust. It is built for deeply extensible native applications. Its default blu dialect pragmatically unifies and extends Luau and modern Lua; explicit compatibility dialects preserve the exact semantics of each upstream language version where those semantics conflict.

Borg Agent embedding, package authority, and customization boundaries are documented in docs/borg-embedding.md. Blu owns the language runtime; the Borg host owns extension admission and capabilities.

Blu starts from Luau's optimized language and VM design, but it is not confined to Roblox's sandboxed Luau surface. Blu supports explicit Luau and Lua compatibility modes alongside a first-class systems profile with io, os, packages, filesystem, network, and process capabilities.

Blu is currently under active compatibility development. The initial milestone is a safe Luau-bytecode loader and interpreter continuously checked against a pinned upstream Luau revision. Lua 5.1–5.5 source profiles, standard libraries, and versioned C API bridges follow behind the same differential conformance gates.

The current Luau-backed Blu path compiles source in-process and covers scalar and table operations, numeric and generic loops, closures and mutable upvalues, variadic and multiple-return calls, globals and imports, Rust native functions, protected calls, pairs/ipairs/next, string method dispatch, core metamethods, an initial standard library, and host-configured cached require. The owned Blu/Luau frontend also lowers value-selecting if ... then ... elseif ... else ... expressions with short-circuit branches; Lua profiles reject that Luau syntax explicitly. The contextual _VERSION global reports Blu, Luau, or the selected Lua 5.1Lua 5.5 profile and remains explicitly overridable by the guest or embedder. The math slice includes shared truncating math.fmod; unlike floor-based %, its remainder preserves the dividend's sign, with modern integer preservation and zero-divisor errors. Shared math.pow provides the library form of floating exponentiation, while math.frexp and math.ldexp split and compose binary exponents with profile-appropriate exponent subtypes and conversion. The legacy sinh/cosh/tanh/log10/atan2 names remain available in Blu, Luau, and Lua 5.1–5.4 profiles and fail explicitly in Lua 5.5, which removed them. math.atan follows the legacy one-argument Luau/Lua 5.1–5.2 contract or modern Blu/Lua 5.3–5.5 atan2(y, x) contract according to the active profile; math.asin and math.acos follow the shared numeric contract. Profile-aware math.floor and math.ceil and the integral result of two-result math.modf preserve number-only legacy behavior or return modern exact integers when representable. math.abs preserves modern integer inputs, while math.log explicitly distinguishes Lua 5.1's ignored extra arguments from modern base selection. math.min and math.max preserve the selected modern subtype and upstream NaN ordering, using exact mixed integer/number comparisons across i64. Modern-profile math.type, math.tointeger, and math.ult provide numeric subtype introspection, exact integral conversion, and unsigned comparison. Profile-aware math.random and math.randomseed provide a deterministic, non-cryptographic generator with explicit legacy coercion, result-subtype, zero-bound, seed-arity, and seed-return contracts; seeded reproducibility is guaranteed, but upstream implementations' exact random sequences are not. Blu/Luau profiles also expose math.clamp, math.sign, and math.round with the pinned Luau edge behavior, plus numeric classification and math.lerp/math.map interpolation helpers and deterministic math.noise. The bit32 library provides variadic AND/OR/XOR, NOT, shifts, rotates, field extraction, and field replacement in Blu, Luau, Lua 5.2, and Lua 5.3 profiles, with explicit profile-specific input conversion and result subtypes. Legacy table.getn and table.maxn are also profile-gated to the pinned versions that retain them, while Blu exposes both for migration. The Lua 5.1 table.foreach and table.foreachi callbacks are available in the Lua 5.1 and Blu profiles; callback results short-circuit iteration, and owned callbacks can yield and resume without restarting iteration. pairs honors profile-available __pairs handlers, including resumable owned handlers. The legacy gcinfo memory counter is likewise available only in Blu, Luau, and Lua 5.1 profiles. Core tonumber conversion preserves profile subtypes, hexadecimal integer and floating strings, and the explicit-base grammar and overflow behavior of each profile. Byte-oriented string.find supports literal searches, relative starts, empty needles, nil misses, explicit plain mode, basic anchors, wildcard bytes, portable byte classes, class negation, and escaped punctuation under a work limit. The %g graph class follows modern profiles while Lua 5.1 preserves its literal escape semantics. Bracket sets support byte ranges, classes, and negation. Unimplemented Lua-pattern syntax fails structurally instead of being treated as literal text. All four Lua repetition suffixes are bounded: greedy */+/? and minimal -. string.find and string.match return bounded nested substring and position captures from the same byte-pattern engine, including %1 through %9 backreferences to completed substring captures and bounded %bxy nested byte pairs. Zero-width %f[set] byte frontiers share the bracket-set engine. string.gmatch returns a stateful function iterator with the same bounded pattern and capture behavior, including correct empty-match progress. string.gsub adds bounded string, number, direct-table, and function replacement, %0, %1%9, %%, empty-match progress, replacement counts, and profile-specific Lua 5.1 escape handling. Synchronous table-__index replacement handlers are included. Owned coroutine callbacks can now yield once per match and resume with explicit operation state and GC roots; other native library callbacks remain pending. Blu/Luau string.split produces bounded byte-string arrays with Luau-compatible default, empty-separator, consecutive-separator, and empty-field behavior. Blu/Luau table.create and table.find provide bounded preallocation/fill and raw array search with profile-typed result indices. table.clear retains allocation while removing entries, and table.clone performs a bounded shallow copy with unprotected metatable preservation. table.freeze/table.isfrozen enforce shallow immutability through every heap mutation path; clones of frozen tables remain mutable. coroutine.running and coroutine.isyieldable dispatch on the executing artifact profile, including Lua 5.1 main-thread nil and Luau main-thread yieldability. The base library exposes GC-safe collectgarbage("collect") and accounted-heap collectgarbage("count"); other version-specific commands fail explicitly. table.sort provides bounded, exact default ordering for uniform number and byte-string sequences, plus custom comparator callbacks and metamethod ordering. Owned comparators and ordering metamethods can yield and resume with explicit sort state and GC roots. Overlap-safe bounded table.move is available for Blu, Luau, and Lua 5.3–5.5, with explicit rejection in Lua 5.1–5.2 profiles. Ordinary bytecode calls use a bounded explicit VM frame stack; saved callers remain GC roots. Initial generational coroutine threads implement create/resume/yield/status/wrap/running/isyieldable/close, including nested calls, resume arguments, successful protected-call suspension, resumed pcall/xpcall error unwinding, yielding error handlers, and GC-traced continuations. Owned BluV1 coroutine entry closures also suspend, resume repeatedly, and retain their frames through collection; native library operations that invoke yielding callbacks still need operation-specific continuations. Owned sole-call returns replace the current closure frame, providing proper tail recursion independently of the configured ordinary call-depth limit. Portable V1 package envelopes provide bounded canonical decoding, SHA-256 identity, explicit dialect and authority requirements, and an opaque validated bytecode payload. The public engine currently executes only dialect-matched packages without imports when their authority profile and exact capability requirements are covered by the host policy; service linking still fails explicitly until its host bindings exist. The default legacy engine selects blu; --!dialect directives are checked against the configured engine. The public Engine::execute_owned_source entry point now exposes the bounded Blu-owned baseline for all seven profiles, including Lua 5.1–5.5, while the legacy bytecode path still rejects those profiles. This is meaningful execution coverage, not yet a claim of complete Luau, Lua, or Blu compatibility.

The first Blu-owned frontend substrate is also present: blu-syntax performs bounded byte-oriented lexing and parses the initial local/assignment-list/return arithmetic slice, including nil, boolean, shared decimal integers plus digit-bearing fraction/exponent forms (1.5, .25, 2e3, 4.5e-2), and quoted or long-bracket byte-string literals. Quoted strings implement the shared escapes plus explicit profile rules for byte, Unicode, whitespace, and line-continuation escapes; long strings use profile-specific newline handling plus semicolon separators, grouping parentheses, and profile-neutral +/-/*///%/^ plus right-associative .., into a spanned arena AST with explicit profile reconciliation. Unary not follows common Lua truthiness and produces a boolean under every profile; unary - preserves integers in Blu and Lua 5.3–5.5 and negates numbers in the other profiles. Unary # measures byte strings, returning an integer for Blu and Lua 5.3–5.5 and a number elsewhere, and executes raw or resumable metamethod-aware table length. Exponentiation follows the shared right-associative precedence above unary operators and always produces a number. Blu and Lua 5.3–5.5 source profiles support 64-bit integer &, |, binary and unary ~, <<, and >>, including Lua precedence and reversed negative shifts. Luau and Lua 5.1–5.2 reject this syntax during lexing. Blu and Luau additionally support +=, -=, *=, /=, //=, %=, ^=, and ..= statements. Indexed receivers and keys and the previous value are evaluated exactly once before the right-hand expression. Hexadecimal integers are accepted in every profile. Blu and Lua 5.3–5.5 use wrapping 64-bit integer representation; Luau and Lua 5.1–5.2 use numbers. Blu fitting decimal integers and Blu-only binary integers are likewise exact 64-bit values. Internal numeric separators are accepted only by Blu and Luau. Binary integers are also available only in Blu and Luau. Hexadecimal floats and their profile matrix are supported: exponent-only forms work in Blu and Lua 5.1–5.5, fractional forms work in Blu and Lua 5.2–5.5, and Luau rejects both. Arithmetic accepts whitespace-trimmed decimal and hexadecimal numeric strings under every profile. Blu and Lua 5.4–5.5 preserve exact parsed integers; Luau and Lua 5.1–5.3 produce numbers from string operands. Other profile-specific numeral extensions remain explicitly unsupported. Quoted strings support decimal byte escapes in every profile. Two-digit hexadecimal byte escapes are available in Blu, Luau, and Lua 5.2–5.5; malformed or out-of-range byte escapes are rejected structurally. Those same profiles support \z, which removes every following ASCII whitespace byte, including line breaks. Lua 5.1 rejects it explicitly. Every profile supports backslash line continuation; LF, CRLF, and CR source line endings normalize to one LF byte in the resulting string. Unicode escapes are byte-oriented and explicitly versioned: Blu, Luau, and Lua 5.3–5.5 accept \u{...} through 0x10ffff; Blu and Lua 5.4–5.5 additionally accept the upstream extended UTF-8 range through 0x7fffffff. Lua 5.1/5.2 reject the syntax. Trailing-dot forms such as 1. and 1.e2 are accepted; as in the pinned runtimes, 1..2 is malformed and must be spaced before future concatenation. blu_compiler::owned::OwnedCompiler resolves and lowers that slice into canonical BluV1 artifacts without native linkage or fallback; the same explicit-profile API is available from the public facade as blu_lang::frontend. Engine::execute_owned_compilation directly executes the single-prototype scalar baseline slice for every declared profile without the Luau compiler or bytecode translator. It revalidates the consumed artifact under caller-supplied execution limits. Canonical register moves preserve arbitrary scalar return lists without numeric coercion. BluV1 floor division executes with Luau number semantics and Blu/Lua 5.3–5.5 integer-preserving modern semantics. The owned path also directly executes profile-neutral ==, ~=, <, <=, >, and >=; ordered comparisons accept only compatible numbers or byte strings, while equality between unlike scalar types is false. Mixed integer/number comparisons remain exact across the full 64-bit range instead of first converting integers to f64; NaN is unequal and unordered. Operand-returning and and or use validated forward branches and preserve short-circuit evaluation in every profile. Structured if/elseif/else blocks execute through the same validated forward control flow, retain branch-local lexical scope, and support path-terminating returns. Shared do/end blocks provide explicit lexical scope without adding a control-flow branch; unreachable statements after an unconditional nested return are not emitted. Block-scoped while loops add separately feature-gated backward branches; validation records the target's definite-initialization state, and runtime execution remains subject to the VM instruction limit. Blu and Lua 5.2–5.5 also support owned ::label:: declarations and validated same-scope goto branches. Cross-scope jumps remain rejected until explicit upvalue-closing control flow is available. Embedders can also request persistent cooperative interruption from another thread through Vm::interrupt_handle or Engine::interrupt_handle; both execution engines stop with a structured error at an instruction boundary. Absolute wall-clock deadlines use the same safe boundary and remain distinct from deterministic instruction fuel. Native callbacks can query the active artifact profile and cooperatively poll both conditions through the public VM API. Coroutine task states have a separate live-count limit that collects unreachable tasks before rejecting growth. Shared break statements are structurally restricted to loop bodies and patch only the innermost loop's exit, including through nested conditional blocks. continue is an explicit Blu/Luau extension that restarts the innermost loop; Lua 5.1–5.5 profiles reject it during lexing. Profile-neutral repeat/until loops execute their body at least once and retain body locals through the trailing condition. In Blu and Luau, continue in a repeat loop transfers to that trailing condition. Shared numeric for loops snapshot their controls once, use the profile's numeric representation for the implicit positive unit step, and scope the index to the loop. Explicit, provably nonzero numeric-literal steps support both directions. Literal zero follows the pinned Lua 5.1–5.3 and Luau non-positive classification; Blu remains unassigned and Lua 5.4–5.5 reject zero explicitly. Dynamic steps execute for Luau and Lua 5.1–5.3 with single-evaluation snapshots and runtime direction selection; Blu and Lua 5.4–5.5 reject them until their possible zero case is executable. Generic for executes iterator/state/control triples in every owned profile, including bounded final-call adjustment, lexical result variables, and nil-only termination. Lua 5.4–5.5 also preserve the fourth to-be-closed control and close it on normal loop exit and break. Lua 5.4/5.5 <const> and <close> local attributes are parsed; const writes are rejected and close locals run their __close handler on normal scope exit, break, return, goto, and protected errors, including reverse-order cleanup and resumable yielding handlers. Full finalizer/GC and abandoned-coroutine semantics remain incomplete. Canonical BluV1 global loads and stores connect the owned frontend to the VM's embedding registry. Unknown scalar reads produce nil, and scalar writes persist in the VM; Lua 5.2–5.5 owned chunks additionally use a rooted default environment synchronized with that registry, while explicit _ENV tables are captured through nested closures. Identifier assignment lists can mix locals, captures, and globals while preserving simultaneous assignment. Owned Lua 5.2–5.5 source execution installs an environment-aware load: string chunks return callable closures rooted to the supplied fourth-argument table (or the default environment), and Engine::load_owned_source exposes the same closure primitive to embedders. Complete environment-rebinding APIs remain unsupported. The Lua 5.1 owned path additionally supports string loadstring plus function-targeted getfenv/setfenv; stack-level environment rebinding remains unsupported; owned load accepts bounded string-producing reader functions, while yielding readers, binary chunks, and exact mode-string behavior remain unsupported. The owned frontend also supports bounded table constructors with sequential array, identifier-keyed, and bracket-keyed fields, plus bracket and dot-name reads and single-target writes. These execute directly through the generational heap, return nil for absent keys, and retain active registers as GC roots during allocation and table growth. Mixed identifier/index/field assignment lists snapshot every target and right-hand side before committing writes. Table and method reads follow __index table chains or resumably invoke closure/native handlers; writes likewise follow __newindex chains or handlers. Binary arithmetic dispatches __add, __sub, __mul, __div, __mod, __pow, and dialect-gated __idiv through the same bounded continuation path; unary negation likewise dispatches __unm. Final-field vararg and call MULTRET expansion are implemented. Concatenation invokes left-then-right __concat handlers through a resumable continuation when string/number coercion is unavailable. Comparison continuations preserve profile-specific handler selection and Lua 5.5's removal of reversed-__lt fallback. Operator event values may themselves be bounded callable-table chains; their final Blu closures remain on the explicit continuation stack. Unary # measures raw table sequences in Lua 5.1. Other profiles resumably invoke a present table __len closure or native handler and otherwise use the raw sequence length. Bounded postfix calls evaluate the callee and fixed scalar arguments left-to-right and dispatch through the VM's existing closure/native/table-call path. Scalar contexts produce the first result or nil; a final call in a local or identifier assignment list requests the remaining bounded result count, truncating excess results and padding missing results with nil. Call statements support side-effecting APIs such as print. A final call or method call in a return statement forwards every result. Sole Blu closure calls replace the current frame; preceding fixed return values remain in a GC-rooted bounded continuation and are prepended after the call completes. Callable tables resolve bounded __call chains, prepend every table receiver, and enter Blu closure handlers through those same continuations. Final call arguments expand every result (target(prefix, producer())) through a GC-rooted continuation; non-final calls still collapse to one value, and nested forwarding preserves left-to-right evaluation. Remaining resumable callbacks stay explicit later work. Owned variadic functions support scalar and fixed-width ... reads with nil padding, dynamic return forwarding, and dynamic final call arguments, and final table-constructor expansion, including fixed prefixes and method receivers; active and suspended varargs remain GC roots. Final calls in table constructors expand every result through a GC-rooted resumable table-fill continuation for both Blu closures and native functions. The older Engine::execute source path continues to use the pinned Luau compatibility compiler while the owned grammar and executor are expanded.

Repository layout

  • blu-lang: public facade crate for embedding Blu.
  • blu-core: dependency-free semantic profiles, source identities, byte spans, and diagnostics.
  • blu-syntax: bounded byte lexer and initial parser/AST for the Blu-owned frontend.
  • blu-compiler: safe-Rust BluV1 compiler slice, with an opt-in legacy Luau compiler adapter.
  • blu-bytecode: bounded BluV1 artifacts plus versioned Luau decoding and loading.
  • blu-package: bounded canonical package envelopes and artifact validation.
  • blu-runtime: values, heap, interpreter, interruption, and Rust host API.
  • blu-conformance: differential execution against pinned Luau and Lua runtimes.
  • .upstream/luau: ignored checkout created by just upstream.

The Rust core, syntax, bytecode, package, runtime, facade, and conformance crates forbid unsafe Rust at the crate level. blu-compiler builds its owned compiler without native dependencies by default. Its opt-in legacy-luau feature contains the isolated boundary for the pinned upstream Luau C++ compiler; a noexcept shim translates native exceptions and owns allocation/deallocation across that boundary. The current blu-lang facade and conformance runner enable that compatibility feature explicitly.

Development

just upstream
just test
just conformance

See NOTICE.md for upstream attribution and UPSTREAM.toml for compatibility revisions. The intended compatibility and authority model is defined in docs/language-contract.md. The explicit profile backlog is tracked in docs/dialect-matrix.md, and the Blu-owned frontend decision is recorded in ADR 0002.

Rust applications should depend on the blu-lang crate. The bare blu name on crates.io belongs to an unrelated project.

use blu_lang::{Engine,Value};let values = Engine::default().execute("return 20 + 22").expect("valid Blu source");assert_eq!(values, vec![Value::Number(42.0)]);

Intended embedders

The core runtime is application-neutral and does not depend on Borg. It is intended for:

  • extensible terminal and desktop applications;
  • game engines and simulation tools;
  • command-line automation;
  • servers and edge runtimes;
  • build, workflow, and configuration systems;
  • editors and developer tools;
  • agent and orchestration platforms.

Application adapters such as a future blu-borg crate belong outside the core runtime and use the same embedding API available to third-party applications.

License

Blu is free and open-source software, available under the MIT License.

About

Lua / Luau superset with a fast Rust runtime

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

200 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Blu: A language extending Lua with a fast Rust runtime

Blu is a fast, embeddable Lua/Luau superset language and runtime written in Rust. It is built for deeply extensible native applications. Its default blu dialect pragmatically unifies and extends Luau and modern Lua; explicit compatibility dialects preserve the exact semantics of each upstream language version where those semantics conflict.

Borg Agent embedding, package authority, and customization boundaries are documented in docs/borg-embedding.md. Blu owns the language runtime; the Borg host owns extension admission and capabilities.

Blu starts from Luau's optimized language and VM design, but it is not confined to Roblox's sandboxed Luau surface. Blu supports explicit Luau and Lua compatibility modes alongside a first-class systems profile with io, os, packages, filesystem, network, and process capabilities.

Blu is currently under active compatibility development. The initial milestone is a safe Luau-bytecode loader and interpreter continuously checked against a pinned upstream Luau revision. Lua 5.1–5.5 source profiles, standard libraries, and versioned C API bridges follow behind the same differential conformance gates.

The current Luau-backed Blu path compiles source in-process and covers scalar and table operations, numeric and generic loops, closures and mutable upvalues, variadic and multiple-return calls, globals and imports, Rust native functions, protected calls, pairs/ipairs/next, string method dispatch, core metamethods, an initial standard library, and host-configured cached require. The owned Blu/Luau frontend also lowers value-selecting if ... then ... elseif ... else ... expressions with short-circuit branches; Lua profiles reject that Luau syntax explicitly. The contextual _VERSION global reports Blu, Luau, or the selected Lua 5.1Lua 5.5 profile and remains explicitly overridable by the guest or embedder. The math slice includes shared truncating math.fmod; unlike floor-based %, its remainder preserves the dividend's sign, with modern integer preservation and zero-divisor errors. Shared math.pow provides the library form of floating exponentiation, while math.frexp and math.ldexp split and compose binary exponents with profile-appropriate exponent subtypes and conversion. The legacy sinh/cosh/tanh/log10/atan2 names remain available in Blu, Luau, and Lua 5.1–5.4 profiles and fail explicitly in Lua 5.5, which removed them. math.atan follows the legacy one-argument Luau/Lua 5.1–5.2 contract or modern Blu/Lua 5.3–5.5 atan2(y, x) contract according to the active profile; math.asin and math.acos follow the shared numeric contract. Profile-aware math.floor and math.ceil and the integral result of two-result math.modf preserve number-only legacy behavior or return modern exact integers when representable. math.abs preserves modern integer inputs, while math.log explicitly distinguishes Lua 5.1's ignored extra arguments from modern base selection. math.min and math.max preserve the selected modern subtype and upstream NaN ordering, using exact mixed integer/number comparisons across i64. Modern-profile math.type, math.tointeger, and math.ult provide numeric subtype introspection, exact integral conversion, and unsigned comparison. Profile-aware math.random and math.randomseed provide a deterministic, non-cryptographic generator with explicit legacy coercion, result-subtype, zero-bound, seed-arity, and seed-return contracts; seeded reproducibility is guaranteed, but upstream implementations' exact random sequences are not. Blu/Luau profiles also expose math.clamp, math.sign, and math.round with the pinned Luau edge behavior, plus numeric classification and math.lerp/math.map interpolation helpers and deterministic math.noise. The bit32 library provides variadic AND/OR/XOR, NOT, shifts, rotates, field extraction, and field replacement in Blu, Luau, Lua 5.2, and Lua 5.3 profiles, with explicit profile-specific input conversion and result subtypes. Legacy table.getn and table.maxn are also profile-gated to the pinned versions that retain them, while Blu exposes both for migration. The Lua 5.1 table.foreach and table.foreachi callbacks are available in the Lua 5.1 and Blu profiles; callback results short-circuit iteration, and owned callbacks can yield and resume without restarting iteration. pairs honors profile-available __pairs handlers, including resumable owned handlers. The legacy gcinfo memory counter is likewise available only in Blu, Luau, and Lua 5.1 profiles. Core tonumber conversion preserves profile subtypes, hexadecimal integer and floating strings, and the explicit-base grammar and overflow behavior of each profile. Byte-oriented string.find supports literal searches, relative starts, empty needles, nil misses, explicit plain mode, basic anchors, wildcard bytes, portable byte classes, class negation, and escaped punctuation under a work limit. The %g graph class follows modern profiles while Lua 5.1 preserves its literal escape semantics. Bracket sets support byte ranges, classes, and negation. Unimplemented Lua-pattern syntax fails structurally instead of being treated as literal text. All four Lua repetition suffixes are bounded: greedy */+/? and minimal -. string.find and string.match return bounded nested substring and position captures from the same byte-pattern engine, including %1 through %9 backreferences to completed substring captures and bounded %bxy nested byte pairs. Zero-width %f[set] byte frontiers share the bracket-set engine. string.gmatch returns a stateful function iterator with the same bounded pattern and capture behavior, including correct empty-match progress. string.gsub adds bounded string, number, direct-table, and function replacement, %0, %1%9, %%, empty-match progress, replacement counts, and profile-specific Lua 5.1 escape handling. Synchronous table-__index replacement handlers are included. Owned coroutine callbacks can now yield once per match and resume with explicit operation state and GC roots; other native library callbacks remain pending. Blu/Luau string.split produces bounded byte-string arrays with Luau-compatible default, empty-separator, consecutive-separator, and empty-field behavior. Blu/Luau table.create and table.find provide bounded preallocation/fill and raw array search with profile-typed result indices. table.clear retains allocation while removing entries, and table.clone performs a bounded shallow copy with unprotected metatable preservation. table.freeze/table.isfrozen enforce shallow immutability through every heap mutation path; clones of frozen tables remain mutable. coroutine.running and coroutine.isyieldable dispatch on the executing artifact profile, including Lua 5.1 main-thread nil and Luau main-thread yieldability. The base library exposes GC-safe collectgarbage("collect") and accounted-heap collectgarbage("count"); other version-specific commands fail explicitly. table.sort provides bounded, exact default ordering for uniform number and byte-string sequences, plus custom comparator callbacks and metamethod ordering. Owned comparators and ordering metamethods can yield and resume with explicit sort state and GC roots. Overlap-safe bounded table.move is available for Blu, Luau, and Lua 5.3–5.5, with explicit rejection in Lua 5.1–5.2 profiles. Ordinary bytecode calls use a bounded explicit VM frame stack; saved callers remain GC roots. Initial generational coroutine threads implement create/resume/yield/status/wrap/running/isyieldable/close, including nested calls, resume arguments, successful protected-call suspension, resumed pcall/xpcall error unwinding, yielding error handlers, and GC-traced continuations. Owned BluV1 coroutine entry closures also suspend, resume repeatedly, and retain their frames through collection; native library operations that invoke yielding callbacks still need operation-specific continuations. Owned sole-call returns replace the current closure frame, providing proper tail recursion independently of the configured ordinary call-depth limit. Portable V1 package envelopes provide bounded canonical decoding, SHA-256 identity, explicit dialect and authority requirements, and an opaque validated bytecode payload. The public engine currently executes only dialect-matched packages without imports when their authority profile and exact capability requirements are covered by the host policy; service linking still fails explicitly until its host bindings exist. The default legacy engine selects blu; --!dialect directives are checked against the configured engine. The public Engine::execute_owned_source entry point now exposes the bounded Blu-owned baseline for all seven profiles, including Lua 5.1–5.5, while the legacy bytecode path still rejects those profiles. This is meaningful execution coverage, not yet a claim of complete Luau, Lua, or Blu compatibility.

The first Blu-owned frontend substrate is also present: blu-syntax performs bounded byte-oriented lexing and parses the initial local/assignment-list/return arithmetic slice, including nil, boolean, shared decimal integers plus digit-bearing fraction/exponent forms (1.5, .25, 2e3, 4.5e-2), and quoted or long-bracket byte-string literals. Quoted strings implement the shared escapes plus explicit profile rules for byte, Unicode, whitespace, and line-continuation escapes; long strings use profile-specific newline handling plus semicolon separators, grouping parentheses, and profile-neutral +/-/*///%/^ plus right-associative .., into a spanned arena AST with explicit profile reconciliation. Unary not follows common Lua truthiness and produces a boolean under every profile; unary - preserves integers in Blu and Lua 5.3–5.5 and negates numbers in the other profiles. Unary # measures byte strings, returning an integer for Blu and Lua 5.3–5.5 and a number elsewhere, and executes raw or resumable metamethod-aware table length. Exponentiation follows the shared right-associative precedence above unary operators and always produces a number. Blu and Lua 5.3–5.5 source profiles support 64-bit integer &, |, binary and unary ~, <<, and >>, including Lua precedence and reversed negative shifts. Luau and Lua 5.1–5.2 reject this syntax during lexing. Blu and Luau additionally support +=, -=, *=, /=, //=, %=, ^=, and ..= statements. Indexed receivers and keys and the previous value are evaluated exactly once before the right-hand expression. Hexadecimal integers are accepted in every profile. Blu and Lua 5.3–5.5 use wrapping 64-bit integer representation; Luau and Lua 5.1–5.2 use numbers. Blu fitting decimal integers and Blu-only binary integers are likewise exact 64-bit values. Internal numeric separators are accepted only by Blu and Luau. Binary integers are also available only in Blu and Luau. Hexadecimal floats and their profile matrix are supported: exponent-only forms work in Blu and Lua 5.1–5.5, fractional forms work in Blu and Lua 5.2–5.5, and Luau rejects both. Arithmetic accepts whitespace-trimmed decimal and hexadecimal numeric strings under every profile. Blu and Lua 5.4–5.5 preserve exact parsed integers; Luau and Lua 5.1–5.3 produce numbers from string operands. Other profile-specific numeral extensions remain explicitly unsupported. Quoted strings support decimal byte escapes in every profile. Two-digit hexadecimal byte escapes are available in Blu, Luau, and Lua 5.2–5.5; malformed or out-of-range byte escapes are rejected structurally. Those same profiles support \z, which removes every following ASCII whitespace byte, including line breaks. Lua 5.1 rejects it explicitly. Every profile supports backslash line continuation; LF, CRLF, and CR source line endings normalize to one LF byte in the resulting string. Unicode escapes are byte-oriented and explicitly versioned: Blu, Luau, and Lua 5.3–5.5 accept \u{...} through 0x10ffff; Blu and Lua 5.4–5.5 additionally accept the upstream extended UTF-8 range through 0x7fffffff. Lua 5.1/5.2 reject the syntax. Trailing-dot forms such as 1. and 1.e2 are accepted; as in the pinned runtimes, 1..2 is malformed and must be spaced before future concatenation. blu_compiler::owned::OwnedCompiler resolves and lowers that slice into canonical BluV1 artifacts without native linkage or fallback; the same explicit-profile API is available from the public facade as blu_lang::frontend. Engine::execute_owned_compilation directly executes the single-prototype scalar baseline slice for every declared profile without the Luau compiler or bytecode translator. It revalidates the consumed artifact under caller-supplied execution limits. Canonical register moves preserve arbitrary scalar return lists without numeric coercion. BluV1 floor division executes with Luau number semantics and Blu/Lua 5.3–5.5 integer-preserving modern semantics. The owned path also directly executes profile-neutral ==, ~=, <, <=, >, and >=; ordered comparisons accept only compatible numbers or byte strings, while equality between unlike scalar types is false. Mixed integer/number comparisons remain exact across the full 64-bit range instead of first converting integers to f64; NaN is unequal and unordered. Operand-returning and and or use validated forward branches and preserve short-circuit evaluation in every profile. Structured if/elseif/else blocks execute through the same validated forward control flow, retain branch-local lexical scope, and support path-terminating returns. Shared do/end blocks provide explicit lexical scope without adding a control-flow branch; unreachable statements after an unconditional nested return are not emitted. Block-scoped while loops add separately feature-gated backward branches; validation records the target's definite-initialization state, and runtime execution remains subject to the VM instruction limit. Blu and Lua 5.2–5.5 also support owned ::label:: declarations and validated same-scope goto branches. Cross-scope jumps remain rejected until explicit upvalue-closing control flow is available. Embedders can also request persistent cooperative interruption from another thread through Vm::interrupt_handle or Engine::interrupt_handle; both execution engines stop with a structured error at an instruction boundary. Absolute wall-clock deadlines use the same safe boundary and remain distinct from deterministic instruction fuel. Native callbacks can query the active artifact profile and cooperatively poll both conditions through the public VM API. Coroutine task states have a separate live-count limit that collects unreachable tasks before rejecting growth. Shared break statements are structurally restricted to loop bodies and patch only the innermost loop's exit, including through nested conditional blocks. continue is an explicit Blu/Luau extension that restarts the innermost loop; Lua 5.1–5.5 profiles reject it during lexing. Profile-neutral repeat/until loops execute their body at least once and retain body locals through the trailing condition. In Blu and Luau, continue in a repeat loop transfers to that trailing condition. Shared numeric for loops snapshot their controls once, use the profile's numeric representation for the implicit positive unit step, and scope the index to the loop. Explicit, provably nonzero numeric-literal steps support both directions. Literal zero follows the pinned Lua 5.1–5.3 and Luau non-positive classification; Blu remains unassigned and Lua 5.4–5.5 reject zero explicitly. Dynamic steps execute for Luau and Lua 5.1–5.3 with single-evaluation snapshots and runtime direction selection; Blu and Lua 5.4–5.5 reject them until their possible zero case is executable. Generic for executes iterator/state/control triples in every owned profile, including bounded final-call adjustment, lexical result variables, and nil-only termination. Lua 5.4–5.5 also preserve the fourth to-be-closed control and close it on normal loop exit and break. Lua 5.4/5.5 <const> and <close> local attributes are parsed; const writes are rejected and close locals run their __close handler on normal scope exit, break, return, goto, and protected errors, including reverse-order cleanup and resumable yielding handlers. Full finalizer/GC and abandoned-coroutine semantics remain incomplete. Canonical BluV1 global loads and stores connect the owned frontend to the VM's embedding registry. Unknown scalar reads produce nil, and scalar writes persist in the VM; Lua 5.2–5.5 owned chunks additionally use a rooted default environment synchronized with that registry, while explicit _ENV tables are captured through nested closures. Identifier assignment lists can mix locals, captures, and globals while preserving simultaneous assignment. Owned Lua 5.2–5.5 source execution installs an environment-aware load: string chunks return callable closures rooted to the supplied fourth-argument table (or the default environment), and Engine::load_owned_source exposes the same closure primitive to embedders. Complete environment-rebinding APIs remain unsupported. The Lua 5.1 owned path additionally supports string loadstring plus function-targeted getfenv/setfenv; stack-level environment rebinding remains unsupported; owned load accepts bounded string-producing reader functions, while yielding readers, binary chunks, and exact mode-string behavior remain unsupported. The owned frontend also supports bounded table constructors with sequential array, identifier-keyed, and bracket-keyed fields, plus bracket and dot-name reads and single-target writes. These execute directly through the generational heap, return nil for absent keys, and retain active registers as GC roots during allocation and table growth. Mixed identifier/index/field assignment lists snapshot every target and right-hand side before committing writes. Table and method reads follow __index table chains or resumably invoke closure/native handlers; writes likewise follow __newindex chains or handlers. Binary arithmetic dispatches __add, __sub, __mul, __div, __mod, __pow, and dialect-gated __idiv through the same bounded continuation path; unary negation likewise dispatches __unm. Final-field vararg and call MULTRET expansion are implemented. Concatenation invokes left-then-right __concat handlers through a resumable continuation when string/number coercion is unavailable. Comparison continuations preserve profile-specific handler selection and Lua 5.5's removal of reversed-__lt fallback. Operator event values may themselves be bounded callable-table chains; their final Blu closures remain on the explicit continuation stack. Unary # measures raw table sequences in Lua 5.1. Other profiles resumably invoke a present table __len closure or native handler and otherwise use the raw sequence length. Bounded postfix calls evaluate the callee and fixed scalar arguments left-to-right and dispatch through the VM's existing closure/native/table-call path. Scalar contexts produce the first result or nil; a final call in a local or identifier assignment list requests the remaining bounded result count, truncating excess results and padding missing results with nil. Call statements support side-effecting APIs such as print. A final call or method call in a return statement forwards every result. Sole Blu closure calls replace the current frame; preceding fixed return values remain in a GC-rooted bounded continuation and are prepended after the call completes. Callable tables resolve bounded __call chains, prepend every table receiver, and enter Blu closure handlers through those same continuations. Final call arguments expand every result (target(prefix, producer())) through a GC-rooted continuation; non-final calls still collapse to one value, and nested forwarding preserves left-to-right evaluation. Remaining resumable callbacks stay explicit later work. Owned variadic functions support scalar and fixed-width ... reads with nil padding, dynamic return forwarding, and dynamic final call arguments, and final table-constructor expansion, including fixed prefixes and method receivers; active and suspended varargs remain GC roots. Final calls in table constructors expand every result through a GC-rooted resumable table-fill continuation for both Blu closures and native functions. The older Engine::execute source path continues to use the pinned Luau compatibility compiler while the owned grammar and executor are expanded.

Repository layout

  • blu-lang: public facade crate for embedding Blu.
  • blu-core: dependency-free semantic profiles, source identities, byte spans, and diagnostics.
  • blu-syntax: bounded byte lexer and initial parser/AST for the Blu-owned frontend.
  • blu-compiler: safe-Rust BluV1 compiler slice, with an opt-in legacy Luau compiler adapter.
  • blu-bytecode: bounded BluV1 artifacts plus versioned Luau decoding and loading.
  • blu-package: bounded canonical package envelopes and artifact validation.
  • blu-runtime: values, heap, interpreter, interruption, and Rust host API.
  • blu-conformance: differential execution against pinned Luau and Lua runtimes.
  • .upstream/luau: ignored checkout created by just upstream.

The Rust core, syntax, bytecode, package, runtime, facade, and conformance crates forbid unsafe Rust at the crate level. blu-compiler builds its owned compiler without native dependencies by default. Its opt-in legacy-luau feature contains the isolated boundary for the pinned upstream Luau C++ compiler; a noexcept shim translates native exceptions and owns allocation/deallocation across that boundary. The current blu-lang facade and conformance runner enable that compatibility feature explicitly.

Development

just upstream
just test
just conformance

See NOTICE.md for upstream attribution and UPSTREAM.toml for compatibility revisions. The intended compatibility and authority model is defined in docs/language-contract.md. The explicit profile backlog is tracked in docs/dialect-matrix.md, and the Blu-owned frontend decision is recorded in ADR 0002.

Rust applications should depend on the blu-lang crate. The bare blu name on crates.io belongs to an unrelated project.

use blu_lang::{Engine,Value};let values = Engine::default().execute("return 20 + 22").expect("valid Blu source");assert_eq!(values, vec![Value::Number(42.0)]);

Intended embedders

The core runtime is application-neutral and does not depend on Borg. It is intended for:

  • extensible terminal and desktop applications;
  • game engines and simulation tools;
  • command-line automation;
  • servers and edge runtimes;
  • build, workflow, and configuration systems;
  • editors and developer tools;
  • agent and orchestration platforms.

Application adapters such as a future blu-borg crate belong outside the core runtime and use the same embedding API available to third-party applications.

License

Blu is free and open-source software, available under the MIT License.

About

Lua / Luau superset with a fast Rust runtime

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

200 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Blu: A language extending Lua with a fast Rust runtime

Blu is a fast, embeddable Lua/Luau superset language and runtime written in Rust. It is built for deeply extensible native applications. Its default blu dialect pragmatically unifies and extends Luau and modern Lua; explicit compatibility dialects preserve the exact semantics of each upstream language version where those semantics conflict.

Borg Agent embedding, package authority, and customization boundaries are documented in docs/borg-embedding.md. Blu owns the language runtime; the Borg host owns extension admission and capabilities.

Blu starts from Luau's optimized language and VM design, but it is not confined to Roblox's sandboxed Luau surface. Blu supports explicit Luau and Lua compatibility modes alongside a first-class systems profile with io, os, packages, filesystem, network, and process capabilities.

Blu is currently under active compatibility development. The initial milestone is a safe Luau-bytecode loader and interpreter continuously checked against a pinned upstream Luau revision. Lua 5.1–5.5 source profiles, standard libraries, and versioned C API bridges follow behind the same differential conformance gates.

The current Luau-backed Blu path compiles source in-process and covers scalar and table operations, numeric and generic loops, closures and mutable upvalues, variadic and multiple-return calls, globals and imports, Rust native functions, protected calls, pairs/ipairs/next, string method dispatch, core metamethods, an initial standard library, and host-configured cached require. The owned Blu/Luau frontend also lowers value-selecting if ... then ... elseif ... else ... expressions with short-circuit branches; Lua profiles reject that Luau syntax explicitly. The contextual _VERSION global reports Blu, Luau, or the selected Lua 5.1Lua 5.5 profile and remains explicitly overridable by the guest or embedder. The math slice includes shared truncating math.fmod; unlike floor-based %, its remainder preserves the dividend's sign, with modern integer preservation and zero-divisor errors. Shared math.pow provides the library form of floating exponentiation, while math.frexp and math.ldexp split and compose binary exponents with profile-appropriate exponent subtypes and conversion. The legacy sinh/cosh/tanh/log10/atan2 names remain available in Blu, Luau, and Lua 5.1–5.4 profiles and fail explicitly in Lua 5.5, which removed them. math.atan follows the legacy one-argument Luau/Lua 5.1–5.2 contract or modern Blu/Lua 5.3–5.5 atan2(y, x) contract according to the active profile; math.asin and math.acos follow the shared numeric contract. Profile-aware math.floor and math.ceil and the integral result of two-result math.modf preserve number-only legacy behavior or return modern exact integers when representable. math.abs preserves modern integer inputs, while math.log explicitly distinguishes Lua 5.1's ignored extra arguments from modern base selection. math.min and math.max preserve the selected modern subtype and upstream NaN ordering, using exact mixed integer/number comparisons across i64. Modern-profile math.type, math.tointeger, and math.ult provide numeric subtype introspection, exact integral conversion, and unsigned comparison. Profile-aware math.random and math.randomseed provide a deterministic, non-cryptographic generator with explicit legacy coercion, result-subtype, zero-bound, seed-arity, and seed-return contracts; seeded reproducibility is guaranteed, but upstream implementations' exact random sequences are not. Blu/Luau profiles also expose math.clamp, math.sign, and math.round with the pinned Luau edge behavior, plus numeric classification and math.lerp/math.map interpolation helpers and deterministic math.noise. The bit32 library provides variadic AND/OR/XOR, NOT, shifts, rotates, field extraction, and field replacement in Blu, Luau, Lua 5.2, and Lua 5.3 profiles, with explicit profile-specific input conversion and result subtypes. Legacy table.getn and table.maxn are also profile-gated to the pinned versions that retain them, while Blu exposes both for migration. The Lua 5.1 table.foreach and table.foreachi callbacks are available in the Lua 5.1 and Blu profiles; callback results short-circuit iteration, and owned callbacks can yield and resume without restarting iteration. pairs honors profile-available __pairs handlers, including resumable owned handlers. The legacy gcinfo memory counter is likewise available only in Blu, Luau, and Lua 5.1 profiles. Core tonumber conversion preserves profile subtypes, hexadecimal integer and floating strings, and the explicit-base grammar and overflow behavior of each profile. Byte-oriented string.find supports literal searches, relative starts, empty needles, nil misses, explicit plain mode, basic anchors, wildcard bytes, portable byte classes, class negation, and escaped punctuation under a work limit. The %g graph class follows modern profiles while Lua 5.1 preserves its literal escape semantics. Bracket sets support byte ranges, classes, and negation. Unimplemented Lua-pattern syntax fails structurally instead of being treated as literal text. All four Lua repetition suffixes are bounded: greedy */+/? and minimal -. string.find and string.match return bounded nested substring and position captures from the same byte-pattern engine, including %1 through %9 backreferences to completed substring captures and bounded %bxy nested byte pairs. Zero-width %f[set] byte frontiers share the bracket-set engine. string.gmatch returns a stateful function iterator with the same bounded pattern and capture behavior, including correct empty-match progress. string.gsub adds bounded string, number, direct-table, and function replacement, %0, %1%9, %%, empty-match progress, replacement counts, and profile-specific Lua 5.1 escape handling. Synchronous table-__index replacement handlers are included. Owned coroutine callbacks can now yield once per match and resume with explicit operation state and GC roots; other native library callbacks remain pending. Blu/Luau string.split produces bounded byte-string arrays with Luau-compatible default, empty-separator, consecutive-separator, and empty-field behavior. Blu/Luau table.create and table.find provide bounded preallocation/fill and raw array search with profile-typed result indices. table.clear retains allocation while removing entries, and table.clone performs a bounded shallow copy with unprotected metatable preservation. table.freeze/table.isfrozen enforce shallow immutability through every heap mutation path; clones of frozen tables remain mutable. coroutine.running and coroutine.isyieldable dispatch on the executing artifact profile, including Lua 5.1 main-thread nil and Luau main-thread yieldability. The base library exposes GC-safe collectgarbage("collect") and accounted-heap collectgarbage("count"); other version-specific commands fail explicitly. table.sort provides bounded, exact default ordering for uniform number and byte-string sequences, plus custom comparator callbacks and metamethod ordering. Owned comparators and ordering metamethods can yield and resume with explicit sort state and GC roots. Overlap-safe bounded table.move is available for Blu, Luau, and Lua 5.3–5.5, with explicit rejection in Lua 5.1–5.2 profiles. Ordinary bytecode calls use a bounded explicit VM frame stack; saved callers remain GC roots. Initial generational coroutine threads implement create/resume/yield/status/wrap/running/isyieldable/close, including nested calls, resume arguments, successful protected-call suspension, resumed pcall/xpcall error unwinding, yielding error handlers, and GC-traced continuations. Owned BluV1 coroutine entry closures also suspend, resume repeatedly, and retain their frames through collection; native library operations that invoke yielding callbacks still need operation-specific continuations. Owned sole-call returns replace the current closure frame, providing proper tail recursion independently of the configured ordinary call-depth limit. Portable V1 package envelopes provide bounded canonical decoding, SHA-256 identity, explicit dialect and authority requirements, and an opaque validated bytecode payload. The public engine currently executes only dialect-matched packages without imports when their authority profile and exact capability requirements are covered by the host policy; service linking still fails explicitly until its host bindings exist. The default legacy engine selects blu; --!dialect directives are checked against the configured engine. The public Engine::execute_owned_source entry point now exposes the bounded Blu-owned baseline for all seven profiles, including Lua 5.1–5.5, while the legacy bytecode path still rejects those profiles. This is meaningful execution coverage, not yet a claim of complete Luau, Lua, or Blu compatibility.

The first Blu-owned frontend substrate is also present: blu-syntax performs bounded byte-oriented lexing and parses the initial local/assignment-list/return arithmetic slice, including nil, boolean, shared decimal integers plus digit-bearing fraction/exponent forms (1.5, .25, 2e3, 4.5e-2), and quoted or long-bracket byte-string literals. Quoted strings implement the shared escapes plus explicit profile rules for byte, Unicode, whitespace, and line-continuation escapes; long strings use profile-specific newline handling plus semicolon separators, grouping parentheses, and profile-neutral +/-/*///%/^ plus right-associative .., into a spanned arena AST with explicit profile reconciliation. Unary not follows common Lua truthiness and produces a boolean under every profile; unary - preserves integers in Blu and Lua 5.3–5.5 and negates numbers in the other profiles. Unary # measures byte strings, returning an integer for Blu and Lua 5.3–5.5 and a number elsewhere, and executes raw or resumable metamethod-aware table length. Exponentiation follows the shared right-associative precedence above unary operators and always produces a number. Blu and Lua 5.3–5.5 source profiles support 64-bit integer &, |, binary and unary ~, <<, and >>, including Lua precedence and reversed negative shifts. Luau and Lua 5.1–5.2 reject this syntax during lexing. Blu and Luau additionally support +=, -=, *=, /=, //=, %=, ^=, and ..= statements. Indexed receivers and keys and the previous value are evaluated exactly once before the right-hand expression. Hexadecimal integers are accepted in every profile. Blu and Lua 5.3–5.5 use wrapping 64-bit integer representation; Luau and Lua 5.1–5.2 use numbers. Blu fitting decimal integers and Blu-only binary integers are likewise exact 64-bit values. Internal numeric separators are accepted only by Blu and Luau. Binary integers are also available only in Blu and Luau. Hexadecimal floats and their profile matrix are supported: exponent-only forms work in Blu and Lua 5.1–5.5, fractional forms work in Blu and Lua 5.2–5.5, and Luau rejects both. Arithmetic accepts whitespace-trimmed decimal and hexadecimal numeric strings under every profile. Blu and Lua 5.4–5.5 preserve exact parsed integers; Luau and Lua 5.1–5.3 produce numbers from string operands. Other profile-specific numeral extensions remain explicitly unsupported. Quoted strings support decimal byte escapes in every profile. Two-digit hexadecimal byte escapes are available in Blu, Luau, and Lua 5.2–5.5; malformed or out-of-range byte escapes are rejected structurally. Those same profiles support \z, which removes every following ASCII whitespace byte, including line breaks. Lua 5.1 rejects it explicitly. Every profile supports backslash line continuation; LF, CRLF, and CR source line endings normalize to one LF byte in the resulting string. Unicode escapes are byte-oriented and explicitly versioned: Blu, Luau, and Lua 5.3–5.5 accept \u{...} through 0x10ffff; Blu and Lua 5.4–5.5 additionally accept the upstream extended UTF-8 range through 0x7fffffff. Lua 5.1/5.2 reject the syntax. Trailing-dot forms such as 1. and 1.e2 are accepted; as in the pinned runtimes, 1..2 is malformed and must be spaced before future concatenation. blu_compiler::owned::OwnedCompiler resolves and lowers that slice into canonical BluV1 artifacts without native linkage or fallback; the same explicit-profile API is available from the public facade as blu_lang::frontend. Engine::execute_owned_compilation directly executes the single-prototype scalar baseline slice for every declared profile without the Luau compiler or bytecode translator. It revalidates the consumed artifact under caller-supplied execution limits. Canonical register moves preserve arbitrary scalar return lists without numeric coercion. BluV1 floor division executes with Luau number semantics and Blu/Lua 5.3–5.5 integer-preserving modern semantics. The owned path also directly executes profile-neutral ==, ~=, <, <=, >, and >=; ordered comparisons accept only compatible numbers or byte strings, while equality between unlike scalar types is false. Mixed integer/number comparisons remain exact across the full 64-bit range instead of first converting integers to f64; NaN is unequal and unordered. Operand-returning and and or use validated forward branches and preserve short-circuit evaluation in every profile. Structured if/elseif/else blocks execute through the same validated forward control flow, retain branch-local lexical scope, and support path-terminating returns. Shared do/end blocks provide explicit lexical scope without adding a control-flow branch; unreachable statements after an unconditional nested return are not emitted. Block-scoped while loops add separately feature-gated backward branches; validation records the target's definite-initialization state, and runtime execution remains subject to the VM instruction limit. Blu and Lua 5.2–5.5 also support owned ::label:: declarations and validated same-scope goto branches. Cross-scope jumps remain rejected until explicit upvalue-closing control flow is available. Embedders can also request persistent cooperative interruption from another thread through Vm::interrupt_handle or Engine::interrupt_handle; both execution engines stop with a structured error at an instruction boundary. Absolute wall-clock deadlines use the same safe boundary and remain distinct from deterministic instruction fuel. Native callbacks can query the active artifact profile and cooperatively poll both conditions through the public VM API. Coroutine task states have a separate live-count limit that collects unreachable tasks before rejecting growth. Shared break statements are structurally restricted to loop bodies and patch only the innermost loop's exit, including through nested conditional blocks. continue is an explicit Blu/Luau extension that restarts the innermost loop; Lua 5.1–5.5 profiles reject it during lexing. Profile-neutral repeat/until loops execute their body at least once and retain body locals through the trailing condition. In Blu and Luau, continue in a repeat loop transfers to that trailing condition. Shared numeric for loops snapshot their controls once, use the profile's numeric representation for the implicit positive unit step, and scope the index to the loop. Explicit, provably nonzero numeric-literal steps support both directions. Literal zero follows the pinned Lua 5.1–5.3 and Luau non-positive classification; Blu remains unassigned and Lua 5.4–5.5 reject zero explicitly. Dynamic steps execute for Luau and Lua 5.1–5.3 with single-evaluation snapshots and runtime direction selection; Blu and Lua 5.4–5.5 reject them until their possible zero case is executable. Generic for executes iterator/state/control triples in every owned profile, including bounded final-call adjustment, lexical result variables, and nil-only termination. Lua 5.4–5.5 also preserve the fourth to-be-closed control and close it on normal loop exit and break. Lua 5.4/5.5 <const> and <close> local attributes are parsed; const writes are rejected and close locals run their __close handler on normal scope exit, break, return, goto, and protected errors, including reverse-order cleanup and resumable yielding handlers. Full finalizer/GC and abandoned-coroutine semantics remain incomplete. Canonical BluV1 global loads and stores connect the owned frontend to the VM's embedding registry. Unknown scalar reads produce nil, and scalar writes persist in the VM; Lua 5.2–5.5 owned chunks additionally use a rooted default environment synchronized with that registry, while explicit _ENV tables are captured through nested closures. Identifier assignment lists can mix locals, captures, and globals while preserving simultaneous assignment. Owned Lua 5.2–5.5 source execution installs an environment-aware load: string chunks return callable closures rooted to the supplied fourth-argument table (or the default environment), and Engine::load_owned_source exposes the same closure primitive to embedders. Complete environment-rebinding APIs remain unsupported. The Lua 5.1 owned path additionally supports string loadstring plus function-targeted getfenv/setfenv; stack-level environment rebinding remains unsupported; owned load accepts bounded string-producing reader functions, while yielding readers, binary chunks, and exact mode-string behavior remain unsupported. The owned frontend also supports bounded table constructors with sequential array, identifier-keyed, and bracket-keyed fields, plus bracket and dot-name reads and single-target writes. These execute directly through the generational heap, return nil for absent keys, and retain active registers as GC roots during allocation and table growth. Mixed identifier/index/field assignment lists snapshot every target and right-hand side before committing writes. Table and method reads follow __index table chains or resumably invoke closure/native handlers; writes likewise follow __newindex chains or handlers. Binary arithmetic dispatches __add, __sub, __mul, __div, __mod, __pow, and dialect-gated __idiv through the same bounded continuation path; unary negation likewise dispatches __unm. Final-field vararg and call MULTRET expansion are implemented. Concatenation invokes left-then-right __concat handlers through a resumable continuation when string/number coercion is unavailable. Comparison continuations preserve profile-specific handler selection and Lua 5.5's removal of reversed-__lt fallback. Operator event values may themselves be bounded callable-table chains; their final Blu closures remain on the explicit continuation stack. Unary # measures raw table sequences in Lua 5.1. Other profiles resumably invoke a present table __len closure or native handler and otherwise use the raw sequence length. Bounded postfix calls evaluate the callee and fixed scalar arguments left-to-right and dispatch through the VM's existing closure/native/table-call path. Scalar contexts produce the first result or nil; a final call in a local or identifier assignment list requests the remaining bounded result count, truncating excess results and padding missing results with nil. Call statements support side-effecting APIs such as print. A final call or method call in a return statement forwards every result. Sole Blu closure calls replace the current frame; preceding fixed return values remain in a GC-rooted bounded continuation and are prepended after the call completes. Callable tables resolve bounded __call chains, prepend every table receiver, and enter Blu closure handlers through those same continuations. Final call arguments expand every result (target(prefix, producer())) through a GC-rooted continuation; non-final calls still collapse to one value, and nested forwarding preserves left-to-right evaluation. Remaining resumable callbacks stay explicit later work. Owned variadic functions support scalar and fixed-width ... reads with nil padding, dynamic return forwarding, and dynamic final call arguments, and final table-constructor expansion, including fixed prefixes and method receivers; active and suspended varargs remain GC roots. Final calls in table constructors expand every result through a GC-rooted resumable table-fill continuation for both Blu closures and native functions. The older Engine::execute source path continues to use the pinned Luau compatibility compiler while the owned grammar and executor are expanded.

Repository layout

  • blu-lang: public facade crate for embedding Blu.
  • blu-core: dependency-free semantic profiles, source identities, byte spans, and diagnostics.
  • blu-syntax: bounded byte lexer and initial parser/AST for the Blu-owned frontend.
  • blu-compiler: safe-Rust BluV1 compiler slice, with an opt-in legacy Luau compiler adapter.
  • blu-bytecode: bounded BluV1 artifacts plus versioned Luau decoding and loading.
  • blu-package: bounded canonical package envelopes and artifact validation.
  • blu-runtime: values, heap, interpreter, interruption, and Rust host API.
  • blu-conformance: differential execution against pinned Luau and Lua runtimes.
  • .upstream/luau: ignored checkout created by just upstream.

The Rust core, syntax, bytecode, package, runtime, facade, and conformance crates forbid unsafe Rust at the crate level. blu-compiler builds its owned compiler without native dependencies by default. Its opt-in legacy-luau feature contains the isolated boundary for the pinned upstream Luau C++ compiler; a noexcept shim translates native exceptions and owns allocation/deallocation across that boundary. The current blu-lang facade and conformance runner enable that compatibility feature explicitly.

Development

just upstream
just test
just conformance

See NOTICE.md for upstream attribution and UPSTREAM.toml for compatibility revisions. The intended compatibility and authority model is defined in docs/language-contract.md. The explicit profile backlog is tracked in docs/dialect-matrix.md, and the Blu-owned frontend decision is recorded in ADR 0002.

Rust applications should depend on the blu-lang crate. The bare blu name on crates.io belongs to an unrelated project.

use blu_lang::{Engine,Value};let values = Engine::default().execute("return 20 + 22").expect("valid Blu source");assert_eq!(values, vec![Value::Number(42.0)]);

Intended embedders

The core runtime is application-neutral and does not depend on Borg. It is intended for:

  • extensible terminal and desktop applications;
  • game engines and simulation tools;
  • command-line automation;
  • servers and edge runtimes;
  • build, workflow, and configuration systems;
  • editors and developer tools;
  • agent and orchestration platforms.

Application adapters such as a future blu-borg crate belong outside the core runtime and use the same embedding API available to third-party applications.

License

Blu is free and open-source software, available under the MIT License.

About

Lua / Luau superset with a fast Rust runtime

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Latest commit

History

200 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Blu: A language extending Lua with a fast Rust runtime

Blu is a fast, embeddable Lua/Luau superset language and runtime written in Rust. It is built for deeply extensible native applications. Its default blu dialect pragmatically unifies and extends Luau and modern Lua; explicit compatibility dialects preserve the exact semantics of each upstream language version where those semantics conflict.

Borg Agent embedding, package authority, and customization boundaries are documented in docs/borg-embedding.md. Blu owns the language runtime; the Borg host owns extension admission and capabilities.

Blu starts from Luau's optimized language and VM design, but it is not confined to Roblox's sandboxed Luau surface. Blu supports explicit Luau and Lua compatibility modes alongside a first-class systems profile with io, os, packages, filesystem, network, and process capabilities.

Blu is currently under active compatibility development. The initial milestone is a safe Luau-bytecode loader and interpreter continuously checked against a pinned upstream Luau revision. Lua 5.1–5.5 source profiles, standard libraries, and versioned C API bridges follow behind the same differential conformance gates.

The current Luau-backed Blu path compiles source in-process and covers scalar and table operations, numeric and generic loops, closures and mutable upvalues, variadic and multiple-return calls, globals and imports, Rust native functions, protected calls, pairs/ipairs/next, string method dispatch, core metamethods, an initial standard library, and host-configured cached require. The owned Blu/Luau frontend also lowers value-selecting if ... then ... elseif ... else ... expressions with short-circuit branches; Lua profiles reject that Luau syntax explicitly. The contextual _VERSION global reports Blu, Luau, or the selected Lua 5.1Lua 5.5 profile and remains explicitly overridable by the guest or embedder. The math slice includes shared truncating math.fmod; unlike floor-based %, its remainder preserves the dividend's sign, with modern integer preservation and zero-divisor errors. Shared math.pow provides the library form of floating exponentiation, while math.frexp and math.ldexp split and compose binary exponents with profile-appropriate exponent subtypes and conversion. The legacy sinh/cosh/tanh/log10/atan2 names remain available in Blu, Luau, and Lua 5.1–5.4 profiles and fail explicitly in Lua 5.5, which removed them. math.atan follows the legacy one-argument Luau/Lua 5.1–5.2 contract or modern Blu/Lua 5.3–5.5 atan2(y, x) contract according to the active profile; math.asin and math.acos follow the shared numeric contract. Profile-aware math.floor and math.ceil and the integral result of two-result math.modf preserve number-only legacy behavior or return modern exact integers when representable. math.abs preserves modern integer inputs, while math.log explicitly distinguishes Lua 5.1's ignored extra arguments from modern base selection. math.min and math.max preserve the selected modern subtype and upstream NaN ordering, using exact mixed integer/number comparisons across i64. Modern-profile math.type, math.tointeger, and math.ult provide numeric subtype introspection, exact integral conversion, and unsigned comparison. Profile-aware math.random and math.randomseed provide a deterministic, non-cryptographic generator with explicit legacy coercion, result-subtype, zero-bound, seed-arity, and seed-return contracts; seeded reproducibility is guaranteed, but upstream implementations' exact random sequences are not. Blu/Luau profiles also expose math.clamp, math.sign, and math.round with the pinned Luau edge behavior, plus numeric classification and math.lerp/math.map interpolation helpers and deterministic math.noise. The bit32 library provides variadic AND/OR/XOR, NOT, shifts, rotates, field extraction, and field replacement in Blu, Luau, Lua 5.2, and Lua 5.3 profiles, with explicit profile-specific input conversion and result subtypes. Legacy table.getn and table.maxn are also profile-gated to the pinned versions that retain them, while Blu exposes both for migration. The Lua 5.1 table.foreach and table.foreachi callbacks are available in the Lua 5.1 and Blu profiles; callback results short-circuit iteration, and owned callbacks can yield and resume without restarting iteration. pairs honors profile-available __pairs handlers, including resumable owned handlers. The legacy gcinfo memory counter is likewise available only in Blu, Luau, and Lua 5.1 profiles. Core tonumber conversion preserves profile subtypes, hexadecimal integer and floating strings, and the explicit-base grammar and overflow behavior of each profile. Byte-oriented string.find supports literal searches, relative starts, empty needles, nil misses, explicit plain mode, basic anchors, wildcard bytes, portable byte classes, class negation, and escaped punctuation under a work limit. The %g graph class follows modern profiles while Lua 5.1 preserves its literal escape semantics. Bracket sets support byte ranges, classes, and negation. Unimplemented Lua-pattern syntax fails structurally instead of being treated as literal text. All four Lua repetition suffixes are bounded: greedy */+/? and minimal -. string.find and string.match return bounded nested substring and position captures from the same byte-pattern engine, including %1 through %9 backreferences to completed substring captures and bounded %bxy nested byte pairs. Zero-width %f[set] byte frontiers share the bracket-set engine. string.gmatch returns a stateful function iterator with the same bounded pattern and capture behavior, including correct empty-match progress. string.gsub adds bounded string, number, direct-table, and function replacement, %0, %1%9, %%, empty-match progress, replacement counts, and profile-specific Lua 5.1 escape handling. Synchronous table-__index replacement handlers are included. Owned coroutine callbacks can now yield once per match and resume with explicit operation state and GC roots; other native library callbacks remain pending. Blu/Luau string.split produces bounded byte-string arrays with Luau-compatible default, empty-separator, consecutive-separator, and empty-field behavior. Blu/Luau table.create and table.find provide bounded preallocation/fill and raw array search with profile-typed result indices. table.clear retains allocation while removing entries, and table.clone performs a bounded shallow copy with unprotected metatable preservation. table.freeze/table.isfrozen enforce shallow immutability through every heap mutation path; clones of frozen tables remain mutable. coroutine.running and coroutine.isyieldable dispatch on the executing artifact profile, including Lua 5.1 main-thread nil and Luau main-thread yieldability. The base library exposes GC-safe collectgarbage("collect") and accounted-heap collectgarbage("count"); other version-specific commands fail explicitly. table.sort provides bounded, exact default ordering for uniform number and byte-string sequences, plus custom comparator callbacks and metamethod ordering. Owned comparators and ordering metamethods can yield and resume with explicit sort state and GC roots. Overlap-safe bounded table.move is available for Blu, Luau, and Lua 5.3–5.5, with explicit rejection in Lua 5.1–5.2 profiles. Ordinary bytecode calls use a bounded explicit VM frame stack; saved callers remain GC roots. Initial generational coroutine threads implement create/resume/yield/status/wrap/running/isyieldable/close, including nested calls, resume arguments, successful protected-call suspension, resumed pcall/xpcall error unwinding, yielding error handlers, and GC-traced continuations. Owned BluV1 coroutine entry closures also suspend, resume repeatedly, and retain their frames through collection; native library operations that invoke yielding callbacks still need operation-specific continuations. Owned sole-call returns replace the current closure frame, providing proper tail recursion independently of the configured ordinary call-depth limit. Portable V1 package envelopes provide bounded canonical decoding, SHA-256 identity, explicit dialect and authority requirements, and an opaque validated bytecode payload. The public engine currently executes only dialect-matched packages without imports when their authority profile and exact capability requirements are covered by the host policy; service linking still fails explicitly until its host bindings exist. The default legacy engine selects blu; --!dialect directives are checked against the configured engine. The public Engine::execute_owned_source entry point now exposes the bounded Blu-owned baseline for all seven profiles, including Lua 5.1–5.5, while the legacy bytecode path still rejects those profiles. This is meaningful execution coverage, not yet a claim of complete Luau, Lua, or Blu compatibility.

The first Blu-owned frontend substrate is also present: blu-syntax performs bounded byte-oriented lexing and parses the initial local/assignment-list/return arithmetic slice, including nil, boolean, shared decimal integers plus digit-bearing fraction/exponent forms (1.5, .25, 2e3, 4.5e-2), and quoted or long-bracket byte-string literals. Quoted strings implement the shared escapes plus explicit profile rules for byte, Unicode, whitespace, and line-continuation escapes; long strings use profile-specific newline handling plus semicolon separators, grouping parentheses, and profile-neutral +/-/*///%/^ plus right-associative .., into a spanned arena AST with explicit profile reconciliation. Unary not follows common Lua truthiness and produces a boolean under every profile; unary - preserves integers in Blu and Lua 5.3–5.5 and negates numbers in the other profiles. Unary # measures byte strings, returning an integer for Blu and Lua 5.3–5.5 and a number elsewhere, and executes raw or resumable metamethod-aware table length. Exponentiation follows the shared right-associative precedence above unary operators and always produces a number. Blu and Lua 5.3–5.5 source profiles support 64-bit integer &, |, binary and unary ~, <<, and >>, including Lua precedence and reversed negative shifts. Luau and Lua 5.1–5.2 reject this syntax during lexing. Blu and Luau additionally support +=, -=, *=, /=, //=, %=, ^=, and ..= statements. Indexed receivers and keys and the previous value are evaluated exactly once before the right-hand expression. Hexadecimal integers are accepted in every profile. Blu and Lua 5.3–5.5 use wrapping 64-bit integer representation; Luau and Lua 5.1–5.2 use numbers. Blu fitting decimal integers and Blu-only binary integers are likewise exact 64-bit values. Internal numeric separators are accepted only by Blu and Luau. Binary integers are also available only in Blu and Luau. Hexadecimal floats and their profile matrix are supported: exponent-only forms work in Blu and Lua 5.1–5.5, fractional forms work in Blu and Lua 5.2–5.5, and Luau rejects both. Arithmetic accepts whitespace-trimmed decimal and hexadecimal numeric strings under every profile. Blu and Lua 5.4–5.5 preserve exact parsed integers; Luau and Lua 5.1–5.3 produce numbers from string operands. Other profile-specific numeral extensions remain explicitly unsupported. Quoted strings support decimal byte escapes in every profile. Two-digit hexadecimal byte escapes are available in Blu, Luau, and Lua 5.2–5.5; malformed or out-of-range byte escapes are rejected structurally. Those same profiles support \z, which removes every following ASCII whitespace byte, including line breaks. Lua 5.1 rejects it explicitly. Every profile supports backslash line continuation; LF, CRLF, and CR source line endings normalize to one LF byte in the resulting string. Unicode escapes are byte-oriented and explicitly versioned: Blu, Luau, and Lua 5.3–5.5 accept \u{...} through 0x10ffff; Blu and Lua 5.4–5.5 additionally accept the upstream extended UTF-8 range through 0x7fffffff. Lua 5.1/5.2 reject the syntax. Trailing-dot forms such as 1. and 1.e2 are accepted; as in the pinned runtimes, 1..2 is malformed and must be spaced before future concatenation. blu_compiler::owned::OwnedCompiler resolves and lowers that slice into canonical BluV1 artifacts without native linkage or fallback; the same explicit-profile API is available from the public facade as blu_lang::frontend. Engine::execute_owned_compilation directly executes the single-prototype scalar baseline slice for every declared profile without the Luau compiler or bytecode translator. It revalidates the consumed artifact under caller-supplied execution limits. Canonical register moves preserve arbitrary scalar return lists without numeric coercion. BluV1 floor division executes with Luau number semantics and Blu/Lua 5.3–5.5 integer-preserving modern semantics. The owned path also directly executes profile-neutral ==, ~=, <, <=, >, and >=; ordered comparisons accept only compatible numbers or byte strings, while equality between unlike scalar types is false. Mixed integer/number comparisons remain exact across the full 64-bit range instead of first converting integers to f64; NaN is unequal and unordered. Operand-returning and and or use validated forward branches and preserve short-circuit evaluation in every profile. Structured if/elseif/else blocks execute through the same validated forward control flow, retain branch-local lexical scope, and support path-terminating returns. Shared do/end blocks provide explicit lexical scope without adding a control-flow branch; unreachable statements after an unconditional nested return are not emitted. Block-scoped while loops add separately feature-gated backward branches; validation records the target's definite-initialization state, and runtime execution remains subject to the VM instruction limit. Blu and Lua 5.2–5.5 also support owned ::label:: declarations and validated same-scope goto branches. Cross-scope jumps remain rejected until explicit upvalue-closing control flow is available. Embedders can also request persistent cooperative interruption from another thread through Vm::interrupt_handle or Engine::interrupt_handle; both execution engines stop with a structured error at an instruction boundary. Absolute wall-clock deadlines use the same safe boundary and remain distinct from deterministic instruction fuel. Native callbacks can query the active artifact profile and cooperatively poll both conditions through the public VM API. Coroutine task states have a separate live-count limit that collects unreachable tasks before rejecting growth. Shared break statements are structurally restricted to loop bodies and patch only the innermost loop's exit, including through nested conditional blocks. continue is an explicit Blu/Luau extension that restarts the innermost loop; Lua 5.1–5.5 profiles reject it during lexing. Profile-neutral repeat/until loops execute their body at least once and retain body locals through the trailing condition. In Blu and Luau, continue in a repeat loop transfers to that trailing condition. Shared numeric for loops snapshot their controls once, use the profile's numeric representation for the implicit positive unit step, and scope the index to the loop. Explicit, provably nonzero numeric-literal steps support both directions. Literal zero follows the pinned Lua 5.1–5.3 and Luau non-positive classification; Blu remains unassigned and Lua 5.4–5.5 reject zero explicitly. Dynamic steps execute for Luau and Lua 5.1–5.3 with single-evaluation snapshots and runtime direction selection; Blu and Lua 5.4–5.5 reject them until their possible zero case is executable. Generic for executes iterator/state/control triples in every owned profile, including bounded final-call adjustment, lexical result variables, and nil-only termination. Lua 5.4–5.5 also preserve the fourth to-be-closed control and close it on normal loop exit and break. Lua 5.4/5.5 <const> and <close> local attributes are parsed; const writes are rejected and close locals run their __close handler on normal scope exit, break, return, goto, and protected errors, including reverse-order cleanup and resumable yielding handlers. Full finalizer/GC and abandoned-coroutine semantics remain incomplete. Canonical BluV1 global loads and stores connect the owned frontend to the VM's embedding registry. Unknown scalar reads produce nil, and scalar writes persist in the VM; Lua 5.2–5.5 owned chunks additionally use a rooted default environment synchronized with that registry, while explicit _ENV tables are captured through nested closures. Identifier assignment lists can mix locals, captures, and globals while preserving simultaneous assignment. Owned Lua 5.2–5.5 source execution installs an environment-aware load: string chunks return callable closures rooted to the supplied fourth-argument table (or the default environment), and Engine::load_owned_source exposes the same closure primitive to embedders. Complete environment-rebinding APIs remain unsupported. The Lua 5.1 owned path additionally supports string loadstring plus function-targeted getfenv/setfenv; stack-level environment rebinding remains unsupported; owned load accepts bounded string-producing reader functions, while yielding readers, binary chunks, and exact mode-string behavior remain unsupported. The owned frontend also supports bounded table constructors with sequential array, identifier-keyed, and bracket-keyed fields, plus bracket and dot-name reads and single-target writes. These execute directly through the generational heap, return nil for absent keys, and retain active registers as GC roots during allocation and table growth. Mixed identifier/index/field assignment lists snapshot every target and right-hand side before committing writes. Table and method reads follow __index table chains or resumably invoke closure/native handlers; writes likewise follow __newindex chains or handlers. Binary arithmetic dispatches __add, __sub, __mul, __div, __mod, __pow, and dialect-gated __idiv through the same bounded continuation path; unary negation likewise dispatches __unm. Final-field vararg and call MULTRET expansion are implemented. Concatenation invokes left-then-right __concat handlers through a resumable continuation when string/number coercion is unavailable. Comparison continuations preserve profile-specific handler selection and Lua 5.5's removal of reversed-__lt fallback. Operator event values may themselves be bounded callable-table chains; their final Blu closures remain on the explicit continuation stack. Unary # measures raw table sequences in Lua 5.1. Other profiles resumably invoke a present table __len closure or native handler and otherwise use the raw sequence length. Bounded postfix calls evaluate the callee and fixed scalar arguments left-to-right and dispatch through the VM's existing closure/native/table-call path. Scalar contexts produce the first result or nil; a final call in a local or identifier assignment list requests the remaining bounded result count, truncating excess results and padding missing results with nil. Call statements support side-effecting APIs such as print. A final call or method call in a return statement forwards every result. Sole Blu closure calls replace the current frame; preceding fixed return values remain in a GC-rooted bounded continuation and are prepended after the call completes. Callable tables resolve bounded __call chains, prepend every table receiver, and enter Blu closure handlers through those same continuations. Final call arguments expand every result (target(prefix, producer())) through a GC-rooted continuation; non-final calls still collapse to one value, and nested forwarding preserves left-to-right evaluation. Remaining resumable callbacks stay explicit later work. Owned variadic functions support scalar and fixed-width ... reads with nil padding, dynamic return forwarding, and dynamic final call arguments, and final table-constructor expansion, including fixed prefixes and method receivers; active and suspended varargs remain GC roots. Final calls in table constructors expand every result through a GC-rooted resumable table-fill continuation for both Blu closures and native functions. The older Engine::execute source path continues to use the pinned Luau compatibility compiler while the owned grammar and executor are expanded.

Repository layout

  • blu-lang: public facade crate for embedding Blu.
  • blu-core: dependency-free semantic profiles, source identities, byte spans, and diagnostics.
  • blu-syntax: bounded byte lexer and initial parser/AST for the Blu-owned frontend.
  • blu-compiler: safe-Rust BluV1 compiler slice, with an opt-in legacy Luau compiler adapter.
  • blu-bytecode: bounded BluV1 artifacts plus versioned Luau decoding and loading.
  • blu-package: bounded canonical package envelopes and artifact validation.
  • blu-runtime: values, heap, interpreter, interruption, and Rust host API.
  • blu-conformance: differential execution against pinned Luau and Lua runtimes.
  • .upstream/luau: ignored checkout created by just upstream.

The Rust core, syntax, bytecode, package, runtime, facade, and conformance crates forbid unsafe Rust at the crate level. blu-compiler builds its owned compiler without native dependencies by default. Its opt-in legacy-luau feature contains the isolated boundary for the pinned upstream Luau C++ compiler; a noexcept shim translates native exceptions and owns allocation/deallocation across that boundary. The current blu-lang facade and conformance runner enable that compatibility feature explicitly.

Development

just upstream
just test
just conformance

See NOTICE.md for upstream attribution and UPSTREAM.toml for compatibility revisions. The intended compatibility and authority model is defined in docs/language-contract.md. The explicit profile backlog is tracked in docs/dialect-matrix.md, and the Blu-owned frontend decision is recorded in ADR 0002.

Rust applications should depend on the blu-lang crate. The bare blu name on crates.io belongs to an unrelated project.

use blu_lang::{Engine,Value};let values = Engine::default().execute("return 20 + 22").expect("valid Blu source");assert_eq!(values, vec![Value::Number(42.0)]);

Intended embedders

The core runtime is application-neutral and does not depend on Borg. It is intended for:

  • extensible terminal and desktop applications;
  • game engines and simulation tools;
  • command-line automation;
  • servers and edge runtimes;
  • build, workflow, and configuration systems;
  • editors and developer tools;
  • agent and orchestration platforms.

Application adapters such as a future blu-borg crate belong outside the core runtime and use the same embedding API available to third-party applications.

License

Blu is free and open-source software, available under the MIT License.

About

Lua / Luau superset with a fast Rust runtime

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages