c2ephp

caos/highlight/CAOSHighlighter.php

Go to the documentation of this file.
00001 <?php
00002 
00003 define('FORMAT_C1','C1');
00004 define('FORMAT_C2','C2');
00005 define('FORMAT_C3','C3');
00006 define('FORMAT_DS','DS');
00007 
00008 
00010 
00028 class CAOSHighlighter {
00030 
00031     private $caosCommands = array();
00032     private $caosVariables = array();
00033     private $caosCommandVariables = array();
00034     private $caosOperators = array();
00035     private $caosFlowControls = array();
00036 
00037     private $scriptFormat;
00038     private $scriptLines = array();
00039     private $scriptSubroutines = array();
00040     private $highlightedLines = array();
00041 
00042     private $previousLineCode;
00043     private $currentLine;
00044     private $currentIndent;
00045     private $currentWord;
00046 
00047     // @endcond
00048 
00051 
00056     public function CAOSHighlighter($format) {
00057         $this->scriptFormat = $format;
00058         //load files...
00059         require_once(dirname(__FILE__).'/'.$format.'/CommandVariables.php');
00060         require_once(dirname(__FILE__).'/'.$format.'/Commands.php');
00061         require_once(dirname(__FILE__).'/'.$format.'/Variables.php');
00062         require_once(dirname(__FILE__).'/'.$format.'/FlowControls.php');
00063         require_once(dirname(__FILE__).'/'.$format.'/Operators.php');
00064 
00065         //put into arrays
00066         $this->caosCommandVariables = call_user_func(array($format.'CAOSCommandVariables','GetTokens'));
00067         $this->caosCommands = call_user_func(array($format.'CAOSCommands','GetTokens'));
00068         $this->caosVariables = call_user_func(array($format.'CAOSVariables','GetTokens'));
00069         $this->caosOperators = call_user_func(array($format.'CAOSOperators','GetTokens'));
00070         $this->caosFlowControls = call_user_func(array($format.'CAOSFlowControls','GetTokens'));
00071     }
00072 
00074 
00080     public function HighlightScript($script) {
00081         if(strpos($script,"\r") !== false) {
00082             $script = str_replace("\r\n","\n",$script); //get rid of mac and windows newlines.
00083             $script = str_replace("\r","\n",$script);
00084         }
00085         //remove tabs and spaces before newlines.
00086         $script = str_replace(" \n","\n",$script);
00087         $script = str_replace("\t",'',$script);
00088         $script = $this->SmartRemoveMultipleSpaces($script);
00089         $this->scriptLines = explode("\n",$script);
00090 
00091         //now that we have the lines, we can make the list of subroutines.
00092         $this->ScanForSubroutines();
00093         $this->currentLine = 0;
00094         $this->highlightedLines = array();
00095         while(($line = $this->HighlightNextLine()) !== false) {
00096 
00097             $this->highlightedLines[] = $line;
00098         }
00099         return implode($this->highlightedLines);
00100     }
00101 
00103 
00105 
00110     private function SmartRemoveMultipleSpaces($text) {
00111         $newString = array();
00112         $inString = false;
00113         $inComment = false;
00114         for($i=0;$i<strlen($text);$i++) {
00115             $character = $text{$i};
00116             if($character == '"') {
00117                 $inString = !$inString;
00118             } else if($character == '*') {
00119                 $inComment = true;
00120             } else if($character == "\n" ) {
00121                 $inComment = false;
00122             } else if(!$inString && !$inComment && $character == ' ') {
00123                 while($i+2 < strlen($text) && $text{$i+1} == ' ') {
00124                     $i++;
00125                 }
00126             }
00127             $newString[] = $character;
00128         }
00129         return trim(implode('',$newString));
00130     }
00131 
00133 
00135     
00137 
00141     private function ScanForSubroutines() {
00142         //expects $scriptLines to be filled out
00143         foreach($this->scriptLines as $line) {
00144             $words = explode(' ',strtolower($line));
00145             if($words[0] == 'subr') {
00146                 $this->scriptSubroutines[] = $words[1];
00147             }
00148         }   
00149     }
00150 
00152 
00154 
00156 
00160     private function HighlightNextLine() {
00161         if(sizeof($this->scriptLines) <= $this->currentLine) {
00162             return false;
00163         }
00164         $line = $this->scriptLines[$this->currentLine];
00165         //$line = $this->SmartRemoveMultipleSpaces($line);
00166         if(strlen($line) == 0 && $this->currentIndent > 0) {
00167             $highlightedLine = $this->CreateIndentForThisLine('')."\n";
00168             $this->currentLine++;
00169             return $highlightedLine;
00170         } else if(strlen($line) == 0) {
00171             $this->currentLine++;
00172             return '';
00173         }
00174         $words = explode(' ',$line);
00175 
00176         $this->SetIndentForThisLine($words[0]);
00177 
00178         $inString = false;
00179         $whenStringBegan = -1;
00180         $inByteString = false;
00181         $highlightedLine = '';
00182         $firstToken = '';
00183 
00184         //if last line is a comment and this line starts with scrp set last line's indent to 0 (remove whitespace at front)
00185         if(in_array($words[0],array('scrp','rscr'))) {
00186             if(!empty($this->scriptLines[$this->currentLine-1])) {
00187                 if($this->scriptLines[$this->currentLine-1]{0} == '*') {
00188                     $this->highlightedLines[$this->currentLine-1] = ltrim($this->highlightedLines[$this->currentLine-1]);
00189                 }
00190             }
00191         } 
00192 
00193         for($this->currentWord=0;$this->currentWord<sizeof($words);$this->currentWord++) {
00194 
00195             $word = $words[$this->currentWord];
00196             $highlightedWord = $word;
00197             if($inString) {
00198                 if($this->currentWord == sizeof($words)-1) {
00199                     if(strpos($word,'"') === false) {
00200                         $highlightedWord = htmlentities($word).'</span>';
00201                         $highlightedLineBeforeString = substr($highlightedLine,0,$whenStringBegan);
00202                         $highlightedLineAfterString = substr($highlightedLine,$whenStringBegan);
00203                         $highlightedLineAfterString .= $highlightedWord;
00204                         $highlightedLineAfterString = str_replace('<span class="string">','<span class="error">',$highlightedLineAfterString);
00205                         $highlightedLine = $highlightedLineBeforeString.$highlightedLineAfterString;
00206                         $inString = false;
00207                         continue;
00208 
00209                     }
00210                 }
00211                 if(($position = strpos($word,'"')) !== false) {
00212                     $firstHalf = substr($word,0,$position);
00213                     $secondHalf = substr($word,$position+1);
00214 
00215                     $highlightedWord = htmlentities($firstHalf).'"</span>'; //end the string
00216                     if($secondHalf != '') {
00217                         $highlightedWord .= '<span class="error">'.htmlentities($secondHalf).'</span>';
00218                     }
00219                     $inString=false;
00220                 } else {
00221                     $highlightedLine .= $word.' ';
00222                     continue;                   
00223                 }
00224             } else if($inByteString) {
00225                 if($this->currentWord == sizeof($words)-1) {
00226                     if(strpos($word,']') === false) {
00227                         $highlightedWord = htmlentities($word).'</span>';
00228                         $highlightedLineBeforeString = substr($highlightedLine,0,$whenStringBegan);
00229                         $highlightedLineAfterString = substr($highlightedLine,$whenStringBegan);
00230                         $highlightedLineAfterString .= $highlightedWord;
00231                         $highlightedLineAfterString = str_replace('<span class="bytestring">','<span class="error">',$highlightedLineAfterString);
00232                         $highlightedLine = $highlightedLineBeforeString.$highlightedLineAfterString;
00233                         continue;
00234                     }
00235                 }
00236                 if(($position = strpos($word,']')) !== false) {
00237                     $firstHalf = substr($word,0,$position);
00238                     $secondHalf = substr($word,$position+1);
00239 
00240                     $highlightedWord = htmlentities($firstHalf).']</span>'; //end the string
00241                     if($secondHalf != '') {
00242                         $highlightedWord .= '<span class="error">'.htmlentities($secondHalf).'</span>';
00243                     }
00244                     $inByteString=false;
00245                 } else {
00246                     $highlightedLine .= $word.' ';
00247                     continue;
00248                 }
00249             } else if($firstToken != '') {
00250                 //sort out unquoted strings
00251                 if($this->currentWord == 1) {
00252                     if($firstToken == 'subr') {
00253                         if(strlen($word) == 4 || $this->scriptFormat != FORMAT_C2) {
00254                             $highlightedWord = '<span class="label">'.$word.'</span>';
00255                         } else {
00256                             $highlightedWord = '<span class="error">'.$word.'</span>';
00257                         }
00258                     } else if($firstToken == 'gsub') {
00259                         if(in_array($word,$this->scriptSubroutines)) {
00260                             //C3/DS allow for any length of subroutine name, C2 and C1 probably only allow 4-character names.
00261                             if(in_array($this->scriptFormat,array(FORMAT_C3,FORMAT_DS)) || strlen($word) == 4) {
00262                                 $highlightedWord = '<span class="label">'.$word.'</span>';
00263                             } else {
00264                                 $highlightedWord = '<span class="error">'.$word.'</span>';
00265                             }
00266                         } else {
00267                             $highlightedWord = '<span class="error">'.$word.'</span>';
00268                         }
00269                     }
00270                     if($this->scriptFormat == FORMAT_C2) {
00271                         if(in_array(strtolower($firstToken),array('tokn','snde','sndc','sndl','sndq','plbs'))) {
00272                             if(strlen($word) == 4) {
00273                                 $highlightedWord = '<span class="string">'.$word.'</span>';
00274                             } else {
00275                                 $highlightedWord = '<span class="error">'.$word.'</span>';
00276                             }
00277                         }
00278                     }
00279                 } else if($this->currentWord == 2) {
00280                     if($this->scriptFormat == 'C2') {
00281                         if(preg_match('/^new: (scen|simp|cbtn|comp|vhcl|lift|bkbd|cbub)$/i',$firstToken)) {
00282                             if(strlen($word) == 4) {
00283                                 $highlightedWord = '<span class="string">'.$word.'</span>';
00284                             } else {
00285                                 $highlightedWord = '<span class="error">'.$word.'</span>';
00286                             }
00287                         }
00288                     }
00289                 } else if($this->currentWord == sizeof($words)-1) {
00290                     if($this->scriptFormat == 'C2') {
00291                         if(strtolower($firstToken) == 'rmsc') {
00292                             if(strlen($word) == 4) {
00293                                 $highlightedWord = '<span class="string">'.$word.'</span>';
00294                             } else {
00295                                 $highlightedWord = '<span class="error">'.$word.'</span>';
00296                             }
00297                         }
00298                     }
00299                 }
00300             }
00301             if($highlightedWord == $word) {
00302                 $highlightedWord = $this->TryToHighlightToken($word);
00303                 if($this->currentWord == 0) {
00304                     $firstToken = $word;
00305                 }
00306                 //Highlight two-word block.
00307                 if($highlightedWord == $word && $this->currentWord < sizeof($words)-1) {
00308                     $wordPair = $word.' '.$words[$this->currentWord+1];
00309                     $highlightedWord = $this->TryToHighlightToken($wordPair);
00310                     if($highlightedWord != $wordPair) {
00311                         if($this->currentWord == 0) {
00312                             $firstToken = $wordPair;
00313                         }
00314                         $this->currentWord++;
00315                     } else {
00316                         $highlightedWord = $word;
00317                     }
00318                 }
00319                 if($highlightedWord == $word) { //invalid caos command
00320                     if($word{0} == '"' && $this->scriptFormat != FORMAT_C2) { //if it begins a string. (C2 has no strings)
00321                         $whenStringBegan = strlen($highlightedLine);
00322                         $highlightedWord = '<span class="string">'.htmlentities($word);
00323                         if($word{strlen($word)-1} == '"') {
00324                             $highlightedWord .= '</span>'; //end the string
00325                             $inString = false;
00326                         } else if($this->currentWord == sizeof($words)-1) {
00327                             $highlightedWord = '<span class="error">'.htmlentities($word).'</span>';
00328                             $inString = false;
00329                         } else {
00330                             $inString = true;
00331                         }
00332                     } else if($word{0} == '[') { //begins a bytestring
00333                         $highlightedWord = '<span class="bytestring">'.htmlentities($word);
00334                         $whenStringBegan = strlen($highlightedLine);
00335                         if($this->scriptFormat == 'C2') {
00336                             //c2 bytestrings are part of the original term, on they're own they're wrong!
00337                             $highlightedWord = '<span class="error">'.htmlentities($word);
00338                         }
00339                         if($word{strlen($word)-1} == ']') {
00340                             $highlightedWord .= '</span>';
00341                             $inByteString = false;
00342                         } else if($this->currentWord == sizeof($words)-1) {
00343                             $highlightedWord = '<span class="error">'.htmlentities($word).'</span>';
00344                             $inByteString = false;
00345                         } else {
00346                             $inByteString = true;
00347                         }
00348                     } else if(is_numeric($word)) {
00349                         $highlightedWord = '<span class="number">'.htmlentities($word).'</span>';
00350                     } else if($word{0} == '*') { // because of SmartRemoveMultipleSpaces, prints exactly as written :)
00351                         $highlightedWord = '<span class="comment">';
00352                         for($i=$this->currentWord;$i<sizeof($words);$i++)
00353                         {
00354                             if($i!=$this->currentWord)
00355                             {
00356                                 $highlightedWord.=' ';
00357                             }
00358                             $highlightedWord.= htmlentities($words[$i]);
00359                         }
00360                         $highlightedWord .= '</span>';
00361                         $highlightedLine .= $highlightedWord;
00362                         break;
00363                     } else { //Well, I don't get it :)
00364                         $highlightedWord = '<span class="error">'.htmlentities($word).'</span>';
00365                     }
00366                 }
00367 
00368             } // end else
00369             $highlightedLine .= $highlightedWord.' ';
00370         }
00371         $highlightedLine = $this->CreateIndentForThisLine($words[0]).$highlightedLine."\n".$this->SetIndentForNextLine($words[0]);
00372         $this->currentLine++;
00373         return $highlightedLine;
00374     }
00375 
00377 
00379 
00384     private function TryToHighlightToken($word) {
00385         $lcword = strtolower($word);
00386         $matches; //used for C2 anim command preg_match
00387 
00388         //first position commands + flow controls only
00389         //2nd position commands + command variables + variables only.
00390         if(in_array($lcword,$this->caosCommands)) {
00391             $word = '<span class="command">'.htmlentities($word).'</span>';
00392         } else if(in_array($lcword,$this->caosVariables)) {
00393             $word = '<span class="variable">'.htmlentities($word).'</span>';
00394             //vaXX, ovXX
00395         } else if(in_array($this->scriptFormat,array('C2','C3','DS')) && preg_match("/^(va|ov)[0-9]{2}$/", $lcword)) {
00396             $word = '<span class="variable">'.htmlentities($word).'</span>';
00397             //mvXX
00398         } else if(in_array($this->scriptFormat,array('C3','DS')) && preg_match('/^(mv)[0-9]{2}$/',$lcword)) {
00399             $word = '<span class="variable">'.htmlentities($word).'</span>';
00400             //obvX
00401         } else if(in_array($this->scriptFormat,array('C1','C2')) && preg_match('/^(obv)[0-9]$/',$lcword)) {
00402             $word = '<span class="variable">'.htmlentities($word).'</span>';
00403         } else if($this->scriptFormat == 'C2' && preg_match('/^([Aa][Nn][Ii][Mm]|[Pp][Rr][Ll][Dd])(\[[0-9]+R?\])$/',$word,$matches)) {
00404             $word = '<span class="variable">'.strtolower($matches[1]).'</span><span class="bytestring">'.$matches[2].'</span>';
00405         } else if(in_array($lcword,$this->caosOperators)) {
00406             $word = '<span class="operator">'.htmlentities($word).'</span>';
00407         } else if(in_array($lcword,$this->caosFlowControls)) {
00408             $word = '<span class="flowcontrol">'.htmlentities($word).'</span>';
00409         } else if(in_array($lcword,$this->caosCommandVariables)) {
00410             if($this->currentWord == 0) {
00411                 $word = '<span class="command">'.htmlentities($word).'</span>';
00412             } else {
00413                 $word = '<span class="variable">'.htmlentities($word).'</span>';
00414             }
00415         }
00416         return $word;
00417     }
00418 
00420 
00422 
00424 
00428     private function SetIndentForThisLine($firstword) {
00429         switch($firstword) {
00430         case 'scrp':
00431         case 'endm':
00432         case 'rscr':
00433             $this->currentIndent = 0;
00434             break;
00435         case 'retn':
00436         case 'subr':
00437             $this->currentIndent = 1;
00438             break;
00439         case 'elif': //doesn't exist in c2, but we still format it to improve readability.
00440             // if someone has used elif in c2 code it will still be tagged as an error :)
00441         case 'else':
00442         case 'endi':
00443         case 'untl':
00444         case 'next':
00445         case 'ever':
00446             case 'repe';
00447             $this->currentIndent--;
00448             break;
00449         }
00450     }
00451 
00453 
00455 
00458     private function SetIndentForNextLine($firstword) {
00459         switch($firstword) {
00460         case 'scrp':
00461         case 'rscr':
00462         case 'iscr':
00463             $this->currentIndent = 0; //falls through
00464         case 'doif':
00465         case 'elif':
00466         case 'else':
00467         case 'inst':
00468         case 'subr':
00469         case 'loop':
00470         case 'reps':
00471         case 'etch':
00472         case 'enum':
00473         case 'esee':
00474         case 'epas':
00475         case 'econ':
00476             $this->currentIndent++;
00477             break;
00478         case 'endm':
00479             return "\n";
00480         }
00481     }
00482 
00484 
00485 
00486     // @cond INTERNAL_DOCS
00487 
00491     private function CreateIndentForThisLine($firstword) {
00492         $indent = '';
00493         if(in_array($firstword,array('scrp','rscr'))) {
00494             if(!empty($this->previousLineCode)) {
00495                 if($this->previousLineCode{0} != '*') {
00496                     $indent = "\n";
00497                 }
00498             }
00499         }
00500         for($i=0;$i<$this->currentIndent;$i++) {
00501             $indent .= "\t";
00502         }
00503         return $indent;
00504     }
00505 
00507 }
00508 
00509 
00510 
00511 ?>
 All Classes Files Functions Variables Enumerations