本帖最后由 xuzuning 于 2012-08-27 16:40:33 编辑

解决方案 »

  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('<tr><td colspan=4>%s 存在左递归</td></tr>', 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("<tr><td>%d</td><td>%s</td><td>%s</td><td>%s</td></tr>", $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("<tr><th>%d</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>", $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; $j<count($this->rule[$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 '<style>table {font-size:10pt</style>'; echo '<table ><tr><td><b>文法</b></td></tr>';
    foreach($this->rule as $rule) {
    echo '<tr><td>';
    echo preg_replace('/ /', ' → ', join(' ', $rule), 1);
    echo '</td></tr>';
    }
    echo '</table>'; $identifier = $this->identifier;
    echo '<table><tr><td><b>标识符</b></td>';
    echo '<td>' . join(' ', $identifier) . '</td></tr>';
    echo '</table>'; $leay = array_diff($this->leay, array('#'));
    $leay[] = '#';
    echo '<table><tr><td><b>终结符</b></td>';
    echo '<td>' . join(' ', $leay) . '</td></tr>';
    echo '</table>'; echo '<table width=60% border=1><tr><th>标识符</th><th>推出空</th><th>FIRST集</th><th>FOLLOW集</th></tr>';
    foreach($identifier as $ch) {
    echo '<tr><td>' . $ch . '</td>';
    echo '<td>' . (in_array('#', $this->first[$ch]) ? 'True' : 'false') . '</td>';
    echo '<td>' . join(' ', $this->first[$ch]) . '</td>';
    echo '<td>' . join(' ', $this->follow[$ch]) . '</td>';
    echo '</tr>';
    }
    echo '</table>'; echo '<table width=100%><tr><td>&nbsp;</td></tr><tr><th>' . $this->ll . '文法分析</th></tr></table>'; echo '<table width=60% border=1><tr><th>产生式</th><th>Select集</th></tr>';
    foreach($this->rule as $i=>$rule) {
    echo '<tr>';
    echo '<td>' . preg_replace('/ /', ' → ', join(' ', $rule), 1) . '</td>';
    echo '<td>' . join(' ', $this->select[$i]) . '</td>';
    echo '</tr>';
    }
    echo '</table>'; $forecast = $this->forecast;
    echo '<table width=60%><tr><td><b>预测分析表</b></td></tr>';
    echo '<tr><td><table width=100% border=1>';
    echo '<tr><th>&nbsp;</th><th>' . join('</th><th>', $leay) . '</th></tr>';
    foreach($identifier as $ch) {
    echo '<tr><td>' . $ch . '</td>';
    foreach($leay as $v) {
    $s = '';
    if(isset($forecast[$ch][$v])) {
    foreach($forecast[$ch][$v] as $t) {
    if($s) $s .= '<br />';
    $s .= $ch . ' → '. join(' ', array_reverse($t));
    }
    if(count($forecast[$ch][$v]) > 1) $s = "<font color=red>$s</font>";
    }else $s .= '&nbsp;';
    echo '<td>' . $s . '</td>';
    }
    echo '<tr>';
    }
    echo '</table></td></tr>';
    echo '</table>'; if($in) {
    echo '<table><tr><th>测试字符串</th>';
    echo '<td>' . trim($in, '#') . '</td></tr></table>';
    echo '<table width=100% border=1>';
    echo '<tr><th>步骤</th><th>分析栈</th><th>剩余字符</th><th>生成式或匹配</th></tr>';
    $this->ll_start($in);
    echo '</table>';
    } echo '<table width=100%><tr><td>&nbsp;</td></tr>';
    echo '<tr><th>' . $this->lr . '文法分析<th></tr></table>';
    echo '<table><tr><th>状态转移表</th></tr></table>';
    echo '<table width=100% border=1>';
    echo '<tr><th rowspan=2>状态</th><th colspan=' . count($leay) . '>Action</th><th colspan=' . count($identifier) . '>Goto</th><th rowspan=2>DFA</th></tr>';
    echo '<tr><th>' . join('</th><th>', $leay) . '</th><th>'. join('</th><th>', $identifier) . '</th></tr>';
    foreach($this->action as $i=>$item) {
    echo "<tr><td>$i</td>";
    foreach($leay as $v) {
    $s = isset($item[$v]) ? join(',', $item[$v]) : '&nbsp;';
    if(strpos($s, ',')) $s = "<font color=red>$s</font>";
    echo "<td>$s</td>";
    }
    foreach($identifier as $v) {
    $s = isset($this->goto[$i][$v]) ? $this->goto[$i][$v] : '&nbsp;';
    echo "<td>$s</td>";
    }
    echo '<td>' . $this->showDFA($i) .'</td>';
    echo '</tr>';
    }
    echo '</table>'; if($in) {
    echo '<table><tr><th>测试字符串</th>';
    echo '<td>' . trim($in, '#') . '</td></tr></table>';
    echo '<table width=100% border=1>';
    echo '<tr><th>步骤</th><th>状态栈</th><th>符号栈</th><th>剩余字符</th><th>生成式</th></tr>';
    $this->lr_start($in);
    echo '</table>';
    }
      }
      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 " <a href=?id=$i>$i</a>";
    $n = current($_GET) or $n = 1;
    $S = '';
    include "$n.txt";$p = new grammar($G);
    $p->report($S);ttrie.php<?php
    class TTrie {
      protected $buffer = array();
      protected $dict = array( array() );
      protected $input = 0; //字符串当前偏移
      protected $backtracking = 0; //字符串回溯位置
      public $debug = 0;
      public $savematch = 1;  function set($word, $action='') {
    if(is_array($word)) {
    foreach($word as $k=>$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);  }
    }
      

  2.   

    相关数据文件
    1.txt<?php
    $G =<<< TXT
    S -> a H
    H -> a M d
    H -> d
    M -> A b
    M -> #
    A -> a M
    A ->eTXT;$S = 'aaaebbd#';
    2.txt<?php
    $G =<<< TXT
    E ->  E
    E ->  E + T
    E ->  T
    T ->  T * F
    T ->  F
    F ->  ( E )
    F ->  iTXT;$S = 'i*(i+i)#';3.txt<?php
    $G =<<< TXT
    E → ( L )
    E → a
    L → L , E
    L → ETXT;$S = '((a),a,(a,a))';
    4.txt<?php
    $G =<<< TXT
    S' → S
    S → A B
    S → b C
    A → #
    A → b
    B → #
    B → a D
    C → A D
    C → b
    D → a S
    D → cTXT;$zS = 'baac';5.txt<?php
    $G =<<< TXT
    S → i S e S | i S | aTXT;$S = 'iiaea#';
    6.txt<?php
    $G =<<< TXT
    S → a A
    S → d
    A → b A S
    A → #TXT;$S = 'abbb';
    7.txt<?php
    $G =<<< TXT
    S' → S
    S → a S
    S → b S
    S → aTXT;$S = 'aabba';
    8.txt<?php
    $G =<<< TXT
    S → A a | b
    A → A c | S d | #TXT;$G1 =<<< TXT
    S → A a | b
    A → b d A
    A → c A | a d A | # TXT;$S = 'bdadcca';
      

  3.   

    这玩意。。 LL, LR 分析早忘了 帮你顶下
      

  4.   

    你这个太学院派了。貌似 你弄的复杂了可以依这lua的代码来画瓢。他的那段代码很简洁的。只是层级有点多好绕!
      

  5.   

    只看了LL(1)
    1中冲突选择非#
    2,3,8 为什么不消除左递归?
    5,7 要处理提取因子.LR晚上再看。