590 likes | 812 Views
字句解析. 字句解析( LexicalAnalysis ) 文字列を単語に区切る 字句解析プログラム. 字句解析. プログラミング言語 区切り記号を使って簡単に行える. 自然言語 ◎ 英語のように分かち書きされているものはかなり簡単 ◎ 日本語などは非常に困難 例:きんきだいがく → 近畿+大学 近畿だ+医学. 字句解析. 入力文字列 write(ab,12) ↓ Symbol [日本語] WRITE 左括弧 変数名 コンマ 整数 右括弧 Symbol [ English]
E N D
字句解析 字句解析(LexicalAnalysis) 文字列を単語に区切る 字句解析プログラム
字句解析 • プログラミング言語 区切り記号を使って簡単に行える. • 自然言語 ◎英語のように分かち書きされているものはかなり簡単 ◎日本語などは非常に困難 例:きんきだいがく → 近畿+大学 近畿だ+医学
字句解析 • 入力文字列 write(ab,12) ↓ • Symbol[日本語] WRITE 左括弧 変数名 コンマ 整数 右括弧 • Symbol[English] WRITELPARENNAMECOMMAINTEGERRPAREN • トークンの文字列(name,value) write ( ab , 12 )
トークンの種類 • 区切り記号: セミコロン,コンマ,,(,),{,},[,] • 演算子:==,!=,<,>,<=,>=,&&,||,!,+,-,*,/,%,= • 名前: ID[変数名,手続き名] • 定数: 整数,文字 • キーワード:if,int,main,readint, readchar,while,writeint,writechar
ADD + SUB - MUL * DIV / MOD % ASSIGN = SEMICOLON ; LPAREN ( RPAREN ) LBRACE{ RBRACE} LBRACKET [ RBRACKET] COMMA , 英語
①SUB - ②MUL * ③LPAREN ( ④RBRACE { ⑤LBRACKET[ ★Symbol[English] 誤りはどれ?
①EQUAL == ②INC ++ ③DEC ーー ④ASSIGN== ⑤ADD + ★Symbol[English] 誤りはどれ?
空白文字を読み飛ばす • 空白文字=' ' '\t' '\n' • write ( ab , 12 ) ↓ 空白文字を読み飛ばす • write(ab,12)
プログラム→トークンタイプ proceduremain; var ei,su,go; procedure gokeiKeisan; begin go := ei+ su end; begin ei := 70; su := 50; call gokeiKeisan end . 変数名 procedure名 整数値
最長一致 • 字句解析の基本=最長一致 =一番長い字句候補を選ぶ • 自然言語の例 きょうとふ → 京都府? → 今日+戸+負? • 最長の 京都府 が正解みたい
最長一致で解決できない場合 • (東京特許許可局) • とうきょうとっきょ • 最長一致=東京都+っきょ • 2文節最長一致方式 • 東京+特許 → 正解みたい
最長一致(コンパイラの場合) 1.a<=b は a<=b でなく, a<=b と字句解析される. 2.+++ は +++ と字句解析される 3.+++ (空白をいれる)は +++ と字句解析される. 4.-++ は -++ と字句解析される.(-+というトークンがないため) 5.== は == でなく == と字句解析される. 6.mainab=c は mainab=c でなく, mainab=c と 字句解析される. mainabという変数名
①+++ INCADD ②ーー= DECASSIGN ③+ーー ADDDEC ④+ ++ INCADD ⑤=== EQUALASSIGN ★ 最長一致 誤りはどれ?
a+++b (=a+++ b) の構文木 <exp> <term> “+” <term> <factor> <factor> <unsigned> <unsigned> NAME “++” NAME “a” “b”
a+++b の構文木 空白あり <exp> <term> “+” <term> <factor> <factor> <unsigned> <unsigned> NAME “++” NAME “a” “b”
字句解析オートマトン +1 + セミコロン 足し算 等しい = 代入 + ; = 0-9 INT 0-9 英字,数字,_ a-z,A-Z,_ NAME
1文字=1トークン ; セミコロン
最長一致=先読みして,決定1文字? 2文字?最長一致=先読みして,決定1文字? 2文字? =以外で確定 何でも 代入 等しい = = 1文字先読み
文字 'a' ← '\n' ← やらなくてよい? 'a' の値 文字コード=>整数 value=(int)'a'
文字 '?' 何でも n,t,.. ¥ ' 文字 ' ¥以外 シングルクォート シングルクォート
最長一致(名前) 英字,数字,_ NAME a-z,A-Z,_ 先読み:英字,数字,_以外 で確定
数(ゼロと非ゼロ) ゼロ 0 0-9 1-9 INTEGER 数字以外 で確定
区切り方 空白は飛ばす 先頭が数字,数字の続き, 次が改行(数字でない) • ab=c12++32 先頭が+,次も+ 先頭が=, 次の文字が英字(=でない) 先頭が英字, 英数字の続き, 次の文字が空白(英字でない) 先頭が英字, 英数字の続き, 次の文字が=(英数字でない)
プログラム:記号部分 • 現在位置の文字:currentChar • 次の位置の文字:nextChar • ++ と + の区別 • if(currentChar==‘+‘) { if(nextChar==‘+‘) { token=new Token(INC); nextChar(); } else token=new Token(ADD); }
プログラム:変更例 • 現在位置の文字:currentChar • 次の位置の文字:nextChar • -= と - の区別 • if(currentChar==‘-‘) { if(nextChar==‘=‘) { token=new Token(SUBASSIGN); nextChar(); } else token=new Token(SUB); }
文字 '?''\?' if(currentChar == '\'') { char ch = nextChar(); if(ch=='\\') {ch=nextChar(); if(nextChar=='\'') { if(ch=='n') value=13; else if (ch=='t') value=11; .... } } else if ( nextChar == '\'') value = (int) ch; nextChar(); ttype = CHARACTER; } '\n'など 訂正
最長一致プログラム:変数名と予約語,大文字のみ最長一致プログラム:変数名と予約語,大文字のみ • 現在位置の文字:currentChar • 変数名トークンの文字列:name • if(Character.isUpperCase(currentChar) ) { name=“”; while(Character.isUpperCase(currentChar)) { name+=currentChar; nextChar(); } } 変数名の終わりまで取り込む
変更例,大文字か小文字 • 現在位置の文字:currentChar • 変数名トークンの文字列:name • if(Character.isLowerCase(currentChar)|| Character.isUpperCase(currentChar)) { name=“”; while(Character.isLowerCase(currentChar)|| Character.isUpperCase(currentChar)) { name+=currentChar; nextChar(); } }
字句解析(2) 字句解析プログラム
トークンクラス class Token { Symbol symbol; int value; String name; } トークンの種類 MAIN IF WHILE ADD SUB MUL LPAREN RBRACE 整数トークン 整数値 変数トークン 変数名
プログラムの構造 ソースファイル nextChar()
方法(1)currentChar のみを用いて • 現在位置の文字:currentChar • <= と < を区別するプログラム • if(currentChar==‘<‘) { nextChar(); if(currentChar==‘=‘) { token=new Token(Symbol.GE); nextChar(); } else token=new Token(Symbol.GT); }
★ 記号 • 現在位置の文字:currentChar • += と + を区別するプログラム • if(currentChar==‘+‘) { nextChar(); if(currentChar==‘=‘) { token=new Token(Symbol.ADDASSIGN); nextChar(); } else token=new Token(Symbol.ADD); }
整数 • 現在位置の文字:currentChar • 整数トークンの文字列(数字列):name • 整数値 value • if(currentChar == '0' ) {name="0";value=0; token=new Token(Symbol.INTEGER,value)} • if(Character.isDigit(currentChar)) { name=“”; while(Character.isDigit(currentChar)) { name+=currentChar; nextChar(); } value=Integer.parseInt(name); token=new Token(Symbol.INTEGER,value); } 終わりの数字 まで取り込む
計算により整数値を求める • 現在位置の文字:currentChar • 整数トークンの文字列(数字列):name • 整数値 value • if(currentChar == '0' ) {name="0";value=0; token=new Token(Symbol.INTEGER,value)} • if(Character.isDigit(currentChar)) { value=0; while(Character.isDigit(currentChar)) { value = value * 10 + Character.digit(currentChar, 10); nextChar(); } token=new Token(Symbol.INTEGER,value); }
変数名と予約語 • 予約語は変数名として使えない main=1+2; と書いてはいけない • 文字列を取り出したとき,予約語と一致していれば予約語,そうでなければ変数名 • whileは予約語 • while1,whilea,whilは変数名
変数名 と 予約語 • 整数とおなじように文字列を最長一致で取り出す. • 文字列:name • main などと一致すれば mainトークンなど • そうでない時は 変数名 • if(Character.isLetter(currentChar)) { • name=“”; while(Character.isLetterOrDigit(currentChar)) { name+=currentChar; } token=new Token(Symbol.NAME,name); if(name.equals(“main”)) token=new Token(Symbol.MAIN); else if(name.equals(“read”)) token=new Token(Symbol.READ); ……..
★変数名 と 予約語 • 整数とおなじように文字列を最長一致で取り出す. • 文字列:name • main などと一致すれば mainトークンなど • そうでない時は 変数名 • if(Character.isLetter(currentChar)) { • name=“”; while(Character.isLetterOrDigit(currentChar)) { name+=currentChar; } token=new Token(Symbol.NAME,name); if(name.equals(“main”)) token=new Token(Symbol.MAIN); else if(name.equals(“read”)) token=new Token(Symbol.READ); ……..
プログラミング方法(2) • 次の文字によって,状態推移する?しない? ↓ • nextChar の導入(currentCharの次の文字) • char lookAhead(){ return nextChar; }
lookAhead()を用いて • 現在位置の文字:currentChar • 次の文字:lookAhead() • <= と < を区別するプログラム • if(currentChar==‘<‘) { if(lookAhead()==‘=‘) { token=new Token(Symbol.LE); nextChar(); } else token=new Token(Symbol.LT); }
プログラミング方法(2-2) • ++と+の区別 • if(currentChar=='+'){ if(nextChar=='+'){ token=new Token(Symbol.INC); nextChar(); } else token=new Token(Symbol.ADD); }
プログラミング方法(2-3) • 整数 • String str; if(Character.isDigit(currentChar)){ str=""+currentChar; while(Character.isDigit(nextChar)){ nextChar(); str += currentChar; } }
プログラミング方法(3ー1) • 字句解析:有限オートマトン風 ↓ • 正規表現を利用する ↓ • 文字単位でなく,行単位で解析する. 行の残り = line
プログラミング方法(3ー2) • ++ と + の区別 • if(line.charAt(0)=='+' || line.cahrAt(1)=='+'){ token=new Token(Symbol.INC); line=line.substring(2); } else if(line.charAt(0)=='+'){ token=new Token(Symbol.ADD); line=line.substring(1); }
プログラミング方法(3ー3) • 変数名 • String str; elseif(Character.isLetterOrDigit(ch0)||ch0=='_'){ substr=line.split("\\W"); str=substr[0]; line=line.substring(str.length()); if(str.equals("main")) token=new Token(Symbol.MAIN); else token=new Token(Symbol.NAME,str); } 非単語文字 \w=単語文字 a-zA-Z0-9_
FileScannerクラス コンストラクタ:ファイル名を与える. ファイル終了:closeFile() ソースの一行lineをget:currentLine() 一文字切り出し:nextChar() 次の文字を返す:lookAhead() 行番号を返す:currentLineNumber()
NULL MAIN IF INT EQUAL GREAT ADD SUB ASSIGN LPAREN RPAREN ・ ・ ・ ・ enumSymbol(トークンのタイプ)
Tokenクラス • コンストラクタ: • LPARENなど:Symbolのみを与える • 整数など:Symbolと整数値 • 変数名:Symbolと名前の文字列 • Symbolのチェック:checkSymbol(symbol)
LexicalAnalyzer • コンストラクタ: 1.ソースファイル開く sourceFileScanner=newFileScanner(fileName); 2.一文字読み込む nextChar(); • currentChar:現在位置の文字とする • token:nextToken()が返すトークン
字句切り出し:nextToken()の全体像 Token nextToken(){ // 空白文字を読み飛ばす while(currentChar==' ' || currentChar=='\n' || currentChar=='\t') nextChar(); //EOF if(currentChar=='\0') return new Token(Symbol.EOF); ・ ・ ・ ・ ・ ・ トークンの決定 ・ ・ ・ ・ ・ // 決まらないときはNULLとする.(エラーとする?) elsetoken= new Token(Symbol.NULL); // 次の文字を読み込む nextChar(); // 決定したトークンを返す returntoken; }