Bouw een interpreter in Java — implementeer de execution engine

voor degenen onder u die net bij ons komen, in mijn kolom” Java In Depth ” de afgelopen maanden, heb ik besproken hoe men zou kunnen gaan over het bouwen van een interpreter in Java. In de eerste kolom interpreter bespraken we enkele van de gewenste attributen van een interpreter; in de tweede kolom bespraken we zowel parsing als de lay-out van een klasse pakket voor het implementeren van de interpreter. In deze kolom zullen we kijken naar het uitvoeren van de interpreter, en de ondersteuning klassen die nodig zijn om dat te bereiken. Tot slot zal ik de serie hier afsluiten met een discussie over hoe een tolk kan worden aangesloten op andere Java-klassen, waardoor hun capaciteiten worden verbeterd.

het bekijken van de relevante bits

laat me beginnen met schetsen wat we tot nu toe hebben behandeld en wijzen op die delen van het ontwerp die belangrijker zullen worden als we de uitvoeringsmodus bespreken. Voor een meer gedetailleerde beschrijving van deze klassen, refereer je naar mijn vorige kolommen of naar de broncode links die in de

Resources sectie

hieronder staan.

er zijn drie basisklassen in de implementatie van de interpreter, Program, Statement, en Expression. Het volgende laat zien hoe de drie gerelateerd zijn:

programma

deze Program klasse lijmt de parsing-en uitvoercomponenten van de parser samen. Deze klasse definieert twee hoofdmethoden, load en run. De load methode leest statements van een invoerstroom en ontleedt ze in een verzameling statements, de run methode herhaalt de verzameling en voert elk statement uit. De Program klasse biedt ook een verzameling variabelen voor het programma om te gebruiken, evenals een stack voor het opslaan van gegevens.

Statement

de Statement klasse bevat één ontleedverklaring. Deze klasse is eigenlijk gesubclasseerd in een specifiek type statement (PRINT, GOTO, IF, enzovoort) maar alle statements bevatten de methode execute die wordt aangeroepen om de statement uit te voeren in de context van een Program klasse instantie.

expressie

de klasse Expression bevat de parse tree van een expressie. Tijdens het uitvoeren wordt de value methode gebruikt om de expressie te evalueren en de waarde ervan terug te geven. Net als Statement is de klasse Expression voornamelijk ontworpen om te worden gesubclasseerd door specifieke expressietypen.

al deze klassen werken samen om de basis te vormen van een interpreter. De klasse Program omvat tegelijkertijd de parsing-en uitvoeroperatie, terwijl de klassen Statement en Expression de werkelijke computationele concepten van de taal die we hebben geïmplementeerd inkapselen. Voor deze drie artikelen over het bouwen van tolken is de voorbeeldtaal BASIC geweest.

faciliteiten voor berekening

er zijn twee uitvoerbare klassen in de interpreter,

Statement

en

Expression

. Laten we eerst eens kijken naar

Expression

de instanties van Expression worden gemaakt met de methode expression in de klasse ParseExpression. De klasse ParseExpression implementeert de expressieparser in deze interpreter. Deze klasse is een peer van de ParseStatement klasse, die de statement methode gebruikt om basis statements te ontleden. Instanties van Expression hebben een interne type die identificeert welke operator de instantie vertegenwoordigt, en twee methoden, value en stringValue, die de berekende waarde van de expressie retourneren. Bovendien, wanneer een expressie instantie wordt gemaakt, wordt het nominaal gegeven twee parameters die de linker en rechter kant van de operatie van de expressie. Getoond in bronvorm, het eerste deel van de uitdrukking is als volgt:

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

zoals u kunt zien in de code hierboven, zijn er de instance variabelen, een operator type genaamd oper, en twee helften van de operatie in arg1 en arg2, en vervolgens enkele constanten om de verschillende types te definiëren. Ook zijn er twee constructors die worden gebruikt door de uitdrukking parser; deze creëren een nieuwe expressie van twee expressies. Hun bron wordt hieronder getoond:

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); }

de eerste constructor bouwt een willekeurig expressieobject, en de tweede bouwt een “Unair” expressieobject — zoals Unair minus. Een ding om op te merken is dat als er maar één argument is, arg2 wordt gebruikt om zijn waarde op te slaan.

de methode die het vaakst wordt gebruikt in de klasse Expression is value, die als volgt wordt gedefinieerd:

 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. ...

je kunt zien dat elk expressieobject een tupel vertegenwoordigt die bestaat uit een operator en een of twee argumenten. Het leuke van het op deze manier ontwerpen van de expression execution engine is dat wanneer je deze set van expression tupels construeert op basis van het Expression object, je de waarde van de expressie kunt berekenen door simpelweg de value methode aan te roepen. De value methode roept recursief de value methode aan van de twee argumenten die deze expressie vormen, past de operatie op hen toe en geeft het resultaat terug. Dit ontwerp werd gebruikt zodat uitdrukkingen gemakkelijk te begrijpen zou zijn.

om de klassenstructuur schoon te houden, zijn alle computationele eenheden — van constanten tot trigonometrische functies — subklassen van Expression. Dit idee, schaamteloos gestolen uit Lisp, sluit volledig het begrip “veroorzaken” van een evaluatie tot stand te brengen, van de daadwerkelijke implementatie van” hoe ” die evaluatie plaatsvindt. Om aan te tonen hoe dit principe wordt toegepast, hoeven we slechts enkele van de gespecialiseerde subklassen van Expressionte bekijken.

constanten in mijn versie van BASIC, die ik COCOA noemde, worden weergegeven door de klasse ConstantExpression, die Expression subklassen en eenvoudig de numerieke waarde opslaat in een lidwaarde. De broncode tot ConstantExpression is hieronder conceptueel weergegeven. Ik zeg “conceptueel” omdat ik ervoor heb gekozen om wat StringConstantExpression en NumericConstantExpression zou zijn gebundeld in een enkele klasse. Dus de echte klasse omvat een constructor voor het creëren van een constante met een string argument en voor het retourneren van de waarde als een string. De volgende code laat zien hoe de ConstantExpression klasse omgaat met numerieke constanten.

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

de hierboven getoonde code vervangt de complexere constructors van Expression door een eenvoudige opslag van een instantievariabele; de value methode wordt simpelweg vervangen door een return van de opgeslagen waarde.

het is waar dat u de Expression Klasse kunt coderen om in zijn constructors een constante te accepteren die u een klasse zou besparen. Een voordeel van het ontwerpen van Expression is echter dat de code in Expression maximaal generiek blijft. Ik vind dat deze stijl van codering me helpt om de complexiteit van speciale gevallen te elimineren en dus als ik “klaar” ben met de Expression code, kan ik verder gaan met andere aspecten van expressies zonder de basisklasse opnieuw en opnieuw te bezoeken. Het voordeel wordt duidelijker wanneer we ons verdiepen in een andere subklasse van Expression met de naam FunctionExpression.

in de klasse FunctionExpression waren er twee ontwerpvereisten waaraan volgens mij moest worden voldaan om de interpreter flexibel te houden. De eerste was het implementeren van de standaard basisfuncties; de andere was om de parsing van de functies argumenten in dezelfde klasse die deze functies geïmplementeerd te kapselen. De tweede vereiste, parsing, werd ingegeven door de wens om deze basis uitbreidbaar te maken door het creëren van extra functiebibliotheken die aan de parser konden worden doorgegeven als subklassen van FunctionExpression. Verder, die doorgegeven-in klassen kunnen worden gebruikt door de parser om het aantal functies beschikbaar voor het programma van de gebruiker te verhogen.

de klasse FunctionExpression is slechts matig ingewikkelder dan a ConstantExpression en wordt hieronder in verkorte vorm weergegeven:

 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 }

bovenstaande bron laat zien hoe de value methode wordt geïmplementeerd. De oper-variabele wordt hergebruikt om de functie-identiteit vast te houden, en de Expressieobjecten waarnaar arg1 en arg2 verwijzen, worden gebruikt als argumenten voor de functies zelf. Tot slot is er een grote switch statement dat de aanvraag verzendt. Een interessant aspect is dat de value methode de potentiële rekenkundige uitzonderingen vangt en ze omzet in gevallen van BASICRuntimeError. De parsingscode in FunctionExpression wordt hieronder weergegeven, opnieuw gecondenseerd om ruimte te besparen. (Vergeet niet, alle broncode is beschikbaar met behulp van links in de Resources sectie.)

 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 }

merk op dat deze code gebruik maakt van het feit dat de expression parser in ParseStatement al heeft ontdekt dat het naar een expressie kijkt en de identiteit van de expressie heeft doorgegeven als parameter ty. Deze parser hoeft dan alleen het openingshaakje en het sluithaakje te lokaliseren dat het argument(de argumenten) bevat. Maar kijk goed: in de regels # 9 tot en met # 18 staat de parser sommige functies toe om geen argumenten te hebben (in dit geval RND en FRE). Dit toont de flexibiliteit die wordt geboden door het hebben van de functie subdeel ingebouwd in deze klasse, in plaats van alle functies te dwingen om te voldoen aan een vooraf gedefinieerde template. Gegeven een functietype in de parameter ty, selecteert het switch statement een branch die de argumenten die nodig zijn voor die functie kan ontleden, of het nu strings, getallen, andere expressies zijn, enzovoort.

andere aspecten: Strings en arrays

twee andere delen van de basistaal worden door de COCOA-interpreter geïmplementeerd: strings en arrays. Laten we eerst kijken naar de implementatie van strings.

om strings als variabelen te implementeren, werd de klasse Expression gewijzigd om de notie van “string” expressies op te nemen. Deze wijziging heeft de vorm gekregen van twee toevoegingen: isString en stringValue. De bron voor deze twee nieuwe methoden wordt hieronder weergegeven.

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

het is duidelijk dat het niet zo nuttig is om een basisprogramma de string waarde van een basisuitdrukking te geven (die altijd of een numerieke of Booleaanse uitdrukking is). Uit het gebrek aan nut zou je kunnen concluderen dat deze methoden toen niet in Expression thuishoorden en in plaats daarvan in een subklasse van Expression behoorden. Echter, door deze twee methoden in de basisklasse te plaatsen, kunnen alle Expression objecten worden getest om te zien of het in feite tekenreeksen zijn.

een andere ontwerpbenadering is het retourneren van de numerieke waarden als tekenreeksen met een StringBuffer object om een waarde te genereren. Dus, bijvoorbeeld, dezelfde code kan worden herschreven als:

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

en als de bovenstaande code wordt gebruikt, kunt u het gebruik van isString elimineren omdat elke expressie een tekenreekswaarde kan retourneren. Verder kunt u de value methode wijzigen om te proberen een getal te retourneren als de expressie evalueert naar een tekenreeks door het uit te voeren via de valueOf methode van java.lang.Double. In veel talen zoals Perl, TCL en REXX wordt dit soort amorfe typen tot groot voordeel gebruikt. Beide benaderingen zijn geldig en u moet uw keuze maken op basis van het ontwerp van uw tolk. In BASIC moet de interpreter een fout retourneren wanneer een string is toegewezen aan een numerieke variabele, dus koos ik de eerste benadering (een fout retourneren).

voor arrays zijn er verschillende manieren waarop u uw taal kunt ontwerpen om ze te interpreteren. C gebruikt de vierkante haakjes rond array-elementen om de indexverwijzingen van de array te onderscheiden van functieverwijzingen die haakjes rond hun argumenten hebben. De taalontwerpers voor BASIC kozen er echter voor om haakjes te gebruiken voor zowel functies als arrays, dus als de tekst NAME(V1, V2) door de parser wordt gezien, kan het een functieaanroep of een array-referentie zijn.

de lexical analyzer maakt onderscheid tussen tokens die gevolgd worden door haakjes door eerst aan te nemen dat het functies zijn en daarvoor te testen. Dan gaat het verder om te zien of het trefwoorden of variabelen zijn. Het is deze beslissing die voorkomt dat uw programma van het definiëren van een variabele genaamd ” SIN.”Elke variabele waarvan de naam overeenkomt met een functie naam zou worden geretourneerd door de lexical analyzer als een functie token in plaats daarvan. De tweede truc die de lexical analyzer gebruikt is om te controleren of de variabele naam onmiddellijk gevolgd wordt door’ (‘. Als dat zo is, neemt de analyzer aan dat het een array referentie is. Door dit in de lexical analyzer te ontleden, wordt de string ‘MYARRAY ( 2 ) ‘ niet geïnterpreteerd als een geldige array (let op de ruimte tussen de naam van de variabele en de open haakjes).

de laatste truc om arrays te implementeren is in de klasse Variable. Deze klasse wordt gebruikt voor een instantie van een variabele, en zoals ik in de kolom van vorige maand heb besproken, is het een subklasse van Token. Echter, het heeft ook een aantal machines om arrays te ondersteunen en dat is wat ik hieronder zal laten zien:

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;

de bovenstaande code toont de instance variabelen geassocieerd met een variabele, zoals in de ConstantExpression klasse. Men moet een keuze maken over het aantal te gebruiken klassen versus de complexiteit van een klasse. Een ontwerpkeuze zou kunnen zijn om een Variable klasse te bouwen die alleen scalaire variabelen bevat en vervolgens een ArrayVariable subklasse toe te voegen om de fijne kneepjes van arrays te behandelen. Ik koos ervoor om ze te combineren, het draaien van scalaire variabelen in wezen arrays van lengte 1.

als u de bovenstaande code leest, ziet u array indices en multipliers. Deze zijn hier omdat multidimensionale arrays in BASIC worden geïmplementeerd met behulp van een enkele lineaire Java array. De lineaire index in de Java array wordt handmatig berekend met behulp van de elementen van de multiplier array. De indexen die in het basisprogramma worden gebruikt, worden op geldigheid gecontroleerd door ze te vergelijken met de maximale wettelijke index in de NDX-array van de indexen.

bijvoorbeeld, een basis array met drie dimensies van 10, 10 en 8, zou de waarden 10, 10 en 8 opgeslagen hebben in ndx. Dit staat de expression evaluator toe om een “index out of bounds” conditie te testen door het getal dat gebruikt wordt in het BASIC programma te vergelijken met het maximale legale getal dat nu opgeslagen is in ndx. De multiplier array in ons voorbeeld zou de waarden 1, 10 en 100 bevatten. Deze constanten vertegenwoordigen de getallen die men gebruikt om van een multidimensionale array index specificatie in een lineaire array index specificatie in kaart te brengen. De werkelijke vergelijking is:

Java-Index = Index1 + Index2 * maximale grootte van Index1 + Index3 * (Maxgrootte van Index1 * MaxSizeIndex 2)

de volgende Java-array in de klasse Variable wordt hieronder weergegeven.

 Expression expns;

de expns-array wordt gebruikt om arrays te behandelen die geschreven zijn als “A(10*B, i).”In dat geval zijn de indices eigenlijk expressies in plaats van constanten, dus moet de referentie verwijzingen bevatten naar die expressies die tijdens runtime worden geëvalueerd. Tot slot is er dit vrij lelijk uitziende stukje code dat de index berekent afhankelijk van wat er in het programma werd doorgegeven. Deze privémethode wordt hieronder getoond.

 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; }

kijkend naar de code hierboven, zult u merken dat de code eerst controleert of het juiste aantal indices werd gebruikt bij het verwijzen naar de array, en dan dat elke index binnen het wettelijke bereik voor die index lag. Als er een fout wordt gedetecteerd, wordt een uitzondering naar de interpreter gegooid. De methoden numValue en stringValue retourneren een waarde van de variabele als respectievelijk een getal of een string. Deze twee methoden worden hieronder weergegeven.

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

er zijn aanvullende methoden om de waarde van een variabele in te stellen die hier niet worden weergegeven.

door veel van de complexiteit van hoe elk stuk wordt geïmplementeerd te verbergen, wanneer het eindelijk tijd is om het basisprogramma uit te voeren, is de Java-code vrij eenvoudig.

het uitvoeren van de code

de code om de basiscommando ‘ s te interpreteren en uit te voeren is opgenomen in de

run

methode van de

Program

klasse. De code voor deze methode wordt hieronder getoond, en Ik zal er doorheen stappen om de interessante delen aan te wijzen.

 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 }

de bovenstaande code geeft aan dat de methode run een InputStream en een OutputStream gebruikt als de “console” voor het uitvoerende programma. In regel 3 wordt het object enumeration e ingesteld op de verzameling statements uit de collectie genaamd stmts. Voor deze collectie gebruikte ik een variant op een binaire zoekboom genaamd een “rood-zwarte” boom. (Voor meer informatie over binaire zoek bomen, Zie mijn vorige kolom over het bouwen van generieke collecties.) Daarna worden twee extra collecties gemaakt-één met een Stack en één met een Vector. De stack wordt gebruikt zoals de stack in elke computer, maar de vector wordt uitdrukkelijk gebruikt voor de data statements in het BASIC programma. De laatste verzameling is een andere rood-zwarte boom die de referenties bevat voor de variabelen die zijn gedefinieerd door het basisprogramma. Deze boom is de symbolentabel die wordt gebruikt door het programma tijdens het uitvoeren.

na de initialisatie worden de invoer-en uitvoerstromen ingesteld, en als e niet null is, beginnen we met het verzamelen van gegevens die zijn gedeclareerd. Dat wordt gedaan zoals aangegeven in de volgende code.

 /* 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); } }

de bovenstaande lus kijkt gewoon naar alle statements, en alle data statements die het Vindt worden dan uitgevoerd. De uitvoering van elk gegevens statement voegt de waarden die door dat statement in de dataStore vector. Vervolgens voeren we het programma goed uit, dat wordt gedaan met behulp van dit volgende stukje code:

 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); }

zoals je kunt zien in de code hierboven, is de eerste stap om e opnieuw te initialiseren. de volgende stap is om het eerste statement op te halen in de variabele s en dan om de execution loop in te voeren. Er is een code om te controleren of de invoer in afwachting is van de invoerstroom, zodat de voortgang van het programma onderbroken kan worden door op het programma te typen, en vervolgens controleert de lus of het uit te voeren statement een data statement is. Als dat zo is, slaat de lus het statement over zoals het al was uitgevoerd. De nogal ingewikkelde techniek om eerst alle gegevensverklaringen uit te voeren is vereist omdat BASIC de gegevensverklaringen die voldoen aan een gelezen verklaring overal in de broncode laat verschijnen. Ten slotte, als tracing is ingeschakeld, wordt een trace record afgedrukt en wordt de zeer unpressive statement s = s.execute(this, in, pout); aangeroepen. Het mooie is dat alle moeite van het inkapselen van de basisconcepten in gemakkelijk te begrijpen klassen de uiteindelijke code triviaal maakt. Als het niet triviaal dan misschien heb je een idee dat er misschien een andere manier om uw ontwerp te splitsen.

afronden en verdere gedachten

de interpreter is zo ontworpen dat het als een thread kan draaien, dus er kunnen meerdere Cocoa interpreter threads tegelijkertijd in uw programmaruimte draaien. Verder, met het gebruik van functieuitbreiding kunnen we een middel bieden waardoor die draden met elkaar kunnen interageren. Er was een programma voor de Apple II en later voor de PC en Unix genaamd C-robots dat was een systeem van interactie “robotachtige” entiteiten die waren geprogrammeerd met behulp van een eenvoudige basic afgeleide taal. Het spel gaf mij en anderen vele uren entertainment, maar was ook een uitstekende manier om de basisprincipes van berekening te introduceren aan jongere studenten (die ten onrechte geloofden dat ze gewoon speelden en niet leerden). Java gebaseerde interpreter subsystemen zijn veel krachtiger dan hun pre-Java tegenhangers, omdat ze direct beschikbaar zijn op elk Java-platform. COCOA liep op Unix-systemen en Macintoshes dezelfde dag kreeg ik werken op een Windows 95-gebaseerde PC. Terwijl Java wordt geslagen door onverenigbaarheden in de thread of window toolkit implementaties, wat vaak over het hoofd wordt gezien is dit: veel code “werkt gewoon. Chuck McManis is momenteel de directeur van systeemsoftware bij FreeGate Corp., een met risicokapitaal gefinancierde start-up die mogelijkheden op de internetmarkt verkent. Voor FreeGate was Chuck lid van de Java Groep. Hij sloot zich aan bij de Java Groep net na de oprichting van FirstPerson Inc. en waseen lid van de portable OS group (de groep die verantwoordelijk is voor de OSportion van Java). Later, toen FirstPerson werd ontbonden, bleef hij bij de groep door de ontwikkeling van de alpha en betaversions van het Java platform. Hij creëerde de eerste” all Java ” homepage op het Internet toen hij de programmering deed voor de Javaversion van de Sun homepage in mei 1995. Hij ontwikkelde ook acryptografische bibliotheek voor Java en versies van de Java classloader die klassen konden screenen op basis van digitale handtekeningen.Voordat hij bij FirstPerson kwam, werkte Chuck in het besturingssystemengebied van SunSoft, waar hij netwerkapplicaties ontwikkelde, waar hij het eerste ontwerp van NIS+deed. Kijk ook eens naar zijn homepage. : END_BIO

meer informatie over dit onderwerp

  • hier zijn links naar de bronbestanden waarnaar hierboven wordt verwezen:
    • Constantexpressie.java
    • Functieexpressie.java
    • programma.java
    • Statement.java
    • Stringexpressie.java
    • variabele.java
    • Variableexpressie.java
  • en hier is a .ZIP-bestand van de bronbestanden:
    diepgaand.zip
  • “Uncommon Lisp” — een Lisp-interpreter geschreven in Java
    http://user03.blue.aol.com/thingtone/workshop/lisp.htm
  • Mike Cowlishaw ‘ s netrexx interpreter geschreven in Java
    http://www2.hursley.ibm.com/netrexx/
  • een oude kopie van de Usenet BASIC FAQ (Er zit nog nuttige informatie in.)
    http://whitworth.me.ic.ac.uk/people/students/djbur/qbasic.htm
  • COCOA, een basisinterpreter geschreven in Java
    http://www.mcmanis.com/~cmcmanis/java/javaworld/examples/BASIC.html
  • Chuck ‘ s Java resources pagina
    http://www.mcmanis.com/~cmcmanis/java/javaworld/
  • TCL Interpreter geschreven in Java
    http://www.cs.cornell.edu/home/ioi/Jacl/
  • :
  • “How to build an interpreter in Java, Part 2The structure”
    The trick to assembling the foundation classes for a simple interpreter.
  • “How to build an interpreter in Java, Part 1 The BASICs”
    voor complexe toepassingen die een scripttaal vereisen, kan Java worden gebruikt om de interpreter te implementeren, door scripting mogelijkheden toe te voegen aan elke Java app.
  • “Lexical analysis, Part 2Build an application”
    het StreamTokenizer-object gebruiken om een interactieve rekenmachine te implementeren.
  • “Lexical analysis and JavaPart 1”
    leer hoe mens-leesbare tekst in machine-leesbare gegevens te converteren met behulp van de stringtokenizer en StreamTokenizer klassen.
  • “code reuse and object-oriented systems”
    gebruik een helperklasse om dynamisch gedrag af te dwingen.
  • “Container ondersteuning voor objecten in Java 1.0.2”
    het organiseren van objecten is eenvoudig wanneer u ze in containers plaatst. Dit artikel begeleidt u door het ontwerp en de uitvoering van een container.
  • “the basics of Java class loaders”
    de grondbeginselen van dit belangrijke onderdeel van de Java-architectuur.
  • “garbage collection niet gebruiken”
    minimaliseer heap thrashing in uw Java-programma ‘ s.
  • “Threads and applets and visual controls”
    dit laatste deel van de serie onderzoekt het lezen van meerdere datakanalen.
  • “Using communication channels in applets, Part 3”
    Visual Basic-style techniques to applet design — and convert temperatures in the process.
  • “Synchronizing threads in Java, Part II”
    leer hoe je een data channel class schrijft, en maak vervolgens een eenvoudig voorbeeldtoepassing die een real-world implementatie van de class illustreert.
  • ” synchronizing threads in Java”
    voormalig Java-teamontwikkelaar Chuck McManis laat u een eenvoudig voorbeeld zien van het synchroniseren van threads om betrouwbaar en voorspelbaar gedrag van applets te garanderen.

Geef een antwoord

Het e-mailadres wordt niet gepubliceerd.