Програмски преводиоци 1/Јануар 2020

Извор: SI Wiki
Пређи на навигацију Пређи на претрагу

Јануарски рок 2021. године одржан је 22. јануара. Поставка рока је доступна са странице предмета.

1. задатак

Поставка

Дата граматика описује регуларне изразе са операторима конкатенације ● и уније |.

  1. <Е> → <Е> | <Т>
  2. <Е> → <Т>
  3. <Т> → <Т> ● <П>
  4. <Т> → <П>
  5. <П> → слово
  6. <П> → епсилон
  1. Конструисати СЛР(1) парсер за дату граматику.
  2. Допунити дату граматику С-атрибутивним правилима тако да сваки нетерминал дате граматике поседује атрибуте који представљају вредности функција поништив и прва_позиција из алгоритма за конструкцију детерминистичког коначног аутомата из регуларног израза. Терминал slovo поседује атрибут који означава позицију тог терминала у изразу. Напомена: epsilon је терминал, а не ознака празне секвенце. Такође су | и ● терминални симболи.

Решење

Аутомат из првог задатка под а.

Релевантни ФИРСТ и ФОЛЛОW скупови СЛР(1) парсера су:

  • ФОЛЛОW(<Е>) = {|, ─┤}
  • ФОЛЛОW(<Т>) = {●} ∪ ФОЛЛОW(<Е>) = {●, |, ─┤}
  • ФОЛЛОW(<П>) = ФОЛЛОW(<Т>) = {●, |, ─┤}
Потисна табела СЛР(1) парсера
Стање <Е> <Т> <П> слово епсилон | ─┤
<Е>x <Т>x1 <П>4 слово5 епсилон6
<Т>x1 3
<Е>x |1 ─┤0
<П>4
слово5
епсилон6
3 <П>3 слово5 епсилон6
|1 <Т>x2 <П>4 слово5 епсилон6
─┤0
<П>3
<Т>x2 3
Контролна табела СЛР(1) парсера
Стање слово епсилон | ─┤
СХИФТ СХИФТ
<Т>x1 СХИФТ РЕДУЦЕ(2) РЕДУЦЕ(2)
<Е>x СХИФТ СХИФТ
<П>4 РЕДУЦЕ(4) РЕДУЦЕ(4) РЕДУЦЕ(4)
слово5 РЕДУЦЕ(5) РЕДУЦЕ(5) РЕДУЦЕ(5)
епсилон6 РЕДУЦЕ(6) РЕДУЦЕ(6) РЕДУЦЕ(6)
3 СХИФТ СХИФТ
|1 СХИФТ СХИФТ
─┤0 АЦЦЕПТ
<П>3 РЕДУЦЕ(3) РЕДУЦЕ(3) РЕДУЦЕ(3)
<Т>x2 СХИФТ РЕДУЦЕ(1) РЕДУЦЕ(1)

Допуњена граматика са атрибутима изгледа:

  1. <Е>пон,прва → <Е>пон1,прва1 | <Т>пон2,прва2
  2. <Е>пон,прва → <Т>пон1,прва1
  3. <Т>пон,прва → <Т>пон1,прва1 ● <П>пон2,прва2
  4. <Т>пон,прва → <П>пон1,прва1
  5. <П>пон,прва → словопоз
  6. <П>пон,прва → епсилон

2. задатак

Поставка

У стандардној Микројави, локално дефинисана променљива сакрива глобалну променљиву са истим именом. Желимо да уведемо следећу модификацију: Ако се жели приступити сакривеној глобалној променљивој, мора се испред имена навести оператор ::.

На пример:

program P {
    int X[];
    void main() int X; { ::X = new int[5]; X = ::X[0];}
}
  1. Променити Микројава граматику да се додају описане могућности (релевантан део граматике дат је у прилогу у ЕБНФ нотацији). Јасно означити шта је промењено.
  2. Додати у микројава табелу симбола метод финд_хидден(Стринг наме) који се позива за тражење имена под дејством оператора ::. Навести комплетну имплементацију овог метода. У прилогу је (на следећој страни) као подсетник дат део постојеће имплементације класе Tab.
  3. Где у Микројава компајлеру треба уградити позив метода find_hidden?

Микројава граматика:

DesignatorStatement := Designator "=" Expr.
DesignatorStatement := Designator "++".
DesignatorStatement := Designator "--".
Statement := DesignatorStatement ";".
Statement := "read" "(" Designator ")" ";".
Statement := "print" "(" Expr [“,” number] ")" ";".
Expr := ["‐"] Term {Addop Term}.
Term := Factor {Mulop Factor}.
Factor := numConst | charConst | "(" Expr ")" | boolConst | "new" Type [ "[" Expr "]" ].
Factor := Designator [ "(" ")" ].
Designator := ident ["[" Expr "]" ].
Addop := "+" | "‐" .
Mulop := "*" | "/" | "%".

Имплементација класе Tab:

public class Tab {
    public static final Struct
        noType = new Struct(Struct.None),
        intType = new Struct(Struct.Int),
        charType = new Struct(Struct.Char),
        nullType = new Struct(Struct.Class);
    public static final Obj noObj = new Obj(Obj.Var, "noObj", noType);
    public static Obj chrObj, ordObj, lenObj;
    public static Scope currentScope; //tekuci opseg
    private static int currentLevel; //nivo ugnezdavanja tekuceg opsega
    public static void init() {
        Scope universe = currentScope = new Scope(null);
        . . . . .
        currentLevel = -1;
    }
    public static void openScope() {
        currentScope = new Scope(currentScope);
        currentLevel++;
    }
    public static void closeScope() {
        currentScope = currentScope.getOuter();
        currentLevel--;
    }
    public static Obj insert(int kind, String name, Struct type) {
        Obj newObj=new Obj(kind,name,type,0,((currentLevel!=0)?1:0));
        . . . . .
    }
    public static Obj find(String name) {
        Obj resultObj = null;
        for (Scope s = currentScope; s != null; s = s.getOuter()) {
            if (s.getLocals() != null) {
                resultObj = s.getLocals().searchKey(name);
                if (resultObj != null) break;
            }
        }
        return (resultObj != null) ? resultObj : noObj;
    }
    public static Scope currentScope() {
        return currentScope;
    }
}

Решење

У граматици је потребно изменити следећу смену:

Designator := ["::"] ident ["[" Expr "]" ].

Метода find_hidden која проналази променљиве из глобалног досега програма може да изгледа овако:

    public static Obj find_hidden(String name) {
        Scope scope = currentScope;
        for (int level = currentLevel; level > 0; --level) {
            scope = scope.getOuter();
        }
        Obj resultObj = null;
        if (scope.getLocals() != null) {
            resultObj = scope.getLocals().searchKey(name);
        }
        return (resultObj != null) ? resultObj : noObj;
    }

У Микројава компајлеру позив ове методе треба уградити током семантичке анализе, како би се одговарајућем чвору у апстрактном синтаксном стаблу доделио симбол који одговара променљивој из глобалног опсега.

3. задатак

Поставка

Решење

Недетерминистички коначни аутомат са дешифрованим стањима.
Стање а б ц Прихвата
→ А А C 0
Б Б, C D 0
C А D 1
D Б, C 0
Детерминистички коначни аутомат са дешифрованим стањима
Стање а б ц Прихвата
→ А, Б А, Б, C D C 0
А, Б, C А, Б, C D C, D 1
D Б, C 0
C А D 1
C, D А Б, C D 1
Б, C А, Б, C D D 1
А А C 0

4. задатак

Поставка

Дата је граматика:

  1. <С> → а <С> б
  2. <С> → б <С> ц
  3. <С> → бц
  4. <С> → <С> а
  1. Трансформисати граматику у ЛЛ(1) и одредити СЕЛЕЦТ скупове тако добијене граматике.
  2. Конструисати парсер на бази рекурзивног спуста за граматику добијену у тачки а).

Решење

Трансформисана граматика може изгледати овако:

  1. <С> → а <С> б <С''>
    СЕЛЕЦТ(1) = {а}
  2. <С> → б <С'> ц <С''>
    СЕЛЕЦТ(2) = {б}
  3. <С'> → <С>
    СЕЛЕЦТ(3) = {а, б}
  4. <С'> → ε
    СЕЛЕЦТ(3) = {ц}
  5. <С''> → а <С''>
    СЕЛЕЦТ(4) = {а}
  6. <С''> → ε
    СЕЛЕЦТ(5) = {ц, ─┤}

Парсер на бази рекурзивног спуста на горњу граматику изгледа овако:

char input;

void terminal(char c) {
    if (input == c) {
        input = advance();
    } else {
        reject();
    }
}

void S2() {
    switch (input) {
        case 'a':
            terminal('a');
            S2();
            break;
        case 'c':
        case EOF:
            break;
        default:
            reject();
    }
}

void S();

void S1() {
    switch (input) {
        case 'a':
        case 'b':
            S();
            break;
        case 'c':
            break;
        default:
            reject();
    }
}

void S() {
    switch (input) {
        case 'a':
            terminal('a');
            S();
            terminal('b');
            S2();
            break;
        case 'b':
            terminal('b');
            S1();
            terminal('c');
            S2();
            break;
        default:
            reject();
    }
}

void main() {
    input = advance();
    S();
    terminal(EOF);
}

5. задатак

Поставка

Дат је следећи део програм на језику сличном Пасцал-у:

procedure proc0;
    var x, y: integer;
    procedure proc1(z: integer);
        procedure proc2;
        begin
            y := x + z;
        end;
    begin
        proc2;
    end;
    procedure proc3(z: integer);
        var x: integer;
        procedure proc4;
        begin
            proc1(x);
        end;
    begin
        x := z + 1;
        proc4;
    end;
begin
    x := 5;
    proc3(x);
end;
  1. Ако је статичко окружење за нелокалне променљиве реализовано преко приступних веза, нацртати изглед стека након извршавања наредбе y := x + z; из процедуре proc2.
  2. Написати 80x86 асемблерски код за наредбу y := x + z; из процедуре proc2.

Решење

Стек из петог задатка под а.
MOV DI, [BP+04]
MOV AX, [DI+06]
MOV DI, [DI+04]
MOV BX, [DI-02]
ADD AX, BX
MOV [DI-04], AX

6. задатак

Поставка

За дати програмски фрагмент:

  1. Написати одговарајући међукод и граф тока контроле на нивоу основних блокова.
  2. Написати међукод у ССА форми.
x = 1;
y = 0;
z = 0;
for (x = 2; y > z; z = z + 1) {
    if (x - 2 > y) {
        x = y - 1;
    } else if(x > y) {
        x = z - 1;
    } else break;
    y = x * 2;
}
x = y + z;

Решење

Граф из шестог задатка под а.

Међукод:

  1. x1 := 1
  2. y1 := 0
  3. з1 := 0
  4. x2 := 2
  5. т1 := Ф(y1, y2)
  6. иф т1 <= з гото 22
  7. т2 := Ф(x2, x3, x4)
  8. т3 := т2 - 2
  9. иф т3 <= т1 гото 13
  10. т4 := т1 - 1
  11. x3 := т4
  12. гото 18
  13. иф т2 <= т1 гото 17
  14. т5 := з1 - 1
  15. x4 := т3
  16. гото 18
  17. гото 22
  18. т6 := Ф(x3, x4)
  19. т7 := т6 * 2
  20. y2 := т7
  21. гото 5
  22. т8 := т1 + з1
  23. x5 := т8