Coloration syntaxique de CSS
Posté : 14 sept. 2005, 17:19
Je vous propose ici un colorateur syntaxique CSS. Celui-ci n'est pas destiné à un site Web ! En effet, son algorithme de coloration est bien trop couteux en ressources, bien que pour les petits sites il ne poserait aucun probleme. Toutefois, son utilisation dans une application, ou un script destiné à peu d'utilisateur (moins de 10 utilisateurs simultannés).
Je l'ai programmé en PHP5. Il se présente sous forme de class.
Les fonctions requises
Je l'ai programmé en PHP5. Il se présente sous forme de class.
Les fonctions requises
function Parser_STR_char_is_escaped( $string, $position, $start = 0 )
{
$length = strlen( $string );
if ( ( $position === $start ) || ( $length <= $position ) )
return false;
$position--;
$escaped = false;
while ( ( $position >= $start ) && ( $string{ $position } === '\\' ) )
{
$position--;
$escaped = !$escaped;
}
return $escaped;
}
function Parser_STR_is_space( $c )
{
return ereg( "[ \t\n]", $c );
}
function Parser_STR_str_in_str( $needle, $haystack )
{
if ( strpos( $haystack, $needle ) === false )
return false;
else
return true;
}
La classe
class CSS_Parser
{
private $bracket_list = '()[]{}';
private $punct_list = ',>;:*=+';
private $quote_list = '\'"';
private $css_combined_punct = array
(
'|=' => 1, '~=' => 1
);
private $css_function_name = array
(
'attr' => 1, 'counter' => 1, 'lang' => 1, 'url' => 1, 'wave' => 1
);
private $css = '';
private $len = 0;
private $css_array = array();
private $size = 0;
public function __construct
(
$sql // string Requête SQL
)
{
$this->parse( $sql );
$this->format_html();
}
public function display()
{
echo $this->css;
}
private function identifier( $c )
{
return
(
ereg( '[-A-Za-z0-9_%/\#\.\!]', $c )
);
}
private function parse( $css )
{
$this->css = $css;
// Convertit tous les retours ligne au type Unix
$this->css = str_replace( "\r\n", "\n", $this->css );
$this->css = str_replace( "\r", "\n", $this->css );
$this->len = strlen( $this->css );
if ( strlen( $css ) === 0 )
return array();
$count1 = 0;
$count2 = 0;
$inbloc = false;
while ( $count2 < $this->len )
{
$c = $this->css{ $count2 };
$count1 = $count2;
if ( Parser_STR_is_space( $c ) )
{
$count2++;
continue;
}
// Recherche les commentaires
if ( ( $c === '/' ) && ( $count2 + 1 < $this->len ) && ( $this->css{ $count2 + 1 } === '*' ) )
{
$count2++;
$pos = strpos( $this->css, "*/", $count2 );
if ( $pos === false )
die( 'PARSER_CSS_ERROR::UNCLOSED_COMMENT@' . $count1 . "\n" . 'STR: ' . substr( $this->css, $count1 ) );
$pos += 2;
if ( $pos < $count2 )
$count2 = $this->len;
else
$count2 = $pos;
$this->array_add( 'comment', substr( $this->css, $count1, $count2 - $count1 ) );
continue;
}
// Recherche les chaines entre quotes
if ( Parser_STR_str_in_str( $c, $this->quote_list ) )
{
$startpos = $count2;
$quotetype = $c;
$count2++;
$pos = $count2;
$oldpos = 0;
while ( $pos < $this->len )
{
$oldpos = $pos;
$pos = strpos( $this->css, $quotetype, $oldpos );
// Quote pas fermée
if ( $pos === false )
die( 'PARSER_CSS_ERROR::UNCLOSED_QUOTE@' . $startpos . "\n" . 'STR: ' . htmlspecialchars( $quotetype ) );
// Si le quote est le premier caractère, il ne peut être echappé.
// Ainsi le reste du code n'a pas besoin d'être exécuté.
if ( $pos === 0 )
break;
// Recherche l'échappement utilisant \
if ( ( $pos < $this->len ) && ( Parser_STR_char_is_escaped( $this->css, $pos ) ) )
{
$pos++;
continue;
}
else
{
break;
}
}
$count2 = $pos;
$count2++;
$this->array_add( 'quote', substr( $this->css, $count1, $count2 - $count1 ) );
continue;
}
// Recherche les brackets
if ( Parser_STR_str_in_str( $c, $this->bracket_list ) )
{
$count2++;
if ( Parser_STR_str_in_str( $c, '([{' ) )
$type = 'open';
else
$type = 'close';
if ( Parser_STR_str_in_str( $c, '()' ) )
$style = 'round';
elseif ( Parser_STR_str_in_str( $c, '[]' ) )
$style = 'square';
else
{
$style = 'curly';
if ( $type === 'open' )
$inbloc = true;
else
$inbloc = false;
}
$this->array_add( 'punct_bracket_' . $style . '_' . $type, $c );
continue;
}
// Recherche la ponctuation
if ( Parser_STR_str_in_str( $c, $this->punct_list ) )
{
while ( ( $count2 < $this->len ) && ( Parser_STR_str_in_str( $this->css{ $count2 }, $this->punct_list ) ) )
$count2++;
$length = $count2 - $count1;
if ( $length === 1 )
$data = $c;
else
$data = substr( $this->css, $count1, $length );
if ( $length === 1 )
{
switch ( $data )
{
case ';':
$suffix = '_property_end';
break;
case ',':
$suffix = '_listsep';
break;
case ':':
$suffix = '_2points';
break;
case '=':
$suffix = '_assing';
break;
default:
$suffix = '';
break;
}
$this->array_add( 'punct' . $suffix, $data );
}
elseif ( isset( $this->css_combined_punct[ $data ] ) )
{
$this->array_add( 'punct_assign', $data );
}
else
{
$first = $data{ 0 };
$last2 = $data{ $length - 2 } . $data{ $length - 1 };
if ( $first === ';' || $first === ',' )
{
$count2 = $count1 + 1;
$data = $first;
}
elseif ( $last2 === '/*' )
{
$count2 -= 2;
$data = substr( $this->css, $count1, $count2 - $count1 );
}
$this->array_add( 'punct', $data );
}
continue;
}
if ( $this->identifier( $c ) || $c === '@' )
{
$count2++;
while ( $count2 < $this->len && $this->identifier( $this->css{ $count2 } ) )
$count2++;
$string = substr( $this->css, $count1, $count2 - $count1 );
if ( $c === '@' )
{
$this->array_add( 'selector_at', $string );
}
elseif ( !$inbloc )
{
$pos = strpos( $string, '#' );
if ( $pos !== false )
{
if ( $pos === 0 )
$this->array_add( 'selector_id', $string );
else
{
$this->array_add( 'selector_html', substr( $string, 0, $pos ) );
$this->array_add( 'selector_id', substr( $string, $pos ) );
}
}
else
{
$pos = strpos( $string, '.' );
if ( $pos !== false )
{
if ( $pos === 0 )
$this->array_add( 'selector', $string );
else
{
$this->array_add( 'selector_html', substr( $string, 0, $pos ) );
$this->array_add( 'selector', substr( $string, $pos ) );
}
}
else
$this->array_add( 'selector_html', $string );
}
}
else
{
$this->array_add( 'alpha', $string );
}
continue;
}
$count2++;
}
if ( $this->size > 0 )
{
$prev_d = '';
$prev_t = '';
$prev_l = '';
$curr_d = '';
$curr_t = '';
$curr_l = '';
$next_d = $this->css_array[ 0 ][ 'data' ];
$next_t = $this->css_array[ 0 ][ 'type' ];
$next_l = strtolower( $next_d );
}
for ( $i = 0; $i < $this->size; $i++ )
{
$prev_d = $curr_d;
$prev_t = $curr_t;
$prev_l = $curr_l;
$curr_d = $next_d;
$curr_t = $next_t;
$curr_l = $next_l;
if ( $i + 1 < $this->size )
{
$next_d = $this->css_array[ $i + 1 ][ 'data' ];
$next_t = $this->css_array[ $i + 1 ][ 'type' ];
$next_l = strtolower( $next_d );
}
else
{
$next_d = '';
$next_t = '';
$next_l = '';
}
if ( $curr_t === 'alpha' )
{
if ( $curr_l === '!important' )
$type = 'important';
elseif ( $next_t === 'punct_2points' )
$type = 'property';
else
$type = 'value';
$this->css_array[ $i ][ 'type' ] = $type;
}
}
}
private function format_html()
{
$str = '';
$arr = array();
$arr[ 0 ] = '';
$arr[ 1 ] = '';
$arr[ 2 ] = '';
$arr[ 3 ] = $this->css_array[ 0 ][ 'type' ];
$inbloc = false;
for ( $i = 0; $i < $this->size; $i++ )
{
$indent = 0;
$before = '';
$after = '';
if ( $i + 1 < $this->size )
$arr[ 4 ] = $this->css_array[ $i + 1 ][ 'type' ];
else
$arr[ 4 ] = '';
for ( $j = 0; $j < 4; $j++ )
$arr[ $j ] = $arr[ $j + 1 ];
switch ( $arr[ 2 ] )
{
case 'comment':
$after = '<br />' . "\n";
break;
case 'important':
$before = ' ';
break;
case 'property':
$this->css_array[ $i ][ 'data' ] = strtolower( $this->css_array[ $i ][ 'data' ] );
break;
case 'punct':
$before = ' ';
if ( $arr[ 3 ] !== 'digit_int' )
$after = ' ';
break;
case 'punct_2points':
if ( $inbloc )
$after = ' ';
break;
case 'punct_bracket_curly_close':
$inbloc = false;
if ( $arr[ 1 ] !== 'punct_bracket_curly_open' )
$before = '</div>';
$after = '<br /><br />' . "\n";
break;
case 'punct_bracket_curly_open':
$inbloc = true;
$before = ' ';
if ( $arr[ 3 ] !== 'punct_bracket_curly_close' )
$after = '<div class="indent1">';
else
$after = ' ';
break;
case 'punct_bracket_round_close':
$before = ' ';
if ( $arr[ 3 ] !== 'punct_property_end' )
$after = ' ';
break;
case 'punct_bracket_round_open':
$after = ' ';
break;
case 'punct_listsep':
$after = ' ';
break;
case 'punct_property_end':
if ( $arr[ 3 ] !== 'comment' )
$after = '<br />' . "\n";
break;
case 'quote':
if ( $arr[ 3 ] !== 'punct_bracket_square_close' && $arr[ 3 ] !== 'punct_listsep' && $arr[ 3 ] !== 'punct_property_end' )
$after = ' ';
break;
case 'selector':
break;
case 'selector_at':
$after = ' ';
break;
case 'selector_html':
if ( $arr[ 3 ] === 'selector_html' )
$after = ' ';
break;
case 'selector_id':
break;
case 'value':
if ( $arr[ 3 ] === 'value' )
$after = ' ';
break;
}
$str .= $before . $this->get_html( $this->css_array[ $i ] ) . $after;
}
$this->css = &$str;
}
private function get_html( $arr )
{
$class = '';
if ( substr( $arr[ 'type' ], 0, 5 ) === 'punct' )
{
$pos = strpos( $arr[ 'type' ], '_' );
if ( $pos !== false )
$class = 'css_' . substr( $arr[ 'type' ], 0, $pos );
else
$class = 'css_' . $arr[ 'type' ];
}
else
{
$class = 'css_' . $arr[ 'type' ];
}
return '<span class="' . $class . '">' . htmlspecialchars( $arr[ 'data' ], ENT_NOQUOTES ) . '</span>';
}
private function array_add( $type, $data )
{
$this->css_array[] = array
(
'type' => $type,
'data' => $data,
);
$this->size++;
}
}
L'exemple
$css =& new CSS_Parser( '
div#menu {
position: absolute; /* placement du menu, à modifier selon vos besoins */
top: 0% !important;
left: 0;
color:red;
background: url( img/smth.png );
}
span[class|=truc] {
color: blue;
}
a[href="page.html"] {
}
@import url(\'style.css\');
@charset "ISO-8859-1";
');
$css->display();
Le CSS de mise en forme
Code : Tout sélectionner
.css_comment{color: #999;}
.css_punct{color: #f0f;}
.css_quote{color: #080;}
.css_property{color: #009;}
.css_selector{color: #f0f;}
.css_selector_at{color: #099; font-weight: bold;}
.css_selector_id{color: #f90;}
.css_selector_html{color: #000;}
.css_important{color: #f00; font-weight: bold;}
.css_value{color: #00f;}
.indent1{ margin-left: 1em;}