在 nim 应用程序中包含一些 mruby 可能/容易吗?

Is it possible / easy to include some mruby in a nim application?

我目前正在尝试学习 Nim(进展缓慢 - 不能投入太多时间)。另一方面,为了获得一些工作代码,我想制作我正在 ruby.

中正在开发的 Nim 应用程序部分的原型。

由于 mruby 允许在 C 应用程序中嵌入 ruby 子集,并且由于 nim 允许将任意 C 代码编译为函数,所以感觉这应该是相对简单的。有人做过吗?

我特别在寻找使用 Nim 时髦的宏功能来分解成内联 ruby 代码的方法。我打算自己尝试一下,但我认为肯定有人已经尝试过它和/或想出比我目前的学习状态更优雅的解决方案:)

https://github.com/micklat/NimBorg

这是一个目标有点相似的项目。它目前针对 python 和 lua,但使用相同的技术与 Ruby 交互应该不会太难。

Nim 中有几个功能有助于以流利的方式与外语交互:

1) 使用 Nim 的 dot operators

从 Nim 调用 Ruby

这些有点像Ruby中的method_missing。 您可以在 Nim 中定义类似 RubyValue 的类型,它将具有点运算符,可以将任何表达式(例如 foo.barfoo.bar(baz) 转换为适当的 Ruby 方法调用。参数可以传递给像 toRubyValue 这样的通用函数,它可以为各种 Nim 和 C 类型重载以自动将它们转换为正确的 Ruby 类型。

2) 从 Ruby

调用 Nim

在大多数脚本语言中,都有一种注册外部类型的方法,通常在特定数据结构中描述,每个导出类型必须填充一次。您可以使用一些通用编程和 Nim 的 .global. 变量来为通过点运算符传递给 Ruby 的每种类型自动创建和缓存所需的数据结构。将有一个像 getRubyTypeDesc(T: typedesc) 这样的通用过程,它可能依赖于 typeinfotypetraits 或用户提供的一些重载过程,定义必须为该类型导出的内容。

现在,如果你真的想依赖 mruby(例如因为你有使用它的经验),你可以考虑使用 .emit. pragma to directly output pieces of mruby code. You can then ask the Nim compiler to generate only source code,你将在第二步编译它,或者你可以更改编译器可执行文件,Nim 在编译项目时将调用它(这在上面链接的同一部分中有解释)。

这是我目前的发现。

从 mruby 执行中获取 return 值并不像我想象的那么容易。也就是说,经过多次试验和错误,这是我发现执行一些 mruby 代码的最简单方法:

const mrb_cc_flags = "-v -I/mruby_1.2.0_path/include/ -L/mruby_1.2.0_path/build/host/lib/"
const mrb_linker_flags = "-v"
const mrb_obj = "/mruby_1.2.0_path/build/host/lib/libmruby.a"
{. passC: mrb_cc_flags, passL: mrb_linker_flags, link: mrb_obj .}
{.emit: """
  #include <mruby.h>
  #include <mruby/string.h>
""".}

proc ruby_raw(str:cstring):cstring =
  {.emit: """
    mrb_state *mrb = mrb_open();
    if (!mrb) { printf("ERROR: couldn't init mruby\n"); exit(0); }
    mrb_load_string(mrb, `str`);
    `result` = mrb_str_to_cstr(mrb, mrb_funcall(mrb, mrb_top_self(mrb), "test_func", 0));
    mrb_close(mrb);
  """.}

proc ruby*(str:string):string =
  echo ruby_raw("def test_func\n" & str & "\nend")
  "done"

let resp = ruby """
  puts 'this was a puts from within ruby'
  "this is the response"
"""

echo(resp)

我很确定您应该能够在配置良好的环境中省略文件开头的一些编译器标志,例如通过正确设置 LD_LIBRARY_PATH(尤其是因为这会使代码更便携)

我目前遇到的一些问题:

  • 我被迫使用 mrb_funcall 因为,出于某种原因,clang 似乎认为 mrb_load_string 函数 return 是一个 int,尽管我能找到所有的 c 代码和文档,但网上的一些人却另有说法:

    error: initializing 'mrb_value' (aka 'struct mrb_value') with an expression of incompatible type 'int'
        mrb_value mrb_out = mrb_load_string(mrb, str);
                  ^         ~~~~~~~~~~~~~~~~~~~~~~~~~
    
  • mrb_str_to_cstr 需要 mruby/string.h header,否则会出现段错误。 RSTRING_PTR 似乎也能正常工作(至少在没有 string.h 的情况下会给出一个合理的错误),但是如果你像上面那样将它写成 one-liner,它将执行该函数两次。

我会继续,写一些稍微更地道的 nim,但这已经完成了我现在需要的。