Parrot - Примеры программирования
Программирование Parrot похоже на программирование на языке ассемблера, и у вас есть возможность работать на более низком уровне. Вот список примеров программирования, которые познакомят вас с различными аспектами программирования Parrot.
Классический Привет, мир!
Использование регистров
Суммирование квадратов
Числа Фибоначчи
Вычисление факториала
Компиляция в PBC
PIR против PASM
Классический Привет, мир!
Создайте файл hello.pir, содержащий следующий код:
.sub _main
print "Hello world!\n"
end
.end
Затем запустите его, набрав:
parrot hello.pir
Как и ожидалось, отобразится текст «Hello world!» на консоли, после чего следует новая строка (из-за \ n).
В приведенном выше примере .sub _main означает, что последующие инструкции составляют подпрограмму с именем «_main» до тех пор, пока не встретится «.end». Вторая строка содержит инструкцию печати. В этом случае мы вызываем вариант инструкции, который принимает постоянную строку. Ассемблер позаботится о том, чтобы решить, какой вариант инструкции использовать для нас. Третья строка содержит инструкцию конца, которая вызывает завершение работы интерпретатора.
Использование регистров
Мы можем изменить hello.pir, чтобы сначала сохранить строку Hello world! \ N в регистре, а затем использовать этот регистр с инструкцией печати.
.sub _main
set S1, "Hello world!\n"
print S1
end
.end
Здесь мы точно указали, какой регистр использовать. Однако, заменив S1 на $ S1, мы можем делегировать выбор того, какой регистр использовать, Parrot. Также можно использовать обозначение = вместо записи инструкции set.
.sub _main
$S0 = "Hello world!\n"
print $S0
end
.end
Чтобы сделать PIR еще более читабельным, можно использовать именованные регистры. Позже они отображаются в регистры с действительными номерами.
.sub _main
.local string hello
hello = "Hello world!\n"
print hello
end
.end
Директива .local указывает, что именованный регистр необходим только внутри текущего модуля компиляции (то есть между .sub и .end). После .local - это тип. Это может быть int (для регистров I), float (для регистров N), строка (для регистров S), pmc (для регистров P) или имя типа PMC.
Суммирование квадратов
В этом примере представлены еще несколько инструкций и синтаксис PIR. Строки, начинающиеся с символа #, являются комментариями.
.sub _main
# State the number of squares to sum.
.local int maxnum
maxnum = 10
# Some named registers we'll use.
# Note how we can declare many
# registers of the same type on one line.
.local int i, total, temp
total = 0
# Loop to do the sum.
i = 1
loop:
temp = i * i
total += temp
inc i
if i <= maxnum goto loop
# Output result.
print "The sum of the first "
print maxnum
print " squares is "
print total
print ".\n"
end
.end
PIR предоставляет немного синтаксического сахара, что делает его более высокоуровневым, чем ассемблер. Например:
temp = i * i
Это просто еще один способ написать более ассемблерный:
mul temp, i, i
И:
if i <= maxnum goto loop
Такой же как:
le i, maxnum, loop
И:
total += temp
Такой же как:
add total, temp
Как правило, всякий раз, когда инструкция Parrot изменяет содержимое регистра, это будет первый регистр при записи инструкции в ассемблерной форме.
Как обычно в языках ассемблера, циклы и выборки реализованы в терминах условных операторов ветвления и меток, как показано выше. Программирование на ассемблере - это то место, где использование goto не является плохим тоном!
Числа Фибоначчи
Ряд Фибоначчи определяется так: возьмите два числа, 1 и 1. Затем несколько раз сложите последние два числа в ряду, чтобы получить следующее: 1, 1, 2, 3, 5, 8, 13 и т. Д. . Число Фибоначчи fib (n) - это n-е число в ряду. Вот простая программа ассемблера Parrot, которая находит первые 20 чисел Фибоначчи:
# Some simple code to print some Fibonacci numbers
print "The first 20 fibonacci numbers are:\n"
set I1, 0
set I2, 20
set I3, 1
set I4, 1
REDO: eq I1, I2, DONE, NEXT
NEXT: set I5, I4
add I4, I3, I4
set I3, I5
print I3
print "\n"
inc I1
branch REDO
DONE: end
Это эквивалентный код на Perl:
print "The first 20 fibonacci numbers are:\n";
my $i = 0;
my $target = 20;
my $a = 1;
my $b = 1;
until ($i == $target) {
my $num = $b;
$b += $a;
$a = $num;
print $a,"\n";
$i++;
}
NOTE:Интересно отметить, что одним из самых коротких и, безусловно, самых красивых способов распечатать ряд Фибоначчи в Perl является perl -le '$ b = 1; напечатайте $ a + = $ b, а напечатайте $ b + = $ a '.
Рекурсивное вычисление факториала
В этом примере мы определяем факториальную функцию и рекурсивно вызываем ее для вычисления факториала.
.sub _fact
# Get input parameter.
.param int n
# return (n > 1 ? n * _fact(n - 1) : 1)
.local int result
if n > 1 goto recurse
result = 1
goto return
recurse:
$I0 = n - 1
result = _fact($I0)
result *= n
return:
.return (result)
.end
.sub _main :main
.local int f, i
# We'll do factorial 0 to 10.
i = 0
loop:
f = _fact(i)
print "Factorial of "
print i
print " is "
print f
print ".\n"
inc i
if i <= 10 goto loop
# That's it.
end
.end
Давайте сначала посмотрим на подпункт _fact. Момент, который ранее был упущен, - это то, почему все имена подпрограмм начинаются с подчеркивания! Это делается просто для того, чтобы показать, что метка глобальна, а не привязана к конкретной подпрограмме. Это важно, поскольку метка становится видимой для других подпрограмм.
Первая строка, .param int n, указывает, что эта подпрограмма принимает один целочисленный параметр и что мы хотели бы ссылаться на регистр, в который она была передана, по имени n для остальной части подпрограммы.
Многое из того, что следует ниже, было замечено в предыдущих примерах, помимо чтения строки:
result = _fact($I0)
Эта единственная линия PIR на самом деле представляет собой довольно много линий PASM. Сначала значение в регистре $ I0 перемещается в соответствующий регистр, чтобы функция _fact приняла его как целочисленный параметр. Затем устанавливаются другие регистры, связанные с вызовами, после чего вызывается _fact. Затем, как только _fact возвращается, значение, возвращаемое _fact, помещается в регистр с учетом имени result.
Прямо перед концом подпрограммы _fact используется директива .return, чтобы гарантировать, что значение хранится в регистре; Именованный результат помещается в правильный регистр, чтобы код, вызывающий подпрограмму, видел его как возвращаемое значение.
Вызов _fact в main работает точно так же, как рекурсивный вызов _fact внутри самого под _fact. Единственная оставшаяся часть нового синтаксиса - это: main, записанный после .sub _main. По умолчанию PIR предполагает, что выполнение начинается с первой подпрограммы в файле. Это поведение можно изменить, отметив подпрограмму для начала с: main.
Компиляция в PBC
Чтобы скомпилировать PIR в байт-код, используйте флаг -o и укажите выходной файл с расширением .pbc.
parrot -o factorial.pbc factorial.pir
PIR против PASM
PIR можно превратить в PASM, запустив:
parrot -o hello.pasm hello.pir
PASM для последнего примера выглядит так:
_main:
set S30, "Hello world!\n"
print S30
end
PASM не обрабатывает выделение регистров и не поддерживает именованные регистры. Он также не имеет директив .sub и .end, вместо этого они заменяются меткой в начале инструкций.