mruby

Embed mruby (lightweight Ruby) in Kit applications

Files

FileDescription
.editorconfigEditor formatting configuration
.gitignoreGit ignore rules for build artifacts and dependencies
.tool-versionsasdf tool versions (Zig, Kit)
BUILD.mdBuild instructions
LICENSEMIT license file
README.mdThis file
c/kit_mruby.cC FFI wrapper
c/kit_mruby.hC header for FFI wrapper
examples/basic.kitBasic usage example
examples/calculator.kitExample: calculator
kit.tomlPackage manifest with metadata and dependencies
src/mruby.kitkit-mruby: Embed mruby (Lightweight Ruby) in Kit
tests/mruby.test.kitTests for mruby

Dependencies

No Kit package dependencies.

Installation

kit add gitlab.com/kit-lang/packages/kit-mruby.git

Usage

import Kit.MRuby as MRuby

main = fn =>
  # Create a new mruby VM
  match MRuby.try-open()
    | Err e -> println "Failed to create VM: ${e}"
    | Ok vm ->
      # Ensure VM is closed when we exit this scope
      defer MRuby.close vm

      # Evaluate Ruby expressions
      result = MRuby.eval-string vm "2 + 2"
      println "2 + 2 = ${result}"

      # Get typed results
      n = MRuby.eval-int vm "10 * 5"
      println "10 * 5 = ${Int.to-string n}"

      is-empty = MRuby.eval-bool? vm "[].empty?"
      println "[].empty? = ${to-string is-empty}"

      # Define and call Ruby methods
      MRuby.define-method vm "square" "n" "n * n"
      squared = MRuby.call-with-int vm "square" 7
      println "square(7) = ${squared}"

      # Set and get global variables
      MRuby.set-global-int vm "$count" 42
      count = MRuby.eval-string vm "$count"
      println "$count = ${count}"

      # Execute multi-line Ruby with heredocs
      calculator = <<~RUBY
        class Calculator
          def self.add(a, b)
            a + b
          end
        end
      RUBY

      MRuby.exec vm calculator
      sum = MRuby.eval-string vm "Calculator.add(10, 20)"
      println "Calculator.add(10, 20) = ${sum}"

main

Development

Running Examples

Run examples with the interpreter:

kit run examples/basic.kit

Compile examples to a native binary:

kit build examples/basic.kit && ./basic

Running Tests

Run the test suite:

kit test

Run the test suite with coverage:

kit test --coverage

Running kit dev

Run the standard development workflow (format, check, test):

kit dev

This will:

  1. Format and check source files in src/
  2. Run tests in tests/ with coverage

Generating Documentation

Generate API documentation from doc comments:

kit doc

Note: Kit sources with doc comments (##) will generate HTML documents in docs/*.html

Cleaning Build Artifacts

Remove generated files, caches, and build artifacts:

kit task clean

Note: Defined in kit.toml.

Local Installation

To install this package locally for development:

kit install

This installs the package to ~/.kit/packages/@kit/mruby/, making it available for import as Kit.MRuby in other projects.

License

This package is released under the MIT License - see LICENSE for details.

mruby is also released under the MIT License.

Exported Functions & Types

MRubyError

MRuby error type for typed error handling.

Variants

MRubyInitError {message}
MRubyEvalError {message}
MRubyTypeError {message}

VM

Represents an mruby virtual machine instance. Create with open(), use with eval/exec, close with close().

Variants

VM {Ptr}

open

Creates a new mruby virtual machine.

Returns a VM instance that can execute Ruby code. Must be closed with close() when done.

VM

vm = MRuby.open()
# ... use vm ...
MRuby.close vm

try-open

Attempts to create a new mruby virtual machine.

Returns Ok with VM on success, Err on failure.

Result VM MRubyError

match MRuby.try-open()
  | Ok vm -> # use vm
  | Err e -> println "Failed to initialize mruby"

close

Closes an mruby virtual machine and frees resources.

Must be called when done with a VM to prevent memory leaks.

VM -> Unit

vm = MRuby.open()
MRuby.eval vm "puts 'hello'"
MRuby.close vm

exec

Executes Ruby code without returning a result.

Use this for statements that don't produce a value you need. Returns Ok on success, Err with error message on failure.

VM -> String -> Result () MRubyError

MRuby.exec vm "puts 'Hello, World!'"
MRuby.exec vm "def greet(name); puts \"Hello, \#{name}!\"; end"

eval-string

Executes Ruby code and returns the result as a string.

The Ruby expression is evaluated and converted to string via to_s. Returns the result string, or an error message if evaluation failed.

VM -> String -> String

result = MRuby.eval-string vm "2 + 2"
# result = "4"

result = MRuby.eval-string vm "[1, 2, 3].map { |x| x * 2 }"
# result = "[2, 4, 6]"

eval-int

Evaluates Ruby code and returns the result as an integer.

VM -> String -> Int

result = MRuby.eval-int vm "10 * 5"
# result = 50

eval-float

Evaluates Ruby code and returns the result as a float.

VM -> String -> Float

result = MRuby.eval-float vm "Math::PI"
# result = 3.141592...

eval-bool?

Evaluates Ruby code and returns the result as a boolean.

VM -> String -> Bool

result = MRuby.eval-bool? vm "5 > 3"
# result = true

try-eval

Safely evaluates Ruby code, returning Result.

Returns Ok with string result on success, Err with error message on failure.

VM -> String -> Result String MRubyError

match MRuby.try-eval vm "some_undefined_method"
  | Ok result -> println "Result: ${result}"
  | Err e -> println "Error: ${e}"

has-error?

Checks if the VM has a pending error.

VM -> Bool

MRuby.exec vm "raise 'oops'"
if MRuby.has-error? vm then
  println "An error occurred"

last-error

Gets the last error message, if any.

VM -> Option String

MRuby.exec vm "undefined_method"
match MRuby.last-error vm
  | Some msg -> println "Error: ${msg}"
  | None -> println "No error"

clear-error

Clears any pending error in the VM.

VM -> Unit

MRuby.exec vm "raise 'error'"
MRuby.clear-error vm

set-global-int

Sets a global Ruby variable to an integer value.

Global variables in Ruby start with $.

VM -> NonEmptyString -> Int -> Unit

MRuby.set-global-int vm "$count" 42
MRuby.exec vm "puts $count"  # prints 42

set-global-str

Sets a global Ruby variable to a string value.

VM -> NonEmptyString -> String -> Unit

MRuby.set-global-str vm "$name" "Kit"
MRuby.exec vm "puts \"Hello, \#{$name}!\""

set-global-float

Sets a global Ruby variable to a float value.

VM -> NonEmptyString -> Float -> Unit

MRuby.set-global-float vm "$pi" 3.14159
MRuby.exec vm "puts $pi * 2"

get-global-str

Gets a global Ruby variable as a string.

VM -> NonEmptyString -> String

MRuby.exec vm "$result = [1, 2, 3].sum"
value = MRuby.get-global-str vm "$result"
# value = "6"

version

Returns the mruby version string.

String

version = MRuby.version()
# e.g., "3.3.0"

description

Returns the mruby description string.

String

desc = MRuby.description()
# e.g., "mruby 3.3.0 (2024-02-14)"

run-script

Runs a Ruby script (multi-line code).

VM -> String -> Bool

script = <<~RUBY
def factorial(n)
  n <= 1 ? 1 : n * factorial(n - 1)
end
puts factorial(5)
RUBY
MRuby.run-script? vm script

define-method

Defines a Ruby method.

This is a convenience wrapper for defining methods. Returns Ok on success, Err with error message on failure.

VM -> NonEmptyString -> String -> String -> Result () MRubyError

MRuby.define-method vm "double" "x" "x * 2"
result = MRuby.eval-int vm "double(21)"
# result = 42

call

Calls a Ruby method by name with no arguments.

VM -> String -> String

MRuby.exec vm "def greet; 'Hello!'; end"
result = MRuby.call vm "greet"
# result = "Hello!"

call-with-str

Calls a Ruby method with a single string argument.

VM -> NonEmptyString -> String -> String

MRuby.exec vm "def greet(name); \"Hello, \#{name}!\"; end"
result = MRuby.call-with-str vm "greet" "World"
# result = "Hello, World!"

call-with-int

Calls a Ruby method with a single integer argument.

VM -> NonEmptyString -> Int -> String

MRuby.exec vm "def square(n); n * n; end"
result = MRuby.call-with-int vm "square" 7
# result = "49"