Можно ли определить инструкции, которые будут выполняться в конце компиляции? (Кристалл Ланг)

Допустим, проект Crystal использует разные осколки. И каждый шард хочет выполнить очистку в конце компиляции всего проекта. Можно ли использовать макросы?

Что-то вроде этого, например:

{% at_end %}
  {% system("rm 'tmp files'") %}
{% end %}

person dgo.a    schedule 31.10.2017    source источник
comment
Не могли бы вы объяснить конкретный вариант использования для этого?   -  person Johannes Müller    schedule 31.10.2017
comment
@JohannesMüller Иногда мне приходится обмениваться данными между макросами. Поэтому я записываю их во временный файл. Если я этого не сделаю, мне придется использовать динамическую память и захватывать блоки/процессы так, как это делает Кемаль. Было бы лучше, если бы я мог удалить файл temp. файлы после компиляции.   -  person dgo.a    schedule 02.11.2017
comment
Интересная идея... но что заставляет вас думать, что временные файлы более производительны, чем выделение кучи? :D   -  person Johannes Müller    schedule 02.11.2017
comment
Вы, вероятно, не получите ответа здесь, поскольку в настоящее время это не похоже на вещь. Но вы можете открыть RFC в репозитории Github.   -  person Johannes Müller    schedule 02.11.2017
comment
@JohannesMüller Временные файлы используются во время компиляции, а не во время выполнения. В них хранятся имена, которые будут использоваться для генерации выражений позже во время компиляции. После компиляции временные файлы больше не используются. Таким образом, во время выполнения они полностью исчезнут. Но спасибо. Я попробую RFC во время github.   -  person dgo.a    schedule 02.11.2017
comment
Вы можете использовать константы, которые являются массивом или хешем, для хранения общего состояния между макросами.   -  person bew    schedule 05.12.2017
comment
Вот пример общего состояния между макросами: carc.in/#/r/372k вы в таком хранилище можно хранить что угодно!   -  person bew    schedule 05.12.2017
comment
Спасибо @bew. Вы хотите написать это как ответ, чтобы я мог его принять?   -  person dgo.a    schedule 05.12.2017
comment
Готово! Я также добавил некоторые пояснения;)   -  person bew    schedule 05.12.2017


Ответы (1)


Если вам нужно обмениваться данными между несколькими макросами, вы можете использовать Hash или Array и получать к ним доступ через Constant, связанный с которым узел AST доступен из макросов.

Вот пример, демонстрирующий это (в прямом эфире на https://carc.in/#/r/372k):

STORAGE = [] of _

macro add(node)
  {% STORAGE << node %}
end

macro list
  {% for elem in STORAGE %}
    {% puts "Elem: #{elem}" %}
  {% end %}
end

add 1
add 2
add "hello"
add :world
add({a: 1, b: 2})
add 3 + 4
add a_call(arg1, 2)

list

Важными частями здесь являются:

STORAGE = [] of _

Здесь я объявляю массив определенного типа (что в данном случае не имеет значения, так как доступ к массиву осуществляется только через макросы, вы не можете использовать этот тип (_) в обычном коде). Макроссистеме нужно только знать, что это массив.

macro add(node)
  {% STORAGE << node %}
end

Затем я создаю макрос, который сможет изменять узел AST массива (который имеет тип ArrayLiteral в системе макросов). Обратите внимание, что он позволяет хранить узлы AST любого типа, от простых узлов, таких как NumberLiteral или SymbolLiteral, до сложных узлов, таких как Call, Def или даже ClassDef.

macro list
  {% for elem in STORAGE %}
    {% puts "Elem: #{elem}" %}
  {% end %}
end

И, наконец, я создаю макрос, который будет просто печатать (во время компиляции) содержимое массива STORAGE.

То же самое можно сделать с HashLiteral:

STORAGE = {} of _ => _

И так же, как описано выше, вы можете использовать любые узлы AST в качестве ключа или значения.


Обратите внимание, что Constants, который вы используете в системе макросов, также можно увидеть в обычном коде, и возможны конфликты имен (что, если пользователь захочет использовать константу STORAGE и для своего собственного кода?). Чтобы свести к минимуму эту возможность (если это нежелательно), я предлагаю вам поместить ваши константы в специальный модуль, такой как MyShard::MacroStorage::SomeInterestingNodes, или использовать какой-нибудь сложный шаблон именования, такой как M____ACRO_Storage____01234 (‹-- это вряд ли будет использоваться пользователем;))

person bew    schedule 05.12.2017
comment
Ух ты. Я бы никогда не подумал использовать [] of _ в макросе. Как вы об этом узнали? (Спасибо за этот ответ.) - person dgo.a; 05.12.2017
comment
Чтение чужого кода, кода компилятора и экспериментирование с гранями языка :D - person bew; 06.12.2017