Web/PHP
구문강조
aucd29
2013. 9. 26. 21:48
<?php
class Syntax
{
var $rule;
var $curr_rule;
var $color;
var $prefix;
function syntax($rule = null)
{
$this->rule = array();
$this->prefix = false;
if ($rule) $this->setRule($rule);
}
function setRule($rule)
{
static $dir = null;
$r = array();
$curr_ = null;
if (array_key_exists($rule, $this->rule)) {
$this->curr_rule = $rule;
return true;
}
if ($dir == null) {
$dir = dirname(__FILE__);
$slash = strpos($dir, '\\')>0?'\\':'/';
$dir .= $slash.'syntax'.$slash;
}
if (!file_exists($dir.$rule.'.stx')) return false;
$fp = fopen($dir.$rule.'.stx', 'r');
while (!feof($fp))
{
$line = trim(fgets($fp, 128));
if (strlen($line) == 0 || $line{0} == ';' || $line{0} == ' ') continue;
else if ($line{0} == '#')
{
list($key, $val) = explode('=', $line, 2);
$key = strtoupper(substr($key, 1));
$val = trim($val);
if (empty($val)) continue;
switch ($key)
{
case 'DELIMITER':
$len = strlen($val);
for ($i = 0; $i < $len; $i++) $r['delimeter'][] = $val{$i};
break;
case 'QUOTATION':
case 'QUOTATION1':
case 'QUOTATION2':
$r['quote'][] = $val;
break;
case 'LINECOMMENT':
case 'LINECOMMENT1':
case 'LINECOMMENT2':
$r['linecomment'][] = $val;
for ($i = 0; $i < strlen($val); $i++) $r['delimeter'][] = $val{$i};
break;
case 'CONTINUE_QUOTE':
$r['continue_quote'] = ($val{0} == 'y' || $val{0} == 'Y')?true:false;
break;
case 'COMMENTON': $r['commenton'][0] = $val; break;
case 'COMMENTOFF': $r['commentoff'][0] = $val; break;
case 'COMMENTON1': $r['commenton'][1] = $val; break;
case 'COMMENTOFF1': $r['commentoff'][1] = $val; break;
case 'ESCAPE':
$r['escape'] = $val;
break;
case 'CASE':
$r['case'] = ($val{0} == 'y' || $val{0} == 'Y')?true:false;
break;
case 'PREFIX':
case 'PREFIX1':
case 'PREFIX2':
case 'PREFIX3':
case 'PREFIX4':
case 'PREFIX5':
$r['prefix'][] = $val;
break;
case 'SUFFIX':
case 'SUFFIX1':
case 'SUFFIX2':
case 'SUFFIX3':
case 'SUFFIX4':
case 'SUFFIX5':
$r['suffix'][] = $val;
break;
case 'EMBED_HTML':
$r['special_stx'] = 'html';
break;
case 'SPECIAL_STX':
$r['special_stx'] = $val;
break;
case 'KEYWORD':
if (!is_array($r['keyword'])) $r['keyword'] = array();
$curr_ = count($r['keyword']);
$r['keyword'][$curr_] = array();
break;
}
}
else if (is_int($curr_))
{
$r['keyword'][$curr_][] = $r['case']?$line:strtolower($line);
}
}
fclose($fp);
if (!is_array($r['linecomment'])) $r['linecomment'] = array();
if (!is_array($r['commenton'])) $r['commenton'] = array();
if (!is_array($r['commentoff'])) $r['commentoff'] = array();
$r['delimeter'] = array_unique($r['delimeter']);
$this->rule[$rule] = $r;
$this->curr_rule = $rule;
unset($r);
return true;
}
function on($str)
{
$rule = &$this->rule[$this->curr_rule];
$str = preg_replace("/\n\r|\r\n|\r/", "\n", $str)."\n";
$ret = '<code class="'.$this->curr_rule.'">';
$delimeter = '';
$word = '';
$last_pos = 0;
$max = strlen($str);
$space = array("\n", "\t", " ");
for ($i=0; $i < $max; $i++)
{
$c = $str{$i};
$is_space = in_array($c, $space);
if ($is_space)
{
if (strlen($delimeter) > 0) {
if (count($this->_pr_comment($delimeter)) > 0) $c = '';
$ret .= $this->_pr_delimeter(&$str, &$i, $delimeter);
$delimeter = '';
} else if (strlen($word) > 0) {
$ret .= $this->_pr_word($word);
$word = '';
}
$ret .= $c;
}
else if (is_array($rule['prefix']) && in_array($c, $rule['prefix']))
{
$this->prefix = true;
$ret .= '<B>'.$c.'</B>';
}
else if (in_array($c, $rule['quote']))
{
if (strlen($delimeter) > 0) {
$ret .= $this->_pr_delimeter(&$str, &$i, $delimeter);
$delimeter = '';
} else if (strlen($word) > 0) {
$ret .= $this->_pr_word($word);
$word = '';
}
$ret .= $this->_pr_quote(&$str, &$i, $c);
}
else if (in_array($c, $rule['delimeter']))
{
$delimeter .= $c;
if (strlen($word) > 0) {
$ret .= $this->_pr_word($word);
$word = '';
}
}
else
{
if (strlen($delimeter) > 0) {
if (count($this->_pr_comment($delimeter)) > 0) $c = '';
$ret .= $this->_pr_delimeter(&$str, &$i, &$delimeter);
$delimeter = '';
}
$word .= $c;
}
}
$ret = rtrim($ret).'</code>';
return nl2br(strtr($ret, array(' '=>' ', "\t"=>' ')));
}
function onFile($filename)
{
if (!file_exists($filename)) return false;
if (function_exists('file_get_contents')) return $this->on(file_get_contents($filename));
else
{
$fp = fopen($filename, 'r');
$str = fread($fp, filesize($filename));
fclose($fp);
return $this->on($str);
}
}
function _pr_delimeter(&$str, &$offset, $delimeter)
{
$ret = '';
$this->prefix = false;
$compare = $this->_pr_comment($delimeter);
if (count($compare))
{
$compare = array_flip($compare); ksort($compare);
$compare = array_flip($compare); reset($compare);
$key = key($compare);
$val = current($compare);
if ($val > 0) {
$ret .= '<A>'.htmlspecialchars(substr($delimeter, 0, $val)).'</A>';
}
if ($key >= 1000) // 블럭 코멘트일때
{
$off = $this->rule[$this->curr_rule]['commentoff'][$key-1000];
$pos = strpos($str, $off, $offset);
$ret .= '<EM>'.htmlspecialchars(substr($delimeter, $val).substr($str, $offset, $pos - $offset + strlen($off))).'</EM>';
$offset = $pos + strlen($off) - 1;
}
else // 라인 코멘트일때
{
$pos = strpos($str, "\n", $offset);
$ret .= '<EM>'.htmlspecialchars(substr($delimeter, $val).substr($str, $offset, $pos - $offset)).'</EM>';
$offset = $pos - 1;
}
}
else
{
$ret = '<A>'.htmlspecialchars($delimeter).'</A>';
}
return $ret;
}
function _pr_quote(&$str, &$offset, $quote)
{
$count = 0;
$len = strlen($str);
$this->prefix = false;
for ($i = $offset+1; $i < $len; $i++)
{
if ($str{$i} == $this->rule[$this->curr_rule]['escape'])
{
if ($str{$i+1} == $this->rule[$this->curr_rule]['escape'] || $str{$i+1} == $quote) { $i++; continue; }
}
else if ($str{$i} == $quote)
{
$ret = substr($str, $offset, $i - $offset + 1);
$offset = $i;
break;
}
}
return '<Q>'.htmlspecialchars($ret).'</Q>';
}
function _pr_word($word)
{
$max = count($this->rule[$this->curr_rule]['keyword']);
if ($this->prefix) {
$this->prefix = false;
return '<SUB>'.htmlspecialchars($word).'</SUB>';
}
for ($i = 0; $i < $max; $i++)
{
$word_temp = $this->rule[$this->curr_rule]['case']?$word:strtolower($word);
if (in_array($word_temp, $this->rule[$this->curr_rule]['keyword'][$i]))
{
$ret = '<S class="'.($i+1).'">'.htmlspecialchars($word).'</S>';
return $ret;
}
}
return htmlspecialchars($word);
}
function _pr_comment($delimeter)
{
$compare = array();
foreach ($this->rule[$this->curr_rule]['linecomment'] as $key=>$val)
{
if ( is_int($tmp = strpos($delimeter, $val)) ) $compare[$key] = $tmp;
}
foreach ($this->rule[$this->curr_rule]['commenton'] as $key=>$val)
{
if ( is_int($tmp = strpos($delimeter, $val)) ) $compare[$key+1000] = $tmp;
}
return $compare;
}
}
/*
$syntax = new Syntax('js');
echo $syntax->on($str);
*/
$_POST['str'] = stripslashes($_POST['str']);
$syntax = new Syntax($_POST['type']);
echo $syntax->on($_POST['str']);
?>
<html>
<body>
<br>
구문강조 테스트<br>
<style>
/* 전체 스타일 */
CODE, CODE * {
color:black;
font-style:normal;
font-weight:normal;
text-decoration:none;
font-size:8pt;
font-family:돋움체;
}
CODE.html A {color:blue} /* 구분자 스타일 */
CODE.html B {color:green} /* Prefix 스타일 */
CODE.html SUB {color:green} /* Prefix 붙은 문자의 스타일 */
CODE.html EM {color:green} /* 코멘트 */
CODE.html Q {color:#000080} /* 따옴표로 묶은 거 */
CODE.html S.1 {color:blue;} /* 키워드 1 */
CODE.html S.2 {color:red;} /* 키워드 2 */
CODE.html S.3 {color:green;} /* 키워드 3 */
CODE.js A {color:red} /* 구분자 스타일 */
CODE.js EM {color:#FFCC00} /* 코멘트 */
CODE.js Q {color:#606060} /* 따옴표로 묶은 거 */
CODE.js S.1 {color:blue;} /* 키워드 1 */
CODE.js S.2 {color:red;} /* 키워드 2 */
CODE.js S.3 {color:green;} /* 키워드 3 */
CODE.js S.4 {color:#999900;} /* 키워드 4 */
CODE.js S.5 {color:#800000;} /* 키워드 5 */
CODE.css Q {color:#FF00FF} /* 따옴표로 묶은 거 */
CODE.css EM {color:green} /* 코멘트 */
CODE.css S.1 {color:blue} /* 키워드 1 */
CODE.css S.2 {color:red} /* 키워드 2 */
</style>
<form method="post">
<select name="type">
<option value="js">자바스크립트</option>
<option value="html">HTML</option>
<option value="css">CSS</option>
<option value="python">파이선기본</option>
<option value="cpp">C++</option>
<option value="c">C</option>
</select><br>
<textarea name="str" rows="10" cols="80"><?=htmlspecialchars($_POST['str'])?></textarea><br>
<input type="submit" value="전송">
</form>
</body>
</html>
class Syntax
{
var $rule;
var $curr_rule;
var $color;
var $prefix;
function syntax($rule = null)
{
$this->rule = array();
$this->prefix = false;
if ($rule) $this->setRule($rule);
}
function setRule($rule)
{
static $dir = null;
$r = array();
$curr_ = null;
if (array_key_exists($rule, $this->rule)) {
$this->curr_rule = $rule;
return true;
}
if ($dir == null) {
$dir = dirname(__FILE__);
$slash = strpos($dir, '\\')>0?'\\':'/';
$dir .= $slash.'syntax'.$slash;
}
if (!file_exists($dir.$rule.'.stx')) return false;
$fp = fopen($dir.$rule.'.stx', 'r');
while (!feof($fp))
{
$line = trim(fgets($fp, 128));
if (strlen($line) == 0 || $line{0} == ';' || $line{0} == ' ') continue;
else if ($line{0} == '#')
{
list($key, $val) = explode('=', $line, 2);
$key = strtoupper(substr($key, 1));
$val = trim($val);
if (empty($val)) continue;
switch ($key)
{
case 'DELIMITER':
$len = strlen($val);
for ($i = 0; $i < $len; $i++) $r['delimeter'][] = $val{$i};
break;
case 'QUOTATION':
case 'QUOTATION1':
case 'QUOTATION2':
$r['quote'][] = $val;
break;
case 'LINECOMMENT':
case 'LINECOMMENT1':
case 'LINECOMMENT2':
$r['linecomment'][] = $val;
for ($i = 0; $i < strlen($val); $i++) $r['delimeter'][] = $val{$i};
break;
case 'CONTINUE_QUOTE':
$r['continue_quote'] = ($val{0} == 'y' || $val{0} == 'Y')?true:false;
break;
case 'COMMENTON': $r['commenton'][0] = $val; break;
case 'COMMENTOFF': $r['commentoff'][0] = $val; break;
case 'COMMENTON1': $r['commenton'][1] = $val; break;
case 'COMMENTOFF1': $r['commentoff'][1] = $val; break;
case 'ESCAPE':
$r['escape'] = $val;
break;
case 'CASE':
$r['case'] = ($val{0} == 'y' || $val{0} == 'Y')?true:false;
break;
case 'PREFIX':
case 'PREFIX1':
case 'PREFIX2':
case 'PREFIX3':
case 'PREFIX4':
case 'PREFIX5':
$r['prefix'][] = $val;
break;
case 'SUFFIX':
case 'SUFFIX1':
case 'SUFFIX2':
case 'SUFFIX3':
case 'SUFFIX4':
case 'SUFFIX5':
$r['suffix'][] = $val;
break;
case 'EMBED_HTML':
$r['special_stx'] = 'html';
break;
case 'SPECIAL_STX':
$r['special_stx'] = $val;
break;
case 'KEYWORD':
if (!is_array($r['keyword'])) $r['keyword'] = array();
$curr_ = count($r['keyword']);
$r['keyword'][$curr_] = array();
break;
}
}
else if (is_int($curr_))
{
$r['keyword'][$curr_][] = $r['case']?$line:strtolower($line);
}
}
fclose($fp);
if (!is_array($r['linecomment'])) $r['linecomment'] = array();
if (!is_array($r['commenton'])) $r['commenton'] = array();
if (!is_array($r['commentoff'])) $r['commentoff'] = array();
$r['delimeter'] = array_unique($r['delimeter']);
$this->rule[$rule] = $r;
$this->curr_rule = $rule;
unset($r);
return true;
}
function on($str)
{
$rule = &$this->rule[$this->curr_rule];
$str = preg_replace("/\n\r|\r\n|\r/", "\n", $str)."\n";
$ret = '<code class="'.$this->curr_rule.'">';
$delimeter = '';
$word = '';
$last_pos = 0;
$max = strlen($str);
$space = array("\n", "\t", " ");
for ($i=0; $i < $max; $i++)
{
$c = $str{$i};
$is_space = in_array($c, $space);
if ($is_space)
{
if (strlen($delimeter) > 0) {
if (count($this->_pr_comment($delimeter)) > 0) $c = '';
$ret .= $this->_pr_delimeter(&$str, &$i, $delimeter);
$delimeter = '';
} else if (strlen($word) > 0) {
$ret .= $this->_pr_word($word);
$word = '';
}
$ret .= $c;
}
else if (is_array($rule['prefix']) && in_array($c, $rule['prefix']))
{
$this->prefix = true;
$ret .= '<B>'.$c.'</B>';
}
else if (in_array($c, $rule['quote']))
{
if (strlen($delimeter) > 0) {
$ret .= $this->_pr_delimeter(&$str, &$i, $delimeter);
$delimeter = '';
} else if (strlen($word) > 0) {
$ret .= $this->_pr_word($word);
$word = '';
}
$ret .= $this->_pr_quote(&$str, &$i, $c);
}
else if (in_array($c, $rule['delimeter']))
{
$delimeter .= $c;
if (strlen($word) > 0) {
$ret .= $this->_pr_word($word);
$word = '';
}
}
else
{
if (strlen($delimeter) > 0) {
if (count($this->_pr_comment($delimeter)) > 0) $c = '';
$ret .= $this->_pr_delimeter(&$str, &$i, &$delimeter);
$delimeter = '';
}
$word .= $c;
}
}
$ret = rtrim($ret).'</code>';
return nl2br(strtr($ret, array(' '=>' ', "\t"=>' ')));
}
function onFile($filename)
{
if (!file_exists($filename)) return false;
if (function_exists('file_get_contents')) return $this->on(file_get_contents($filename));
else
{
$fp = fopen($filename, 'r');
$str = fread($fp, filesize($filename));
fclose($fp);
return $this->on($str);
}
}
function _pr_delimeter(&$str, &$offset, $delimeter)
{
$ret = '';
$this->prefix = false;
$compare = $this->_pr_comment($delimeter);
if (count($compare))
{
$compare = array_flip($compare); ksort($compare);
$compare = array_flip($compare); reset($compare);
$key = key($compare);
$val = current($compare);
if ($val > 0) {
$ret .= '<A>'.htmlspecialchars(substr($delimeter, 0, $val)).'</A>';
}
if ($key >= 1000) // 블럭 코멘트일때
{
$off = $this->rule[$this->curr_rule]['commentoff'][$key-1000];
$pos = strpos($str, $off, $offset);
$ret .= '<EM>'.htmlspecialchars(substr($delimeter, $val).substr($str, $offset, $pos - $offset + strlen($off))).'</EM>';
$offset = $pos + strlen($off) - 1;
}
else // 라인 코멘트일때
{
$pos = strpos($str, "\n", $offset);
$ret .= '<EM>'.htmlspecialchars(substr($delimeter, $val).substr($str, $offset, $pos - $offset)).'</EM>';
$offset = $pos - 1;
}
}
else
{
$ret = '<A>'.htmlspecialchars($delimeter).'</A>';
}
return $ret;
}
function _pr_quote(&$str, &$offset, $quote)
{
$count = 0;
$len = strlen($str);
$this->prefix = false;
for ($i = $offset+1; $i < $len; $i++)
{
if ($str{$i} == $this->rule[$this->curr_rule]['escape'])
{
if ($str{$i+1} == $this->rule[$this->curr_rule]['escape'] || $str{$i+1} == $quote) { $i++; continue; }
}
else if ($str{$i} == $quote)
{
$ret = substr($str, $offset, $i - $offset + 1);
$offset = $i;
break;
}
}
return '<Q>'.htmlspecialchars($ret).'</Q>';
}
function _pr_word($word)
{
$max = count($this->rule[$this->curr_rule]['keyword']);
if ($this->prefix) {
$this->prefix = false;
return '<SUB>'.htmlspecialchars($word).'</SUB>';
}
for ($i = 0; $i < $max; $i++)
{
$word_temp = $this->rule[$this->curr_rule]['case']?$word:strtolower($word);
if (in_array($word_temp, $this->rule[$this->curr_rule]['keyword'][$i]))
{
$ret = '<S class="'.($i+1).'">'.htmlspecialchars($word).'</S>';
return $ret;
}
}
return htmlspecialchars($word);
}
function _pr_comment($delimeter)
{
$compare = array();
foreach ($this->rule[$this->curr_rule]['linecomment'] as $key=>$val)
{
if ( is_int($tmp = strpos($delimeter, $val)) ) $compare[$key] = $tmp;
}
foreach ($this->rule[$this->curr_rule]['commenton'] as $key=>$val)
{
if ( is_int($tmp = strpos($delimeter, $val)) ) $compare[$key+1000] = $tmp;
}
return $compare;
}
}
/*
$syntax = new Syntax('js');
echo $syntax->on($str);
*/
$_POST['str'] = stripslashes($_POST['str']);
$syntax = new Syntax($_POST['type']);
echo $syntax->on($_POST['str']);
?>
<html>
<body>
<br>
구문강조 테스트<br>
<style>
/* 전체 스타일 */
CODE, CODE * {
color:black;
font-style:normal;
font-weight:normal;
text-decoration:none;
font-size:8pt;
font-family:돋움체;
}
CODE.html A {color:blue} /* 구분자 스타일 */
CODE.html B {color:green} /* Prefix 스타일 */
CODE.html SUB {color:green} /* Prefix 붙은 문자의 스타일 */
CODE.html EM {color:green} /* 코멘트 */
CODE.html Q {color:#000080} /* 따옴표로 묶은 거 */
CODE.html S.1 {color:blue;} /* 키워드 1 */
CODE.html S.2 {color:red;} /* 키워드 2 */
CODE.html S.3 {color:green;} /* 키워드 3 */
CODE.js A {color:red} /* 구분자 스타일 */
CODE.js EM {color:#FFCC00} /* 코멘트 */
CODE.js Q {color:#606060} /* 따옴표로 묶은 거 */
CODE.js S.1 {color:blue;} /* 키워드 1 */
CODE.js S.2 {color:red;} /* 키워드 2 */
CODE.js S.3 {color:green;} /* 키워드 3 */
CODE.js S.4 {color:#999900;} /* 키워드 4 */
CODE.js S.5 {color:#800000;} /* 키워드 5 */
CODE.css Q {color:#FF00FF} /* 따옴표로 묶은 거 */
CODE.css EM {color:green} /* 코멘트 */
CODE.css S.1 {color:blue} /* 키워드 1 */
CODE.css S.2 {color:red} /* 키워드 2 */
</style>
<form method="post">
<select name="type">
<option value="js">자바스크립트</option>
<option value="html">HTML</option>
<option value="css">CSS</option>
<option value="python">파이선기본</option>
<option value="cpp">C++</option>
<option value="c">C</option>
</select><br>
<textarea name="str" rows="10" cols="80"><?=htmlspecialchars($_POST['str'])?></textarea><br>
<input type="submit" value="전송">
</form>
</body>
</html>