Parrot-프로그래밍 예

Parrot 프로그래밍은 어셈블리 언어 프로그래밍과 유사하며 낮은 수준에서 작업 할 수 있습니다. 다음은 Parrot 프로그래밍의 다양한 측면을 알 수있는 프로그래밍 예제 목록입니다.

  • 클래식 안녕하세요!

  • 레지스터 사용

  • 제곱합

  • 피보나치 수

  • 계승 계산

  • PBC로 컴파일

  • PIR 대 PASM

클래식 안녕하세요!

다음 코드가 포함 된 hello.pir라는 파일을 만듭니다.

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

그런 다음 다음을 입력하여 실행하십시오.

parrot hello.pir

예상대로 'Hello world!'라는 텍스트가 표시됩니다. 콘솔에서 새 줄이 뒤 따릅니다 (\ n 때문에).

위의 예에서 '.sub _main'은 '.end'를 만날 때까지 '_main'이라는 이름의 서브 루틴을 구성하는 명령어를 나타냅니다. 두 번째 줄에는 인쇄 명령이 포함됩니다. 이 경우 상수 문자열을 받아들이는 명령어의 변형을 호출합니다. 어셈블러는 우리를 위해 사용할 명령어의 변형을 결정합니다. 세 번째 줄에는 인터프리터가 종료되도록하는 '종료'명령어가 포함되어 있습니다.

레지스터 사용

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 번째 수입니다. 다음은 처음 20 개의 피보나치 수를 찾는 간단한 Parrot 어셈블러 프로그램입니다.

# 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에서 반환 한 값이 이름 결과가 지정된 레지스터에 배치됩니다.

_fact 하위의 .end 바로 직전에 .return 지시문을 사용하여 레지스터에있는 값을 확인합니다. 명명 된 결과는 하위를 호출하는 코드에서 반환 값으로 볼 수 있도록 올바른 레지스터에 배치됩니다.

main에서 _fact에 대한 호출은 sub _fact 자체 내에서 _fact에 대한 재귀 호출과 동일한 방식으로 작동합니다. 새로운 구문의 유일한 나머지 부분은 .sub _main 뒤에 작성된 : 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 지시문이 없으며 대신 지시문 시작시 레이블로 대체합니다.