如何将值从 Red/System 传递给 Red?

How to pass value from Red/System to Red?

我需要将我在 Red/System 中生成的值传递给 Red。我发现了文档,但没有找到如何使用它的示例。这是我的代码:

Red []

#system [   
    data!: alias struct! [
        a   [integer!]
        b   [c-string!]
    ] 

    data: declare data!

    _foo: func [return: [data!]]
    [
        data/a: 123
        data/b: "Hello"
        return data
    ]

]

sqlite: context
 [

    my-red-block: []; I want to place here: 123 "Hello"

    foo: routine [
        /local
        x [data!]
    ]
    [
        x: _foo
        ; next line do now work
        ; push my-red-block x/a
    ]
 ]

view [button "Select" [sqlite/foo]] 

my-red-block 这里是红色 block,我想用 Red/System 部分的数据填充。

https://github.com/meijeru/red.specs-public/blob/master/specs.adoc#routine-type

简介

Red 使用数据栈传递参数,return 传递结果。堆栈上的每个值都是一个盒装结构,大小为 4 个平台指针,并且可能包含对外部缓冲区的引用;这意味着您需要构造它们并将它们压入堆栈,尽管如果您 return 某些原始 Red/System 类型(例如 logic!integer!)会自动提升.

但是,在您的情况下,不需要使用堆栈,因为您希望直接在块中分配值。 low-level 编程经验和 Red/System with Red runtime API 知识是完成此任务的必要先决条件。因此,让我们以您的示例为例,逐步完成。

开箱

  1. 您有一个块,您想要向其附加两个值,123"Hello"。假设您想从 Red/System 开始。为此,我们需要编写一个例程。
    list: []
    foo: routine [][...]
    
  2. 在这个例程中,您需要获取list 字引用的块。困难的方法是实例化一个符号并通过其 ID 在全局上下文中查找值:

    list: []
    
    foo: routine [
        /local
            blk [red-block!]
    ][
        blk: as red-block! _context/get-global symbol/make "list"
    ]
    

    list 作为参数传递会更合理,但出于教育目的我会保留它 as-is。

  3. 现在我们要将 123 附加到此块。 block/rs-append 函数正是这样做的,但它接受一个盒装参数。所以我们需要先给自己装箱123

    1. This 是装箱整数的样子;如您所见,它只是 32 位 123 值 + 插槽 header 和填充。我们可以自己构造和初始化这样的结构:
      int: stack/push*         ; allocate slot on data stack
      int/header: TYPE_INTEGER ; set datatype
      int/value: 123           ; set value
      
      幸运的是,Red 运行时已经用 integer/box 函数覆盖了它,该函数采用 Red/System integer! 和 returns 盒装 red-integer! 结构:
      integer/box 123
      
    2. 现在我们需要将这个装箱的整数附加到一个块中。直觉上,我们可以检查 block.reds definitions 并找到符合我们要求的 block/rs-append
      block/rs-append blk as red-value! integer/box 123
      
      在这一步结束时,我们有:
    list: []
    
    foo: routine [
        /local
            blk [red-block!]
    ][
        blk: as red-block! _context/get-global symbol/make "list"
        block/rs-append blk as red-value! integer/box 123
    ]
    
  4. 现在我们要追加一个"Hello"字符串,但首先我们需要构造它。红色字符串支持 UTF-8 并使用 fixed-size 内部编码(每个字符 1、2 或 4 个字节,具体取决于最大代码点大小);需要手动处理很多细节,因此构造此类字符串的典型方法是将其从 c-string! 转换而来。

    list: []
    
    foo: routine [
        /local
            blk [red-block!]
            str [c-string!]
    ][
        blk: as red-block! _context/get-global symbol/make "list"
        block/rs-append blk as red-value! integer/box 123
        str: "Hello"
    ]
    

    检查 string! datatype runtime definitions 你会发现一些方便的包装器以 load 为前缀;这是一个约定,表明此类函数可用于构造(即 "load")high-level 来自 low-level Red/System 部分的红色值,在我们的例子中来自 red-string! c-string!。由于我们想在块的尾部构造它,我们可以使用 string/load-in:

    str: "Hello"
    string/load-in str length? str blk UTF-8
    

    请注意,我使用 length? 而不是 size? 来排除 NUL-terminated 字节。

结论

就是这样。在一天结束时,我们可以稍微整理一下代码并检查它是否有效:

Red [Note: "compile in release mode (-r flag)"]

list: []

foo: routine [
    /local
        blk [red-block!]
        int [integer!]
        str [c-string!]
][
    blk: as red-block! _context/get-global symbol/make "list"
    int: 123
    str: "Hello"

    block/rs-append blk as red-value! integer/box int
    string/load-in str length? str blk UTF-8
]

foo
probe list

在发布模式下编译此脚本并从 shell 执行生成的二进制文件给我们预期的结果:

[123 "Hello"]

不用说,对于新手来说,这一切看起来都让人不知所措:虽然 Red 和 Red/System 都有不错的文档和学习资源,但它们通过运行时交互的桥接是未知领域。这样做的原因是因为该项目正在发展并且 API 尚未稳定,因此,目前还不是记录它并将设计决策一成不变的时候。经验丰富的开发人员虽然可以很快掌握方向,但这需要对 Red 的评估模型有扎实的概念理解——这些基础知识是您首先需要掌握的。

还有大量的 library bindings 可供您学习——从原始示例来看,您正试图在 SQLite 之上创建一个 CRUD 视图界面。