REBOL 3 Docs Guide Concepts Functions Datatypes Errors
  TOC < Back Next >   Updated: 9-Nov-2010 Edit History  

REBOL 3 Concepts: Extensions: Example extensions

Editor note: This page has fallen out of date with recent changes.

Contents

Quick start package

You can find a small package that contains source files for getting started at: example REBOL extension source code and binary It's a 12K zip.

To try it: run REBOL R3 with the test-ext.r script and ext-test.dll file in the same directory.

A few notes to be aware of at this time:

A blank extension

Here's a blank extension that does nothing useful, but shows the general format:

#include "reb-c.h"
#include "reb-ext.h"

const char *init_block =
    "REBOL [\n"
        "Title: {Blank extension}\n"
        "Type: module\n"
        "Exports: [nothing]\n"
    "]\n"
    "nothing: command [{Does nothing.}]\n"
;

RXIEXT const char *RX_Init(int opts, RL_LIB *lib) {
    RL = lib;
    if (!CHECK_STRUCT_ALIGN) return 0;
    return init_block;
}

RXIEXT int RX_Quit(int opts) {
    return 0;
}

RXIEXT int RX_Call(int cmd, RXIFRM *frm, REBCEC *ctx) {
    return RXR_NONE
}

Format of the examples

Each example section below will show the REBOL code and the C code as separate sections. To fully understand the details of this code, read the extensions: making extensions section.

For example, if the REBOL module code is:

REBOL [
    Title: "Add two integers"
    Type: module
    Export: [addi]
]
addi: command [i1 [integer!] i2 [integer!]

and the C code is:

RXIEXT int RX_Call(int cmd, RXIFRM *frm, REBCEC *ctx) {
    RXA_INT64(frm,1) = RXA_INT64(frm, 1) + RXA_INT64(frm, 2);
    return RXR_VALUE;
}

The extension source code would be:

#include "reb-c.h"
#include "reb-ext.h"

const char *init_block =
    "REBOL [\n"
        "Title: {Add two integers}\n"
        "Type: module\n"
        "Export: [addi]\n"
    "]\n"
    "addi: command [i1 [integer!] i2 [integer!]\n"
;

RXIEXT const char *RX_Init(int opts, RL_LIB *lib) {
    RL = lib;
    if (!CHECK_STRUCT_ALIGN) return 0;
    return 0;
}

RXIEXT int RX_Call(int cmd, RXIFRM *frm, REBCEC *ctx) {
    RXA_INT64(frm,1) = RXA_INT64(frm, 1) + RXA_INT64(frm, 2);
    return RXR_VALUE;
}

A script to build the init block

Once you go beyond a simple init block, it gets tedious to maintain it as C strings. To make it easier to build the init_block text, we provide the make-ext.r script.

Using this script will convert the extension .r source file into a C data statement (as UTF-8). It will also create the exports block and will generate an enum constant for each command.

#include "reb-c.h"
#include "reb-ext.h"

#include "ext-data.h"

RXIEXT const char *RX_Init(int opts, RL_LIB *lib) {
    RL = lib;
    if (!CHECK_STRUCT_ALIGN) return 0;
    return init_block;
}

RXIEXT int RX_Call(int cmd, RXIFRM *frm, REBCEC *ctx) {
switch (cmd) {
case CMD_MY_CMD1:
    ...
case CMD_MY_CMD2:
    ...

Returning different values

Here is an example that shows how to return a few different values.

The REBOL module code is:

REBOL [
    Title: {Example}
    Type: module
    Exports: [t-unset t-none t-true t-false t-value t-block]
]

t-unset: command []
t-none: command []
t-true: command []
t-false: command []
t-value: command [v]
t-block: command [a b c]

The relevant C code is:

RXIEXT int RX_Call(int cmd, RXIFRM *frm, REBCEC *ctx) {
    switch (cmd) {
    case 0:
        return RXR_UNSET;

    case 1:
        return RXR_NONE;

    case 2:
        return RXR_TRUE;

    case 3:
        return RXR_FALSE;

    case 4:
        return RXR_VALUE; // return first arg

    case 5:
        return RXR_BLOCK; // 3 args become a block
    }
}

Math functions

Fibonacci and factorial

This extension provides two functions to compute the Fibonacci number and the factorial.

The REBOL extension module definition:

REBOL [
    Title: {Math functions}
    Type: module
    Exports: [fibonacci factorial]
]
fibonacci: command [n [integer!]]
factorial: command [n [integer!]]

The C code:

RXIEXT int RX_Call(int cmd, RXIFRM *frm, REBCEC *ctx) {
    switch (cmd) {

    case 0:  // fibonacci
    {
        i64 n, a, b, c, i;
        n = RXA_INT64(frm, 1);
        for (a = b = 1, i = 3; i <= n; i++) {
            c = b;
            b += a;
            a = c;
        }
        RXA_INT64(frm, 1) = b;
        RXA_TYPE(frm, 1) = RXT_INTEGER; // not strictly necessary
        break;
    }

    case 1:  // factorial
    {
        i64 n, i, f = 1;
        n = RXA_INT64(frm, 1);
        for (i = 2; i <= n; i++) f *= i;
        RXA_INT64(frm, 1) = f;
        RXA_TYPE(frm, 1) = RXT_INTEGER; // not strictly necessary
        break;
    }

    default:
        return RXR_NO_COMMAND;
    }
    return RXR_VALUE;
}

The test code:

print fibonacci 80
23416728348467685
print factorial 20
2432902008176640000

If you time these functions, you will find that they are about 25 times faster than interpreted code.

String functions

Checksum of characters

Here's an extension that provides a function to compute a 64 bit checksum on a string chars. It works for both types of strings (Latin-1 and Unicode).

The REBOL extension module definition:

REBOL [
    Title: {String 64 bit checksum}
    Type: module
    Exports: [sum-chars]
]
sum-chars: command [str [string!]]

The C code:

RXIEXT int RX_Call(int cmd, RXIFRM *frm, REBCEC *ctx) {
    switch (cmd) {

    case 0:
    {
        i32 idx, tail;
        i64 sum = 0;
        REBSER *ser;

        ser = RXA_SERIES(frm, 1);
        idx = RXA_INDEX(frm, 1);
        tail = RXI_SERIES_INFO(ser, RXI_INFO_TAIL);

        for (; idx < tail; idx++) {
            sum += RXI_GET_CHAR(ser, idx);
        }

        RXA_INT64(frm, 1) = sum;
        RXA_TYPE(frm, 1) = RXT_INTEGER;
        break;
    }

    // ...other command cases...

    default:
        return RXR_NO_COMMAND;
    }

    return RXR_VALUE;
}

Test code:

probe sum-chars "testing"
766
probe sum-chars to-string read http://www.rebol.com
751827

Reverse a string

Although REBOL provides the reverse function, here's a simple example that helps show how strings can be modified.

The REBOL extension module definition:

REBOL [
    Title: {String example}
    Type: module
    Exports: [reverse-str]
]
reverse-str: command [str [string!]]

The C code:

RXIEXT int RX_Call(int cmd, RXIFRM *frm, REBCEC *ctx) {
    switch (cmd) {

    case 0:
    {
        u32 idx, tail, chr;
        REBSER *ser;

        ser = RXA_SERIES(frm, 1);
        idx = RXA_INDEX(frm, 1);
        tail = RXI_SERIES_INFO(ser, RXI_INFO_TAIL);
        if (tail > 0) tail--;

        for (; idx < tail; idx++, tail--) {
            chr = RXI_GET_CHAR(ser, idx);
            RXI_SET_CHAR(ser, idx, RXI_GET_CHAR(ser, tail));
            RXI_SET_CHAR(ser, tail, chr);
        }

        break; // returns same string
    }

    default:
        return RXR_NO_COMMAND;
    }

    return RXR_VALUE;
}

Test code:

probe reverse-str ""
""
probe reverse-str "ab"
"ba"
probe reverse-str "abc"
"cba"
probe reverse-str "abcd"
"dcba"
probe head reverse-str next "ab"
"ab"
probe head reverse-str next "abc"
"acb"
probe head reverse-str next "abcd"
"adcb"

Block functions

Sum of numeric values

This extension shows how to handle values within a block. It provides a function that returns a sum of all integer and decimal values within the block. If the block contains other datatypes, they are ignored.

The REBOL extension module definition:

REBOL [
    Title: {Sum a block of integers and decimals}
    Type: module
    Exports: [sum-nums]
]
sum-nums: command [blk [block!]]

The C code:

RXIEXT int RX_Call(int cmd, RXIFRM *frm, REBCEC *ctx) {
    switch (cmd) {
    case 0:
    {
        i32 idx, tail, type;
        REBDEC sum = 0.0;
        REBSER *ser;
        RXIARG val;

        ser = RXA_SERIES(frm, 1);
        idx = RXA_INDEX(frm, 1);
        tail = RXI_SERIES_INFO(ser, RXI_INFO_TAIL);

        for (; idx < tail; idx++) {
            type = RXI_GET_VALUE(ser, idx, &val);
            if (type == RXT_INTEGER) sum += (REBDEC)val.int64;
            else if (type == RXT_DECIMAL) sum += val.dec64;
            // else skip it
        }

        RXA_DEC64(frm, 1) = sum;
        RXA_TYPE(frm, 1) = RXT_DECIMAL;
        break;
    }

    default:
        return RXR_NO_COMMAND;
    }
    return RXR_VALUE;
}

Here's some test code:

probe sum-nums []
0.0
probe sum-nums [1 2 3]
6.0
probe sum-nums [1 2.3 4 5.6]
12.9

Reverse a block

Although REBOL provides the reverse function, here's a simple example that helps show how blocks can be modified.

Note that this code only works for values that extensions are allowed to access. See the list in the prior section.

The REBOL extension module definition:

REBOL [
    Title: {Block example}
    Type: module
    Exports: [reverse-blk]
]
reverse-blk: command [blk [block!]]

The C code:

RXIEXT int RX_Call(int cmd, RXIFRM *frm, REBCEC *ctx) {
    switch (cmd) {

    case 0:
    {
        u32 idx, tail, type1, type2;
        REBSER *ser;
        RXIARG arg1, arg2;

        ser = RXA_SERIES(frm, 1);
        idx = RXA_INDEX(frm, 1);
        tail = RXI_SERIES_INFO(ser, RXI_INFO_TAIL);
        if (tail > 0) tail--;

        for (; idx < tail; idx++, tail--) {
            type1 = RXI_GET_VALUE(ser, idx, &arg1);
            type2 = RXI_GET_VALUE(ser, tail, &arg2);
            RXI_SET_VALUE(ser, idx, arg2, type2);
            RXI_SET_VALUE(ser, tail, arg1, type1);
        }

        break; // returns same block
    }

    default:
        return RXR_NO_COMMAND;
    }

    return RXR_VALUE;
}

The test code:

probe reverse-blk []
[]
probe reverse-blk [1]
[1]
probe reverse-blk [1 2]
[2 1]
probe reverse-blk [1 2 3]
[3 2 1]
probe reverse-blk [1 2 3 4]
[4 3 2 1]
probe head reverse-blk next [1 2]
[1 2]
probe head reverse-blk next [1 2 3]
[1 3 2]
probe head reverse-blk next [1 2 3 4]
[1 4 3 2]


  TOC < Back Next > REBOL.com - WIP Wiki Feedback Admin