3v4l.org

run code in 150+ php & hhvm versions
Bugs & Features
<?php interface IErrorManager { public function ariseFatal($message); public function ariseWarning($message); public function ariseNotice($message); } interface ILexer { public function tokenize($input); } interface IParser { public function setKeywords(array $keywords); public function parse($input); } interface IParseResult { public function getDefinition(); public function getName(); public function getConstants(); } interface INameValidator { public function validate($name); } interface ICompiler { public function compile($input); } class ErrorManager implements IErrorManager { public function ariseFatal($message) { $this->triggerError($message, E_USER_FATAL); } public function ariseWarning($message) { $this->triggerError($message, E_USER_WARNING); } public function ariseNotice($message) { $this->triggerError($message, E_USER_NOTICE); } private function triggerError($message, $type) { trigger_error((string)$message, (int)$type); } } abstract class CompilerElement { private $errorManager; protected function getErrorManager() { return $this->errorManager; } protected function __construct(IErrorManager $errorManager) { $this->errorManager = $errorManager; } } class Lexer extends CompilerElement implements ILexer { public function tokenize($input) { if (!is_string($input)) { $this->getErrorManager()->ariseFatal(get_class($this).'::parse expects parameter 1 to be string. '.gettype($input).' given.'); } } public function __construct(IErrorManager $errorManager) { parent::__construct($errorManager); } } class Parser extends CompilerElement implements IParser { private $lexer; private $nameValidator; private $keywords = array(); public function setKeywords(array $keywords) { $this->keywords = $keywords; } public function parse($input) { if (!is_string($input)) { $this->getErrorManager()->ariseFatal(get_class($this).'::parse expects parameter 1 to be string. '.gettype($input).' given.'); } $errorManager = $this->getErrorManager(); $input = $this->cleanUp($input); $tokens = $this->lexer->tokenize($input); $result = array(); $currentResult = null; $processingBody = false; reset($tokens); while (($token = key($tokens)) !== false && ($tokenValue = current($tokens)) !== false) { switch ($token) { case 'type' && $processingBody === false: if ($currrentResult !== null || !isset($this->keywords[$tokenValue])) { $errorManager->ariseFatal('Syntax error. Unexpected '.$tokenValue); } $currentResult = new ParseResult($this->keywords[$tokenValue], $errorManager); break; case 'name' && $processingBody === false: // Will this ever happen? if ($currentResult === null) { $errorManager->ariseFatal('Syntax error. Unexpected '.$tokenValue); } $this->nameValidator->validate($name); $currentResult->setName($name); break; case 'start_body': if ($currentResult === null || $currentResult->getName() === null) { $errorManager->ariseFatal('Syntax error. Unexpected '.$tokenValue); } $processingBody = true; break; case 'end_body': if ($currentResult === null || $currentResult->getName() === null || $processingBody === false) { $errorManager->ariseFatal('Syntax error. Unexpected '.$tokenValue); } $result[] = $currentResult; $currentResult = null; $processingBody = false; break; default: if ($processingBody === true && $currentResult !== null) { $value = $tokenValue !== null ? $tokenValie : 0; $currentResult->setItem($token, $value); } else { $errorManager->ariseFatal('Syntax error. Unexpected '.$tokenValue); } break; } next($tokens); } return $return; } private function cleanUp($input) { return trim($input); } public function __construct(ILexer $lexer, INameValidator $nameValidator, IErrorManager $errorManager) { parent::__construct($errorManager); $this->lexer = $lexer; $this->nameValidator = $nameValidator; } } class ParseResult extends CompilerElement implements IParseResult { private $definition; private $name; private $constants; public function getDefinition() { return $this->definition; } public function getName() { return $this->name; } public function getConstants() { return $this->constants; } public function setItem($name, $value) { if (!is_string($name)) { $this->getErrorManager()->ariseFatal('Enumeration item name can only be string.'); } if (!is_numeric($value)) { $this->getErrorManager()->ariseFatal('Enumeration item can only hold numeric value.'); } $this->constants[$name] = (ctype_digit($value) === false) ? (int)$value : (double)$value; } public function setName($name) { if (!is_string($name)) { $this->getErrorManager()->ariseFatal(get_class($this).'::setName expects parameter 1 to be string. '.gettype($name).' given.'); } $this->name = $name; } public function __construct($definition, IErrorManager $errorManager) { parent::__construct($errorManager); if (!is_string($definition)) { $this->getErrorManager()->ariseFatal(get_class($this).'::__construct expects parameter 1 to be string. '.gettype($definition).' given.'); } $this->definition = $definition; } } class NameValidator extends CompilerElement implements INameValidator { private $allowedChars; public function validate($name) { if (!is_string($name)) { $this->gerErrorManager()->ariseFatal(get_class($this).'::validate expects parameter 1 to be string. '.gettype($name).' given.'); } $nameLength = strlen($name); for ($i = 0; $i < $nameLength; $i++) { $current = $name[$i]; if ($i === 0 && !ctype_alpha($current)) { $this->gerErrorManager()->ariseFatal('Name should start with alphabetic characters. Given name '.$name.' starts with: '.$current); } if (!in_array($current, $this->allowedChars, true)) { $this->gerErrorManager()->ariseFatal('Unexpected character '.$current.' in name '.$name.'.'); } } } public function __construct(IErrorManager $errorManager) { parent::__construct($errorManager); // Yes, I hate that everything is allowed as name in php. $this->allowedChars = str_split('abcdefghijklmnopqrstuvwxyz0123456789'); } } class Enum2PhpCompiler implements ICompiler { private $parser; const INTENDATION_CHAR = "\t"; public function compile($input) { $parseResult = $this->parser->parse($input); $return = $parseResult->getDefinition().' '.$parseResult->getName().' {'.PHP_EOL; foreach ($parseResult->getConstants() as $name => $value) { $return .= Compiler::INTENDATION_CHAR.'const '.$name.' = '.$value.PHP_EOL; } $return .= Compiler::INTENDATION_CHAR.'private function __construct() {}'.PHP_EOL; $return .= '}'; return $return; } public function __construct(IParser $parser) { $this->parser = $parser; } } $code = <<<PHP enum MyEnum { Item1, Item2 = 5, Item3 } PHP;
Output for 5.4.0 - 5.5.1
Parse error: syntax error, unexpected end of file, expecting variable (T_VARIABLE) or heredoc end (T_END_HEREDOC) or ${ (T_DOLLAR_OPEN_CURLY_BRACES) or {$ (T_CURLY_OPEN) in /in/HpZnB on line 266
Process exited with code 255.
Output for 5.3.0 - 5.3.27
Parse error: syntax error, unexpected $end, expecting T_VARIABLE or T_END_HEREDOC or T_DOLLAR_OPEN_CURLY_BRACES or T_CURLY_OPEN in /in/HpZnB on line 266
Process exited with code 255.
Output for 5.1.0 - 5.2.17
Parse error: syntax error, unexpected $end in /in/HpZnB on line 266
Process exited with code 255.
Output for 5.0.0 - 5.0.5
Parse error: parse error, unexpected T_ARRAY, expecting '&' or T_VARIABLE in /in/HpZnB on line 13
Process exited with code 255.
Output for 4.4.2 - 4.4.9
Parse error: syntax error, unexpected T_STRING in /in/HpZnB on line 2
Process exited with code 255.
Output for 4.3.0 - 4.3.1, 4.3.5 - 4.4.1
Parse error: parse error, unexpected T_STRING in /in/HpZnB on line 2
Process exited with code 255.
Output for 4.3.2 - 4.3.4
Parse error: parse error in /in/HpZnB on line 2
Process exited with code 255.