Построение и возврат типов Julia в C - PullRequest
0 голосов
/ 14 сентября 2018

Как бы вы сконструировали и вернули Julia Expr, скажем :(x + 2), из функции C, вызываемой с помощью ccall из Julia, предпочтительно, чтобы Юлия могла собирать мусор?

Edit:

Поскольку редактирование было предложено с некоторыми дополнительными деталями, вот они. Начнем с того, что вопрос не в том, чтобы генерировать Expr за один раз, а в том, чтобы иметь возможность постепенно наращивать произвольные Expr с и т.п., см. Ниже.

Мне нужен парсер для проекта в Юлии. Содержимое входных данных синтаксического анализатора частично является алгебраическим выражением, и я хотел бы получить в конце эквивалент Джулии Expr для них. Поскольку, похоже, нет генераторов синтаксического анализатора с целями Julia, я думал о создании синтаксического анализатора в C ++ (что удобно, так как в любом случае мне нужно использовать одну и ту же грамматику синтаксического анализатора для кода C ++ в том же проекте), либо с помощью flex / bison, либо ANTLR. Таким образом, вопрос в том, как на самом деле построить AST в Julia из синтаксического анализатора C ++. У меня есть несколько идей для решения этой проблемы:

  1. Иметь в Джулии несколько обратных вызовов, которые может вызывать код C ++ (например, push literal / add и т. Д. Для алгебраических выражений), и что Джулия управляет распределением узлов AST. Или, может быть, просто вызов Symbol(), Expr() и т. Д., Плюс более специализированные обратные вызовы для структур, специфичных для проблемы. Даже если я совсем новичок в Джулии, я думаю, что справлюсь.
  2. Пусть синтаксический анализатор C ++ генерирует AST простых структур, которые код Джулии может пройти, чтобы построить эквивалентные структуры Джулии. Немного грязное решение, но, может быть.
  3. Пусть код C ++ выделит и вернет Julia AST напрямую, что, я думаю, если это будет возможно, равнозначно AST jl_type_t и друзьям. Большой вопрос, как устроена такая структура. Я не нашел никакой документации, кроме файлов .h, которые не так хорошо прокомментированы. И я также сомневаюсь, что память, выделенная в коде C ++, может быть освобождена (сборщик мусора) в Джулии, и мне интересно, как вы могли бы решить эту проблему.

1 Ответ

0 голосов
/ 17 сентября 2018

Отвечая на мой собственный вопрос, я думаю, что использование стратегии 1, как указано в вопросе, является самым простым и надежным способом решения этой проблемы. Стратегия 2 возможна, но она включает в себя зеркальное отображение AST в C и Julia, что усложняет решение. Стратегия 3, если это возможно, требует глубоких знаний о том, как работает система типов Julia, и, по сути, сводится к дублированию функций из исходного кода Julia C.

Единственное (незначительное) неудобство с моим решением заключается в том, что я не смог получить указатель на vararg Expr, используя @cfunction. Поэтому в контексте алгебраических выражений вам понадобятся отдельные указатели на функции, например, для создания унарных и бинарных операций. Если у кого-то больше с этим успехов, дайте мне знать.

Код

expr.c

// struct to gather neccessary callbacks
typedef struct {
  void* (*symbol_callback)(char const *); // Symbol(Cstring)
  void* (*int_callback)(int); // Int(Int) - boxing an int
  void* (*expr4_callback)(void *, void*, void*, void*); // Expr for binary op
} callbacks;

void *expr(callbacks *c) {
  return c->expr4_callback(
    c->symbol_callback("call"),
    c->symbol_callback("+"),
    c->symbol_callback("x"),
    c->int_callback(2)
  );
}

Makefile

CC=gcc

CFLAGS=-c -Wall -fPIC

SOURCES=expr.c
OBJECTS=$(SOURCES:.c=.o)

.c.o:
    $(CC) $(CFLAGS) $< -o $@

lib: $(OBJECTS)
    $(CC) -shared -fPIC -o libexpr.so $(OBJECTS)

clean:
    rm *.o *.so

Test.jl

# define a proxy function for Symbol(::String) that does the conversion from
# a C byte string
symbol_proxy(name::Ptr{UInt8})::Symbol = Symbol(unsafe_string(name))

struct Callbacks
    # pointer to Symbol(::String) proxy
    symbol::Ptr{Nothing}
    # pointer to Int(::Int) function
    int64::Ptr{Nothing}
    # pointer to Expr() function with four arguments (for binary op)
    expr4::Ptr{Nothing}
end

# get callbacks
c = Callbacks(
    @cfunction(symbol_proxy, Ref{Symbol}, (Ptr{UInt8},)),
    @cfunction(Int, Ref{Int64}, (Cint,)),
    @cfunction(Expr, Ref{Expr}, (Any,Any,Any,Any))
)

# call shared library to construct :(x+2)
e = ccall((:expr, "./libexpr"), Ref{Expr}, (Ref{Callbacks},), c)
dump(e)

выход

Компиляция совместно используемой библиотеки с make lib и запуск Test.jl приводит к выводу

Expr
  head: Symbol call
  args: Array{Any}((3,))
    1: Symbol +
    2: Symbol x
    3: Int64 2
...