Recode ast grammaire du php

Description

Ces classes servent a faire un systeme de _recode_ d'un code php.

Exemples :
- refaire une indentation correcte
- faire des commentaires doxygen pour les fonctions (les preparer du moins)
- transformer les "foo $a bar $b" en 'foo '.$a.' bar '.$b

(ces trois choses sont implementees dans cet exemple.)

J'aurais aime pouvoir implementer le tail recursif, mais en php5, c'est impossible.
En php6, on devrait avoir des goto, ce qui permetrait de faire ces choses.

exemple :

function sub_f ($a, $b){
if ($a==0) return $b;
else{return sub_f($a-1, $a+$b);}
}

function sumOfNaturals($n){
return sub_f($n, 0);
}

echo sumOfNaturals(5000000);

ce code ne fonctionne pas en php, parce-qu'il contient trop d'appels recursif,
ce qui fait que le code plante. Ca segfault, (erreur de segmentation), en effet,
un appel de fonction necessite un empillement des arguments sur la pile,
au retour de la fonction, on les depile.

En C, avec l'option -O2 (ou 1 ou 3), lorsque le code est _tail_recursif_ (ou recursivite finnie),
des optimisations sont faites, et le code ne conserve pas sa recursivite initiale.

quand un return reutilise la fonction est la derniere operation de la fonction, on peut remplacer le code par :
$argument1= ... ; $argument2= ... ; goto debut_de_la_fonction;

function sub_f ($a, $b){

label debut:

if ($a==0) return $b;
else{
$a1=$a-1;
$b1=$a+$b;

$a=$a1; $b=$b1;

goto debut;
}
}

function sumOfNaturals($n){
return sub_f($n, 0);
}

comme vous pouvez le voir, ici, on ne conserve pas de recursivite, pourtant, la fonction fait la meme chose.
en Ocaml, en C, en Cpp, et dans plein d'autres langages, la recursivite est supprimee (lors de la compilation)
ce qui permet d'eviter ces erreurs de segmentation, et ces empilements inutiles.

en php6, on pourra faire ca (on aura alors les goto). C'etait mon projet initial quand j'ai voulu coder ca.

Ce programme utilise le lexer de php. Il nous renvoie des _Tokens_, que je transforme en Objets Tokens.
Ensuite, un parseur utilise un reducteur pour faire un AST (arbre syntaxique)
exemple :
if - condition
- instructions

Ce qui permet de faire des manipulations sur cet arbre.

Ce code contient donc une grammaire simple sur le php

Source / Exemple :


<?php

error_reporting(E_ALL);

if (isset($_SERVER['argv'][1]))
	$filename=$_SERVER['argv'][1];
else if (isset($_GET['argv']))
	$filename=$_GET['argv'];
else
	$filename='';

function makeTab($n){
	$out='';
	for ($i=0;$i<$n;$i++){
		$out.='	';
	}
	return $out;
}
function parse_error($s){
	echo $s.'

';
	exit(1);
}
class Locator{
	public function __construct($l){$this->ligne=$l;}
	public function __toString(){return 'l:'.$this->ligne;}
	public function Val(){return $this->ligne;}
	public function isBetween(Locator $v1, Locator $v2){
		return $v1->Val() <= $this->ligne && $v2->Val() >= $this->ligne;
	}
	private $ligne;
}
class Node{
	public function is_important(){ return true; }
	public function isFermant(){ return false; }
	public function getContent(){ return ''; }
	public function isVal(){return false;}
	public function isOperator(){return false;}
	public function isOperatorAlone(){return false;}
	public function isInstruction(){ return false; }
	public function contient(){ return false; }
	public function getChilds(){ return array();}
	public function accept($visiteur){
		$visiteur->visite($this);
	}
}
class Token extends Node{
	public function __construct($t){
		if (is_array($t)){
			$this->type=token_name($t[0]);
			$this->locator=new Locator($t[2]);
			self::$last=$t[2];
			if ($this->type=='T_OPEN_TAG'){
				$this->content='<?php';
			}else{
				$this->content=$t[1];
			}
		}else{
			if (!isset(self::$mytoken[$t])){
				throw new Exception ('Token '.$t.' non defini');
			}
			$this->type=self::$mytoken[$t];
			$this->content=$t;
			$this->locator=new Locator(self::$last);
		}
	}
	public function getContent(){ return $this->content; }
	public function __toString(){
		return $this->content; //.' '.$this->type.' ';
	}
	public function contient($a, $b){
		return $this->type==$a && $this->content==$b;
	}
	private static $mytoken = array(
		';'=>'MT_PT_VIRGULE',
		'('=>'MT_PARENTHESE_OPEN',
		')'=>'MT_PARENTHESE_CLOSE',
		','=>'MT_VIRGULE',
		'{'=>'MT_ACCOLADE_OPEN',
		'}'=>'MT_ACCOLADE_CLOSE',
		'*'=>'MT_FOIS',
		'-'=>'MT_MOINS',
		'+'=>'MT_PLUS',
		'='=>'MT_EGAL',
		'"'=>'MT_DOUBLE_QUOTE',
		'.'=>'MT_POINT',
		'<'=>'MT_INF_A',
		':'=>'MT_DEUX_POINTS'
	);
	private static $fin = array(
		'MT_PARENTHESE_CLOSE'=>'MT_PARENTHESE_OPEN',
		'MT_ACCOLADE_CLOSE'=>'MT_ACCOLADE_OPEN',
		'T_CLOSE_TAG'=>'T_OPEN_TAG',
		'MT_DOUBLE_QUOTE'=>'MT_DOUBLE_QUOTE'
	);
	public function is_important(){
		return !in_array($this->type, self::$no_imp);
	}
	public function isFermant(){
		return isset (self::$fin[$this->type]);
	}
	public function getOuvre(){
		return self::$fin[$this->type];
	}
	public function getName(){ return $this->type;}

	public function isVal(){
		return in_array($this->type, self::$val);
	}
	public function isOperator(){
		return in_array($this->type, self::$op);
	}
	public function isOperatorAlone(){
		return in_array($this->type, self::$opAlone);
	}
	private static $op=array('MT_FOIS', 'MT_PLUS', 'MT_MOINS', 'T_IS_EQUAL','MT_POINT', 'MT_INF_A');
	private static $opAlone=array('T_INC');
	private static $val=array(
		'Couple_MT_PARENTHESE_OPEN', 'T_VARIABLE', 'T_LNUMBER','EXPR', 'T_CONSTANT_ENCAPSED_STRING'
	);
	public function getLine(){
		return $this->locator;
	}
	public function getChilds(){ return array(); }
	private static $no_imp=array('T_WHITESPACE', 'T_COMMENT', 'T_DOC_COMMENT');
	private $type;
	private $content;
	private $locator;
	private static $last=0;
}
class Couple extends Node{
	public function __construct(){ $this->stack=array(); }
	public function append($t){ $this->stack[]=$t; }
	public function end_(){ $this->stack=array_reverse($this->stack);}
	public function getName(){
		return 'Couple_'.$this->stack[0]->getName().'';
	}
	public function __toString($tab=0){
		$acc=($this->stack[0]->getName()=='MT_ACCOLADE_OPEN' || $this->stack[0]->getName()=='T_OPEN_TAG');
		if ($acc)
			$out=makeTab($tab -1);
		else
			$out='';
		$len=count($this->stack)-1;
		foreach ($this->stack as $i=>$e){
 			if ($i==$len && $acc){
				$out.=makeTab($tab -1).$e.'
';
			}else{
				$out.=$e->__toString($tab);
			}
			if ($i==0 && $acc){
				$out.='
';
			}else if ($e->getName()=='MT_VIRGULE'){
				$out.=' ';
			}
		}
		return $out;
	}
	public function getLine(){
		return $this->stack[0]->getLine();
	}
	public function getLastLine(){
		return $this->stack[ count($this->stack)-1 ]->getLine();
	}
	public function contient($a, $b){
		foreach ($this->stack as $e){
			if ($e->contient($a, $b)) return true;
		}
		return false;
	}
	public function accept($visiteur){
		parent::accept($visiteur);
		foreach ($this->stack as $e){
			$e->accept($visiteur);
		}
	}
	public function getStack(){return $this->stack;}
	public function isInstruction(){ return $this->stack[0]->getName()=='MT_ACCOLADE_OPEN'; }
	public function getChilds(){ return $this->stack; }
	private $stack;
}
class PHP_function extends Node {
	public static $devant_function=array('T_PUBLIC', 'T_STATIC', 'T_PRIVATE', 'T_PROTECTED');
	public function isInstruction(){ return true; }
	public function getLine(){return $this->line;}
	public function __construct($content, $argList, $func){
		$this->devant=array();
		$this->line=$func->getLine();
		$this->name = $func->getContent();
		$this->argList=$argList;
		$this->content=$content;
	}
	public function ajouter_devant($e){
		$this->devant[]=$e;
	}
	public function getName(){ return 'FUNCTION'; }
	public function __toString($tab=0){
		$tab2=$tab+1;
		$fd='function '.$this->name.$this->argList;
		foreach ($this->devant as $a){
			$fd=$a.' '.$fd;
		}
		$comments=Tokens__::getComments($this->line, $this->content->getLastLine());
		$out='
'.makeTab($tab).'/**
'.makeTab($tab).' * @fn '.$fd.'
'.makeTab($tab).' * @brief
';
		if ($this->isRecursive()){
			$out.=makeTab($tab).' * recursive function.
';
		}
		foreach ($comments as $c){
			$out.=Tokens__::noComment($c->getContent(), $tab);
		}
		foreach ($this->argList->getStack() as $p)
			if ($p->getName()=='T_VARIABLE')
				$out.=makeTab($tab).' * @param '.$p.'
';
		$out.=makeTab($tab).' * @return
'.makeTab($tab).' */
';
	$out.=makeTab($tab).$fd.'
'.$this->content->__toString($tab2).'
';

		return $out;
	}
	public function isRecursive(){
		return $this->content->contient('T_STRING', $this->name);
	}
	public function accept($visiteur){
		parent::accept($visiteur);
		$this->content->accept($visiteur);
	}
	private $line;
	private $name;
	private $argList;
	private $content;
	private $devant;
	public function getChilds(){ return $this->content; }
}
class Expr extends Node{
	public function getName(){ return 'EXPR';}
	public function getLine(){return $this->v2->getLine();}
	public function __toString(){
		return $this->v2.' '.$this->op.' '.$this->v1;
	}
	public function __construct($v1, $op, $v2){
		$this->op=$op; $this->v1=$v1; $this->v2=$v2;
	}
	public function isVal(){ return true; }
	public function contient($a, $b){
		if ($this->v1->contient($a, $b)) return true;
		if ($this->v2->contient($a, $b)) return true;
		return false;
	}
	public function accept($visiteur){
		parent::accept($visiteur);
		$this->v1->accept($visiteur);
		$this->v2->accept($visiteur);
		$this->op->accept($visiteur);
	}
	private $op, $v1, $v2;
	public function getChilds(){ return array($this->v1, $this->v2); }
}
class ExprAlone extends Node{
	public function getName(){ return 'EXPRAlone';}
	public function getLine(){return $this->v2->getLine();}
	public function __toString(){
		return $this->v2.' '.$this->op;
	}
	public function __construct($op, $v2){
		$this->op=$op; $this->v2=$v2;
	}
	public function isVal(){ return true; }
	public function contient($a, $b){
		if ($this->v2->contient($a, $b)) return true;
		return false;
	}
	public function accept($visiteur){
		parent::accept($visiteur);
		$this->v2->accept($visiteur);
		$this->op->accept($visiteur);
	}
	private $op, $v2;
	public function getChilds(){ return array($this->v2); }
}
class CALL extends Node{
	public function getLine(){return $this->func->getLine();}
	public function isInstruction(){ return true; }
	public function getName(){ return 'CALL_FUNC';}
	public function isVal(){ return true; }
	public function __toString(){
		return $this->func.$this->args;
	}
	public function __construct($args, $func){
		$this->func=$func;$this->args=$args;
	}
	public function contient($a, $b){
		if ($this->func->contient($a, $b)) return true;
		if ($this->args->contient($a, $b)) return true;
		return false;
	}
	public function accept($visiteur){
		parent::accept($visiteur);
		$this->func->accept($visiteur);
		$this->args->accept($visiteur);
	}
	public function getChilds(){ return $this->args;}
	private $func;
	private $args;
}
class ff_return extends Node{
	public function getLine(){return $this->expr->getLine();}
	public function isInstruction(){ return true; }
	public function getName(){ return 'FF_RETURN';}
	public function __toString($tab=0){
		return makeTab($tab).'return '.$this->expr.';
';
	}
	public function __construct($expr){
		$this->expr=$expr;
	}
	public function contient($a, $b){
		if ($this->expr->contient($a, $b)) return true;
		return false;
	}
	public function getChilds(){ return array($this->expr); }
	public function accept($visiteur){
		parent::accept($visiteur);
		$this->expr->accept($visiteur);
	}
	private $expr;
}
class Assign extends Node{
	public function getLine(){return $this->varName->getLine();}
	public function isVal(){ return true; }
	public function isInstruction(){ return true; }
	public function getName(){ return 'ASSIGN';}
	public function __toString(){
		return $this->varName->__toString().' '.$this->op->__toString().' '.$this->value->__toString();
	}
	public function __construct($op,$varName, $value){
		$this->varName=$varName; $this->value=$value; $this->op=$op;
	}

	public function contient($a, $b){
		if ($this->value->contient($a, $b)) return true;
		return false;
	}
	public function getChilds(){ return array($this->value); }
	public function accept($visiteur){
		parent::accept($visiteur);
		$this->varName->accept($visiteur);
		$this->value->accept($visiteur);
		$this->op->accept($visiteur);
	}
	private $varName;
	private $value;
	private $op;
}
class Instruction extends Node{
	public function getLine(){return $this->instr->getLine();}
	public function isVal(){ return false; }
	public function isInstruction(){ return true; }
	public function getName(){ return 'INSTR';}
	public function __toString($tab=0){
		return makeTab($tab).$this->instr->__toString().';
';
	}
	public function __construct($instr){
		$this->instr=$instr;
	}

	public function contient($a, $b){
		if ($this->instr->contient($a, $b)) return true;
		return false;
	}
	public function getContent(){ return $this->instr; }
	public function getChilds(){ return array($this->instr); }

	public function accept($visiteur){
		parent::accept($visiteur);
		$this->instr->accept($visiteur);
	}

	private $instr;
}
class _Echo extends Node{
	public function getLine(){return $this->val->getLine();}
	public function isVal(){ return false; }
	public function isInstruction(){ return true; }
	public function getName(){ return 'ECHO';}
	public function __toString(){
		return 'echo '.$this->val->__toString();
	}
	public function __construct($val){
		$this->val=$val;
	}

	public function contient($a, $b){
		if ($this->val->contient($a, $b)) return true;
		return false;
	}
	public function getChilds(){ return array($this->val); }

	public function accept($visiteur){
		parent::accept($visiteur);
		$this->val->accept($visiteur);
	}

	private $val;
}
class PHP_String extends Node{
	public function getContent(){ return $this->content; }
	public function getLine(){return $this->line;}
	public function isVal(){ return true; }
	public function getName(){ return 'DOUBLE_STRING';}
	public function __toString(){
		return $this->content;
	}
	public function __construct($double){
		$this->content=$double;
		$this->line=$double->getLine();
	}
	public function contient($a, $b){
		if ($this->content->contient($a, $b)) return true;
		return false;
	}
	public function getChilds(){ return $this->content; }

	public function accept($visiteur){
		parent::accept($visiteur);
		$this->content->accept($visiteur);
	}

	public function setElement($e){
		$this->content=$e;
	}

	private $content;
	private $line;
}
class php_if extends Node{
	public function getLine(){return $this->condition->getLine();}
	public function isVal(){ return false; }
	public function isInstruction(){ return true; }
	public function getName(){ return 'IF';}
	public function __construct($content, $condition){
		$this->condition=$condition;
		$this->content=$content;
	}
	public function __toString($tab=0){
		return makeTab($tab).'if '.$this->condition.'
'.$this->content->__toString($tab+1);
	}
	public function contient($a, $b){
		if ($this->content->contient($a, $b)) return true;
		if ($this->condition->contient($a, $b)) return true;
		return false;
	}
	public function getChilds(){ return array($this->condition, $this->content); }

	public function accept($visiteur){
		parent::accept($visiteur);
		$this->condition->accept($visiteur);
		$this->content->accept($visiteur);
	}

	protected $condition;
	protected $content;
}
class php_ifelse extends Node{
	public function getLine(){return $this->if_->getLine();}
	public function isVal(){ return false; }
	public function isInstruction(){ return true; }
	public function getName(){ return 'ELSE';}
	public function __construct($content, $else, $if){
		$this->if_=$if;
		$this->content=$content;
	}
	public function contient($a, $b){
		if ($this->if_->contient($a, $b)) return true;
		if ($this->content->contient($a, $b)) return true;
		return false;
	}
	public function __toString($tab=0){
		return $this->if_->__toString($tab).makeTab($tab).'else
'.$this->content->__toString($tab+1);
	}
	public function getChilds(){ return array($this->content, $this->if_); }

	public function accept($visiteur){
		parent::accept($visiteur);
		$this->if_->accept($visiteur);
		$this->content->accept($visiteur);
	}

	private $if_;
	private $content;

}
class Tokens__{
	public static function evalString($s){
		$s=str_replace('\\n', "\n", $s);
		$s=str_replace('\\t', "\t", $s);
		$s=str_replace('\\r', "\r", $s);
		$s=str_replace('\\"', '"', $s);
		return $s;
	}
	public static function noComment($str, $tab){
		$c=explode("\n", str_replace(array('/**', '/*', '*/', '//!', '//', '#'), array(''), $str));
		$out='';
		foreach ($c as $a){
			$out.=makeTab($tab).' * '.trim($a).'
';
		}
		return $out;
	}
	public static function makeString($s){
		$s=str_replace('\\', '\\\\', $s);
		$s=str_replace('\'', '\\\'', $s);
		return new Token(
			array(
			T_CONSTANT_ENCAPSED_STRING,
			'\''.$s.'\'',
			0)
		);
	}
	public static function getPoint(){
		if (self::$Point==NULL)
			self::$Point=new Token('.');
		return self::$Point;
	}
	public static function AddComment($t){
		self::$comment[]=$t;
	}
	public static function getcomments($v1, $v2){
		$out=array();
		if (isset(self::$comment)){
			foreach (self::$comment as $c){
				if ($c->getLine()->isBetween($v1, $v2)){
					$out[]=$c;
				}
			}
		}
		return $out;
	}
	private static $Point;
	private static $comment;
	public static $affect=array('T_PLUS_EQUAL', 'MT_EGAL');
}
class php_while extends Node{
	public function __construct($content, $condition){
		$this->content=$content;
		$this->condition=$condition;
	}

	public function getLine(){return $this->condition->getLine();}
	public function isVal(){ return false; }
	public function isInstruction(){ return true; }
	public function getName(){ return 'WHILE';}
	public function contient($a, $b){
		if ($this->condition->contient($a, $b)) return true;
		if ($this->content->contient($a, $b)) return true;
		return false;
	}
	public function __toString($tab=0){
		return makeTab($tab).'while '.$this->condition->__toString($tab+1).'
'.$this->content->__toString($tab+1);
	}
	public function getChilds(){ return array($this->content, $this->condition); }
	private $condition;
	private $content;

	public function accept($visiteur){
		parent::accept($visiteur);
		$this->condition->accept($visiteur);
		$this->content->accept($visiteur);
	}

}
class php_for extends Node{

	public function getLine(){return $this->line;}
	public function isVal(){ return false; }
	public function isInstruction(){ return true; }
	public function getName(){ return 'INSTR';}
	public function __toString($tab=0){
		$out=makeTab($tab).'for (';
		foreach ($this->affect as $i=>$e){
			if ($i!=0) $out.=', ';
			$out.=$e;
		}
		$out.=';';
		foreach ($this->sortie as $i=>$e){
			if ($i!=0) $out.=', ';
			$out.=$e;
		}
		$out.=';';
		foreach ($this->step as $i=>$e){
			if ($i!=0) $out.=', ';
			$out.=$e;
		}
		$out.=')
'.$this->content->__toString($tab+1);
		return $out;
	}
	public function __construct($content, $couple, $for){
		$this->ligne=$for->getLine();
		$this->content=$content;
		$this->affect=array();
		$this->sortie=array();
		$this->step=array();

		$imp=array('MT_PT_VIRGULE', 'MT_VIRGULE', 'MT_PARENTHESE_OPEN','MT_PARENTHESE_CLOSE');

		$b=false;
		$s=$couple->getStack();
		foreach ($s as $i=>$a){
			if ($a->getName()=='INSTR') { $a=$a->getContent(); $b=true; }
			if (!in_array($a->getName(), $imp))
				$this->affect[] = $a;
			unset($s[$i]);
			if ( $b ) break;
		}
		$b=false;
		foreach ($s as $i=>$a){
			if ($a->getName()=='INSTR') { $a=$a->getContent(); $b=true; }
			if (!in_array($a->getName(), $imp))
				$this->sortie[] = $a;
			unset($s[$i]);
			if ( $b ) break;
			if ($a->getName()=='MT_PT_VIRGULE') break;
		}
		foreach ($s as $i=>$a){
			if (!in_array($a->getName(), $imp))
				$this->step[] = $a;
		}
	}

	public function contient($a, $b){
		if ($this->content->contient($a, $b)) return true;
		if ($this->affect->contient($a, $b)) return true;
		if ($this->sortie->contient($a, $b)) return true;
		if ($this->step->contient($a, $b)) return true;
		return false;
	}
	public function getChilds(){ return array($this->content, $this->affect, $this->sortie, $this->step); }

	public function accept($visiteur){
		parent::accept($visiteur);
		foreach ($this->step as $step)
		$step->accept($visiteur);
		foreach ($this->sortie as $sortie)
		$sortie->accept($visiteur);
		foreach ($this->affect as $affect)
		$affect->accept($visiteur);
		$this->content->accept($visiteur);
	}

	private $ligne;
	private $content;
	private $step;
	private $affect;
	private $sortie;
}
class _Case extends Node{
	public function getLine(){return $this->line;}
	public function isVal(){ return false; }
	public function isInstruction(){ return true; }
	public function getName(){ return 'CASE';}
	public function __construct($a, $const_, $b){
		$this->const_=$const_;
		$this->line=$b->getLine();
	}
	public function __toString($tab=0){
		return makeTab($tab).'case '.$this->const_.':
';
	}
	public function contient($a, $b){
		if ($this->const->contient($a, $b)) return true;
		return false;
	}
	public function getChilds(){ return array($this->const_); }
	private $const_;

	public function accept($visiteur){
		parent::accept($visiteur);
		$this->const_->accept($visiteur);
	}

	private $line;
}
class _default extends Node{
	public function getLine(){return $this->line;}
	public function isVal(){ return false; }
	public function isInstruction(){ return true; }
	public function getName(){ return 'DEFAULT';}
	public function __construct($a, $b){ $this->line=$b->getLine(); }
	public function __toString($tab=0){ return makeTab($tab).'default:
'; }
	public function contient($a, $b){ return false; }
	public function getChilds(){ return array(); }
	private $line;
}
class php_switch extends php_if{
	public function getName(){ return 'SWITCH';}
	public function __toString($tab=0){
		return makeTab($tab).'switch '.$this->condition.'
'.$this->content->__toString($tab+1);
	}
}
class VarModifier extends Node{
	public function getLine(){return $this->line;}
	public function isVal(){ return false; }
	public function isInstruction(){ return true; }
	public function getName(){ return 'ModifieurVar';}
	public function __construct($a, $b){ $this->line=$a->getLine(); $this->_var=$a; $this->modifier=$b; }
	public function __toString($tab=0){
		if (isset($this->className))
			return $this->className.$this->modifier.$this->_var;
		else
			return $this->modifier.' '.$this->_var;
	}
	public function contient($a, $b){ return  $this->_var->contient($a, $b) || $this->_modifier->contient($a, $b); }
	public function getChilds(){ return array($this->_var, $this->modifier); }
	public function setString($a){
		$this->className=$a;
	}
	private $_var, $modifier, $line;
}
class ClassName extends Node{
	public function getLine(){return $this->line;}
	public function isVal(){ return false; }
	public function isInstruction(){ return true; }
	public function getName(){ return 'ClassName';}
	public function __construct($a, $b){ $this->line=$b->getLine(); $this->name=$a; }
	public function __toString($tab=0){ return 'class '.$this->name; }
	public function getChilds(){ return array($this->name); }
	
	private $name, $line;
}
class ClassExtends extends Node{
	public function getLine(){return $this->line;}
	public function isVal(){ return false; }
	public function isInstruction(){ return true; }
	public function getName(){ return 'ClassName';}
	public function __construct($a, $b, $c){ $this->line=$b->getLine(); $this->name=$a; $this->classN=$c; }
	public function __toString($tab=0){ return $this->classN.' extends '.$this->name; }
	public function getChilds(){ return array($this->name); }
	
	private $name, $line, $classN;
}
class ClassImplements extends Node{
	public function getLine(){return $this->line;}
	public function isVal(){ return false; }
	public function isInstruction(){ return true; }
	public function getName(){ return 'ClassNameImplements';}
	public function __construct($a, $b, $c){ $this->line=$b->getLine(); $this->name=$a; $this->classN=$c; }
	public function __toString($tab=0){ return $this->classN.' implements '.$this->name; }
	public function getChilds(){ return array($this->name); }
	
	private $name, $line, $classN;
}
class ClassImplements2 extends Node{
	public function getLine(){return $this->line;}
	public function isVal(){ return false; }
	public function isInstruction(){ return true; }
	public function getName(){ return 'ClassNameImplements';}
	public function __construct($a, $b, $c){ $this->line=$b->getLine(); $this->name=$a; $this->classN=$c; }
	public function __toString($tab=0){ return $this->classN.', '.$this->name; }
	public function getChilds(){ return array($this->name); }
	
	private $name, $line, $classN;
}
class ClassE extends Node{
	public function getLine(){return $this->line;}
	public function isVal(){ return false; }
	public function isInstruction(){ return true; }
	public function getName(){ return 'Class';}
	public function __construct($b, $a){ $this->line=$a->getLine(); $this->name=$a; $this->content=$b; }
	public function __toString($tab=0){ return $this->name.'
'.$this->content->__toString($tab+1); }
	public function getChilds(){ return array($this->name, $this->content); }
	private $name, $line, $content;

}
class objectnew extends Node{
	public function getLine(){return $this->line;}
	public function isVal(){ return true; }
	public function isInstruction(){ return true; }
	public function getName(){ return 'new'; }
	public function __construct($b, $a){ $this->line=$a->getLine(); $this->call=$b; }
	public function __toString($tab=0){ return 'new '.$this->call->__toString($tab); }
	public function getChilds(){ return array($this->call); }
	private $line, $call;

}

class throwException extends Node{
	public function getLine(){return $this->line;}
	public function isVal(){ return true; }
	public function isInstruction(){ return true; }
	public function getName(){ return 'throw'; }
	public function __construct($c, $b, $a){ $this->line=$a->getLine(); $this->objnew=$b; }
	public function __toString($tab=0){ return makeTab($tab).'throw '.$this->objnew->__toString($tab).';
'; }
	public function getChilds(){ return array($this->objnew); }
	private $line, $objnew;

}

function reduce($stack){
	$l=count($stack)-1;
	if ($l==-1) return false;

	if ( $l>=1 &&
	$stack[$l-1]->getName()=='T_NEW' &&
	$stack[$l]->getName()=='CALL_FUNC'){
		array_push($stack,new objectnew(array_pop($stack), array_pop($stack)));
		return true;
	}

	if ( $l>=2 &&
	$stack[$l-2]->getName()=='T_THROW' &&
	$stack[$l-1]->getName()=='new' &&
	$stack[$l]->getName()=='MT_PT_VIRGULE'){
		array_push($stack,new throwException(array_pop($stack), array_pop($stack), array_pop($stack)));
		return true;
	}

	$modifieurs=array('T_STATIC', 'T_PUBLIC', 'T_PRIVATE', 'T_PROTECTED', 'T_VAR');
	if ( $l>=1 &&
	in_array($stack[$l-1]->getName(), $modifieurs) &&
	$stack[$l]->getName()=='T_VARIABLE' ){
		array_push($stack, new VarModifier(array_pop($stack), array_pop($stack)));
		return true;
	}

	if ( $l>=3 &&
	$stack[$l-2]->getName()=='T_STRING' &&
	$stack[$l-1]->getName()=='T_DOUBLE_COLON' &&
	$stack[$l]->getName()=='T_VARIABLE' ){
		$f=new VarModifier(array_pop($stack), array_pop($stack));
		$f->setString(array_pop($stack));
		array_push($stack, $f);
		return true;
	}

	if ( $l>=3 &&
	$stack[$l-2]->getName()=='T_VARIABLE' &&
	$stack[$l-1]->getName()=='T_OBJECT_OPERATOR' &&
	$stack[$l]->getName()=='T_STRING' ){
		$f=new VarModifier(array_pop($stack), array_pop($stack));
		$f->setString(array_pop($stack));
		array_push($stack, $f);
		return true;
	}

	if ( $l>=1 &&
	in_array($stack[$l-1]->getName(), array('ClassNameImplements', 'ClassName')) &&
	$stack[$l]->getName()=='Couple_MT_ACCOLADE_OPEN' ) {
		array_push($stack, new classE(array_pop($stack), array_pop($stack)));
		return true;
	}

	if ( $l>=1 &&
	$stack[$l-1]->getName()=='T_CLASS' &&
	$stack[$l]->getName()=='T_STRING' ){
		array_push($stack, new ClassName(array_pop($stack), array_pop($stack)));
		return true;
	}

	if ( $l>=2 &&
	$stack[$l-2]->getName()=='ClassName' &&
	$stack[$l-1]->getName()=='T_EXTENDS' &&
	$stack[$l]->getName()=='T_STRING' ){
		array_push($stack, new ClassExtends(array_pop($stack), array_pop($stack), array_pop($stack)));
		return true;
	}

	if ( $l>=2 &&
	$stack[$l-2]->getName()=='ClassName' &&
	$stack[$l-1]->getName()=='T_IMPLEMENTS' &&
	$stack[$l]->getName()=='T_STRING' ){
		array_push($stack, new ClassImplements(array_pop($stack), array_pop($stack), array_pop($stack)));
		return true;
	}

	if ( $l>=2 &&
	$stack[$l-2]->getName()=='ClassNameImplements' &&
	$stack[$l-1]->getName()=='MT_VIRGULE' &&
	$stack[$l]->getName()=='T_STRING' ){
		array_push($stack, new ClassImplements2(array_pop($stack), array_pop($stack), array_pop($stack)));
		return true;
	}

	// les switchs
	if (
	$l>=2 &&
	$stack[$l]->isInstruction() &&
	$stack[$l-1]->getName()=='Couple_MT_PARENTHESE_OPEN' &&
	$stack[$l-2]->getName()=='T_SWITCH'){
		array_push($stack, new php_switch(array_pop($stack), array_pop($stack), array_pop($stack)));
		return true;
	}
	// les case
	if ( $l>=2 &&
	in_array($stack[$l]->getName(), array('MT_PT_VIRGULE', 'MT_DEUX_POINTS')) &&
	$stack[$l-1]->isVal() &&
	$stack[$l-2]->getName()=='T_CASE'){
		array_push($stack, new _Case(array_pop($stack), array_pop($stack), array_pop($stack)));
		return true;
	}
	// les default
	if ( $l>=2 &&
	in_array($stack[$l]->getName(), array('MT_PT_VIRGULE', 'MT_DEUX_POINTS')) &&
	$stack[$l-1]->getName()=='T_DEFAULT'
	){
		array_push($stack, new _default(array_pop($stack), array_pop($stack)));
		return true;
	}
	//assign
	if (
	$l>=3 &&
	!$stack[$l]->isOperator() &&
	$stack[$l-1]->isVal() &&
	in_array($stack[$l-2]->getName(), Tokens__::$affect) &&
	in_array($stack[$l-3]->getName(), array('T_VARIABLE', 'ModifieurVar'))){
		$n=array_pop($stack);
		$var=array_pop($stack);
		$op=array_pop($stack);
		$val=array_pop($stack);
		array_push($stack, new Assign($op, $val, $var));
		while (reduce(&$stack)) continue; // IMPORTANT !
		array_push($stack, $n);
		return true;
	}

	if (
	$l>=2 &&
	$stack[$l]->isInstruction() &&
	$stack[$l-1]->getName()=='Couple_MT_PARENTHESE_OPEN' &&
	$stack[$l-2]->getName()=='T_FOR'){
		array_push($stack, new php_for(array_pop($stack), array_pop($stack), array_pop($stack)));
		return true;
	}

	// les while
	if (
	$l>=2 &&
	$stack[$l]->isInstruction() &&
	$stack[$l-1]->getName()=='Couple_MT_PARENTHESE_OPEN' &&
	$stack[$l-2]->getName()=='T_WHILE'){
		array_push($stack, new php_while(array_pop($stack), array_pop($stack), array_pop($stack)));
		return true;
	}
	//les chaines
	if ($stack[$l]->getName()=='Couple_MT_DOUBLE_QUOTE'){
		$stack[$l]=new PHP_String($stack[$l]);
		return true;
	}
	//les if
	if (
	$l>=2 &&
	$stack[$l]->isInstruction() &&
	$stack[$l-1]->getName()=='Couple_MT_PARENTHESE_OPEN' &&
	$stack[$l-2]->getName()=='T_IF'){
		array_push($stack, new php_if(array_pop($stack), array_pop($stack), array_pop($stack)));
		return true;
	}
	//les else
	if (
	$l>=2 &&
	$stack[$l]->isInstruction() &&
	$stack[$l-1]->getName()=='T_ELSE' &&
	$stack[$l-2]->getName()=='IF'){
		array_push($stack, new php_ifelse(array_pop($stack), array_pop($stack), array_pop($stack)));
		return true;
	}
	//les couples () {} [] etc...
	if ($stack[$l]->isFermant()){
		$e=$stack[$l];
		$fake=false;
		if ($e->getName()=='MT_DOUBLE_QUOTE'){
			$f=0;
			foreach ($stack as $el){
				if ($el->getName()=='MT_DOUBLE_QUOTE'){
					$f++;
				}
			}
			if ($f==1){
				$fake=true;
			}
		}
		if (!$fake){
			$couple=new Couple();
			$couple->append(array_pop($stack));
			while (true){
				$l--;
				$n=array_pop($stack);
				$couple->append($n);
				if ($n->getName()==$e->getOuvre()) break;
				if ($l==0) parse_error('Erreur : element de fin sans element de debut... '.$n->getName().' '.$e->getOuvre());
			}
			$couple->end_();
			array_push($stack, $couple);
			return true;
		}
	}
	// les declarations de fonctions
	if (
	$l>=3 &&
	$stack[$l]->getName()=='Couple_MT_ACCOLADE_OPEN' &&
	$stack[$l-1]->getName()=='Couple_MT_PARENTHESE_OPEN' &&
	$stack[$l-2]->getName()=='T_STRING' &&
	$stack[$l-3]->getName()=='T_FUNCTION'){
		$f=new PHP_function(array_pop($stack), array_pop($stack), array_pop($stack), array_pop($stack));
		$l-=4;
		while(in_array($stack[$l]->getName(), PHP_function::$devant_function)){ $f->ajouter_devant(array_pop($stack)); $l--; }
		array_push($stack, $f);
		return true;
	}
	//les expressions
	if (
	$l>=2 &&
	$stack[$l]->isVal() &&
	$stack[$l-1]->isOperator() &&
	$stack[$l-2]->isVal()){
		array_push($stack, new Expr(array_pop($stack), array_pop($stack), array_pop($stack)));
		return true;
	}
	// expression alone
	if (
	$l>=1 &&
	$stack[$l-1]->isVal() &&
	$stack[$l]->isOperatorAlone()){
		array_push($stack, new ExprAlone(array_pop($stack), array_pop($stack)));
		return true;
	}
	//les appels de fonctions
	if (
	$l>=2 &&
	$stack[$l]->getName()!='MT_ACCOLADE_OPEN' &&
	$stack[$l-1]->getName()=='Couple_MT_PARENTHESE_OPEN' &&
	$stack[$l-2]->getName()=='T_STRING'){
		$a=array_pop($stack);
		array_push($stack, new CALL(array_pop($stack), array_pop($stack)));
		while (reduce(&$stack)) continue; // IMPORTANT !
		array_push($stack, $a);
		return true;
	}
	//return
	if (
	$l>=2 &&
	$stack[$l]->getName()=='MT_PT_VIRGULE' &&
	$stack[$l-1]->isVal() &&
	$stack[$l-2]->getName()=='T_RETURN'){
		array_pop($stack);
		$f=new ff_return(array_pop($stack));
		array_pop($stack);
		array_push($stack, $f);
		return true;
	}
	//echo
	if (
	$l>=1 &&
	$stack[$l]->isVal() &&
	$stack[$l-1]->getName()=='T_ECHO'){
		$val=array_pop($stack);
		array_pop($stack);
		array_push($stack, new _Echo($val));
		return true;
	}
	//instructions
	if (
	$l>=1 &&
	$stack[$l]->getName()=='MT_PT_VIRGULE' &&
	($stack[$l-1]->isInstruction() || in_array($stack[$l-1]->getName(), array('EXPRAlone', 'T_BREAK', 'T_CONTINUE'))
	)){
		array_pop($stack);
		array_push($stack, new Instruction(array_pop($stack)));
		return true;
	}
	return false;
}

interface Transformer{ // design : visiteur
	public function visite(Node $e);
	public function CanModify($e);
	public function transforme($e);
}

class Rewrite_String_double implements Transformer{
	public function visite(Node $e){
		if ($this->canModify($e)){
			$this->transforme($e);
		}
	}
	public function CanModify($e){
		return $e instanceof PHP_String;
	}
	public function transforme($e){
		$s=$e->getcontent()->getStack();
		$l=count($s)-1;
		if ($s[1]->getName()=='T_VARIABLE'){
			$chaine=$s[1];
		}else{
			$chaine=Tokens__::makeString(Tokens__::evalString($s[1]->getContent()));
		}
		for ($i=2;$i<$l;$i++){
			if ($s[$i]->getName()=='T_VARIABLE')
				$o=$s[$i];
			else
				$o=Tokens__::makeString(Tokens__::evalString($s[$i]->getContent()));
			$chaine=new Expr($o,Tokens__::getPoint(),$chaine);
		}
		$e->setElement($chaine);
	}
}

if (file_exists($filename)){
	$lexer=token_get_all(file_get_contents($filename));
	$stack=array();
	foreach ($lexer as $token){
		$t=new Token($token);
		if ($t->is_important()){
			array_push($stack, $t);
			while (reduce(&$stack)) continue;
		}else if ($t->getName()!='T_WHITESPACE'){
			Tokens__::AddComment($t);
		}
	}

	// les regles de reecritures
	$rewrite=array();
	$rewrite[]=new Rewrite_String_double();
	// on les applique.
	foreach ($stack as $i=>$e){
		foreach ($rewrite as $r){
			$e->accept($r);
		}
	}

	// on affiche
	echo '<pre>';
	foreach ($stack as $e){
		echo  htmlentities($e);
	}
	echo '</pre>';
}else{
	echo 'fichier non trouve';
}
?>

Conclusion :


Je dois avouer que la grammaire que j'emploies n'est pas complete.

Codes Sources

A voir également

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.