Created
July 11, 2013 09:07
-
-
Save 123jimin/5973864 to your computer and use it in GitHub Desktop.
The parser I made (100% hand-coded) back in 2011.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
require_once('echoErr.php'); | |
require_once('token.php'); | |
require_once('complex.php'); | |
require_once('constant.php'); | |
require_once('function.php'); | |
require_once('evalInfo.php'); | |
mb_internal_encoding('UTF-8'); | |
set_time_limit(5); | |
$EVALEQ_ZERO = new Complex(); | |
$INTEGER = 'I'; | |
$IMAGINARY = 'i'; | |
$FLOAT = 'F'; | |
$OPERATOR = 'O'; | |
$PAREN = 'P'; | |
$STRING = 'S'; | |
$COMMA = 'C'; | |
$EXCLMARK = '!'; | |
$PERCENT = '%'; | |
$NULL = 'X'; | |
$QUOTE = 'Q'; | |
$NT_S = 'A'; | |
$NT_F = 'B'; | |
$NT_P = 'D'; | |
$NT_AP = 'E'; | |
$NT_E0 = '0'; | |
$NT_E0a = 'a'; | |
//E1 was originally for %(mod), but %'s usage was changed. | |
$NT_E2 = '2'; | |
$NT_E2a = 'c'; | |
$NT_E3 = '3'; | |
$NT_E3a = 'd'; | |
$NT_E4 = '4'; | |
$NT_E4a = 'e'; | |
$NT_E5 = '5'; | |
$NT_E5a = 'f'; | |
$NT_E6 = '6'; | |
$NT_E7 = '7'; | |
$NT_LS = 'r'; | |
function toName($s){ | |
switch($s){ | |
case 'I': return '정수'; | |
case 'i': return '허수 i'; | |
case 'F': return '실수'; | |
case 'O': return '연산자'; | |
case '+': return '더하기'; | |
case '-': return '빼기'; | |
case '*': return '곱하기'; | |
case '/': return '나누기'; | |
case 'P': return '괄호'; | |
case '(': return '여는 괄호'; | |
case ')': return '닫는 괄호'; | |
case 'Q': return '따옴표'; | |
case '"': return '따옴표(")'; | |
case "'": return "따옴표(')"; | |
case 'A': return '식'; | |
case 'B': return '함수'; | |
case 'D': return '함수 인자 앞부분'; | |
case 'E': return '함수 인자 뒷부분'; | |
case '0': return '덧셈 또는 뺄셈 앞부분'; | |
case 'a': return '덧셈 또는 뺄셈 뒷부분'; | |
case '2': return '생략된 곱셈 앞부분'; | |
case 'c': return '생략된 곱셈 뒷부분'; | |
case '3': return '곱셈 또는 나눗셈 앞부분'; | |
case 'd': return '곱셈 또는 나눗셈 뒷부분'; | |
case '4': return '지수 앞부분'; | |
case 'e': return '지수 뒷부분'; | |
case '5': return '팩토리얼 식'; | |
case 'f': return '팩토리얼 식의 느낌표'; | |
case '6': return '단항식'; | |
case '7': return '퍼센트'; | |
case 'r': return '문자열'; | |
default: return $s; | |
} | |
} | |
$GRAMMAR = array( | |
array($NT_S,$NT_E0), | |
array($NT_F,$STRING,$NT_P), | |
array($NT_P,$NT_E0,$NT_AP), | |
array($NT_AP,')'), | |
array($NT_AP,$COMMA,$NT_P), | |
array($NT_E0,$NT_E3,$NT_E0a), | |
array($NT_E0a,'+',$NT_E3,$NT_E0a), | |
array($NT_E0a,'-',$NT_E3,$NT_E0a), | |
array($NT_E0a,$NULL), | |
array($NT_P,')'), | |
array($NT_E7,$PERCENT), | |
array($NT_E7,$NULL), | |
array($NT_E2,$NT_E4,$NT_E2a), | |
array($NT_E2a,'.',$NT_E4,$NT_E2a), | |
array($NT_E2a,$NULL), | |
array($NT_E3,$NT_E2,$NT_E3a), | |
array($NT_E3a,'*',$NT_E2,$NT_E3a), | |
array($NT_E3a,'/',$NT_E2,$NT_E3a), | |
array($NT_E3a,$NULL), | |
array($NT_E4,$NT_E5,$NT_E4a), | |
array($NT_E4a,'^',$NT_E5,$NT_E4a), | |
array($NT_E4a,$NULL), | |
array($NT_E5,$NT_E6,$NT_E5a), | |
array($NT_E5a,$EXCLMARK,$NT_E5a), | |
array($NT_E5a,$NULL), | |
array($NT_E6,$INTEGER,$NT_E7), | |
array($NT_E6,$FLOAT,$NT_E7), | |
array($NT_E6,$IMAGINARY,$NT_E7), | |
array($NT_E6,'(',$NT_E0,')',$NT_E7), | |
array($NT_E6,$NT_F,$NT_E7), | |
array($NT_E6,"'",$NT_LS,"'"), | |
array($NT_E6,'"',$NT_LS,'"'), | |
array($NT_LS,$STRING), | |
array($NT_LS,$NULL) | |
); | |
$TABLE['index'] = array($INTEGER,$FLOAT,$IMAGINARY,'+','-',$PERCENT,'.','*','/','^','(',')',$STRING,$COMMA,$EXCLMARK,$NULL,"'",'"'); | |
$TABLE[$NT_S] = array( 0, 0, 0,-1,-1,-1,-1,-1,-1,-1, 0,-1, 0,-1,-1,-1, 0, 0); | |
$TABLE[$NT_F] = array(-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, 1,-1,-1,-1,-1,-1); | |
$TABLE[$NT_P] = array( 2, 2, 2,-1,-1,-1,-1,-1,-1,-1, 2, 9, 2,-1,-1,-1, 2, 2); | |
$TABLE[$NT_AP] = array(-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, 3,-1, 4,-1,-1,-1,-1); | |
$TABLE[$NT_E0] = array( 5, 5, 5,-1,-1,-1,-1,-1,-1,-1, 5,-1, 5,-1,-1,-1, 5, 5); | |
$TABLE[$NT_E0a] = array(-1,-1,-1, 6, 7,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, 8,-1,-1); | |
$TABLE[$NT_E2] = array(12,12,12,-1,-1,-1,-1,-1,-1,-1,12,-1,12,-1,-1,-1,12,12); | |
$TABLE[$NT_E2a] = array(-1,-1,-1,-1,-1,-1,13,-1,-1,-1,-1,-1,-1,-1,-1,14,-1,-1); | |
$TABLE[$NT_E3] = array(15,15,15,-1,-1,-1,-1,-1,-1,-1,15,-1,15,-1,-1,-1,15,15); | |
$TABLE[$NT_E3a] = array(-1,-1,-1,-1,-1,-1,-1,16,17,-1,-1,-1,-1,-1,-1,18,-1,-1); | |
$TABLE[$NT_E4] = array(19,19,19,-1,-1,-1,-1,-1,-1,-1,19,-1,19,-1,-1,-1,19,19); | |
$TABLE[$NT_E4a] = array(-1,-1,-1,-1,-1,-1,-1,-1,-1,20,-1,-1,-1,-1,-1,21,-1,-1); | |
$TABLE[$NT_E5] = array(22,22,22,-1,-1,-1,-1,-1,-1,-1,22,-1,22,-1,-1,-1,22,22); | |
$TABLE[$NT_E5a] = array(-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,23,24,-1,-1); | |
$TABLE[$NT_E6] = array(25,26,27,-1,-1,-1,-1,-1,-1,-1,28,-1,29,-1,-1,-1,30,31); | |
$TABLE[$NT_E7] = array(-1,-1,-1,-1,-1,10,-1,-1,-1,-1,-1,-1,-1,-1,-1,11,-1,-1); | |
$TABLE[$NT_LS] = array(-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,32,-1,-1,33,-1,-1); | |
$VALS = explode("\n",file_get_contents('_recent.txt')); | |
if(empty($VALS)||count($VALS)==0||!is_array($VALS)) $VALS = array(); | |
while(count($VALS)%3!=0) $VALS = array_pop($VALS); | |
if(empty($VALS)||count($VALS)==0) $VALS = array(); | |
function looksLikeEq($s){ | |
if($s=='.') return FALSE; //점 | |
if(preg_match('/https?:\/\//',$s)) return FALSE; //웹 사이트 주소 | |
if(preg_match('/^[0-9.]*$/',$s)) return FALSE; //그냥 숫자 | |
if(preg_match('/^"[^"]*"$/',$s)) return FALSE; //"텍스트" | |
if(preg_match('/^\'[^\']*\'$/',$s)) return FALSE; //'텍스트' | |
return TRUE; | |
} | |
function evalEq($s){ | |
global $EVALEQ_ZERO, $VALS; | |
$flag = ''; $s = trim($s); | |
if(!looksLikeEq($s)) $flag = 'T'; | |
if(empty($s)) return "TSuccess|".$EVALEQ_ZERO; | |
if($s=='LG') return 'Success|DTD'; | |
$calcResult = evalTree(optimize(parser(adjustTokens(lexer($s))))); | |
if(count($VALS)>=3*10){ | |
for($i=0;$i<3;$i++) array_shift($VALS); | |
} | |
if(is_string($calcResult)){ | |
array_push($VALS, 'STRING'); | |
array_push($VALS, base64_encode($calcResult)); | |
array_push($VALS, '-'); | |
}else{ | |
array_push($VALS, 'COMPLEX'); | |
array_push($VALS, isset($calcResult->re)?$calcResult->re:'NaN'); | |
array_push($VALS, isset($calcResult->im)?$calcResult->im:'NaN'); | |
} | |
file_put_contents('_recent.txt',implode("\n",$VALS)); | |
return "${flag}Success|".$calcResult; | |
} | |
function isInComArr($s,$needle){ | |
if(empty($s)) return FALSE; | |
$arr = explode(',',$s); | |
for($i=0;$i<count($arr);$i++) if($needle==$arr[$i]) return TRUE; | |
return FALSE; | |
} | |
function clearToken($notThis,&$arr,&$t,&$s,$inQuote=FALSE){ | |
global $CONSTANTS,$VALS; | |
global $INTEGER,$IMAGINARY,$FLOAT,$OPERATOR,$PAREN,$STRING,$COMMA,$EXCLMARK,$NULL,$PERCENT,$QUOTE; | |
if($t==$NULL) return; | |
if(isInComArr($notThis,$t)==FALSE){ | |
if($t==$STRING){ | |
$pushed=FALSE; | |
if(!$inQuote){ | |
if($s[0]=='$'){ | |
$pushed=TRUE; | |
$si = intval($s[1]); | |
if(empty($si)) $si=0; | |
if($s[1]=='$') $si=1; | |
$si = (count($VALS)/3)-$si-1; | |
if($si<0||$si>=count($VALS)/3){ | |
echoErr('히스토리가 없거나, 범위(0~'.(count($VALS)/3-1).')를 벗어난 히스토리를 보려 했습니다.'); | |
} | |
$data1 = $VALS[3*$si+1]; | |
$data2 = $VALS[3*$si+2]; | |
switch($VALS[3*$si]){ | |
case 'STRING': | |
array_push($arr,new Token($QUOTE,'"')); | |
array_push($arr,new Token($STRING,base64_decode($data1))); | |
array_push($arr,new Token($QUOTE,'"')); | |
break; | |
case 'COMPLEX': | |
$vv = new Complex($data1,$data2); | |
if(bccomp($vv->im,0)==0){ | |
array_push($arr,new Token($FLOAT,$vv->re)); | |
}else{ | |
array_push($arr,new Token($PAREN,'(')); | |
if(bccomp($vv->re,0)!=0){ | |
array_push($arr,new Token($FLOAT,$vv->re)); | |
array_push($arr,new Token($OPERATOR,'+')); | |
} | |
array_push($arr,new Token($IMAGINARY,'i')); | |
array_push($arr,new Token($OPERATOR,'*')); | |
array_push($arr,new Token($FLOAT,$vv->im)); | |
array_push($arr,new Token($PAREN,')')); | |
} | |
break; | |
default: | |
echoErr('알 수 없는 히스토리 타입('.$VALS[3*$si].')입니다.'); | |
} | |
}else{ | |
for($i=0;$i<count($CONSTANTS);$i++) | |
if($CONSTANTS[$i]->name==$s){ | |
$pushed=TRUE; | |
$vv = $CONSTANTS[$i]->value; | |
if($vv->im==0){ | |
array_push($arr,new Token($FLOAT,$CONSTANTS[$i]->value->re)); | |
}else{ | |
array_push($arr,new Token($PAREN,'(')); | |
if($vv->re!=0){ | |
array_push($arr,new Token($FLOAT,$CONSTANTS[$i]->value->re)); | |
array_push($arr,new Token($OPERATOR,'+')); | |
} | |
array_push($arr,new Token($IMAGINARY,'i')); | |
array_push($arr,new Token($OPERATOR,'*')); | |
array_push($arr,new Token($FLOAT,$CONSTANTS[$i]->value->im)); | |
array_push($arr,new Token($PAREN,')')); | |
} | |
break; | |
} | |
} | |
} | |
if($pushed==FALSE){ | |
array_push($arr,new Token($t,$s)); | |
} | |
$t=$NULL; | |
$s=''; | |
}else{ | |
array_push($arr,new Token($t,$s)); | |
$t=$NULL; | |
$s=''; | |
} | |
} | |
} | |
function lexer($s){ | |
global $INTEGER,$IMAGINARY,$FLOAT,$OPERATOR,$PAREN,$STRING,$COMMA,$EXCLMARK,$NULL,$PERCENT,$QUOTE; | |
$prevType = $NULL; | |
$prevStr = ''; | |
$arrProcA = array(new Token($NULL,'')); | |
$quoteStr = ''; | |
for($i=0;$i<mb_strlen($s);$i++){ | |
$sc=mb_substr($s,$i,1); | |
if($quoteStr==''||$quoteStr==$sc){ | |
switch($sc){ | |
case '0': case '1': case '2': | |
case '3': case '4': case '5': case '6': | |
case '7': case '8': case '9': | |
clearToken("$INTEGER,$FLOAT,$STRING",$arrProcA,$prevType,$prevStr); | |
if($prevType==$NULL) $prevType=$INTEGER; | |
$prevStr.=$sc; | |
break; | |
case '.': | |
clearToken("$INTEGER,$FLOAT,$STRING",$arrProcA,$prevType,$prevStr); | |
if($prevType==$NULL||$prevType==$INTEGER) $prevType=$FLOAT; | |
else echoErr("".($i+1)."번째 위치에 있는 점은 올바르지 않습니다."); | |
if($prevType==$FLOAT) $prevStr.=$sc; | |
break; | |
case '+': case '-': case '*': | |
case '/': case '^': | |
clearToken('',$arrProcA,$prevType,$prevStr); | |
$prevType=$OPERATOR; | |
$prevStr.=$sc; | |
break; | |
case '%': | |
clearToken('',$arrProcA,$prevType,$prevStr); | |
$prevType=$PERCENT; | |
$prevStr.=$sc; | |
break; | |
case '"': case "'": | |
clearToken('',$arrProcA,$prevType,$prevStr); | |
$prevType=$QUOTE; | |
$prevStr.=$sc; | |
if($quoteStr=='') $quoteStr=$sc; else $quoteStr=''; | |
break; | |
case "\\": | |
clearToken("$STRING",$arrProcA,$prevType,$prevStr); | |
$i++; | |
$sc=mb_substr($s,$i,1); | |
switch($sc){ | |
case '"': | |
case "'": | |
case "\\": | |
$prevType=$STRING; | |
$prevStr.=$sc; | |
break; | |
default: | |
$i--; | |
$prevStr.="\\"; | |
} | |
break; | |
case '(': case ')': | |
clearToken('',$arrProcA,$prevType,$prevStr); | |
$prevType=$PAREN; | |
$prevStr.=$sc; | |
break; | |
case ',': | |
clearToken('',$arrProcA,$prevType,$prevStr); | |
$prevType=$COMMA; | |
$prevStr.=$sc; | |
break; | |
case '!': | |
clearToken('',$arrProcA,$prevType,$prevStr); | |
$prevType=$EXCLMARK; | |
$prevStr.=$sc; | |
break; | |
case ' ': case "\t": case "\r": case "\n": | |
clearToken('',$arrProcA,$prevType,$prevStr); | |
break; | |
default: | |
clearToken("$STRING",$arrProcA,$prevType,$prevStr); | |
$prevType=$STRING; | |
$prevStr.=$sc; | |
break; | |
} | |
}else{ | |
clearToken("$STRING",$arrProcA,$prevType,$prevStr); | |
$prevType=$STRING; | |
$prevStr.=$sc; | |
} | |
} | |
clearToken('',$arrProcA,$prevType,$prevStr); | |
//for($i=0;$i<count($arrProcA);$i++) echo $arrProcA[$i]."\n"; | |
return $arrProcA; | |
} | |
function addToArray(&$arr,$val,$n){ | |
array_unshift($arr,$val); | |
if(count($arr)>$n){ | |
array_pop($arr); | |
} | |
} | |
function compTwo($a,$b){ | |
global $OPERATOR,$PAREN,$QUOTE; | |
if($b->type==$OPERATOR||$b->type==$PAREN||$b->type==$QUOTE) | |
if(strpos($a,$b->str)!==FALSE) return TRUE; | |
return strpos($a,$b->type)!==FALSE; | |
} | |
function isMatchToArray($target,$rule){ | |
if(count($rule)>count($target)) return FALSE; | |
for($i=0;$i<count($rule);$i++){ | |
if(compTwo($rule[count($rule)-$i-1],$target[$i])==FALSE) return FALSE; | |
} | |
return TRUE; | |
} | |
function adjustTokens($a){ | |
global $INTEGER,$IMAGINARY,$FLOAT,$OPERATOR,$PAREN,$STRING,$COMMA,$EXCLMARK,$NULL,$PERCENT; | |
if(count($a)==2){ | |
if($a[1]->type==$OPERATOR||$a[1]->type==$PAREN||$a[1]->type==$STRING||$a[1]->type==$COMMA||$a[1]->type==$EXCLMARK) | |
echoErr('이 식은 올바르지 않은 단 하나의 토큰 ['.$a[1]->str.'] 만을 포함하고 있습니다.'); | |
return $a; | |
} | |
$newArr = array(); | |
$tempArr = array(); | |
for($i=0;$i<count($a);$i++){ | |
addToArray($tempArr,$a[$i],4); | |
// Multiply | |
if(isMatchToArray($tempArr,array($INTEGER.$IMAGINARY.$FLOAT.')'.$EXCLMARK,$INTEGER.$IMAGINARY.$FLOAT.$STRING.'('))){ | |
array_push($newArr, new Token($OPERATOR,'.')); | |
array_push($newArr, $a[$i]); | |
} | |
// Minus sign 1 | |
else if(isMatchToArray($tempArr,array($OPERATOR,$OPERATOR,$INTEGER.$IMAGINARY.$FLOAT))){ | |
$opTemp = array_pop($newArr); | |
if($opTemp->str=='+'||$opTemp->str=='-'){ | |
array_push($newArr, new Token($PAREN,'(')); | |
array_push($newArr, new Token($INTEGER,$opTemp->str.'1')); | |
array_push($newArr, new Token($OPERATOR,'*')); | |
array_push($newArr, $a[$i]); | |
array_push($newArr, new Token($PAREN,')')); | |
}else echoErr('연산자 '.$opTemp->str.'의 앞에 다른 연산자가 있습니다.'); | |
} | |
// Minus sign 2 | |
else if(isMatchToArray($tempArr,array($NULL.'(',$OPERATOR,$INTEGER.$IMAGINARY.$FLOAT.$STRING.'('))){ | |
$opTemp = array_pop($newArr); | |
if($opTemp->str=='+'||$opTemp->str=='-'){ | |
array_push($newArr, new Token($INTEGER,'0')); | |
array_push($newArr, $opTemp); | |
array_push($newArr, $a[$i]); | |
}else echoErr('연산자 '.$opTemp->str.' 앞에 식이 없습니다.'); | |
} | |
// Function | |
else if(isMatchToArray($tempArr,array($STRING,$INTEGER.$IMAGINARY.$FLOAT))){ | |
array_push($newArr, new Token($PAREN,'(')); | |
array_push($newArr, $a[$i]); | |
array_push($newArr, new Token($PAREN,')')); | |
} | |
else{ | |
array_push($newArr, $a[$i]); | |
} | |
} | |
$a = $newArr; $newArr = array(); | |
for($i=0;$i<count($a);$i++){ | |
addToArray($tempArr,$a[$i],4); | |
if(isMatchToArray($tempArr,array($STRING,'('))){ | |
//empty | |
}else{ | |
array_push($newArr, $a[$i]); | |
} | |
} | |
$a = $newArr; $newArr = array(); | |
for($i=0;$i<count($a);$i++){ | |
addToArray($tempArr,$a[$i],4); | |
if(isMatchToArray($tempArr,array($OPERATOR.'('.$NULL,'-',$STRING))){ | |
$pp = array_pop($newArr); | |
/* | |
array_push($newArr, new Token($INTEGER,'0')); | |
array_push($newArr, new Token($OPERATOR,'-')); | |
array_push($newArr, $a[$i]); | |
*/ | |
array_push($newArr, new Token($STRING,'-'.$a[$i]->str)); | |
}else{ | |
array_push($newArr, $a[$i]); | |
} | |
} | |
return $newArr; | |
} | |
function getInd($s){ | |
global $TABLE; | |
for($i=0;$i<count($TABLE['index']);$i++){ | |
if($TABLE['index'][$i]===$s) return $i; | |
} | |
return -1; | |
} | |
function parser($a){ | |
global $INTEGER,$IMAGINARY,$FLOAT,$OPERATOR,$PAREN,$STRING,$COMMA,$EXCLMARK,$NULL,$PERCENT,$QUOTE; | |
global $NT_S,$NT_F,$NT_P,$NT_AP,$NT_E0,$NT_E0a,$NT_E2,$NT_E2a,$NT_E3,$NT_E3a,$NT_E4,$NT_E4a,$NT_E5,$NT_E5a,$NT_E6,$NT_E7; | |
global $GRAMMAR,$TABLE; | |
if(count($a)==1) return $a[0]; | |
if($a[0]->type==$NULL) array_shift($a); | |
$ruleArr = array(); | |
$tempArr = array(); | |
$stack = array($NULL,$NT_S); | |
$resultTree = new TreeNode($NT_S); | |
$curTreePoint = $resultTree; | |
$pointer = 0; | |
$MAX_THRESHOLD = 100000; | |
while($MAX_THRESHOLD>0){ | |
$MAX_THRESHOLD--; | |
$curString = array_pop($stack); | |
if($curString==$NULL) break; | |
$curToken = $a[$pointer]; | |
$curType = $curToken->type; | |
if($curType==$OPERATOR||$curType==$PAREN||$curType==$QUOTE) $curType = $curToken->str; | |
//echo toName($curString).' '.$curType."\n"; | |
if(compTwo($curString,$curToken)){ | |
$curTreePoint->str = $curToken->str; | |
$curTreePoint = $curTreePoint->next; | |
$pointer++; | |
//echo "===================== 삽입 =====================\n"; | |
}else{ | |
$gInd = $TABLE[$curString][getInd($curType)]; | |
if(!is_int($gInd) || $gInd==-1){ | |
if($TABLE[$curString][$NULL]===-1) | |
echoErr($curToken->str." 근처에 올바르지 않은 식이 있습니다."); | |
else{ | |
$rmNode = $curTreePoint; | |
$curTreePoint = $curTreePoint->next; | |
if(empty($rmNode->p)) echoErr($curToken->str." 근처가 비어있습니다."); | |
else $rmNode->p->removeNode($rmNode); | |
} | |
}else{ | |
$curGrammar = $GRAMMAR[$gInd]; | |
if(array_shift($curGrammar)!==$curString) | |
echoErr('올바르지 않은 문법 테이블입니다.'); | |
array_push($ruleArr,$gInd); | |
for($i=0;$i<count($curGrammar);$i++){ | |
array_push($stack,$curGrammar[count($curGrammar)-$i-1]); | |
$curTreePoint->addNode(new TreeNode($curGrammar[$i])); | |
} | |
$curTreePoint = $curTreePoint->arr[0]; | |
} | |
} | |
} | |
if($MAX_THRESHOLD==0) echoErr('식을 처리하는 데 너무 오랜 시간이 걸립니다.'); | |
if($pointer!==count($a)) echoErr('['.$a[$pointer-1]->str.'] 뒤에 올바르지 않은 식이 있습니다.'); | |
return $resultTree; | |
} | |
function optimize($t){ //그냥 노드를 간소화 시킨다. | |
global $INTEGER,$IMAGINARY,$FLOAT,$OPERATOR,$PAREN,$STRING,$COMMA,$EXCLMARK,$NULL,$PERCENT; | |
global $NT_S,$NT_F,$NT_P,$NT_AP,$NT_E0,$NT_E0a,$NT_E2,$NT_E2a,$NT_E3,$NT_E3a,$NT_E4,$NT_E4a,$NT_E5,$NT_E5a,$NT_E6,$NT_E7; | |
global $FUNCTIONS; | |
if(empty($t)) echoErr("(optimize) 빈 노드가 감지되었습니다."); | |
switch($t->type){ | |
case $NT_S: | |
return optimize($t->arr[0]); | |
case $NT_E0: | |
if(count($t->arr)==1) return optimize($t->arr[0]); | |
break; | |
case $NT_E3: | |
if(count($t->arr)==1) return optimize($t->arr[0]); | |
break; | |
case $NT_E2: | |
if(count($t->arr)==1) return optimize($t->arr[0]); | |
break; | |
case $NT_E4: | |
if(count($t->arr)==1) return optimize($t->arr[0]); | |
break; | |
case $NT_E5: | |
if(count($t->arr)==1) return optimize($t->arr[0]); | |
break; | |
} | |
for($i=0;$i<count($t->arr);$i++){ | |
$t->arr[$i] = optimize($t->arr[$i]); | |
} | |
return $t; | |
} | |
function evalTree($t){ | |
global $INTEGER,$IMAGINARY,$FLOAT,$OPERATOR,$PAREN,$STRING,$COMMA,$EXCLMARK,$NULL,$PERCENT; | |
global $NT_S,$NT_F,$NT_P,$NT_AP,$NT_E0,$NT_E0a,$NT_E2,$NT_E2a,$NT_E3,$NT_E3a,$NT_E4,$NT_E4a,$NT_E5,$NT_E5a,$NT_E6,$NT_E7; | |
global $FUNCTIONS; | |
if(empty($t)) echoErr("(evalTree) 빈 노드가 감지되었습니다."); | |
switch($t->type){ | |
case $NT_S: | |
return evalTree($t->arr[0]); | |
break; | |
case $NT_F: | |
$label = $t->arr[0]->str; | |
$minus = FALSE; | |
if($label[0]==='-'){ | |
$minus = TRUE; | |
$label = substr($label,1); | |
} | |
$argsArr = evalTree($t->arr[1]); | |
for($i=0;$i<count($FUNCTIONS);$i++){ | |
if($FUNCTIONS[$i][0]==$label){ | |
if($minus) return Complex::minusSign(@$FUNCTIONS[$i][1]($argsArr)); | |
else return @$FUNCTIONS[$i][1]($argsArr); | |
} | |
} | |
echoErr("[${label}] 함수는 정의되지 않았습니다."); | |
break; | |
case $NT_P: | |
if($t->arr[0]->type==')') return array(new Complex(0)); | |
return array_merge(array(evalTree($t->arr[0])),evalTree($t->arr[1])); | |
break; | |
case $NT_AP: | |
if($t->arr[0]->type!=$COMMA) return array(); | |
return evalTree($t->arr[1]); | |
break; | |
case $NT_E0: //+- | |
if(count($t->arr)==2){ | |
if($t->arr[1]->type==$NT_E0a){ | |
$arr = evalTree($t->arr[1]); | |
$vs = evalTree($t->arr[0]); | |
for($i=0;$i<count($arr);$i++){ | |
if($arr[$i][0]=='+') $vs = evAdd($vs,$arr[$i][1]); | |
else if($arr[$i][0]=='-') $vs = evMinus($vs,$arr[$i][1]); | |
else echoErr("인식할 수 없는 부호 ".$arr[$i][0]."이(가) 나왔습니다."); | |
} | |
return $vs; | |
}else echoErr("E0a 형식이 예상되는 자리에 다른 형식 ".toName($t->arr[1]->type)."이(가) 나왔습니다."); | |
}else return evalTree($t->arr[0]); | |
break; | |
case $NT_E0a: | |
if(count($t->arr)==3){ | |
$arr = evalTree($t->arr[2]); | |
array_unshift($arr,array($t->arr[0]->str,evalTree($t->arr[1]))); | |
return $arr; | |
}else if(count($t->arr)==2) | |
return array(array($t->arr[0]->str,evalTree($t->arr[1]))); | |
else echoErr("E0a 형식은 다음과 같은 길이를 가지면 안 됩니다 : ".count($t->arr)); | |
break; | |
case $NT_E2: //. | |
if(count($t->arr)==2) return evMultiply(evalTree($t->arr[0]),evalTree($t->arr[1])); | |
else return evalTree($t->arr[0]); | |
break; | |
case $NT_E2a: | |
if(count($t->arr)==2) return evalTree($t->arr[1]); | |
else return Complex::multiply(evalTree($t->arr[1]),evalTree($t->arr[2])); | |
break; | |
case $NT_E3: //*÷ | |
if(count($t->arr)==2) return evMultiply(evalTree($t->arr[0]),evalTree($t->arr[1])); | |
else return evalTree($t->arr[0]); | |
break; | |
case $NT_E3a: | |
if(count($t->arr)==3) | |
if($t->arr[0]->str=='/') return evMultiply(Complex::inv(evalTree($t->arr[1])),evalTree($t->arr[2])); | |
else if($t->arr[0]->str=='*') return evMultiply(evalTree($t->arr[1]),evalTree($t->arr[2])); | |
else echoErr("E3a 형식에서 나오면 안 될 연산자가 나왔습니다 : ".$t->arr[0]->str); | |
else if(count($t->arr)==2) | |
if($t->arr[0]->str=='/') return Complex::inv(evalTree($t->arr[1])); | |
else if($t->arr[0]->str=='*') return evalTree($t->arr[1]); | |
else echoErr("E3a 형식에서 나오면 안 될 연산자가 나왔습니다 : ".toName($t->arr[0]->str)); | |
else echoErr("E3a 형식은 다음과 같은 길이를 가지면 안 됩니다 : ".count($t->arr)); | |
break; | |
case $NT_E4: //^ | |
if(count($t->arr)==2){ | |
$pows = array(evalTree($t->arr[0])); | |
$e4aTree = $t->arr[1]; | |
while(count($e4aTree->arr)==3){ | |
array_push($pows,evalTree($e4aTree->arr[1])); | |
$e4aTree = $e4aTree->arr[2]; | |
} | |
array_push($pows,evalTree($e4aTree->arr[1])); | |
while(count($pows)>1){ | |
$p2 = array_pop($pows); | |
$p1 = array_pop($pows); | |
array_push($pows,Complex::pow($p1,$p2)); | |
} | |
return $pows[0]; | |
}else return evalTree($t->arr[0]); | |
break; | |
case $NT_E4a: | |
echoErr("E4a가 evalTree 함수를 통해 호출되었습니다."); | |
break; | |
case $NT_E5: | |
if(count($t->arr)==2){ | |
return Complex::factorial(evalTree($t->arr[0])); | |
//echoErr("팩토리얼은 아직 구현되지 않았습니다."); | |
}else return evalTree($t->arr[0]); | |
break; | |
case $NT_E6: | |
$cc = Complex::$NaN; | |
switch($t->arr[0]->type){ | |
case $INTEGER: | |
case $FLOAT: | |
$cc = new Complex($t->arr[0]->str); | |
break; | |
case $IMAGINARY: | |
$cc = new Complex(0,1); | |
break; | |
case '(': | |
$cc = evalTree($t->arr[1]); | |
break; | |
case $NT_F: | |
$cc = evalTree($t->arr[0]); | |
break; | |
case '"': | |
case "'": | |
return $t->arr[1]->arr[0]->str; | |
default: | |
echoErr("단항식의 이 형식은 계산할 수 없습니다 : ".toName($t->arr[0]->type)); | |
} | |
if(count($t->arr)>1&&$t->arr[count($t->arr)-1]->arr[0]->type=='%'){ | |
return new Complex(bcdiv($cc->re,100),bcdiv($cc->im,100)); | |
}else{ | |
return $cc; | |
} | |
break; | |
default: | |
echoErr("아직 이 tree 형식은 계산할 수 없습니다 : ".toName($t->type)); | |
} | |
} | |
?> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment