Skip to content

Latest commit

 

History

History
886 lines (602 loc) · 21.2 KB

libred.adoc

File metadata and controls

886 lines (602 loc) · 21.2 KB

LibRed API

目录

1. 摘要

LibRed 是 Red 解释器和运行时库的一个特殊版本,适合集成到 Red 之外的语言开发的软件里。为了允许非 Red 软件与 Red 交互,libRed 暴露了一个专用的底层 API(遵循 C 语言的 cdecl 或微软的 stdcall 标准),该 API 描述在该文档中。它支持的特性当中包含:

  • 从全局或局部语境中设置/获取一个单词的值的能力。

  • 大部分常见的 Red 数据类型的快捷构造器。

  • 与宿主语言(主要是 C 语言)兼容的 Red 数据类型的转换函数。

  • 由宿主语言发起的序列操纵。

  • 允许 Red 调用宿主语言函数的回调函数。

  • 面向控制台的调试函数。

术语:名词宿主(host)用来指宿主语言或嵌入了 libRed 的应用程序。

libRed 的使用样例可以从这里找到。

2. 构建 libRed

构建你的本地版本的 libRed 很简单:

red build libRed

或者由 Rebol 的控制台和 Red 的源码构建:

rc "build libRed"

这些命令行将会构建出用于 C 语言的版本的 libRed(采用 cdecl ABI)。如果你需要 stdcall ABI(为了兼容微软的应用),你需要使用:

red build libRed stdcall

3. 值引用

Red 值可以通过 libRed 函数调用被返回,以 32 位的不透明引用表示。这些引用的生命周期很短,所以它们只适合于在受限的局部范围内使用,比如传递那个引用到另一个 libRed 的函数调用。可以把这种引用设置给宿主变量,它应紧接其后被使用。这些引用使用一个特定的内存管理器,它将仅在接下来的大约 50 次 API 调用内保持引用有效。例:

long a, blk;

a = redSymbol("a");
redSet(a, redBlock(0));                   // 这里立即就使用了返回的引用

blk = redGet(a);
redPrint(blk);                            // 引用的使用是安全的

for(i = 0; i < 100, i++) {
    // redAppend(blk, redNone());         // 引用的使用不安全!
    redAppend(redGet("a"), redNone());    // 安全的版本
}

4. C API

C API 可以用在 C/C++ 应用程序中,也可以集成 Red 到任何其它有与 C 兼容的外部函数接口的编程语言中。

4.1. 管理库

为了使用 API 中的函数,需要创建 libRed实例

注意

当前,每个进程只允许单个 libRed 会话,预定未来会将其扩展为允许支持多实例。

4.1.1. redOpen()

void redOpen(void)

初始化一个新的 Red 运行时库会话。必须在调用任何其他 API 函数之前调用该函数。在同一个进程中调用多次是安全的,无论如何只会打开一个会话。

注意

如果在 redOpen 之前调用了另一个函数,那个函数的返回值将为 -2,表明这是一次非法的访问尝试。

4.1.2. redClose()

void redClose(void)

终止当前的 Red 运行时库会话,释放所有分配的资源。

4.2. 运行 Red 代码

宿主软件可以使用不同的控制级别来直接运行 Red 代码,上到提供文本形式的 Red 代码用于执行,下到直接调用任意 Red 函数并传递在宿主端构造的参数。

4.2.1. redDo()

red_value redDo(const char* source)

执行作为字符串传递的 Red 表达式并返回最后一个 Red 值。

redDo("a: 123");

redDo("view [text {hello}]");

char *s = (char *) malloc(100);
const char *caption = "Hello";
redDo(sprintf(s, "view [text \"%s\"]", caption));

4.2.2. redDoFile()

red_value redDoFile(const char* filename)

加载并执行以 filename 引用的 Red 脚本并返回最后一个 Red 值。filename 格式使用 Red 独立于操作系统的惯例(基本为 Unix 风格)。

redDoFile("hello.red");
redDoFile("/c/dev/red/demo.red");

4.2.3. redDoBlock()

red_value redDoBlock(red_block code)

执行参数区块并返回最后一个 Red 值。

redDoBlock(redBlock(redWord("print"), redInteger(42)));

4.2.4. redCall()

red_value redCall(red_word name, ...)

调用以 name 单词引用的 Red 函数(any-function 类型),传递任何所需的参数(作为 Red 值)。返回函数的最后一个 Red 值。参数列表必须null0 值终止,作为结束标记。

redCall(redWord("random"), redInteger(6), 0);     // 返回一个 1 到 6 之间的随机 integer! 值

4.3. 注册回调函数

响应在 Red 发生的事件或将一些 Red 的调用重定向到宿主端(如重定向 printask)需要一种从 Red 端调用宿主函数的方法。这可以使用 redRoutine() 函数来实现。

4.3.1. redRoutine()

red_value redRoutine(red_word name, const char* spec, void* func_ptr)

定义一个新的 Red 例程叫做 name,以 spec 作为规格区块,func-ptr C 函数指针做为主体。C 函数 必须 返回一个 Red 值(redUnset() 可以用来表示没有使用返回值)。

#include "red.h"
#include <stdio.h>

red_integer add(red_integer a, red_integer b) {
    return redInteger(redCInt32(a) + redCInt32(b));
}

int main(void) {
    redRoutine(redWord("c-add"), "[a [integer!] b [integer!]]", (void*) &add);
    printf(redCInt32(redDo("c-add 2 3")));
    return 0;
}

4.4. 从 C 创建 Red 值

libRed API 中的许多函数需要传递 Red 值(作为引用)。以下函数是最常用数据类型的简单的构造函数。

4.4.1. redSymbol()

long redSymbol(const char* word)

返回与加载的 word(以 C 字符串的形式提供)相关联的符号 ID,之后可以将此 ID 传递给不需要单词值而需要符号 ID 的其他 libRed API 函数。

long a = redSymbol("a");
redSet(a, redInteger(42));
printf("%l\n", redGet(a));

4.4.2. redUnset()

red_unset redUnset(void)

返回一个 unset! 值。

4.4.3. redNone()

red_none redNone(void)

返回一个 none! 值。

4.4.4. redLogic()

red_logic redLogic(long logic)

返回一个 logic! 值。logic 值为 0 会产生 false 值,所有其他的值都产生 true

4.4.5. redDatatype()

red_datatype redDatatype(long type)

返回一个对应于 type ID 的 datatype! 值,它是 RedType 枚举中的一个值。

4.4.6. redInteger()

red_integer redInteger(long number)

number 返回一个 integer! 值。

4.4.7. redFloat()

red_float redFloat(double number)

number 返回一个 float! 值。

4.4.8. redPair()

red_pair redPair(long x, long y)

从两个整数值返回一个 pair! 值。

4.4.9. redTuple()

red_tuple redTuple(long r, long g, long b)

从三个整数值(通常用于表示 RGB 颜色)返回一个 tuple! 值,传递的参数将被截断为 8 位元值。

4.4.10. redTuple4()

red_tuple redTuple4(long r, long g, long b, long a)

从四个整数值(通常用于表示 RGBA 颜色)返回一个 tuple! 值,传递的参数将被截断为 8 位元值。

4.4.11. redBinary()

red_binary redBinary(const char* buffer, long bytes)

从内存 buffer 指针和这个缓冲区的长度(以字节为单位)返回一个 binary! 值。输入缓冲区将在内部被复制。

4.4.12. redImage()

red_image redImage(long width, long height, const void* buffer, long format)

从内存 buffer 指针返回一个`image!` 值。图像的大小以 widthheight 的形式定义,以像素为单位。输入缓冲区将在内部被复制。接受的缓冲区格式有:

  • RED_IMAGE_FORMAT_RGB:每一个像素 24 位元。

  • RED_IMAGE_FORMAT_ARGB: 每一个像素 32 位元,透明通道在最前。

4.4.13. redString()

red_string redString(const char* string)

string 指针返回一个 string! 值。参数字符串的默认预期编码为 UTF-8,其他编码可以使用 redSetEncoding() 函数定义。

4.4.14. redWord()

red_word redWord(const char* word)

从 C 字符串返回一个 word! 值。参数字符串的默认预期编码为 UTF-8,其他编码可以使用 redSetEncoding() 函数定义,不能加载成单词的字符串将返回一个 error! 值。

4.4.15. redBlock()

red_block redBlock(red_value v,...)

从参数列表返回一个新的 block! 序列。列表 必须null0 值终止,作为结束标记。

redBlock(0);                                  // 创建一个空区块
redBlock(redInteger(42), redWord("hi"), 0);   // 创建区块 [42 hi]

4.4.16. redPath()

red_path redPath(red_value v, ...)

从参数列表返回一个新的 path! 序列。列表必须null0 值终止,作为结束标记。

redDo("a: [b 123]");
long res = redGetPath(redPath(redWord("a"), redWord("b"), 0));
printf("%l\n", redCInt32(res));    // 会输出 123

4.4.17. redLoadPath()

red_path redLoadPath(const char* path)

从一个以 C 字符串表示的路径返回 path! 序列。这提供了一种构建路径的快捷的方法,不用单独构建每个元素。

redGetPath(redLoadPath("a/b"));    // 创建并对该 path! a/b 进行求值。

4.4.18. redMakeSeries()

red_value redMakeSeries(unsigned long type, unsigned long slots)

返回一个新的 type 类型的,有足够大小存储 slots 个元素的 series!。这是一个泛用的序列构造函数。类型需要为 RedType 枚举值之一。

redMakeSeries(RED_TYPE_PAREN, 2);  // Creates a paren! series

long path = redMakeSeries(RED_TYPE_SET_PATH, 2); // 创建一个 set-path!
redAppend(path, redWord("a"));
redAppend(path, redInteger(2));    // 现在 path 为 `a/2:`

4.5. 从 Red 创建 C 值

将 Red 值转换到宿主端是可能的,然而会受限于 C 语言中有限个数量的类型。

4.5.1. redCInt32()

long redCInt32(red_integer number)

从一个 Red integer! 值返回一个 32 位有符号整数。

4.5.2. redCDouble()

double redCDouble(red_float number)

从一个 Red float! 值返回一个 C 双精度浮点值。

4.5.3. redCString()

const char* redCString(red_string string)

从一个 Red string! 值返回一个 UTF-8 字符串缓冲区指针。其他编码可以使用 redSetEncoding() 函数定义。

4.5.4. redTypeOf()

long redTypeOf(red_value value)

返回一个 Red 值的类型 ID,类型 ID 值定义在 RedType 枚举中。参考数据类型小节。

4.6. 调用 Red 动作

虽然可以使用 redCall 调用任何Red函数,但为了方便和更好的性能,它提供了对于大部分常见的操作的一些快捷方式。

4.6.1. redAppend()

red_value redAppend(red_series series, red_value value)

value 追加到 series 中,并返回该序列头部。

4.6.2. redChange()

red_value redChange(red_series series, red_value value)

修改 series 中的 value,并返回修改的部分之后的序列。

4.6.3. redClear()

red_value redClear(red_series series)

删除 series 中从当前索引到尾部的值,并在新尾部返回序列。

4.6.4. redCopy()

red_value redCopy(red_value value)

返回非标量值的副本。

4.6.5. redFind()

red_value redFind(red_series series, red_value value)

返回指向找到 value 的位置的序列,或返回 none

4.6.6. redIndex()

red_value redIndex(red_series series)

返回 series 相对于头部的当前的索引,或返回语境中的单词。

4.6.7. redLength()

red_value redLength(red_series series)

返回 series 中从当前索引到尾部的值的个数。

4.6.8. redMake()

red_value redMake(red_value proto, red_value spec)

返回一个从 spec 创建的 proto 类型的新的值。

4.6.9. redMold()

red_value redMold(red_value value)

返回一个值的源格式字符串表示形式。

4.6.10. redPick()

red_value redPick(red_series series, red_value value)

返回在给定的索引 value 上的 series

4.6.11. redPoke()

red_value redPoke(red_series series, red_value index, red_value value)

使用 value 替换给定 index 上的 series,并返回这个新的值。

4.6.12. redPut()

red_value redPut(red_series series, red_value index, red_value value)

替换 seriesmap! 中接在键的后面的值,并返回这个新的值。

4.6.13. redRemove()

red_value redRemove(red_series series)

删除在当前 series 索引上的值,并在删除后返回序列。

4.6.14. redSelect()

red_value redSelect(red_series series, red_value value)

series 中查找 value 并返回其下一个值,或返回 none

4.6.15. redSkip()

red_value redSkip(red_series series, red_integer offset)

返回相对于当前索引的 series

4.6.16. redTo()

red_value redTo(red_value proto, red_value spec)

spec 值转换为 proto 所指定的数据类型。

4.7. 访问 Red 单词

设置 Red 单词或获取 Red 单词的值是在宿主和 Red 运行时环境之间传递值的最直接的方式。

4.7.1. redSet()

red_value redSet(long id, red_value value)

将由 id 符号定义的一个单词设置为 value。从该符号创建的单词会被绑定到全局语境。此函数返回 value

4.7.2. redGet()

red_value redGet(long id)

返回由 id 符号定义的一个单词的值。从该符号创建的词会被绑定到全局语境。

4.8. 访问 Red 路径

路径是用来访问 Red 中的数据的非常灵活的方式,因此他们在 libRed 中具有专用的访问器函数。值得注意的是,它们允许访问在对象语境中的单词。

4.8.1. redSetPath()

red_value redSetPath(red_path path, red_value value)

将一个 path 设置为一个 value 并返回该 value

4.8.2. redGetPath()

red_value redGetPath(red_path path)

返回被 path 引用的 value

4.9. 访问 Red 对象的字段

当对象的字段需要进行多次设置/获取操作时,比起构建路径,直接使用对象值更简单、更好。以下两个函数是针对这种访问量身打造的。

注意

这些访问器对任意其它关联数组类型都有效,不仅仅有 object!。所以它们也可以允许传递一个 map!

4.9.1. redSetField()

red_value redSetField(red_value object, long field, red_value value)

objectfield 设置为 value 并返回该 valuefield 参数是使用 redSymbol() 创建的符号 ID。

4.9.2. redGetField()

red_value redGetField(red_value obj, long field)

返回存储在 objectfield 中的 valuefield 参数为使用 redSymbol() 创建的符号 ID。

4.10. 调试

它还提供了一些方便的调试功能。虽然大多数需要系统 shell 窗口用来输出,但是强制打开一个日志窗口或将输出重定向到一个文件也是可能的。

4.10.1. redPrint()

void redPrint(red_value value)

在标准输出中打印 value;若打开了调试控制台,打印在调试控制台里。

4.10.2. redProbe()

red_value redProbe(red_value value)

在标准输出中探查 value;若打开了调试控制台,探查在调试控制台里。该函数调用返回此 value

4.10.3. redHasError()

red_value redHasError(void)

如果在之前的 API 调用中发生了一个错误,返回一个 error! 值;或如果没有发生错误则返回 null

4.10.4. redFormError()

const char* redFormError(void)

如果发生了一个错误,返回包含格式化的错误的 UTF-8 字符串指针;如果没有发生错误,则返回 null

4.10.5. redOpenLogWindow()

int redOpenLogWindow(void)

打开日志窗口并将所有 Red 打印输出重定向到该窗口。如果宿主应用程序不是从系统 shell 运行,该功能非常有用,默认使用它打印输出。如果日志窗口已经打开,多次调用此函数不会有效果。成功时返回 1,失败时返回 0

注意

仅适用于 Windows 平台。

4.10.6. redCloseLogWindow()

int redCloseLogWindow(void)

关闭日志窗口。当日志窗口已经关闭时调用此功能不会有效果。成功时返回 1,失败时返回 0

注意

仅适用于 Windows 平台。

4.10.7. redOpenLogFile()

void redOpenLogFile(const string *name)

将 Red 打印函数的输出重定向到 name 所指定的文件中。name 可以使用特定于操作系统的文件路径格式提供相对或绝对路径。

4.10.8. redCloseLogFile()

void redCloseLogFile(void)

关闭使用 redOpenLogFile() 打开的日志文件。

注意

目前,日志文件必须在退出时关闭,否则它会继续保持加锁,这甚至可能导致某些宿主卡住或崩溃(如微软 Office 应用程序)。

4.11. 数据类型定义

libRed API 中的一些函数可以引用 Red 数据类型:redTypeOf()redMakeSeries()redDatatype()。Red 数据类型在宿主端表示为枚举(RedType),类型为使用以下结构的名称:

RED_TYPE_<DATATYPE>

完整的清单可以在这里找到。

5. Visual Basic API

Visual Basic API 可用于 VB 和 VBA(来自微软 Office 应用程序)。它基本上与 C API 相同,因此以下小节将仅描述差异。差异主要在于变长函数,它们分为两种风格:

  • redBlock()redPath()redCall() 只接收 Red 值,不要求一个终止的 null0 值,就像 C 版本那样。

  • redBlockVB()redPathVB()、 `redCallVB() 只接收 VB 值,它自动根据以下表格被转换:

VisualBasic Red

vbInteger

integer!

vbLong

integer!

vbSingle

float!

vbDouble

float!

vbString

string!

5.1. 准备

为了在 VB/VBA 中使用 libRed,你需要一个为 stdcall ABI 编译的 libRed 二进制文件版本。如果你需要重新编译此类版本:

red build libRed stdcall

你还会需要在项目中导入libRed.bas模块文件。

5.2. redLogic()

Function redLogic(bool As Boolean) As Long

返回从 VB boolean 值构造的 Red logic! 值。

5.3. redBlockVB()

Function redBlockVB(ParamArray args() As Variant) As Long

返回一个从参数列表构建的新的 block! 序列。参数个数是可变的,仅由 VisualBasic 值组成。

redProbe redBlockVB()              ' 创建空区块
redProbe redBlockVB(42, "hello")   ' 创建 [42 "hello"] 区块

5.4. redPathVB()

Function redPathVB(ParamArray args() As Variant) As Long

返回一个从参数列表构建的新的 path! 序列。参数个数是可变的,仅由 VisualBasic 值组成。

redDo("a: [b 123]")
res = redGetPath(redPathVB("a", "b"))
Debug.print redCInt32(res))        ' 会输出 123

5.5. redCallVB()

Function redCallVB(ParamArray args() As Variant) As Long

调用由传递的字符串(第一个参数)引用的 Red 函数(any-function! 类型),最后传递一些参数给它(作为 VisualBasic 值)。返回该函数的最后一个值。参数个数是可变的,仅由 VisualBasic 值组成。

redCallVB("random", 6);            ' 返回在 1 到 6 之间的随机 integer! 值

5.6. 注册回调函数

创建可以从 Red 端调用的 VisualBasic 函数的做法类似于 C API,使用 redRoutine() 调用。该函数的最后一个参数是一个函数指针。在 VB 中,这样的指针只能从在 module 而不是在 UserForm 中定义的函数取得。

这是 Excel “Red 控制台” 演示所使用的回调函数:

Sub RegisterConsoleCB()
    redRoutine redWord("print"), "[msg [string!]]", AddressOf onConsolePrint
End Sub

Function onConsolePrint(ByVal msg As Long) As Long
    If redTypeOf(msg) <> red_unset Then Sheet2.AppendOutput redCString(msg)
    onConsolePrint = redUnset
End Function