Учебники

Parrot – Примеры программирования

Программирование Parrot похоже на программирование на ассемблере, и вы получаете возможность работать на более низком уровне. Вот список примеров программирования, чтобы вы знали о различных аспектах программирования Parrot.

Классический Привет, мир!

Использование регистров

Суммирующие квадраты

Числа Фибоначчи

Вычислительный факториал

Компиляция в PBC

PIR vs. PASM

Классический Привет, мир!

Создайте файл с именем hello.pir, который содержит следующий код:

.sub _main
   print "Hello world!\n"
   end
.end

Затем запустите его, набрав:

parrot hello.pir

Как и ожидалось, будет отображаться текст «Привет, мир!» на консоли, после чего следует новая строка (из-за \ n).

В этом вышеприведенном примере «.sub _main» гласит, что последующие инструкции составляют подпрограмму с именем «_main», пока не встретится «.end». Вторая строка содержит инструкцию печати. В этом случае мы вызываем вариант инструкции, которая принимает постоянную строку. Ассемблер сам решает, какой вариант инструкции использовать для нас. Третья строка содержит инструкцию ‘end’, которая приводит к завершению интерпретатора.

Использование регистров

Мы можем изменить hello.pir, чтобы сначала сохранить строку Hello world! \ N в регистре, а затем использовать этот регистр с инструкцией печати.

.sub _main
   set S1, "Hello world!\n"
   print S1
   end
.end

Здесь мы указали, какой именно регистр использовать. Однако, заменив S1 на $ S1, мы можем делегировать выбор, какой регистр использовать для Parrot. Также возможно использовать обозначение = вместо написания заданной инструкции.

.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 регистров), string (для 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++;
}

ПРИМЕЧАНИЕ. В качестве интересного факта один из самых коротких и, безусловно, самых красивых способов распечатать ряд Фибоначчи в Perl – это perl -le ‘$ b = 1; выведите $ a + = $ b, а print $ 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, помещается в регистр с заданным именем результата.

Непосредственно перед концом .ef субфактора _fact используется директива .return, чтобы гарантировать значение в регистре; именованный результат помещается в правильный регистр, чтобы код, вызывающий подпрограмму, рассматривался как возвращаемое значение.

Вызов _fact в main работает точно так же, как рекурсивный вызов _fact в самом субфакторе. Единственный оставшийся бит нового синтаксиса – это: main, написанный после .sub _main. По умолчанию PIR предполагает, что выполнение начинается с первого элемента в файле. Это поведение можно изменить, отметив подпрограмму для запуска с: main.

Компиляция в PBC

Чтобы скомпилировать PIR в байт-код, используйте флаг -o и укажите выходной файл с расширением .pbc.

parrot -o factorial.pbc factorial.pir

PIR vs. PASM

PIR можно превратить в PASM, запустив:

parrot -o hello.pasm hello.pir

PASM для последнего примера выглядит следующим образом:

_main:
   set S30, "Hello world!\n"
   print S30
end

PASM не обрабатывает распределение регистров и не обеспечивает поддержку именованных регистров. Он также не имеет директив .sub и .end, вместо этого заменяя их меткой в ​​начале инструкций.