diff --git a/tests/bibtexbrowser-test.php b/tests/bibtexbrowser-test.php index e54fca5..a8728f1 100755 --- a/tests/bibtexbrowser-test.php +++ b/tests/bibtexbrowser-test.php @@ -40,7 +40,7 @@ if (!is_file('reflectivedoc.php')) { } require('reflectivedoc.php'); $nsnippet=0; -foreach(getAllSnippetsInFile('bibtexbrowser.php') as $snippet) { +foreach(getAllSnippetsInFile('../src/bibtexbrowser.php') as $snippet) { ob_start(); eval($snippet); ob_get_clean(); @@ -137,7 +137,7 @@ class BTBTest extends PHPUnit_Framework_TestCase { // listing the CSS classes $css_classes_before = $this->extract_css_classes($first_entry->toHTML()); - + // IEEE style bibtexbrowser_configure('BIBLIOGRAPHYSTYLE','JanosBibliographyStyle'); $this->assertEquals("Foo Bar and Jane Doe, \"An Article\", In New Results, vol. 5, pp. 1-2, 2009.\n ",strip_tags($first_entry->toHTML())); @@ -646,7 +646,7 @@ class BTBTest extends PHPUnit_Framework_TestCase { ob_get_clean(); $this->assertEquals("[2]", $btb->getEntryByKey('aKey')->getAbbrv()); $this->assertEquals("[1]", $btb->getEntryByKey('aKey-withSlash')->getAbbrv()); - + } function test_identity() { diff --git a/tests/reflectivedoc.php b/tests/reflectivedoc.php new file mode 100644 index 0000000..99d3c23 --- /dev/null +++ b/tests/reflectivedoc.php @@ -0,0 +1,718 @@ +parse($text); +} + + +?>\n different + + */ + +if (defined('gakoparser.php')) return; +define('gakoparser.php',true); + +class Delimiter{} + +class GakoParserException extends Exception {} + +/** provides a parametrizable parser. The main method is "parse" */ +class GakoParser { + + /** is the PHP5 constructor. */ + function __construct() { + // an array of Delimiter objects + $this->start_delimiters = array(); + $this->end_delimiters = array(); + $this->nonesting = array(); + $this->delegate = array(); + } + + /** specifies that $tag can not contain nested tags */ + function noNesting($tag) { + $this->nonesting[] = $tag; + return $this; + } + + function setDelegate($obj) { + $this->delegate = $obj; + return $this; + } + + function addDelim($name, $delim) { + return $this->addDelimX($name, $delim, $delim); + } + + /** adds a trigger $tag -> execution of function $name */ + function addTag($name, $tag) { + $start = substr($tag,0,strlen($tag)-1); + $end = substr($tag,strlen($tag)-1); + return $this->addDelimX($name, $start, $end); + } + + /** setDelegate must be called before this method */ + function addDelimX($name, $start, $end) { + if (!method_exists($this->delegate,$name)) {throw new GakoParserException('no method '.$method.' in delegate!');} + + $x = new Delimiter(); + $x->value = $start; +// $x->type = 'start'; + $x->action = $name; + + $y = new Delimiter(); + $y->value = $end; +// $y->type = 'end'; + $y->action = $name; + + // crossing links + $y->startDelim = $x; + $x->endDelim = $y; + + if (in_array($start, $this->get_start_delimiters())) { + throw new GakoParserException("delimiter ".$start." already exists"); + } + + $this->start_delimiters[$start] = $x; + $this->end_delimiters[$end] = $y; + return $this; + } + + function get_start_delimiters() { + return array_keys($this->start_delimiters); + } + + function get_end_delimiters() { + return array_keys($this->end_delimiters); + } + + function array_preg_quote($x) { + $result = array(); + foreach($x as $k) { $result[] = preg_quote($k, '/'); } + return $result; + } + + function addDelimXML($name, $delim) { + return $this->addDelimX($name, '<'.$delim.'>', ''); + } + + function getCandidateDelimiters_man($str) { + $result = array(); + + for ( $i=0; $i < strlen( $str ); $i++) { + foreach(array_merge($this->get_start_delimiters(),$this->get_end_delimiters()) as $v) { + $new_fragment = substr($str, $i, strlen($v)); + if ($new_fragment === $v) { + $x = array(); + $x[0] = array(); + $x[0][0] = $v; + $x[0][1] = $i; + $result[] = $x; + } + } + } + //print_r($result); + return $result; + } + + function getCandidateDelimiters($str) { + //return $this->getCandidateDelimiters_man($str); + return $this->getCandidateDelimiters_preg($str); + } + + function getCandidateDelimiters_preg($str) { + // getting the start delimiters + preg_match_all('/'.implode('|',array_merge($this->array_preg_quote($this->get_start_delimiters()),$this->array_preg_quote($this->get_end_delimiters()))).'/', $str, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE ); + return $matches; + } + + function parse($str) { +// echo "---------parse $str\n"; + $method = '__pre'; + if (method_exists($this->delegate,$method)) { + $str = $this->delegate->$method($str); + } + + $matches = $this->getCandidateDelimiters($str); + +// print_r($matches); + + //echo 'parse '.$str.'
'; + // the stack contains the current markup environment + $init = new Delimiter(); $init->action = '__init__'; $init->value = '__init__'; $init->endDelim = $init; + $stack = array($init); + $strings = array(1=>''); + $last = 0; + +// if (count($matches) == 0) return $str; + +// print_r($matches); + +// $strings[1] = substr($str, 0, $matches[0][0][1]); + + // for each tags found + foreach ( $matches as $_ ) { + + list($v, $pos) = $_[0]; + +// echo "studying ".$v." at ".$pos." (last=".$last.")\n"; + + // if $_ is a start delimiter, + // we'll get '' in $k + //echo $stack[0]; + if (isset($this->end_delimiters[$v]) +// && $stack[0] != '__init__' + && $stack[0]->endDelim->value == $v + && $pos>=$last + ) { + $k = $stack[0]->endDelim->action; + + $strings[count($stack)] .= substr($str,$last, $pos-$last); + +// echo "popping ".$k." at ".$pos." with ".$v." (last=".$last.", stack=".count($stack).")\n"; + + $closedtag = array_shift($stack); + + $method = $k; +// echo $method." ".$value. "\n"; + $value = $strings[count($stack)+1]; + $transformed = $this->delegate->$method($value); +// echo $transformed ."---".$value." \n"; + $strings[count($stack)] .= $transformed; + + $strings[count($stack)+1] = ''; + $last = $pos+strlen($v); + + } + + // certain tags do not support nesting + else if (!in_array($stack[0]->action, $this->nonesting) + && in_array($v, $this->get_start_delimiters()) && $pos >= $last) + { + $delim = $this->start_delimiters[$v]; + $k = $delim->action; + +// echo "putting ".$k." at ".$pos." with ".$last."
\n"; + + if ($pos>$last) { + $strings[count($stack)] .= substr($str, $last, $pos-$last); + } + array_unshift($stack, $delim); + // init the new stack + $strings[count($stack)] = ''; + $last = $pos+strlen($v); +// print_r($strings); + + } else { + //die('oops'); + } + + } // end foreach + + + if ($stack[0]->action!="__init__") { + //print_r($strings); + throw new GakoParserException("parsing error: ending with ".$stack[0]->action. " ('".$strings[1]."')"); + } + + $result = $strings[count($stack)]; + + // adding the rest + if ($lastdelegate,$method)) { + $result = $this->delegate->$method($result); + } + //echo "$$$$$ ".$result."\n"; + + return $result; + + } +} + + +?> + +// high level +$parser = create_wiki_parser(); +$html_text = $parser->parse($wiki_text); + +// low level + $parser = new Gakoparser(); + $parser->setDelegate(new MarkupInterpreter()); + $parser->addDelim('bold','**'); + echo $parser->parse('hello **world**'); + + + */ + + +class GakowikiMarkupToHTMLTranslator { + + var $toc = array(); + + var $references = array(); + + /** replaces all line breaks by "__newline__" that are meant to replaced back by a call to __post() */ + function __pre($str) { + $result = $str; + + // we often use nelines to have pretty HTML code + // such as in tables + // however, they are no "real" newlines to be transformed in
+ $result = preg_replace("/>\s*(\n|\r\n)/",'>__newline__',$result); + return $result; + } + + function bib($str) { + $this->references[] = $str; + return '['.count($this->references).'] '.$str; + } + + function cite($str) { + return "@@@".$str."@@@"; + } + + function escape_newline($str) { + return preg_replace("/(\n|\r\n)/","__newline__",$str); + } + + function toc($str) { + return '+++TOC+++'; + } + + function __post($str) { + $result = $str; + $result = preg_replace("/(\n|\r\n)/","
\n",$result); + + // workaround to support the semantics change in pre mode + // and the semantics of embedded HTML + $result = preg_replace("/__newline__/","\n",$result);// must be at the end + + // cleaning the additional
+ // this is really nice + $result = preg_replace("/(<\/h.>)/i","\\1 ",$result); + + // adding the table of contents + $result = str_replace($this->toc(''),implode('
',$this->toc),$result); + + // adding the references + $citeregexp = '/@@@(.*?)@@@/'; + if (preg_match_all($citeregexp,$result,$matches)) { + foreach($matches[1] as $m) { + $theref = ''; + foreach ($this->references as $k => $ref) { + if (preg_match('/'.preg_quote($m).'/i', $ref)) { +//echo $m.' '.$ref; + // if we have already a match it is not deterministic + if ($theref!='') $result = "undeterministic citation: ".$m; + $theref = $ref; + $result = preg_replace('/@@@'.preg_quote($m).'@@@/i', '['.($k+1).']', $result); + } + } + } + } + + return $result; + } + + /** adds
 tags and prevents newline to be replaced by 
by __post */ + function pre($str) { + return '
'.$this->escape_newline($str).'
'; + } + + /** prevents newline to be replaced by
by __post */ + function unwrap($str) { + return $this->escape_newline($str); + } + + /** adds tags */ + function bold($str) { + return ''.$str.''; + } + + /** adds tags */ + function italic($str) { + return ''.$str.''; + } + + function table($str) { + $result = ''; + foreach(preg_split('/\n/',$str) as $line) { + if (strlen(trim($line))>0) { + $result .= ''; + foreach(preg_split('/&&/',$line) as $field) { + $result .= ''.$field.''; + + } + $result .= ''; + } + } + + return ''.$result.'
'; + } + + + function __create_anchor($m) { + return preg_replace("/[^a-zA-Z]/","",$m); + } + function h2($str) { + $tag = $this->__create_anchor($str); + $this->toc[] = "".$str.""; + return ''.'

'.$str."

"; + } + + function h3($str) { + $tag = $this->__create_anchor($str); + $this->toc[] = "  ".$str.""; + return ''.'

'.$str."

"; + } + + function monotype($str) { + return ''.str_replace('<','<',$str).''; + } + + function link($str) { + + if (preg_match('/(.*)\|(.*)/',$str, $matches)) { + $rawurl = $matches[1]; + $text = $matches[2]; + } else {$rawurl=$str;$text=$str;} + + $url=$rawurl; + + if (!preg_match("/(#|^http|^mailto)/",$rawurl)) { + if (function_exists('logical2url')) { + $url=logical2url($rawurl); + } else { + $url=$rawurl; + + } + } + + return ''.trim($text).''; + } + + function phpcode($str) { + ob_start(); + eval($str); + return $this->escape_newline(ob_get_clean()); + } + + function phpcode2($str) { + return gk_wiki2html($this->phpcode($str)); + } + + function a($str) { + return ''; + } + + function script($str) { + return 'escape_newline($str).''; + } + + function img($str) { + return ''; + } + + function img2($str) { + return ''; + } + + + function html($str) { + return '<'.$str.'>'; + } + + function iframe($str) { + return ''; + } + + + function comment($str) { + return ''; // comments are discarded + } + + function silent($str) { + return ''; + } + +} // end class + + +/** returns a parser object to parse wiki syntax. + +The returned object may be used with the parse method: +
+$parser = create_wiki_parser();
+$html_text = $parser->parse($wiki_text);
+
+*/ +function create_wiki_parser() { + $x = new Gakoparser(); + return $x->setDelegate(new GakowikiMarkupToHTMLTranslator()) + ->addDelimX('comment','')->noNesting('comment') + + ->addDelim('bold','**') + ->addDelim('italic','//')//->noNesting('italic') + ->addDelim('bold',"'''") + ->addDelim('monotype',"''")->noNesting('monotype') + ->addDelim('h2',"=====") // the longest comes before, it has the highest priority + ->addDelim('h3',"====") + ->addDelim('table',"|||") + ->addDelimXML('pre','pre')->noNesting('pre') // this is essential otherwise you have infinite loops + ->addDelimX('pre','{{{','}}}')->noNesting('pre2') // à la Google Code wiki syntax + ->addDelimX('link','[[',']]')->noNesting('link') + ->addDelimX('phpcode2','')->noNesting('phpcode2') + ->addDelimX('phpcode','') ->noNesting('phpcode') + ->addDelimX('img2','')->noNesting('img2') + ->addDelim('img','%%')->noNesting('img')// huge bug when I did this for 1000 index :( + ->addDelimX('script','') ->noNesting('script') + ->addDelimX('unwrap','^^','^^') + ->addTag('toc','+++TOC+++') + + ->addDelimX('a','')->noNesting('a') // important to support cross tags + + ->addDelimX('iframe','')->noNesting('iframe') + + // Dec 30 2012 + ->addDelimX('bib','\bib{','}') + ->addDelimX('cite','\cite{','}') + + // this one is really not good + //->addDelimX('html','<','>')->noNesting('html') // a link often contains // (e.g. http:// which clash with italics + ; +} // end create_wiki_parser + +function gakowiki__doc() { +?> +Syntax specification:
+**this is bold**
+//this is italic//
+''this is code''
+[[link to a page on this wiki]],[[http://www.google.fr|link to google]]
+

=====Section=====

+

====Subsection====

+', $result); + $result = str_replace('</pre>', '
', $result); + // removes lines prefixed "*" often used to have nice API comments + $result = preg_replace('/^.*?\*/m', '', $result); + return '
'.$result.'
'; + //return gk_wiki2html($comment); + } catch (GakoParserException $e) {return '
'.$comment.'
';} +} + +/** outputs the API doc of the function called $fname */ +function printDocFuncName($fname, $prefix='') { + $funcdeclared = new ReflectionFunction($fname); + return printDocFuncObj($funcdeclared, $prefix); +} + +function getComment($funcdeclared) { + $comment = trim(substr($funcdeclared->getDocComment(),3,-2)); + return $comment; +} +function printDocFuncObj($funcdeclared, $prefix='', $documented = true) { + $comment = trim(substr($funcdeclared->getDocComment(),3,-2)); + if ($documented && strlen($comment)<1) { return ''; } + $res = ""; + $res .= '
'; + $res .= ''.$prefix.$funcdeclared->getName().''; + $res .= '('.implode(', ',array_map('f',$funcdeclared->getParameters())).') '; + $res .= printGk($comment); + $res .= '
'; + return $res; +} + +// Anonymous functions are available only since PHP 5.3.0 +function f($x){return '$'.$x->getName();} + + +/** this is printNewFunctions + the main limitation is that this does not fully work if there is an exit/die in the included script +*/ +function printNewFunctions($beforef) { + $afterf=get_defined_functions(); + + + foreach($afterf['user'] as $fname ) { + $funcdeclared = new ReflectionFunction($fname); + if (!in_array($fname,$beforef['user']) && $funcdeclared->getFileName()==realpath($_GET['file'])) { + printDocFunc($funcdeclared); + } + } +} + +/** outputs an HTML representation of the API doc of the class called $cname */ +function printAPIDocClass($cname, $documented = true) { + $res = ''; + $cdeclared = new ReflectionClass($cname); + //if ($cdeclared->getFileName()!=realpath($_GET['file'])) {continue;} + $res .= ''.$cdeclared->getName().' '; + $comment = trim(substr($cdeclared->getDocComment(),3,-2)); + if ($documented && strlen($comment)<1) { return '';} + $res .= printGk($comment); + foreach($cdeclared->getMethods() as $method) { + $f = printDocFuncObj($method, "      "/*,$cname.'.'*/, true); + if (strlen($f)>0) { + $res .= $f; + } + } + return "
".$res."

"; +} + +function getCodeSnippetsInClass($cname) { + $res = array(); + $cdeclared = new ReflectionClass($cname); + $res[] = _getCodeSnippet($cdeclared); + foreach($cdeclared->getMethods() as $method) { + $res[] = _getCodeSnippet($method); + } + return $res; +} + +/** returns the snippet of a function */ +function getCodeSnippet($function_name) { + $funcdeclared = new ReflectionFunction($function_name); + return _getCodeSnippet($funcdeclared); +} + + +function _getCodeSnippet($obj) { + $comment = getComment($obj); + if (preg_match('/
(.*)<\/pre>/is', $comment, $matches)) {
+      return $matches[1];
+  }  
+  return "";  
+}
+
+
+function getAllSnippetsInFile($file) {
+  $res = array();
+  foreach (get_functions_in($file) as $f) {
+    $x=getCodeSnippet($f);
+    if (strlen($x)>0) $res[] = $x;
+  }
+
+  foreach (get_classes_in($file) as $klass) {
+    foreach (getCodeSnippetsInClass($klass) as $x) {
+      if (strlen($x)>0)  $res[] = $x;
+    }
+  }
+  return $res;
+}
+
+
+
+
+?>