Skip to content

Instantly share code, notes, and snippets.

@123jimin
Created July 11, 2013 09:07
Show Gist options
  • Save 123jimin/5973864 to your computer and use it in GitHub Desktop.
Save 123jimin/5973864 to your computer and use it in GitHub Desktop.
The parser I made (100% hand-coded) back in 2011.
<?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