', '=>', '[', '(', '{', '--', '++' ); 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; }); } } ?>