AKADEMIA PODLASKA
Interpreter Pascal’a.
Projekt wykonany w
ramach przedmiotu: „Sieci i systemy wirtualne”
autorzy
projektu:
Mariusz
Adameczek
Marek
Obuchowski
Jacek
Mucha
Dawid
Dembowski
Siedlce, 2003
Spis treści
1. Cel projektu.................................................................................... 3
2. Wymagania sprzętowe............................................................... 4
3. Pliki projektu.................................................................................. 5
4. Uruchomienie Interpretera................................................... 6
5. Specyfikacja BNF............................................................................ 7
6. Zasady i ograniczenia
pisania programów................... 11
7. Opis działania
Interpretera................................................. 13
8. Opis kodu programu.................................................................. 19
Celem projektu było stworzenie interpretera wybranego języka programowania przy wykorzystaniu dowolnego języka programowania.
Interpreter powinien udostępniać następujące możliwości programistyczne:
– funkcja „main” programu,
– deklaracja zmiennych,
– tablice statyczne,
– zmienne dynamiczne,
– operacja przypisania,
– wypisanie na ekran,
– wczytanie z klawiatury,
– pętla,
– operacje matematyczne: „+”, „-”, „*”, „/”
Wybranym przez nas językiem programowania, który będzie interpretowany jest Pascal. Natomiast kod interpretera napisany został przy użyciu języka Java 1.4.
Program „Interpreter” został napisany w języku Java.
Wymagania programu:
● komputer PC,
● dowolny system operacyjny,
● zainstalowany interpreter Javy JDK 1.4 lub nowszy.
Plik źródłowy – Interpreter.java
Pliki programu:
AnalizaPliku.class
Funkcje.class
Instrukcja.class
Interpreter.class
Tablica.class
Typy.class
Wskaznik.class
Zmienna.class
Pliki projektu dostępne są również na sourceforge pod adresem:
http://sourceforge.net/projects/pascal2delphi
Do projektu dołączone są również pliki z przykładowymi programami pokazujące działanie Interpretera. Opis tych programów znajduje się w następnym rozdziale.
W celu uruchomienia programu należy uruchomić plik:
Interpreter.class
Interpreter - bez parametrów wyświetli informację o programie.
Interpreter <nazwa_pliku_z_kodem_pascala>
np. Interpreter SILNIA.PAS
Do projektu dołączone zostały przykładowe pliki z kodem pascalowym.
– PLIK1.PAS – jest plikiem zawierającym kod programu, w którym wykorzystano wszystkie możliwe do zastosowania instrukcje programowe. Plik ten powstawał wraz z postępem prac nad Interpreterem uzyskując ostatecznie obecną formę. Zawiera zagnieżdżone pętle, operacje na wskaźnikach, instrukcje wypisania na ekran, wczytania z klawiatury, operacje przypisania, operacje na tablicach, wyrażenia matematyczne, itd. Pozwala na dogłębne przetestowanie Interpretera. Dokonując niewielkich modyfikacji można testować zachowanie Interpretera na różne sytuacje.
– SLNIA.PAS – program obliczający silnię. Program najpierw poprosi o wpisanie liczby, a następnie policzy silnię tej liczby.
– SUMA.PAS – program zawiera kilka pętli, wyświetla w pętli liczby, liczy, sumę liczb parzystych od 0 do 100, prosi o wpisanie tekstu, który później wyświetla na ekranie monitora.
Specyfikacja języka została opisana przy pomocy notacji BNF (Backus-Naur Definition).
W notacji tej posługujemy się następującymi formami opisu:
– ::= - przypisanie
– [] - element może wystąpić 0 lub 1 raz (co najwyżej 1 raz)
– {} - wybierz jeden z elementów występujących (dokładnie 1 raz)
– {}+ - element musi wystąpić co najmniej 1 raz (1 lub więcej razy)
– {}* - element może wystąpić 0 lub więcej razy (0 lub więcej razy)
– <> - zostało zdefiniowane wcześniej.
Treść specyfikacji.
zero::={0}
cyfra1_9::={1|2|3|4|5|6|7|8|9}
cyfra::={<zero>|<cyfra1_9>}
mala_litera::={a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z}
duza_litera::={A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z}
litera::={<mala_litera>|<duza_litera>}
znak::={<cyfra>|<litera>|_}
nazwa::=<litera>{<znak>}*
nazwa_zmiennej::=<nazwa>
nazwa_programu::=<nazwa>
spacja={" "}
cudzyslow::={"}
znak_inny::={~|`|!|@|#|$|%|^|&|*|(|)|-|+|=|[|]|{|}|
;|:|<|>|,|.|/|?|\|"|"|<spacja>}
znak_x::={<znak>|<znak_inny>}
tekst::=<cudzyslow>{<znak_x>}*<cudzyslow>
operator_matematyczny::={+|-|*|/}
liczba_nat_bez_0::=<cyfra1_9>{<cyfra>}*
liczba_naturalna::=["-"]{<zero>|<liczba_nat_bez_0>}
liczba_zmiennoprzecinkowa::=
["-"]<liczba_naturalna>"."{<cyfra>}+
liczba::={<liczba_naturalna>|<liczba_zmiennoprzecinkowa>}
SŁOWA KLUCZOWE
s_array::={array}
s_begin::={begin}
s_do::={do}
s_end::={end}
s_for::={for}
s_nil::={nil}
s_of::={of}
s_program::={program}
s_to::={to}
s_var::={var}
TYPY
id_typu_podstawowego::={integer|real|string}
zakres_tablicy::=<liczba_naturalna>".."<liczba_naturalna>
typ_tablicowy::=<s_array>"["<zakres_tablicy>"]"<s_of>
<id_typu_podstawowego><separator>
typ_wskznikowy::="^"{<id_typu_podstawowego>|<typ_tablicowy>}<separator>
id_typu::={<id_typu_podstawowego>|<typ_tablicowy>|<typ_wskaznikowy>}
DEKLARACJA ZMIENNEJ
deklaracja_zmiennych::=
<nazwa_zmiennej>{","<nazwa_zmiennej>}*":"<id_typu><separator>
TABLICE I ZMIENNE DYNAMICZNE
zmienna_wskazywana::=<nazwa_zmiennej>"^"
indeks::={<liczba_naturalna>|<nazwa_zmiennej>}
zmienna_tab::=<nazwa_zmiennej>"["<indeks>"]"
zmienna_tab_wskazywana::=<zmienna_wskazywana>"["<indeks>"]"
utworzenie_zmiennej_dynamicznej::=
"new("<nazwa_zmiennej>")"
skasowanie_zmiennej_dynamicznej::=
"dispose("<nazwa_zmiennej>")"
OPERACJE MATEMATYCZNE
zmienna::={<nazwa_zmiennej>|<zmienna_wskazywana>|
<zmienna_tab>|<zmienna_tab_wskazywana>
}
wartosc::={<liczba>|<zmienna>}
operacja_matematyczna::=<wartosc>{<operator_matematyczny><wartosc>}*
operacja_mat_w_nawias::=
{<operacja_matematyczna>|"("<operacja_matematyczna>")"}
operacja_mat_z_nawias::=
<operacja_mat_w_nawias>{<operator_matematyczny><operacja_mat_w_nawias>}*
operacja_mat_z_nawias - umożliwia kombinacje z nawiasami ale bez zagnieżdżeń nawiasów. W szczególnym przypadku da: operacja_matematyczna lub operacja_mat_w_nawias, a nawet liczbę lub wartosc (w nawiasach i bez nawiasów). Dzięki temu możliwe są konstrukcje takie jak:
2; a; a + b; a * (b - c);
(a + b) / 3; a + b * (c - d); (a + b) * (c - d);
wyrazenie_mat::={
"("{<operacja_mat_z_nawias>|<wyrazenie_mat>}
{<operator_matematyczny><wyrazenie_mat>}*")"
|
{<operacja_mat_z_nawias>|<wyrazenie_mat>}
{<operator_matematyczny><wyrazenie_mat>}*
}
OPERACJA PRZYPISANIA
operacja_przypisania::=
<zmienna>":="{<liczba>|<zmienna>|<wyrazenie_mat>|<tekst>|<s_nil>}
<separator>
OPERACJE EKRAN, KLAWIATURA
output::={<liczba>|<zmienna>|<wyrazenie_mat>|<tekst>}
proc_write::="write("<output>{","<output>}*")"<separator>
proc_writeln::="writeln("<output>{","<output>}*")"<separator>
proc_read::=
"read("<zmienna>")"<separator>
proc_readln::=
"readln("<zmienna>")"<separator>
wypisanie_na_ekran::={<proc_write>|<proc_writeln>}
wczytanie_z_klawiatury::={<proc_read>|<proc_readln>}
INSTRUKCJA I BLOK INSTRUKCJI
instrukcja::={<operacja_przypisania> |
<wypisanie_na_ekran>
|
<wczytanie_z_klawiatury>
|
<utworzenie_zmiennej_dynamicznej> |
<skasowanie_zmiennej_dynamicznej> |
<petlaFor>
}
blok_instrukcji::=<s_begin> {<instrukcja>}* <s_end><separator>
PĘTLA
petlaFor::=
<s_for><zmienna>":="{<liczba_naturalna>|<zmienna>|<wyrazenie_mat>}
<s_to>
{<liczba_naturalna>|<zmienna>|<wyrazenie_mat>}
<s_do>
<blok_instrukcji>
PROGRAM
def_programu::=
[<s_program><nazwa_programu><separator>]
[<s_var>{<deklaracja_zmiennych>}+]
<s_begin>
{<instrukcja>}*
<s_end>"."
1. Małe i duże litery są rozróżnialne.
2. Każdą instrukcję piszemy w oddzielnej linii i małymi literami.
3. Dopuszczalne są białe znaki (spacje i tabulatory). W przypadku, gdy znaki te nie będą tolerowane pojawi się odpowiedni komunikat o błędzie.
4. W deklaracji tablicy zakres tablicy musi spełniać warunki:
- min: 0,
- max 65535,
- pierwszy indeks <= drugi indeks.
5. Przy odwoływaniu się do komórek tablicy dozwolone jest korzystanie z liczb całkowitych bez znaku oraz nazw zmiennych (np: tab[5]; tab^[a];).
6. Pętle piszemy jak w przykładzie poniżej:
for i:=1 to 50 do begin -> to musi być w jednej linii
................................
instrukcje lub ich brak (pętla może być pusta)
................................
end;
Czyli w pętli zawsze stosujemy instrukcję złożoną.
7. Pętle mogą być zagnieżdżone.
8. W stringach stosujemy apostrof: " (podwójny).
9. Instrukcje write i writeln mogą też być stosowane tak:
write(.., .., ...);
np: writeln("wiek dinozaura = ", wiek);
writeln("wynik obliczenia = ", 2*(a+3)-9);
10. Zakres liczby całkowitej „integer” - odpowiednik „int” w Javie (32 bity).
11. Zakres liczby rzeczywistej „real” - odpowiednik „double” w Javie (64 bity).
12. Nie ma części TYPE ale typy są rozpoznawane np.
var tab1, tab2: ^array [1.50] of integer;
tab3: ^array [1.50] of integer;
tabAB: ^array [1.40] of integer;
to przypisanie: tab1:= tab3; jest poprawne i nie ma błędu,
natomiast : tab1:= tabAB; spowoduje zgłoszenie błędu niezgodności typów.
Działanie interpretera można podzielić na 3 główne etapy.
Rys. 1. Ogólna zasada działania Interpretera.
W pierwszym etapie następuje wczytanie pliku oraz analiza leksykalna i składniowa kodu programu. W trakcie tej operacji tworzone również są dwie specjalne listy: zmiennych i instrukcji. W listach tych umieszczone są stringi zmiennych i instrukcji według ściśle określonej notacji.
Ogólna notacja stringów w liście zmiennych wygląda następująco:
typ_zmiennej:[typ_składowy:indeks_początkowy:indeks_końcowy:]
nazwa_zmiennej:numer_linii
Możliwe warianty to:
● integer:nazwa_zmiennej:numer_linii
● real:nazwa_zmiennej:numer_linii
● string:nazwa_zmiennej:numer_linii
● tablica:typ_składowy:indeks_początkowy:indeks_końcowy: nazwa_zmiennej:numer_linii
● ^integer:nazwa_zmiennej:numer_linii
● ^real:nazwa_zmiennej:numer_linii
● ^string:nazwa_zmiennej:numer_linii
● ^tablica:typ_składowy:indeks_początkowy:indeks_końcowy: nazwa_zmiennej:numer_linii
Typ_składowy oznacza typ elementów tablicy i może przyjmować wartości: integer, real lub string, zaś indeks_początkowy i indeks_końcowy są liczbami całkowitymi i opisują rozmiar tablicy.
Znak „^” oznacza, że dana zmienna jest wskaźnikiem do określonego dalej typu.
Numer_linii mówi o tym, w której linii programu została zadeklarowana dana zmienna.
Ogólna notacja stringów w liście instrukcji wygląda następująco:
nazwa_instrukcji:[arg_1:arg_2: ... arg_N:]numer_linii
Możliwe warianty to:
● przypisz:na_co_przypisujemy:co_przypisujemy:numer_linii
● write:do_wyświetlenia:numer_linii
● writeln:do_wyświetlenia:numer_linii
● read:na_co_zczytujemy:numer_linii
● readln:na_co_zczytujemy:numer_linii
● new:nazwa_zmiennej:numer_linii
● dispose:nazwa_zmiennej:numer_linii
● petlaFor:zmienna_iteracyjna:wyrażenie_1:wyrażenie_2: ilość_instrukcji_w_pętli:numer_linii
Jeśli pierwszy etap zakończy się pomyślnie tzn. program nie zawiera błędów leksykalnych i składniowych, następuje utworzenie „właściwych” list: zmiennych i instrukcji, które posłużą do wykonania programu.
W trakcie tego etapu wykorzystane zostają utworzone wcześniej listy.
Jako pierwsza tworzona jest lista zmiennych. Wczytane zostają do niej wszystkie zadeklarowane zmienne. Każda zmienna zawiera następujące pola:
– int typ
– Object wartosc
Typ zmiennej zapisany jest jako wartość integer, która stanowi powiązanie z reprezentacją typu w postaci stringu. Powiązania nr_typu – opis_typu znajdują się w wcześniej utworzonym drzewie typów. Pozwala to na szybkie odnalezienie określonego numeru typu (na podstawie jego reprezantacji tekstowej) i ewentualne porównanie z typem typu innej zmiennej. Ponadto umieszczone tam opisy typów zawierają informacje, które posłużą do dynamicznego tworzenia zmiennych, w szczególności wskaźników do tablic.
Wartość zmiennej może być dowolnego typu, a dostęp do niej możliwy jest dzięki operacji rzutowania. Ostatecznie w tablicy zmiennych będą przechowywane wartości takie jak:
– liczba całkowita (integer),
– liczba zmiennoprzecinkowa (real),
– tekst (string),
– tablica
– wskaźnik do integer (^integer),
– wskaźnik do real (^real),
– wskaźnik do string (^string),
– wskaźnik tablicy (^tablica),
– wyrażenie matematyczne – po wczytaniu instrukcji.
W trakcie wczytywania zmiennych do tablicy następuje sprawdzenie poprawności zakresów tablicy oraz wyszukanie duplikatów nazw zmiennych.
Kolejnym krokiem w drugim etapie jest utworzenie „właściwej” listy instrukcji. Przed wykonaniem tego kroku utworzona zostaje lista stałych, które zostaną do niej wczytane podczas analizy kolejnych instrukcji. Ostatecznie wszystkie stałe znajdą się w tablicy zmiennych, zaś sama lista stałych potrzebna jest tylko do „wyłapania” powtarzających się wartości stałych. Dzięki temu, jeśli w kodzie programu w kilku różnych instrukcjach istnieją odwołania do takiej samej stałej (np.: liczba 9, tekst „jakis tekst”), to po wczytaniu wszystkich tych instrukcji będą się one odwoływały do jednej i tej samej wartości umieszczonej w tablicy zmiennych.
Wszystkie wczytane instrukcje zawierają następujące pola:
– int nrInstrukcji – numer instrukcji,
– Object[] argumenty – argumenty instrukcji,
– Int nrLinii – numer linii instrukcji.
Numery instrukcji mogą przyjmować wartości:
–
0 - przypisz
–
1 - write
–
2 - writeln
–
3 - read
–
4 - readln
–
5 - new
–
6 - dispose
–
7 - petlaFor
Tablica argumenty zawiera informacje o numerach zmiennych do których odwołuje się instrukcja oraz informacje o sposobie odwołania się do zmiennych. Może zawierać następujące wartości:
–
wartość null,
–
obiekt typu Integer
–
obiekt typu int[]
Rodzaj instancji danego obiektu oraz wartości w nim zapisane pozwalają jednoznacznie zidentyfikować zmienną do której instrukcja się odwołuje oraz rodzaj odwołania. Przykładowo zmienna a może być zmienną typu: wskaźnik do tablicy integer. Zatem mogą wystąpić odwołania: a i a^[3]. Odczytując typ zmiennej oraz instancję reprezentującą daną zmienną w tablicy argumenty możliwa jest późniejsza identyfikacja sposobu odwołania do niej.
Oprócz opisanych czynności podczas tworzenia listy instrukcji wykonywane są również następujące zadania:
● powiązanie nazw zmiennych w instrukcjach ze zmiennymi w tablicy zmiennych oraz „wyłapanie” nieznanych identyfikatorów,
● sprawdzenie zgodności typów,
● ustalenie typów wyników operacji matematycznych.
Tak przygotowane i sprawdzone zmienne oraz instrukcje posłużą w trzecim etapie do wykonania programu. Wykonanie programu polega na sekwencyjnym pobraniu instrukcji z listy instrukcji i wykonaniu każdej instrukcji na przypisanych jej argumentach czyli zmiennych. W celu realizacji powyższego zaimplementowana została pętla:
do {
switch (numer_instrukcji) {
case 0 : instrukcja przypisania
case 1 : instrukcja write
case 2 : instrukcja writeln
case 3 : instrukcja read
case 4 : instrukcja readln
case 5 : instrukcja new
case 6 : instrukcja dispose
case 7 : instrukcja pętlaFor (aktywacja pętli i dodanie jej na stos_pętli)
}
if (stos_pętli jest nie pusty) {
steruj_przebiegiem_pętli;
ustal_wartosc licznik_instrukcji;
} else licznik_instrukcji++
} while (licznik_instrukcji < kolejny_numer_instrukcji);
W trakcie wykonywania programu sprawdzane są:
● odwołania do tablic (czy nie przekraczają zakresu tablicy),
● odwołania do wskazań zmiennych dynamicznych (czy nie nastąpiła próba odwołania do wartości, która nie istnieje);
● zakresy liczb, itp.
Na każdym etapie działania Interpretera może być wygenerowany komunikat o błędzie, który oprócz numeru linii i jej zawartości zawiera opis błędu.
Interpreter został napisany przy użyciu języka Java. Kod Interpretera składa się z kilku klas, w których zaimplementowano poszczególne elementy programu. Poniżej zostały wyszczególnione wszystkie zaimplementowane klasy oraz ich pola i metody.
Zaimportowane klasy:
import
java.io.*;
import java.util.*;
import java.util.regex.*;
Klasa „AnalizaPliku” służy do wczytania pliku oraz do analizy składniowej i leksykalnej kodu programu. Większość podanych poniżej metod bazuje na wyrażeniach regularnych. Wszystkie te wyrażenia można zastosować w wielu innych językach wykorzystujących wyrażenia regularne. Wpisując dane wyrażenie regularne można „sparsować” plik tekstowy tak aby był on możliwy do wykonania przez jądro interpretera. Podstawowe języki wykorzystujące wyrażenia regularne to: Perl, Python, gawk(awk) oraz narzędzia skryptowe takie jak sed, egrep.
class AnalizaPliku {
private String nazwaPliku;
private Vector tablicaLiniiOrginal; - oryginalna tablica linii pliku.
private
Vector tablicaLinii; - tablica linii pliku wstępnie zmodyfikowana podczas
wczytywania pliku.
private
Vector tabZmiennych; - tablica „stringów” zmiennych tworzona podczas analizy
leksykalnej i składniowej pliku.
private
Vector tabInstrukcji; - tablica
„stringów” instrukcji tworzona podczas analizy leksykalnej i składniowej pliku.
private
Vector defzmiennych; - tablica pomocnicza z nazwami instrukcji.
AnalizaPliku(String
nazwaPliku) – konstruktor.
String
wczytajPlik() – metoda wczytująca plik, zwraca ewentualny komunikat błędu lub pusty
string - jeśli plik został wczytany bez błędu.
String
removeallspace(String inputStr) - metoda usuwa wszystkie białe znaki z podanego
„inputStr” i zwraca string bez białych znaków.
String
removeDuplicateWhitespace(String inputStr)
int separateandremovevar(String inputStr,int numerlinii) - służy do
wstawiania do tablicy zmiennych i sprawdzania poprawności wpisanych zmiennych.
Zwraca kod błędu.
public
static String find(String patternStr, CharSequence input) - jedna z
najważniejszych metod. Sprawdza wystąpienie danego stringa z podanym
regularnym wyrażeniem. Zwraca znalezioną cześć.
String[] cotozainstrukcja(String inputStr) - sprawdza czy
dany string pasuje do definicji danej instrukcji i jeżeli pasuje to zwraca
instrukcje z wcześniej zdefiniowanym ciągiem znaków.
Object analizujProgram() - główna metoda
która dzieli cały dostępny kod z vector’a „tabLinii” na trzy grupy: program,
definicja zmiennych oraz ciało programu a także uruchamia wcześniejsze metody
które doprowadzają do postaci zrozumiałej dla jądra kompilatora.
String getLiniaOrginal(int nrLinii) – metoda zwraca
linię programu o numerze podanym w argumencie.
String[][] getTabZmiennych() – zwraca tablicę
zmiennych podzielonych na poszczególne argumenty. Bazuje na metodach z klasy
„Funkcje”.
String[][] getTabInstrukcji() – zwraca tablicę
instrukcji podzielonych na poszczególne argumenty. Bazuje na metodach z klasy
„Funkcje”.
void
usunTabliceLinii()
void
usunTabliceZmiennych()
void usunTabliceInstrukcji()
Vector getTL()
Vector getTZ()
Vector getTI()
private static void wyswietlTablice(Vector
tablica)
void wyswietlTabliceLinii()
void wyswietlTabliceZmiennych()
void wyswietlTabliceInstrukcji()
}
Klasa „Funkcje” jest swego rodzaju zbiorem funkcji, które są wykorzystywane w innych klasach. Są to zazwyczaj funkcje ogólnego zastosowania. Nie zawiera żadnych pól. Metody to:
class
Funkcje {
static
Vector getVParameters(String linia, String chrs) - pobiera ze stringu linia parametry (stringi) rozdzielone
znakami (stringami) chrs.
static Vector getVParameters(String linia, String
chrs,
String chrs1) – działa podobnie jak „getVParameters” z tą różnicą, że
pomija znaczenie stringów chrs wewnątrz stringów chrs1.
static String[] getSAParameters(String linia,
String chrs) – działa jak „getVParameters”
tylko, że zwraca wynik w postaci tablicy.
static String[][] getTabTab(Vector v, String chrs) – zwraca tablicę tablic parametrów.
static String[] getSAParameters(String linia,
String chrs,
String chrs1) – podobnie jak wyżej.
static String[][] getTabTab(Vector v, String chrs,
String chrs1) – podobnie jak wyżej.
static boolean czyLiczbaNaturalna(String str)
static boolean czyLiczbaRzeczywista(String str)
static boolean czyTekst(String str)
static boolean czyNazwaZmiennej(String str)
static
boolean czyZmiennaWskazywana(String str)
static boolean czyZmiennaTablicowa(String str)
static
boolean czyZmiennaTablicowaWskazywana(String str)
Reszta poniższych metod klasy Funkcje służy do
sprowadzenia wyrażenia matematycznego do postaci „odwrotnej notacji polskiej”
ONP. Główna metoda to „getONP(...)”, której argumentem jest wyrażenie
matematyczne z nawiasami. Wyrażenie zostaje przekonwertowane i zwrócone w
postaci ONP jako string.
private
static int priorytet(char operator)
private
static int sprawdzZnak(char c)
private
static void naStos(char element, Vector stos)
private
static char zeStosu(Vector stos)
static
String getONP(String wyrazenieMatematyczne)
}
Klasa „Typy” służy do
przechowywania wszystkich typów zmiennych używanych w działającym programie.
Posiada metody służące do szukania i dodawania określonego typu.
class
Typy {
private
static TreeMap TYPY; - drzewo przechowywanych typów.
private
static int ileTypow; - ilość typów.
Typy()
static
boolean containsTyp(String strTyp)
static
boolean containsTyp(int intTyp)
static
int getTyp(String strTyp)
static
String getTyp(int intTyp)
static
int addTyp(String strTyp)
}
Klasa „Tablica” służy do
budowania tablic. Posiada metody umożliwiające stworzenie nowej tablicy,
modyfikowanie zawartości już istniejącej oraz odczytanie zawartości określonej
komórki tablicy. Klasa „Zmienna” posiada pole „wartosc”, które może być
elementem typu „Tablica”.
class
Tablica {
private
Object tablica; - tablica.
private
int rozmiar; - rozmiar tablicy.
private
int indeksPocz; - indeks początkowy tablicy.
private
int typSkladowy; - typ składowy tablicy.
Tablica(int
typ,int indPocz, int indKon) – konstruktor.
boolean
czyJestIndeks(int ktory) – sprawdza istnienie indeksu „który” w tablicy.
boolean
zapiszElement(int ktory, Object arg) – zapisuje obiekt „arg” do tablicy pod indeksem
„który”.
Object
getElement(int ktory) – zwraca pobrany z tablicy element o numerze
„który”.
}
Klasa „Wskaznik” posiada pola
i metody umożliwiające implementację zmiennych dynamicznych. Podobnie, jak w
przypadku tablic, pole „wartosc” z klasy „Zmienna” może być elementem typu
„Wskaznik”.
class
Wskaznik {
Object
object;
Wskaznik() – konstruktor.
Wskaznik(Object
object) – konstruktor.
boolean
czyNull() – sprawdza, czy wskaźnik jest null.
Object
getObject() – zwraca wskazywany obiekt;
void
setObject(Object object) – ustawia wartość wskazywaną.
void
dispose() – usunięcie wskazywanego obiektu.
}
Klasa „Zmienna” to jedna z
najważniejszych klas. Stanowi podwalinę listy zmiennych używanej w czasie
działania programu. Posiada pola i metody umożliwiające wszelkie operacje na
zmiennych.
class
Zmienna {
private
int typ; - numer typu zmiennej.
private
Object wartosc; - wartość zmiennej.
Zmienna(String[]
tabTyp, Object wart) – konstruktor.
Zmienna(String
strTyp, Object[] skladniki, Vector tabZmiennych) – konstruktor
tylko dla wyrażeń matematycznych.
static
int getNrZmiennej(Object odwolanie)
Integer
getInteger()
private
Object wykonajDzialanie(String operator,
Object arg1,
Object arg2) – metoda ta wykonuje działanie arytmetyczne na argumentach „arg1” i „arg2”,
po czym zwraca wynik lub kod błędu.
Object[]
getWartosc(Object argInstrukcji, Vector tabZmiennych) – służy do odczytania
wartości zmiennej. Odczyt zależy od „argInstrukcji”, która jest swego rodzaju
„odwołaniem” do zmiennej, mówiącym jakiego rodzaju wartość ma zostać zwrócona
(np. czy wskaźnik, czy wartość wskazywana). Metoda potrafi obliczyć także
wartość wyrażenia matematycznego i zwrócić tę wartość jako wynik. W przypadku niepomyślnego wykonania
zwraca odpowiedni kod błędu.
int
setWartosc(Object odwolanie, Object value, Vector tabZmiennych) – ustawia
wartość zmiennej na „value”. Zwraca ewentualny kod błędu.
void
setInteger(int value)
void
newWskaznik() – tworzy nowy wskaźnik.
void
disposeWskaznik() – usuwa wskaźnik.
void
setNil() – ustawia wskaźnik na Nil.
int
getTyp()
boolean
czyInteger()
boolean
czyReal()
boolean
czyTypPodstawowy()
boolean
czyWskaznik()
boolean
czyWskaznikDoInteger()
boolean
czyWskaznikDoReal()
boolean
czyWskaznikDoTypuPodstawowego()
boolean
czyTablica()
boolean
czyTablicaInteger()
boolean
czyTablicaReal()
boolean
czyWskaznikDoTablicy()
boolean
czyWskaznikDoTablicyInteger()
boolean
czyWskaznikDoTablicyReal()
boolean
czyWyrazenieMat()
boolean
czyWyrazenieMatInteger()
int
getTypKoncowy() – każda zmienna niezależnie od jej typu, ostatecznie
przechowuje typ podstawowy (np. wskaźnik do integer). Metoda zwraca numer typu
składowego, którym jest ostatecznie integer, real lub string.
boolean
czyKoncowyInteger()
boolean
czyKoncowyReal()
}
Klasa „Instrukcja” stanowi
szkielet dla instrukcji umieszczonych w
liście instrukcji. Pozwala na usystematyzowanie informacji niezbędnych do wykonania
instrukcji.
class
Instrukcja {
private
int nrInstrukcji; - numer instrukcji.
private
Object[] argumenty; - argumenty instrukcji.
private
int nrLinii; - numer linii z której pochodzi instrukcja.
Instrukcja(int
nrInstrukcji, Object[] argumenty, int nrLinii) – konstruktor
instrukcji.
int
getNrInstrukcji()
Object[]
getArgumenty()
int
getNrLinii()
int
przypisz(Vector tabZmiennych) – metoda służąca do wykonania instrukcji przypisania.
int
write(Vector tabZmiennych, String naKoniecLinii) – metoda służąca
do wykonania instrukcji write i writeln.
int
read(String zKlawiatury, Vector tabZmiennych) – metoda służąca
do wykonania instrukcji read i readln.
Object
aktywujPetleFor(int pc, Vector tabZmiennych) – służy do aktywacji pętli, tzn.
tworzy obiekt – tablicę – zawierającą wszelkie niezbędne informacje potrzebne
przy wykonywaniu pętli. Obiekt ten umieszczany jest później na stosie pętli.
}
Klasa „Interpreter” zawiera
metodę główną służącą do uruchomienia Interpretera. W metodzie tej
zaimplementowana jest pętla, w której wykonuje się program.
class
Interpreter {
static
void procBLAD(int nrLinii, AnalizaPliku ap, int nrBladu) – metoda ta zawiera
komunikaty błędów. Służy do wyświetlania wszystkich informacji związanych z
błędem tj. numeru linii w której wystąpił błąd, opisu błędu oraz tekstu danej
linii z błędem.
static
int getNumerZmiennej(String nazwaZmiennej, String[][] tabZ) – metoda zwraca
numer zmiennej w tablicy zmiennych na podstawie jej nazwy.
static
int getRodzajArgumentu(String argument) – służy do określenia rodzaju argumentu instrukcji.
Zwraca numer, gdzie:
0 - nil
1 – liczba
naturalna
2 – liczba
zmiennoprzecinkowa
3 - tekst
4 – nazwa
zmiennej
5 – zmienna
wskazywana
6 – zmienna
tablicowa
7 – zmienna
tablicowa wskazywana
8 – wyrażenie
matematyczne
Metoda stosowana
w metodzie „konwertujArgument(...)”.
static
int getOrAddNumerZmiennejStalej(Object wartoscStalej,
Vector stale, Vector tabZmiennych) – metoda ta,
służy do przeszukiwania tablicy stałych w celu optymalizacji ilości odwołań do
stałych reprezentujących taką samą wartość. Dopisuje nowe wartości do tablicy
stałych i listy zmiennych.
static
Object konwertujArgument(String arg, String dozwolone,
int nrLinii, AnalizaPliku ap,
Vector tabZmiennych,
String[][] tabZ, Vector stale) – bardzo ważna
metoda. Przeprowadza wstępną analizę poprawności zastosowanych argumentów w instrukcjach
oraz wiąże konkretne zmienne z argumentami instrukcji.
static
void budujTabZmiennychiTabInstrukcji(AnalizaPliku ap,
Vector tabZmiennych,
Vector tabInstrukcji) – podstawowa metoda służąca do budowy „właściwych” list:
zmiennych i instrukcji. Korzysta z metod opisanych powyżej. Przeprowadza
analizę zgodności typów.
public
static void main(String[] args) – metoda główna programu.
}