Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Wyrazenia Regularne
Forum PHP.pl > Forum > PHP
y3ti
Witam,

Wraz z moim znajomym piszemy skrypt odpowiedzialny za kolorowanie skladni w artykulach i forum w naszym serwisie. Jak dotad wszystko nam szlo jak po masle, ale natknelismy sie na pewien problem, z ktorym od pewnego juz czasu nie mozemy
dac sobie sami rady.

Problem polega na tym, ze jak kolorujemy slowa kluczowe (dla przykladu niech to bedzie int z C) to slowa te nam sie koloruja, ale niestety rowniez wtedy, jesli
slowo to jest lancuchem znakow. Mamy powiedzmy taki oto prosty kod napisany w C:

Kod
#include <stdio.h>

int main()
{
   printf("Hello int World \n");

   return 0;
}



i teraz powiedzmy, ze chcemy pokolorowac wszystkie int na niebiesko, wiec:

  1. <?php 
  2.  
  3.  $zrodlo = join('', file('zrodlo.c'));
  4.  $zrodlo = htmlspecialchars( $zrodlo );
  5.  
  6.  $zrodlo = preg_replace('/bintb/', '<span style=\"color: blue\">int</span>', $zrodlo );
  7.  
  8. ?>


I teraz pytanie. Jak powinno wygladac wyrazenie regularne, aby nie kolorowalo tekstu w cudzyslowach?

Probowalem tak:

  1. <?php
  2.  
  3.  $zrodlo = preg_replace('/([^\"]*?)bintb(.*[^\"])/', '1<span style=\"color:blue\">int</span>2', $zrodlo );
  4.  
  5. ?>


Niestety tez nie dziala. Juz nie mam pomyslu jak mozna sobie z tym poradzic.

Bede wdzieczny za wszelkie informacje.
DeyV
Wydaje mi się, zę w takim przypadku najlepiej by było zobaczyć jak robią to inni.
Niestety - problem nie jest łatwy, i nie ma jednego dobrego rozwiązania.
Te które widziałem napisane w php nie zapewniają jednak 100% skteczności (jak choćby kolorowanie SQL na tym forum - które jest tylko delikatnie zmodyfikowanym orginalnym mechanizmem z IPB)

Widziałe natomiast niedawno skrypt, który kolorował kod dokłądnie tak, jak by "mi się to podobało" Niestety - jest napisany w JS... Mimo to sądzę, że przestudiowanie jesgo konstrukcji może przynieść Wam spory pożytek
http://gosu.pl/dhtml/JavascriptDecoder.html

ps. Brawo, cagrET
cagrET
W tym dekoderze kolorowanie skladni dziala niestety tylko na Mozilli. Na IE nie dziala poniewaz string zawierajacy znak nowej lini "\n" ktory został wygenerowany przez javascript nie działa w tagach PRE, nie mam pojęcia czemu.

Jak znajdę trochę czasu to spróbuję napisać w php kolorowanie składni dla C/SQL i może dołącze mały tutorial (niestety pewnie po angielsku). W sumie to nie jest trudne, jak się idzie na skróty tak jak ja to zrobiłem, niezbyt profejonalnie, ale spełnia swoje założenia, najważniejsze że działa ! Już słyszałem od paru osób że jedynym słusznym rozwiązaniem tego problemu jest napisanie parsera, ktory zajmie z minimum 200 KB, a napisanie go zajmie z kilka miechów, no thx winksmiley.jpg

W sumie to kolorowanie tej skladni C mozna by napisac w javascript, a później mały skrypt który pobiera na stronie wszystkie elementy PRE z klasa np "clanguage" i je koloruje.
Kod
<pre class="clanguage">
#include <stdio.h>

int main()
{
  printf("Hello int World \n");

  return 0;
}
</pre>
y3ti
Postanowilem jeszcze raz poszukac na googlach i znalazlem kilka rozwiazan. Idealnym (no prawie) zostal projekt Geshi - skrypt w php do kolorowania skladni.

http://qbnz.com/highlighter/

Mozliwosci:

- dosc dobrze koloruje, sa drobne bledy, ale mozna je sobie odpusic
- projekt jest wciaz rozwijany
- numerowanie linii kodu
- latwa rozbudowa parsera (dodanie "nowych jezykow")
- latwo mozna modyfikowac style w kolorowaniu (te proponowane przez tworcow maja
brzydkie kolorki, ale to tylko kwestia gustu)

Jedyna wada jaka znalazlem jest to, ze nie zwraca nam lancucha z pokolorowanym tekstem, tylko odrazu wyswietla. Naszczescie mozna sie z tym uporac za pomoca
manipulacji buforem.
DeyV
Bardzo ciekawy projekt. Jestem pod wrażeniem. Załuje tylko, że powstał dopiero teraz (first release: 2004-07-17 05:00) ponieważ poświęciłem kiedyś sporo czasu na znalezienie czegoś takiego w php (w innych językach oczywiście były odpowiednie moduły) i niestety - nic z tego nie wyszło.

A teraz juyż chyba jest nawet odpowiedni mod dla bb, wykorzystujący ten system kolorowania. nono...
Parti
Cytat(cagrET @ 2004-08-20 02:32:50)
Już słyszałem od paru osób że jedynym słusznym rozwiązaniem tego problemu jest napisanie parsera, ktory zajmie z minimum 200 KB, a napisanie go zajmie z kilka miechów, no thx winksmiley.jpg

To bardzo ciekawe. Do kolorowania składni wystarczy lekser, a nie parser. Napisanie go wcale nie zajmuje pare miesięcy. Widze, że ostatnio ten temat dość często poruszany jest na forum. Proponuje zatem na początek zainteresować się programem GNU Enscript

Może kiedyś zmusze się do napisania czegoś, bo ten problem również i mnie troche interesuje.

Teraz podam przykład prostego leksera. Nie daje żadnej gwarancji, że działa. Jest mocno uproszczony. To tylko przykład jak napisać lekser.

  1. <?php
  2.  
  3.     class cToken
  4.     {
  5.         var $type;
  6.         var $val;
  7.     }
  8.     // typy tokenow
  9.     define('OTHER', 0);
  10.     define('OPERATOR', 1);
  11.     define('VARIABLE', 2);
  12.     define('COMMENT', 3);
  13.     define('STRING_VAL', 4);
  14.     define('NUMBER', 5);
  15.     define('IDEN', 6);
  16.     define('KEYWORD', 7);
  17.  
  18.     // kawalek kodu php do pokolorawania
  19.     $str = &#092;"echo 12 . \"jakis tekst i \"tekst\"\"; # komentarznecho funkcja($zmienna);\";
  20.     $pos = 0;
  21.     $len = strlen($str);
  22.  
  23.     $slowa_kluczowe = array('echo', 'class'); // itd.
  24.  
  25.     function gettoken(&$token)
  26.     {
  27.         global $pos;
  28.         global $str;
  29.         global $len;
  30.         global $slowa_kluczowe;
  31.  
  32.         if ($pos == $len) // doszlismy do konca napisu, zwracamy zero
  33.             return 0;
  34.  
  35.         switch ($str{$pos})
  36.         {
  37.             case &#092;" \":  // wszelkie białe znaki zostawiamy jak są
  38.             case &#092;"t\": 
  39.             case &#092;"r\":
  40.             case &#092;"n\": 
  41.                 $token->type = OTHER;
  42.                 $token->val = $str{$pos++};
  43.                 return -1;
  44.  
  45.             case &#092;";\": // tutaj trzeba by wypisac wszystkie jedno-znakowe operatory i znaki przestankowe
  46.             case &#092;"(\": case \")\":
  47.             case &#092;"[\": case \"]\":
  48.             case &#092;".\": $token->type = OPERATOR; $token->val = $str{$pos++}; return -1;
  49.  
  50.             // niektore znaki moga byc czescia dluzszego oeratora
  51.             case &#092;"+\":
  52.                 $token->type = OPERATOR;
  53.                 if ($str{$pos+1} == &#092;"+\") // operator ++
  54.                 {
  55.                     $token->val = &#092;"++\";
  56.                     $pos += 2;
  57.                 }
  58.                 else if ($str{$pos+1} == &#092;"=\") // operator +=
  59.                 {
  60.                     $token->val = &#092;"+=\";
  61.                     $pos += 2;
  62.                 }
  63.                 else // jednak jest tylko plus
  64.                 {
  65.                     $token->val = &#092;"+\";
  66.                     $pos++;
  67.                 }
  68.  
  69.                 return -1;
  70.             // tutaj trzeba by rozwazyc przypadki z innymi tego typu oeratorami jak -, --, -=, *, *= itp
  71.  
  72.             case &#092;"$\": // zmienna
  73.                 $nazwa = &#092;"\";
  74.                 $pos++;
  75.                 while (ctype_alnum($str{$pos}) || $str{$pos} == &#092;"_\")
  76.                 {
  77.                     $nazwa .= $str{$pos};
  78.                     $pos++;
  79.                 }
  80.  
  81.                 $token->type = VARIABLE;
  82.                 $token->val = '$' . $nazwa;
  83.  
  84.                 return -1;
  85.             
  86.             case &#092;"#\": // komentarz
  87.             // ten typ komentarza jest bardzo prosty do rozpatrzenia
  88.             // najtrudniejszy jest /* */
  89.                 $koment = &#092;"\";
  90.  
  91.                 while ($str{$pos} != &#092;"n\")
  92.                     $koment .= $str{$pos++};
  93.  
  94.                 $token->type = COMMENT;
  95.                 $token->val = $koment;
  96.                 
  97.                 return -1;
  98.  
  99.             case &#092;"\"\": // stala napisowa, podobnie dla '
  100.                 $napis = &#092;"\"\";
  101.                 $pos++;
  102.  
  103.                 while ($str{$pos} != &#092;"\"\")
  104.                 {
  105.                     if ($str{$pos} == &#092;"\") // wszelkie wyescapowane znaki
  106.                     {
  107.                         $napis .= &#092;"\";
  108.                         $pos++;
  109.                         $napis .= $str{$pos};
  110.                     }
  111.                     else
  112.                         $napis .= $str{$pos};
  113.  
  114.                     $pos++;
  115.                 }
  116.  
  117.                 $pos++;
  118.                 $token->type = STRING_VAL;
  119.                 $token->val = $napis . &#092;"\"\";
  120.  
  121.                 return -1;
  122.  
  123.             default:
  124.                 if (ctype_digit($str{$pos})) // rozwaze tylko liczby calkowite
  125.                 {
  126.                     $num = &#092;"\";
  127.  
  128.                     while (ctype_digit($str{$pos}))
  129.                         $num .= $str{$pos++};
  130.  
  131.                     $token->type = NUMBER;
  132.                     $token->val = $num;
  133.  
  134.                     return -1;
  135.                 }
  136.                 else if (ctype_alpha($str{$pos}) || $str{$pos} == &#092;"_\") // identyfikator jakis
  137.                 { // byc moze slowo kluczowe lub nazwa funkcji
  138.                     $iden = &#092;"\";
  139.  
  140.                     while (ctype_alnum($str{$pos}) || $str{$pos} == &#092;"_\")
  141.                         $iden .= $str{$pos++};
  142.  
  143.                     $token->val = $iden;
  144.  
  145.                     if (array_search($iden, $slowa_kluczowe) === false) // nie jest slowem kluczowym
  146.                     { // malo wydajne przeszukiwanie, tutaj wlasciwie trzeba by wpakowac jakis maly auto
  147. at skonczony
  148.                         $token->type = IDEN;
  149.                         return -1;                        
  150.                     }
  151.                     else // slowo kluczowe
  152.                     {
  153.                         $token->type = KEYWORD;
  154.                         return -1;
  155.                     }
  156.                 }
  157.                 else // bledny, nierozpoznany znak (w naszym przypadku poprostu go zwracamy)
  158.                 {
  159.                     $token->type = OTHER;
  160.                     $token->val = $str{$pos++};
  161.                     return -1;
  162.                 }
  163.         }
  164.     }
  165.  
  166.     $token = new cToken;
  167.  
  168.     echo &#092;"<pre>\";
  169.  
  170.     while (($t = gettoken($token)) != 0)
  171.     {
  172.         switch ($token->type)
  173.         {
  174.             case OTHER: echo $token->val; break;
  175.             case OPERATOR: echo &#092;"<font color=red>\" . $token->val . \"</font>\"; break;
  176.             case VARIABLE: echo &#092;"<font color=blue>\" . $token->val . \"</font>\"; break;
  177.             case COMMENT: echo &#092;"<font color=green>\" . $token->val . \"</font>\"; break;
  178.             case STRING_VAL: echo &#092;"<font color=pink>\" . $token->val. \"</font>\"; break;
  179.             case NUMBER: echo &#092;"<font color=gray>\" . $token->val . \"</font>\"; break;
  180.             case IDEN: echo &#092;"<font color=yellow>\" . $token->val . \"</font>\"; break;
  181.             case KEYWORD: echo &#092;"<font color=magenta>\" . $token->val . \"</font>\"; break;
  182.             default:
  183.                 echo &#092;"hm.. jakis blad?\";
  184.         }
  185.     }
  186.     
  187.     echo &#092;"</pre>\";
  188.  
  189. ?>
cagrET
Cytat
To bardzo ciekawe. Do kolorowania składni wystarczy lekser, a nie parser.
Możliwe, miałem też na myśli ten Dekoder, czyli formatowanie kodu. Niestety jestem zielony w tej dziedzinie. Btw. masz może jakieś materiały / linki do artykułów na ten temat ? (leksery, parsery).
Parti
Cytat(cagrET @ 2004-08-20 15:55:14)
Btw. masz może jakieś materiały / linki do artykułów na ten temat ? (leksery, parsery).

Polecam książke: Aho, Sethi, Ullman "Kompilatory" (jakiś czas temu wydana po Polsku) tzw. Dragon Book.

Manuale do Flexa (automatyczny generator lekserów) oraz Bisona (automatyczny generator parserów).

Jeszcze mi sie przypomniało. Na pierwszym roku informatyki na przedmiocie programowanie można poznać podstawy pisania kompilatorów. Tutaj skrypt do tego przedmiotu.
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.