Программирование Parrot похоже на программирование на ассемблере, и вы получаете возможность работать на более низком уровне. Вот список примеров программирования, чтобы вы знали о различных аспектах программирования Parrot.
Классический Привет, мир!
Создайте файл с именем 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, вместо этого заменяя их меткой в начале инструкций.