本帖最後由 xuzuning 於 2012-08-27 16:40:33 編輯
最近嘗試做了文法分析的東東,問題較多。
請提建議。代碼放不下,分兩頁。下載地址 http://download.csdn.net/detail/xuzuning/4529066
include 'ttrie.php';class Rule extends TTrie { public $rule = array(); public $savematch = 0; function __construct($s='') {$this->set( array(' ' => 'Separated',"\r\n" => 'set_rule',"\n" => 'set_rule',"\t" => 'Separated','->' => 'Separated','→' => 'Separated','|' => 'set_parallel_rule',));$this->match($s);if($this->rule[0][0] == $this->rule[0][1]) {if(count($this->rule[0]) == 2) $this->rule[0][0] .= "'";else array_unshift($this->rule, array($this->rule[0][0]."'", $this->rule[0][0]));}else {$c = $this->rule[0][0];$n = 0;foreach($this->rule as $r) if($r[0] == $c) $n++;if($n > 1) array_unshift($this->rule, array($this->rule[0][0]."'", $this->rule[0][0]));} } function Separated() { } function set_rule() {$this->rule[] = $this->buffer;$this->buffer = array(); } function set_parallel_rule() {$t = $this->buffer[0];$this->set_rule();$this->buffer[] = $t; }}class Grammar { var $closure = array(); var $first = array(); var $follow = array(); var $rule = array(); var $identifier = array(); var $leay = array(); var $forecast = array(); var $stack = array(); var $ll = 'LL(0)'; var $lr = 'LR(0)'; function __construct($s='') {$p = new Rule($s);$this->rule = $p->rule;$this->set_grammar(); } function set_grammar() {foreach($this->rule as $rule) {if(! in_array($rule[0], $this->identifier)) $this->identifier[] = $rule[0];}foreach($this->rule as $rule) {foreach($rule as $v)if(! in_array($v, $this->identifier) && ! in_array($v, $this->leay))$this->leay[] = $v;}$this->set_first();$this->set_follow();$this->set_closure();$this->set_select();$this->set_forecast(); } function set_first() {foreach($this->rule as $rule) $this->first[$rule[0]] = array();//直接收取 形如U->a…的產生式(其中a是終結符),把a收入到First(U)中foreach($this->rule as $v) {if(in_array($v[1], $this->leay)) $this->first[$v[0]][] = $v[1];}//反覆傳遞 形入U->P1P2P3…Pn的產生式(其中P是非終結符),應先把First(P1)中的全部內容傳送到First(U)中,如果P1中有ε,把First(P2)中的內容傳送到First(U)中,類推直到Pi中無εdo {$t = serialize($this->first);foreach($this->rule as $rule) {for($i=1; $iidentifier)) {$this->first[$rule[0]] = array_unique(array_merge($this->first[$rule[0]], $this->first[$v]));if(! in_array('#', $this->first[$v])) break;}else break;}}}while($t != serialize($this->first)); } function set_follow() {foreach($this->rule as $rule) $this->follow[$rule[0]] = array();//直接收取 形如 …Ua… 的,把a直接收入到Follow(U)中foreach($this->rule as $rule) {for($i=1; $iidentifier) && in_array($rule[$i+1], $this->leay))$this->follow[$rule[$i]][] = $rule[$i+1];}if(in_array($rule[$i], $this->identifier)) $this->follow[$rule[$i]][] = '#';}foreach($this->follow as &$v) if(! $v) $v[] = '#';//直接收取 形如 …UP…(P是非終結符)的,把First(P)中非ε收入到Follow(U)中foreach($this->rule as $rule) {for($i=1; $iidentifier) && in_array($rule[$i+1], $this->identifier)) {$this->follow[$rule[$i]] = array_unique(array_merge($this->follow[$rule[$i]], array_diff($this->first[$rule[$i+1]], array('#'))));}}}//反覆傳遞 形如U->aP的(P是非終結符)或U->aPQ(P,Q為非終結符且Q中含ε),應把Follow(U)中的全部內容傳送到Follow(P)中do {$t = serialize($this->follow);foreach($this->rule as $rule) {$s = $rule[0];$d = end($rule);if(in_array($d, $this->leay)) continue;$p = prev($rule);if(in_array($p, $this->leay)) $this->follow[$d] = array_unique(array_merge($this->follow[$d], $this->follow[$s]));elseif(in_array('#', $this->follow[$d])) $this->follow[$p] = array_unique(array_merge($this->follow[$p], $this->follow[$s]));}}while($t != serialize($this->follow)); } function set_closure() {$shift = array();$this->closure[0][] = array('offs' => 1, 'rule' => 0);for($i=0 ; $i < count($this->closure); $i++) {$cnt = count($this->closure);//構造閉包 closure$ex = array();$j = 0;$tmp = array();do {$size = count($this->closure[$i]);for($j=0; $jclosure[$i]); $j++) {$dfa = $this->closure[$i][$j];$rule = $this->rule[$dfa['rule']];if(isset($rule[$dfa['offs']])) {$ch = $ex[] = $rule[$dfa['offs']];}foreach($this->rule as $r=>$rule) {if(in_array($rule[0], $ex)) {$t = array('offs' => 1, 'rule' => $r);if(!isset($tmp[$r][1])) $this->closure[$i][] = $t;$tmp[$r][1] = 1;}}}}while(count($this->closure[$i]) != $size); //直到不再增大//判斷狀態轉向 go$out = array();foreach($this->closure[$i] as $k=>$dfa) {$rule = $this->rule[$dfa['rule']];if(isset($rule[$dfa['offs']])) {$t = "$dfa[rule],$dfa[offs]";$ch = $rule[$dfa['offs']];$this->closure[$i][$k]['char'] = $ch;if(isset($out[$ch])) $shift[$t] = $out[$ch];if(isset($shift[$t])) {$this->closure[$i][$k]['target'] = $shift[$t];$dfa['offs']++;if(!$this->in_closure($dfa, $this->closure[$shift[$t]])) $this->closure[$shift[$t]][] = $dfa;} else {$cnt = count($this->closure);$this->closure[$i][$k]['target'] = $cnt;$shift[$t] = $cnt;$dfa['offs']++;$this->closure[count($this->closure)][] = $dfa;$out[$ch] = $cnt;}}}//構造狀態轉換表foreach($this->closure[$i] as $k=>$dfa) {if(isset($dfa['target'])) {$v = $dfa['char'];if(in_array($v, $this->identifier)) $this->goto[$i][$v] = $dfa['target'];else {$this->action[$i][$v][] = "S$dfa[target]";$this->request[$i][$v] = $dfa['rule'];}} else {$ch = $this->rule[$dfa['rule']][0];foreach($this->follow[$ch] as $v) {$this->action[$i][$v][] = "r$dfa[rule]";$this->request[$i][$v] = $dfa['rule'];}}}foreach($this->action[$i] as $c=>$v) {$v = array_unique($v);if(count($v) > 1) $this->lr = 'SLR(1)';$this->action[$i][$c] = $v;}} } function in_closure($t, $s) {foreach($s as $r) if($t['offs'] == $r['offs'] && $t['rule'] == $r['rule']) return true;return false;return in_array(serialize($t), array_map('serialize', $s)); } function set_select() {foreach($this->rule as $i=>$rule) {$y = array($rule[1]);if(in_array($y[0], $this->leay)) {if($y[0] != '#') {$this->select[$i] = $y;continue;}}else $y = $this->first[$rule[1]];$x = $this->follow[$rule[0]];//SELECT(X->Y)=(FIRST(Y)-{ε})並FOLLOW(X)$this->select[$i] = array_unique( array_merge(array_diff($y, array('#')), $x) );} } /** * 構造預測性分析表 **/ function set_forecast() {foreach($this->select as $i=>$r) {$c = $this->rule[$i][0];$v = array_reverse(array_slice($this->rule[$i], 1));foreach($r as $k) {$this->forecast[$c][$k][] = $v;}}//檢查衝突foreach($this->forecast as $c=>$r) {foreach($r as $k) {if(count($k) > 1) {$this->ll = 'LL(1)';}}} }
回複討論(解決方案)
續
function ll_start($s) {$t = array();foreach($this->rule as $rule) if($rule[0] == $rule[1]) $t[] = $rule;if($t) {foreach($t as $rule) printf('%s 存在左遞迴', preg_replace('/ /', ' → ', join(' ', $rule), 1));return;}$stack = array('#', key($this->forecast));$i = 0;$step = 1;$timeout = 10 * strlen($s);while($stack && $i < strlen($s) && $timeout--) {$r = end($stack);if($r == $s{$i}) {$msg = $r == '#' ? '成功' : "$r 匹配";}elseif(isset($this->forecast[$r][$s{$i}])) $msg = $r . ' → ' . join(' ', array_reverse($this->forecast[$r][$s{$i}][0]));else $msg = '錯誤';printf("%d%s%s%s", $step++, substr(join('', $stack), -50), substr($s, $i), $msg);if($r == $s{$i}) {array_pop($stack);$i++;}elseif(isset($this->forecast[$r][$s{$i}])) {array_pop($stack);if(current($this->forecast[$r][$s{$i}][0]) != '#')$stack = array_merge($stack, $this->forecast[$r][$s{$i}][0]);}else break;} } function lr_start($s) {$State = array(0); //狀態棧$Symbol = array('#'); //符號棧$i = 0;$step = 1;$timeout = 10 * strlen($s);while($i < strlen($s) && $timeout--) {$ch = $s{$i};$sp = end($State);$msg = substr($s, $i);if(isset($this->action[$sp][$ch]) && $this->action[$sp][$ch][0] == 'r0') $msg = 'acc';if(isset($this->request[$sp][$ch])) $request = preg_replace('/ /', ' → ', join(' ', $this->rule[$this->request[$sp][$ch]]), 1);else $request = 'error';printf("%d%s%s%s%s", $step++, substr(join('', $State), -50), join('', $Symbol), $msg, $request);if(isset($this->action[$sp][$ch]) || isset($this->goto[$sp][$ch])) {$t = isset($this->action[$sp][$ch]) ? $this->action[$sp][$ch][0] : $this->goto[$sp][$ch];$n = substr($t, 1) + 0;if($t{0} == 'r') {for($j=0; $jrule[$n])-1; $j++) {array_pop($State);array_pop($Symbol);}if($n == 0) break;$c = $Symbol[] = $this->rule[$n][0];$State[] = $this->goto[end($State)][$c];}elseif($t{0} == 'S') {$State[] = $n;$Symbol[] = $ch;$i++;}else ;}else break;} } function report($in='') {if($in) $in = trim($in, '#') . '#';echo '';echo '
| 文法 |
';foreach($this->rule as $rule) {echo '
| ';echo preg_replace('/ /', ' → ', join(' ', $rule), 1);echo ' |
';}echo '
';$identifier = $this->identifier;echo '
| 標識符 | ';echo '
' . join(' ', $identifier) . ' |
';echo '
';$leay = array_diff($this->leay, array('#'));$leay[] = '#';echo '
| 終結符 | ';echo '
' . join(' ', $leay) . ' |
';echo '
';echo '
| 標識符 |
推出空 |
FIRST集 |
FOLLOW集 |
';foreach($identifier as $ch) {echo '
| ' . $ch . ' | ';echo '
' . (in_array('#', $this->first[$ch]) ? 'True' : 'false') . ' | ';echo '
' . join(' ', $this->first[$ch]) . ' | ';echo '
' . join(' ', $this->follow[$ch]) . ' | ';echo '
';}echo '
';echo '
';echo '
| 產生式 |
Select集 |
';foreach($this->rule as $i=>$rule) {echo '
';echo '
| ' . preg_replace('/ /', ' → ', join(' ', $rule), 1) . ' | ';echo '
' . join(' ', $this->select[$i]) . ' | ';echo '
';}echo '
';$forecast = $this->forecast;echo '
| 預測性分析表 |
';echo '
';echo '
| |
' . join(' |
', $leay) . ' |
';foreach($identifier as $ch) {echo '
| ' . $ch . ' | ';foreach($leay as $v) {$s = '';if(isset($forecast[$ch][$v])) {foreach($forecast[$ch][$v] as $t) {if($s) $s .= '';$s .= $ch . ' → '. join(' ', array_reverse($t));}if(count($forecast[$ch][$v]) > 1) $s = "$s";}else $s .= ' ';echo '
' . $s . ' | ';}echo '
';}echo '
|
';echo '
';if($in) {echo '
| 測試字串 | ';echo '
' . trim($in, '#') . ' |
';echo '
';echo '
| 步驟 |
分析棧 |
剩餘字元 |
產生式或匹配 |
';$this->ll_start($in);echo '
';}echo '
| |
';echo '
| ' . $this->lr . '文法分析 |
|
';echo '
';echo '
';echo '
| 狀態 |
Action |
Goto |
DFA |
';echo '
| ' . join(' |
', $leay) . ' |
'. join(' |
', $identifier) . ' |
';foreach($this->action as $i=>$item) {echo "
| $i | ";foreach($leay as $v) {$s = isset($item[$v]) ? join(',', $item[$v]) : ' ';if(strpos($s, ',')) $s = "$s";echo "
$s | ";}foreach($identifier as $v) {$s = isset($this->goto[$i][$v]) ? $this->goto[$i][$v] : ' ';echo "
$s | ";}echo '
' . $this->showDFA($i) .' | ';echo '
';}echo '
';if($in) {echo '
| 測試字串 | ';echo '
' . trim($in, '#') . ' |
';echo '
';echo '
| 步驟 |
狀態棧 |
符號棧 |
剩餘字元 |
產生式 |
';$this->lr_start($in);echo '
';} } function showDFA($i) {$res = array();foreach($this->closure[$i] as $dfa) {$rule = $this->rule[$dfa['rule']];array_splice($rule, $dfa['offs'], 0, '·');array_splice($rule, 1, 0, '→');if(isset($dfa['target'])) $rule[] = " [$dfa[target]]";$res[] = join('', $rule);}return join('; ', $res); }}for($i=1; $i<=count(glob('*.txt')); $i++) echo " $i";$n = current($_GET) or $n = 1;$S = '';include "$n.txt";$p = new grammar($G);$p->report($S);
ttrie.php
$v) $this->set($k, $v);return;}$p = count($this->dict);$cur = 0; //當前節點號foreach(str_split($word) as $c) {if (isset($this->dict[$cur][$c])) { //已存在就下移$cur = $this->dict[$cur][$c];continue;}$this->dict[$p]= array(); //建立新節點$this->dict[$cur][$c] = $p; //在父節點記錄子節點號$cur = $p; //把當前節點設為新插入的$p++;}$this->dict[$cur]['acc'] = $action; //一個詞結束,標記葉子節點 } function match($s) {$ret = array();$cur = 0; //當前節點,初始為根節點$i =& $this->input; //字串當前位移$p =& $this->backtracking; //字串回溯位置$s .= "\0"; //附加結束符$len = strlen($s);$buf = '';while($i < $len) {$c = $s{$i};if(isset($this->dict[$cur][$c])) { //如果存在$cur = $this->dict[$cur][$c]; //轉到對應的位置if(isset($this->dict[$cur][$s[$i+1]])) {//檢查下一個字元是否也能匹配,長度優先$i++;continue;}if(isset($this->dict[$cur]['acc'])) { //是葉子節點,單詞匹配!if($buf) {$this->buffer[] = $buf;$buf = '';}if($this->savematch) $this->buffer[] = substr($s, $p, $i - $p + 1); //取出匹配位置和匹配的詞$ar = explode(',', $this->dict[$cur]['acc']);call_user_func_array( array($this, array_shift($ar)), $ar );$p = $i + 1; //設定下一個回溯位置$cur = 0; //重設當前節點為根節點}} else { //不匹配$buf .= $s{$p}; //substr($s, $p, $i - $p + 1); //儲存未匹配位置和未匹配的內容$cur = 0; //重設當前節點為根節點$i = $p; //把當前位移設為回溯位置$p = $i + 1; //設定下一個回溯位置}$i++; //下一個字元}if(trim($buf, "\0")) $this->buffer[] = trim($buf, "\0"); } function __call($method, $param) {if($this->debug) printf("位移:%d 回溯:%d\n", $this->input, $this->backtracking); }}
相關資料檔案
1.txt
a HH -> a M dH -> dM -> A bM -> #A -> a MA ->eTXT;$S = 'aaaebbd#';
2.txt
EE -> E + TE -> TT -> T * FT -> FF -> ( E )F -> iTXT;$S = 'i*(i+i)#';
3.txt
4.txt 5.txt 6.txt 7.txt 8.txt 有興趣,可惜編譯原理完全忘記了。
弱弱地求教下:進階語言做編譯器,效率會不會太低下了。。
寫點文字,畫點圖就更好了。看代碼,太費勁
寫點文字,畫點圖就更好了。看代碼,太費勁
你把代碼複製下來運行一下
我打包上傳了的,但一時看不見
偶都不知道編譯原理是啥
有時間慢慢研讀
嘮叨是科學家
我們是一線生產工人
進來站隊圍觀科學家研究
偶都不知道編譯原理是啥
有時間慢慢研讀
好東西,下午載後,好好看看
編譯原理純粹沒學過!
回去翻翻 編譯原理 ,清華大學出版社出版的,看看就知道了
詞法分析,文法分析,語義分析....剛畢業還有點印象的飄過
yacc.lex工具很強大
最好有注釋
最好有注釋
你這個太學院派了。
貌似 你弄的複雜了
可以依這lua的代碼來畫瓢。他的那段代碼很簡潔的。只是層級有點多好繞!
這語言也太進階了, 不過效能上不用擔心.
呃,求注釋。
牛,看不懂
先描述一下你的文法,要不怎麼叫人看得懂?
關注下!
多謝提供
學習了
不?喔!
快要學了,會多多關注
站內大神果然很多,小弟初到貴寶地,望大家多多關照
不懂編譯,所以關注
懂編譯原理的都很強悍!!
不好意思,回帖賺積分。
當時編譯的課程設計就是這個,你要是早兩年放上來就好了,我就不用自己做了嘛~
20分直接算入總分呀有木有有木有~
牛,看不懂
對頭。
先描述一下你的文法,要不怎麼叫人看得懂?
支援的是這句。上一樓,引用錯了。
要不要先描述下文法啊,外行表示直接從代碼裡猜鴨梨很大。。
朋友 不是中國人吧?
形式語言正學呢,剛接觸文法,編譯原理下學期才學呢
看了大半?小??於明白了,做??????注....
完全看不懂
下載次數:33
回複次數:45
沒有人對輸出的結果提出異議
沒有人對程式的改進提出建議
沒有人對演算法上的問題提出質疑
使用的資料都是網上找的(當然也是抄自教科書)
程式中未對出現的衝突做任何處理(只是羅列)
可見這些文法例子是經過精心設計的,怎麼弄都不會錯
只看了LL(1)
1中衝突選擇非#
2,3,8 為什麼不消除左遞迴?
5,7 要處理提取因子.
LR晚上再看。
編譯原理真心看不懂
只看了LL(1)
1中衝突選擇非#
2,3,8 為什麼不消除左遞迴?
5,7 要處理提取因子.
LR晚上再看。 謝謝
其實我也算是初學,以前看過,根本就沒弄明白
這回算是明白了點,對消除左遞迴、提取因子的演算法實現這在研究。所以程式你並沒有
PHP哦,,,,,
想想大學那會這門課程還考的滿分。現在都忘光了
去複習下
看了看,跟自己當年寫的1+1=2完全不是那麼回事啊!
挺複雜的演算法,沒想到做了3年業務系統還有一部分看不懂
得好好研究演算法了
完全看不懂。研究中。。
我最近也在自己做一個很簡易的兩步編譯器,樓主多交流多溝通啊
程式是直接運行Grammar.php檔案吧,運行出來就是。我表示看不懂,壓力很大。
我就看到了$
超強悍的詞法分析工具:flex
超強悍的文法分析工具:bison,BYACC。
PHP是解釋型的程式設計語言,本來文法就是模擬C語言,做文法分析太慢。
直接上C語言+flex+BISON吧
同用LR分析法寫過文法分析器的路過,樓主你的程式對較大的文法支援的怎麼樣,速度怎麼樣?
我這有個自己寫的SQL語言的部分文法,你要不要試一試?
演算法很重要 在學習!
好東西,下午載後,好好看看
現在剛開始接觸php,努力向上爬
好眼熟的東西 不過似乎早就還給老師了- -
沒看懂
拿php寫,服了
貌似好高深的樣子,php小白,就會做簡單的東西,mark一個,學習下
哎,這東西在大學上了一個學期之後就還給老師了
完全眼花@@
完全眼花@@
太複雜拉,看不懂
哈哈哈,樓主整好了給個串連唄,線上編譯。
沒學過,暫時也用不到。以後再說吧,精力有限。
運行結果看著很熟悉,可就是看不懂。。。
仰視中……
寫多點注釋嘛,這樣看起來很累的
沒看明白。
你是基於什麼演算法實現的?
嘮叨前輩你好