Files
Ismo Vuorinen 7e73994e40 Sparks: Assets, 1.5.1
http://getsparks.org/packages/assets/versions/HEAD/show

Creates our minified and combined .js and .css files
2013-07-11 07:46:29 +03:00

553 lines
14 KiB
PHP
Executable File

<?php
namespace CoffeeScript;
Init::init();
class Rewriter
{
static $BALANCED_PAIRS = array(
array('(', ')'),
array('[', ']'),
array('{', '}'),
array('INDENT', 'OUTDENT'),
array('CALL_START', 'CALL_END'),
array('PARAM_START', 'PARAM_END'),
array('INDEX_START', 'INDEX_END'),
);
static $INVERSES = array();
static $EXPRESSION_START = array();
static $EXPRESSION_END = array();
static $EXPRESSION_CLOSE = array('CATCH', 'WHEN', 'ELSE', 'FINALLY');
static $IMPLICIT_FUNC = array('IDENTIFIER', 'SUPER', ')', 'CALL_END', ']', 'INDEX_END', '@', 'THIS');
static $IMPLICIT_CALL = array(
'IDENTIFIER', 'NUMBER', 'STRING', 'JS', 'REGEX', 'NEW', 'PARAM_START', 'CLASS',
'IF', 'TRY', 'SWITCH', 'THIS', 'BOOL', 'UNARY', 'SUPER',
'@', '->', '=>', '[', '(', '{', '--', '++'
);
static $IMPLICIT_UNSPACED_CALL = array('+', '-');
static $IMPLICIT_BLOCK = array('->', '=>', '{', '[', ',');
static $IMPLICIT_END = array('POST_IF', 'FOR', 'WHILE', 'UNTIL', 'WHEN', 'BY', 'LOOP', 'TERMINATOR');
static $SINGLE_LINERS = array('ELSE', '->', '=>', 'TRY', 'FINALLY', 'THEN');
static $SINGLE_CLOSERS = array('TERMINATOR', 'CATCH', 'FINALLY', 'ELSE', 'OUTDENT', 'LEADING_WHEN');
static $LINEBREAKS = array('TERMINATOR', 'INDENT', 'OUTDENT');
static $initialized = FALSE;
static function init()
{
if (self::$initialized) return;
self::$initialized = TRUE;
foreach (self::$BALANCED_PAIRS as $pair)
{
list($left, $rite) = $pair;
self::$EXPRESSION_START[] = self::$INVERSES[$rite] = $left;
self::$EXPRESSION_END[] = self::$INVERSES[$left] = $rite;
}
self::$EXPRESSION_CLOSE = array_merge(self::$EXPRESSION_CLOSE, self::$EXPRESSION_END);
}
function __construct($tokens)
{
self::init();
$this->tokens = $tokens;
}
function add_implicit_braces()
{
$stack = array();
$start = NULL;
$starts_line = NULL;
$same_line = TRUE;
$start_indent = 0;
$self = $this;
$condition = function( & $token, $i) use ( & $self, & $same_line, & $starts_line)
{
$list = array();
for ($j = 0; $j < 3; $j++)
{
$k = ($i + 1) + $j;
$list[$j] = isset($self->tokens[$k]) ? $self->tokens[$k] : array(NULL, NULL);
}
list($one, $two, $three) = $list;
if ($one[0] === t('HERECOMMENT'))
{
return FALSE;
}
$tag = $token[0];
if (in_array($tag, t(Rewriter::$LINEBREAKS)))
{
$same_line = FALSE;
}
return
( (in_array($tag, t('TERMINATOR', 'OUTDENT')) || (in_array($tag, t(Rewriter::$IMPLICIT_END)) && $same_line)) &&
( ( ! $starts_line && $self->tag($i - 1) !== t(',')) ||
! ($two[0] === t(':') || $one[0] === t('@') && $three[0] === t(':'))) ) ||
($tag === t(',') &&
! in_array($one[0], t('IDENTIFIER', 'NUMBER', 'STRING', '@', 'TERMINATOR', 'OUTDENT')) );
};
$action = function( & $token, $i) use ( & $self)
{
$tok = $self->generate(t('}'), '}', $token[2]);
array_splice($self->tokens, $i, 0, array($tok));
};
$this->scan_tokens(function( & $token, $i, & $tokens) use (& $self, & $stack, & $start, & $start_indent, & $condition, & $action, & $starts_line, & $same_line)
{
if (in_array(($tag = $token[0]), t(Rewriter::$EXPRESSION_START)))
{
$stack[] = array( ($tag === t('INDENT') && $self->tag($i - 1) === t('{')) ? t('{') : $tag, $i );
return 1;
}
if (in_array($tag, t(Rewriter::$EXPRESSION_END)))
{
$start = array_pop($stack);
return 1;
}
$len = count($stack) - 1;
if ( ! ($tag === t(':') && (($ago = $self->tag($i - 2)) === t(':') || ( ! isset($stack[$len]) || $stack[$len][0] !== t('{'))) ))
{
return 1;
}
$same_line = TRUE;
$stack[] = array(t('{'));
$idx = (isset($ago) && $ago === t('@')) ? $i - 2 : $i - 1;
while ($self->tag($idx - 2) === t('HERECOMMENT'))
{
$idx -= 2;
}
$prev_tag = $self->tag($idx - 1);
$starts_line = ! $prev_tag || in_array($prev_tag, t(Rewriter::$LINEBREAKS));
$value = wrap('{');
$value->generated = TRUE;
$tok = $self->generate(t('{'), $value, $token[2]);
array_splice($tokens, $idx, 0, array($tok));
$self->detect_end($i + 2, $condition, $action);
return 2;
});
}
function add_implicit_indentation()
{
$self = $this;
$starter = $indent = $outdent = NULL;
$condition = function($token, $i) use ( & $starter)
{
return $token[1] !== ';' && in_array($token[0], t(Rewriter::$SINGLE_CLOSERS)) && ! ($token[0] === t('ELSE') && ! in_array($starter, t('IF', 'THEN')));
};
$action = function($token, $i) use ( & $self, & $outdent)
{
if ($outdent !== NULL)
{
array_splice($self->tokens, $self->tag($i - 1) === t(',') ? $i - 1 : $i, 0, array($outdent));
}
};
$this->scan_tokens(function( & $token, $i, & $tokens) use ( & $action, & $condition, & $self, & $indent, & $outdent, & $starter)
{
$tag = $token[0];
if ($tag === t('TERMINATOR') && $self->tag($i + 1) === t('THEN'))
{
array_splice($tokens, $i, 1);
return 0;
}
if ($tag === t('ELSE') && $self->tag($i - 1) !== t('OUTDENT'))
{
array_splice($tokens, $i, 0, $self->indentation($token));
return 2;
}
if ($tag === t('CATCH') && in_array($self->tag($i + 2), t('OUTDENT', 'TERMINATOR', 'FINALLY')))
{
array_splice($tokens, $i + 2, 0, $self->indentation($token));
return 4;
}
if (in_array($tag, t(Rewriter::$SINGLE_LINERS)) && $self->tag($i + 1) !== t('INDENT') &&
! ($tag === t('ELSE') && $self->tag($i + 1) === t('IF')))
{
$starter = $tag;
list($indent, $outdent) = $self->indentation($token, TRUE);
if ($starter === t('THEN'))
{
$indent['fromThen'] = TRUE;
}
array_splice($tokens, $i + 1, 0, array($indent));
$self->detect_end($i + 2, $condition, $action);
if ($tag === t('THEN'))
{
array_splice($tokens, $i, 1);
}
return 1;
}
return 1;
});
}
function add_implicit_parentheses()
{
$no_call = $seen_single = $seen_control = FALSE;
$self = $this;
$condition = function( & $token, $i) use ( & $self, & $seen_single, & $seen_control, & $no_call)
{
$tag = $token[0];
if ( ! $seen_single && (isset($token['fromThen']) && $token['fromThen']))
{
return TRUE;
}
if (in_array($tag, t('IF', 'ELSE', 'CATCH', '->', '=>', 'CLASS')))
{
$seen_single = TRUE;
}
if (in_array($tag, t('IF', 'ELSE', 'SWITCH', 'TRY', '=')))
{
$seen_control = TRUE;
}
if (in_array($tag, t('.', '?.', '::')) && $self->tag($i - 1) === t('OUTDENT'))
{
return TRUE;
}
return ! (isset($token['generated']) && $token['generated']) && $self->tag($i - 1) !== t(',') &&
(in_array($tag, t(Rewriter::$IMPLICIT_END)) || ($tag === t('INDENT') && ! $seen_control)) &&
($tag !== t('INDENT') ||
( ! in_array($self->tag($i - 2), t('CLASS', 'EXTENDS')) && ! in_array($self->tag($i - 1), t(Rewriter::$IMPLICIT_BLOCK)) &&
! (($post = isset($self->tokens[$i + 1]) ? $self->tokens[$i + 1] : NULL) && (isset($post['generated']) && $post['generated']) && $post[0] === t('{'))));
};
$action = function( & $token, $i) use ( & $self)
{
array_splice($self->tokens, $i, 0, array($self->generate(t('CALL_END'), ')', isset($token[2]) ? $token[2] : NULL)));
};
$this->scan_tokens(function( & $token, $i, & $tokens) use ( & $condition, & $action, & $no_call, & $self, & $seen_control, & $seen_single )
{
$tag = $token[0];
if (in_array($tag, t('CLASS', 'IF', 'FOR', 'WHILE')))
{
$no_call = TRUE;
}
$prev = NULL;
if (isset($tokens[$i - 1]))
{
$prev = & $tokens[$i - 1];
}
$current = $tokens[$i];
$next = isset($tokens[$i + 1]) ? $tokens[$i + 1] : NULL;
$call_object = ! $no_call && $tag === t('INDENT') &&
$next && (isset($next['generated']) && $next['generated']) && $next[0] === t('{') &&
$prev && in_array($prev[0], t(Rewriter::$IMPLICIT_FUNC));
$seen_single = FALSE;
$seen_control = FALSE;
if (in_array($tag, t(Rewriter::$LINEBREAKS)))
{
$no_call = FALSE;
}
if ($prev && ! (isset($prev['spaced']) && $prev['spaced']) && $tag === t('?'))
{
$token['call'] = TRUE;
}
if (isset($token['fromThen']) && $token['fromThen'])
{
return 1;
}
if ( ! ($call_object || ($prev && (isset($prev['spaced']) && $prev['spaced'])) &&
( (isset($prev['call']) && $prev['call']) || in_array($prev[0], t(Rewriter::$IMPLICIT_FUNC)) ) &&
( in_array($tag, t(Rewriter::$IMPLICIT_CALL)) || ! ( (isset($token['spaced']) && $token['spaced']) ||
(isset($token['newLine']) && $token['newLine']) ) &&
in_array($tag, t(Rewriter::$IMPLICIT_UNSPACED_CALL)) )
))
{
return 1;
}
array_splice($tokens, $i, 0, array($self->generate(t('CALL_START'), '(', $token[2])));
$self->detect_end($i + 1, $condition, $action);
if ($prev[0] === t('?'))
{
$prev[0] = t('FUNC_EXIST');
}
return 2;
});
}
function close_open_calls()
{
$self = $this;
$condition = function($token, $i) use ( & $self)
{
return in_array($token[0], t(')', 'CALL_END')) || $token[0] === t('OUTDENT') &&
$self->tag($i - 1) === t(')');
};
$action = function($token, $i) use ( & $self)
{
$self->tokens[($token[0] === t('OUTDENT') ? $i - 1 : $i)][0] = t('CALL_END');
};
$this->scan_tokens(function($token, $i) use ( & $self, $condition, $action)
{
if ($token[0] === t('CALL_START'))
{
$self->detect_end($i + 1, $condition, $action);
}
return 1;
});
}
function close_open_indexes()
{
$condition = function($token, $i)
{
return in_array($token[0], t(']', 'INDEX_END'));
};
$action = function( & $token, $i)
{
$token[0] = t('INDEX_END');
};
$self = $this;
$this->scan_tokens(function($token, $i) use ( & $self, $condition, $action)
{
if ($token[0] === t('INDEX_START'))
{
$self->detect_end($i + 1, $condition, $action);
}
return 1;
});
}
function detect_end($i, $condition, $action)
{
$tokens = & $this->tokens;
$levels = 0;
while (isset($tokens[$i]))
{
$token = & $tokens[$i];
if ($levels === 0 && $condition($token, $i))
{
return $action($token, $i);
}
if ( ! $token || $levels < 0)
{
return $action($token, $i - 1);
}
if (in_array($token[0], t(Rewriter::$EXPRESSION_START)))
{
$levels++;
}
else if (in_array($token[0], t(Rewriter::$EXPRESSION_END)))
{
$levels--;
}
$i++;
}
return $i - 1;
}
function generate($tag, $value, $line)
{
return array($tag, $value, $line, 'generated' => TRUE);
}
function indentation($token, $implicit = FALSE)
{
$indent = array(t('INDENT'), 2, $token[2]);
$outdent = array(t('OUTDENT'), 2, $token[2]);
if ($implicit)
{
$indent['generated'] = $outdent['generated'] = TRUE;
}
return array($indent, $outdent);
}
function remove_leading_newlines()
{
$key = 0;
foreach ($this->tokens as $k => $token)
{
$key = $k;
$tag = $token[0];
if ($tag !== t('TERMINATOR'))
{
break;
}
}
if ($key)
{
array_splice($this->tokens, 0, $key);
}
}
function remove_mid_expression_newlines()
{
$self = $this;
$this->scan_tokens(function( & $token, $i, & $tokens) use ( & $self)
{
if ( ! ($token[0] === t('TERMINATOR') && in_array($self->tag($i + 1), t(Rewriter::$EXPRESSION_CLOSE))))
{
return 1;
}
array_splice($tokens, $i, 1);
return 0;
});
}
function rewrite()
{
$this->remove_leading_newlines();
$this->remove_mid_expression_newlines();
$this->close_open_calls();
$this->close_open_indexes();
$this->add_implicit_indentation();
$this->tag_postfix_conditionals();
$this->add_implicit_braces();
$this->add_implicit_parentheses();
return $this->tokens;
}
function scan_tokens($block)
{
$i = 0;
while (isset($this->tokens[$i]))
{
$i += $block($this->tokens[$i], $i, $this->tokens);
}
return TRUE;
}
function tag($i)
{
return isset($this->tokens[$i]) ? $this->tokens[$i][0] : NULL;
}
function tag_postfix_conditionals()
{
$original = NULL;
$self = $this;
$condition = function($token, $i)
{
return in_array($token[0], t('TERMINATOR', 'INDENT'));
};
$action = function($token, $i) use ( & $original, & $self)
{
if ($token[0] !== t('INDENT') || ((isset($token['generated']) && $token['generated']) && ! (isset($token['fromThen']) && $token['fromThen'])))
{
$self->tokens[$original][0] = t('POST_'.t_canonical($self->tokens[$original][0]));
// $original[0] = t('POST_'.t_canonical($original[0]));
}
};
$self = $this;
$this->scan_tokens(function( & $token, $i) use ( & $original, & $condition, & $action, & $self)
{
if ( ! ($token[0] === t('IF')))
{
return 1;
}
$original = $i;
// $original = & $token;
$self->detect_end($i + 1, $condition, $action);
return 1;
});
}
}
?>