Wprowadzenie
Tematem zadania jest interpreter, czyli "program wykonujący programy" w języku Malina ("MAłe LItery i NAwiasy klamrowe").
Składnia programów w Malinie opisana jest gramatyką:
Program -> CiągInstrukcji
CiągInstrukcji -> ε | CiągInstrukcji Instrukcja
Instrukcja -> InstrukcjaOdejmowania | InstrukcjaPętli
InstrukcjaOdejmowania -> Zmienna Zmienna
InstrukcjaPętli -> Zmienna { CiągInstrukcji }
Zmienna -> ZmiennaZwykła | ZmiennaSpecjalna
ZmiennaZwykła -> a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x
ZmiennaSpecjalna -> y|z
ε jest tu oznaczeniem słowa pustego a małe litery i nawiasy klamrowe to symbole końcowe gramatyki. Oprócz małych liter i klamerek, żadne inne znaki, nawet spacje, w programie nie mogą wystąpić.
Program w Malinie jest ciągiem instrukcji, które wykonujemy w kolejności od pierwszej do ostatniej.
Mamy dwa rodzaje instrukcji: odejmowanie i pętlę.
Instrukcja odejmowania postaci Zmienna Zmienna odejmuje wartość drugiej zmiennej od pierwszej. Np. instrukcji Maliny postaci ab odpowiada w Pascalu a := a - b.
Instrukcja pętli postaci Zmienna { CiągInstrukcji } wykonuje ciąg instrukcji w klamerkach dopóki wartość zmiennej przed klamerką otwierającą jest większa od zera. Np. instrukcji Maliny postaci a{...} odpowiada w Pascalu while a > 0 do begin ... end.
Program w Malinie ma dostęp do 26 zmiennych o nazwach od a do z. Zmienne od a do w mają wartości początkowe 0, a zmienna x ma wartość początkową 1. Dwie zmienne, y i z, mają specjalne znaczenie. Zmienna y służy do realizacji wejścia/wyjścia liczbowego (od
"lYczby"), a zmienna z do realizacji wejścia/wyjścia znakowego (od "Znaki").
Jeśli zmienna y wystąpiła na drugiej pozycji w instrukcji przypisania lub przed klamerką otwierającą w pętli, pobranie jej wartości powoduje odczytanie z wejścia liczby całkowitej. Np. instrukcja
ay wczytuje z wejścia liczbę i odejmuje ją od wartości zmiennej a.
Jeśli zmienna y wystąpiła na pierwszej pozycji w instrukcji odejmowania, wykonanie instrukcji powoduje wypisanie na wyjście liczby będącej wartością zmiennej z drugiej pozycji. Np. instrukcja ya wypisuje na wyjście wartość zmiennej a.
Jeśli zmienna z wystąpiła na drugiej pozycji w instrukcji przypisania lub przed klamerką otwierającą w pętli, pobranie jej wartości powoduje odczytanie z wejścia znaku i przekazanie jego kodu. Gdyby na wejściu znaków nie było, przekazaną wartością będzie -1. Np. instrukcja
az wczytuje z wejścia znak i odejmuje jego kod od wartości zmiennej a.
Jeśli zmienna z wystąpiła na pierwszej pozycji w instrukcji odejmowania, wykonanie instrukcji powoduje wypisanie na wyjście znaku o kodzie, będącym wartością zmiennej z drugiej pozycji tej instrukcji. Np. instrukcja za wypisuje na wyjście znak, którego kod jest zapisany na
zmiennej a.
Polecenie
Napisz interpreter programów w języku Malina.
Interpreter powinien sprawdzić, czy argument, z którym został wywołany, jest poprawnym składniowo programem w Malinie i jeśli tak, wykonać go, a jeśli nie, wypisać odpowiedni komunikat o błędzie.
Przykłady programów w Malinie
Wczytanie liczby i wypisanie liczby o 1 większej:
Sumowanie wyrazów ciągu nieujemnych liczb zakończonego zerem:
Wypisywanie liczb od 10 do 1:
./malina axaxbaabaxcacac{yccx}
Wyznaczanie algorytmem Euklidesa NWD dwóch liczb wczytanych z wejścia:
./malina aybycac{dddcdbd{baddcc}c{abcc}ca}vbyv
Wypisanie na wyjście wiersza o treści Hello:
./malina axaxbabacbcbdcdcededfefefczffafbfxfezffcfxzfzffcfbfxzfbcbxbxzb
Rozwiązanie zadania laboratoryjnego o medianie trzech liczb:
./malina aybycyeafbefe{dagbaaagbbbdee}haibjckjkawxlwk{yhkkll}l{mjmbnwm{yjmmnn}n{yinn}ll}
Rozwiązanie zadania laboratoryjnego o choince:
./malina axaxbabacbcbdcdcededsegegcgaucuanyini{kkkijkjxj{zsjx}jnjij{zgzgjx}zgzuix}icibixi{jnjxjxj{zsjx}zgzgzgzuix}
Informacje uzupełniające
Do rozwiązania zadania będzie potrzebnych kilka nowych funkcji standardowych. Przedstawimy je na przykładzie programu realizującego "szyfr" ROT13 (zastępowanie litery literą, która jest w alfabecie o 13 pozycji dalej, cyklicznie). Dla uproszczenia, poniższy program zajmuje się tylko małymi literami (rot13.pas):
program rot13;
var s : String;
i : Integer;
c : Char;
begin
if paramCount <> 1 then begin
writeln('Program oczekuje jednego argumentu');
halt
end;
s := paramStr(1);
for i := 1 to length(s) do begin
c := s[i];
if ('a' <= c) and (c <= 'z') then
write(chr(((ord(c) - ord('a') + 13) mod 26) + ord('a')))
else
write(c)
end;
writeln
end.
Mamy tu:
-
funkcje
paramCount() i paramStr()
-
procedurę
halt
-
typ
String, funkcję length(), odczytywanie znaków napisu
-
typ
Char, funkcje chr() i ord()
Przyda się też wczytywanie z wejścia pojedynczych znaków za pomocą operacji read() z argumentem będącym zmienną typu Char oraz funkcja eof(), której bezparametrowy wariant sprawdza, czy na wejściu jest koniec danych.