入力システム及び字句解析:ゼロからコンパイラ(A)を書くために

序文

セミの半完成コピーからいくつかの時間が今にJavaバイトコードにCコンパイラを変更、常にするシリーズ書きたかっ見直し、コンパイラを書くのプロセスを整理し、それが研究ノートとみなすことができます。それを書くために今日から。

プロジェクトの完全なコードC2j、コンパイラ

私は直接直接実行ASTを横切る、C言語インタプリタを書くために起動し、コードを生成するJavaバイトコードにコンパイルされるセクション、後に添加され

ほとんどは、上記のリンクを見て具体的には、C言語のサポートを使用し、もちろん、まだおもちゃよりおもちゃのクラス・コンパイラレベルを持っています。

開始

おそらく、完全なコンパイラこれらの主要部分

  • 字句解析

一般的には有限状態オートマトンを使用するか、手動で達成書き込み、このステップでは、出力トークン列であります

  • パージング

トップダウンおよびボトムアップ解析、一般的に再帰下降、LLに分ける(1)、LR(1)、LALR(1)いくつかの方法で実施しました。このステップは、構文木の出力であります

  • 意味解析

メインタスクの意味解析は、シンボルテーブルを生成することで、文の意味論に適合しないことが判明、このステップまたはASTの出力

  • コード生成

本明細書で一般的にプラットフォームとボトム(IR)近接比較的独立した中間言語を生成し、このステップ入力ASTは、IRを出力します

  • コードの最適化

名前が示唆するこの作業の工程とは、パフォーマンスを向上させる、などのコードを最適化することです

  • ターゲットコード生成

このステップのタスクは、プラットフォームに依存するアセンブリ言語を生成することです

上記のコンパイラのほぼ全体一般的な意味でですが、また、実行可能ファイルを生成するために、リンカ、アセンブラを呼び出し含めることができます

時間は3つの段階で制限されたC2jコンパイラレベルは、任意の最適化を行わず、直接、対象のJavaバイトコードを生成するASTをトラバースすることです。LALR(1)解析テーブルを使用して手書き解析を使用して字句解析

入力システム

ソースファイルドルの1000行のためにそれが必要である入力入力の効率を改善するためのシステムを構築します。

システムに入力された3つの文書があります。

  • FileHandler.java
  • DiskFileHandler.java
  • Input.java

FileHadnler

インタフェース入力として、DiskFileHandlerがファイルから読み込ま達成するために、このインタフェースを実装しています。三つの主要な方法があります。

void open();
int close();
int read(byte[] buf, int begin, int end);

リードが指定された長さとバッファ開始位置に指定されたデータをコピーすることであるバッファを指定します

私の倉庫での完全なソースコードdejavudwh

入力

入力は、入力ポインタは、危険ゾーンを横断しようとするとき、バッファにファイルの内容の最初の部分であり、入力の効率を向上させるためにバッファを利用全体入力システムの実装のキーポイントであり、バッファの再入力は、あなたがIOに多くの時間を避けるために、ファイルの内容に作品全体を読むことができます。

入力を取得するために前進位置inputAdvance、入力を取得する前に、バッファフラッシュを最初のチェックは必要ありませんでしょう

public byte inputAdvance() {
        char enter = '\n';

        if (isReadEnd()) {
            return 0;
        }

        if (!readEof && flush(false) < 0) {
            //缓冲区出错
            return -1;
        }

        if (inputBuf[next] == enter) {
            curCharLineno++;
        }

        endCurCharPos++;

        return inputBuf[next++];
}

フラッシュメインロジックは、バッファfillbufを充填するために呼び出され、次のポインタが危険区域、または真のフラッシュを強制するために必要な力を超えないかを決定することです

private int flush(boolean force) {
        int noMoreCharToRead = 0;
        int flushOk = 1;

        int shiftPart, copyPart, leftEdge;
        if (isReadEnd()) {
            return noMoreCharToRead;
        }

        if (readEof) {
            return flushOk;
        }

        if (next > DANGER || force) {
            leftEdge = next;
            copyPart = bufferEndFlag - leftEdge;
            System.arraycopy(inputBuf, leftEdge, inputBuf, 0, copyPart);
            if (fillBuf(copyPart) == 0) {
                System.err.println("Internal Error, flush: Buffer full, can't read");
            }

            startCurCharPos -= leftEdge;
            endCurCharPos -= leftEdge;
            next  -= leftEdge;
        }

        return flushOk;
}
private int fillBuf(int startPos) {
        int need;
        int got;
        need = END - startPos;
        if (need < 0) {
            System.err.println("Internal Error (fill buf): Bad read-request starting addr.");
        }

        if (need == 0) {
            return 0;
        }

        if ((got = fileHandler.read(inputBuf, startPos, need)) == -1) {
            System.err.println("Can't read input file");
        }

        bufferEndFlag = startPos + got;
        if (got < need) {
            //输入流已经到末尾
            readEof = true;
        }

        return got;
}

字句解析

入力ストリームは、ソースファイルのトークンに分割されていることを字句解析作業、レクサー出力は<キーワード、場合>と同様とすることができます。このセクションでは、識別子、数字、キーワードを認識する。

二つのファイルの合計レクサー:

  • Token.java
  • Lexer.java

トークン

トークンは主にように主レクサーで使用される各トークン、識別するために使用される名前は、識別子、数字を表すNUMBER、構造体キーワードを表す構造体を発現します。

//terminals
NAME, TYPE, STRUCT, CLASS, LP, RP, LB, RB, PLUS, LC, RC, NUMBER, STRING, QUEST, COLON,
RELOP, ANDAND, OR, AND, EQUOP, SHIFTOP, DIVOP, XOR, MINUS, INCOP, DECOP, STRUCTOP,
RETURN, IF, ELSE, SWITCH, CASE, DEFAULT, BREAK, WHILE, FOR, DO, CONTINUE, GOTO,

レクサー

出力ストリームトークンに、入力ストリーム前にレクサーが使用され、入力して読んで

public void advance() {
        lookAhead = lex();
}

レクサーレックスが(にあるメインロジック)を満たす空白や改行はトークンの少なくとも一方の端部を表すまで、inputAdvanceを使用するたびに、入力ストリームから読み込む、(文字列が空白でない場合は二重引用符が満たされる場合空白処理など)した後、分析を始めました。

コードの一部だけが長すぎる、ロジックは非常に単純で切り取り、別のは彼が後でそれに追加、コメントしていない対処していなかった時に書き始めました

for (int i = 0; i < current.length(); i++) {
                length = 0;
                text = current.substring(i, i + 1);
                switch (current.charAt(i)) {
                    case ';':
                        current = current.substring(1);
                        return Token.SEMI.ordinal();
                    case '+':
                        if (i + 1 < current.length() && current.charAt(i + 1) == '+') {
                            current = current.substring(2);
                            return Token.INCOP.ordinal();
                        }

                        current = current.substring(1);
                        return Token.PLUS.ordinal();

                    case '-':
                        if (i + 1 < current.length() && current.charAt(i + 1) == '>') {
                            current = current.substring(2);
                            return Token.STRUCTOP.ordinal();
                        } else if (i + 1 < current.length() && current.charAt(i + 1) == '-') {
                            current = current.substring(2);
                            return Token.DECOP.ordinal();
                        }

                        current = current.substring(1);
                        return Token.MINUS.ordinal();

                        ...
                        ...
}                        

ここでは、システムを入力して、字句解析は終わりました。

作業字句解析ステージは、特定のトークンの入力文字列を変換することです。このステップは、合成文字、主に識別番号、識別子、キーワードおよび他のプロセスのプロセスを識別することです。この部分は、全体のコンパイラ最も簡単な一部である必要があります

また、私のgithubのブログ:https://dejavudwh.cn/

おすすめ

転載: www.cnblogs.com/secoding/p/11367511.html
おすすめ