2013-07-25 14 views
16

私は現在、レキシカルアナライザプログラムで作業しています。私はJavaを使用しています。私はこの問題の答えを探していましたが、今までは何も見つけられませんでした。ここに私の問題だ:字句解析ツールを作成する

入力:

System.out.println ("Hello World"); 

所望の出力:

Lexeme----------------------Token 

System [Key_Word] 

.  [Object_Accessor] 

out [Key_Word] 

. [Object_Accessor] 

println [Key_Word] 

( [left_Parenthesis] 

"Hello World" [String_Literal] 

) [right_Parenthesis] 

; [statement_separator] 

私はので、私はあなたたちはこの上で私を助けることを願って、まだ初心者です。ありがとう。

答えて

33

単純な字句アナライザーを手作業で書くには、ANTLRもドラゴンブックも必要ありません。完全な言語(Javaなど)のための語彙アナライザでさえ、手作業で書くのはあまり複雑ではありません。 ANTLRやいくつかのレックス・バリアントのような産業用ツールを考慮する必要があるかもしれませんが、字句解析の仕組みを学ぶためには、手作業で書くことは有益なことになります。私はあなたがまだ初心者だと言ったので、これが当てはまると仮定しています。

ここでは、Javaで書かれたシンプルな字句アナライザをSchemeライクな言語のサブセットとして使用しています。私はコードが単に字句のストリーム(この場合はList<Token>)に文字のストリーム(この場合はString)を分割するだけでレクサーを見たことがなくても、比較的簡単に理解できると思いますハード。ご質問がある場合、私はより深く説明しようとすることができます。

import java.util.List; 
import java.util.ArrayList; 

/* 
* Lexical analyzer for Scheme-like minilanguage: 
* (define (foo x) (bar (baz x))) 
*/ 
public class Lexer { 
    public static enum Type { 
     // This Scheme-like language has three token types: 
     // open parens, close parens, and an "atom" type 
     LPAREN, RPAREN, ATOM; 
    } 
    public static class Token { 
     public final Type t; 
     public final String c; // contents mainly for atom tokens 
     // could have column and line number fields too, for reporting errors later 
     public Token(Type t, String c) { 
      this.t = t; 
      this.c = c; 
     } 
     public String toString() { 
      if(t == Type.ATOM) { 
       return "ATOM<" + c + ">"; 
      } 
      return t.toString(); 
     } 
    } 

    /* 
    * Given a String, and an index, get the atom starting at that index 
    */ 
    public static String getAtom(String s, int i) { 
     int j = i; 
     for(; j < s.length();) { 
      if(Character.isLetter(s.charAt(j))) { 
       j++; 
      } else { 
       return s.substring(i, j); 
      } 
     } 
     return s.substring(i, j); 
    } 

    public static List<Token> lex(String input) { 
     List<Token> result = new ArrayList<Token>(); 
     for(int i = 0; i < input.length();) { 
      switch(input.charAt(i)) { 
      case '(': 
       result.add(new Token(Type.LPAREN, "(")); 
       i++; 
       break; 
      case ')': 
       result.add(new Token(Type.RPAREN, ")")); 
       i++; 
       break; 
      default: 
       if(Character.isWhitespace(input.charAt(i))) { 
        i++; 
       } else { 
        String atom = getAtom(input, i); 
        i += atom.length(); 
        result.add(new Token(Type.ATOM, atom)); 
       } 
       break; 
      } 
     } 
     return result; 
    } 

    public static void main(String[] args) { 
     if(args.length < 1) { 
      System.out.println("Usage: java Lexer \"((some Scheme) (code to) lex)\"."); 
      return; 
     } 
     List<Token> tokens = lex(args[0]); 
     for(Token t : tokens) { 
      System.out.println(t); 
     } 
    } 
} 

使用例:

~/code/scratch $ java Lexer "" 
~/code/scratch $ java Lexer "(" 
LPAREN 
~/code/scratch $ java Lexer "()" 
LPAREN 
RPAREN 
~/code/scratch $ java Lexer "(foo)" 
LPAREN 
ATOM<foo> 
RPAREN 
~/code/scratch $ java Lexer "(foo bar)" 
LPAREN 
ATOM<foo> 
ATOM<bar> 
RPAREN 
~/code/scratch $ java Lexer "(foo (bar))" 
LPAREN 
ATOM<foo> 
LPAREN 
ATOM<bar> 
RPAREN 
RPAREN 

あなたが1を書かれたり、このような2つの単純なレクサーしたら、この問題が分解する方法のかなり良いアイデアを得るでしょう。次に、lexのような自動化されたツールの使い方を探るのは面白いでしょう。正規表現ベースのマッチャーの背後にある理論はそれほど難しくありませんが、完全に理解するにはしばらく時間がかかります。レクサーを手で書くことは、その学習の動機となり、正規表現を有限オートメーション(最初のNFA、次にNFAからDFAへ)に変換する背後にある理論に潜むよりも、問題を把握するのに役立ちます。すぐに取り込むことが大変で、圧倒されるのは簡単です。

個人的には、ドラゴンの本は良いと非常に徹底していますが、必ずしもアクセス可能ではなく、完全であることを目的としているため、カバレッジは理解しにくいかもしれません。ドラゴンブックを開く前に、他のコンパイラのテキストを試してみてください。ここではかなり良い入門カバレッジ、私見を持っているいくつかの無料の書籍は、以下のとおりです。

http://www.ethoberon.ethz.ch/WirthPubl/CBEAll.pdf

http://www.diku.dk/~torbenm/Basics/

正規表現の実装に関するいくつかの記事(自動化された字句解析は、通常、正規表現を使用しています)

http://swtch.com/~rsc/regexp/

私は役立つことを望みます。がんばろう。

+1

本当にありがとうございます。それは私をたくさん助けました。私はコンピュータサイエンスの学生としてこれらのことを学ぶ必要があるかどうか尋ねたいと思います。それは私の専攻との関連性は何ですか? – KLoverated

+1

レキシカル分析は、解析する前に、コンパイラまたはインタプリタが行う最初のステップです。コンパイラ(とインタプリタ)は非常に便利で、一日中はマシンコードを書く必要があります。私は、CS学生がコンパイラを勉強すべきか否かについてコメントしません。私は彼らが彼ら自身の面白さが面白いと思っています。あなたが興味をそそるプログラマーなら、彼らがどのように機能するのか疑問に思うかもしれません。 CSにはたくさんの話題がありますが、理解の集大成は面白くないかもしれません。それも大丈夫です。つまり、一般的にコンパイラはCSと確かに関係しています。 – spacemanaki

+0

あなたの考えを共有してくれてありがとう、ありがとう。コンパイル/コンパイルのプロセスを勉強することに興味があります。私はいつかそのデザインを夢見ていたからです。私が恐れるのは、私がそれをとてもよく理解できないかもしれないということです。私はまだ私は初心者だと言ったように。私はコンピューター・プログラミングについての知識がなくても、コンピューター・サイエンスを勉強し始めました。私はいつどこから始めるのだろうか。 – KLoverated

2

字句解析は、通常、コンパイラの設計と解析と一緒になるトピックです。何かをコード化しようとする前にそれについてお読みください。このトピックに関する私の好きな本は、コンパイラ設計の良い紹介を提供し、Javaに簡単に変換しそこから移動できるすべてのコンパイラ段階の擬似コードを提供する、Dragonの本です。要約すると、主な考え方は、入力を解析し、有限状態マシンを使用して、入力を特定のクラスに属するトークン(括弧またはキーワード、たとえば、希望の出力に)に分割することです。状態機械の構築プロセスは実際にはこの分析の唯一の難しい部分であり、ドラゴンの本はあなたにこのことについて大きな洞察を与えます。

+0

ありがとうございました!私は本当にあなたの提案に感謝します。概念をよりよく理解するために私が語彙分析を深く研究する必要が本当に大きな必要です。 – KLoverated

5

ANTLR 4は、参考文法Java.g4でこれを正確に行います。言語仕様に従ってUnicodeエスケープシーケンスを処理する程度に応じて、2つのオプションがあります。

編集:この文法で生成されるトークンの名前は、あなたのテーブルと少し異なります。

  • あなたKey_WordトークンがあるIdentifier
  • あなたObject_Accessorトークンがあなたのleft_ParenthesisトークンがDOT
  • ですLPAREN
  • あなたString_LiteralトークンがあるStringLiteral
  • あなたright_ParenthesisトークンがあるRPAREN
  • あなたstatement_separatorトークンは、あなたはJavaでCでLex & BisonまたはAntlrのようなライブラリを使用することができますSEMI
2

です。字句解析は、オートマトンを作成することによって行うことができます。私はあなたに小さな例を与えます:

キーワード(言語)が{'echo', '.', ' ', 'end')の文字列をトークン化する必要があるとします。キーワードでは、言語は次のキーワードのみで構成されています。だから私入力

echo . 
end . 

私のレクサーが出力今

echo ECHO 
SPACE 
. DOT 
end END 
SPACE 
. DOT 

なトークナイザのためのオートマトンを構築するために、私は上図

->(SPACE) (Back) 
| 
(S)-------------E->C->H->O->(ECHO) (Back) 
|    | 
.->(DOT)(Back) ->N->D ->(END) (Back to Start) 

で起動することができなければならない場合は、prolly非常に悪いですが、あなたが開始状態をSと表示していて、今度はEを消費し、他の州に行くと、今度はNまたはとなりますはそれぞれENDECHOになります。このシンプルな有限状態マシンでは、文字を消費し続け、さまざまな状態に到達します。最終的には、ENDを消費した後、ENDの放出状態に達した後にstart州に戻ると、特定のEmit状態になります。あなたのトークナイザに文字ストリームが来る限り、このサイクルは永遠に続きます。無効な文字では、デザインに応じてエラーをスローしたり、無視したりすることができます。

関連する問題