Welcome to Ragnar

A Rebol-inspired programming language hosted on .NET.

Ragnar is a programming language designed to combine the unique, expressive dialecting capabilities of Rebol with the power, performance, and library ecosystem of the .NET Platform.

Key Philosophies

  • Vibecoded & Lightweight: Designed for fun, expressiveness, and ease of scripting directly from your command line.
  • Fully Lexically Scoped: Unlike traditional Rebol, Ragnar's variables are lexically scoped and functions behave as true closures.
  • Tail-Call Optimized (TCO): Supports elegant recursive algorithms (including mutual recursion trampolining) without overflowing the stack.
  • Erlang-inspired Actor Model: Built-in support for concurrent, isolated actor processes communicating via message-passing.

Getting Started

Ragnar runs on .NET 10.0 and can be executed easily on Windows, macOS, or Linux.

Prerequisites

Ensure you have the .NET 10.0 Runtime (or SDK) installed on your machine.

Download

Download the latest package from the GitHub Releases page and extract the ZIP archive to a folder of your choice.

Running Ragnar

Ragnar features both an interactive REPL mode and the ability to execute script files and expressions directly via command-line arguments.

Command Line Options

Ragnar supports the following options and flags:

Usage: ragnar [options] [script-file] [script-args]

Options:
  -f, --file <path>       a ragnar file to be evaluated
  -e, --eval <string>     a ragnar expression to be evaluated
  --no-banner             do not print the banner
  --no-config             do not evaluate the user startup script
  --no-repl               do not start repl mode (just exit when evaluation is done)
  -v, --version           prints the version number and exits
  -h, --help              prints command line arguments help and exits

Interactive REPL

Launch the REPL by running the executable directly from your terminal:

# Windows
.\Ragnar.exe

# macOS / Linux
chmod +x Ragnar
./Ragnar

Executing Scripts & Expressions

You can execute a Ragnar script file directly by passing its path as a positional argument (Ragnar options parsing will stop at the script path, feeding subsequent inputs directly to the script):

# Executing a script positionally (with optional arguments)
# Windows
.\Ragnar.exe examples/git-check.r src/

# macOS / Linux
./Ragnar examples/git-check.r src/

Unix-like environments also support executing files directly using a shebang line (hashbang):

# Make the script executable
chmod +x examples/git-check.r

# Run the script directly
./examples/git-check.r src/

You can also evaluate a script file using the -f option:

# Windows
.\Ragnar.exe -f examples/pingpong.r

# macOS / Linux
./Ragnar -f examples/pingpong.r

Or evaluate an expression directly using the -e option (use --no-repl to prevent dropping into the REPL):

# Windows
.\Ragnar.exe -e "print 42" --no-repl

# macOS / Linux
./Ragnar -e "print 42" --no-repl

Multiple -f and -e targets can be specified in any order, and they will be evaluated in the order they are provided.

Script Example: Git Status Checker

Below is a working shell script example (available in examples/git-check.r). It accepts a target directory as a command line argument, checks if it is a git repository, and runs git status checks via the platform-aware call/output utility:

#!/usr/bin/env ragnar

args: system/options/args

; 1. Determine directory to check
target-dir: either empty? args ["."] [first args]

print rejoin ["Checking repository status in: " target-dir]

; 2. Ensure directory exists
either exists? to-file target-dir [
    cd to-file target-dir
] [
    print rejoin ["Error: Directory '" target-dir "' does not exist!"]
    quit/with 1
]

; 3. Check for .git directory
either exists? %.git [
    print "Git repository detected."
    
    ; Get branch name
    branch: trim call/output "git branch --show-current"
    print rejoin ["Current branch: " branch]
    
    ; Check status summary
    status: trim call/output "git status --short"
    either empty? status [
        print "Working tree is clean."
    ] [
        print "Uncommitted changes found:"
        print status
    ]
] [
    print "Not a git repository (no .git folder found)."
    quit/with 1
]

quit/with 0

Ragnar Koans

Ragnar features an interactive Koans mode to help users learn the language's syntax and concepts step-by-step.

Running the Koans

To start learning, simply start the Ragnar REPL and evaluate the following function:

>> start-koan-mode

This displays an interactive menu of the available koans. Select a topic by entering its number, and complete each exercise by filling in the missing code blank (represented as __). You can type q or quit at any point to exit and return to the main menu.

Configuration

When starting up, Ragnar looks for a configuration file named .ragnar.r in your user home directory. You can use it to configure your REPL prompt, set up common shortcuts, or print messages on startup.

Configuring the REPL

Here is an example showing how you can write a configuration script directly from the REPL:

>> config-path: join home rc-file-name
== %C:\Users\bob/.ragnar.r

>> write config-path mold/only [
..   me: "Bob"
..   print ["Hello" me "it is now" now/time]
..   system/console/prompt: "?? "
.. ]

The next time you restart your REPL, the message will print and your custom prompt will be displayed:

Hello Bob it is now 10:53:01 AM
REPL Mode (type 'quit' to exit)
?? me
== "Bob"
?? 

Reloading Configuration

You can force-reload your configuration file at any time by executing:

do join home rc-file-name

REPL & Reflection

Ragnar provides a robust interactive REPL for immediate evaluation, along with powerful reflection capabilities to inspect types, functions, and active word bindings.

Reflective Commands

Use reflective functions to discover the environment:

  • type?: Returns the datatype of a given value.
  • help (or shortcut ?): Inspects the definition, arguments, and details of a word.
  • what: Lists all available global functions and their brief descriptions.
  • probe: Prints a value to output and returns it (useful in chains).

REPL Interactive Session

>> type? 42
== integer!

>> type? "hello"
== text!

>> type? [1 2 3]
== block!

>> help add
WORD: add
TYPE:  Native Function
TITLE: Returns the sum of two values.
ARITY: 2
ARGS:  [ a b ]

>> what
add             Returns the sum of two values.
print           Prints a value to the output.
...

>> probe 10 + 20
30
== 30

REPL as a Shell

The Ragnar REPL integrates standard shell operations as mezzanine functions. Using C#/.NET interop, you can navigate directories, list contents, and manipulate files and folders directly from your interactive session.

Navigation & Working Directory

Manage your current directory context:

  • pwd / what-dir: Returns the current working directory as a file! path type (e.g., %C:/path/to/dir).
  • cd target: Changes the working directory to target (accepts text! or file!) and returns the new directory path.
  • pushd target: Pushes the current directory onto the directory stack and changes directory to target.
  • popd: Pops the last directory from the stack and navigates back to it.

Listing Directory Contents

Use ls to inspect directories:

  • ls: Returns a block of file! paths representing the contents of the current directory (directories are automatically suffixed with /). Filters out hidden and dot-prefixed files.
  • ls/all: Lists all entries in the current directory, including hidden and dot-prefixed files.

Directory Manipulation

Create and remove directories:

  • mkdir path: Recursively creates directory levels. Supports verbose printout refinements /verbose or /v.
  • rmdir path: Removes a directory if it is empty. Supports verbose printout refinements /verbose or /v.

File & Directory Operations

Move, copy, compress, or remove files:

  • cp src dest: Copies a file (or files via wildcards) to a destination file path or directory. Supports overwriting targets with /force (or /f) and verbose output with /verbose (or /v).
  • mv src dest: Moves or renames a file or directory. Supports overwriting targets with /force (or /f) and verbose output with /verbose (or /v).
  • rm path: Deletes files. Supports wildcards (e.g., %*.txt), confirmation prompts with /interactive (or /i), recursive folder removal with /recursive (or /r), and verbose output with /verbose (or /v).
  • zip archive sources: Compresses files or directories recursively into a .zip file. Supports overwriting with /force (or /f), verbose output with /verbose (or /v), and custom compression levels with /level lvl (optimal, fastest, none).
  • unzip archive dest: Extracts a .zip archive into the destination directory. Supports overwriting existing files with /force (or /f) and verbose output with /verbose (or /v).

Process Management

Start processes asynchronously and manage them:

  • call/pid command: Starts a process and returns its PID immediately as an integer, without waiting or capturing output.
  • proc-status pid: Checks if a process is alive and running (returns a logic value).
  • proc-kill pid: Terminates a process and all of its descendants (returns a logic value indicating success).

Example Session

>> pwd
== %C:/Users/username

>> cd %ragnar-projects/
== %C:/Users/username/ragnar-projects

>> mkdir/verbose %sandbox/sub-level
Creating directory: sandbox/sub-level

>> cd %sandbox
== %C:/Users/username/ragnar-projects/sandbox

>> ls
== [%sub-level/ %file.txt]

>> pushd %sub-level
== %C:/Users/username/ragnar-projects/sandbox/sub-level

>> popd
== %C:/Users/username/ragnar-projects/sandbox

>> cp/verbose %file.txt %file-copy.txt
Copying file: file.txt to file-copy.txt

>> mv/verbose %file.txt %sub-level/file-new.txt
Moving file: file.txt to sub-level/file-new.txt

>> zip/verbose %sandbox.zip %sub-level
Adding file: file-new.txt

>> unzip/force/verbose %sandbox.zip %extracted
Extracting: file-new.txt
== %C:/Users/username/ragnar-projects/sandbox/extracted

>> rm/recursive/verbose %sub-level
Removing file: sub-level/file-new.txt
Removing directory: sub-level
== none

>> pid: call/pid "ping 127.0.0.1 -n 10"
== 12345

>> proc-status pid
== true

>> proc-kill pid
== true

>> proc-status pid
== false

Core Language Features

Ragnar syntax is built around words, blocks, assignment, and conditional evaluation. Values inside blocks are not evaluated until explicitly requested, allowing you to use blocks as control structures or custom data formats.

Variable Assignment

name: "Ragnar"
age: 25

Conditionals & Logic

Conditionals in Ragnar use Rebol-style words:

  • if [cond] [block]: Evaluates block if condition is true.
  • either [cond] [true-block] [false-block]: If-else branching.
  • all [blocks...]: Returns true if all expressions are truthy.
  • any [blocks...]: Returns true if any expression is truthy.
either age > 18 [
    print "Adult"
] [
    print "Minor"
]
; Output: Adult

all [age > 18 name == "Ragnar"] ; returns true
any [age < 18 name == "Ragnar"] ; returns true

Loops and Collections

Iterate through blocks and manipulate values easily:

; Foreach loop
data: [10 21 30 43 50]
evens: []
foreach n data [
    if (n // 2) == 0 [ append evens n ]
]
; evens is now [10 30 50]

; Path navigation & Series manipulation
pick evens 1   ; returns 10 (1-indexed)
evens/2        ; returns 30 (path access)
select [a 1 b 2] 'b ; returns 2

.NET Interop

Ragnar is hosted on the .NET platform and features native, seamless integration with C# and the .NET Base Class Library (BCL). You can instantiate .NET classes, invoke instance or static methods, access or set properties, and iterate over any standard collection that implements IEnumerable.

1. Instantiation and Method Calls

Use the new native function to instantiate types. You can call instance methods using the call-method function:

; Instantiate a StringBuilder with initial text "Hello"
builder: new "System.Text.StringBuilder" ["Hello"]

; Call instance method to append text
call-method builder "Append" [" World"]

; Print the string value
print [call-method builder "ToString" []]
; Output: Hello World

2. Path Navigation Syntax

Ragnar supports path notation to access and mutate .NET fields, properties, and static members in a clean and readable format:

; Retrieve an instance property (Length)
len: builder/Length
print ["Length:" len] ; Output: 11

; Mutate an instance property
builder/Length: 5
print [call-method builder "ToString" []] ; Output: Hello

; Access a static property or field
pi: System.Math/PI
print ["PI value:" pi] ; Output: 3.141592653589793

3. Enumerating IEnumerables

Ragnar provides the enumerate mezzanine function to iterate through .NET collections that implement IEnumerable. You bind a block variable to each item and evaluate a body block:

; Retrieve a Dictionary containing environment variables
vars: call-static "System.Environment" "GetEnvironmentVariables" []

; Print the total count (property access)
print ["Total variables:" vars/Count]

; Iterate and print Key/Value properties
enumerate vars item [
    print ["Key:" item/Key "Value:" item/Value]
]

Objects Support

Objects in Ragnar are dynamic contexts containing key-value pairs (bindings). They allow you to bundle state and behavior. You can access and mutate fields using path notation, and perform dynamic scoping or lookup using in and bind.

Creating and Using Objects

Define objects using make object!:

square: make object! [
    side: 0
    area: does [ self/side * self/side ]
    perimeter: does [ 4 * self/side ]
    multiply: func [x] [
        self/side: x * self/side
    ]
]

; Set property
square/side: 3

; Retrieve property/method result
square/area      ; returns 9
square/perimeter ; returns 12

Dynamic Scoping

You can retrieve a word bound to an object context using the in keyword, which allows you to inspect or mutate that specific binding:

word: in square 'side
get word ; returns 3

Parse Dialect

One of Ragnar's most powerful features is its Rebol-style parsing engine. It supports both simple string splitting and complex dialect-based pattern matching with full backtracking.

1. Simple Splitting

Split a string by a simple delimiter:

parse "alice,30,engineer" ","
; == [ "alice" "30" "engineer" ]

2. Dialect Pattern Matching

Build complex grammars using Ragnar's parse dialect. You can match character sets, check sequence orders, and enforce rules:

; Define a set of digits
digits: charset "0123456789"

; Define a phone number rule (3 digits, a dash, and 4 digits)
phone-num: [3 digits "-" 4 digits]

; Match the rule against a string
parse "467-8000" phone-num
; == true

Visual Dialect (GUI)

Ragnar features a powerful, lightweight, cross-platform Visual Dialect (view). It spawns a local background HTTP/SSE server and automatically launches your application in the default web browser, allowing you to write reactive GUI scripts with minimal code.

1. Syntax & Examples

To declare a GUI, pass a layout block to the view function. Widgets can be styled, nested inside row or column blocks, and dynamically updated using get-face and set-face:

view [
    title "Ragnar Command Center v1.0"
    
    row [
        image "assets/ragnar-logo-small.png" 100
        heading "Ragnar Command Center"
    ]
    
    text "Configure system settings below:"
    
    row [
        text "Subsystem Target:"
        subsystem-choice: choice ["MAIN FRAME" "PROPULSION" "LIFE SUPPORT" "COMMUNICATIONS"] [
            target: get-face subsystem-choice
            set-face status-lbl rejoin ["Target Subsystem selected: " target]
        ]
    ]
    
    row [
        text "User Command:"
        cmd-field: field "ACTIVATE"
    ]
    
    row [
        text "Power Level:"
        pwr-slider: slider 75
    ]
    
    row [
        check-override: check "Override Safety Protocols" false
    ]
    
    status-lbl: text "System status: STANDBY"
    
    row [
        button "Execute Command" [
            target: get-face subsystem-choice
            cmd: get-face cmd-field
            level: get-face pwr-slider
            safety: get-face check-override
            
            status-msg: rejoin [
                "Target: " target
                " | Command: " cmd 
                " | Power: " level "%" 
                " | Safety: " either safety ["DISABLED"] ["ENABLED"]
            ]
            set-face status-lbl status-msg
        ]
        
        button "Reset System" [
            set-face subsystem-choice "MAIN FRAME"
            set-face cmd-field "ACTIVATE"
            set-face pwr-slider 75
            set-face check-override false
            set-face status-lbl "System status: STANDBY"
        ]
    ]
]

2. Supported Widgets

  • heading: Displays a styled title header text.
  • text: Standard inline text block.
  • field: Single-line text input field.
  • button: A clickable button that runs a block of Ragnar code on click.
  • check: A toggleable checkbox widget storing boolean values.
  • slider: An integer range input slider (value ranges 0 to 100).
  • choice: A dropdown list element (e.g. choice ["OptA" "OptB"]).
  • image: Renders an image widget (e.g. image "path.png" [width] [height]). Supports both URLs and local filepaths which are base64-encoded on-the-fly.
  • textarea: A multi-line text input/output block (e.g. textarea [rows] "initial value" where row count is optional).
  • spinner: A visual loading indicator spinner (e.g. spinner [true/false] to set default state, which can be toggled hidden or visible using set-face).

3. Visual Themes

Ragnar supports multiple visual themes. You can switch the active theme dynamically using set-theme 'theme-name. The active styles are shown below in the gallery:

Tail-Call Optimization (TCO)

Functional programming constructs often rely heavily on recursion. To prevent stack overflows, Ragnar implements tail-call optimization, allowing functions in the tail position to reuse the current stack frame.

Tail Recursion

Here is an implementation of a tail-recursive factorial function using a nested loop closure:

factorial: func [n] [
    loop: func [i accum] [
        either i > n [
            accum
        ] [
            loop (i + 1) (accum * i)  ; Recursion in tail position
        ]
    ]
    loop 1 1 
]

factorial 10  ; returns 3628800

Trampolining for Mutual Recursion

Ragnar also supports mutual recursion trampolining. Two or more functions calling each other in tail positions can run indefinitely without expanding the stack:

is-even?: func [n] [
    either n == 1 [ false ] [ is-odd? (n - 1) ]
]
is-odd?: func [n] [
    either n == 1 [ true ] [ is-even? (n - 1) ]
]

is-even? 10001  ; returns false
is-even? 10002  ; returns true

Actor Model Concurrency

Ragnar features a lightweight actor model implementation inspired by Erlang. Actors run on separate tasks and communicate strictly via asynchronous message-passing, removing the need for explicit threads and locks.

Actor Operations

  • spawn [block]: Starts a new actor process executing the block in the background.
  • receive: Blocks the calling actor until a message is sent to its mailbox. Returns a [sender message] block.
  • tell actor msg: Sends a message asynchronously to another actor's mailbox (implicitly packaging the sender as [sender message]).
  • kill actor: Terminates the target actor's thread.

Area Server Example

start-area-server: does [
    spawn [  ; Starts a new actor process
        forever [
            msg: receive  ; Wait for a message in mailbox, returns [sender message]
            client: first msg  ; Sender actor reference
            shape: second msg  ; Content of shape details
            
            switch/default first shape [
                rectangle [
                    tell client reform [
                        "area of rectangle is" (shape/2 * shape/3) ]
                ]
                circle [
                    tell client reform [ 
                        "area of circle is" (3.14159 * (shape/2 * shape/2)) ]
                ]
            ] [
                tell client reform [ 
                    "i don't know what the area of a" shape/1 "is." ] 
            ]
        ]
    ]
]

server: start-area-server
tell server [rectangle 5 10]
print ["Response:" second receive]
; Response: area of rectangle is 50

tell server [circle 5]
print ["Response:" second receive]
; Response: area of circle is 78.53975

tell server [triangle 5 10]
print ["Response:" second receive]
; Response: i don't know what the area of a triangle is.

kill server

Functional Programming

Ragnar treats functions as first-class citizens. It supports lexical closures, partial application, and function composition operations.

1. Lexical Closures

Functions capture the lexical environment of where they are defined, enabling state-carrying closures:

make-counter: func [start] [
    current: start
    func [] [       
        current: current + 1
    ]
]
counter: make-counter 10
counter ; returns 11
counter ; returns 12

2. Partial Application

Partially apply arguments to functions to create specialized variations using the partial keyword:

add-five: partial :add 5
add-five 10 ; returns 15

3. Function Composition

Combine multiple functions together using composition operators (forward >> and backward <<):

inc: func [n] [n + 1]
double: func [n] [n * 2]

f-forward: :inc >> :double  ; (x + 1) * 2
f-backward: :inc << :double ; (x * 2) + 1

f-forward 5  ; returns 12
f-backward 5 ; returns 11

4. Higher-Order Mezzanine Functions

Ragnar provides built-in higher-order functions to map, filter, and reduce collections:

; 1. map - Applies a function to each item
double: func [x] [x * 2]
map :double [1 2 3]  ; returns [2 4 6]

; 2. flatmap - Applies a function and flattens the results
expand: func [x] [reduce [x x * 10]]
flatmap :expand [1 2 3]  ; returns [1 10 2 20 3 30]

; 3. filter - Keeps items where the function returns true
even?: func [x] [x // 2 = 0]
filter :even? [1 2 3 4 5 6]  ; returns [2 4 6]

; 4. fold - Reduces a block using a binary function (optional /initial)
sum: func [a b] [a + b]
fold :sum [1 2 3 4]  ; returns 10
fold/initial :sum [1 2 3 4] 10  ; returns 20

SQL Server Library

Ragnar integrates seamlessly with .NET. By utilising standard .NET assemblies like Microsoft.Data.SqlClient, we can create libraries to communicate with SQL databases in just a few lines of code.

Library Source

The library (available in lib/sqlserver.r) wraps the ADO.NET SQL client connection and command processes:

make object! [
    connect: func [connection-string] [
        conn: new "Microsoft.Data.SqlClient.SqlConnection" [connection-string]
        conn/open
        conn
    ]

    query: func [connection query-text parameters] [
        cmd: connection/CreateCommand
        cmd/CommandText: query-text

        if not none? parameters [
            idx: 1
            while [idx <= length? parameters] [
                param-name: pick parameters idx
                param-val: pick parameters (idx + 1)
                clean-name: join "@" replace (to-string param-name) ":" ""
                cmd/Parameters/AddWithValue clean-name param-val
                idx: idx + 2
            ]
        ]

        reader: cmd/ExecuteReader
        result: copy []
        while [reader/Read] [
            row: copy []
            col-idx: 0
            while [col-idx < reader/FieldCount] [
                append row reader/GetName col-idx
                append row reader/GetValue col-idx
                col-idx: col-idx + 1
            ]
            append result row
        ]
        reader/close
        result
    ]
]

How to Use

Load the library into your script using do, open a connection, and execute parameterized queries:

; 1. Load the library
sql: do %lib/sqlserver.r

; 2. Open connection
conn: sql/connect "Data Source=localhost;Initial Catalog=master;Integrated Security=True"

; 3. Run query with parameters
result: sql/query conn {
    SELECT name, database_id
    FROM sys.databases
    WHERE state_desc = @state
} [
    state: "ONLINE"
]

; 4. Parse output
print ["Row count:" length? result] 
db1: first result ; Get first row
print ["Database Name:" select db1 "name" "ID:" select db1 "database_id"]

; 5. Close connection
conn/close

Excel Library

The Ragnar Excel library provides a clean wrapper around the ClosedXML .NET library, allowing you to read, write, format, and structure spreadsheets in pure Ragnar scripts.

How to Use

To use the library, load it via do, create/load workbooks, populate sheets, insert tables, style themes, and retrieve range data:

; 1. Load the library
excel: do %lib/excel.r

; 2. Create a new workbook and add a sheet
wb: excel/new
ws: wb/worksheets/add "Employee Data"

; 3. Write individual cell values
c1: ws/cell "A1"
c1/set-value "Staff List"

; 4. Insert bulk data starting from a cell
c2: ws/cell "A3"
tbl: c2/insert-data reduce [
    ["Name" "Department" "Active"]
    reduce ["Alice" "Engineering" true]
    reduce ["Bob" "Product" false]
    reduce ["Charlie" "Sales" true]
]

; 5. Set table styles using Word/LitWord themes
tbl/set-theme 'TableStyleMedium9

; 6. Save the workbook
wb/save-as %employees.xlsx

; 7. Load workbook back and retrieve data blocks
wb2: excel/load %employees.xlsx
ws2: wb2/worksheets/get "Employee Data"

data: ws2/get-data "A3:C6"
foreach row data [
    print ["Employee:" pick row 1 "Dept:" pick row 2 "Active:" pick row 3]
]

API Reference

Workbook Operations

  • excel/new: Instantiates a new Excel workbook.
  • excel/load [file]: Loads an existing Excel workbook from a file path.
  • wb/save-as [file]: Saves the workbook to the specified file path.

Worksheet Operations

  • wb/worksheets/add [name]: Adds a new worksheet with the given name to the workbook.
  • wb/worksheets/get [name]: Retrieves a worksheet by name.
  • ws/cell [address]: Returns a wrapped cell object at the specified address (e.g. "A1").
  • ws/range [address]: Returns a wrapped range object at the specified address (e.g. "A1:C5").
  • ws/get-data [address]: Retrieves the values of cells in a range as a block of rows (lists of native values).

Cell Operations

  • cell/set-value [val]: Sets the value of the cell. Supports numbers, strings, and booleans.
  • cell/get-value: Returns the native Ragnar value (or none) of the cell.
  • cell/insert-data [data-block]: Bulk-inserts a collection of collections (block of blocks) starting at the cell, converts the range to an Excel table, and returns a wrapped table object.

Table Operations

  • tbl/set-theme [theme-name]: Sets the visual style theme of the table (e.g., 'TableStyleMedium9 or 'TableStyleLight16).

JSON Library

Ragnar includes a built-in JSON library that supports serializing and deserializing JSON strings using Ragnar's parse dialect and a new record! data type.

The record! Datatype

JSON objects are represented in Ragnar as record! values (constructed using to-record or returned from JSON parsing). Records print with a #( ... ) prefix and hold key-value pairs sequentially, similar to block representation, but report type record! (for which record? is true and block? is false).

; Construct a record
r: to-record [name "Alice" age 30]
print [type? r] ; Output: record!

; Retrieve values by word key
print [select r 'name] ; Output: Alice
print [select r 'age]  ; Output: 30

; check type
record? r  ; returns true
block? r   ; returns false

Serialization & Deserialization

To use the library, load it via do %lib/json.r. It exposes two functions:

  • json/parse [json-string]: Parses a JSON string using Ragnar's native parse dialect, returning records for objects, blocks for arrays, and native datatypes for primitives.
  • json/stringify [value] /pretty: Serializes any Ragnar value to a JSON string. Use the /pretty refinement to produce formatted, indented JSON output.

How to Use

; 1. Load the library
json: do %lib/json.r

; 2. Parse JSON strings into Ragnar records and arrays
data: json/parse "{ \"name\": \"Alice\", \"skills\": [\"C#\", \"Ragnar\"], \"age\": 30 }"

print [select data 'name] ; Output: Alice
skills: select data 'skills
print [pick skills 2] ; Output: Ragnar

; 3. Stringify Ragnar structures back to JSON strings
ragnar-obj: to-record [
    title "Language Implementer"
    years 12
    languages ["Ragnar" "C#" "Rebol"]
]

; Standard stringification
str: json/stringify ragnar-obj
print [str]
; Output: {"title":"Language Implementer","years":12,"languages":["Ragnar","C#","Rebol"]}

; Pretty printed stringification
pretty-str: json/stringify/pretty ragnar-obj
print [pretty-str]
; Output:
; {
;     "title": "Language Implementer",
;     "years": 12,
;     "languages": [
;         "Ragnar",
;         "C#",
;         "Rebol"
;     ]
; }

Pretty Table Library

The Ragnar Pretty Table library stringifies series data (block of blocks or block of records) into a beautifully formatted ASCII table.

How to Use

To use the library, load it via do %lib/prettytable.r, which returns the table generator function. Column widths are dynamically calculated, and numeric types (integers and decimals) are right-aligned, while other types are left-aligned.

1. Formatting a Block of Blocks

When given a block of blocks, the library treats all blocks as regular data rows and prints top and bottom border separators:

; Load the library
make-table: do %lib/prettytable.r

data: [
    ["Name" "Age" "City"]
    ["Alice" 30 "New York"]
    ["Bob" 25 "Los Angeles"]
    ["Charlie" 35 "Chicago"]
]

print make-table data
; Output:
; +---------+-----+-------------+
; | Name    | Age | City        |
; | Alice   |  30 | New York    |
; | Bob     |  25 | Los Angeles |
; | Charlie |  35 | Chicago     |
; +---------+-----+-------------+

2. Formatting a Block of Records

When given a block of records, the first record's keys determine the column headers. A separator line is printed after the header row, followed by the record values in corresponding columns:

; Format records
print make-table map :to-record [
    ["Name" "Alice" "Age" 30 "City" "New York"]
    ["Name" "Bob" "Age" 25 "City" "Los Angeles"]
    ["Name" "Charlie" "Age" 35 "City" "Chicago"]
]
; Output:
; +---------+-----+-------------+
; | Name    | Age | City        |
; +---------+-----+-------------+
; | Alice   |  30 | New York    |
; | Bob     |  25 | Los Angeles |
; | Charlie |  35 | Chicago     |
; +---------+-----+-------------+