Bygg en tolk i Java-implementera exekveringsmotorn

för de av er som bara går med oss, i min ”Java in Depth” – kolumn under de senaste månaderna, har jag diskuterat hur man kan gå om att bygga en tolk i Java. I den första tolkkolumnen täckte vi några av de önskvärda egenskaperna hos en tolk; i den andra kolumnen diskuterade vi både tolkning och layout av ett klasspaket för att implementera tolken. I den här kolumnen tittar vi på att köra tolken och de stödklasser som krävs för att uppnå det. Slutligen ska jag avsluta serien här med en diskussion om hur en tolk kan anslutas till andra Java-klasser, vilket förbättrar deras förmågor.

granska relevanta bitar

Låt mig börja med att skissa på vad vi hittills har täckt och påpeka de delar av designen som blir viktigare när vi diskuterar exekveringsläge. För en mer detaljerad beskrivning av dessa klasser, se mina tidigare kolumner eller källkodslänkarna som finns i avsnittet

resurser

nedan.

det finns tre grundklasser i implementeringen av tolken, Program, Statement och Expression. Följande visar hur de tre är relaterade:

Program

denna Program – klass klistrar samman parserns parsnings-och exekveringskomponenter. Denna klass definierar två huvudmetoder, load och run. Metoden load läser uttalanden från en inmatningsström och tolkar dem i en samling uttalanden, metoden run upprepar över samlingen och kör var och en av uttalandena. Klassen Program ger också en samling variabler för programmet att använda samt en stapel för lagring av data.

uttalande

Statement klassen innehåller ett enda tolkat uttalande. Denna klass är faktiskt underklassad till en viss typ av uttalande (PRINT, GOTO, IF och så vidare) men alla uttalanden innehåller metoden execute som kallas för att utföra uttalandet i samband med en Program klassinstans.

uttryck

klassen Expression innehåller tolkningsträdet för ett uttryck. Under körning används value – metoden för att utvärdera uttrycket och returnera dess värde. Liksom Statement är klassen Expression främst avsedd att underklassas av specifika typer av uttryck.

alla dessa klasser arbetar tillsammans för att ligga till grund för en tolk. Klassen Program inkapslar samtidigt analysoperationen och exekveringsoperationen, medan klasserna Statement och Expression inkapslar de faktiska beräkningskoncepten för det språk vi har implementerat. För dessa tre artiklar om byggnadstolkar har exempelspråket varit grundläggande.

faciliteter för beräkning

det finns två körbara klasser i tolken,

Statement

och

Expression

. Låt oss först titta på

Expression

instanserna av Expression skapas med metoden expressioni klassen ParseExpression. Klassen ParseExpression implementerar uttrycksparsern i denna tolk. Denna klass är en peer av klassen ParseStatement, som använder metoden statement för att tolka grundläggande uttalanden. Instanser av Expression har en intern type som identifierar vilken operator instansen representerar, och två metoder, value och stringValue, som returnerar det beräknade värdet av uttrycket. Dessutom, när en Uttrycksinstans skapas, ges den nominellt två parametrar som representerar vänster och höger sida av uttryckets operation. Visas i källform, den första delen av uttrycket är som följer:

class Expression { Expression arg1, arg2; int oper; final static int OP_ADD = 1; // Addition '+' final static int OP_SUB = 2; // Subtraction '-' final static int OP_MUL = 3; // Multiplication '*' final static int OP_DIV = 4; // Division '/' final static int OP_BNOT = 19; // Boolean negation '.NOT.' final static int OP_NEG = 20; // Unary minus

som du kan se i koden ovan finns det instansvariablerna, en operatörstyp som heter oper och två halvor av operationen i arg1 och arg2, och sedan några konstanter för att definiera de olika typerna. Det finns också två konstruktörer som används av uttrycksparsern; dessa skapar ett nytt uttryck från två uttryck. Deras källa visas nedan:

protected Expression(int op, Expression a, Expression b) throws BASICSyntaxError { arg1 = a; arg2 = b; oper = op; /* * If the operator is a boolean, both arguments must be boolean. */ if (op > OP_GE) { if ( (! (arg1 instanceof BooleanExpression)) || (! (arg2 instanceof BooleanExpression)) ) throw new BASICSyntaxError(typeError); } else { if ((arg1 instanceof BooleanExpression) || (arg2 instanceof BooleanExpression)) throw new BASICSyntaxError(typeError); } } protected Expression(int op, Expression a) throws BASICSyntaxError { arg2 = a; oper = op; if ((oper == OP_BNOT) && (! (arg2 instanceof BooleanExpression))) throw new BASICSyntaxError(typeError); }

den första konstruktören bygger ett godtyckligt uttrycksobjekt, och den andra bygger ett ”unärt” uttrycksobjekt-till exempel unärt minus. En sak att notera är att om det bara finns ett argument används arg2 för att lagra dess värde.

den metod som används mest i klassen Expression är value, som definieras enligt följande:

 double value(Program pgm) throws BASICRuntimeError { switch (oper) { case OP_ADD : return arg1.value(pgm) + arg2.value(pgm); case OP_SUB : return arg1.value(pgm) - arg2.value(pgm); ... etc for all of the other operator types. ...

du kan se att varje uttrycksobjekt representerar en tupel bestående av en operator och ett eller två argument. Den roliga delen av att utforma expression execution engine på detta sätt är att när du konstruerar denna uppsättning expression tuples baserat på Expression – objektet kan du beräkna värdet av uttrycket genom att helt enkelt åberopa value – metoden. Metoden value åberopar rekursivt metoden value för de två argumenten som komponerar detta uttryck, tillämpar operationen på dem och returnerar resultatet. Denna design användes så att uttryck skulle vara lätta att förstå.

för att hålla klasstrukturen ren är alla beräkningsenheter-från konstanter till trigonometriska funktioner-underklasser av Expression. Den här tanken, som skamlöst stulits från Lisp, inkapslar fullständigt begreppet att ”orsaka” en utvärdering, från det faktiska genomförandet av ”hur” den utvärderingen sker. För att visa hur denna princip tillämpas behöver vi bara titta på några av de specialiserade underklasserna Expression.

konstanter i min version av BASIC, som jag heter COCOA representeras av klassen ConstantExpression, vilka underklasser Expression och helt enkelt lagrar det numeriska värdet i ett medlemsvärde. Källkoden till ConstantExpression visas konceptuellt nedan. Jag säger ”konceptuellt” eftersom jag valde att bunta vad som skulle ha varit StringConstantExpression och NumericConstantExpression i en enda klass. Så den verkliga klassen innehåller en konstruktör för att skapa en konstant med ett strängargument och för att returnera dess värde som en sträng. Följande kod visar hur klassen ConstantExpression hanterar numeriska konstanter.

class ConstantExpression extends Expression { private double v; ConstantExpression(double a) { super(); v = a; } double value(Program pgm) throws BASICRuntimeError { return v; }}

koden som visas ovan ersätter de mer komplicerade konstruktörerna av Expression med en enkel lagring av en instansvariabel; metoden value ersätts helt enkelt med en retur av det lagrade värdet.

det är sant att du kan koda Expression – klassen för att acceptera i sina konstruktörer en konstant som skulle spara dig en klass. En fördel med att designa Expression som jag har är dock att koden i Expression förblir maximalt Generisk. Jag tycker att denna typ av kodning hjälper mig att eliminera komplexiteten i speciella fall och därmed när jag är ”klar” med Expression – koden kan jag gå vidare till andra aspekter av uttryck utan att återkomma basklassen om och om igen. Fördelen blir tydligare när vi dyker in i en annan underklass av Expression med namnet FunctionExpression.

i klassen FunctionExpression fanns det två designkrav som jag kände borde uppfyllas för att hålla tolken flexibel. Den första var att implementera de grundläggande standardfunktionerna; den andra var att inkapsla parsningen av funktionsargumenten i samma klass som implementerade dessa funktioner. Det andra kravet, parsing, motiverades av en önskan att göra denna grundläggande utbyggbar genom att skapa ytterligare funktionsbibliotek som kunde skickas till parsern som underklasser av FunctionExpression. Vidare kan de godkända klasserna användas av parsern för att öka antalet funktioner som är tillgängliga för användarens program.

FunctionExpression klassen är bara måttligt mer komplicerad än en ConstantExpression och visas i kondenserad form nedan:

 1 class FunctionExpression extends Expression { 2 3 double value(Program p) throws BASICRuntimeError { 4 try { 5 switch (oper) { 6 case RND : 7 if (r == null) 8 r = p.getRandom(); 9 return (r.nextDouble() * arg2.value(p));10 case INT :11 return Math.floor(arg2.value(p));12 case SIN :13 return Math.sin(arg2.value(p));14 15 default :16 throw new BASICRuntimeError("Unknown or non-numeric function.");17 }18 } catch (Exception e) {19 if (e instanceof BASICRuntimeError)20 throw (BASICRuntimeError) e;21 else22 throw new BASICRuntimeError("Arithmetic Exception.");23 }24 }

ovanstående källa visar hur metoden value implementeras. Oper-variabeln återanvänds för att hålla funktionsidentiteten, och Uttrycksobjekten som refereras av arg1 och arg2 används som argument för själva funktionerna. Slutligen finns det ett stort switch-uttalande som skickar begäran. En intressant aspekt är att value – metoden fångar de potentiella aritmetiska undantagen och omvandlar dem till instanser av BASICRuntimeError. Tolkningskoden i FunctionExpression visas nedan, kondenseras igen för att spara utrymme. (Kom ihåg att all källkod är tillgänglig med länkar i avsnittet Resurser.)

 1 static FunctionExpression parse(int ty, LexicalTokenizer lt) throws BASICSyntaxError { 2 FunctionExpression result; 3 Expression a; 4 Expression b; 5 Expression se; 6 Token t; 7 8 t = lt.nextToken(); 9 if (! t.isSymbol('(')) {10 if (ty == RND) {11 lt.unGetToken();12 return new FunctionExpression(ty, new ConstantExpression(1));13 } else if (ty == FRE) {14 lt.unGetToken();15 return new FunctionExpression(ty, new ConstantExpression(0));16 }17 throw new BASICSyntaxError("Missing argument for function.");18 }19 switch (ty) {20 case RND:21 case INT:22 case SIN:23 case COS:24 case TAN:25 case ATN:26 case SQR:27 case ABS:28 case CHR:29 case VAL:30 case STR:31 case SPC:32 case TAB:33 case LOG:34 a = ParseExpression.expression(lt);35 if (a instanceof BooleanExpression) {36 throw new BASICSyntaxError(functions.toUpperCase()+" function cannot accept boolean expression.");37 }38 if ((ty == VAL) && (! a.isString()))39 throw new BASICSyntaxError(functions.toUpperCase()+" requires a string valued argument.");40 result = new FunctionExpression(ty, a);41 break; 42 default:43 throw new BASICSyntaxError("Unknown function on input.");4445 }46 t = lt.nextToken();47 if (! t.isSymbol(')')) {48 throw new BASICSyntaxError("Missing closing parenthesis for function.");49 }50 return result;51 }

Observera att denna kod utnyttjar det faktum att uttrycksparsern i ParseStatement redan har räknat ut att den tittar på ett uttryck och har passerat identiteten på uttrycket i som parameter ty. Denna parser behöver då bara lokalisera den inledande parentesen och den avslutande parentesen som innehåller argumentet / argumenten. Men titta noga: i raderna # 9 till #18 tillåter parsern att vissa funktioner inte har några argument (i detta fall RND och FRE). Detta visar den flexibilitet som ges genom att ha funktionen subparser inbyggd i denna klass, snarare än att tvinga alla funktioner att överensstämma med någon fördefinierad mall. Med tanke på en funktionstyp i parametern ty väljer switch-uttalandet en gren som kan tolka de argument som krävs för den funktionen, vare sig de är strängar, siffror, andra uttryck och så vidare.

andra aspekter: strängar och matriser

två andra delar av grundspråket implementeras av COCOA-tolken: strängar och matriser. Låt oss titta på genomförandet av strängar först.

för att implementera strängar som variabler modifierades klassen Expression för att inkludera begreppet ”sträng” – uttryck. Denna modifiering har formen av två tillägg: isString och stringValue. Källan för dessa två nya metoder visas nedan.

 String stringValue(Program pgm) throws BASICRuntimeError { throw new BASICRuntimeError("No String representation for this."); } boolean isString() { return false; }

det är uppenbart att det inte är för användbart för ett grundläggande program för att få strängvärdet för ett basuttryck (vilket alltid är antingen numeriskt eller booleskt uttryck). Du kan dra slutsatsen från bristen på nytta att dessa metoder då inte hörde hemma i Expression och tillhörde i en underklass av Expression istället. Men genom att sätta dessa två metoder i basklassen kan alla Expression objekt testas för att se om de faktiskt är strängar.

en annan designmetod är att returnera de numeriska värdena som strängar med ett StringBuffer – objekt för att generera ett värde. Så, till exempel, kan samma kod skrivas om som:

 String stringValue(Program pgm) throws BASICRuntimeError { StringBuffer sb = new StringBuffer(); sb.append(this.value(pgm)); return sb.toString(); }

och om koden ovan används kan du eliminera användningen av isString eftersom varje uttryck kan returnera ett strängvärde. Vidare kan du ändra value – metoden för att försöka returnera ett tal om uttrycket utvärderas till en sträng genom att köra det genom valueOf – metoden för java.lang.Double. På många språk som Perl, TCL och REXX används denna typ av amorf typning till stor fördel. Båda metoderna är giltiga, och du bör göra ditt val baserat på din tolks design. I BASIC måste tolken returnera ett fel när en sträng tilldelas en numerisk variabel, så jag valde det första tillvägagångssättet (returnerar ett fel).

när det gäller matriser finns det olika sätt på vilka du kan designa ditt språk för att tolka dem. C använder hakparenteserna runt matriselement för att skilja matrisens indexreferenser från funktionsreferenser som har parenteser runt sina argument. SPRÅKDESIGNERNA för BASIC valde dock att använda parenteser för både funktioner och matriser, så när texten NAME(V1, V2) ses av tolkaren kan det vara antingen ett funktionsanrop eller en arrayreferens.

den lexikala analysatorn diskriminerar mellan tokens som följs av parenteser genom att först anta att de är funktioner och testning för det. Sedan fortsätter det att se om de är nyckelord eller variabler. Det är detta beslut som hindrar ditt program från att definiera en variabel som heter ” SIN.”Varje variabel vars namn matchade ett funktionsnamn skulle returneras av lexical analyzer som en funktionstoken istället. Det andra tricket som den lexikala analysatorn använder är att kontrollera om variabelnamnet omedelbart följs av `(’. Om det är, antar analysatorn att det är en matrisreferens. Genom att analysera detta i den lexikala analysatorn eliminerar vi strängen ’ MYARRAY ( 2 )’ från att tolkas som en giltig array (notera utrymmet mellan variabelnamnet och den öppna parentesen).

det sista tricket för att implementera arrayer är i klassen Variable. Denna klass används för en instans av en variabel, och som jag diskuterade i förra månadens kolumn är det en underklass av Token. Men det har också några maskiner för att stödja arrays och det är vad jag ska visa nedan:

class Variable extends Token { // Legal variable sub types final static int NUMBER = 0; final static int STRING = 1; final static int NUMBER_ARRAY = 2; final static int STRING_ARRAY = 4; String name; int subType; /* * If the variable is in the symbol table these values are * initialized. */ int ndx; // array indices. int mult; // array multipliers double nArrayValues; String sArrayValues;

ovanstående kod visar instansvariablerna associerade med en variabel, som i klassen ConstantExpression. Man måste göra ett val om antalet klasser som ska användas kontra komplexiteten i en klass. Ett designval kan vara att bygga en Variable – klass som bara innehåller skalära variabler och sedan lägga till en ArrayVariable – underklass för att hantera komplexa matriser. Jag valde att kombinera dem, vrida skalära variabler i huvudsak i matriser av Längd 1.

om du läser ovanstående kod ser du matrisindex och multiplikatorer. Dessa är här eftersom flerdimensionella arrayer i BASIC implementeras med en enda linjär Java-array. Det linjära indexet i Java-arrayen beräknas manuellt med hjälp av elementen i multiplikatormatrisen. Indexen som används i grundprogrammet kontrolleras för giltighet genom att jämföra dem med det maximala juridiska indexet i indexens ndx-array.

till exempel skulle en grundläggande array med tre dimensioner av 10, 10 och 8 ha värdena 10, 10 och 8 lagrade i ndx. Detta gör det möjligt för expression evaluator att testa för ett ”index out of bounds” – tillstånd genom att jämföra numret som används i grundprogrammet med det maximala juridiska numret som nu lagras i ndx. Multiplikatormatrisen i vårt exempel skulle innehålla värdena 1, 10 och 100. Dessa konstanter representerar siffrorna man använder för att kartlägga från en flerdimensionell array index specifikation till en linjär array index specifikation. Den faktiska ekvationen är:

Java Index = Index1 + Index2 * Max storlek på Index1 + Index3 * (Max storlek på Index1 * MaxSizeIndex 2)

nästa Java-array i klassen Variable visas nedan.

 Expression expns;

expns-arrayen används för att hantera arrayer som är skrivna som ” A(10*B, i).”I så fall är indexen faktiskt uttryck snarare än konstanter, så referensen måste innehålla pekare till de uttryck som utvärderas vid körning. Slutligen finns det denna ganska fula utseende kod som beräknar indexet beroende på vad som passerade i programmet. Denna privata metod visas nedan.

 private int computeIndex(int ii) throws BASICRuntimeError { int offset = 0; if ((ndx == null) || (ii.length != ndx.length)) throw new BASICRuntimeError("Wrong number of indices."); for (int i = 0; i < ndx.length; i++) { if ((ii < 1) || (ii > ndx)) throw new BASICRuntimeError("Index out of range."); offset = offset + (ii-1) * mult; } return offset; }

om du tittar på koden ovan noterar du att koden först kontrollerar att det korrekta antalet index användes när man refererar till arrayen och sedan att varje index var inom det lagliga intervallet för det indexet. Om ett fel upptäcks kastas ett undantag till tolken. Metoderna numValue och stringValue returnerar ett värde från variabeln som ett tal respektive en sträng. Dessa två metoder visas nedan.

 double numValue(int ii) throws BASICRuntimeError { return nArrayValues; } String stringValue(int ii) throws BASICRuntimeError { if (subType == NUMBER_ARRAY) return ""+nArrayValues; return sArrayValues; }

det finns ytterligare metoder för att ställa in värdet på en variabel som inte visas här.

genom att dölja mycket av komplexiteten i hur varje bit implementeras, när det äntligen är dags att utföra grundprogrammet, är Java-koden ganska enkel.

kör koden

koden för att tolka de grundläggande uttalandena och köra dem finns i

run

metod för

Program

klass. Koden för den här metoden visas nedan, och jag går igenom den för att påpeka de intressanta delarna.

 1 public void run(InputStream in, OutputStream out) throws BASICRuntimeError { 2 PrintStream pout; 3 Enumeration e = stmts.elements(); 4 stmtStack = new Stack(); // assume no stacked statements ... 5 dataStore = new Vector(); // ... and no data to be read. 6 dataPtr = 0; 7 Statement s; 8 9 vars = new RedBlackTree();1011 // if the program isn't yet valid.12 if (! e.hasMoreElements())13 return;1415 if (out instanceof PrintStream) {16 pout = (PrintStream) out;17 } else {18 pout = new PrintStream(out);19 }

ovanstående kod visar att metoden run tar en InputStream och en OutputStream för användning som ”konsol” för det exekverande programmet. I rad 3 är uppräkningsobjektet e inställt på uppsättningen uttalanden från samlingen som heter stmts. För denna samling använde jag en variant på ett binärt sökträd som kallas ett ”Röd-svart” träd. (För ytterligare information om binära sökträd, se min tidigare kolumn om att bygga generiska samlingar.) Därefter skapas ytterligare två samlingar – en med en Stackoch en med en Vector. Stacken används som stacken i vilken dator som helst, men vektorn används uttryckligen för datauttalanden i grundprogrammet. Den slutliga samlingen är ett annat rött-svart träd som innehåller referenserna för de variabler som definieras av grundprogrammet. Detta träd är symboltabellen som används av programmet medan det körs.

efter initialiseringen ställs in ingångs-och utgångsströmmarna, och om e inte är null börjar vi med att samla in data som har deklarerats. Det görs som visas i följande kod.

 /* First we load all of the data statements */ while (e.hasMoreElements()) { s = (Statement) e.nextElement(); if (s.keyword == Statement.DATA) { s.execute(this, in, pout); } }

ovanstående slinga tittar helt enkelt på alla uttalanden, och alla datauttalanden som den finner utförs sedan. Exekveringen av varje datauttalande infogar de värden som deklareras av det uttalandet i datalagervektorn. Därefter kör vi programmet korrekt, vilket görs med hjälp av nästa kodkod:

 e = stmts.elements(); s = (Statement) e.nextElement(); do { int yyy; /* While running we skip Data statements. */ try { yyy = in.available(); } catch (IOException ez) { yyy = 0; } if (yyy != 0) { pout.println("Stopped at :"+s); push(s); break; } if (s.keyword != Statement.DATA) { if (traceState) { s.trace(this, (traceFile != null) ? traceFile : pout); } s = s.execute(this, in, pout); } else s = nextStatement(s); } while (s != null); }

som du kan se i koden ovan, det första steget är att initiera e. nästa steg är att hämta det första uttalandet i variabeln s och sedan gå in i körningsslingan. Det finns en viss kod för att kontrollera för väntande inmatning på ingångsströmmen för att tillåta att programmets framsteg avbryts genom att skriva på programmet, och sedan kontrollerar slingan för att se om uttalandet att utföra skulle vara ett datauttalande. Om det är, hoppar slingan över uttalandet som det redan utfördes. Den ganska invecklade tekniken för att utföra alla datauttalanden först krävs eftersom BASIC tillåter DATAUTTALANDEN som uppfyller ett LÄSAT uttalande att visas var som helst i källkoden. Slutligen, om spårning är aktiverad, skrivs en spårningspost ut och det mycket oinpressiva uttalandet s = s.execute(this, in, pout); åberopas. Skönheten är att all ansträngning att inkapsla baskoncepten i lättförståeliga klasser gör den slutliga koden trivial. Om det inte är trivialt så kanske du har en aning om att det kan finnas ett annat sätt att dela din design.

inslagning och ytterligare tankar

tolken var utformad så att den kunde köras som en tråd, så det kan finnas flera COCOA-tolktrådar som körs samtidigt i ditt programutrymme samtidigt. Vidare kan vi med hjälp av funktionsutvidgning tillhandahålla ett sätt varigenom dessa trådar kan interagera med varandra. Det fanns ett program för Apple II och senare för PC och Unix som heter C-robots som var ett system för att interagera ”Robot” enheter som programmerades med ett enkelt grundläggande derivatspråk. Spelet gav mig och andra många timmars underhållning men var också ett utmärkt sätt att introducera de grundläggande principerna för beräkning till yngre studenter (som felaktigt trodde att de bara spelade och inte lärde sig). Java-baserade tolkundersystem är mycket kraftfullare än sina pre-Java-motsvarigheter eftersom de är direkt tillgängliga på alla Java-plattformar. COCOA sprang på Unix-system och Macintoshes samma dag som jag arbetade på en Windows 95-baserad dator. Medan Java blir slagen av inkompatibiliteter i tråd-eller fönsterverktygssatsimplementeringarna, är det som ofta förbises detta: mycket kod ”fungerar bara.”

Chuck McManis är för närvarande chef försystemprogramvara på FreeGate Corp., en venturefinansierad start som utforskar möjligheter på internetmarknaden. Innan han gick med i FreeGate var Chuck medlem i Java-gruppen. Han gick med i Java-gruppen strax efter bildandet av FirstPerson Inc. och varen medlem i portable OS-gruppen (den grupp som ansvarar för Osportionen av Java). Senare, när FirstPerson upplöstes, stannade hanmed gruppen genom utvecklingen av alfa och betaversioner av Java-plattformen. Han skapade den första ”all Java” – hemsidan på Internet när han gjorde programmeringen för Javaversion Of The Sun-hemsidan i maj 1995. Han utvecklade också akryptografiskt bibliotek för Java och versioner av Java classloader som kunde skärmklasser baserat på digitala signaturer.Innan han gick med i FirstPerson arbetade Chuck i operativsystemenområde av SunSoft, utveckla nätverksapplikationer, där han gjordeden ursprungliga utformningen av NIS+. Också, kolla in hans hemsida. : END_BIO

Läs mer om detta ämne

  • här är länkar till källfilerna som refereras ovan:
    • ConstantExpression.java
    • FunctionExpression.java
    • Program.java
    • uttalande.java
    • Stränguttryck.java
    • variabel.java
    • VariableExpression.java
  • och här är en .ZIP-fil för källfilerna:
    indepth.zip
  • ”Mindre vanliga Lisp” – en Lisp tolk skriven i Java
    http://user03.blue.aol.com/thingtone/workshop/lisp.htm
  • Mike Cowlishaws NetREXX-tolk skriven i Java
    http://www2.hursley.ibm.com/netrexx/
  • en gammal kopia av UseNet BASIC FAQ (den har fortfarande användbar information i den.)
    http://whitworth.me.ic.ac.uk/people/students/djbur/qbasic.htm
  • kakao, en grundläggande tolk skriven i Java
    http://www.mcmanis.com/~cmcmanis/java/javaworld/examples/BASIC.html
  • Chucks Java resources-sida
    http://www.mcmanis.com/~cmcmanis/java/javaworld/
  • Tcl tolk skriven i Java
    http://www.cs.cornell.edu/home/ioi/Jacl/
  • :
  • ”hur man bygger en tolk i Java, del 2strukturen”
    tricket att montera grundklasserna för en enkel tolk.
  • ”hur man bygger en tolk i Java, del 1grunderna”
    för komplexa applikationer som kräver ett skriptspråk kan Java användas för att implementera tolken och lägga till skriptförmåga i alla Java-appar.
  • ”Lexical analysis, Part 2build an application”
    hur man använder StreamTokenizer-objektet för att implementera en interaktiv kalkylator.
  • ”Lexical analysis and JavaPart 1”
    lär dig hur du konverterar läsbar text till maskinläsbar data med hjälp av klasserna StringTokenizer och StreamTokenizer.
  • ”kodåteranvändning och objektorienterade system”
    använd en hjälparklass för att genomdriva dynamiskt beteende.
  • ”behållarstöd för objekt i Java 1.0.2”
    det är enkelt att organisera objekt när du lägger dem i behållare. Den här artikeln går igenom design och implementering av en behållare.
  • ”grunderna i Java class loaders”
    grunderna i denna nyckelkomponent i Java-arkitekturen.
  • ”inte använder sophämtning”
    minimera heap stryk i dina Java-program.
  • ”trådar och applets och visuella kontroller”
    den här sista delen av serien utforskar läsning av flera datakanaler.
  • ”använda kommunikationskanaler i applets, del 3”
    utveckla Visual Basic-stil tekniker för att applet design-och konvertera temperaturer i processen.
  • ”synkronisera trådar i Java, del II”
    lär dig hur du skriver en datakanalklass och skapa sedan ett enkelt exempelapplikation som illustrerar en verklig implementering av klassen.
  • ”synkronisera trådar i Java”
    tidigare Java-teamutvecklare Chuck McManis går igenom ett enkelt exempel som illustrerar hur man synkroniserar trådar för att säkerställa tillförlitligt och förutsägbart appletbeteende.

Lämna ett svar

Din e-postadress kommer inte publiceras.