Унифицированный язык скриптов генерации тестов

Последняя правка 3792 дня назад
Материал из Daysper manual
Перейти к навигации Перейти к поиску

Унифицированный язык генерации тестов — язык, на котором должны быть написаны .d-файлы, описывающие процесс генерации тестов.

Уже реализованные вещи[править | править код]

Общая структура файла со скриптом генерации задач[править | править код]

  • "Структурная единица" файла скрипта — строка.
  • Переводы строк допускаются как в Windows, так и в Unix-кодировке. Допускается в одном файле смешение обеих кодировок.
  • Символ tab эквивалентен пробелу.
  • Несколько пробелов подряд эквивалентны одному пробелу.
  • Пробелы в начале и в конце каждой строки игнорируются.
  • Пустые строки (или ставшие пустыми с учетом предыдущих пунктов) игнорируются.
  • Каждая оставшаяся строка является отдельной командой и состоит из разделенных пробелами имени команды и нуля или более ее аргументов.
  • Имена всех файлов в скрипте указываются относительно каталога, в котором будет происходить генерация тестов (скрипт может считать, что это тот каталог, где лежит сам скрипт). Скрипт может полагать, что основной каталог задачи — это родительский (..) каталог относительно текущего, но не понятно, зачем это может быть надо скрипту.

Шаблоны имен файлов с тестами[править | править код]

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

Конкретное соответствие между номерами тестов и именами файлов задается в файле daysper.ini.

Каждый шаблон имени состоит из трех символов, первый из которых — ^. Третий символ может иметь следующие значения:

  • + — перед заменой этого шаблона номер текущего теста должен быть увеличен на единицу.
  • * — ничего дополнительно делать не надо.

Второй символ может иметь следующие значения:

  • t — шаблон должен быть заменен на имя input-файла текущего теста. Например, на 02.
  • a — шаблон должен быть заменен на имя answer-файла текущего теста. Например, на 02.a.

Особое значение имеет последовательность ^^^. Она заменяется на одиночный символ ^, который уже не имеет значения начала шаблона.

Переменные[править | править код]

Именем переменной может быть любая строка, начинающаяся с символа $ и не содержащая пробелов и символов ^; рекомендуется, чтобы именем переменной была строка, начинающаяся на $ и далее содержащая только латинские буквы. В частности, в будущем обработка переменных, содержащих другие символы, может быть изменена.

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

Подстановка значений переменной[править | править код]

В любой аргумент любой команды может быть подставлено значение переменной. На данный момент правила подстановки таковы:

  • если первый символ аргумента — $, то весь аргумент (независимо от того, состоит он только из латинских букв или нет) считается именем переменной и заменяется на ее значение, если оно определено, или оставляется как есть, если значение переменной не определено (т.е. если такой переменной нет в текущем scope).
  • если первый символ не $, то:
    • каждая подстрока вида {var} заменяется на значение переменной var или оставляется как есть, если такой переменной нет в текущем scope. (В частности, если строка var не начинается с символа $.) В кажестве значения var выбирается подстрока до ближайшей закрывающеся фигурной скобки.
    • для каждого символа $ берется это символ и далее наидлиннейшая подстрока, содержащая только латинские буквы, и заменяется на значение переменной или оставляется как есть, если такая переменная не определена.

Пример:

echo $foo $foo.o a$foo.o {$foo}.o a{$f}oo.o

— здесь последовательно упоминаются переменные $foo, $foo.o, $foo, $foo, $f.

Если, например, на момент выполнения этой строчки определено лишь значение $foo=bar, то получится

echo bar $foo.o abar.o bar.o a{$f}oo.o


Взаимодействие с шаблонами имен тестов[править | править код]

Не определено. Т.е. если в имени переменной, в ее замещающем тексте и т.п. будет символ ^; а также если в замещающем тексте шаблона будут символы $ или {, то поведение daysper не определено.

Команды языка[править | править код]

Общие замечания[править | править код]

  • В команде run само имя команды может быть опущено, если это не вызывает неоднозначности. Т.е. если daysper встречает неизвестное имя команды, то предполагается, что это — команда run, а "неизвестное имя команды" на самом деле является первым параметром команды run. Например, команда gen 123 456 эквивалентна run gen 123 456.

Компиляция[править | править код]

compile <source>
Компиляция указанного файла. Язык программирования, на котором написан файл, определяется по расширению (суффиксу имени) файла. Соответствие между языками программирования и строками компиляции (включая основные параметры командной строки) определяется в файле daysper.ini. Полный список компиляторов также определяется файлом daysper.ini; рекомендуется, чтобы во всех установках daysper поддерживались как минимум следующие расширения (суффиксы имен файлов): .pas, .dpr, .c, .cpp, .java.
  • source — имя (без расширения) файла с исходником. Программа-транслятор сама найдет файл с таким именем и каким найдется расширением и использует его. Если таких файлов несколько, то daysper будет использовать первый попавшийся, для которого можно определить компилятор.
Более строгие правила работы команды compile в текущей версии daysper: для каждого суффикса-расширения, указанного в daysper.ini, команда compile ищет файл с именем, определяемым параметром source, и этим суффиксом-расширением. Команда выполняет компиляцию произвольного из найденных таким образом файлов. Если ни для одного суффикса-расширения файл не найден, выдается ошибка.

Запуск скомпилированного файла[править | править код]

[run] <file> [parameters]
Запускает файл, скомпилированный ранее командой compile, передавая ему в качестве параметров командной строки все параметры parameters.
  • file — имя файла без расширения, который надо запустить. Запускать можно только программы, которые были ранее скомпилированы командой compile (т.е. ранее должны была быть команда compile с таким же именем в качестве параметра source)
  • parameters — параметры, которые должны быть переданы программе.
  • вывод программы может быть перенаправлен в файл синтаксисом >file и <file
  • Само имя команды (run) может быть опущено, если это не вызывает неоднозначности.

Вывод на экран[править | править код]

echo <text>
Выводит на экран указанный текст

Копирование файлов[править | править код]

copy <file1> <file2>
Копирует файл.

Переименование файлов[править | править код]

move <file1> <file2>
Переименовывает файл.

Удаление файлов[править | править код]

delete <file>
Удаляет файл, если он существует.

Циклы for[править | править код]

Циклы for бывают трех видов: по множеству строк, по множеству имен файлов, и по множеству чисел. Любой цикл начинается с команды for и заканчивается командой endfor.

Общие замечания по циклам:

  • Если значение переменной цикла уже определено, что будет в случае вложенных циклов по одной и той же переменной, daysper сигнализирует ошибку.
  • Множество значений, которые должна будет принимать переменная, обязано быть непустым.
  • Циклы интерпретируются программой-транслятором, т.е. в выходном cmd/sh-скрипте цикла не будет.
  • Если в теле цикла встречаются шаблоны имен файлов с тестами, на каждой итерации цикла они будут раскрываться заново.
  • Имя переменной цикла должно начинаться на $ (в соответствии с общей концепцией имен переменных)

См. примеры ниже.

for <variable> in <values>
endfor
values — список разделенных проблеми значений, которые последовательно будет принимать переменная variable.
for -f <variable> in <values>
endfor
values — список разделенных пробелами масок файлов. Каждая маска будет раскрыта в список файлов (не каталогов), удовлетворяющих маске, каждый такой список сортируется по алфавиту, после чего списки конкатенируются в порядке перечисления масок; переменная variable будет последовательно пробегать значения из полученного списка.
Если какой-то маске не соответствует ни одного файла, она раскрывается в пустой список. Раскрытие масок происходит в момент трансляции этой строки, т.е. до реального запуска всего .d-скрипта и выполнения каких-либо действий. В случае вложенных циклов раскрытие масок внутреннего цикла будет происходить заново на каждой итерации внешнего цикла в рамках трансляции (т.е. если в маске внутреннего цикла используется переменная внешнего цикла, то на каждой итерации внешнего будет использоваться новое значение его переменной и маски внутреннего цикла будут раскрываться заново).
for <variable> from <value1> to <value2> [format <format>]
endfor
value1 и value2 должны быть числами. Каждое число от value1 до value2 включительно будет преобразовано в строку командой sprintf в соответствии с форматом format (по умолчанию — %d), переменная variable будет последовательно пробегать соответствующие значения. См. примеры ниже.

Валидация тестов и генерация answer-файлов[править | править код]

Валидация тестов и автоматическая генерация answer-файлов, использующая решения жюри, не должна быть прописана в файле со скриптом. Она будет автоматически выполнена, т.е. при создании cmd/sh-скрипта генерации задачи будет добавлена валидация тестов и генерация answer-файлов.

А именно:

Валидация тестов[править | править код]

При генерации тестов код для валидации всех тестов вставляется в результирующий cmd/sh-скрипт автоматически, таким образом, все тесты автоматически валидируются.

Answer-файлы[править | править код]

Скрипт генерации может предусматривать создание некоторых answer-файлов без участия решения жюри (например, могут быть ручные answer-файлы на ручные тесты, или отдельные генераторы могут создавать также и answer-файлы в случаях, когда они создаются легко с учетом структуры теста, и т.п.); в таком случае скрипт генерации должен явно содержать команды для генерации этих файлов (и соответствующие шаблоны ^a+ или ^a*), в таком случае daysper не осуществляет особых действий по генерации answer-файлов для этих тестов.

Для всех же тестов, для которых не генерируются answer-файлы (т.е. отсутствуют шаблоны ^a+ или ^a*), в конец .cmd/.sh-файла создания задачи вставляется код генерации answer-файлов с использованием решения жюри. Конкретное решение жюри определяется параметром команды build или, по умолчанию, указывается в daysper.ini.

Примеры[править | править код]

run genrnd 2853 3 100 0 ^t+ ^a*

Запустить программу genrnd с шастью параметрами, первые четыре из которых — 2853 3 100 0, а пятый и шестой — имена input- и answer-файлов очередного теста (т.е. надо увеличить текущий тест на единицу, после чего имена input- и answer-файлов этого теста передать в качестве параметров программе).

genrnd 2853 3 100 0 ^t+ ^a*

То же самое.

copy 01.hand ^t+
copy 01.hand.a ^a*

скопировать файлы 01.hand и 01.hand.a в input- и answer-файлы очередного (т.е. следующего) теста.

for $i in a bb ccc
   echo a$i
endfor

выводит на экран последовательно строки aa, abb, accc.

for $i in small large
  for -f $j in test?.$i
    copy $j ^t+
    copy {$j}.a ^a*
  endfor
endfor

последовательно копирует в очередные тесты сначала все файлы вида test?.small, потом все файлы вида test?.large; в соответствующие answer-файлы копирует файлы с теми же именами, к которым приписан суффикс .a.

for $i from 5 to 15 format %03d
  echo $i
endfor

Выводит на экран последовательно 005, 006, 007, 008, 009, 010, 011, 012, 013, 014 и 015.

Планы на будущее[править | править код]

Общая структура файла со скриптом генерации задач[править | править код]

  • Символ #, а также вся часть строки от него до конца, игнорируется (комментарии).

Шаблоны имен файлов с тестами[править | править код]

  • Вторая буква шаблона n — шаблон должен быть заменен на номер теста. Например, на 2. Я пока не вижу применений у этого шаблона. Использовать его в качестве randseed нельзя, т.к. тогда нельзя будет безболезненно менять порядок тестов.

Команды языка[править | править код]

  • Для каждой команды, которая создает новый файл (копирование, компиляция и т.д.): удаление старого файла с тем же именем, если он ес ть.
  • Явное указание языка в команде compile. Явное указание компилятора в команде compile. Возможность указания параметров командной строки в команде compile. Возможность указания имени результирующего файла в команде compile (compile <source> [dest]). Более корректная работа в случае нескольких подходящих для компиляции файлов.
(Я думаю, что надо рекомендовать все ключи компилятора, которые нужны, указывать в файле с исходниками (например, ключами компилятора); единственный случай, когда это вроде создает проблему — маленький стек по умолчанию стека в программах на C/C++ и невозможность его увеличить в исходнике (???), но тут просто надо в строке компиляции для всех C/C++ программ указывать стек, рекомендовав это в свойства программы-трансляторов.)
  • Команда run: указание перенаправлений:
  • redirections — перенаправления. Допускаются перенаправления следующих форматов:
>file
число>file
<file
тот же смысл, что и в операционной системе: перенаправление стандартных потоков ввода/вывода. Верно ли, что и в Windows, и в Linux они имеют один и тот же смысл и потом программа-транслятор может их рассматривать как аргументы и особо не обрабатывать?
file1=file2
Обозначает, что программа выводит свой результат работы в файл file1, а надо этот файл сохранить под именем file2. Программа-транслятор должна вставить после вызова программы команду переименования файла. Рекомендуется, чтобы перед вызовом программы была вставлена команда удаления файлов file1 и file2, а после вызова программы перед копирование — проверка, что файл file1 появился. Программа-транслятор должна поддерживать указание нескольких таких перенаправлений в одной строке.
  • Допустить указание пути к файлу во всех командах
  • Настройки скрипта
  • Кавычки
    • ' (начало/конец жёстких кавычек)
    • " (начало/конец мягких кавычек)
    • [] (функциональный префикс)
    • ^ (начало шаблона)
    • \ (экранирует)
  • Префиксы для команд
[ignore-fail] run nya
  • Переменные + циклы
Идеи:
Переменные будут. Причём нельзя использовать переменную до присвоения ей начального значения.
У переменных есть типы. Пока понадобятся три: строки, массивы (строк), специальные (вроде исполняемого файла, что вернёт compile)
Можно делать for как префикс к команде, умеющий перебрать элементы массива.
manualTests = find '??.manual' # похоже, что «=» — ещё один специальный символ.
[for i $manualTests] copy $i ^t+ # почему именно такой синтаксис, неясно. Почему бы и нет?

Еще команды[править | править код]

Вызов другого скрипта. include <file>
Вызывает другой скрипт, написанный на этом же языке. Результат в точности эквивалентен тому, как если бы вместо этой команды в текущем файла присутствовало все содержимое файла file.
  • file — имя вызываемого скрипта без расширения (требует стандартного расширения этим скриптам, но в любом случае его стоит придумать, наверное).
  • Непонятно, почему без. Пусть будет имя файла полностью, как в c-style #include — burunduk3
echo -n
Вывод без перевода строки (если поддерживается системой)
Вызов архиваторов.
unrar <file>
unzip <file>
Распаковывает указанный файл.