Home > javalette

javalette

Javalette is a project mainly written in C and SHELL, based on the View license.

javalette compiler in c

  1. Spis tresci
1- Analiza leksykalna
2- Analiza skladniowa
3- Analiza kontekstowa (semantyczna)
4- Generowanie kodu do asemblera (NASM)
  1. Analiza leksykalna
Komentarze:
    - /* komentarz w bloku */
    - // komentarz w linni
    - # komentarz w linni

Wartosci:
    - [0-9]+.[0-9]*  - wartosc typu double
    - [0-9]+         - wartosc typu int
    - true|false     - wartosc typu boolean
    - "[^"]"         - wartosc typu string

Typy:
    - "double", "int", "boolean", "string", "void"

Slowa kluczowe:
    - "if", "else", "for", "while", "return"

Operatory:
    - '+', '-', '*', '/', ...

Identyfikatory:
    - [a-zA-Z][a-zA-Z0-9_]*
  1. Analiza skladniowa
- Tablica nazw dla identyfikatorow

- Za pomoca regul w bisonie tworzone jest drzewo

- Rozszerzenia w jezyku:
    - Rzutowanie, domyslne i jawne
    - Zagniezdzone funkcje
    - Przypisanie jako wyrazenie
  1. Analiza kontekstowa (semantyczna)
Przed rozpoczeciem analizy, dolaczane sa do programu funkcje 'printString',
'printInt', 'printDouble', 'readInt', 'readDouble' i 'error'. Sa one 
umieszczane w glownym kontekscie (struktura 'j_context_t') wraz z innymi 
funkcjami zdefiniowanymi w programie (nie liczac zagniezdzonych). Funkcje 
zdefiniowane globalnie sa widoczne w kazdym miejscu programu, o ile nie 
zostana przykryte przez inna definicje zagniezdzonej funkcji o tej samej 
nazwie. Na poziomie tego samego kontekstu nazwy funkcji musza byc unikalne.
Podobnie ze zmiennymi, w tym samym konteksie moze wystapic tylko jedna 
deklaracja tej samej zmiennej. Ciekawy wyjatek - 'int x = (x = 11) + 1;' -
zadziala jesli kontekscie nizej jest zadeklarowana zmienna x, jej to zostanie
przypisana wartosc 11 a nastepnie nowemu x-owi 12.

Tablica symboli zostala podzielona na odrebna tablice zmiennych 'j_vartbl_t'
i tablice funkcji 'j_funtbl_t'. Mozliwe jest dzieki temu istnienie w tym 
samym kontekscie zmiennej oraz funkcji o tej samej nazwie. Kazda struktura
'j_context_t' ma wlasna tablice symboli oraz wskaznik na kontekst nizej.
Dodatkowo kontekst wie w jakiej funkcji sie znajduje (potrzebne przy 
intrukcji powrotu). Natomiast w tablicy symboli dla kazdego symbolu trzymany 
jest wskaznik do wystapienia jego deklaracji w drzewie. 

W wywolaniach funkcji, przypisaniach czy operacjach na wyrazeniach moga 
wystapowac roznice typow. Wtedy tworzony zostaje dodatkowy wezel w drzewie
wykonujacy operacje rzutowania na wlasciwy typ.

Analiza przykladow z katalogu 'examples/':

    - examples/good/core*.jl - ok

    - examples/bad/bad{001,002,004,005,010,011,021}.jl - syntax error
    - examples/bad/bad{006,007,012,017,018,019}.jl - context error
    - examples/bad/bad009.jl - ok : dziala domyslne rzutowanie 'boolean' -> 'int'
    - examples/bad/bad013.jl - ok : dziala domyslne rzutowanie 'int' -> 'double'
    - examples/bad/bad015.jl - ok : dziala domyslne rzutowanie 'double' -> 'int'
    - examples/bad/bad016.jl - ok : dziala domyslne rzutowanie 'int' -> 'double'
    - examples/bad/bad020.jl - ok : dziala domyslne rzutowanie 'boolean' -> 'double'
    - examples/bad/bad022.jl - ok : dziala domyslne rzutowanie 'double' -> 'int'
    - examples/bad/bad023.jl - ok : dziala domyslne rzutowanie 'int' -> 'double'
    - examples/bad/bad026.jl - ok : dziala domyslne rzutowanie 'double' -> 'int'
    - examples/bad/bad027.jl - ok : dziala domyslne rzutowanie 'int' -> 'double'

    - examples/extensions/typecast/explicit_cast.jl - context error: brak 'return'

Wlasne przyklady z katalogu 'tests/' (w plikach napisane co sprawdzaja)

    - tests/correct{001-010}.jl - ok
    - tests/explicit_cast.jl - ok: poprawiony 'return' z 'examples/extensions'
    - tests/failure{001-040}.jl - error
  1. Generowanie kodu do asemblera (NASM)
Kod w asemblerze dzieli sie na sekcje '.rodata' ze stalymi wartosciami 
uzytymi w programie a takze innymi uzytymi do predefiniowanych funkcji.
Kazda uzyta wartosc w programie dostaje unikalny numer, ktory potem 
umozliwia dostep. 

Funkcje predefiniowane zostaly napisane w asemblerze, ze wspomozeniem 
funkcjami z C jak printf czy scanf. Stad linkowac nalezy z bibliotekami
do C. np przez gcc. W kodzie jest zdefiniowana funkcja 'main', ktora 
wywoluje funkcje 'main' zdefiniowana w programie. 

4.1. Realizacja generowania kodu funkcji

Program w jezyku javalette, sklada sie z listy funkcji. Wystarczy zatem je
zdefiniowac pokolei w osobnych miejscach kodu. W tym celu kazda funkcja 
dostaje unikalny identyfikator(takze te zagniezdzone). Jesli w pewnym
momencie programu jest jej wywolanie to jest jednoznacznie okreslona 
etykieta funkcji. Nie trzeba sie przejmowac, czy funkcja jest zagniezdzona
czy nie. Zakladajac, ze analiza semantyczna jest poprawna nigdy nie wystapi
nieuprawnione wolanie funkcji.

Funkcja musi odkladac na stos wartosc ebp, aby w razie zagniezdzenia moc
odpowiednio trafic do zmiennych. Argumenty funkcji sa odkladane przy 
jej wywolaniu, latwo wiec okreslic ich pozycje na stosie:
    1 - [ebp+8], 2 - [ebp+12], 3 - [ebp+16], ...
Zmienne lokalne w funkcji beda rowniez odkladane na stosie, na biezaco:
    1 - [ebp-4], 2 - [ebp-8], 3 - [ebp-12], ...
Musi byc spamietywany licznik ile zmiennych zostalo odlozonych na stosie 
aby przy wyjsciu z funkcji odpowiednio przywrocic wielkosc stosu. 
Dodatkowo kazda zmienna musi pamietac, skad pochodzi, aby tam jej szukac
a nie w bierzacym kontekcie - bowiem podczas generowania kodu mogloby sie
zdarzyc, ze odwolywalibysmy sie do zmiennej, ktora dopiero za chwile
bedzie zadeklarowana(ale w biezacym kontekscie, badz co badz...).

4.2. Realizacja instrukcji blokowej

Przy wejsciu do bloku tworzony jest nowy kontekst, moga byc definiowane 
zmiennej o zdefiniowanej juz nazwie. Przy ich deklaracji stos jest
odpowiednio podnoszony oraz przy wyjsciu z bloku wierzcholek stosu przywracany.
Zatem rowniez trzeba zliczac aktualne przesuniecie na stosie.

4.3. Realizacja instrukcji warunkowej

Instrukcja warunkowa jest realizowana za pomoca skokow do odpowiednich 
etykiet. Etykiety musza miec unikalne nazwy, wiec kazda instukcja warunkowa
ma unikalny numer (tak jak w przypadku funkcji). 

    < EXPRESION -> eax >
    JZ NEAR C[0-9]+f[0-9]+else
    < STATEMENT -> jesli prawda >
    JMP NEAR C[0-9]+f[0-9]+fi
    C[0-9]+f[0-9]+else:
    < STATEMENT -> jesli falsz >
    C[0-9]+f[0-9]+fi:

4.4. Realizacja instrukcji iteracji

Instrukcja iteracji rowniez wymaga unikalnych etykiet. 

    < EXPRESSION - wstepne przypisanie >
    I[0-9]+f[0-9]+begin:
    < EXPRESSION - warunek petli >
    JZ NEAR I[0-9]+f[0-9]+end
    < STATEMENT - tresc petli >
    < EXPRESSION - przypisanie co obrot >
    JMP NEAR I[0-9]+f[0-9]+begin
    I[0-9]+f[0-9]+end:

4.5. Realizacja instrukcji powrotu

Wykonujac wyjscie z funkcji, nalezy zwinac stos, w tym celu w bierzacym
kontekscie jest trzymana informacja o wielkosci stosu oraz przywrocic 
poprzednia wartosc rejestru ebp. Kazda funkcja musi miec instrukcje powrotu.

4.6. Realizacja instrukcji deklaracji

Na stosie jest rezerwowane miejsce na nowe zmienne oraz jesli trzeba
sa im przypisywane wartosci. W bierzacym kontekscie jest uwzgledniany
nowy rozmiar stosu. 

Mozliwe jest uzycie nazwy zmiennej w jej deklaracji o ile tylko taka zmienna
byla juz wczesniej zadeklarowana i taka wartosc bedzie reprezentowala.

int i = 1; // i = 1
{
    int i = i + 2; // i = 3
}

4.7. Realizacja wyrazen

Wyrazenia czesto moga przybierac forme drzewa zaleznosci. Konieczne
jest zatem utworzenie zmiennych tymczasowych dla wyliczenia posrednich
wartosci. 

Tworzone jest drzewo zaleznosci wyrazen i na stosie jest rezerwowane
miejsce na zmienne tymczasowe. Wszystkie wyrazenia wystepujace w drzewie
umieszczamy na liscie od najbardziej lewego do najbardziej prawego.
Za kazdym razem obliczamy wyrazenie, z mozliwych do wyliczenia, najbardziej
na lewo. Dzieki temu latwo jest zadbac o leniwe wykonywanie operacji 
'&&' i '||'. Skoro idziemy od lewej, to zostawiamy w kodzie odpowiednie 
porownania oraz skoki. Porzadek na liscie zapewnia, ze beda pominiete tylko
te obliczenia, ktore trzeba. Gdy wszystkie wyrazenia zostana policzone
wynik jest odkladany na rejestr eax. 

Aby odnlezc adres zmiennej, do ktorej sie odnosimy, trzeba odnalezc 
kontekst jej deklaracji oraz skaczac po wartosci ebp dostac odpowiedni
adres - stamtad bierzemy/przypisujemy wartosc.

4.8. Zgodnosc testow z katalogu 'examples'.

Wszystkie wyniki sa zgodne ze wzorcowymi. Jest jednak kilka drobnych 
roznic. Moje printInt, printDouble, printString same z siebie nie doklejaja
na koniec znaku nowej linni. Nieco inaczej tez zachowuja sie funkcje 
dzialajace na doublach. Po pierwsze sa wypisywane w innym formacie (%g),
po drugie rzutowanie z double-a na int-a zaokragla do najblizszej liczby
calkowitej.

Dodatkowo sprawdzajac poprawnosc programu zapisalem kilka wlasnych testow
w 'tests/other'. Sprawdzaja poprawnosc operacji arytmetycznych, porownan,
rzutowan, wywolywan funkcji, deklaracji zmiennych, itd...
Previous:faceDetection