WarCraft 3: 14. Аннотации

WurstScript
Практически любое объявление в Wurst может сопровождаться одной или более опциональной аннотацией. Аннотации - это инструкции, выполняемые исключительно во время компиляции и работающие исключительно с метаданными. Они могут быть использованы для вызова функций во время компиляции, тестирования модулей, редактирования объектов (обо всем этом позже). В большинстве случаев аннотации игнорируются, если только вы не обращаетесь к ним явно.
Вот список всех существующих в Wurst аннотаций:
@configurable constant SOME_VAR = 12
@config constant SOME_VAR = 24

@compiletime function foo()

@test function someTest()

@deprecated("Use .size() instead") function getSize() returns int
Как вы можете заметить, аннотация начинается с символа @ и располагается перед объявлением. Как упоминалось выше, аннотации позволяют вызывать функции во время компиляции кода, для чего Wurst поддерживает специальный интерпретатор, далее мы разберем этот процесс немного подробнее.
Вы уже должны быть знакомы с аннотациями @configurable и @config из главы, посвященной пакетам.
Об аннотации @deprecated можно вкратце сказать, что функции, ею помеченные, будут выделяться в редакторе VSCode, дополняя описание этих функций сообщением, указанным в скобках после аннотации. То есть, вы можете явно указать нежелательные к использованию функции вашей библиотеки и оставить поясняющее сообщение, если эту функцию нельзя просто удалить в целях обратной совместимости.
Подробнее же мы остановимся на аннотациях @compiletime и @test.

Функции времени компиляции

Функция времени компиляции - это функция, выполняемая на этапе компиляции кода карты. Ее основная возможность, это создание и редактирование объектов из кода (для чего обычно используется редактор объектов World Editor).
Функция времени компиляции, это обычная функция, отмеченная аннотацией @compiletime.
@compiletime function foo()
Такие функции ничего не принимают и ничего не возвращают.
Чтобы на этапе компиляции производился вызов функций помеченных аннотацией @compiletime, необходимо включить опцию компилятора Run compiletime functions, что можно сделать в меню на панели сверху редактора WurstPack или SharpCraft, либо добавив строку -runcompiletimefunctions в текстовый файл wurst_run.args внутри директории вашего проекта (который можно просто создать, в случае отсутствия). Если вы используете вызов функций во время компиляции для создания объектов, Wurst будет генрировать файл внутри каталога вашего проекта, который можно использовать для импорта всех таким образом созданных объектов в карту, посредством Редактора Объектов. Если вы желаете, чтобы созданные объекты автоматически добавлялись в карту сразу, вам необходимо активировать опцию Inject compiletime objects в редакторе или добавить строку -injectobjects в файл wurst_run.args внутри директории вашего проекта, однако это не позволит увидеть изменения внутри Редактора Объектов напрямую.
Вы можете использовать один и тот же код как в игре, так и во время компиляции. Для того чтобы различать, какой код одной и той же функции должен выполняться на этапе компиляции, а какой во время игрового процесса, предусмотрена специальная константа - compiletime. Данная константа принимает значение true на этапе компиляции и false в игре. Следующий простой пример призван ее продемонстрировать:
init
	doInit()

@compiletime
function doInit()
	for i = 1 to 100
		if compiletime
			// создаем объекты во время компиляции
		else
			// размещаем созданные объекты на карте во время игры

Выражения времени компиляции

Близкие своей природой к функциям времени компиляции, Wurst так же поддерживает выражения времени компиляции. Как и предполагает название, данные выражения выполняются на этапе компиляции кода карты. Результат работы такого выражения будет помещен в код карты заместо самого выражения.
Синтаксис выражений - это вызов функций времени компиляций, определенных внутри пакета MagicFunctions стандартной библиотеки. Эти функции принимают один аргумент в виде выражения, которое нужно выполнить. Например, следующий код определит глобальную переменную blub посредством выражения fac(5):
int blub = compiletime(fac(5))

function fac(int x) returns int
    if x <= 1
        return 1
    return x*fac(x-1)
В данном случае, во время компиляции будет вызвана функция вычисления факториала fac, которая в результате своей работы вернет значение 120. В последствии, выражение будет заменено этим значением в коде карты.
Аналогично функциям времени компиляции, выражения могут быть использованы в сочетании с редактированием объектов из кода и точно так же они требуют чтобы опция Run compiletime functions была активирована.

Порядок выполнения

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

Подробнее о редактировании объектов

Стандартная библиотека предоставляет некоторый набор функций времени компиляции для редактирования объектов карты (таких как юниты, предметы, способности и пр.). Эти функции можно найти в директории objediting внутри стандартной библиотеки.
Пакет ObjEditingNatives содержит функции реализованные внутри компилятора, которые позволяют манипулировать объектами карты, подобно Редактору Объектов World Editor. Если вам знаком формат объектов Wc3, а так же у вас есть опыт работы с такими инструментами как Lua Object Generation или ObjectMerger в составе JNGP, у вас не возникнет никаких проблем с использование данной возможности. Запустив процесс компиляции карты с активированной опцией Run compiletime functions, компилятор создаст файл с именем, вроде “WurstExportedObjects_w3a.wurst.txt”, что может быть найден в директории вашего проекта. Вы можете использовать этот код как отправную точку для использования ранее упомянутых нативных функций.
Стандартная библиотека Wurst так же предоставляет высокоуровневую абстракцию. Например, пакет AbilityObjEditing содержит множество различных классов, для работы с большинством стандартных объектов Wc3, с удобочитаемыми именами методов, что избавляет вас от необходимости изучать ID каждой способности, юнита, декорации и пр.
Следующий пример создаст способность на основе "Молота бурь". Созданная способность получит ID "A005", а уже в следующей строке имя способности будет изменено на "Test Spell". Свойства, специфичные для данного рода способности устанавливаются внутри цикла.
package Objects
import AbilityObjEditing

@compiletime function myThunderBolt()
	// Создание способности на основе "Молота бурь" Горного Короля
	let a = new AbilityDefinitionMountainKingThunderBolt('A005')
	// Изменяем имя
	a.setName("Test Spell")
    // Изменяем описание
	a.setTooltipLearn("Кидает свою колбасу в цель.")
	for i=1 to 3
		// 400 урона + 100 за каждый уровень
		a.setDamage(i, 400. + i*100)
		// 10 секунд перезарядка
		a.setCooldown(i, 10.)
		// 0 маны, ибо пользователю Wurst магия не нужна
		a.setManaCost(i, 0)
		// ...и так далее...
Прим. пер.: Не бойтесь изучить пакеты в директории objediting внутри стандартной библиотеки, для получения лучшего представления о широких возможностях Wurst. Это не так сложно, как может показаться.

Модульное тестирование

Модульное тестирование не относится к происходящему внутри Wc3, это индивидуальная возможность самого компилятора.
Тестирование позволяет вам убедиться в верности поведения вашего кода вне Warcraft 3, на этапе компиляции кода карты. Вместо интерпретации Jass-кода игрой, сам компилятор Wurst интерпретирует и выполняет его, позволяя отлавливать ошибки прежде их появления в игре.
Подобное тестирование возможно благодаря имитации поведения функций игры компилятором. Отсюда вытекает главное предостережение - возможности модульного тестирования ограничены количеством уже реализованных нативных функций игры внутри компилятора, а так же правильностью их реализации, поскольку нам неизвестно достоверно, как именно устроен Wc3 изнутри, а значит имитировать его поведение идентично во всем мы просто не можем.
Тем не менее, модульное тестирование дарит нам такие существенные преимущества, как:
  • возможность убедиться, что код делает ровно то, что было задумано
  • лучшее понимание вашего кода
  • внедрение существенных изменений в кодовую базу, будучи уверенным, что все работает правильно
  • фактически, уменьшение количества багов

Простой тест

В качестве примера, напишем небольшой тест. Как уже говорилось ранее, для этого необходимо использовать аннотацию @Test перед объявлением функции. Любая функция, отмеченная данной аннотацией будет распознана компилятором и выполнена во время тестирования. У вас нет ограничений на использование тестовой функции, подобно функциям времени компиляций, тем не менее, не рекомендуется использовать такие функции когда-либо, кроме этапа тестирования.
Модульное тестирование можно разделить на 2 части - выполнение тестируемого кода и сравнение результатов его работы с ожидаемым результатом. Вы можете обратится к пакету Wurstunit, чтобы получить представление о функциях, проверяющих полученный и ожидаемый результат.
Самым простым примером модульного тестирования, будет проверка арифметики:
@Test function example()
    let actual = (10 .squared()) * 6
    let expected = 600
    actual.assertEquals(expected)
Здесь мы описали конечный результат (переменная actual) и ожидаемый (переменная expected), затем выполнили их сравнение с помощью расширяющей функции assertEquals из пакета Wurstunit.
Чтобы выполнить тестирование, необходимо нажать клавишу F1 в редакторе VSCode и выполнить команду Run test:
Результат тестирования вы можете наблюдать в окне "Вывод" редактора
Прим. пер.: не забудьте выбрать "wurstLanguageServer" справа, на верхней панели окна "Вывод", в раскрывающемся списке, ото будете сидеть и смотреть в чистый лист, как я, дурачок.
Вот и всё, теперь вы - гуру модульного тестирования.

Просмотров: 232

GetLocalPlayer #1 - 1 год назад 0
Глава собиралась из кусков как гайдов прошлого, так и не столь давних новостей в блоге Wurst, посему ее не могло быть на момент написание прочих глав более года назад.