новости
статьи
.программирование

первый сайт на Perl 6

В апреле 2004 года я создал пятистраничный сайт http://real.perl6.ru, который работал под управлением Perl 6. Сегодня особенно интересно заметить, что на сервере выполнялся байт-код, в который был предварительно скомпилирован исходный код скриптов на Perl 6. Иными словами, одна из основных идей — иметь возможность компилировать код — была успешно применена, и позволила сделать быстро работающий мини-сайт, хотя в то время сам по себе компилятор работал довольно медленно. Но даже и при быстром компиляторе идея выполнять байт-код вместо интерпретации исходного кода всегда разумна.

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

исполнение байт-кода

Сервер настроен таким образом, что индексным файлом в каталогах является файл main.pbc. Байт код, хранящийся в файлах с расширением .pbc (Parrot Byte-code), исполняется виртуальной машиной Parrot (до сих пор работает машина версии 0.1.0, вышедшая в феврале 2004 года — в ее составе был первый компилятор Perl 6, место которого сейчас занимает Rakudo). Сам же Parrot запускался через крошечное CGI-приложение, написанное на C:

int main ()
{
char* argv[] =
{"parrot", getenv ("PATH_TRANSLATED"), NULL};
execvp ("/parrot-0.1.0/parrot", argv);
return 0;
}


чтение переменных окружения

Это крайне простая и полезная операция, использующая значения, полученные в хеше %ENV, работает почти так же, как и в Perl 5. Основное отличие в том, что при обращении к элементу хеша нужно использовать сигил %, а не $:

my @keys = (
'SERVER_NAME',
'REMOTE_ADDR',
'HTTP_USER_AGENT'
);
my $key;
foreach $key (@keys){
print "$key=%ENV{$key}<br />\n";
}


Демонстрация работы этого кода доступна на странице http://real.perl6.ru/p6/environment/. Здесь печатаются значения нескольких переменных окружения, которые перечислены в списке @keys. Вывод выглядит, примерно так:

SERVER_NAME real.perl6.ru
REMOTE_HOST
REMOTE_ADDR 81.211.3.86
HTTP_USER_AGENT Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/525.13 (KHTML, like Gecko) Chrome/0.2.149.27 Safari/525.13 SERVER_PROTOCOL HTTP/1.1


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

разбор строки запроса

Разбор переданных странице параметров — еще одна задача, которая обязательно присутствует в коде любого веб-проекта. Задача может быть сформулирована крайне просто: разбить строку на пары ключ=значение, разделенные символом амперсанда (экранирование символов для простоты не учитывается). Это легко осуществить с помощью регулярных выражений, однако в то время они не поддерживались в достаточной степени, поэтому функция чтения параметров выглядела так:

sub read_params ($query, @params_key, @params_value){
my $pos = 0;
loop (my $c = 0; 1; $c++) {
my $newpos = index ($query, '&', $pos);
my @keyvalue;
my $pair = substr ($query, $pos, $newpos == -1 ??
length ($query) :: $newpos - $pos);
split_pair ($pair, @keyvalue);
@params_key[$c] = @keyvalue[0];
@params_value[$c] = @keyvalue[1];
last if $newpos == -1;
$pos = $newpos + 1;
}
}


Здесь стоит обратить на четыре особенности. Самое интересное в коде этой функции — она принимает параметры, причем два из них — массивы. Во-вторых, для создания C-подобного цикла используется ключевое слово loop.

В-третьих, тернарный оператор имеет вид ?? ::. Он не был таким в Perl 5, но и в сегодняшнем Perl 6 выглядит по-иному (?? !!).

И наконец, аналогично доступу к элементу хеша, упомянутому ранее, при обращении к элементу массива не нужно изменять сигил @:

@params_key[$c] = @keyvalue[0];

Для каждой найденной пары функция read_params() вызывает функцию split_pair(), которая выделяет из строки ключ и его значение:

sub split_pair ($pair, @keyvalue){
my $pos = index ($pair, '=');
if ($pos == -1){
@keyvalue[0] = $pair;
@keyvalue[1] = '';
}
else{
@keyvalue[0] = substr ($pair, 0, $pos);
@keyvalue[1] = substr ($pair, $pos + 1);
}
}


Итоговый же вызов выглядит так:

my @params_key;
my @params_value;
read_params (%ENV{'QUERY_STRING'}, @params_key, @params_value);


После этого ключи и значения оказываются в двух отдельных массивах @params_key и @params_value. Для удобства их целесообразно объединить в один хеш:

sub params2hash (%params, @params_key, @params_value){
for 0 .. @params_key -> $c {
%params{@params_key[$c]} = @params_value[$c];
}
}


Здесь встречается новая конструкция Perl 6 для создания циклов. Помимо того, что скобки для списка, переданного оператору for, необязательны, переменная цикла объявлена после стрелки ->, что делает всю запись выразительнее.

Кстати, весьма необычно с точки зрения Perl 6 выглядит вызов этой функции: в аргументах передаются и хеш, и списки:

my %params;
params2hash (%params, @params_key, @params_value);


Посмотреть работу программы можно по адресу http://real.perl6.ru/p6/querystring/ (версия без хеша) или http://real.perl6.ru/p6/queryhash/ (с хешом). Чтобы убедиться в работоспособности, достаточно дописать в адресную строку любое число параметров, в том числе с повторяющимися ключами. Например:
http://real.perl6.ru/p6/querystring/?one=alpha&two=beta&three=gamma∅&four=delta

работа с куками

Программа, работающая по адресу http://real.perl6.ru/p6/cookie/, делает попытку установить куки, а затем читает их. При второй загрузке этой страницы должно отобразиться установленное содержимое: cookie-name=test-value.

Программа на Perl 6 представляет собой обычный CGI-скрипт (независимо от того, что выполняется совсем не Perl 6, а байт-код), поэтому куки устанавливаются обычным способом в заголовке HTTP-ответа:

print "Content-Type: text/html\n"
print "Set-Cookie: cookie-name=test-value; path=/;\n\n";


Куки, переданные браузером обратно на сервер, доступны в переменной окружения HTTP_COOKIE:

print %ENV{'HTTP_COOKIE'};

счетчик посещений

Разумеется, на сайт хотелось поставить и счетчик посещений. В то время в реализации Perl 6 не было механизмов работы с файлами, поэтому счетчик написан как отдельная подпрограмма на ассемблере Parrot. Чтобы поставить счетчик на страницу, нужно было сначала скомпилировать исходный код на Perl 6, получить промежуточный файл в формате IMC (прототип современного PIR), и вставить туда строку, подключающую imc-файл счетчика:

.include "counter.imc"

Сам же основной код счетчика выглядит так:

open P0, "counter.txt"
readline S0, P0
set I0, S0
inc I0
set I2, 0
seek P0, I2, I2
print P0, I0
close P0


в заключение

Не стоит удивляться тому, что некоторые показанные здесь решения выглядят странными и нелогичными. Многие привычные конструкции просто невозможно применить из-за неполной реализации языка на момент создания этого первого сайта.



Андрей Шитов, andy@shitov.ru
обсудить статью
© сетевые решения
.
.