Возможно ли / легко включить mruby в приложение nim?
В настоящее время я пытаюсь выучить Нима (он идет медленно - не могу уделять ему много времени). С другой стороны, в интересах получения рабочего кода я хотел бы создать прототипы разделов приложения Nim, над которым я работаю в ruby.
Поскольку mruby позволяет встраивать подмножество ruby в приложение C, а nim позволяет компилировать произвольный код C в функции, создается впечатление, что это должно быть относительно просто. Кто-нибудь сделал это?
Я особенно ищу способы использования прикольных макросовых функций Нима для проникновения во встроенный код ruby. Я собираюсь попробовать себя, но я думаю, что кто-то непременно попробовал это и / или придумал более изящные решения, чем я могу в моем текущем состоянии обучения:)
2 ответа
https://github.com/micklat/NimBorg
Это проект с несколько схожей целью. На данный момент он нацелен на python и lua, но использование тех же методов для взаимодействия с Ruby не должно быть слишком сложным.
В Nim есть несколько функций, которые помогают свободно взаимодействовать с иностранным языком:
1) Вызов Ruby из Nim с использованием точечных операторов Nim
Это немного похоже method_missing
в рубине. Вы можете определить тип как RubyValue
в Nim, который будет иметь точечные операторы, которые будут переводить любое выражение, как foo.bar
или же foo.bar(baz)
на соответствующий вызов метода Ruby. Аргументы могут быть переданы универсальной функции, такой как toRubyValue
это может быть перегружено для различных типов Nim и C, чтобы автоматически преобразовать их в правильный тип Ruby.
2) Зов Нима из Руби
В большинстве языков сценариев существует способ регистрации стороннего типа, который часто описывается в определенной структуре данных, которая должна быть заполнена один раз для экспортируемого типа. Вы можете использовать немного общего программирования и Нима .global.
vars для автоматического создания и кэширования требуемой структуры данных для каждого типа, который был передан в Ruby через операторы точек. Там будет общий процесс, как getRubyTypeDesc(T: typedesc)
на что можно положиться typeinfo
, typetraits
или некоторые перегруженные процедуры, предоставленные пользователем, определяющие, что должно быть экспортировано для типа.
Теперь, если вы действительно хотите положиться на mruby (потому что у вас есть опыт работы с ним, например), вы можете изучить использование .emit. Прагма для прямого вывода фрагментов кода Mruby. Затем вы можете попросить компилятор Nim генерировать только исходный код, который вы скомпилируете на втором шаге, или вы можете просто изменить исполняемый файл компилятора, который Nim будет вызывать при компиляции проекта (это объяснено в том же разделе, связанном выше).
Вот что я обнаружил до сих пор.
Получение возвращаемого значения из выполнения mruby не так просто, как я думал. Тем не менее, после долгих проб и ошибок, я нашел самый простой способ получить некоторый код 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 возвращает
int
Несмотря на весь код, который я могу найти, и документацию, и несколько человек онлайн, говорящих иначе:error: initializing 'mrb_value' (aka 'struct mrb_value') with an expression of incompatible type 'int' mrb_value mrb_out = mrb_load_string(mrb, str); ^ ~~~~~~~~~~~~~~~~~~~~~~~~~
Заголовок mruby/string.h необходим для
mrb_str_to_cstr
В противном случае вы получите segfault. RSTRING_PTR, кажется, также работает нормально (что, по крайней мере, дает ощутимую ошибку без string.h), но если вы напишите его как однострочный, как описано выше, он выполнит функцию дважды.
Я собираюсь продолжать, напишу немного более идиоматическую ним, но это сделало то, что мне было нужно сейчас.