<?php
/**
* Harmony
* Copyright (c) 2008 Maxime Bouroumeau-Fuseau
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @category Harmony
* @package Harmony_Script
* @author Maxime Bouroumeau-Fuseau
* @copyright 2008 (c) Maxime Bouroumeau-Fuseau
* @license http://www.opensource.org/licenses/mit-license.php
* @link http://www.harmony-framework.com
*/
/** Harmony_Parser_Context */
require_once 'Harmony/Parser/Context.php';
/**
* @category Harmony
* @package Harmony_Script
* @copyright 2008 (c) Maxime Bouroumeau-Fuseau
* @license http://www.opensource.org/licenses/mit-license.php
*/
class Harmony_Script_Context extends Harmony_Parser_Context
{
/**
* Javascript code
*
* @var string
*/
public $_js = '';
/**
* Current value of the self keyword
*
* @var string
*/
public static $_self = 'this';
/**
* The last doc block
*
* @var string
*/
protected $_previousDocBlock;
/**
* Stores doc blocks
*/
public function tokenDocComment($text)
{
$this->_previousDocBlock = $text;
}
/**
* Transforms an echo instruction to document.write
*/
public function tokenEcho($text)
{
$this->_js .= 'document.body.write(' . $this->getParser()->enterContext('Line') . ');';
}
/**
* Removes the dollar sign from variables name
*/
public function tokenVariable($text)
{
$this->_js .= substr($text, 1);
}
/**
* Transforms php array syntax to json
*/
public function tokenArray($text)
{
$this->_js .= $this->getParser()->enterContext('Array');
}
/**
* Transforms -> to .
*/
public function tokenObjectOperator($text)
{
$this->_js .= '.';
}
/**
* Transforms :: to .
*/
public function tokenDoubleColon($text)
{
$this->_js .= '.';
}
/**
* Transforms . to +
*/
public function tokenConcat($text)
{
$this->_js .= '+';
}
/**
* Transforms === to ==
*/
public function tokenIsIdentical($text)
{
$this->_js .= '==';
}
/**
* Transforms !== to !=
*/
public function tokenIsNotIdentical($text)
{
$this->_js .= '!=';
}
/**
* Transforms &= to =
*/
public function tokenAndEqual($text)
{
$this->_js .= '=';
}
/**
* Transforms .= to +=
*/
public function tokenConcatEqual($text)
{
$this->_js .= '+=';
}
/**
* Transforms elseif to else if
*/
public function tokenElseif($text)
{
$this->_js .= 'else if';
}
/**
* Transform and to &&
*/
public function tokenLogicalAnd($text)
{
$this->_js .= '&&';
}
/**
* Transform or to ||
*/
public function tokenLogicalOr($text)
{
$this->_js .= '||';
}
/**
* Parses a T_STRING token
*/
public function tokenString($text)
{
if ($text == 'self') {
/* replaced with current class name represented by self */
$this->_js .= self::$_self;
} else if ($text == 'define') {
/* defines a javascript variable */
$args = explode(',', $this->getParser()->enterContext('Line'));
$this->_js .= 'var ' . trim($args[0], '"\'(') . ' = ' . rtrim($args[1], ')') . ";\n";
} else if ($text == 'func_get_args') {
/* replaces func_get_args() with arguments */
$this->getParser()->enterContext('Line');
$this->_js .= 'arguments;';
} else if ($text == 'count') {
/* replaces count() with .length */
$var = trim(str_replace('$', '', $this->getParser()->enterContext('Line', array('endChar' => ')'))));
$this->_js .= ltrim($var, '(') . '.length';
} else if ($text == 'parent') {
$line = $this->getParser()->enterContext('Line');
$this->_js .= 'this.parent' . substr($line, strpos($line, '(')) . ';';
} else if ($text == 'Callback') {
/* special syntax for callbacks */
$args = explode(',', ltrim(str_replace('$', '', $this->getParser()->enterContext('Line', array('endChar' => ')'))), '('));
if (count($args) == 1) {
$this->_js .= $args[0];
return;
}
$this->_js .= trim($args[0]) . '[' . trim($args[1]) . '].bind(' . trim($args[0]) . ')';
} else {
/* any other, raw add */
$this->_js .= $text;
}
}
/**
* Transforms foreach instruction
*/
public function tokenForeach($text)
{
$this->_js .= $this->getParser()->enterContext('ForEach');
}
/**
* Transforms catch instruction
*/
public function tokenCatch($text)
{
$this->_js .= $this->getParser()->enterContext('Catch');
}
/**
* Transforms unset() to delete
*/
public function tokenUnset($text)
{
$args = explode(',', trim(trim($this->getParser()->enterContext('Line')), '()'));
foreach ($args as $arg) {
$this->_js .= 'delete ' . trim(str_replace('$', '', $arg)) . ";\n";
}
}
/**
* Removes include()
*/
public function tokenInclude($text)
{
$this->_includeFile();
}
/**
* Removes include_once()
*/
public function tokenIncludeOnce($text)
{
$this->_includeFile();
}
/**
* Removes require()
*/
public function tokenRequire($text)
{
$this->_includeFile();
}
/**
* Removes require_once()
*/
public function tokenRequireOnce($text)
{
$this->_includeFile();
}
/**
* Includes an external file
*/
protected function _includeFile()
{
$filename = trim($this->getParser()->enterContext('Line'), '() \'"');
if (!empty($this->_previousDocBlock)) {
$docBlock = $this->_previousDocBlock;
$this->_previousDocBlock = null;
if (preg_match('/@harmony-ignore/', $docBlock)) {
return;
}
if (preg_match('/@harmony-include/', $docBlock)) {
/* finds the real path of the file using the include paths */
foreach (explode(PATH_SEPARATOR, get_include_path()) as $path) {
$testFilename = trim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $filename;
if (file_exists($testFilename)) {
$filename = $testFilename;
break;
}
}
/* parses the targeted file and include it in the current one */
$script = new Harmony_Script();
$script->load($filename);
$this->_js .= $script->saveJavascript();
return;
}
}
if (substr($filename, 0, 10) == 'harmony://') {
$filename = substr($filename, 10);
}
if (substr($filename, 0, 15) == 'Harmony/Toolkit') {
if ($filename == 'Harmony/Toolkit.php') {
return;
}
$this->_js .= 'Harmony_Toolkit.loadToolkit("' . substr($filename, 16) . '");';
return;
}
$this->_js .= 'Harmony_Toolkit.loadAsset("' . $filename . '");';
}
/**
* Removes exit()
*/
public function tokenExit($text)
{
$this->getParser()->enterContext('Line');
}
/**
* End of file
*/
public function tokenEof($text)
{
$this->exitContext($this->_js);
}
/** references are not supported */
public function tokenAmp($text) {}
/** var keyword not supported (see Class context) */
public function tokenVar($text) {}
/* class modifiers not supported */
public function tokenPublic($text) {}
public function tokenProtected($text) {}
public function tokenPrivate($text) {}
public function tokenAbstract($text) {}
public function tokenFinal($text) {}
/* cast not supported */
public function tokenArrayCast($text) {}
public function tokenBoolCast($text) {}
public function tokenDoubleCast($text) {}
public function tokenIntCast($text) {}
public function tokenObjectCast($text) {}
public function tokenStringCast($text) {}
public function tokenUnsetCast($text) {}
/** @ syntax to remove errors not supported */
public function tokenNoError($text) {}
/**
* Catch all calls to unmatched tokens and appends them
* to the js code (no transformation)
*/
public function __call($method, $args)
{
$this->_js .= $args[0];
}
}