Javaでインタプリタを構築する-実行エンジンを実装する

私たちに参加したばかりの人のために、過去数ヶ月にわたって私の”Java In Depth”コラムで、私はJavaでインタプリタを構築する方法について議論してきました。 最初のインタプリタの列では、インタプリタの望ましい属性のいくつかをカバーし、第二の列では、インタプリタを実装するためのクラスパッケージの解析とレイアウトの両方について説明しました。 このコラムでは、インタプリタの実行と、それを達成するために必要なサポートクラスを見ていきます。 最後に、ここでは、インタプリタが他のJavaクラスにどのようにフックされ、その能力を高めることができるかについての議論でシリーズをまとめます。

関連するビットの見直し

これまでに説明したことをスケッチし、実行モードについて議論する際に重要になる設計の部分を指摘しましょう。 これらのクラスの詳細な説明については、私の前の列、または下の

リソースセクション

にあるソースコードのリンクを参照してください。

インタプリタの実装には、ProgramStatementExpressionの三つの基礎クラスがあります。

プログラム

このProgramクラスは、パーサーの解析コンポーネントと実行コンポーネントをまとめます。 このクラスは、loadrunの2つのプリンシパルメソッドを定義します。 loadメソッドは、入力ストリームからステートメントを読み取り、ステートメントのコレクションに解析し、runメソッドはコレクションを反復処理し、各ステー Programクラスは、データを格納するためのスタックだけでなく、プログラムが使用する変数のコレクションも提供します。

ステートメント

Statementクラスには、単一の解析されたステートメントが含まれています。 このクラスは、実際には特定の型の文(PRINT、GOTO、IFなど)にサブクラス化されますが、すべての文にはexecuteメソッドが含まれています。Programクラスインスタンスのコンテクストで文を実行するために呼び出されます。

Expressionクラスには、式の解析ツリーが含まれています。 実行中は、valueメソッドを使用して式を評価し、その値を返します。 Statementと同様に、Expressionクラスは主に特定の型の式によってサブクラス化されるように設計されています。

これらのクラスはすべて、インタプリタの基礎を形成するために一緒に動作します。 Programクラスは解析操作と実行操作を同時にカプセル化しますが、StatementクラスとExpressionクラスは実装した言語の実際の計算概念をカプセル化します。 インタプリタの構築に関するこれらの三つの記事では、例の言語は基本的なものでした。

計算のための機能

インタプリタには二つの実行可能クラスがあります,

Statement

Expression

. 最初に見てみましょう

Expression

Expressionのインスタンスは、クラスParseExpressionのメソッドexpressionによって作成されます。 ParseExpressionクラスは、このインタプリタに式パーサーを実装します。 このクラスはParseStatementクラスのピアであり、statementメソッドを使用して基本的なステートメントを解析します。 Expressionのインスタンスには、インスタンスが表す演算子を識別する内部typeと、式の計算値を返す2つのメソッドvaluestringValueがあります。 さらに、Expressionインスタンスが作成されると、名目上、expressionの操作の左辺と右辺を表す2つのパラメータが与えられます。 ソース形式で表示される式の最初の部分は次のとおりです:

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

上記のコードでわかるように、インスタンス変数、operという名前の演算子型、arg1とarg2の操作の2つの半分、およびさまざまな型を定義するための また、式パーサーで使用されるコンストラクターは2つあり、これらは2つの式から新しい式を作成します。 そのソースを以下に示します:

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

最初のコンストラクターは任意の式オブジェクトを構築し、2番目のコンストラクターは単項マイナスなどの”単項”式オブジェクトを構築します。 注意すべきことの1つは、引数が1つしかない場合、arg2がその値を格納するために使用されることです。

Expressionクラスで最も頻繁に使用されるメソッドはvalueで、次のように定義されています:

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

各expressionオブジェクトは、演算子と1つまたは2つの引数で構成されるタプルを表していることがわかります。 この方法で式実行エンジンを設計するのが楽しいのは、Expressionオブジェクトに基づいてこの式タプルのセットを構築するときに、valueメソッドを呼び出すだけで式の値を計算できることです。 valueメソッドは、この式を構成する2つの引数のvalueメソッドを再帰的に呼び出し、それらに操作を適用して結果を返します。valueメソッドは、この式を構成する2つの引数のvalueメソッドを再帰的に呼び出して、その結果を返します。 このデザインは、表現が理解しやすいように使用されました。

クラス構造をきれいに保つために、定数から三角関数までのすべての計算単位はExpressionのサブクラスです。 Lispから恥知らずに盗まれたこのアイデアは、その評価がどのように起こるかの実際の実装から、評価を”発生させる”という概念を完全にカプセル化し この原則がどのように適用されるかを示すために、Expressionの特殊なサブクラスのいくつかだけを見る必要があります。COCOAという名前の私のバージョンのBASICの定数は、ConstantExpressionクラスで表され、Expressionをサブクラス化し、単に数値をメンバー値に格納します。 ConstantExpressionへのソースコードを概念的に以下に示します。 私はStringConstantExpressionNumericConstantExpressionであったものを単一のクラスにバンドルすることを選択したので、私は”概念的に”と言います。 したがって、実際のクラスには、文字列引数を使用して定数を作成し、その値を文字列として返すためのコンストラクタが含まれています。 次のコードは、ConstantExpressionクラスが数値定数をどのように処理するかを示しています。

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

上記のコードは、Expressionのより複雑なコンストラクタをインスタンス変数の単純なストアに置き換えます。valueメソッドは、格納された値の戻り値に置き換え

Expressionクラスをコード化して、コンストラクタでクラスを保存する定数を受け入れることができるのは事実です。 しかし、私が持っている方法でExpressionを設計することの1つの利点は、Expressionのコードが最大限に汎用的なままであることです。 このスタイルのコーディングは、特殊なケースの複雑さを排除するのに役立つため、Expressionコードで”完了”すると、基本クラスを何度も再訪することなく、式の他の面 利点は、FunctionExpressionという名前のExpressionの別のサブクラスを掘り下げたときに明確になります。

クラスFunctionExpressionには、通訳者を柔軟に保つために満たすべきであると感じた二つの設計要件がありました。 最初は、標準の基本機能を実装することでした; もう1つは、関数の引数の解析を、これらの関数を実装したのと同じクラスにカプセル化することでした。 第二の要件である解析は、FunctionExpressionのサブクラスとしてパーサーに渡すことができる追加の関数ライブラリを作成することによって、この基本的な拡張可能 さらに、これらの渡されたクラスは、ユーザーのプログラムで使用できる関数の数を増やすためにパーサーによって使用される可能性があります。

FunctionExpressionクラスはConstantExpressionよりも適度に複雑であり、以下に凝縮された形式で示されています:

 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 }

上記のソースは、valueメソッドがどのように実装されているかを示しています。 Oper変数は関数の同一性を保持するために再利用され、arg1とarg2によって参照される式オブジェクトは関数自体の引数として使用されます。 最後に、要求をディスパッチする大きなswitch文があります。 興味深い点の1つは、valueメソッドが潜在的な算術例外をキャッチし、それらをBASICRuntimeErrorのインスタンスに変換することです。 FunctionExpressionの解析コードを以下に示し、スペースを節約するために再び凝縮します。 (すべてのソースコードは、”リソース”セクションのリンクを使用して使用できます。)

 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 }

このコードは、ParseStatementの式パーサーが式を見ていることを既に認識しており、式のidをパラメータtyとして渡しているという事実を利用していることに注意してく このパーサーは、引数を含む開始括弧と終了括弧のみを見つける必要があります。 しかし、慎重に見てください:#9から#18行目では、パーサーはいくつかの関数に引数を持たないことを許可します(この場合はRNDとFRE)。 これは、すべての関数が事前定義されたテンプレートに準拠するように強制するのではなく、このクラスに関数subparserを組み込むことによって得られる柔軟性を示しています。 パラメータtyに関数型が指定された場合、switch文は、その関数に必要な引数(文字列、数値、その他の式など)を解析できるブランチを選択します。

その他の側面:文字列と配列

基本言語の他の二つの部分は、COCOAインタプリタによって実装されています:文字列と配列。 最初に文字列の実装を見てみましょう。

文字列を変数として実装するために、Expressionクラスは”文字列”式の概念を含むように変更されました。 この変更は、isStringstringValueの2つの追加の形をとりました。 これら2つの新しいメソッドのソースを以下に示します。

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

明らかに、基本的なプログラムが基本式(常に数値式またはブール式)の文字列値を取得するのはあまり便利ではありません。 有用性の欠如から、これらのメソッドはExpressionに属しておらず、代わりにExpressionのサブクラスに属していたと結論づけることができます。 ただし、これら2つのメソッドを基本クラスに入れることで、すべてのExpressionオブジェクトをテストして、実際には文字列であるかどうかを確認できます。

別の設計アプローチは、値を生成するためにStringBufferオブジェクトを使用して数値を文字列として返すことです。 たとえば、同じコードを次のように書き換えることができます:

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

また、上記のコードを使用すると、すべての式が文字列値を返すことができるため、isStringの使用を排除できます。 さらに、valueメソッドを変更して、式が文字列に評価された場合に数値を返すようにするには、java.lang.DoublevalueOfメソッドを実行します。 Perl、TCL、REXXなどの多くの言語では、この種の非晶質型付けが大きな利点に使用されています。 どちらのアプローチも有効であり、インタプリタの設計に基づいて選択する必要があります。 基本的には、文字列が数値変数に代入されたときにインタプリタがエラーを返す必要があるため、最初のアプローチ(エラーを返す)を選択しました。

配列に関しては、それらを解釈するためにあなたの言語を設計することができるさまざまな方法があります。 Cでは、配列要素の周りに角括弧を使用して、配列のインデックス参照と、引数の周りに括弧を持つ関数参照を区別します。 しかし、BASICの言語設計者は関数と配列の両方に括弧を使用することを選択したため、テキストNAME(V1, V2)がパーサーに表示されると、関数呼び出しまたは配列参照のいず

字句解析器は、最初に関数であると仮定し、それをテストすることによって、括弧が続くトークンを区別します。 次に、それらがキーワードまたは変数であるかどうかを確認します。 あなたのプログラムが”SIN”という名前の変数を定義するのを妨げるのはこの決定です。”名前が関数名と一致する変数は、代わりに字句解析器によって関数トークンとして返されます。 字句アナライザが使用する第二のトリックは、変数名の直後に`(‘が続くかどうかを確認することです。 そうである場合、アナライザはそれが配列参照であると仮定します。 字句解析でこれを解析することにより、文字列`MYARRAY ( 2 )‘が有効な配列として解釈されないようにします(変数名と開括弧の間のスペースに注意してください)。

配列を実装する最後のトリックはVariableクラスにあります。 このクラスは変数のインスタンスに使用され、先月のコラムで説明したように、Tokenのサブクラスです。 しかし、それはまた、配列をサポートするためのいくつかの機械を持っており、それは私が以下に示すものです:

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;

上記のコードは、ConstantExpressionクラスのように、変数に関連付けられたインスタンス変数を示しています。 クラスの複雑さに対して使用するクラスの数を選択する必要があります。 設計上の選択肢の1つは、スカラー変数のみを保持するVariableクラスを構築し、配列の複雑さに対処するためにArrayVariableサブクラスを追加することです。 私はそれらを結合し、スカラー変数を本質的に長さ1の配列に変換することを選択しました。

上記のコードを読むと、配列インデックスと乗算器が表示されます。 BASICの多次元配列は単一の線形Java配列を使用して実装されているため、これらはここにあります。 Java配列への線形インデックスは、乗数配列の要素を使用して手動で計算されます。 基本プログラムで使用されるインデックスは、インデックスのndx配列内の最大有効インデックスと比較することによって有効性がチェックされま

たとえば、3次元が10、10、および8の基本配列は、10、10、および8の値をndxに格納します。 これにより、式エバリュエーターは、基本プログラムで使用されている数と、現在ndxに格納されている最大有効数とを比較することによって、”範囲外のイン この例の乗数配列には、値1、10、および100が含まれています。 これらの定数は、多次元配列インデックス仕様から線形配列インデックス仕様にマップするために使用する数値を表します。 実際の式は次のとおりです。

Java Index=Index1+Index2*Index1の最大サイズ+Index3*(Maxsize of Index1*MaxSizeIndex2)

Variableクラスの次のJava配列を以下に示します。

 Expression expns;

expns配列は、”A(10*B, i)と書かれた配列を処理するために使用されます。”その場合、インデックスは実際には定数ではなく式であるため、参照には実行時に評価される式へのポインタが含まれている必要があります。 最後に、プログラムで渡されたものに応じてインデックスを計算する、このかなり醜い見た目のコードがあります。 このプライベートメソッドを以下に示します。

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

上記のコードを見ると、コードは最初に配列を参照するときに正しい数のインデックスが使用されていることを確認し、各インデックスがそのインデ エラーが検出されると、インタプリタに例外がスローされます。 メソッドnumValuestringValueは、それぞれ変数から数値または文字列として値を返します。 これらの2つの方法を以下に示します。

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

ここでは示されていない変数の値を設定するための追加の方法があります。

各ピースがどのように実装されているかの複雑さの多くを隠すことによって、最終的に基本プログラムを実行する時が来たとき、Javaコードは非常に簡

コードの実行

基本的な文を解釈して実行するコードは、次のコードに含まれています。

run

の方法

Program

クラス。 このメソッドのコードは以下に示されており、興味深い部分を指摘するためにステップスルーします。

 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 }

上記のコードは、runメソッドが実行中のプログラムの”コンソール”として使用するためにInputStreamOutputStreamを取ることを示しています。 3行目では、列挙オブジェクトeはstmtsという名前のコレクションからのステートメントのセットに設定されています。 このコレクションのために、私は”赤黒”木と呼ばれる二分探索木のバリエーションを使用しました。 (バイナリ検索ツリーの詳細については、ジェネリックコレクションの構築に関する私の前の列を参照してください。)その後、2つの追加のコレクションが作成されます-1つはStackを使用し、1つはVectorを使用します。 スタックはどのコンピュータでもスタックと同様に使用されますが、ベクトルは基本プログラムのDATAステートメントに明示的に使用されます。 最終的なコレクションは、基本プログラムによって定義された変数の参照を保持する別の赤黒いツリーです。 このツリーは、プログラムの実行中に使用されるシンボルテーブルです。

初期化の後、入力ストリームと出力ストリームがセットアップされ、eがnullでない場合は、宣言されたデータを収集することから始めます。 これは、次のコードに示すように行われます。

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

上記のループは、単にすべての文を見て、見つかったデータ文が実行されます。 各DATAステートメントを実行すると、そのステートメントで宣言された値がデータストアベクトルに挿入されます。 次に、この次のコードを使用して実行されるプログラムを適切に実行します:

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

次のステップは、最初のステートメントを変数sにフェッチしてから実行ループに入ることです。 入力ストリームの保留中の入力をチェックして、プログラムを入力することによってプログラムの進行を中断できるようにするコードがいくつかあり、ループは実行する文がデータ文であるかどうかをチェックします。 そうであれば、ループはすでに実行されたステートメントをスキップします。 READ文を満たすDATA文をソースコードのどこにでも表示できるようにするため、すべてのdata文を最初に実行するという複雑な手法が必要です。 最後に、トレースが有効になっている場合は、トレースレコードが出力され、非常に重要でないステートメントs = s.execute(this, in, pout);が呼び出されます。 美しさは、基本概念を理解しやすいクラスにカプセル化するすべての努力が、最終的なコードを些細なものにすることです。 それが些細なことでないなら、おそらくあなたはあなたのデザインを分割する別の方法があるかもしれないという手掛かりを持っています。

ラップアップとさらなる考え

インタプリタはスレッドとして実行できるように設計されているため、プログラム空間で同時に複数のCOCOAインタプリタ さらに、関数拡張を使用することで、これらのスレッドが互いに相互作用できる手段を提供することができます。 C-robotsと呼ばれるプログラムは、単純な基本的な派生言語を使用してプログラムされた相互作用する「ロボット」エンティティのシステムでした。 ゲームは私と他の人に多くの時間の娯楽を提供しましたが、若い学生(誤って遊んでいて学習していないと信じていた)に計算の基本原則を紹介する優れた方法でもありました。 Javaベースのインタプリタ-サブシステムは、どのJavaプラットフォームでも即座に利用できるため、java以前のインタプリタ-サブシステムよりも COCOAは、私がWindows95ベースのPCで作業したのと同じ日にUnixシステムとMacintoshesで実行されました。 Javaはthreadやwindow toolkitの実装で非互換性に悩まされていますが、見過ごされることが多いのはこれです:多くのコードは「うまくいくだけです。”

Chuck McManisは現在、インターネット市場での機会を模索するベンチャー資金によるスタートアップであるFreeGate Corp.のシステムソフトウェアのディレクターです。 FreeGateに参加する前は、ChuckはJavaグループのメンバーでした。 FirstPerson Inc.の設立直後にJavaグループに参加しました。 そして、portable OSグループ(JavaのOSportionを担当するグループ)のメンバーでした。 その後、FirstPersonが解散したとき、彼はjavaプラットフォームのalphaとbetaversionsの開発を通じてグループに滞在しました。 彼は、1995年5月にSunのホームページのJavaversionのプログラミングを行ったときに、インターネット上で最初の「すべてのJava」ホームページを作成しました。 彼はまた、Java用のacryptographicライブラリと、デジタル署名に基づいてクラスをスクリーニングできるJava classloaderのバージョンを開発しました。FirstPersonに入社する前は、SunSoftのオペレーティングシステム領域で働き、ネットワークアプリケーションを開発し、NIS+の最初の設計を行った。 また、彼のホームページをチェックしてください。 :END_BIO

このトピックの詳細

  • 上記で参照されているソースファイルへのリンクは次のとおりです:
    • ConstantExpression。java
    • FunctionExpression.java
    • プログラム。java
    • ステートメント。java
    • StringExpression。java
    • 変数。java
    • VariableExpression。java
  • そして、ここにあります。ソースファイルのZIPファイル:
    indepth。zip
  • “Uncommon Lisp”–Javaで書かれたLispインタプリタ
    http://user03.blue.aol.com/thingtone/workshop/lisp.htm
  • Mike CowlishawのJavaで書かれたNetREXXインタプリタ
    http://www2.hursley.ibm.com/netrexx/
  • USENETの基本的なFAQの古いコピー(それはまだそれに有用な情報を持っています。)
    http://whitworth.me.ic.ac.uk/people/students/djbur/qbasic.htm
  • COCOA、Javaで書かれた基本的なインタプリタ
    http://www.mcmanis.com/~cmcmanis/java/javaworld/examples/BASIC.html
  • ChuckのJavaリソースページ
    http://www.mcmanis.com/~cmcmanis/java/javaworld/
  • Javaで書かれたTCLインタプリタ
    http://www.cs.cornell.edu/home/ioi/Jacl/
  • :
  • “Javaでインタプリタを構築する方法,第2部構造”
    単純なインタプリタのための基礎クラスを組み立てるためのトリック。
  • “Javaでインタプリタを構築する方法第1部基本”
    スクリプト言語を必要とする複雑なアプリケーションでは、Javaを使用してインタプリタを実装し、任意のJavaア
  • “字句解析、第2部アプリケーションの構築”
    StreamTokenizerオブジェクトを使用して対話型計算機を実装する方法。
  • “字句解析とJavaPart1”
    StringTokenizerクラスとStreamTokenizerクラスを使用して、人間が読めるテキストを機械が読めるデータに変換する方法について説明します。
  • “コードの再利用とオブジェクト指向システム”
    動的な動作を強制するためにヘルパークラスを使用します。
  • “Java1.0.2でのオブジェクトのコンテナサポート”
    オブジェクトをコンテナに入れると簡単に整理できます。 この記事では、コンテナの設計と実装について説明します。
  • “Javaクラス-ローダーの基本”
    Javaアーキテクチャのこの重要なコンポーネントの基礎。
  • “ガベージコレクションを使用していない”
    Javaプログラムのヒープスラッシングを最小限に抑えます。
  • “スレッドとアプレットとビジュアルコントロール”
    このシリーズの最後の部分では、複数のデータチャネルの読み取りについて説明します。
  • “アプレットでの通信チャネルの使用、パート3”
    アプレット設計のためのVisual Basicスタイルのテクニックを開発し、プロセス内の温度を変換します。
  • “Javaでのスレッドの同期、パートII”
    データチャネルクラスの記述方法を学び、クラスの実際の実装を示す簡単なサンプルアプリケーションを作成します。
  • “Javaでのスレッドの同期”
    元Javaチーム開発者Chuck McManisは、信頼性と予測可能なアプレットの動作を保証するためにスレッドを同期する方法を示す簡単な例

コメントを残す

メールアドレスが公開されることはありません。