Advertisement
snake5

regex alternative basic impl v0.2

Feb 28th, 2013
257
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 6.95 KB | None | 0 0
  1. <?php
  2.  
  3. $test = "crud <img src='foo' alt='heh'></img> crud <img src='blah' alt='foo' /> crud";
  4.  
  5. //$expr = "string '<img' not string '/>' string 'src=' extract quoted '\\'','\\\"' '\\\\' not string '<img' string '/>'";
  6. $expr = "'<img' not '/>', '</img' 'src=' not '<img' '>'";
  7.  
  8.  
  9. /*
  10.     Searching in a multimatch environment...
  11.     > goal is to grab the first possible match
  12.     > need anything shorter? use "not" to avoid repetition
  13.     - " A A B A C C "
  14.     1) "string 'A' string 'C'" => "A A B A C"
  15.     2) "string 'A' not string 'A' string 'C'" => "A C"
  16.     3) "string 'A' not string 'B' string 'C'" => "A C"
  17.     --- process ---
  18.     1) find A => 1, find C => 9  ||  1-10
  19.     2) find A => 1, ( find A => 3, find C => 9 ) => not FAIL, restart from 2 ..
  20.     .. find A => 3, ( find A => 7, find C => 9 ) => not FAIL, restart from 4 ..
  21.     .. find A => 7, ( find A => false, find C => 9 ) => not SUCCESS  ||  7-10
  22.     3) find A => 1, ( find B => 5, find C => 9 ) => not FAIL, restart from 2 ..
  23.     .. find A => 3, ( find B => 5, find C => 9 ) => not FAIL, restart from 4 ..
  24.     .. find A => 7, ( find B => false, find C => 9 ) => not SUCCESS  ||  7-10
  25.     --- rules ---
  26.    
  27. */
  28.  
  29.  
  30. function hent( $txt ){ return htmlspecialchars( $txt, ENT_QUOTES, 'UTF-8' ); }
  31. function hdr( $name ){ echo "<h3>{$name}</h3>\n"; }
  32.  
  33.  
  34. hdr( "input" );
  35. echo "test data: \"  <b>".hent( $test )."</b>  \"\n<br />\n";
  36. echo "pattern: \"  <b>".hent( $expr )."</b>  \"\n<br />\n";
  37.  
  38. /*
  39.     Tokenize
  40. */
  41. function coltok( $toks )
  42. {
  43.     $out = array();
  44.     foreach( $toks as $tok )
  45.         $out []= "{ type={$tok->type}, data='{$tok->data}', pos={$tok->pos} }";
  46.     return $out;
  47. }
  48. function mktoken( $type, $data, $pos ){ return (object) array( 'type' => $type, 'data' => $data, 'pos' => $pos ); }
  49. function te_parse_number( $expr, &$i, $len, &$out, &$errors )
  50. {
  51.     $pos = $i;
  52.     while( strpos( "0123456789", $expr[ $i ] ) !== false )
  53.         $i++;
  54.     $out[] = mktoken( 'ident', substr( $expr, $pos, $i - $pos ), $pos );
  55. }
  56. function te_parse_ident( $expr, &$i, $len, &$out, &$errors )
  57. {
  58.     $pos = $i;
  59.     while( strpos( "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_", $expr[ $i ] ) !== false )
  60.         $i++;
  61.     $out[] = mktoken( 'ident', substr( $expr, $pos, $i - $pos ), $pos );
  62. }
  63. function te_parse_string( $expr, &$i, $len, &$out, &$errors )
  64. {
  65.     $pos = $i;
  66.     $ec = $expr[ $i ];
  67.     $i++;
  68.     $outstr = '';
  69.     for( ; $i < $len; ++$i )
  70.     {
  71.         $c = $expr[ $i ];
  72.         if( $c == '\\' )
  73.         {
  74.             $i++;
  75.             $nc = $expr[ $i ];
  76.             if( $nc == 'n' ) $outstr .= "\n";
  77.             elseif( $nc == 't' ) $outstr .= "\t";
  78.             elseif( $nc == '\\' ) $outstr .= '\\';
  79.             else
  80.                 $outstr .= $c.$nc;
  81.         }
  82.         elseif( $c == $ec )
  83.         {
  84.             $out[] = mktoken( 'string', $outstr, $pos );
  85.             return;
  86.         }
  87.         else
  88.             $outstr .= $c;
  89.     }
  90. }
  91. function tokenize_expr( $expr )
  92. {
  93.     $len = strlen( $expr );
  94.     $out = array();
  95.     $errors = array();
  96.     for( $i = 0; $i < $len; ++$i )
  97.     {
  98.         $c = $expr[ $i ];
  99.        
  100.         // whitespace
  101.         if( strpos( " \t\n\r", $c ) !== false )
  102.             continue;
  103.         // special symbols
  104.         elseif( strpos( ",", $c ) !== false )
  105.             $out[] = mktoken( 'special', $c, $i );
  106.         // strings
  107.         elseif( strpos( "\'\"", $c ) !== false )
  108.             te_parse_string( $expr, $i, $len, $out, $errors );
  109.         // numbers
  110.         elseif( strpos( "0123456789", $c ) !== false )
  111.             te_parse_number( $expr, $i, $len, $out, $errors );
  112.         // keywords
  113.         elseif( strpos( "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_", $c ) !== false )
  114.             te_parse_ident( $expr, $i, $len, $out, $errors );
  115.         else
  116.             $errors[] = "invalid character at position {$i} ({$c})";
  117.     }
  118.     if( count( $errors ) )
  119.     {
  120.         var_dump( coltok( $out ), $errors );
  121.         die;
  122.     }
  123.     return $out;
  124. }
  125.  
  126. hdr( 'tokenizer' );
  127. $tokens = tokenize_expr( $expr );
  128. var_dump( coltok( $tokens ) );
  129.  
  130.  
  131. function mkparseitem( $type, $data ){ return (object) array( 'type' => $type, 'data' => $data ); }
  132. function gen_parsetree( $tokens, &$from = null )
  133. {
  134.     $ptree = array();
  135.     $len = count( $tokens );
  136.     $errors = array();
  137.     $i = 0;
  138.     $onlyone = false;
  139.     if( isset( $from ) )
  140.     {
  141.         $i = $from;
  142.         $onlyone = true;
  143.     }
  144.     for( ; $i < $len; ++$i )
  145.     {
  146.         $token = $tokens[ $i ];
  147.        
  148.         if( $token->type == 'string' )
  149.         {
  150.             $items = array( $token->data );
  151.             $i++;
  152.             while( count( $tokens ) > $i && $tokens[ $i ]->type == 'special' && $tokens[ $i ]->data == ',' )
  153.             {
  154.                 $i++;
  155.                 if( $tokens[ $i ]->type != 'string' )
  156.                 {
  157.                     $errors = "expected string after string and ','";
  158.                     $i--; continue;
  159.                 }
  160.                 $items[] = $tokens[ $i ]->data;
  161.                 $i++;
  162.             }
  163.             $i--;
  164.             $ptree[] = mkparseitem( 'string', $items );
  165.         }
  166.         elseif( $token->type == 'ident' && $token->data == 'not' )
  167.         {
  168.             $i++;
  169.             $notwhat = gen_parsetree( $tokens, $i );
  170.             $i++;
  171.             $befwhat = gen_parsetree( $tokens, $i );
  172.             $ptree[] = mkparseitem( 'not', array( $notwhat, $befwhat ) );
  173.         }
  174.         else
  175.         {
  176.             $errors[] = "unexpected token found: {$token->type} '{$token->data}'";
  177.         }
  178.         if( $onlyone )
  179.         {
  180.             $from = $i;
  181.             break;
  182.         }
  183.     }
  184.     if( count( $errors ) )
  185.     {
  186.         var_dump( $ptree, $errors );
  187.         die;
  188.     }
  189.     return $ptree;
  190. }
  191.  
  192. hdr( 'parsing tree' );
  193. $ptree = gen_parsetree( $tokens );
  194. var_dump( $ptree );
  195.  
  196.  
  197. function do_matching( $ptree, $str, $ofs = 0 )
  198. {
  199. restart:
  200.     if( $ofs >= strlen( $str ) )
  201.         return false;
  202.     $first = null;
  203.     foreach( $ptree as $pitem )
  204.     {
  205.         if( $pitem->type == 'string' )
  206.         {
  207.             $pos = strlen( $str );
  208.             $matched = null;
  209.             foreach( $pitem->data as $ms )
  210.             {
  211.                 $pp = strpos( $str, $ms, $ofs );
  212.                 if( $pp === false ) continue;
  213.                 $prevpos = $pos;
  214.                 $pos = min( $pos, $pp );
  215.                 if( $pos != $prevpos )
  216.                     $matched = $ms;
  217.             }
  218.             if( $pos >= strlen( $str ) )
  219.                 return false;
  220.             else
  221.             {
  222.                 if( $first === null )
  223.                     $first = $pos;
  224.                 $ofs = $pos + strlen( $matched );
  225.             }
  226.         }
  227.         else if( $pitem->type == 'not' )
  228.         {
  229.             $p1 = do_matching( $pitem->data[0], $str, $ofs );
  230.             $p2 = do_matching( $pitem->data[1], $str, $ofs );
  231.             if( $p2 === false || ( $p1 !== false && $p1[0] < $p2[0] ) )
  232.             {
  233.                 $ofs = ( $first === null ? $ofs : $first ) + 1;
  234.                 goto restart;
  235.             }
  236.             if( $first === null )
  237.                 $first = $p2[ 0 ];
  238.             $ofs = $p2[1];
  239.         }
  240.     }
  241.     if( $first === null )
  242.         return false;
  243.     return array( $first, $ofs );
  244. }
  245.  
  246. function multimatch( $ptree, $str )
  247. {
  248.     $match = do_matching( $ptree, $str );
  249.     if( $match === false )
  250.         return array();
  251.     $matches = array( $match );
  252.     while( $match !== false )
  253.     {
  254.         $match = do_matching( $ptree, $str, $match[0]+1 );
  255.         if( $match !== false )
  256.             $matches[] = $match;
  257.     }
  258.     return $matches;
  259. }
  260.  
  261. hdr( 'matching' );
  262. $out = do_matching( $ptree, $test );
  263. var_dump( $out );
  264. if( $out === false )
  265.     echo "no matches\n";
  266. else
  267. {
  268.     $match = substr( $test, $out[0], $out[1]-$out[0] );
  269.     $match = hent( $match );
  270.     echo "match: '{$match}'\n";
  271. }
  272.  
  273. hdr( 'multimatch' );
  274. $out = multimatch( $ptree, $test );
  275. var_dump( $out );
  276. foreach( $out as $mtch )
  277. {
  278.     $match = substr( $test, $mtch[0], $mtch[1]-$mtch[0] );
  279.     $match = hent( $match );
  280.     echo "match: '{$match}'<br/>\n";
  281. }
  282.  
  283.  
  284.  
  285. /* output:
  286. http://pastehtml.com/view/ctzxi3h87.html
  287. */
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement