Informationsteknologi Tom Smedsaas 22 augusti 2018 Obligatorisk uppgift: Numerisk kalkylator Programmet skall läsa in aritmetiska uttryck samt beräkna och skriva ut deras värden. Programmet ska läsa från standard input (System.in) och skriva till standard output (System.out). Exempel: (programmet använder > som prompter): > 1 - (5-2*2)/(1+1) - (-2 + 1) 1.5 > sin(3.14159265) 3.5897930298416118E-9 > exp(4*0.5-1) 2.7182818284590455 > exp(log(10)) 10.000000000000002 > sin(2)*sin(2) + cos(2)*cos(2) 1.0 > 1 + 2 + 3 = x 6.0 > x 6.0 > x/2 + x 9.0 > (1=x) + sin(2=y) 1.9092974268256817 > x 1.0 > y 2.0 > 2*PI 6.283185307179586 Kommentarer: 1. Programmet skall hantera uttryck med konstanter, variabler, de aritmetiska operatorerna +, -, * och / samt de elementära funktionerna sin, cos, exp och log. 2. De vanliga prioritetsreglerna skall gälla och parenteser skall kunna användas för att ändra beräkningsordning på vanligt sätt. 3. Alla beräkningar görs i flyttalsaritmetik (double). 4. Variabeltilldelning görs från vänster till höger. Exempel: Uttrycket 1+2*3 = y skall tilldela y värdet 7. 5. Variabeltilldelningar skall kunna göras i deluttryck. Exempel: (2=x) + (3=y=z) = a skall ge värden till x, y, z och a. 1
6. Den fördefinierade variabeln ans skall innehålla värdet av det senast beräknade fullständiga uttrycket. Denna variabel kan användas i nästa uttryck. Exempel: > 1+1 2.0 > ans 2.0 > exp(2) 7.38905609893065 > ans 7.38905609893065 > ans + 3 10.38905609893065 > 3 + ans 13.38905609893065 7. Programmet skall upptäcka och diagnostisera fel. Exempel: > 1++2 *** Syntax error. Expected number, or ( *** The error occured at token + just after token + > 1+-2-1.0 > 1--2 3.0 > 1**2 *** Syntax error. Expected number, or ( *** The error occured at token * just after token * > 1/0 *** Evaluation error. Division by zero > 1+2*y-4 1.0 > 1+2*k-4 *** Evaluation error. Undefined variable: k > 1+2=3+4**x - 1/0 *** Syntax error. Expected variable after = *** The error occured at token 3.0 just after token = > 1+2*(3-1 a *** Syntax error: Expected ) *** The error occured at token a just after token 1.0 > 1+2+3+ *** Syntax error: Expected number, or ( *** The error occured at token *EOL* just after token + > Syntaxfel ska ge upphov till följande felutskrift: "*** Syntax error. <Beskrivning av felet> <Beskrivning av var felet uppkom>" (Observera radbrytningen.) Ett evalueringsfel ska ge upphov till följande felutskrift: *** Evaluation error. <Beskrivning av felet> Det är viktigt att felmeddelandena följer beskrivningarna ovan för att de skall godkännas av testfilerna! Observera att uttrycket skall kastas bort när ett fel har upptäckts så att inte man får fler diagnoser på samma uttryck. Variabeln ans skall inte ändras om ett uttryck är felaktigt. 2
8. Kommandot vars visar alla lagrade variabler med värden och kommandot quit avslutar körningen. Exempel: > vars ans : 13.38905609893065 E : 2.718281828459045 PI : 3.141592653589793 x : 1.0 y : 2.0 > quit Bye! Syntaxen för uttrycken definieras av följande diagram: = + eller ( ) * eller / function name number function name sin cos exp log Det syntaktiska elementet finns med som en platshållare för uppgift 5. statement EOL quit EOL vars file filename (EOL står för End Of Line dvs radslut). 3
Programdesign Klasser Programmet skall ha följande klasser (med angivna namn): Parser: Sköter tolkandet och beräkningarna. Klassen skall ha en metod för varje syntaktiskt element (dvs,,, och ) Dessa metoder skall fungera som syntaxdiagrammen beskriver. (Metoden statement med dess hjälpmetod line finns i klassen Calculator). Stokenizer: Sköter uppdelningen av indatasträngen i symboler (ord, tal mm). Denna klass som tillhandahålls av oss skall användas. Calculator: Denna klass är given av oss. Den innehåller bland annat programmets main-metod. SyntaxException: Beskriver syntaxfel EvaluationException: Beskriver beräkningsfel Variabelvärden För att hålla reda på variablers värden skall en TreeMap användas. Eftersom värden variablerna identifieras av sitt namn och värden är symboliska blir deklarationen: Map<String,Double> variables = new TreeMap<String,Double>(); Även den fördefinierade variabeln ans ligger i listan! Hantering av syntaxfel Om användaren skriver ett uttryck som inte stämmer med syntaxen enligt syntaxdiagrammen (t ex a ++b eller sin + 4 eller 2*3)+ 8) är det ett syntaxfel. Programmet ska upptäcka sådan fel och hantera dem med undantaget SyntaxException. Det kan vara frestande att leta efter fel överallt men det är bara meningsfullt på fyra ställen: I : likhetstecknet följs inte av ett ord I : 1. Första fallet: högerparentesen efter saknas 2. Andra fallet: vänsterparentes saknas efter function name 3. Inget av de fem fallen stämmer dvs inte vänsterparentes, inte funktionsnamn, inte tal, inte minustecken och inte ord. 4
= + eller ( ) * eller / function name function name sin cos exp log number De röda punkterna visar var man skall kontrollera syntaxen. I uttrycken för,, och function name finns det inget som kan gå fel och dessa metoder ska alltså inte leta efter fel. När koden upptäcker ett syntaxfel ska den kasta ett SyntaxException. Viktigt kodningstips: Lägg in felkontrollerna från början och dessutom med vettiga utskrifter om vad som förväntades, vad som hittades och var felet inträffade dvs efter vilken tok det upptäcktes. Detta underlättar er egen felsökning och testning av programmet! Undantagen fångas i statement (klassen Calculator). Där ska utskriften göras. Metoden har tillgång till tokenizern så den kan se både den aktuella och den föregående token. Det är alltså bara vad parsern hade väntat sig som behöver skickas med i undantaget. När felet har diagnostiserats ska resten av raden läsas bort (nexttoken) eftersom det inte är någon mening att fortsätta beräkningen av uttrycket. Hantering av beräkningsfel En annan typ av fel är att användaren ger ett uttryck med korrekt syntax men som inte kan beräknas. I denna uppgift är det enda beräkningsfelet att använda odefinierade variabler men i uppgift 5 tillkommer ytterligare några. Man kan betrakta t ex division med 0 eller logaritm av negativa tal som beräkningsfel även om flyttalsaritmetiken definiera värden för sådana operationer. Beräkningsfel ska signaleras med klassen EvaluationException. 5
Ett tips Skriv en hjälpmetod double functionhandler(string functionname) som anropas när man konstaterat att ett ord är ett funktionsnamn. Den givna Parser-klassen innehåller ett s.k. TreeSet som lagrar funktionsnamnen (exp, log,... ) med vars hjälp man avgör om ett ord är ett funktionsnamn (metoden contains). Observera att alla funktionsuttryck har samma syntax som således kan hanteras på ett ställe nämligen först i hjälpmetoden. Klassen Calculator Klassen Calculator som definierar den översta nivån i kalkylatorn är given. Den innehåller main- och statement-metoderna samt några hjälpmetoder. Den enda ändring du skall göra i den är att lägga till hanteringen av undantagen SyntaxException och EvaluationException på indikerade platser i statemet: public void statement() { try { line(); } catch (SyntaxException syntaxerror) {... } } catch (EvaluationException evaluationexception) {... } } Krav för att bli godkänd på uppgiften 1. Programmet skall fungera i enlighet med specifikationen och enligt det körexempel som finns i början på detta dokument. Även felhateringen skall fungera enligt exemplen. 2. Koden skall följa kodkonventionerna och även på andra sätta vara läsvänlig. 3. Den givna tokenizern (Stokenizer) och Calculator-klassen skall användas. 4. Recursive descent skall användas som parse-metod. Det skall finnas en metod för varje syntaktisk enhet (varje enskilt syntaxdiagram) med undantag för enheten som hanterar funktionsnamn. 5. Du skall kunna förklara hur programmet inklusive Calculator-klassen (förutom metoden fileinput) fungerar och beskriva hur man kan utöka det till exempel med nya funktioner och operatorer. 6