2017-02-18 5 views
1

Я хотел бы создать новую переменную в макросе. Мой код можно сократить до:Назначение значения новой переменной в макросе Crystal

macro test() 
    %p = "a = 3" 
    %p 
end 

test() 
puts(a) 

но я тогда получаю ошибку undefined local variable or method 'a'

Я попытался обернуть %p в {{ }} или {% %}, но он не компилируется («unexpected token: %»)

EDIT

Вот несколько контекстов.

Мне часто приходится использовать два именованных кортежа с некоторыми общими полями, которые затем преобразуются в JSON и отправляются различным клиентам.

a = {x: xx, y: yy, w: ww} 
b = {x: xx, y: yy, z: zz} 

В настоящее время я пишу оба из них с повторением. Значения для ww, xx, yy, zz не известны во время компиляции. Я хотел бы заменить это вызовом макроса, делающего слияние во время компиляции.

я придумал следующий код, который создает строку кода, я в настоящее время вводится вручную:

macro merge(common, aOnly, bOnly) 
    # Start, middle and end parts of target instructions 
    %p1a = "a = {" 
    %p1b = "b = {" 
    %p2 = "" 
    %p2a = "" 
    %p2b = "" 
    %p3 = "}" 

    # Creating the middle part 
    {% for key, value in common %} 
    %p2 = %p2 + "{{key.id}}" + ": " + "{{value}}" + ',' 
    {% end %} 
    %p2a = %p2 
    {% for key, value in aOnly %} 
    %p2a = %p2a + "{{key.id}}" + ": " + "{{value}}" + ',' 
    {% end %} 
    %p2b = %p2 
    {% for key, value in bOnly %} 
    %p2b = %p2b + "{{key.id}}" + ": " + "{{value}}" + ',' 
    {% end %} 

    # Removing unneeded comma 
    %p2a = %p2a.chomp(",") 
    %p2b = %p2b.chomp(",") 

    # Display 
    puts(%p1a + %p2a + %p3) 
    puts(%p1b + %p2b + %p3) 

    # Definition of new variables 
    %p1a + %p2a + %p3 
    %p1b + %p2b + %p3 
end 

merge({x: xx, y: yy}, {w: ww}, {z: zz}) 
+0

[Ваша программа] (https://carc.in/#/r/1nfd) расширяется до: '__temp_22 =" a = 3 "; __temp_22; puts (a) ', что я и ожидал бы с учетом этого синтаксиса, но вы, вероятно, ожидали чего-то другого. Но невозможно догадаться, что вы ожидали, поэтому, пожалуйста, разверните свой вопрос. –

+0

Я добавил некоторый контекст. –

+2

Макросы не могут определять новые переменные. Переменные определяются во время разбора. Например, если вы используете 'a = 1' внутри метода, синтаксический анализатор теперь знает, что' a' является переменной. Если вы делаете это внутри макросообщения, парсер не знает об этом. И это не ошибка, это языковая функция. Внезапное появление переменных в методе является ошибкой и запутанностью. – asterite

ответ

2

Основной проблемой здесь является непонимание %names. Это не конструкция, которая заставляет время компиляции команд. Написание %a = 5 - это то же самое, что и запись guaranteed_unique_name = 5 – - это способ избежать вмешательства в другие переменные, которые могут существовать в одной области, но это все равно нормальное назначение переменной времени выполнения.

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

macro merge(common, aOnly, bOnly) 
    # Start, middle and end parts of target instructions 
    {% 
    p1a = "a = {" 
    p1b = "b = {" 
    p2 = "" 
    p2a = "" 
    p2b = "" 
    p3 = "}" 
    %} 

    # Creating the middle part 
    {% for key, value in common %} 
    {% p2 = p2 + "#{key.id}: #{value}," %} 
    {% end %} 
    {% p2a = p2 %} 
    {% for key, value in aOnly %} 
    {% p2a += "#{key.id}: #{value}," %} 
    {% end %} 
    {% p2b = p2 %} 
    {% for key, value in bOnly %} 
    {% p2b += "#{key.id}: #{value}," %} 
    {% end %} 

    # Removing unneeded comma 
    {% p2a = p2a.chomp(",") %} 
    {% p2b = p2b.chomp(",") %} 

    # Definition of new variables 
    {{(p1a + p2a + p3).id}} 
    {{(p1b + p2b + p3).id}} 
end 

merge({x: xx, y: yy}, {w: ww}, {z: zz}) 

Теперь я просто переписать пример с более обычными конструкциями:

macro merge(common, a_only, b_only) 
    a = { 
    {% for key, value in common %} 
     {{key}}: {{value}}, 
    {% end %} 
    {% for key, value in a_only %} 
     {{key}}: {{value}}, 
    {% end %} 
    } 

    b = { 
    {% for key, value in common %} 
     {{key}}: {{value}}, 
    {% end %} 
    {% for key, value in b_only %} 
     {{key}}: {{value}}, 
    {% end %} 
    } 
end 

merge({x: "a", y: 5}, {w: "b"}, {z: 7}) 

pp a, b 

Как вы можете видеть, обычный код, который вы помещаете в тело макроса, - это код времени выполнения, в то время как во время компиляции выполняются вещи внутри {% %} и {{ }}. Вы можете создавать циклы времени компиляции и переплетать их с помощью обычных строк кода для естественного создания сложного кода.

Надеюсь, что это ответ на заголовок вопроса «Присвоение значения новой переменной в макросе Crystal»: чтобы присвоить что-то переменной времени выполнения, вы делаете то же самое, что и для вывода любого кода времени выполнения в макросе –, вы просто пишете его out, как a = 5, потому что по умолчанию макросы выводятся во время выполнения кода, и только {% и {{ разграничить время компиляции.


Чтобы ответить на комментарий, вот, пожалуй, еще более идиоматических версия, и она даже иллюстрирует временные/уникальные имена переменных. Он использует их для создания кортежей, а затем результат выполнения макроса является кортежем тех, которые могут быть удобно распакованы, вместо того, чтобы полагаться на побочные эффекты, что редко делается.

macro merge(common, a_only, b_only) 
    %a = { 
    {% for key, value in common %} 
     {{key}}: {{value}}, 
    {% end %} 
    {% for key, value in a_only %} 
     {{key}}: {{value}}, 
    {% end %} 
    } 

    %b = { 
    {% for key, value in common %} 
     {{key}}: {{value}}, 
    {% end %} 
    {% for key, value in b_only %} 
     {{key}}: {{value}}, 
    {% end %} 
    } 

    { %a, %b } 
end 

first, second = merge({x: "a", y: 5}, {w: "b"}, {z: 7}) 

puts first 
+0

Большое вам спасибо. Я понимаю, как вторая версия более идиоматична. Однако я не понимаю, почему я не могу использовать «puts» для отображения переменных a и b. Выполняя это, он получает сообщение «неопределенная локальная переменная или метод« a ». –

+0

@ClementJ., Хм, это немного странное поведение, пограничная ошибка. Макросы обычно не используются для создания новых переменных с жестко обозначенным именем, поэтому это не хорошо изученная часть языка. Я думаю, вам нужно «предвидеть» переменную перед вызовом макроса: 'a = nil; merge ({x: "a", y: 5}, {w: "b"}, {z: 7}); помещает '([полный код] (https://carc.in/#/r/1nfz)) –

+0

@ClementJ., проверьте дополнение к моему ответу –