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 afile!path type (e.g.,%C:/path/to/dir).cd target: Changes the working directory totarget(acceptstext!orfile!) and returns the new directory path.pushd target: Pushes the current directory onto the directory stack and changes directory totarget.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 offile!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/verboseor/v.rmdir path: Removes a directory if it is empty. Supports verbose printout refinements/verboseor/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.zipfile. 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.ziparchive 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 usingset-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.,
'TableStyleMedium9or'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
/prettyrefinement 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 |
; +---------+-----+-------------+