Same name in other branches
- 5.0.x advagg_js_minify/jsminplus.inc \JSMinPlus::parseTree()
- 6.0.x advagg_js_minify/jsminplus.inc \JSMinPlus::parseTree()
- 7.x-2.x advagg_js_compress/jsminplus.inc \JSMinPlus::parseTree()
- 8.x-2.x advagg_js_minify/jsminplus.inc \JSMinPlus::parseTree()
- 8.x-3.x advagg_js_minify/jsminplus.inc \JSMinPlus::parseTree()
- 8.x-4.x advagg_js_minify/jsminplus.inc \JSMinPlus::parseTree()
1 call to JSMinPlus::parseTree()
- JSMinPlus::min in advagg_js_compress/
jsminplus.inc
File
-
advagg_js_compress/
jsminplus.inc, line 273
Class
Code
public function parseTree($n, $noBlockGrouping = false) {
$s = '';
switch ($n->type) {
case JS_MINIFIED:
$s = $n->value;
break;
case JS_SCRIPT:
// we do nothing yet with funDecls or varDecls
$noBlockGrouping = true;
// FALL THROUGH
case JS_BLOCK:
$childs = $n->treeNodes;
$lastType = 0;
for ($c = 0, $i = 0, $j = count($childs); $i < $j; $i++) {
$type = $childs[$i]->type;
$t = $this->parseTree($childs[$i]);
if (strlen($t)) {
if ($c) {
$s = rtrim($s, ';');
if ($type == KEYWORD_FUNCTION && $childs[$i]->functionForm == DECLARED_FORM) {
// put declared functions on a new line
$s .= "\n";
}
elseif ($type == KEYWORD_VAR && $type == $lastType) {
// mutiple var-statements can go into one
$t = ',' . substr($t, 4);
}
else {
// add terminator
$s .= ';';
}
}
$s .= $t;
$c++;
$lastType = $type;
}
}
if ($c > 1 && !$noBlockGrouping) {
$s = '{' . $s . '}';
}
break;
case KEYWORD_FUNCTION:
$s .= 'function' . ($n->name ? ' ' . $n->name : '') . '(';
$params = $n->params;
for ($i = 0, $j = count($params); $i < $j; $i++) {
$s .= ($i ? ',' : '') . $params[$i];
}
$s .= '){' . $this->parseTree($n->body, true) . '}';
break;
case KEYWORD_IF:
$s = 'if(' . $this->parseTree($n->condition) . ')';
$thenPart = $this->parseTree($n->thenPart);
$elsePart = $n->elsePart ? $this->parseTree($n->elsePart) : null;
// empty if-statement
if ($thenPart == '') {
$thenPart = ';';
}
if ($elsePart) {
// be carefull and always make a block out of the thenPart; could be more optimized but is a lot of trouble
if ($thenPart != ';' && $thenPart[0] != '{') {
$thenPart = '{' . $thenPart . '}';
}
$s .= $thenPart . 'else';
// we could check for more, but that hardly ever applies so go for performance
if ($elsePart[0] != '{') {
$s .= ' ';
}
$s .= $elsePart;
}
else {
$s .= $thenPart;
}
break;
case KEYWORD_SWITCH:
$s = 'switch(' . $this->parseTree($n->discriminant) . '){';
$cases = $n->cases;
for ($i = 0, $j = count($cases); $i < $j; $i++) {
$case = $cases[$i];
if ($case->type == KEYWORD_CASE) {
$s .= 'case' . ($case->caseLabel->type != TOKEN_STRING ? ' ' : '') . $this->parseTree($case->caseLabel) . ':';
}
else {
$s .= 'default:';
}
$statement = $this->parseTree($case->statements, true);
if ($statement) {
$s .= $statement;
// no terminator for last statement
if ($i + 1 < $j) {
$s .= ';';
}
}
}
$s .= '}';
break;
case KEYWORD_FOR:
$s = 'for(' . ($n->setup ? $this->parseTree($n->setup) : '') . ';' . ($n->condition ? $this->parseTree($n->condition) : '') . ';' . ($n->update ? $this->parseTree($n->update) : '') . ')';
$body = $this->parseTree($n->body);
if ($body == '') {
$body = ';';
}
$s .= $body;
break;
case KEYWORD_WHILE:
$s = 'while(' . $this->parseTree($n->condition) . ')';
$body = $this->parseTree($n->body);
if ($body == '') {
$body = ';';
}
$s .= $body;
break;
case JS_FOR_IN:
$s = 'for(' . ($n->varDecl ? $this->parseTree($n->varDecl) : $this->parseTree($n->iterator)) . ' in ' . $this->parseTree($n->object) . ')';
$body = $this->parseTree($n->body);
if ($body == '') {
$body = ';';
}
$s .= $body;
break;
case KEYWORD_DO:
$s = 'do{' . $this->parseTree($n->body, true) . '}while(' . $this->parseTree($n->condition) . ')';
break;
case KEYWORD_BREAK:
case KEYWORD_CONTINUE:
$s = $n->value . ($n->label ? ' ' . $n->label : '');
break;
case KEYWORD_TRY:
$s = 'try{' . $this->parseTree($n->tryBlock, true) . '}';
$catchClauses = $n->catchClauses;
for ($i = 0, $j = count($catchClauses); $i < $j; $i++) {
$t = $catchClauses[$i];
$s .= 'catch(' . $t->varName . ($t->guard ? ' if ' . $this->parseTree($t->guard) : '') . '){' . $this->parseTree($t->block, true) . '}';
}
if ($n->finallyBlock) {
$s .= 'finally{' . $this->parseTree($n->finallyBlock, true) . '}';
}
break;
case KEYWORD_THROW:
case KEYWORD_RETURN:
$s = $n->type;
if ($n->value) {
$t = $this->parseTree($n->value);
if (strlen($t)) {
if ($this->isWordChar($t[0]) || $t[0] == '\\') {
$s .= ' ';
}
$s .= $t;
}
}
break;
case KEYWORD_WITH:
$s = 'with(' . $this->parseTree($n->object) . ')' . $this->parseTree($n->body);
break;
case KEYWORD_VAR:
case KEYWORD_CONST:
$s = $n->value . ' ';
$childs = $n->treeNodes;
for ($i = 0, $j = count($childs); $i < $j; $i++) {
$t = $childs[$i];
$s .= ($i ? ',' : '') . $t->name;
$u = $t->initializer;
if ($u) {
$s .= '=' . $this->parseTree($u);
}
}
break;
case KEYWORD_IN:
case KEYWORD_INSTANCEOF:
$left = $this->parseTree($n->treeNodes[0]);
$right = $this->parseTree($n->treeNodes[1]);
$s = $left;
if ($this->isWordChar(substr($left, -1))) {
$s .= ' ';
}
$s .= $n->type;
if ($this->isWordChar($right[0]) || $right[0] == '\\') {
$s .= ' ';
}
$s .= $right;
break;
case KEYWORD_DELETE:
case KEYWORD_TYPEOF:
$right = $this->parseTree($n->treeNodes[0]);
$s = $n->type;
if ($this->isWordChar($right[0]) || $right[0] == '\\') {
$s .= ' ';
}
$s .= $right;
break;
case KEYWORD_VOID:
$s = 'void(' . $this->parseTree($n->treeNodes[0]) . ')';
break;
case KEYWORD_DEBUGGER:
throw new Exception('NOT IMPLEMENTED: DEBUGGER');
break;
case TOKEN_CONDCOMMENT_START:
case TOKEN_CONDCOMMENT_END:
$s = $n->value . ($n->type == TOKEN_CONDCOMMENT_START ? ' ' : '');
$childs = $n->treeNodes;
for ($i = 0, $j = count($childs); $i < $j; $i++) {
$s .= $this->parseTree($childs[$i]);
}
break;
case OP_SEMICOLON:
if ($expression = $n->expression) {
$s = $this->parseTree($expression);
}
break;
case JS_LABEL:
$s = $n->label . ':' . $this->parseTree($n->statement);
break;
case OP_COMMA:
$childs = $n->treeNodes;
for ($i = 0, $j = count($childs); $i < $j; $i++) {
$s .= ($i ? ',' : '') . $this->parseTree($childs[$i]);
}
break;
case OP_ASSIGN:
$s = $this->parseTree($n->treeNodes[0]) . $n->value . $this->parseTree($n->treeNodes[1]);
break;
case OP_HOOK:
$s = $this->parseTree($n->treeNodes[0]) . '?' . $this->parseTree($n->treeNodes[1]) . ':' . $this->parseTree($n->treeNodes[2]);
break;
case OP_OR:
case OP_AND:
case OP_BITWISE_OR:
case OP_BITWISE_XOR:
case OP_BITWISE_AND:
case OP_EQ:
case OP_NE:
case OP_STRICT_EQ:
case OP_STRICT_NE:
case OP_LT:
case OP_LE:
case OP_GE:
case OP_GT:
case OP_LSH:
case OP_RSH:
case OP_URSH:
case OP_MUL:
case OP_DIV:
case OP_MOD:
$s = $this->parseTree($n->treeNodes[0]) . $n->type . $this->parseTree($n->treeNodes[1]);
break;
case OP_PLUS:
case OP_MINUS:
$left = $this->parseTree($n->treeNodes[0]);
$right = $this->parseTree($n->treeNodes[1]);
switch ($n->treeNodes[1]->type) {
case OP_PLUS:
case OP_MINUS:
case OP_INCREMENT:
case OP_DECREMENT:
case OP_UNARY_PLUS:
case OP_UNARY_MINUS:
$s = $left . $n->type . ' ' . $right;
break;
case TOKEN_STRING:
//combine concatted strings with same quotestyle
if ($n->type == OP_PLUS && substr($left, -1) == $right[0]) {
$s = substr($left, 0, -1) . substr($right, 1);
break;
}
// FALL THROUGH
default:
$s = $left . $n->type . $right;
}
break;
case OP_NOT:
case OP_BITWISE_NOT:
case OP_UNARY_PLUS:
case OP_UNARY_MINUS:
$s = $n->value . $this->parseTree($n->treeNodes[0]);
break;
case OP_INCREMENT:
case OP_DECREMENT:
if ($n->postfix) {
$s = $this->parseTree($n->treeNodes[0]) . $n->value;
}
else {
$s = $n->value . $this->parseTree($n->treeNodes[0]);
}
break;
case OP_DOT:
$s = $this->parseTree($n->treeNodes[0]) . '.' . $this->parseTree($n->treeNodes[1]);
break;
case JS_INDEX:
$s = $this->parseTree($n->treeNodes[0]);
// See if we can replace named index with a dot saving 3 bytes
if ($n->treeNodes[0]->type == TOKEN_IDENTIFIER && $n->treeNodes[1]->type == TOKEN_STRING && $this->isValidIdentifier(substr($n->treeNodes[1]->value, 1, -1))) {
$s .= '.' . substr($n->treeNodes[1]->value, 1, -1);
}
else {
$s .= '[' . $this->parseTree($n->treeNodes[1]) . ']';
}
break;
case JS_LIST:
$childs = $n->treeNodes;
for ($i = 0, $j = count($childs); $i < $j; $i++) {
$s .= ($i ? ',' : '') . $this->parseTree($childs[$i]);
}
break;
case JS_CALL:
$s = $this->parseTree($n->treeNodes[0]) . '(' . $this->parseTree($n->treeNodes[1]) . ')';
break;
case KEYWORD_NEW:
case JS_NEW_WITH_ARGS:
$s = 'new ' . $this->parseTree($n->treeNodes[0]) . '(' . ($n->type == JS_NEW_WITH_ARGS ? $this->parseTree($n->treeNodes[1]) : '') . ')';
break;
case JS_ARRAY_INIT:
$s = '[';
$childs = $n->treeNodes;
for ($i = 0, $j = count($childs); $i < $j; $i++) {
$s .= ($i ? ',' : '') . $this->parseTree($childs[$i]);
}
$s .= ']';
break;
case JS_OBJECT_INIT:
$s = '{';
$childs = $n->treeNodes;
for ($i = 0, $j = count($childs); $i < $j; $i++) {
$t = $childs[$i];
if ($i) {
$s .= ',';
}
if ($t->type == JS_PROPERTY_INIT) {
// Ditch the quotes when the index is a valid identifier
if ($t->treeNodes[0]->type == TOKEN_STRING && $this->isValidIdentifier(substr($t->treeNodes[0]->value, 1, -1))) {
$s .= substr($t->treeNodes[0]->value, 1, -1);
}
else {
$s .= $t->treeNodes[0]->value;
}
$s .= ':' . $this->parseTree($t->treeNodes[1]);
}
else {
$s .= $t->type == JS_GETTER ? 'get' : 'set';
$s .= ' ' . $t->name . '(';
$params = $t->params;
for ($i = 0, $j = count($params); $i < $j; $i++) {
$s .= ($i ? ',' : '') . $params[$i];
}
$s .= '){' . $this->parseTree($t->body, true) . '}';
}
}
$s .= '}';
break;
case TOKEN_NUMBER:
$s = $n->value;
if (preg_match('/^([1-9]+)(0{3,})$/', $s, $m)) {
$s = $m[1] . 'e' . strlen($m[2]);
}
break;
case KEYWORD_NULL:
case KEYWORD_THIS:
case KEYWORD_TRUE:
case KEYWORD_FALSE:
case TOKEN_IDENTIFIER:
case TOKEN_STRING:
case TOKEN_REGEXP:
$s = $n->value;
break;
case JS_GROUP:
if (in_array($n->treeNodes[0]->type, array(
JS_ARRAY_INIT,
JS_OBJECT_INIT,
JS_GROUP,
TOKEN_NUMBER,
TOKEN_STRING,
TOKEN_REGEXP,
TOKEN_IDENTIFIER,
KEYWORD_NULL,
KEYWORD_THIS,
KEYWORD_TRUE,
KEYWORD_FALSE,
))) {
$s = $this->parseTree($n->treeNodes[0]);
}
else {
$s = '(' . $this->parseTree($n->treeNodes[0]) . ')';
}
break;
default:
throw new Exception('UNKNOWN TOKEN TYPE: ' . $n->type);
}
return $s;
}