cJass 1.4.0.2 |
Самый простой способ начать пользоваться парсером cJass — это скачать и установить дистрибутив, для этого вам нужен Jass New Gen Pack. При установке, все необходимые для работы парсера изменения в файлах NewGen будут сделаны автоматически, а вам останется просто запустить редактор мира и пользоваться синтаксисом cJass.
Как известно, скриптовый язык JASS2 был создан Blizzard Entertainment для использования в Warcraft III. Синтаксически он больше всего напоминает Turing, поэтому не отличается лаконичностью. Для вызова функций используется ключевое слово call
, для присвоения значения переменным — set
, для объявления локальных переменных — local
. Также необходимо выделять глобальные переменные в отдельный блок:
При использовании cJass парсер самостоятельно определяет смысл выражения исходя из контекста его применения, что позволяет опускать указаные выше ключевые слова и добиваться большего удобства при записи кода:
В JASS2 все локальные переменные должны быть объявлены сразу после начала функции, в которой они используются. Используя cJass, вы можете объявлять локальные переменные в любом месте тела функции.
Парсер перенесет в начало функции объявления всех локальных переменных. Так, как в JASS2 вместе с объявлением, переменная может быть инициализирована, cJass анализирует присваемое ей значение перед переносом в начало. Инициализация переменных явными значениями однозначно переносится вместе с соответствуюшими переменными. Остальные строки инициализации переменных остаются на местах объявления, например:
После трансляции приведенный выше фрагмент кода будет выглядеть так:
Также синтаксис cJass позволяет объявлять несколько переменных одного типа на одной строке, разделяя их запятой. Переменные могут быть инициализированы тут же:
Синтаксис cJass вводит некоторые новые операторы для облегчения жизни программистам и повышения наглядности кода.
Операторы инкремента ++
(увеличение на 1) и декремента --
(уменьшение на 1) являются унарными, то есть получающиими всего один операнд. Они могут как использоваться самостоятельно на отдельной строке, так и являться частью более сложных выражений:
что транслируется в следующее:
Эти операторы имеют две формы записи: префиксную (оператор записывается перед переменной) и постфиксную (оператор записан после переменной). При использовании унарных операторов на отдельной строке, форма записи значения не имеет. Однако, используя их внутри сложных выражений, необходимо помнить, что они ведут себя по-разному в зависимости от формы записи: при префиксной форме записи сначала переменная изменяется, а затем её новое значение передается в вычисляемое выражение. В случае постфиксной записи, сначала в выражение передается значение переменной, а лишь затем её значение изменяется.
что после трансляции будет выглядеть так:
Вводимые синтаксисом cJass операторы присваивания позволяют сокращать запись присваиваемых выражений. Например, выражение
может быть сокращено применением составной операции сложения +=
:
Оператор +=
прибавляет значение выражения, записанного справа, к выражению, записанному слева от него. Так же образуются и другие составные операторы: -=
, *=
и /=
.
после трансляции выглядит так:
При использовании синтаксиса cJass, все логические операции можно записывать в сокращенной форме наряду со стандартной. Вы можете использовать тот вариант, который вам наиболее удобен.
Синтаксис языка JASS2 весьма многословен и, кроме всего прочего, использует ограничение блоков ключевыми словами. cJass вводит облегченный вариант записи блоков с помощью фигурных скобок. Теперь вам необходимо лишь указать название блока, после чего заключить его содержимое в фигурные скобки.
Такая запись может быть использована для всех блоков, присутствующих в JASS2 ( loop
, if
, else
и elseif
), а также блоков, вводимых расширением vJass (library
, scope
, struct
, interface
и module
). При использовании сокращенной записи в блоках if
и elseif
, последующее ключевое слово then
может быть безнаказанно пропущено:
что после трансляции вполне предсказуемо выглядит так:
Примечание: описанный здесь синтаксис блоков не является обязательным к применению. Вы также можете использовать и стандартную запись — это зависит только от вас.
Синтаксис cJass вводит цикл whilenot
как аналог часто используемого варианта цикла с предусловием. Способ использования этого цикла аналогичен стандартному: после оператора whilenot
следует условие, при выполнении которого цикл завершается:
что транслируется в следующее:
Также, существует запись этого цикла, при которой проверка условия происходит после выполнения свех действий тела:
что ожидаемо выглядит на JASS2 как:
Кроме того, вы можете использовать ключевое слово do
вместо loop
— в cJass они взаимозаменяемы.
Для большего удобства и повышения наглядности, синтаксис cJass допускает упрощенную запись функций (и методов vJass). В общем виде она выглядит так:
Теперь разберем её подробнее. Сначала пишется тип возвращаемого значения (в приведенном примере функция не возвращает ничего, поэтому там стоит тип nothing
), за которым следует имя функции, после чего в круглых скобках записываются принимаемые функцией аргументы (если функция не принимает аргументов, скобки можно оставить пустыми), и завершается запись блоком кода функции, заключенным в фигурные скобки. Вот, например, во что превратится приведенный выше фрагмент кода после трансляции:
Таким же образом записываются и методы, применяемые в vJass. Парсер сам определит, чего от него ждут — объявления функции или метода.
cJass предоставляет несколько удобных способов управления внешним видом вашего кода. Это — операторы, позволяющие записывать несколько выражений на одной строке либо разбивать одно длинное выражение на несколько строк. Вот пример их использования:
Символ ;
при трансляции заменяется на перенос строки, а символ \
сшивает строку с последующей.
Макросы являются одним из ключевых элементов cJass. Обработка макросов полностью проходит во время трансляции кода (которая осуществляется при сохранении карты), что позволяет добиться высокой наглядности кода без его ненужного усложнения.
Для первого знакомства, процесс обработки макросов можно представить как простую замену имен макросов их значениями (как если бы вы использовали замену в любом текстовом редакторе).
При записи макросов используется ключевое слово define
:
здесь FOOTMAN
является именем макроса, которое вы в дальнейшем можете использовать в коде, а
— значением, на которое заменятся все вызовы этого макроса после обработки парсером. К примеру,
'hfoo'
будет транслировано в
Вы также можете записывать несколько макросов за раз, просто используя define
с последующим блоком:
Для тех, кто предпочитает стандартную запись команд JASS2, имеется следующий вариант:
Обычно имена макросов состоят из букв латинского алфавита, цифр и знака подчеркивания, но иногда возникает необходимость использовать для имени макроса специальные символы вроде скобок, кавычек или просто нескольких слов. Для достижения этого, имя макроса нужно заключить в угловые скобки:
А что, если вам необходимо определить макрос, который заменяется на выражение, имеющее несколько строк? Никаких проблем — просто используйте блок внутри макроса!
Макросы могут быть определены внутри регионов, библиотек, структур и модулей как принадлежащие только этой области видимости с помощью ключевого слова private
:
Тогда они будут иметь данное значение только внутри текущей области видимости и не будут конфликтовать с макросами, имеющими такое же имя, но находящимися вне данной области видимости, например:
что после трансляции примет следующий вид:
При повторном определении макроса в пределах одной области видимости cJass выдаст ошибку о том, что данный макрос уже определен (это не касается приватных макросов во вложенных элементах). Если же вы хотите изменить значение ранее определенного макроса, то для этого стоит использовать директиву setdef
. Для отмены назначения макроса используется директива undef
, после которой вызов макроса приведет к ошибке во время трансляции.
Для расширения диапазона возможных применений макросов были введены макросы, принимающие аргументы. Макрос может принимать любое количество аргументов, которые используются аналогично аргументам функций. Тип аргумента макроса может быть любым — парсер не производит проверки типов, что с другой стороны является причиной осторожнее относиться к передаваемым в макрос аргументам.
Внимание! Так как парсер не проверяет типы аргументов макроса, это может приводить к ошибкам по невнимательности. Контроль за значениями, передаваемыми макросам, остаётся на совести программиста.
Определение в пределах одной области видимости нескольких макросов с одинаковыми именами не вызовет ошибки, если они отличаются количеством принимаемых аргументов. Такие макросы называются перегружеными. В зависимости от количества переданных при вызове аргументов, будет вызван тот или иной из перегруженных макросов.
будет транслировано в следующее:
Внимание! Если в перегруженной группе есть макрос, не принимающий аргументов, его все равно необходимо записывать с указанием пустых скобок после него.
Иногда возникает потребность вывести аргумент макроса в текстовом виде (обратите внимание: не значение аргумента, а аргумент). Для этого существует инструкция ``
, которая представляет переданный ей аргумент в виде строки. Также в макросах можно использовать оператор конкатенации (склеивания) ##
, который склеивает в одно слово выражения, находящиеся с двух сторон от него (этот оператор можно использовать в любом месте кода, а не только внутри макросов).
что после трансляции будет выглядеть так:
Для удобства программиста, cJass имеет несколько заранее определённых макросов, которые могут использоваться при написании кода. Все предопределённые макросы заменяются на свои значения во время трансляции.
DATE
— возвращает текущую дату в виде гггг.мм.дд
TIME
— возвращает текущее время в виде чч:мм:сс
COUNTER
— возвращает целое число начиная с 0, при каждом использовании число увеличивается на 1. Пример использования таков:
DEBUG
— возвращает 1 если включен флажок "Debug mode", иначе возвращает ноль. Используется в условной трансляции (см. 4.1) для обозначения действий, выполняемых только в режиме отладки.
FUNCNAME
— возврашает имя функции, в которой использован.
WAR3VER
— возвращает WAR3VER_23
или WAR3VER_24
в зависимости от положения переключателя в меню cJass. Может использоваться в блоках условной компиляции (см. 4.1) для легкой поддержки двух версий карты: для игры до 1.24 и после. Например:
Все предопределённые макросы возвращают не строковое значение. Для представления их в виде строки используйте оператор преобразования в строку или форматированный вывод (см. 6.2)
В данном разделе приведено несколько примеров использования макросов для решения нетривиальных задач.
Необходимость выполнять некоторые действия каждый раз при вызове native-функций возникает достаточно часто. В таких случаях вводят вспомогательные функции-обертки и используют их. С помощью макросов cJass вы можете прозрачно заменить вызовы конкретной функции на вызовы другой функции, либо на заданный вами набор операций в макросе.
что после трансляции выглядит так:
Теперь приведу немного пояснений к данному примеру. Для замены функции RemoveUnit
на несколько действий мы просто определяем макрос с таким же именем, тогда при обработке карты все вызовы функции будут расценены препроцессором как обращения к данному макросу и будут заменены на его значение. Обратите внимание, что внутри макроса вызов перехватываемой функции записан с использованием оператора склеивания строк. Делается это для того, чтобы парсер не принимал данный вызов функции как вызов макроса и не заменял его при обработке. Для замены функции SetUnitPosition
на нужный нам вариант, мы просто определяем функцию-обертку и с помощью макроса заменяем все вызовы оригинальной функции на вызовы функции, введенной нами.
Во многих языках аргументам функций можно назначать значения по умолчанию — в таком случае при их вызове эти аргументы можно безнаказанно опускать если вас устраивает значение по умолчанию. Подобное поведение функций можно эмулировать в cJass, используя перегруженные макросы и оператор конкатенации.
что вполне ожидаемо транслируется в
Директивы препроцессора являются первым, что выполняет парсер при обработке кода карты. Из-за этой особенности они позволяют делать некоторые интересные вещи.
Иногда удобно держать отдельные части кода во внешних файлах — часто используемые библиотеки, системы и заклинания, созданные другими, и прочее. Для включения в карту кода из внешних файлов в синтаксисе cJass существует директива include
, после которой должна следовать строка с именем подключаемого файла. Файлы с кодом, подключаемые этой директивой, могут находиться в подпапке "lib" папки с парсером AdicHelper или в папке с картой — тогда можно передавать путь относительно этих папок. Также можно подключать файлы из любых других папок, но при этом нужно указывать полный путь к ним.
Код, подключаемый с помощью этой директивы, может быть написан с использованием синтаксиса cJass и vJass.
Внимание! Не забывайте, что обратный слэш в строках нужно экранировать: \\
Иногда возникает ситуация, при которой один и тот же внешний файл включается в карту несколько раз, например, разными библиотеками. В таком случае возникнут неизбежные ошибки из-за повторения кода, чего хотелось бы избежать. Для этого в cJass существует директива #
guard
идентификатор
, которая предотвращает повторное подключение внешних файлов с одинаковыми идентификаторами. В качестве идентификаторов может выступать любое слово, но принято использовать для этого имя файла, в котором все недопустимые символы (например, точки) заменены на знаки подчёркивания. К примеру, во внешнем файле
идентификатор может быть таким: "my-system.j"
#
guard
my_system_j
.
Внимание! Директива #
guard
должна стоять в самом начале подключаемого файла.
Синтаксис cJass вводит в язык полезные команды условной трансляции. С помощью этих команд части кода могут быть включены или исключены из карты на стадии трансляции. Управляющими элементами в командах условной трансляции могут быть значения, макросы или перечисления (см 5). Синтаксис этих команд следующий:
На данный момент в командах #
if
и #
elseif
возможно использование только операций сравнения ==
и !=
В блоках условной трансляции можно писать любой код с одним ограничением: в них не должны определяться макросы с одинаковыми именами:
При попытке сохранить данный код транслятор выдаст ошибку повторного объявления макроса. Если вы хотите использовать код, аналогичный приведенному выше, то нужные макросы стоит объявить до блока условной трансляции, а в самом блоке просто назначить этим макросам значения с помощью директивы setdef
:
Директивы условной трансляции также могут срабатывать при определенном состоянии флага — им выступает особого вида макроопределение.
Директива #
if
value
сработает только если сравниваемое макроопределение имеет значение true
или 1.
В некоторых случаях возникает необходимость остановить компиляцию и выдать пользователю ошибку: например, если не выполнено какое-то требование библиотеки. Тогда на помощь придёт директива #
error
, которая остановит компиляцию с этим сообщением. Выгодно использовать внутри блоков условной компиляции."MSG"
В данном разделе описаны элементы синтаксиса, о которых не было упомянуто раньше, хотя от этого они и не стали менее важными.
Как известно, перечисления - это тип который может содержать значения указанные программистом. Целочисленные именованные константы могут быть определены как члены перечисления. Например,
определяет три целочисленные константы и присваивает им значения. По умолчанию, значения присваиваются по порядку начиная с нуля, т.е. приведенное перечисление аналогично следующим макросам:
Перечисление также может быть именованным:
Именуемые перечисления имеют собственные счетчики — в каждом из них значения элементов будут начинаться с нуля. При использовании же нескольких безымянных перечислений, значения их элементов будут продолжать друг друга:
Перечисления поддерживают JASS2-подобный синтаксис записи:
Перечисления могут быть использованы в условиях при условной компиляции:
Или вместо именованных констант как маркеры для выполнения некоторых действий:
В поставку cJass, кроме парсера, входят также файлы стандартной библиотеки, найти которые вы можете в подпапке "lib" папки "AdicHelper". Эти файлы были созданы специально для облегчения написания кода, а также простой оптимизации некоторых его аспектов. Для использования их при разработке, вы просто подключаете нужные директивой include
. Все стандартные подключаемые файлы имеют префикс "cj_" расширение ".j".
Данные подключаемые файлы содержат макросы, приближающие работу с типами в JASS2 к таковой в C++. В первом из них вводятся псевдонимы для элементарных типов, соответствующие наименованиям этих типов в языке C:
Во втором файле определяются конструкции вида new
<
type
>
для всех основных типов, например:
Полный список этих конструкций вы можете посмотреть, открыв в текстовом редакторе файл "cj_typesEx.j".
Для использования при создании своих библиотек, созданы версии этих двух файлов, в которых все макросы определяются как приватные. Данные файлы имеют постфикс "_priv" в названии файла.
Данные подключаемые файлы предназначены для удобства и небольшой оптимизации карт во время сохранения.
Первый из них заменяет все вызовы функций вида OrderId
(
"smart"
)
на соответствующие им целочисленные значения приказов, а также вводит макросы вида order_smart
для целочисленных значений всех существующих приказов.
Второй же выполняет легкую оптимизацию при использовании в карте функций и констант из файла "Blizzard.j". Используемые константы заменяются на их значения, а многие функции заменяются на свои аналоги из "common.j". Хочу заметить, что эта оптимизация работает даже на ГУИ-триггерах в вашей карте.
Включение данного файла позволяет вам использовать функции форматирования вывода согласно заданным шаблонам. Эти шаблоны определяются составленной по специальным правилам строкой (форматной строкой). Аргументы, передаваемые для форматирования, должны следовать после форматной строки в порядке, указазанном в ней.
В описаниях функций строка формата записана как string
format
, а аргументы для форматирования показаны в виде ...
Функция sprintf
форматирует принимаемую строку и возращает ее в качестве результата. После трансляции не является вызовом функции, а полностью заменяется на результирующую строку.
Функция printf
выводит форматированную строку на экран локальному игроку. Для вывода используется DisplayTimedTextToPlayer
.
Приведенные ниже функции делают то же самое, что их стандартные аналоги, но в качестве текстового аргумента принимают форматную строку и список аргументов для форматирования.
Форматная строка является шаблоном для подстановки передаваемых значений. Все символы строки форматирования, кроме управляющих последовательностей, копируются в итоговую строку без изменений. Стандартным признаком начала управляющей последовательности является символ %
(процент), для вывода самого знака %
используется его экранирование \%
. В управляющую последовательность, кроме знака её начала, также входит название типа подставляемой переменной. По умолчанию определены следующие типы:
Кроме того, пользователь может легко добавить свою разметку типов. Для этого требуется переназначить макрос cj_sprintf_argTyp_User
, отвечающий за пользовательскую разметку.
Разберем синтаксис определения обработчиков на примере %
i
:
Внимание! В секции нестандартного кода (в дереве триггеров имеет иконку карты) символ %
ведет себя странным образом при сохранении карты. Это — ошибка редактора мира, но она до сих пор не исправлена. Чтобы использовать в секции нестандартного кода функции форматирования, приведенные в данном разделе, в качестве символа начала управляющей последовательности стоит использовать ^
This document was translated from LATEX by HEVEA.