46
BAKALÁŘSKÁ PRÁCE Programovatelný kalkulátor 2019 Michael Zapletal Vedoucí práce: RNDr. Arnošt Ve- čerka Studijní obor: Aplikovaná informatika, prezenční forma

Programovatelný kalkulátor (bakalářská práce)

  • Upload
    others

  • View
    11

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Programovatelný kalkulátor (bakalářská práce)

BAKALÁŘSKÁ PRÁCE

Programovatelný kalkulátor

2019 Michael ZapletalVedoucí práce: RNDr. Arnošt Ve-čerka

Studijní obor: Aplikovaná informatika,prezenční forma

Page 2: Programovatelný kalkulátor (bakalářská práce)

Bibliografické údaje

Autor: Michael Zapletal

Název práce: Programovatelný kalkulátor

Typ práce: bakalářská práce

Pracoviště: Katedra informatiky, Přírodovědecká fakulta, UniverzitaPalackého v Olomouci

Rok obhajoby: 2019

Studijní obor: Aplikovaná informatika, prezenční forma

Vedoucí práce: RNDr. Arnošt Večerka

Počet stran: 46

Přílohy: 1 CD/DVD

Jazyk práce: český

Bibliograhic info

Author: Michael Zapletal

Title: Programmable Calculator

Thesis type: bachelor thesis

Department: Department of Computer Science, Faculty of Science, Pa-lacký University Olomouc

Year of defense: 2019

Study field: Applied Computer Science, full-time form

Supervisor: RNDr. Arnošt Večerka

Page count: 46

Supplements: 1 CD/DVD

Thesis language: Czech

Page 3: Programovatelný kalkulátor (bakalářská práce)

Anotace

Bakalářská práce se zaměřuje na sestrojení aplikace emulující programovatelnýkalkulátor. V práci jsou popsány algoritmy pro převod výrazů používajících in-fixovou notaci a programů ve formě textu do interní podoby a vyhodnocováníinterní podoby výrazů a programů. Dále práce popisuje infrastrukturu aplikace ajejí implementaci v programovacím jazyce Java 8. Na konci práce je uživatelskápříručka a diskuze.

Synopsis

This bachelor’s thesis focuses on creating an application which emulates a pro-grammable calculator. It describes algorithms for transforming expressions usinginfix notation and programs into internal representation and evaluating the in-ternal representation of expressions using infix notation and programs. Next,it describes the infrastructure of the application and its implementation in theJava 8 programming language. A user manual and a discussion are included atthe end of the thesis.

Klíčová slova: programovatelný kalkulátor; závěrečná práce; dokumentace; emu-látor; java; shunting-yard; ide; program; interpret

Keywords: programmable calculator; thesis; documentation; emulator; java;shunting-yard; ide; program; interpreter

Page 4: Programovatelný kalkulátor (bakalářská práce)

Děkuji všem, kteří jakkoliv přispěli k úspěšnému vzniknutí této práce, ať už tobyl vedoucí práce s cennými konzultacemi, či osoby blízké s duševní podporou.

Místopřísežně prohlašuji, že jsem celou práci včetně příloh vypracoval/a samo-statně a za použití pouze zdrojů citovaných v textu práce a uvedených v seznamuliteratury.

datum odevzdání práce podpis autora

Page 5: Programovatelný kalkulátor (bakalářská práce)

Obsah1 Úvod 8

2 Programovatelný kalkulátor 92.1 Historie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92.2 Sencor SEC 103 . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9

3 Formalizace 113.1 Výraz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113.2 Program . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113.3 Operátor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12

4 Vyhodnocování výrazů 134.1 Shunting-yard algortimus . . . . . . . . . . . . . . . . . . . . . . . 134.2 Vyhodnocení výrazu v reverzní polské notaci . . . . . . . . . . . . 134.3 Vlastní úprava Shunting-yard algoritmu . . . . . . . . . . . . . . . 14

5 Implementace 155.1 Číslo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155.2 Operátor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16

5.2.1 Programování operátorů . . . . . . . . . . . . . . . . . . . 175.3 Výraz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20

5.3.1 Překlad . . . . . . . . . . . . . . . . . . . . . . . . . . . . 205.3.2 Vyhodnocování . . . . . . . . . . . . . . . . . . . . . . . . 21

5.4 Úhlová jednotka . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215.4.1 Převod . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22

5.5 Proměnná . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225.6 Vyhodnocovací kontext . . . . . . . . . . . . . . . . . . . . . . . . 225.7 Příkaz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22

5.7.1 Výrazový příkaz . . . . . . . . . . . . . . . . . . . . . . . . 235.7.2 Podmíněný příkaz . . . . . . . . . . . . . . . . . . . . . . . 235.7.3 Příkaz přiřazení . . . . . . . . . . . . . . . . . . . . . . . . 235.7.4 Příkaz uživatelského vstupu . . . . . . . . . . . . . . . . . 235.7.5 Příkaz skoku . . . . . . . . . . . . . . . . . . . . . . . . . 235.7.6 Příkaz návěští . . . . . . . . . . . . . . . . . . . . . . . . . 235.7.7 Příkaz změny jednotky . . . . . . . . . . . . . . . . . . . . 23

5.8 Program . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 245.8.1 Překlad . . . . . . . . . . . . . . . . . . . . . . . . . . . . 245.8.2 Vykonávání . . . . . . . . . . . . . . . . . . . . . . . . . . 24

5.9 Kalkulátor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 255.9.1 Komunikace s uživatelem . . . . . . . . . . . . . . . . . . . 255.9.2 Běh programu . . . . . . . . . . . . . . . . . . . . . . . . . 26

5.10 Emulátor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 275.10.1 Uživatelské rozhraní . . . . . . . . . . . . . . . . . . . . . 27

5

Page 6: Programovatelný kalkulátor (bakalářská práce)

5.10.2 Příkaz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 285.10.3 Konzolové uživatelské rozhraní . . . . . . . . . . . . . . . . 295.10.4 Grafické uživatelské rozhraní . . . . . . . . . . . . . . . . . 30

6 Uživatelská příručka 306.1 Standardní rozhraní kalkulátoru . . . . . . . . . . . . . . . . . . . 336.2 Editor programů . . . . . . . . . . . . . . . . . . . . . . . . . . . 346.3 Ladění . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 356.4 Načítání a ukládání programů . . . . . . . . . . . . . . . . . . . . 376.5 Konzolový režim a spouštěcí parametry . . . . . . . . . . . . . . . 38

6.5.1 Spouštěcí parametry . . . . . . . . . . . . . . . . . . . . . 386.6 Psaní výrazů . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 396.7 Vyhodnocování výrazů . . . . . . . . . . . . . . . . . . . . . . . . 406.8 Psaní programů . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40

7 Diskuze 41

Závěr 43

Conclusions 44

A Obsah přiloženého CD 45

Bibliografie 46

6

Page 7: Programovatelný kalkulátor (bakalářská práce)

Seznam obrázků1 Sencor SEC 103 . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102 Uživatelské rozhraní ihned po spuštění aplikace . . . . . . . . . . 313 Výčet „goniometrických“ operátorů . . . . . . . . . . . . . . . . . 324 Výčet operátorů, které jsou inverzní k jiným operátorům . . . . . 335 Příklad vyhodnocení výrazu ze standardního rozhraní . . . . . . . 346 Příklad programu v editoru programů . . . . . . . . . . . . . . . . 357 Příklad ladění běžícího programu; parametr krokování je zapnutý 368 Příklad úpravy proměnné za běhu programu . . . . . . . . . . . . 379 Výběr programu ze souborového systému . . . . . . . . . . . . . . 38

Seznam zdrojových kódů1 Pseudokód Shunting-yard algoritmu . . . . . . . . . . . . . . . . . 132 Pseudokód vyhodnocení výrazu v reverzní polské notaci . . . . . . 143 Pseudokód upraveného Shunting-yard algoritmu . . . . . . . . . . 154 Definice operátoru odečítání a operátoru záměny znaménka . . . . 175 Definice operátorů sinus a arkus sinus . . . . . . . . . . . . . . . . 186 Definice operátoru faktoriálu . . . . . . . . . . . . . . . . . . . . . 197 Příklad použití iterátoru ve vylepšené for smyčce . . . . . . . . . 248 Použití vylepšené for smyčky pro vykonávání programu . . . . . . 259 Příklad programu na výpočet prvočísel . . . . . . . . . . . . . . . 4110 Příklad programu na výpočet n-tého Fibonacciho čísla . . . . . . 41

7

Page 8: Programovatelný kalkulátor (bakalářská práce)

1 ÚvodProgramovatelný kalkulátor umožňuje uživateli automatizovat svou činnost

při výpočtech. Uživatel může přímo do kalkulátoru zadávat, ukládat a načítatprogramy, které pracují s potenciálně libovolným vstupem a vypočítají výsledekbez zbytečné interakce s uživatelem. Ačkoliv programovatelné kalkulátory ne-musí být turingovsky úplné, zejména kvůli omezené velikosti paměti, lze v nichvyužívat užitečné programovací konstrukce, například podmíněné vyhodnoco-vání nebo cykly, či je simulovat nízkoúrovňovými instrukcemi nepodmíněných ipodmíněných skoků.

Zadáním práce bylo sestrojit emulátor programovatelného kalkulátoru podlezvolené předlohy, kterou se stal Sencor SEC 103. Emulátor je doplněný o užitečnéladící funkce a editor programů se zvýrazňováním syntaxe. Kvůli neúplné doku-mentaci ze strany výrobce bylo chování kalkulátoru zreplikováno jistou formoureverzního inženýrství.

Výstupem je uživatelská aplikace, která podporuje většinu funkcionality kal-kulátoru Sencor SEC 103. Emulátor byl vyvinut pro desktopové počítače a pod-poruje grafický i textový (konzolový) režim. Pro spustitelnost na většině platfo-rem vyžaduje běhové prostředí Java 8 a vyšší.

Emulátor plně podporuje vyhodnocování libovolně složitých výrazů v infixovénotaci, práci s libovolně složitými programy a programovou interakci s uživate-lem. Oproti předloze v implementaci chybí numerické derivace, určité integrálya statistické výpočty. Nicméně, API bylo navrženo tak, aby bylo snadno rozšiři-telné, což usnadňuje budoucí doplnění funkcionality.

8

Page 9: Programovatelný kalkulátor (bakalářská práce)

2 Programovatelný kalkulátorProgramovatelný kalkulátor je kalkulátor, který podporuje zpracování po-

sloupnosti příkazů dle nějakého uloženého programu. Programovatelný kalkulá-tor umožňuje uživateli psát a ukládat programy do kalkulátoru, aby bylo možnéřešit složité problémy, či automatizovat náročnou činnost.

Často se možnost programovat objevuje u kalkulátorů s grafickým režimem,jelikož jejich velká obrazovka umožňuje sledovat větší množství zdrojového kóduv jeden čas.[2]

2.1 HistoriePrvní programovatelné kalkulátory, jako například IBM CPC, používaly děrné

štítky nebo jiná média pro ukládání programů. Pozdější kalkulátory, které jižbyly o velikosti běžného vědeckého kalkulátoru, ukládaly programy na magne-tické pásky, cartridge nebo do vnitřní paměti, která byla perzistentní díky baterii.

Původně bylo možné psát programy pouze v programovacím jazyce kalku-látoru, ale když hackeři objevili způsob, jak obejít rozhraní kalkulátoru a psátprogramy v assembleru, společnosti vyrábějící programovatelné kalkulátory (ob-zvlášť Texas Instruments) vytvořili podporu pro programování v nativním jazycekalkulátoru přímo z uživatelského rozhraní.

2.2 Sencor SEC 103Vzorem pro emulátor byl kalkulátor Sencor SEC 103. Tento kalkulátor má

následující funkce:

• vyhodnocování výrazů v infixové notaci

• práce se zlomky

• goniometrické funkce

• výpočty numerických derivací

• výpočty určitých integrálů

• statistické funkce (lineární regrese, kvadratická regrese, . . . )

• podpora pro kombinatoriku

• práce v různých úhlových jednotkách s možností převodu

• komplexní číselná aritmetika

• práce v různých číselných soustavách

• programovatelnost

9

Page 10: Programovatelný kalkulátor (bakalářská práce)

Obrázek 1: Sencor SEC 103

Kalkulátor měl přiloženou uživatelskou příručku, která, co se týče implemen-tace, zmiňovala pouze využití dvou zásobníků k vyhodnocení výrazu a bytecoduk ukládání rovnic a programů. Implementace emulátoru spočívala v napodobenífunkcí kalkulátoru skrze reverzní inženýrství.

Ne vše bylo možné napodobit, jelikož hardware kalkulátoru nebyl podrobenanalýze. Tím pádem emulátor, který je výsledkem této práce, nedokáže zpra-covat originální formát kalkulátoru, nýbrž zavádí vlastní reprezentaci výrazů aprogramů, které jsou uloženy v plaintextu. To umožňuje snadné psaní a čteníprogramů v běžném uživatelském prostředí s běžně dostupnými editory a záro-veň je konformní s bežným uložením zdrojových programů u interpretovanýchprogramovacích jazyků.[3]

10

Page 11: Programovatelný kalkulátor (bakalářská práce)

3 FormalizaceNásledující podkapitoly formalizují nosné pojmy této práce.

3.1 VýrazInfixový výraz je řetězec generováný následující gramatikou:

S -> VAL

VAL -> NUM | NUL | VAR | (VAL)VAL -> PREFIX VAL | VAL POSTFIX | VAL INFIX VAL

NUM -> D | D.D | .D

D -> 0 | 0D | 1 | 1D | ... | 8 | 8D | 9 | 9D

PREFIX -> sqrt | cbrt | (-) | (+) | sin | ...

POSTFIX -> ! | deg | rad | grad | ...

INFIX -> + | - | * | / | mod | rt | ...

NUL -> Pi | Ran# | e | ...

VAR -> Ans | A | B | C | ...

(kde S je startovní symbol)VAL zde reprezentuje infixový výraz samotný. NUM je číslo. D je posloupnost

číslic. PREFIX je operátor s prefixovou notací. POSTFIX je operátor s postfixo-vou notací. INFIX je operátor s infixovou notací. NUL je tzv. nulový operátor(viz 3.3). VAR je název proměnné.

3.2 ProgramProgram je posloupnost příkazů. Příkazy a program jsou definovány následu-

jícími regulárními výrazy:

Statement = ExpressionStatement| AssignmentStatement| InputStatement| ConditionalStatement| LabelStatement| GotoStatement

11

Page 12: Programovatelný kalkulátor (bakalářská práce)

ExpressionStatement = Expression

AssignmentStatement = Expression -> Variable

InputStatement = ? -> Variable

ConditionalStatement = ExpressionStatement => Statement

LabelStatement = Lbl[0-9]+

GotoStatement = Goto[0-9]+

Program = (Statement[;:])* Statement

(kde Expression je infixový výraz a Variable je název proměnné obdobnějako VAR v sekci 3.1)

3.3 OperátorOperátor je matematická funkce. Vstupem operátoru je n-tice čísel s plovoucí

desetinnou čárkou, kde n je 0, 1 nebo 2. Výstupem operátoru je jediné číslos plovoucí desetinnou čárkou. Operátor má několik vlastností:

• Arita – počet argumentů operátoru (0, 1 nebo 2), operátor s nulovýmpočtem argumentů se nazýva nulový

• Notace – udává pozici operátoru ve výrazu

– Prefixová – operátor se píše před svůj argument– Infixová – operátor se píše mezi své argumenty– Postfixová – operátor se píše za svůj argument

• Priorita – číslo, které určuje pořadí, v jakém se operátory vyhodnocujíuvnitř výrazu, pokud není jinak udáno jednoznačné pořadí (např. závor-kami nebo notací); operátory s vyšší prioritou budou vyhodnoceny předoperátory s nižší prioritou

• Asociativita – u operátorů se stejnou prioritou se používá k dalšímu určenípořadí; asociativita „zleva“ znamená, že nejdříve se vyhodnotí operátorblíže k začátku výrazu, asociativita „zprava“ znamená, že se nejdříve vy-hodnotí operátor blíže ke konci výrazu; v této práci mají operátory dlepoužité notace následující asociativitu:

– Zleva – operátory s prefixovou a infixovou notací– Zprava – operátory s postfixovou notací

12

Page 13: Programovatelný kalkulátor (bakalářská práce)

4 Vyhodnocování výrazůNejnosnější částí implementace programovatelného kalkulátoru byl bezesporu

algoritmus pro vyhodnocování výrazů.Vyhodnotit matematický výraz v infixové notaci bez vytváření stromových

struktur, které by mohly být paměťově náročné pro typický programovatelnýkalkulátor, lze pomocí dvou zásobníků.

4.1 Shunting-yard algortimusShunting-yard algoritmus, který byl vyvinut Edsgerem Dijsktrou, očekává na

vstupu výraz v infixové notaci, výstupem je pak stejný výraz v reverzní polskénotaci. Tento algoritmus pracuje se zásobníkem, do kterého se ukládají matema-tické operátory a závorky.[1]

1 shuntingYard(expression, output):2 stack = a new empty stack3

4 for token in expression:5 if token is an operand:6 output.put(token)7 else if (token is ’(’) or (token uses prefix notation):8 stack.push(token)9 else if token is ’)’:10 while (stack.size > 0) and (stack.top != ’(’):11 output.put(stack.pop())12 stack.pop() // discard left parenthesis13 else if token is an operator:14 while (stack.size > 0)15 and ((stack.top.priority > token.priority)16 or (stack.top uses postfix notation)17 or (stack.top is nullary)):18 operator = stack.pop()19 if operator != ’(’:20 output.put(operator)21 stack.push(token)22

23 while stack.size > 0:24 operator = stack.pop()25 if operator != ’(’:26 output.put(operator)

Zdrojový kód 1: Pseudokód Shunting-yard algoritmu

4.2 Vyhodnocení výrazu v reverzní polské notaciPro obdržení výsledku vyhodnocení výrazu je nutno výstup Shunting-yard

algoritmu odděleně vyhodnotit.

13

Page 14: Programovatelný kalkulátor (bakalářská práce)

1 evaluateRPN(expression):2 stack = a new empty stack3

4 for token in expression:5 if token is an operand:6 stack.push(token)7 else if token is an operator:8 operands = stack.pop(token.arity)9 stack.push(token.apply(operands))10

11 return stack.pop()

Zdrojový kód 2: Pseudokód vyhodnocení výrazu v reverzní polské notaci

4.3 Vlastní úprava Shunting-yard algoritmuPřidáním druhého zásobníku pro uložení číselných hodnot lze podvýrazy vy-

hodnocovat za běhu, čímž lze efektivně vyhodnotit celý výraz v jednom prů-chodu.

Úprava algoritmu je výsledkem reverzního inženýrství na základě krátkéhopříkladu vyhodnocování v uživatelské příručce ke kalkulátoru Sencor SEC 103 aexperimentací s různými výrazy a jejich výsledky. Při implementaci nebyl vyu-žit Shunting-yard algortimus, jeho upravená podoba byla odvozena nezávisle naexistujících zdrojích.

Oproti Shunting-yard algoritmu má jeho upravená podoba výhodu v tom, žese nevytváří žádný dočasný výraz a výsledek lze tedy obdržet dříve. Nevýhodou jevyužití dvou zásobníků místo recyklace jednoho a mírně složitější zápis algoritmu.Tato implementace byla zvolena pro zachování co největší autenticity.

14

Page 15: Programovatelný kalkulátor (bakalářská práce)

1 evaluate(operator, numStack):2 if operator is ’(’:3 return // skip parenthesis4 operands = numStack.pop(operator.arity)5 result = operator.apply(operands)6 numStack.push(result)7

8 shuntingYardEvaluate(expression):9 opStack = a new empty stack10 numStack = a new empty stack11

12 for token in expression:13 if token is an operand:14 numStack.push(token)15 else if (token is ’(’) or (token uses prefix notation):16 opStack.push(token)17 else if token is ’)’:18 while (opStack.size > 0) and (opStack.top != ’(’):19 evaluate(opStack.pop(), numStack)20 opStack.pop() // discard left parenthesis21 else if token is an operator:22 while (opStack.size > 0)23 and ((opStack.top.priority > token.priority)24 or (opStack.top uses postfix notation)25 or (opStack.top is nullary)):26 evaluate(opStack.pop(), numStack)27 opStack.push(token)28

29 while opStack.size > 0:30 evaluate(opStack.pop(), numStack)31

32 return numStack.pop()

Zdrojový kód 3: Pseudokód upraveného Shunting-yard algoritmu

5 ImplementaceNásledující podkapitoly popisují implementaci funkcionalit kalkulátoru. Pro

implementaci byl použit programovací jazyk Java 8. Všechny třídy sídlí v balíčkucz.upol.zaplmi06.calculator.core.

5.1 ČísloVeškerá čísla v kalkulátoru jsou realizována jako datový typ double (64bitové

číslo s plovoucí desetinnou čárkou dle standardu IEEE 754). Použití primitivníhotypu double bylo zvoleno hlavně pro zjednodušení programování operátorů v ja-zyce Java.

15

Page 16: Programovatelný kalkulátor (bakalářská práce)

5.2 OperátorOperátor je reprezentován objektem, jehož nejvýznamnější metodou je vyhod-

nocení nějakých číselných argumentů (metoda apply()) – jedná se o zobrazenídouble... na double.

Třída Operator dále definuje následující vlastnosti:

• Kategorie – zařazení operátoru do kategorie (např. aritmetický, statistický,. . . )

• Omezení – seznam funkcí, které musí vrátit true pro vstupní parametrydo metody apply(), aby bylo možné operátor použít (např. ošetření protidělení nulou, povolení pouze přirozených čísel a nuly pro faktoriál, . . . )

• Vstupní a/nebo výstupní jednotka – pokud operátor pracuje s jednotkami(stupně, radiány, . . . ), je nutno je uvést pro koretní převody

• Inverzní operátor – reference na inverzní operátor (např. sin má inverzníoperátor asin)

• Notace – zda se operátor píše před parametr (prefixová notace), za pa-rametr (postfixová notace), mezi parametry (infixová notace), či se píšesamostatně (v tomto případě má nulovou aritu)

• Priorita – číslo definující prioritu operátoru (čím vyšší číslo, tím vyšší pri-orita)

• Náhrady – operátory, které mohou zastoupit tento operátor dle asociati-vity (např. binární mínus nápsáno před parametr, nikoliv mezi dva, budenahrazeno unárním mínusem)

• Symbol – notace operátoru ve výrazu

• Jméno – uživatelsky čitelný název operátoru

• Skrytý – pravdivostní hodnota, která může skrýt operátor, pokud má in-terní využití a neměl by být dostupný přímo pro uživatele aplikace (např. im-plicitní násobení, které se při překladu výrazů vkládá mezi po sobě jdoucíkonstanty)

• Arita – počet argumentů (0, 1 nebo 2)

• Funkční definice – volána v metodě apply() pro získání výsledku

16

Page 17: Programovatelný kalkulátor (bakalářská práce)

5.2.1 Programování operátorů

Pro programátorsky příjemné programování operátorů byl zvolen návrhovývzor „Builder“. Ve spojení s Javovským konstruktem enum lze říci, že se jedná opříjemný způsob zápisu.

Konstruktor třídy Operator bere jako parametr objekt vnořené třídyVerifiedBuilder. Třída Builder, která je rovněž definována uvnitř třídyOperator, má bezparametrový konstruktor. Na vytvořenou instanci třídyBuilder lze pak volat metody, které nastavují proměnné instance třídy Builder avrací aktuální objekt. Lze tedy metody volat opakovaně po sobě – tomuto se říka„method chaining“ a je to hlavní rys tzv. fluent API. Každá ze zmiňovaných me-tod má jméno podle parametru operátoru. Jakmile se nastaví všechny potřebnéparametry operátoru, zavolá se na instanci třídy Builder metoda build(), kterávrací VerifiedBuilder. Tento krok je nutné provést, protože Builder nemusí býtnadefinován správně – je třeba tedy provést kontrolu korektní definice operátoru.Pokud má Builder špatnou konfiguraci, bude vyvolána výjimka.

1 public enum Operator {2

3 // ...4

5 MINUS(new Builder()6 .category(ARITHMETIC)7 .infix((a, b) -> a - b)8 .name("Minus")9 .priority(PLUS.getPriority())10 .representation(’-’)11 .build(),12

13 UNARY_MINUS(new Builder()14 .category(ARITHMETIC)15 .name("Unary minus")16 .prefix(a -> -a)17 .replacementOf(MINUS)18 .representation("(-)")19 .build())20

21 // ...22

23 }

Zdrojový kód 4: Definice operátoru odečítání a operátoru záměny znaménka

17

Page 18: Programovatelný kalkulátor (bakalářská práce)

1 public enum Operator {2

3 // ...4

5 SIN(new Builder()6 .category(TRIGONOMETRIC)7 .input(RAD)8 .name("Sine")9 .prefix(Math::sin)10 .representation("sin")11 .build()),12

13 ASIN(new Builder()14 .category(TRIGONOMETRIC)15 .constraint(a -> Math.abs(a) <= 1, "asin accepts [-1:1]")16 .inverseOf(SIN)17 .name("Arc sine")18 .output(RAD)19 .prefix(Math::asin)20 .representation("asin")21 .build())22

23 // ...24

25 }

Zdrojový kód 5: Definice operátorů sinus a arkus sinus

18

Page 19: Programovatelný kalkulátor (bakalářská práce)

1 public enum Operator {2

3 // ...4

5 FACTORIAL(new Builder()6 .category(STATISTICAL)7 .constraint(a -> Math.floor(a) == a,8 "Argument must be a whole number")9 .constraint(a -> a >= 0,10 "Argument must be a non-negative number")11 .name("Factorial")12 .postfix(n -> {13 double ret = 1;14 /*15 * checking if itermediate result is finite16 * for quicker termination17 */18 while (n > 1 && Double.isFinite(ret)) {19 ret *= n--;20 }21 return ret;22 })23 .representation(’!’)24 .build())25

26 // ...27

28 }

Zdrojový kód 6: Definice operátoru faktoriálu

19

Page 20: Programovatelný kalkulátor (bakalářská práce)

5.3 VýrazVýraz implementuje třída Expression. Výraz se přeloží do interní reprezen-

tace zavoláním jediného konstruktoru třídy Expression, který bere jako para-metr řetězec znaků String reprezentující zápis celého výrazu.

5.3.1 Překlad

V konstruktoru třídy Expression dochází k překladu vstupního řetězce dointerní reprezentace, což je posloupnost „tokenů“.

V první fázi se celý řetězec rozdělí na tokeny. Jedná se o iterativní proces, přikterém se v řetězci postupně hledají tokeny, dokud se celý nevyčerpá. Postup jenastíněn níže:

1. Přeskočí se libovolné množství tzv. „bílých mezer“.

2. Ze začátku řetězce se pokusí vyhledat token, přičemž první nález ukončujehledání:

(a) Číslo(b) Operátor(c) Proměnná

3. Pokud bylo hledání úspěšné, nalezený token se přidá na konec seznamutokenů a postup se opakuje s podřetězcem, který pokračuje za nalezenýmtokenem, případně se končí při prázdném podřetězci

4. Pokud bylo hledání neúspěšné, vyvolá se výjimka nerozpoznaného tokenu

V druhé fázi překladu se kontroluje konzistence seznamu tokenů, tj. zda jsoutokeny v syntakticky správném pořadí dle formalizace výrazu v kapitole 3.1.Zároveň dochází k úpravám seznamu tokenů tak, aby byly umožněny některékonvence pro pohodlnější zadávání výrazů.

Správnost pořadí tokenů se ověřuje průchodem seznamu po dvojicích.Správné pořadí tokenů splňuje následující podmínky:

• Neexistují prázdné závorky

• První token je buď číslo, proměnná, nulární operátor, operátor s prefixovounotací nebo levá závorka

• Za operátorem s prefixovou nebo infixovou notací a levou závorkou je buďčíslo, proměnná, nulární operátor, operátor s prefixovou notací nebo levázávorka

• Za nulárním operátorem, operátorem s postfixovou notací a pravou závor-kou je buď operátor s infixovou notací, operátor s postfixovou notací nebopravá závorka

20

Page 21: Programovatelný kalkulátor (bakalářská práce)

• Poslední token je buď číslo, proměnná, operátor s postfixovou notací nebopravá závorka

Rozdíl počtu levých a pravých závorek nemusí být nutně nulový, ani zá-vorkování podvýrazů nemusí být konzistentní. Pro autentičnost implementacese, podle vzorového Sencor SEC 103, závorky „automaticky doplňují“ ze za-čátku a z konce výrazu.1 Například výraz 7 - 5)/ 3 * (1 - 7 * (1 + 2 se vy-hodnotí jako (7 - 5)/ 3 * (1 - 7 * (1 + 2)). Toto „automatické uzávorková-vání“ není však implementováno ve fázi překladu doplňováním tokenů závorek,ale je realizováno při vyhodnocování výrazu.

Implementovány jsou také náhrady za operátory. Například operátor odečí-tání (psaný „-“), který má aritu dva (je to „binární“ operátor), bude nahrazen zasvůj ekvivalent arity jedna („unární“), operátor změny znaménka (psaný „(-)“),pokud se vyskytne v místě, kde se očekává operátor s prefixovou notací.

Existuje i podpora pro implicitní násobení, které umožňuje číslům, proměn-ným, nulárním operátorům, operátorům s postfixovou notací a pravým závor-kám, aby byly následovány číslem, proměnnou, nulárním operátorem, operátorems prefixovou notací a levou závorkou. V tomto případě se mezi dva takové tokenyvloží token implicitního násobení. Například seznam tokenů [3, Pi, sqrt, 5]

bude převeden na [3, (*), Pi, (*), sqrt, 5], kde (*) je token implicitníhonásobení.

Jakékoliv výjimky vyvolané během vytváření instance třídy Expression dědíz IllegalSyntaxException a jsou předány volajícímu. Jedná se o syntaktickéchyby ve výrazu (např. prázdný výraz, chybějící parametr operátoru, neexistujícíoperátor, . . . ).

5.3.2 Vyhodnocování

Hlavní algoritmus použitý pro vyhodnocování výrazů popisuje kapitola 4.3.Vyhodnocování operátoru je oproti předchozímu popisu rozšířeno o práci s úh-

lovymi jednotkami (viz. 5.4.1) a omezením se na „konečné“ hodnoty datovéhotypu double – tj. hodnoty kromě Double.NaN

(hexadecimálně 0x7ff8000000000000L), Double.POSITIVE_INFINITY

(hexadecimálně 0x7ff0000000000000L) a Double.NEGATIVE_INFINITY

(hexadecimálně 0xfff0000000000000L).Při vyhodnocování výrazů skrze instanční metodu Expression.evaluate se

předává kontext, což je instance třídy EvaluationContext obsahující vazby pro-měnných a úhlovou jednotku.

5.4 Úhlová jednotkaÚhlové jednotky jsou řešeny výčtovým datovým typem („enum“) Unit. Jed-

notky se mohou mezi sebou libovolně převádět buď skrze parametry v kon-1Sencor SEC 103 podporuje pouze automatické doplňování všech pravých závorek. Auto-

matické doplňování levých závorek bylo implementováno nad rámec.

21

Page 22: Programovatelný kalkulátor (bakalářská práce)

struktoru, které udávájí vztah definované jednotky ke zvolené „unifikované“ jed-notce (v této konkrétní implementaci to jsou stupně) ve smyslu lineární rov-nice (y = velikost × x + posunutí), nebo definicí konkrétních metod pro převodz a do „unifikované“ jednotky. Druhý z přístupů je vhodný pro jednotky, které,kvůli reprezentaci parametrů v datovém typu double, ztrácí přesnost při převodudle výchozí implementace, a nezbytný pro jednotky, které nemají lineární vztahk ostatním jednotkám.2

5.4.1 Převod

Převod z jedné jednotky do druhé je realizován ve dvou krocích. Nejprve sez výchozí jednotky provede převod do „unifikované“ jednotky zavoláním metodytoUnified (výchozí implementace násobí množství velikostí a přičítá posunutí).Poté se z unifikované jednotky provede převod do cílové jednotky zavolánímmetody fromUnified (výchozí implementace odečte od množství posunutí a vý-sledek vydělí velikostí).

5.5 ProměnnáVýčtový datový typ Variable implementuje paměťové buňky kalkulátoru

(„proměnné“). Definuje A, B, C, D, M, X, Y a ANS3. Konstanta ANS se používáv emulátoru pro uložení výsledku posledního úspěšného vyhodnocení. Při spuš-tění emulátoru jsou všechny proměnné inicializované na 0.

5.6 Vyhodnocovací kontextTřída EvaluationContext obsahuje úhlovou jednotku a vazby proměnných.

Slouží k vyhodnocení výrazů a příkazů v kontextu.

5.7 PříkazAbstraktní třída Statement je nejbližším společným rodičem všech příkazů.

Definuje statickou metodu parse, která přijímá String a vrací nějaký příkaz.Dále definuje abstraktní instanční metodu evaluate, která přijímá vyhodnoco-vací kontext a vrací double. Každý příkaz má atribut výstupu, což je booleovskáhodnota určijící, zda má příkaz způsobit pozastavení chodu programu a zobrazitvýsledek svého vyhodnocení uživateli, či zda má jen tiše proběhnout na pozadíbez výstupu.

2Tento přístup je využit v implementaci radiánů pro účel vyšší přesnosti; žádná jednotkav této práci nemá složitější vztah, než lineární.

3Ve výrazech se ANS píše jako Ans, protože při parsování výrazů se proměnné převádí natextové řetězce tím, že se vezme jejich interní pojmenování a všechna písmena kromě prvníhose převedou na malá.

22

Page 23: Programovatelný kalkulátor (bakalářská práce)

5.7.1 Výrazový příkaz

Třída ExpressionStatement jednoduše obaluje výraz (Expression) do pří-kazu pro učely obsažení instanci třídy v Program. Vyhodnocení je implemento-váno vyhodnocením obaleného výrazu.

5.7.2 Podmíněný příkaz

Třída ConditionalStatement obsahuje výrazový příkaz (podmínka) a libo-volný jiný příkaz (včetně dalšího podmíněného příkazu). Používá se k realizacivětvení na základě vyhodnocení podmínky. Pokud se podmínka vyhodnotí na ne-nulové číslo, výsledkem vyhodnocení je výsledek vyhodnocení druhého příkazu.Jinak je výsledek Double.NaN.

5.7.3 Příkaz přiřazení

Třída AssignmentStatement obsahuje výrazový příkaz a proměnnou. Používáse k přiřazení hodnoty do paměťového místa kalkulátoru. Výsledek vyhodnoceníje výsledkem vyhodnocením výrazového příkazu.

5.7.4 Příkaz uživatelského vstupu

Třída InputStatement dědí z příkazu přiřazení. Používá se k získání vstupuod uživatele kalkulátoru. Výsledek vyhodnocení je hodnota proměnné ve vyhod-nocovacím kontextu.

5.7.5 Příkaz skoku

Třída GotoStatement dědí z IndexedStatement. Obsahuje celé číslo,které slouží jako symbolické pojmenování místa v programu. Příkaz skoku ozna-čuje skok aktuálního vykonávání na pojmenované místo. Vyhodnocuje se naDouble.NaN.

5.7.6 Příkaz návěští

Třída LabelStatement dědí z IndexedStatement. Obsahuje celé číslo, kteréslouží jako symbolické pojmenování místa v programu. Příkaz návěští označujetoto pojmenované místo. Vyhodnocuje se na Double.NaN.

5.7.7 Příkaz změny jednotky

Třída ChangeUnitStatement dědí z ChangeStatement. Obsahuje cílovou jed-notku, do které přepnout režim kalkulátoru. Vyhodnocuje se na Double.NaN.

23

Page 24: Programovatelný kalkulátor (bakalářská práce)

5.8 ProgramTřída Program ukládá programy jako posloupnosti (java.util.List) pří-

kazů. Program se přeloží do interní reprezentace zavoláním jediného 4 konstruk-toru třídy Program, který bere jako parametr řetězec znaků String reprezentu-jící zápis celého programu. Druhý parametr je volitelný a jedná se o booleovskouhodnotu, která, při hodnotě true, potlačí veškeré výjimky vyvolané chybou připřekladu. Toto slouží hlavně pro vestavěný editor programů, který takto můžeodhalit více chyb zároveň, namísto pouze první, která se vyskytne.

5.8.1 Překlad

V první fázi se celý text rozdělí po znacích ’:’ a ’;’, které slouží jako separá-tory v programu, na příkazové řetězce. Znak ’:’ slouží jako obyčejný oddělovač,’;’ je oddělovač, který způsobí výstup vyhodnocení na displej kalkulátoru (apozastaví tím vyhodnocování, dokud uživatel nepotvrdí, že výstup viděl). Po-slední příkaz programu má vždy atribut výstupu nastaven na true, bude tedyvždy zobrazen. Každý příkazový řetězec je přeložen na příkaz statickou metodouStatement.parse a přidán do seznamu příkazů.

V druhé fázi se inicializuje pomocné zobrazení (java.util.Map), které určujepozici počátečního znaku příkazu v celém programu. Používá se k lokalizaci chybv chybových hláškách při vykonávání programu.

Ve třetí vázi se inicializuje pomocné zobrazení, které určuje pozice pojmeno-vaných míst v programu pro rychlé skoky během vykonávání.

Ve čtvrté fázi dojde ke kontrole skoků. Zde se kontroluje, zda nějaký příkazskoku neodkazuje na neexistující místo v programu.

Jakékoliv výjimky jsou buď předány volajícímu (při vypnutém debuggingu),nebo uloženy do seznamu chyb v programu, který lze po překladu obdržet skrzebezparametrovou instanční metodu getErrors.

5.8.2 Vykonávání

Vykonování programu je realizováno skrze třídu java.util.Iterator, kteráumožňuje následující zápis v jazyce Java:

1 for (int n : new int[]{ 1, 2, 3, 4, 5 }) {2 System.out.println(n);3 }

Zdrojový kód 7: Příklad použití iterátoru ve vylepšené for smyčce

Iterátor lze obdržet zavoláním metody inContext, které se předá vyhodno-covací kontext, ve kterém program vykonávat. Spojením konstrukce vylepšené

4Striktně vzato má Program dva konstruktory, ale pouze k implementaci volitelných para-metrů v jazyce Java.

24

Page 25: Programovatelný kalkulátor (bakalářská práce)

for smyčky (též nazývané „foreach“ v jiných jazycích) lze dosáhnout následují-cího zápisu:

1 for (Statement statement : program.inContext(context)) {2 // ...3 }

Zdrojový kód 8: Použití vylepšené for smyčky pro vykonávání programu

Iterátor se stará o správné pořadí vracených příkazů. Vyhodnocovací kontextje nutné předat kvůli podmíněným příkazům, jejichž podmínky je nutno vyhod-nocovat přímo v iterátoru, jiné vyhodnocování v iterátoru neprobíhá. Volajícívidí vracené podmíněné příkazy, ale nemůže měnit chod programu.

5.9 KalkulátorTřída Calculator implementuje abstraktní kalkulátor, který má aktuální vy-

hodnocovací kontext (který lze za běhu měnit) a umí spouštět programy.Spustit program lze skrze metodu run, která bere jako parametr Program a

instanci Communicable, což je vnořené rozhraní (interface) třídy Calculator,které zajišťuje komunikaci s uživatelem kalkulátoru.

5.9.1 Komunikace s uživatelem

Rozhraní Calculator.Communicable definuje následující metody pro komu-nikaci s uživatelem během vykonávání programu:

• boolean abort(Statement)

– zavoláno před vyhodnocením příkazu, vrací true, pokud se má vy-konávání programu zrušit (vhodné pro „čisté“ ukončení nekonečnýchsmyček)

• void evaluated(Statement)

– zavoláno po úspěšném vyhodnocení příkazu

• void output(Statement, double)

– zavoláno po vyhodnocení příkazu, který vyžaduje výstup

• void displayInputError(IndexedException)

– ukáže uživateli chybu ve vstupu

• String input(InputStatement)

25

Page 26: Programovatelný kalkulátor (bakalářská práce)

– získá vstup od uživatele, prázdný String indikuje ponechání stávajícíhodnoty

• boolean tryInputAgain()

– zavoláno po zobrazení chyby ve vstupu, vrací true pokud chce uživatelopravit svůj vstup

5.9.2 Běh programu

Při spuštění programu se obdrží iterátor ve vyhodnocovacím kontextu kal-kulátoru (viz 5.8.2) a pro komunikaci se využije předané Communicable. Předvyhodnocením se zavolá metoda abort. Pokud si uživatel přeje vyhodnocovat,pokračuje se dle následujícího seznamu:

1. Pokud je příkaz podmíněný, pokračuje se dalším (jelikož vyhodnocováníprovádí samotný iterátor).

2. Pokud je příkaz skok nebo návěští, pokračuje se dalším (jelikož nemá smysltento příkaz vyhodnocovat).

3. Pokud příkaz vyžaduje změnu nastavení kalkulátoru (např. změna úhlovéjednotky), změna se provede a pokračuje se dalším příkazem

4. Jinak se musí jednat o příkaz, který má smysl vyhodnotit. Vyhodnocenízávisí na typu příkazu:

• Přiřazení – do proměnné se uloží výsledek vyhodnocení.• Vstup – od uživatele se metodou input získává vstup tak dlouho,

dokud není správný nebo si uživatel nepřeje skončit. Při správemvstupu je výsledek jeho vyhodnocení uložen do proměnné. Při nespráv-ném vstupu se metodou displayInputError chyba oznámí a metodoutryInputAgain se zjistí, zda vstup opakovat. Pokud si uživatel nepřejeopakovat vstup, běh programu je ukončen.

• Výrazový – výraz se jednoduše vyhodnotí.

Pokud příkaz vyžaduje výstup, je zavolána metoda output. V každém případěse nastaví proměnná Variable.ANS ve vyhodnocovacím kontextu kalkulátoru navýsledek posledního vyhodnocení. Úspěšné zpracování každého příkazu (včetněnevyhodnocovaných příkazů) je oznámeno metodou evaluated.

Pokud se během vykonávání vyskytne nějaká chyba (např. nedodržení ome-zení operátoru, mezivýsledek nebo výsledek není konečná hodnota double, . . . ),je předána výjimkou volajícímu.

26

Page 27: Programovatelný kalkulátor (bakalářská práce)

5.10 EmulátorNásledující podkapitola je výjimkou oproti zbytku. Popisuje implementaci

aplikační logiky a definované třídy se již nenachází v balíčkucz.upol.zaplmi06.calculator.core. Třídy zde popisované se nachází v balíčkucz.upol.zaplmi06.calculator.

Vstupem je metoda main ve třídě ProgrammableCalculatorApplication. Přispuštění aplikace proběhne nejprve kontrola parametrů příkazového řádku. Apli-kace rozpoznává pouze první parametr, kterým je buď -graphics, který vynutípoužití grafického uživatelského rozhraní, nebo -console, který vynutí použitítextového uživatelského rozhraní. Při nepřítomném parametru je použití rozhranívyhodnoceno běhovým prostředím Java, které rozpozná, zda uživatel spouštíaplikaci z příkazového řádku nebo z grafického prostředí operačního systému.

ProgrammableCalculatorApplication má bezparametrový konstruktor, je-hož zavoláním se vytvoří samotná aplikace. Aplikaci se poté předá instanceuživatelského rozhraní, které implementuje rozhraní UserInterface. Uživatel-ské rozhraní poté komunikuje s aplikací posíláním příkazů (např. vypni aplikaci,vyhodnoť výraz, načti program ze souboru, . . . ), což jsou instance třídy Command.

5.10.1 Uživatelské rozhraní

UserInterface je rozhraní (interface), které popisuje uživatelské rozhraní.Definuje následující metody:

• void confirm(Command)

– zavoláno aplikací po úspěšném vykonání příkazu jako zpětná vazbapro uživatelské rozhraní

• void displayFatalError(Exception)

– zobrazí fatální chybu uživateli; aplikace se nejpravděpodobněji ukončíihned po zobrazení chyby

• void displayError(Exception)

– zobrazí chybu uživateli; aplikace se nejpravděpodobněji z chyby zotaví

• void displayIOError(IOException)

– zobrazí chybu vstupu/výstupu; aplikace se nejpravědoboněji z chybyzotaví

• void displayIndexedError(IndexedException)

– zobrazí chybu, která má dohledatelnou pozici v nějakém řetězci(např. syntaktická chyba v 19. znaku zadávaného výrazu); aplikacese nejpravděpodobněji z chyby zotaví

27

Page 28: Programovatelný kalkulátor (bakalářská práce)

• Command getCommand()

– získá od uživatele příkaz (např. vyhodnoť výraz, ukonči aplikaci, . . . )

• Communicable getCommunicable()

– vrátí implementaci komunikace s běžícím programem (viz 5.9.1)

• void receive(Map<Integer, Program>)

– předá uživatelskému rozhraní úložiště programů v paměti

• void receive(Calculator)

– předá uživatelskému rozhraní abstraktní kalkulátor

• void setEvaluating(boolean)

– uvede uživatelské rozhraní do režimu vyhodnocovaní nebo jej z něj vy-vede (dle hodnoty parametru); slouží zejména pro zablokování některéfunkcionality rozhraní během vyhodnocování (např. úprav běžícíhoprogramu)

• boolean isEvaluating()

– vrací true, pokud je abstraktní kalkulátor uprostřed vyhodnocování

• void hide()

– schová rozhraní

• void show()

– zobrazí rozhraní

5.10.2 Příkaz

Třída Command slouží k realizaci komunikace mezi uživatelským rozhraním aaplikační logikou. Definuje typ příkazu jako výčtový datový typ pro efektivnízpracování aplikací (použitím konstrukce switch v jazyce Java, pro kterou pře-kladač implementuje hlídání ošetření všech hodnot výčtového typu). Každý pří-kaz je definován soukromým (private) konstruktorem, aby příkazy šlo vytvářetpouze skrze tzv. „factory“ metody, které vrací instance třídy Command.

Podporované jsou následující příkazy:

• smazání uloženého programu

• změna jednotky

• ukončení aplikace

28

Page 29: Programovatelný kalkulátor (bakalářská práce)

• načtení programu ze souboru

• prázdný příkaz

• načtení uloženého programu

• spuštění programu

• spuštění uloženého programu

• uložení programu

• uložení programu do souboru

5.10.3 Konzolové uživatelské rozhraní

Konzolový režim aplikace implementuje třída ConsoleInterface. Uživatel-ský vstup načítá z java.lang.System.in (ekvivalent stdin) a výstup posílá dojava.lang.System.out (ekvivalent stdout).

Definuje vnořený výčtový typ InputParser, který zpracovává uživatelskývstup a na základě něj posílá příkazy do aplikace. Definice třídy InputParser

je deklarativní; každý prvek je definován konstruktorem, který má následujícíprototyp:

InputParser(String keyword,String description,Predicate<String> matcher,Function<String, Command> getter)

Parametr keyword slouží k definici slova, které stojí na začátku vstupu a ak-tivuje tento příkaz. Parametr description obsahuje uživatelsky přívětivý popissyntaxe příkazu. Parametr matcher je funkce, která je zavolána na vstup (bezklíčového slova) a vrací true, pokud je možné příkaz provést (např. uživatelneudělal chybu v syntaxi, program je doopravdy uložen v daném paměťovémmístě, . . . ). Parametr getter je funkce, která je zavolána na vstup (bez klíčo-vého slova) a vrací instanci Command, kterou lze přímo předat aplikaci. Speciálnípříkaz HELP tiskne na vstup přehled všech definovaných příkazů a lze jej z konzolezpřístupnut zadáním řetězce help.

Samotná práce s konzolovým rozhraním se sestává ze zadávání vstupů, jejichžzačátek se porovná postupně s každým klíčovým slovem. Jakmile se narazí nashodu, ověří se proveditelnost příkazu. V kladném případě se příkaz vykoná,v záporném se zobrazí jeho syntaxe a čeká se na další vstup. Pokud nedošlo keshodě se žádným z klíčových slov, celý vstup se interpretuje jako zadání programua pokusí se přeložit a vyhodnotit.

29

Page 30: Programovatelný kalkulátor (bakalářská práce)

5.10.4 Grafické uživatelské rozhraní

Grafický režim aplikace implementuje třída GraphicalInterface a její po-mocná třída Utilities, situovaná v balíčku cz.upol.zaplmi06.calculator.gui.K implementaci grafického rozhraní byla použita knihovna Swing. Z tohoto dů-vodů je GraphicalInterface také potomkem třídy javax.swing.JFrame.

Swing je grafická knihovna, která se zakládá na principu tzv. „lightweight“komponent, což znamená, že každý grafický prvek je zodpovědný za své vy-kreslení na obrazovce. Narozdíl od „heavyweight“ komponent, které vykreslujeoperační systém, nemusí lightweight komponenty mít svůj protějšek dostupnýv operačním systému, což umožňuje vyšší použitelnost díky menší závislosti nadostupnosti daných grafických prvků v cílové platformě. Knihovna Swing je nad-stavbou knihovny AWT (Abstract Window Toolkit), která se skládá pouze z hea-vyweight komponent. Swing navíc definuje vlastní „look & feel“ (vzhled a chováníkomponent), díky kterému je zcela nezávislý na operačním systému. Nicméně,Swing podporuje emulaci grafických prvků operačního systému, což je výchozíchování v této práci. Look & feel, který Swing poskytuje, se využije teprve přinedostupnosti implementace v operačním systému.

Třída GraphicalInterface obsahuje mnoho vnořených tříd (i do více úrovní),jelikož to usnadnilo přístup ke grafickému oknu samotnému, které je takto do-stupné lexikálně a nemusí se předávat jako parametr. Hlavními celky jsou lištas menu (MenuBar), hlavní panel (CalculatorPanel), panel s editorem programů(IDEPanel) a ladící panel (DebugPanel).

Pro zjednodušení práce byla vytvořena třída Utilities, která obsahuje sta-tické metody pro zjednodušení definování líšty s menu, pro které má Swing jenvelmi omezené API.

Většina ovladacích prvků je generována dynamicky na základě dostupnýchoperátorů a jednotek.

6 Uživatelská příručkaTato kapitola popisuje ovládání aplikace z pohledu uživatele a uvádí příklady

programů.Aplikaci lze spustit otevřením souboru ProgrammableCalculator.jar v bě-

hovém prostředí Java 8 nebo vyšším.Grafické rozhraní se skládá z menu a zobrazovací oblasti, jejíž obsah je dán

zvolenou kartou v pásu karet pod ní. Uživatelské menu umožňuje načítat pro-gramy, přepínat jednotky (stupně, radiány a gradiány), vkládat různé operátorydle kategorií, vkládat proměnné, vkládat rezervovaná slova a upravovat ladícíparametry. Pás karet se skládá z karet Calculator (standardní rozhraní kalkulá-toru), IDE (editor programů) a Debug (ladění). Při spuštění aplikace se zobrazujekarta standardního rozhraní kalkulátoru.

30

Page 31: Programovatelný kalkulátor (bakalářská práce)

Obrázek 2: Uživatelské rozhraní ihned po spuštění aplikace

31

Page 32: Programovatelný kalkulátor (bakalářská práce)

Obrázek 3: Výčet „goniometrických“ operátorů

32

Page 33: Programovatelný kalkulátor (bakalářská práce)

Obrázek 4: Výčet operátorů, které jsou inverzní k jiným operátorům

6.1 Standardní rozhraní kalkulátoruStandardní rozhraní kalkulátoru se skládá ze vstupního řádku, výstupního

řádku a ovladacích prvků (tlačítek). Je označeno kartou Calculator.Kliknutí na ovladací prvek simuluje stlačení tlačítka na kalkulátoru a má

následující významy:

• EXE nechá vyhodnotit vstupní řádek, popřípadě potvrdí výstup, pokud senějaký právě zobrazuje

• AC zruší právě probíhající výpočet nebo program

• jiné tlačítko způsobí vložení svého obsahu do vstupního řádku

Vstupní řádek lze editovat i přímo (např. klávesnicí), je tudíž možné do nějvložit libovolný text, včetně nějakého programu – nejde však o nejpříjemnějšízpůsob programování, a proto aplikace poskytuje editor programů.

33

Page 34: Programovatelný kalkulátor (bakalářská práce)

Obrázek 5: Příklad vyhodnocení výrazu ze standardního rozhraní

6.2 Editor programůEditor programů se skládá z menu, textového pole a oznamovací oblasti. Menu

obsahuje tlačítko pro spuštění programu (dostupné přes klávesovou kombinaciCtrl+R), tlačítka pro ukládání programů (dostupné přes klávesovou kombinaciCtrl+S) a tlačítko pro kontrolu syntaxe.

Textové pole obsahuje zdrojový kód programu. Řádky programu jsou očíslo-vané pro lepší navigaci. Jednotlivé prvky jazyka programovatelného kalkulátorupoužívají různá barevná zvýraznění pro lepší čitelnost.

Oznamovací oblast zobrazuje aktuální pozici kurzoru a, pokud to má smysl,cestu k aktuálně upravovanému programu na disku a jeho slot.

34

Page 35: Programovatelný kalkulátor (bakalářská práce)

Obrázek 6: Příklad programu v editoru programů

6.3 LaděníSekce ladění obsahuje údaje o vnitřním stavu kalkulátoru, a to zejména ak-

tuálně vyhodnocovaný příkaz a tabulku hodnot proměnných.Uživatel si může zvolit, zda chce program krokovat příkaz po příkazu.

35

Page 36: Programovatelný kalkulátor (bakalářská práce)

Obrázek 7: Příklad ladění běžícího programu; parametr krokování je zapnutý

Tabulku proměnných lze za běhu měnit vkládáním výrazů, které se vyhod-notí; výsledná hodnota se uloží do zvolené proměnné. Nelze však z tabulky měnithodnotu proměnné programem.

36

Page 37: Programovatelný kalkulátor (bakalářská práce)

Obrázek 8: Příklad úpravy proměnné za běhu programu

6.4 Načítání a ukládání programůPři načítání programu do kalkulátoru se otevře dialog pro zvolení souboru

(prezentace je závislá na operačním systému). Povoleny jsou pouze soubory s pří-ponou sec, které obsahují zdrojové kódy pro kalkulátor. Stejný dialog je použiti pro ukládání programů z editoru programů, bude tedy popsán ve stejném kon-textu.

Při zvolení souboru se zobrazí jeho náhled. Také má uživatel možnost zvolittzv. slot, do kterého bude program uložen. Obdobně při ukládání programu můžeuživatel zvolit slot, program bude potom nejen uložen na médium (např. hard-disk), ale také načten do zvoleného slotu.

Omezením načítání a ukládání do slotů je, že daný program musí být syntak-ticky správný. Pokud daný program obsahuje syntaktickou chybu, je oznámena

37

Page 38: Programovatelný kalkulátor (bakalářská práce)

a načtení či uložení neproběhne.5Uživatel má také možnost načítat program ne do slotu, ale nýbrž do okamžité

paměti kalkulátoru. Při tomto okamžitém načtení neprobíhá syntaktická kontrolaa lze tak ukládat a načítat rozpracované programy.

Obrázek 9: Výběr programu ze souborového systému

6.5 Konzolový režim a spouštěcí parametryBěhem vývoje aplikace byl kalkulátor testován v konzolovém režimu. Tento

režim zůstal dostupný a je automaticky použit při spouštění kalkulátoru z kon-zole, pokud je zjištěna běhovým prostředím Java.

Aplikace pak interaktivně vyhodnocuje zadávané výrazy a programy. Pokudse některý z příkazů vyskytuje na začátku vstupu, je vykonán místo vyhodno-cování vstupu jako výrazu či programu. Návod k příkazům konzole lze získatnapsáním příkazu help.

6.5.1 Spouštěcí parametry

Vynucení konzolového režimu lze dosáhnout předáním parametru -console.Vynucení grafického režimu lze dosáhnout předáním parametru -graphics.Předáváný parametr musí být první. Při předání více parametrů se ostatní pa-rametry ignorují.

5Oproti Sencor SEC 103 je toto radikálnější přístup, neboť předlohový kalkulátor umožňovalukládát i syntakticky neplatné programy do paměti; o chybě syntaxe se uživatel dozvěděl ažza běhu programu.

38

Page 39: Programovatelný kalkulátor (bakalářská práce)

6.6 Psaní výrazůKalkulátor pracuje s výrazy v infixové notaci, které se běžně používají a jsou

součástí osnov základních škol. Jsou to například následující výrazy:

• 1 + 1

• 4 * 7 (znak hvězdičky se používá pro násobení)

• 12 + 6 - 9

• 3 * 5 + 1.7 (používá se desetinná tečka)

• 3 / (5 + 1) (znak lomítka se používá pro dělení)

Kalkulátor podporuje plný algebraický zápis, což umožňuje psát funkce (zdenazývané operátory) před i za jejich argumenty (podvýrazy). Příklady použití:

• sqrt(25) (odmocnina z 25)

• sqrt 25 (to samé, jako předchozí)

• sqrt25 (opět to samé; na mezerách nezáleží)

• 3! (faktoriál 3)

• 7^8 (umocnění čísla 7 na 8)

Pro násobení konstant není nutno explicitně psát násobení, to umožňuje na-příklad místo 3*Pi psát jen 3Pi.

Při pracování s úhlovými jednotkami (např. funkcí sin) se používá aktuálnínastavená úhlová jednotka. Proto při práci ve stupních se sin90 vyhodnotí na1.0. Při práci v radiánech se ale výsledek liší a je přibližně 0.89. Pro specifikacijednotky lze použít operátory deg, rad a grad. Jednoznačný zápis by tedy mohlbýt sin(90 deg).

Závorky lze použít pro řízení pořadí vyhodnocování. Bez závorek platí stan-dardní konvence přednosti:

1. konstanty (Pi, e, . . . )

2. proměnné (Ans, A, B, . . . )

3. operátory s postfixovou notací (!, deg, rad, grad, . . . )

4. operátory s prefixovou notací (sqrt, sin, cos, tan, . . . )

5. umocňování (^)

6. násobení a dělení (*, /)

7. sčítání a odečítání (+, -)

39

Page 40: Programovatelný kalkulátor (bakalářská práce)

Při použítí závorek se mohou vynechat všechny závorky na konci výrazu avšechny závorky na začátku výrazu – budou doplněny automaticky.Například výraz 7 - 5)/ 3 * (1 - 7 * (1 + 2 se vyhodnotí jako(7 - 5)/ 3 * (1 - 7 * (1 + 2)).

6.7 Vyhodnocování výrazůPo zadání výrazu do vstupního řádku lze příkaz nechat vyhodnotit tlačítkem

EXE. Výsledek posledního vyhodnocení je dostupný v proměnné Ans. Napříkladvyhodnocení výrazu 3 + 9 zobrazí ve výstupním řádku 12.0; pokud ihned poténecháme vyhodnotit výraz Ans, výsledkem bude rovněž 12.0.

6.8 Psaní programůProgram je posloupnost příkazů. Nejjednodušším příkazem je tzv. výrazový

příkaz, což je příkaz skládající se pouze z výrazu, který má syntaxi identickous výrazem.

Více příkazů lze za sebe zřetězit pomocí znaku dvojtečky (:) nebo středníku(;). Středník slouží k výstupu na obrazovku. Za poslední příkaz se žádný z dřívezmíněných dvou znaků nepíše a je implicitně výstupní.

Příkaz přiřazení se píše tak, že za výraz se přidá dvojice znaků -> a název pro-měnné. Například příkaz 3Pi -> B uloží do proměnné B trojnásobek π.6 Pokudmísto výrazu napíšeme otazník, dostaneme vstupní příkaz, při kterém uživatelzadává vstup do proměnné. Například příkaz ? -> A načte vstup od uživatele doproměnné A.

Podmíněný příkaz se realizuje napsáním výrazu, dvojice znaků => a dalšímpříkazem (včetně dalšího podmíněného). Pokud se výraz vlevo od => vyhodnotína nenulovou hodnotu, vyhodnotí se i příkaz napravo od =>, jinak se pokračujeaž dalším příkazem. Například příkaz 3Pi > 8 => 1337 se vyhodnotí na 1337.0,jelikož je podmínka pravdivá.

Příkazem skoku, který se píše jako Goto a nějaké číslo, skočíme na místov programu označené příkazem návěští, které se píše podobně, jen místo Goto

obsahuje Lbl. Například program Lbl1:Goto1 implementuje nekonečnou smyčku.Jako komplexnější příklad slouží program na výpočet prvočísel, který po-

stupně zobrazuje prvočísla na obrazovce, dokud není násilně ukončen.

6Při vykonávání příkazů se stále mezivýsledky ukládají do Ans.

40

Page 41: Programovatelný kalkulátor (bakalářská práce)

1 2;2 3->A:3 Lbl0: 3->B:4 Lbl1: B>sqrtA => Goto2:5 AmodB=0 => Goto3:6 B+2->B:7 Goto1:8 Lbl2: A;9 Lbl3: A+2->A:10 Goto0

Zdrojový kód 9: Příklad programu na výpočet prvočísel

Druhý příklad je program, který spočítá n-té Fibonacciho číslo (kde n je číslozadané uživatelem do proměnné X).

1 ?->X:2

3 0->A:4 1->B:5

6 0->C:7 Lbl1: C>=X => Goto2:8

9 A+B->D:10 B->A:11 D->B:12

13 C+1->C:14 Goto1:15 Lbl2:16 A

Zdrojový kód 10: Příklad programu na výpočet n-tého Fibonacciho čísla

7 DiskuzeSestrojený emulátor programovatelného kalkulátoru plně podporuje vyhodno-

cování libovolných infixových výrazů, vykonávání programů se syntaxí korespon-dující Sencor SEC 1037 a implementuje grafické rozhraní s obdobným rozmís-těním ovladacích prvků, jako tomu je u většiny programovatelných kalkulátorů.API aplikace bylo navrženo pro co nejpohodlnější rozšíření o novou funkcionalitu,která zde nebyla implementována.

7Rozšíření o nové funkce (např. logické operátory) umožňuje ještě vyšší úroveň pohodlí.

41

Page 42: Programovatelný kalkulátor (bakalářská práce)

Vypracovanému rešení by šlo vytknout několik věcí. Nebyla pokryta celá funk-cionalita předlohy, jako jsou například komplexní čísla, numerické derivace, ur-čité integrály nebo statistické výpočty. Rozšíření o komplexní čísla by vyžado-valo podstatný zásah do kódu tříd Operator a Expression. Rozšíření o nume-rické derivace a určité integrály by vyžadovalo podstatný zásah do kódu třídyExpression. Statistické výpočty by vyžadovaly vlastní režim, což by nejspíš bylarozsáhla funkcionalita, která by zasáhla do podstatné části kódu. Dále, z progra-mátorského hlediska, nebylo grafické rozhraní napsané lehce rozšířitelným způso-bem. Vyhodnocovací proces byl implementován s využitím lineární datové struk-tury. Třída Expression by mohla být reimplementována jako stromová struk-tura pro zvýšenou přehlednost implementace, a tím pádem nižší pravděpodob-nost chyb při rozšiřování funkcionality. Lineární datová struktura byla použitave snaze pochopit implementaci popsanou v uživatelské příručce předlohovéhokalkulátoru.

Alternativou k ukládání programů ve zdrojovém kódu by byl tzv. bytecode,který by byl kompaktnější a rychleji načtený. Tento způsob ale nebyl zvolen prolepší čitelnost kódu i mimo vývojová prostředí a celkovou podobnost k běžnýmkódům interpretovaných jazyků.

42

Page 43: Programovatelný kalkulátor (bakalářská práce)

ZávěrByl sestrojen emulátor programovatelného kalkulátoru Sencor SEC 103 v ja-

zyce Java 8 s grafickým uživatelským rozhraním. Implementuje téměr veškeroufunkcionalitu předlohy a přidává některé funkce navíc (např. více operátorů, edi-tor programů, ladící nástroje, . . . ). Neimplementuje pouze pokročilejší funkcio-nalitu předlohy, kterou mnoho uživatelů nepoužíva, například komplexní čísla.

Vyhodnocování výrazů je implementováno upravenou verzí algoritmu Shunting-yard, který využívá k vyhodnocování linární datovou strukturu. Čísla jsou repre-zentována 64bitovým číslem s plovoucí desetinnou čárkou dle standardu IEEE 754.Programy jsou rovněž implementovány linární datovou strukturou. Umožňujízákladní programové konstrukce, včetně podmíněných skoků. Komunikace meziuživatelským rozhraním a aplikací je realizována objekty příkazů, které pomáhajírealizovat modularitu celé aplikace. Programování nové funkcionality (např. ope-rátory) do kalkulátoru je zjednodušeno příjemným API.

Práce je dostupná na CD přiloženém k práci pod svobodnou licencí.

43

Page 44: Programovatelný kalkulátor (bakalářská práce)

ConclusionsAn emulator of the programmable calculator Sencor SEC 103 with a graphical

user interface has been created using Java 8. It implements almost all function-ality of its model and adds additional functionality (eg. more operators, an IDE,debugging tools, . . . ) It does not implement the more advanced functionality ofthe model, not used by many users, such as complex numbers.

Evaluating expressions is implemented using a custom version of the Shunting-yard algorithm, which utilizes a linear data structure. Numbers are representedusing the IEEE 754 64-bit floating point number. Programs are also implementedusing a linear data structure. They support basic programming constructions,including conditional jumps. Communication between the user interface and theunderlying application is achieved using command objects, which help create themodularity of the application. Programming more functionality (eg- operators)into the calculator is made easier thanks to a convenient API.

The thesis is available on the attached CD under open license.

44

Page 45: Programovatelný kalkulátor (bakalářská práce)

A Obsah přiloženého CDbin/

Program ProgrammableCalculator.jar, spustitelné přímo z CD.K běhu je vyžadováno běhové prostředí Java 8 nebo vyšší.

doc/Text práce ve formátu PDF, vytvořený s použitím závazného stylu KI PřFUP v Olomouci pro závěrečné práce, včetně všech příloh, a všechny souborypotřebné pro bezproblémové vygenerování PDF dokumentu textu (v ZIParchivu), tj. zdrojový text textu, vložené obrázky, apod.

src/Kompletní zdrojové kódy emulátoru.

readme.txtInstrukce pro spuštění emulátoru, včetně všech požadavků pro jeho bez-problémový provoz.

Navíc CD obsahuje:

programs/Ukázkové programy pro emulátor.

45

Page 46: Programovatelný kalkulátor (bakalářská práce)

Bibliografie[1] Algoritmus shunting-yard. In: Wikipedia: the free encyclopedia [online]. San

Francisco (CA): Wikimedia Foundation, 2001 [cit. 2019-04-06]. Dostupné z:https://cs.wikipedia.org/wiki/Algoritmus_shunting-yard

[2] Programmable Calculator. In: Wikipedia: the free encyclopedia [online]. SanFrancisco (CA): Wikimedia Foundation, 2001 [cit. 2018-02-05]. Dostupné z:https://en.wikipedia.org/wiki/Programmable_calculator

[3] Scientific Calculator | SEC 103. [cit. 2019-05-06]. Dostupné z:http://www.sencor.eu/Sencor/files/9f/9f2b9b9e-bc9a-4060-b816-9b1438329b30.png

46