c2ephp
|
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 ?>