Del 3: Evaluere uttrykk Hva skal vi gjøre? Hvordan lagre Asp-verdier Hvilke operasjoner må jeg implementere? Er operasjonen lovlig? Utføre operasjonen
Strukturen til interpreten vår f.asp 3&4 Interpret 1 Skanner :Token :Token :Token 2 Parser <program> <while stmt> <expr> <suite> I denne omgang skal vi bare beregne uttrykk.
Hva skal vi gjøre? Evaluering av uttrykk Når vi kjører $ java -jar asp.jar -testexpr minfil.asp kalles denne metoden i Main: private static void dotestexpr(scanner s) { RuntimeScope emptyscope = new RuntimeScope(); while (s.curtoken().kind!= eoftoken) { AspExpr e = AspExpr.parse(s); AspSyntax.skip(s, newlinetoken); e.prettyprint(); log.prettywriteln(" ==>"); try { RuntimeValue res = e.eval(emptyscope); log.traceeval(res.showinfo(), e); catch (RuntimeReturnValue rrv) { panic("uncaught return exception!");
Hva skal vi gjøre? Et lite eksempel 1+1 == 2 1og1.asp Når vi kjører $ java -jar asp.jar -testexpr 1og1.asp This is the IN2030 Asp interpreter (2019-08-08) blir resultatet 1 + 1 == 2 ==> Trace line 1: True 1og1.log
Hva trenger vi? Asp-verdier For å kunne definere hvordan vi skal representere Asp-verdier, må vi tenkte på dette: Vi skal ikke bare lagre en verdi, men vi må ha informasjon om dens type også. Vi må kunne lagre mange ulike former for verdier; boolean True og False True dictionary Tabell over verdier { Ja : 17, Nei : 0 float Flyt-tall 3.14159 int Heltall 124 list Liste av verdier [1, 2, "Ja"] none None None string Tekster "Abrakadabra" Vi må kunne implementere samme operator (for eksempel +) på ulike måter.
OO er løsningen! Løsningen OO-programmering gir oss løsningen: RuntimeValue RuntimeBoolValue RuntimeIntValue - boolvalue: boolean - intvalue: long...
OO er løsningen! 1 Vi definerer public abstract class RuntimeValue { abstract protected String typename(); 2 Så definerer vi en subklasse for hver type verdier: public class RuntimeBoolValue extends RuntimeValue { boolean boolvalue; public RuntimeBoolValue(boolean v) { boolvalue = v; @Override protected String typename() { return "boolean"; @Override public String tostring() { return (boolvalue? "True" : "False");
OO er løsningen! RuntimeValue, RuntimeBoolValue og de andre tilsvarende klassene ligger i en egen modul. Main main LogFile scanner Token TokenKind Scanner parser runtime AspSyntax AspProgram Value Runtime- Runtime- IntValue AspExpr...... Runtime- BoolValue
OO er løsningen! Hvordan beregner vi verdier? Etter parseringen ligger «programmet» vårt (dvs uttrykket) lagret som et syntakstre av AspXxxx-noder. Vi lar dem alle få en eval-metode som kan beregne «seg selv»: public abstract class AspSyntax { public int linenum; AspSyntax(int n) { linenum = n; abstract RuntimeValue eval(runtimescope curscope) throws RuntimeReturnValue; (RuntimeScope og RuntimeReturnValue kommer vi til i del 4 av prosjektet.)
Literaler gir alltid samme verdi Literal Literaler er det enkleste å generere verdi for, for de skal alltid gi samme verdi. boolean literal True False class AspBooleanLiteral extends AspAtom { boolean value; @Override RuntimeValue eval(runtimescope curscope) throws RuntimeReturnValue { return new RuntimeBoolValue(value);
Er det like enkelt for operatorer? Operatorer not test not comparison Når vi skal implementere operatorer (unære eller binære), må vi først evaluere operandene: class AspNotTest extends AspSyntax { boolean withnot = false; AspComparison comp; @Override RuntimeValue eval(runtimescope curscope) throws RuntimeReturnValue { RuntimeValue v = comp.eval(curscope); if (withnot) { v = v.evalnot(this); return v;
Er det like enkelt for operatorer? Selve evalueringen gjøres ved å kalle på en metode hos den første (eller eneste) operanden. Vi får automatisk den riktige håndteringen av operator som brukes av ulike typer (for eksempel + som kan gjelde heltall, flyt-tall eller tekster). Om vi definerer en feilmelding som standardmetode, får vi automatisk riktig feilhåndtering.
Er det like enkelt for operatorer? RuntimeValue RuntimeValue - evalnot(): RuntimeValue - evalnot(): RuntimeValue RuntimeBoolValue RuntimeIntValue RuntimeBoolValue RuntimeIntValue...... - evalnot(): RuntimeValue - evalnot(): RuntimeValue - evalnot(): RuntimeValue «Vanlig» bruk Vår bruk
Er det like enkelt for operatorer? public abstract class RuntimeValue { public RuntimeValue evaladd(runtimevalue v, AspSyntax where) { runtimeerror(" + undefined for "+typename()+"!", where); return null; // Required by the compiler! public RuntimeValue evaldivide(runtimevalue v, AspSyntax where) { runtimeerror(" / undefined for "+typename()+"!", where); return null; // Required by the compiler! public RuntimeValue evalequal(runtimevalue v, AspSyntax where) { runtimeerror(" == undefined for "+typename()+"!", where); return null; // Required by the compiler! public RuntimeValue evalgreater(runtimevalue v, AspSyntax where) { runtimeerror(" > undefined for "+typename()+"!", where); return null; // Required by the compiler! public RuntimeValue evalgreaterequal(runtimevalue v, AspSyntax where) { runtimeerror(" >= undefined for "+typename()+"!", where); return null; // Required by the compiler! public RuntimeValue evalintdivide(runtimevalue v, AspSyntax where) { runtimeerror(" // undefined for "+typename()+"!", where); return null; // Required by the compiler!
Er det like enkelt for operatorer? public RuntimeValue evalless(runtimevalue v, AspSyntax where) { runtimeerror(" < undefined for "+typename()+"!", where); return null; // Required by the compiler! public RuntimeValue evallessequal(runtimevalue v, AspSyntax where) { runtimeerror(" <= undefined for "+typename()+"!", where); return null; // Required by the compiler! public RuntimeValue evalmodulo(runtimevalue v, AspSyntax where) { runtimeerror(" % undefined for "+typename()+"!", where); return null; // Required by the compiler! public RuntimeValue evalmultiply(runtimevalue v, AspSyntax where) { runtimeerror(" * undefined for "+typename()+"!", where); return null; // Required by the compiler! public RuntimeValue evalnegate(aspsyntax where) { runtimeerror("unary - undefined for "+typename()+"!", where); return null; // Required by the compiler! public RuntimeValue evalnot(aspsyntax where) { runtimeerror(" not undefined for "+typename()+"!", where); return null; // Required by the compiler!
Er det like enkelt for operatorer? public RuntimeValue evalnotequal(runtimevalue v, AspSyntax where) { runtimeerror("!= undefined for "+typename()+"!", where); return null; // Required by the compiler! public RuntimeValue evalpositive(aspsyntax where) { runtimeerror("unary + undefined for "+typename()+"!", where); return null; // Required by the compiler! public RuntimeValue evalsubtract(runtimevalue v, AspSyntax where) { runtimeerror(" - undefined for "+typename()+"!", where); return null; // Required by the compiler! // General: public static void runtimeerror(string message, int lnum) { Main.error("Asp runtime error on line " + lnum + ": " + message); public static void runtimeerror(string message, AspSyntax where) { runtimeerror(message, where.linenum);
Er det like enkelt for operatorer? Da kan de datatypene som tillater operatoren not, lage den riktige implementasjonen for seg: public class RuntimeBoolValue extends RuntimeValue { boolean boolvalue; @Override public RuntimeValue evalnot(aspsyntax where) { return new RuntimeBoolValue(! boolvalue);
Hva om det er flere operander? Flere operander Om det er mer enn én operand, er det opp til eval-metoden å sjekke disse. term term opr factor class AspTerm extends AspSyntax { ArrayList<AspFactor> factors = new ArrayList<>(); ArrayList<AspTermOpr> oprs = new ArrayList<>(); @Override RuntimeValue eval(runtimescope curscope) throws RuntimeReturnValue { RuntimeValue v = factors.get(0).eval(curscope); for (int i = 1; i < factors.size(); ++i) { TokenKind k = oprs.get(i-1).kind; switch (k) { case minustoken: v = v.evalsubtract(factors.get(i).eval(curscope), this); case plustoken: v = v.evaladd(factors.get(i).eval(curscope), this); default: Main.panic("Illegal term operator: " + k + "!"); return v; break; break;
Hva om det er flere operander? public class RuntimeIntValue extends RuntimeValue { long intvalue; public RuntimeIntValue(long v) { intvalue = v; @Override public RuntimeValue evaladd(runtimevalue v, AspSyntax where) { if (v instanceof RuntimeIntValue) { return new RuntimeIntValue(intValue + v.getintvalue("+ operand",where)); else if (v instanceof RuntimeFloatValue) { return new RuntimeFloatValue(intValue + v.getfloatvalue("+ operand",where)); runtimeerror("type error for +.", where); return null; // Required by the compiler.
Noen nyttige hjelperutiner Hjelperutiner For å kunne jobbe med verdier (heltall, flyt-tall, tekst), er det nyttig med rutiner som gir oss Java-verdien. Når dette gjøres med virtuelle metoder, gir det samtidig automatisk feilsjekking. public abstract class RuntimeValue { public long getintvalue(string what, AspSyntax where) { runtimeerror("type error: "+what+" is not an integer!", where); return 0; // Required by the compiler! public class RuntimeIntValue extends RuntimeValue { long intvalue; @Override public long getintvalue(string what, AspSyntax where) { return intvalue; @Override public double getfloatvalue(string what, AspSyntax where) { return (double)intvalue;
Noen nyttige hjelperutiner De datatypene som kan brukes som en gitt verdi, må implementere en metode som gir riktig verdi; de øvrige beholder standardmetoden (som gir feilmelding). Disse metodene kan også konvertere verdier; for eksempel vil både runtimeintvalue og runtimefloatvalue ha sin egen getfloatvalue. Spesielt er det viktig for den boolske typen siden alle datatypene er lovlige boolske verdier; se tabell 2.2 i kompendiet.
Noen nyttige hjelperutiner Type False True bool False True dict { ikke-tomme ordbøker float 0.0 alle andre verdier int 0 alle andre verdier list [] ikke-tomme lister none None str "" alle andre tekststrenger
En oppsummering Et eksempel Anta at vi har uttrykket 1 + 2.5. Da blir følgende funksjoner kalt i AspTerm.eval: parser.aspterm.eval: v = «literal:1».eval() parser.aspterm.eval: xxx = «literal:2.5».eval() parser.aspterm.eval: v = v.evaladd(xxx) runtime.runtimeintvalue.evaladd(v): v.getfloatvalue() runtime.runtimefloatvalue.getfloatvalue(): Retur: 2.5 Retur: 3.5 Retur: 3.5
En oppsummering Hvilke operatorer skal jeg implementere? Tabell 2.3 på side 28 i kompendiet viser hvilke operasjoner vi skal implementere: Resultat Kommentar + float float Resultatet er v 1 + int int Resultatet er v 1 - float float - int int float + float float float + int float int + float float int + int int str + str str Tekststrengene skjøtes...
Uvante operatorer Uvante operatorer Et par operatorer oppfører seg litt anderledes enn vi er vant til fra Java: <, ==,... I Asp er det lov å skrive slikt som 1 <= a == b < 10 og dette tolkes som om det i Java hadde stått (1 <= a) && (a == b) && (b < 10) (men hvert ledd skal bare beregnes én gang).
Uvante operatorer and a and b and c and d tolkes som 1 Hvis a er usann, er svaret a. 2 Hvis b er usann, er svaret b. 3 Hvis c er usann, er svaret c. 4 Ellers er svaret d. Alternativ forklaring: Finn første ledd som er usant. Eksempel 1 and "x" and [] and True ==> []
Uvante operatorer or a or b or c or d tolkes som 1 Hvis a er sann, er svaret a. 2 Hvis b er sann, er svaret b. 3 Hvis c er sann, er svaret c. 4 Ellers er svaret d. Alternativ forklaring: Finn første ledd som er sant. Eksempel 1 or "x" or [] or True ==> 1
Vi må legge igjen spor Sporing For å sjekke at utregningene blir riktige, skal interpreten skrive ut såkalt sporingsinformasjon («tracing»): 2 + 5 * 7 ==> Trace line 4: 37 (3-1) * 3 ==> Trace line 5: 6 Dette er allerede implementert i metoden Main.doTestExpr: RuntimeValue res = e.eval(emptyscope); log.traceeval(res.showinfo(), e);
Vi må legge igjen spor Det eneste vi trenger, er metoden RuntimeValue.showInfo. I utgangspunktet gir den samme resultat som tostring: public abstract class RuntimeValue { public String showinfo() { return tostring(); men i RuntimeStringValue er den redefinert til å ta med anførselstegn: public class RuntimeStringValue extends RuntimeValue { String strvalue; @Override public String showinfo() { if (strvalue.indexof( \ ) >= 0) return " + strvalue + " ; else return " " + strvalue + " ";
Hva skal gjøres i del 3? Prosjektet del 3 Dere skal 1 implementere evaluering av uttrykk som ikke inneholder navn (dvs uttrykk uten variabler og funksjoner). 2 skrive ut sporingsinformasjon noenlunde likt det referanseinterpreten gjør. Til hjelp finnes ~inf2100/oblig/obligatorisk-del3/expressions.asp inneholder noen enkle uttrykk som er et minimum for å få godkjent. ~inf2100/oblig/feil/del-3-uttrykk inneholder diverse andre uttrykk som er feil; interpreten bør også kunne håndtere disse på riktig måte.