Construiți un interpret în Java-implementați motorul de execuție

pentru cei dintre voi care tocmai ni s-au alăturat, în coloana mea „Java în profunzime” din ultimele luni, am discutat despre cum s-ar putea construi un interpret în Java. În prima coloană interpret, am acoperit unele dintre atributele dorite ale unui interpret; în a doua coloană am discutat atât parsarea, cât și aspectul unui pachet de clasă pentru implementarea interpretului. În această coloană ne vom uita la rularea interpret, și clasele de sprijin necesare pentru a realiza acest lucru. În cele din urmă, voi încheia seria aici cu o discuție despre modul în care un interpret poate fi conectat la alte clase Java, sporindu-și astfel abilitățile.

revizuirea biții relevanți

permiteți-mi să încep prin a schița ceea ce am acoperit până acum și să subliniez acele părți ale designului care vor deveni mai importante pe măsură ce discutăm modul de execuție. Pentru o descriere mai detaliată a acestor clase, consultați coloanele mele anterioare sau legăturile codului sursă care se află în secțiunea

resurse

de mai jos.

există trei clase fundamentale în implementarea interpretului, Program, Statement și Expression. Următoarele arată modul în care cele trei sunt legate:

Program

această clasă Program lipeste împreună componentele de parsare și execuție ale parserului. Această clasă definește două metode principale, load și run. Metoda load citește declarații dintr-un flux de intrare și le analizează într-o colecție de declarații, metoda run iterează peste colecție și execută fiecare dintre declarații. Clasa Program oferă, de asemenea, o colecție de variabile pentru utilizarea programului, precum și o stivă pentru stocarea datelor.

declarație

clasa Statement conține o singură declarație analizată. Această clasă este de fapt subclasată într-un anumit tip de instrucțiune (PRINT, GOTO, IF și așa mai departe), dar toate declarațiile conțin metoda execute care este chemată să execute instrucțiunea în contextul unei instanțe de clasă Program.

Expresie

clasa Expression conține arborele de analiză al unei expresii. În timpul execuției, metoda value este utilizată pentru a evalua expresia și a returna valoarea acesteia. La fel ca Statement, clasa Expression este concepută în primul rând pentru a fi subclasată de tipuri specifice de expresii.

toate aceste clase lucrează împreună pentru a forma baza unui interpret. Clasa Program încapsulează simultan operația de parsare și operația de execuție, în timp ce clasele Statement și Expression încapsulează conceptele computaționale reale ale limbajului pe care l-am implementat. Pentru aceste trei articole despre construirea interpreților, exemplul limbii a fost de bază.

facilități pentru calcul

există două clase executabile în interpret,

Statement

și

Expression

. Mai întâi să aruncăm o privire la

Expression

instanțele Expression sunt create prin metoda expression în clasa ParseExpression. Clasa ParseExpression implementează parserul expression în acest interpret. Această clasă este un egal al clasei ParseStatement , care folosește metoda statement pentru a analiza declarațiile de bază. Instanțele Expression au un type intern care identifică ce operator reprezintă instanța și două metode, value și stringValue, care returnează valoarea calculată a expresiei. În plus, atunci când este creată o instanță de Expresie, i se dau nominal doi parametri reprezentând partea stângă și cea dreaptă a operației expresiei. Prezentat în formă sursă, prima parte a expresiei este după cum urmează:

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

după cum puteți vedea în codul de mai sus, există variabilele de instanță, un tip de operator numit oper și două jumătăți ale operației în arg1 și arg2 și apoi câteva constante pentru a defini diferitele tipuri. De asemenea, există doi constructori care sunt utilizați de parserul expresiei; acestea creează o nouă expresie din două expresii. Sursa lor este prezentată mai jos:

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

primul constructor construiește un obiect de Expresie arbitrar, iar al doilea construiește un obiect de Expresie „unar” – cum ar fi unar minus. Un lucru de remarcat este că, dacă există un singur argument, arg2 este folosit pentru a-și stoca valoarea.

metoda utilizată cel mai frecvent în clasa Expression este value, care este definită după cum urmează:

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

puteți vedea că fiecare obiect de Expresie reprezintă un tuplu format dintr-un operator și unul sau două argumente. Partea distractivă a proiectării motorului de execuție a expresiei în acest fel este că atunci când construiți acest set de tupluri de expresie bazate pe obiectul Expression, puteți calcula valoarea expresiei invocând pur și simplu metoda value. Metoda value invocă recursiv metoda value a celor două argumente care compun această expresie, le aplică operația și returnează rezultatul. Acest design a fost folosit astfel încât expresiile să fie ușor de înțeles.

pentru a menține structura clasei curată, toate unitățile de calcul-de la constante la funcții trigonometrice-sunt subclase de Expression. Această idee, furată fără rușine din Lisp, încapsulează complet noțiunea de” a provoca „o evaluare, din implementarea efectivă a” modului ” în care are loc acea evaluare. Pentru a demonstra modul în care se aplică acest principiu, trebuie să ne uităm doar la unele dintre subclasele specializate ale Expression.

constante în versiunea mea de bază, pe care am numit cacao sunt reprezentate de clasa ConstantExpression, care subclase Expression și stochează pur și simplu valoarea numerică într-o valoare membru. Codul sursă la ConstantExpression este prezentat conceptual mai jos. Spun „conceptual” pentru că am ales să grupez ceea ce ar fi fost StringConstantExpression și NumericConstantExpression într-o singură clasă. Deci, clasa reală include un constructor pentru crearea unei constante cu un argument șir și pentru returnarea valorii sale ca șir. Următorul cod arată modul în care clasa ConstantExpression gestionează constantele numerice.

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

codul prezentat mai sus înlocuiește Constructorii mai complicați ai Expression cu o simplă stocare a unei variabile de instanță; metoda value este pur și simplu înlocuită cu o returnare a valorii stocate.

este adevărat că ați putea codifica clasa Expression pentru a accepta în constructorii săi o constantă care v-ar salva o clasă. Cu toate acestea, un avantaj al proiectării Expression modul în care am este că codul din Expression rămâne maxim generic. Consider că acest stil de codificare mă ajută să elimin complexitatea cazurilor speciale și, astfel, când sunt „terminat” cu codul Expression, pot trece la alte aspecte ale expresiilor fără a revizui clasa de bază din nou și din nou. Avantajul devine mai clar atunci când aprofundăm într-o altă subclasă de Expression numită FunctionExpression.

în clasa FunctionExpression, au existat două cerințe de proiectare pe care am considerat că ar trebui îndeplinite pentru a menține interpretul flexibil. Primul a fost implementarea funcțiilor de bază standard; celălalt a fost să încapsuleze analiza argumentelor funcțiilor în aceeași clasă care a implementat aceste funcții. A doua cerință, analiza, a fost motivată de dorința de a face această bază extensibilă prin crearea de biblioteci de funcții suplimentare care ar putea fi transmise parserului ca subclase de FunctionExpression. Mai mult, acele clase trecute ar putea fi utilizate de parser pentru a crește numărul de funcții disponibile pentru programul utilizatorului.

clasa FunctionExpression este doar moderat mai complicată decât a ConstantExpression și este prezentată sub formă condensată mai jos:

 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 }

sursa de mai sus arată modul în care este implementată metoda value. Variabila oper este refolosită pentru a păstra identitatea funcției, iar obiectele de Expresie la care se face referire arg1 și arg2 sunt folosite ca argumente pentru funcțiile în sine. În cele din urmă, există o declarație de comutare mare care expediază cererea. Un aspect interesant este că metoda value captează potențialele excepții aritmetice și le transformă în instanțe de BASICRuntimeError. Codul de parsare din FunctionExpression este prezentat mai jos, din nou condensat pentru a economisi spațiu. (Amintiți-vă, tot codul sursă este disponibil folosind link-uri în secțiunea Resurse.)

 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 }

rețineți că acest cod profită de faptul că parserul expresiei din ParseStatement și-a dat seama deja că se uită la o expresie și a trecut identitatea expresiei în parametrul as ty. Acest parser trebuie apoi să localizeze doar paranteza de deschidere și paranteza de închidere care conțin argumentul(argumentele). Dar priviți cu atenție: în liniile #9 până la #18, parserul permite unor funcții să nu aibă argumente (în acest caz RND și FRE). Acest lucru demonstrează flexibilitatea oferită de a avea funcția subperser încorporată în această clasă, mai degrabă decât forțarea tuturor funcțiilor să se conformeze unui șablon predefinit. Având în vedere un tip de funcție în parametrul ty, instrucțiunea switch Selectează o ramură care poate analiza argumentele necesare pentru acea funcție, fie că sunt șiruri, numere, alte expresii și așa mai departe.

alte aspecte: șiruri și tablouri

alte două părți ale limbajului de bază sunt implementate de interpretul COCOA: șiruri și tablouri. Să ne uităm mai întâi la implementarea șirurilor.

pentru a implementa șiruri ca variabile, clasa Expression a fost modificată pentru a include noțiunea de expresii „șir”. Această modificare a luat forma a două adăugiri: isString și stringValue. Sursa pentru aceste două metode noi este prezentată mai jos.

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

în mod clar, nu este prea util pentru un program de bază pentru a obține valoarea șir de o expresie de bază (care este întotdeauna fie numeric sau boolean Expresie). S-ar putea concluziona din lipsa de utilitate că aceste metode nu aparțineau atunci Expression și aparțineau într-o subclasă de Expression în schimb. Cu toate acestea, punând aceste două metode în clasa de bază, toate obiectele Expression pot fi testate pentru a vedea dacă, de fapt, sunt șiruri.

o altă abordare de proiectare este de a returna valorile numerice ca șiruri folosind un obiect StringBuffer pentru a genera o valoare. Deci, de exemplu, același cod ar putea fi rescris ca:

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

și dacă se utilizează codul de mai sus, puteți elimina utilizarea isString deoarece fiecare expresie poate returna o valoare șir. Mai mult, puteți modifica metoda value pentru a încerca să returnați un număr dacă expresia se evaluează la un șir rulând-o prin metoda valueOfa java.lang.Double. În multe limbi, cum ar fi Perl, TCL și REXX, acest tip de tastare amorfă este folosit cu mare avantaj. Ambele abordări sunt valabile și ar trebui să vă alegeți pe baza designului interpretului dvs. În BASIC, interpretul trebuie să returneze o eroare atunci când un șir este atribuit unei variabile numerice, așa că am ales prima abordare (returnarea unei erori).

în ceea ce privește tablourile, există diferite moduri în care vă puteți proiecta limbajul pentru a le interpreta. C folosește parantezele pătrate în jurul elementelor matricei pentru a distinge referințele index ale matricei de referințele funcționale care au paranteze în jurul argumentelor lor. Cu toate acestea, designerii de limbi pentru BASIC au ales să utilizeze paranteze atât pentru funcții, cât și pentru matrice, astfel încât atunci când textul NAME(V1, V2) este văzut de parser, ar putea fi fie un apel de funcții, fie o referință de matrice.

analizorul lexical discriminează între jetoanele care sunt urmate de paranteze, presupunând mai întâi că sunt funcții și testează pentru asta. Apoi se merge pe pentru a vedea dacă acestea sunt cuvinte cheie sau variabile. Această decizie împiedică programul dvs. să definească o variabilă numită ” păcat.”Orice variabilă al cărei nume se potrivea cu un nume de funcție ar fi returnat de analizorul lexical ca jeton de funcție în schimb. Al doilea truc pe care îl folosește analizorul lexical este să verifice dacă numele variabilei este urmat imediat de `(‘. Dacă este, analizorul presupune că este o referință matrice. Analizând acest lucru în analizorul lexical, eliminăm șirul ‘MYARRAY ( 2 ) ‘ de a fi interpretat ca o matrice validă (notați spațiul dintre numele variabilei și paranteza deschisă).

trucul final pentru implementarea matricelor este în clasa Variable. Această clasă este utilizată pentru o instanță a unei variabile și, așa cum am discutat în coloana de luna trecută, este o subclasă de Token. Cu toate acestea, ea are, de asemenea, unele mașini pentru a sprijini matrice și că este ceea ce voi arăta mai jos:

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;

codul de mai sus arată variabilele de instanță asociate cu o variabilă, ca în clasa ConstantExpression. Unul trebuie să facă o alegere cu privire la numărul de clase pentru a fi utilizate față de complexitatea unei clase. O alegere de proiectare ar putea fi construirea unei clase Variable care conține doar variabile scalare și apoi adăugarea unei subclase ArrayVariable pentru a face față complexităților matricelor. Am ales să le combin, transformând variabilele scalare în esență în matrice de lungime 1.

dacă citiți codul de mai sus, veți vedea indici și multiplicatori de matrice. Acestea sunt aici deoarece matricele multidimensionale din BASIC sunt implementate folosind o singură matrice Java liniară. Indexul liniar în matricea Java este calculat manual folosind elementele matricei multiplicatoare. Indicii utilizați în programul de bază sunt verificați pentru validitate comparându-i cu indicele legal maxim din matricea ndx a indicilor.

de exemplu, o matrice de bază cu trei dimensiuni de 10, 10 și 8 ar avea valorile 10, 10 și 8 stocate în ndx. Acest lucru permite evaluatorului expresiei să testeze o condiție” index out of bounds ” comparând numărul utilizat în programul de bază cu numărul legal maxim care este acum stocat în ndx. Matricea multiplicatoare din exemplul nostru ar conține valorile 1, 10 și 100. Aceste constante reprezintă numerele pe care le folosește pentru a mapa dintr-o specificație a indexului matricei multidimensionale într-o specificație a indexului matricei liniare. Ecuația reală este:

Java Index = Index1 + Index2 * dimensiunea maximă a Index1 + Index3 * (dimensiunea maximă a Index1 * MaxSizeIndex 2)

următoarea matrice Java din clasa Variable este prezentată mai jos.

 Expression expns;

matricea expns este utilizată pentru a trata matricele care sunt scrise ca „A(10*B, i).”În acest caz, indicii sunt de fapt expresii, mai degrabă decât constante, astfel încât referința trebuie să conțină indicii pentru acele expresii care sunt evaluate în timpul rulării. În cele din urmă, există această bucată de cod destul de urâtă, care calculează indexul în funcție de ceea ce a fost trecut în program. Această metodă privată este prezentată mai jos.

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

privind Codul de mai sus, veți observa că codul verifică mai întâi pentru a vedea că numărul corect de indici a fost utilizat la referențierea matricei și apoi că fiecare index a fost în intervalul legal pentru acel index. Dacă este detectată o eroare, o excepție este aruncată interpretului. Metodele numValue și stringValue returnează o valoare din variabilă ca număr sau respectiv șir. Aceste două metode sunt prezentate mai jos.

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

există metode suplimentare pentru setarea valorii unei variabile care nu sunt afișate aici.

ascunzând o mare parte din complexitatea modului în care este implementată fiecare piesă, când vine în sfârșit timpul să execute programul de bază, codul Java este destul de simplu.

rularea codului

codul pentru interpretarea declarațiilor de bază și executarea acestora este conținut în

run

metoda metodei

Program

clasă. Codul pentru această metodă este prezentat mai jos și voi trece prin ea pentru a sublinia părțile interesante.

 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 }

codul de mai sus arată că metoda run ia un InputStream și un OutputStream pentru utilizare ca „consolă” pentru programul de executare. În linia 3, obiectul de enumerare e este setat la setul de declarații din colecția numită stmts. Pentru această colecție am folosit o variantă pe un arbore de căutare binar numit copac” roșu-negru”. (Pentru informații suplimentare despre arborii de căutare binari, consultați coloana mea anterioară despre construirea colecțiilor generice.) După aceea, sunt create două colecții suplimentare-una folosind un Stack și una folosind un Vector. Stiva este utilizată ca stiva în orice computer, dar Vectorul este utilizat în mod expres pentru declarațiile de date din programul de bază. Colecția finală este un alt copac roșu-negru care deține referințele pentru variabilele definite de programul de bază. Acest arbore este tabelul simbol care este utilizat de program în timp ce se execută.

în urma inițializării, fluxurile de intrare și ieșire sunt configurate și apoi, dacă e nu este nul, începem prin colectarea oricăror date care au fost declarate. Acest lucru se face așa cum se arată în următorul cod.

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

bucla de mai sus pur și simplu se uită la toate declarațiile, și orice declarații de date se găsește sunt apoi executate. Executarea fiecărei declarații de date inserează valorile declarate de acea declarație în vectorul de stocare a datelor. Apoi executăm programul propriu-zis, care se face folosind următoarea bucată de cod:

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

după cum puteți vedea în codul de mai sus, primul pas este Reinițializarea e. următorul pas este să aduceți prima instrucțiune în variabila s și apoi să introduceți bucla de execuție. Există un cod pentru a verifica intrarea în așteptare în fluxul de intrare pentru a permite întreruperea progresului programului tastând la program, iar apoi bucla verifică dacă instrucțiunea de executat ar fi o declarație de date. Dacă este, bucla omite declarația așa cum a fost deja executată. Tehnica destul de complicată de a executa mai întâi toate declarațiile de date este necesară, deoarece BASIC permite declarațiilor de date care satisfac o declarație de citire să apară oriunde în codul sursă. În cele din urmă, dacă urmărirea este activată, se imprimă o înregistrare de urmărire și se invocă declarația foarte neinpresivă s = s.execute(this, in, pout);. Frumusețea este că tot efortul de a încapsula conceptele de bază în clase ușor de înțeles face ca codul final să fie banal. Dacă nu este banal, atunci probabil că aveți un indiciu că ar putea exista un alt mod de a vă împărți designul.

înfășurarea și alte gânduri

interpretul a fost conceput astfel încât să poată rula ca un fir, astfel pot exista mai multe fire de interpret de cacao care rulează simultan în spațiul programului dvs. în același timp. Mai mult, cu utilizarea funcției de expansiune putem oferi un mijloc prin care aceste fire pot interacționa unele cu altele. A existat un program pentru Apple II și mai târziu pentru PC și Unix numit C-robots, care a fost un sistem de entități „robotizate” care au fost programate folosind un limbaj derivat de bază simplu. Jocul mi-a oferit mie și altora multe ore de divertisment, dar a fost, de asemenea, o modalitate excelentă de a introduce principiile de bază ale calculului elevilor mai tineri (care au crezut în mod eronat că doar se joacă și nu învață). Subsistemele de interpretare bazate pe Java sunt mult mai puternice decât omologii lor pre-Java, deoarece sunt disponibile instantaneu pe orice platformă Java. Cacao a rulat pe sisteme Unix și Macintoshes în aceeași zi în care am lucrat la un PC bazat pe Windows 95. În timp ce Java este bătut de incompatibilități în implementările thread sau window toolkit, ceea ce este adesea trecut cu vederea este acesta: o mulțime de cod „funcționează doar.”

Chuck McManis este în prezent directorul software-ului de sistem la FreeGate corp., un start-up finanțat de risc care explorează oportunități pe piața internetului. Înainte de a se alătura FreeGate, Chuck a fost membru al Grupului Java. S-a alăturat grupului Java imediat după formarea FirstPerson Inc. și a fostun membru al grupului portable OS (grupul responsabil pentru Osportarea Java). Mai târziu, când Primulpersoana a fost dizolvată, a rămascu grupul prin dezvoltarea alpha și betaversions ale platformei Java. El a creat prima pagină de pornire” All Java ” pe Internet când a făcut programarea pentru pagina de pornire Javaversion of the Sun în mai 1995. El a dezvoltat, de asemenea, acryptographic library pentru Java și versiuni ale Java classloader care ar putea ecran clase bazate pe semnături digitale.Înainte de a se alătura FirstPerson, Chuck a lucrat în sistemele de operarezona SunSoft, dezvoltând aplicații de rețea, unde a făcut-odesignul inițial al NIS+. De asemenea, verificați pagina sa de pornire. : END_BIO

Aflați mai multe despre acest subiect

  • aici sunt link-uri către fișierele sursă menționate mai sus:
    • expresie constantă.java
    • FunctionExpression.java
    • Program.java
    • declarație.java
    • StringExpression.java
    • variabilă.java
    • VariableExpression.java
  • și aici este un .Fișier ZIP al fișierelor sursă:
    indepth.zip
  • „mai puțin frecvente Lisp” – un interpret Lisp scris în Java
    http://user03.blue.aol.com/thingtone/workshop/lisp.htm
  • interpretul NetREXX al lui Mike Cowlishaw scris în Java
    http://www2.hursley.ibm.com/netrexx/
  • o copie veche a Usenet basic FAQ (are încă informații utile în ea.)
    http://whitworth.me.ic.ac.uk/people/students/djbur/qbasic.htm
  • COCOA, un interpret de bază scris în Java
    http://www.mcmanis.com/~cmcmanis/java/javaworld/examples/BASIC.html
  • pagina de resurse Java a lui Chuck
    http://www.mcmanis.com/~cmcmanis/java/javaworld/
  • interpret TCL scris în Java
    http://www.cs.cornell.edu/home/ioi/Jacl/
  • :
  • „cum se construiește un interpret în Java, partea 2structura”
    trucul pentru asamblarea claselor de fundație pentru un interpret simplu.
  • „cum se construiește un interpret în Java, partea 1bazele de bază”
    pentru aplicații complexe care necesită un limbaj de scripting, Java poate fi utilizat pentru a implementa interpretul, adăugând abilități de scripting oricărei aplicații Java.
  • „analiza lexicală, partea 2construiți o aplicație”
    cum să utilizați obiectul StreamTokenizer pentru a implementa un calculator interactiv.
  • ” analiza lexicală și JavaPart 1″
    Aflați cum să convertiți textul care poate fi citit de om în date care pot fi citite de mașină folosind clasele StringTokenizer și StreamTokenizer.
  • „reutilizarea codului și sistemele orientate pe obiecte”
    utilizați o clasă de ajutor pentru a impune un comportament dinamic.
  • ” suport Container pentru obiecte în Java 1.0.2″
    organizarea obiectelor este ușoară atunci când le puneți în containere. Acest articol vă ghidează prin proiectarea și implementarea unui container.
  • ” elementele de bază ale încărcătoarelor de clasă Java”
    fundamentele acestei componente cheie a arhitecturii Java.
  • ” nu se utilizează colectarea gunoiului”
    minimizarea bătaie heap în programele Java.
  • „fire și applet-uri și controale vizuale”
    această parte finală a seriei explorează citirea mai multor canale de date.
  • „utilizarea canalelor de comunicare în applet-uri, Partea 3”
    dezvoltarea tehnicilor de stil Visual Basic pentru proiectarea applet-și conversia temperaturilor în proces.
  • ” sincronizarea firelor în Java, partea a II-a”
    Aflați cum să scrieți o clasă de canale de date și apoi creați un exemplu simplu de aplicație care ilustrează o implementare din lumea reală a clasei.
  • ” sincronizarea firelor în Java”
    fostul dezvoltator Java Team Chuck McManis vă prezintă un exemplu simplu care ilustrează modul de sincronizare a firelor pentru a asigura un comportament fiabil și previzibil al appletului.

Lasă un răspuns

Adresa ta de email nu va fi publicată.