Partially revert "Update scssphp to 0.6.6"
authorTim Düsterhus <duesterhus@woltlab.com>
Mon, 31 Oct 2016 13:53:29 +0000 (14:53 +0100)
committerTim Düsterhus <duesterhus@woltlab.com>
Mon, 31 Oct 2016 13:54:05 +0000 (14:54 +0100)
This partially reverts commit 0300f0d2a8f585744f9e4e5ae3c163c13adbec49.

Only the changes to the compiler have been reverted, the changes to
the .scss file have been preserved.

26 files changed:
wcfsetup/install/files/lib/system/style/scssphp/LICENSE.md [deleted file]
wcfsetup/install/files/lib/system/style/scssphp/classmap.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/style/scssphp/scss.inc.php
wcfsetup/install/files/lib/system/style/scssphp/src/Base/Range.php
wcfsetup/install/files/lib/system/style/scssphp/src/Block.php [deleted file]
wcfsetup/install/files/lib/system/style/scssphp/src/Colors.php
wcfsetup/install/files/lib/system/style/scssphp/src/Compiler.php
wcfsetup/install/files/lib/system/style/scssphp/src/Compiler/Environment.php [deleted file]
wcfsetup/install/files/lib/system/style/scssphp/src/Exception/CompilerException.php [deleted file]
wcfsetup/install/files/lib/system/style/scssphp/src/Exception/ParserException.php [deleted file]
wcfsetup/install/files/lib/system/style/scssphp/src/Exception/ServerException.php [deleted file]
wcfsetup/install/files/lib/system/style/scssphp/src/Formatter.php
wcfsetup/install/files/lib/system/style/scssphp/src/Formatter/Compact.php
wcfsetup/install/files/lib/system/style/scssphp/src/Formatter/Compressed.php
wcfsetup/install/files/lib/system/style/scssphp/src/Formatter/Crunched.php
wcfsetup/install/files/lib/system/style/scssphp/src/Formatter/Debug.php [deleted file]
wcfsetup/install/files/lib/system/style/scssphp/src/Formatter/Expanded.php
wcfsetup/install/files/lib/system/style/scssphp/src/Formatter/Nested.php
wcfsetup/install/files/lib/system/style/scssphp/src/Formatter/OutputBlock.php [deleted file]
wcfsetup/install/files/lib/system/style/scssphp/src/Node.php [deleted file]
wcfsetup/install/files/lib/system/style/scssphp/src/Node/Number.php [deleted file]
wcfsetup/install/files/lib/system/style/scssphp/src/Parser.php
wcfsetup/install/files/lib/system/style/scssphp/src/Server.php
wcfsetup/install/files/lib/system/style/scssphp/src/Type.php [deleted file]
wcfsetup/install/files/lib/system/style/scssphp/src/Util.php
wcfsetup/install/files/lib/system/style/scssphp/src/Version.php

diff --git a/wcfsetup/install/files/lib/system/style/scssphp/LICENSE.md b/wcfsetup/install/files/lib/system/style/scssphp/LICENSE.md
deleted file mode 100644 (file)
index 2f5412f..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-Copyright (c) 2015 Leaf Corcoran, http://leafo.github.io/scssphp
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-"Software"), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-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.
diff --git a/wcfsetup/install/files/lib/system/style/scssphp/classmap.php b/wcfsetup/install/files/lib/system/style/scssphp/classmap.php
new file mode 100644 (file)
index 0000000..2d8a52b
--- /dev/null
@@ -0,0 +1,63 @@
+<?php
+/**
+ * SCSSPHP
+ *
+ * Stub classes for backward compatibility
+ *
+ * @copyright 2012-2015 Leaf Corcoran
+ *
+ * @license http://opensource.org/licenses/MIT MIT
+ *
+ * @link http://leafo.github.io/scssphp
+ */
+
+// @codingStandardsIgnoreStart
+/**
+ * @deprecated since 0.1.0
+ */
+class scssc extends \Leafo\ScssPhp\Compiler
+{
+}
+
+/**
+ * @deprecated since 0.1.0
+ */
+class scss_parser extends \Leafo\ScssPhp\Parser
+{
+}
+
+/**
+ * @deprecated since 0.1.0
+ */
+class scss_formatter extends \Leafo\ScssPhp\Formatter\Expanded
+{
+}
+
+/**
+ * @deprecated since 0.1.0
+ */
+class scss_formatter_nested extends \Leafo\ScssPhp\Formatter\Nested
+{
+}
+
+/**
+ * @deprecated since 0.1.0
+ */
+class scss_formatter_compressed extends \Leafo\ScssPhp\Formatter\Compressed
+{
+}
+
+/**
+ * @deprecated since 0.1.0
+ */
+class scss_formatter_crunched extends \Leafo\ScssPhp\Formatter\Crunched
+{
+}
+
+/**
+ * @deprecated since 0.1.0
+ */
+class scss_server extends \Leafo\ScssPhp\Server
+{
+}
+// @codingStandardsIgnoreEnd
index b6892fec4af75a80a991ac332636c5256ac8e99c..51ae57e5c4633d3ffe7d556db6b67cb2c055615f 100644 (file)
@@ -1,30 +1,21 @@
 <?php
-if (version_compare(PHP_VERSION, '5.4') < 0) {
-    throw new \Exception('scssphp requires PHP 5.4 or above');
+if (version_compare(PHP_VERSION, '5.3') < 0) {
+    die('Requires PHP 5.3 or above');
 }
 
 if (! class_exists('scssc', false)) {
     include_once __DIR__ . '/src/Base/Range.php';
-    include_once __DIR__ . '/src/Block.php';
     include_once __DIR__ . '/src/Colors.php';
     include_once __DIR__ . '/src/Compiler.php';
-    include_once __DIR__ . '/src/Compiler/Environment.php';
-    include_once __DIR__ . '/src/Exception/CompilerException.php';
-    include_once __DIR__ . '/src/Exception/ParserException.php';
-    include_once __DIR__ . '/src/Exception/ServerException.php';
     include_once __DIR__ . '/src/Formatter.php';
     include_once __DIR__ . '/src/Formatter/Compact.php';
     include_once __DIR__ . '/src/Formatter/Compressed.php';
     include_once __DIR__ . '/src/Formatter/Crunched.php';
-    include_once __DIR__ . '/src/Formatter/Debug.php';
     include_once __DIR__ . '/src/Formatter/Expanded.php';
     include_once __DIR__ . '/src/Formatter/Nested.php';
-    include_once __DIR__ . '/src/Formatter/OutputBlock.php';
-    include_once __DIR__ . '/src/Node.php';
-    include_once __DIR__ . '/src/Node/Number.php';
     include_once __DIR__ . '/src/Parser.php';
-    include_once __DIR__ . '/src/Type.php';
     include_once __DIR__ . '/src/Util.php';
     include_once __DIR__ . '/src/Version.php';
     include_once __DIR__ . '/src/Server.php';
+    include_once __DIR__ . '/classmap.php';
 }
index a591d7b094ff97906ad9bb84a1eabd9666a4a9e2..769205560e767b54ea00fa423b6ed41584d0dffa 100644 (file)
@@ -12,7 +12,7 @@
 namespace Leafo\ScssPhp\Base;
 
 /**
- * Range
+ * Range class
  *
  * @author Anthon Pang <anthon.pang@gmail.com>
  */
diff --git a/wcfsetup/install/files/lib/system/style/scssphp/src/Block.php b/wcfsetup/install/files/lib/system/style/scssphp/src/Block.php
deleted file mode 100644 (file)
index 6b972a1..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-<?php
-/**
- * SCSSPHP
- *
- * @copyright 2012-2015 Leaf Corcoran
- *
- * @license http://opensource.org/licenses/MIT MIT
- *
- * @link http://leafo.github.io/scssphp
- */
-
-namespace Leafo\ScssPhp;
-
-/**
- * Block
- *
- * @author Anthon Pang <anthon.pang@gmail.com>
- */
-class Block
-{
-    /**
-     * @var string
-     */
-    public $type;
-
-    /**
-     * @var \Leafo\ScssPhp\Block
-     */
-    public $parent;
-
-    /**
-     * @var integer
-     */
-    public $sourceIndex;
-
-    /**
-     * @var integer
-     */
-    public $sourceLine;
-
-    /**
-     * @var integer
-     */
-    public $sourceColumn;
-
-    /**
-     * @var array
-     */
-    public $selectors;
-
-    /**
-     * @var array
-     */
-    public $comments;
-
-    /**
-     * @var array
-     */
-    public $children;
-}
index ff48c199ee0129c4dde1ccfa56a0544992aeea8a..dd32be2146367aebc0737de18c1b4788a58507e2 100644 (file)
@@ -22,10 +22,8 @@ class Colors
      * CSS Colors
      *
      * @see http://www.w3.org/TR/css3-color
-     *
-     * @var array
      */
-    public static $cssColors = [
+    public static $cssColors = array(
         'aliceblue' => '240,248,255',
         'antiquewhite' => '250,235,215',
         'aqua' => '0,255,255',
@@ -145,7 +143,6 @@ class Colors
         'plum' => '221,160,221',
         'powderblue' => '176,224,230',
         'purple' => '128,0,128',
-        'rebeccapurple' => '102,51,153',
         'red' => '255,0,0',
         'rosybrown' => '188,143,143',
         'royalblue' => '65,105,225',
@@ -174,6 +171,6 @@ class Colors
         'white' => '255,255,255',
         'whitesmoke' => '245,245,245',
         'yellow' => '255,255,0',
-        'yellowgreen' => '154,205,50',
-    ];
+        'yellowgreen' => '154,205,50'
+    );
 }
index 592f53b93bfba99ca17dd081fd7596425fec663c..bef345bc6cf93f2d8ea63c76d5d1921841575d97 100644 (file)
 namespace Leafo\ScssPhp;
 
 use Leafo\ScssPhp\Base\Range;
-use Leafo\ScssPhp\Block;
 use Leafo\ScssPhp\Colors;
-use Leafo\ScssPhp\Compiler\Environment;
-use Leafo\ScssPhp\Exception\CompilerException;
-use Leafo\ScssPhp\Formatter\OutputBlock;
-use Leafo\ScssPhp\Node;
-use Leafo\ScssPhp\Type;
 use Leafo\ScssPhp\Parser;
 use Leafo\ScssPhp\Util;
 
@@ -59,99 +53,82 @@ class Compiler
     const LINE_COMMENTS = 1;
     const DEBUG_INFO    = 2;
 
-    const WITH_RULE     = 1;
-    const WITH_MEDIA    = 2;
-    const WITH_SUPPORTS = 4;
-    const WITH_ALL      = 7;
-
     /**
      * @var array
      */
-    static protected $operatorNames = [
-        '+'   => 'add',
-        '-'   => 'sub',
-        '*'   => 'mul',
-        '/'   => 'div',
-        '%'   => 'mod',
-
-        '=='  => 'eq',
-        '!='  => 'neq',
-        '<'   => 'lt',
-        '>'   => 'gt',
-
-        '<='  => 'lte',
-        '>='  => 'gte',
-        '<=>' => 'cmp',
-    ];
+    static protected $operatorNames = array(
+        '+' => 'add',
+        '-' => 'sub',
+        '*' => 'mul',
+        '/' => 'div',
+        '%' => 'mod',
+
+        '==' => 'eq',
+        '!=' => 'neq',
+        '<' => 'lt',
+        '>' => 'gt',
+
+        '<=' => 'lte',
+        '>=' => 'gte',
+    );
 
     /**
      * @var array
      */
-    static protected $namespaces = [
-        'special'  => '%',
-        'mixin'    => '@',
+    static protected $namespaces = array(
+        'special' => '%',
+        'mixin' => '@',
         'function' => '^',
-    ];
-
-    static public $true = [Type::T_KEYWORD, 'true'];
-    static public $false = [Type::T_KEYWORD, 'false'];
-    static public $null = [Type::T_NULL];
-    static public $nullString = [Type::T_STRING, '', []];
-    static public $defaultValue = [Type::T_KEYWORD, ''];
-    static public $selfSelector = [Type::T_SELF];
-    static public $emptyList = [Type::T_LIST, '', []];
-    static public $emptyMap = [Type::T_MAP, [], []];
-    static public $emptyString = [Type::T_STRING, '"', []];
-    static public $with = [Type::T_KEYWORD, 'with'];
-    static public $without = [Type::T_KEYWORD, 'without'];
-
-    protected $importPaths = [''];
-    protected $importCache = [];
-    protected $importedFiles = [];
-    protected $userFunctions = [];
-    protected $registeredVars = [];
-    protected $registeredFeatures = [
-        'extend-selector-pseudoclass' => false,
-        'at-error'                    => true,
-        'units-level-3'               => false,
-        'global-variable-shadowing'   => false,
-    ];
-
-    protected $encoding = null;
+    );
+
+    /**
+     * @var array
+     */
+    static protected $unitTable = array(
+        'in' => array(
+            'in' => 1,
+            'pt' => 72,
+            'pc' => 6,
+            'cm' => 2.54,
+            'mm' => 25.4,
+            'px' => 96,
+            'q'  => 101.6,
+        )
+    );
+
+    static public $true = array('keyword', 'true');
+    static public $false = array('keyword', 'false');
+    static public $null = array('null');
+    static public $defaultValue = array('keyword', '');
+    static public $selfSelector = array('self');
+    static public $emptyList = array('list', '', array());
+    static public $emptyMap = array('map', array(), array());
+    static public $emptyString = array('string', '"', array());
+
+    protected $importPaths = array('');
+    protected $importCache = array();
+    protected $userFunctions = array();
+    protected $registeredVars = array();
+
+    protected $numberPrecision = 5;
     protected $lineNumberStyle = null;
 
     protected $formatter = 'Leafo\ScssPhp\Formatter\Nested';
 
-    protected $rootEnv;
-    protected $rootBlock;
-
-    protected $env;
-    protected $scope;
-    protected $storeEnv;
-    protected $charsetSeen;
-    protected $sourceNames;
-
     private $indentLevel;
     private $commentsSeen;
     private $extends;
     private $extendsMap;
     private $parsedFiles;
+    private $env;
+    private $scope;
     private $parser;
-    private $sourceIndex;
-    private $sourceLine;
-    private $sourceColumn;
+    private $sourcePos;
+    private $sourceParser;
+    private $storeEnv;
+    private $charsetSeen;
     private $stderr;
     private $shouldEvaluate;
-    private $ignoreErrors;
-
-    /**
-     * Constructor
-     */
-    public function __construct()
-    {
-        $this->parsedFiles = [];
-        $this->sourceNames = [];
-    }
 
     /**
      * Compile scss
@@ -159,37 +136,32 @@ class Compiler
      * @api
      *
      * @param string $code
-     * @param string $path
+     * @param string $name
      *
      * @return string
      */
-    public function compile($code, $path = null)
+    public function compile($code, $name = null)
     {
+        $this->indentLevel  = -1;
+        $this->commentsSeen = array();
+        $this->extends      = array();
+        $this->extendsMap   = array();
+        $this->parsedFiles  = array();
+        $this->env          = null;
+        $this->scope        = null;
+
+        $this->stderr = fopen('php://stderr', 'w');
+
         $locale = setlocale(LC_NUMERIC, 0);
         setlocale(LC_NUMERIC, 'C');
 
-        $this->indentLevel    = -1;
-        $this->commentsSeen   = [];
-        $this->extends        = [];
-        $this->extendsMap     = [];
-        $this->sourceIndex    = null;
-        $this->sourceLine     = null;
-        $this->sourceColumn   = null;
-        $this->env            = null;
-        $this->scope          = null;
-        $this->storeEnv       = null;
-        $this->charsetSeen    = null;
-        $this->shouldEvaluate = null;
-        $this->stderr         = fopen('php://stderr', 'w');
-
-        $this->parser = $this->parserFactory($path);
+        $this->parser = new Parser($name);
+
         $tree = $this->parser->parse($code);
-        $this->parser = null;
 
         $this->formatter = new $this->formatter();
-        $this->rootBlock = null;
-        $this->rootEnv   = $this->pushEnv($tree);
 
+        $this->rootEnv = $this->pushEnv($tree);
         $this->injectVariables($this->registeredVars);
         $this->compileRoot($tree);
         $this->popEnv();
@@ -201,23 +173,6 @@ class Compiler
         return $out;
     }
 
-    /**
-     * Instantiate parser
-     *
-     * @param string $path
-     *
-     * @return \Leafo\ScssPhp\Parser
-     */
-    protected function parserFactory($path)
-    {
-        $parser = new Parser($path, count($this->sourceNames), $this->encoding);
-
-        $this->sourceNames[] = $path;
-        $this->addParsedFile($path);
-
-        return $parser;
-    }
-
     /**
      * Is self extend?
      *
@@ -240,24 +195,23 @@ class Compiler
     /**
      * Push extends
      *
-     * @param array     $target
-     * @param array     $origin
-     * @param \stdClass $block
+     * @param array $target
+     * @param array $origin
      */
-    protected function pushExtends($target, $origin, $block)
+    protected function pushExtends($target, $origin)
     {
         if ($this->isSelfExtend($target, $origin)) {
             return;
         }
 
         $i = count($this->extends);
-        $this->extends[] = [$target, $origin, $block];
+        $this->extends[] = array($target, $origin);
 
         foreach ($target as $part) {
             if (isset($this->extendsMap[$part])) {
                 $this->extendsMap[$part][] = $i;
             } else {
-                $this->extendsMap[$part] = [$i];
+                $this->extendsMap[$part] = array($i);
             }
         }
     }
@@ -268,17 +222,17 @@ class Compiler
      * @param string $type
      * @param array  $selectors
      *
-     * @return \Leafo\ScssPhp\Formatter\OutputBlock
+     * @return \stdClass
      */
     protected function makeOutputBlock($type, $selectors = null)
     {
-        $out = new OutputBlock;
-        $out->type      = $type;
-        $out->lines     = [];
-        $out->children  = [];
-        $out->parent    = $this->scope;
+        $out = new \stdClass;
+        $out->type = $type;
+        $out->lines = array();
+        $out->children = array();
+        $out->parent = $this->scope;
         $out->selectors = $selectors;
-        $out->depth     = $this->env->depth;
+        $out->depth = $this->env->depth;
 
         return $out;
     }
@@ -286,52 +240,26 @@ class Compiler
     /**
      * Compile root
      *
-     * @param \Leafo\ScssPhp\Block $rootBlock
+     * @param \stdClass $rootBlock
      */
-    protected function compileRoot(Block $rootBlock)
+    protected function compileRoot($rootBlock)
     {
-        $this->rootBlock = $this->scope = $this->makeOutputBlock(Type::T_ROOT);
+        $this->scope = $this->makeOutputBlock('root');
 
-        $this->compileChildrenNoReturn($rootBlock->children, $this->scope);
+        $this->compileChildren($rootBlock->children, $this->scope);
         $this->flattenSelectors($this->scope);
-        $this->missingSelectors();
-    }
-
-    /**
-     * Report missing selectors
-     */
-    protected function missingSelectors()
-    {
-        foreach ($this->extends as $extend) {
-            if (isset($extend[3])) {
-                continue;
-            }
-
-            list($target, $origin, $block) = $extend;
-
-            // ignore if !optional
-            if ($block[2]) {
-                continue;
-            }
-
-            $target = implode(' ', $target);
-            $origin = $this->collapseSelectors($origin);
-
-            $this->sourceLine = $block[Parser::SOURCE_LINE];
-            $this->throwError("\"$origin\" failed to @extend \"$target\". The selector \"$target\" was not found.");
-        }
     }
 
     /**
      * Flatten selectors
      *
-     * @param \Leafo\ScssPhp\Formatter\OutputBlock $block
-     * @param string                               $parentKey
+     * @param \stdClass $block
+     * @parent string   $parentKey
      */
-    protected function flattenSelectors(OutputBlock $block, $parentKey = null)
+    protected function flattenSelectors($block, $parentKey = null)
     {
         if ($block->selectors) {
-            $selectors = [];
+            $selectors = array();
 
             foreach ($block->selectors as $s) {
                 $selectors[] = $s;
@@ -346,18 +274,16 @@ class Compiler
 
                     // remove duplicates
                     array_walk($selectors, function (&$value) {
-                        $value = serialize($value);
+                        $value = json_encode($value);
                     });
-
                     $selectors = array_unique($selectors);
-
                     array_walk($selectors, function (&$value) {
-                        $value = unserialize($value);
+                        $value = json_decode($value);
                     });
                 }
             }
 
-            $block->selectors = [];
+            $block->selectors = array();
             $placeholderSelector = false;
 
             foreach ($selectors as $selector) {
@@ -397,42 +323,23 @@ class Compiler
             }
 
             if ($this->matchExtendsSingle($part, $origin)) {
-                $after = array_slice($selector, $i + 1);
                 $before = array_slice($selector, 0, $i);
-
-                list($before, $nonBreakableBefore) = $this->extractRelationshipFromFragment($before);
+                $after = array_slice($selector, $i + 1);
+                $s = count($before);
 
                 foreach ($origin as $new) {
                     $k = 0;
 
                     // remove shared parts
                     if ($initial) {
-                        while ($k < $i && isset($new[$k]) && $selector[$k] === $new[$k]) {
+                        while ($k < $s && isset($new[$k]) && $before[$k] === $new[$k]) {
                             $k++;
                         }
                     }
 
-                    $replacement = [];
-                    $tempReplacement = $k > 0 ? array_slice($new, $k) : $new;
-
-                    for ($l = count($tempReplacement) - 1; $l >= 0; $l--) {
-                        $slice = $tempReplacement[$l];
-                        array_unshift($replacement, $slice);
-
-                        if (! $this->isImmediateRelationshipCombinator(end($slice))) {
-                            break;
-                        }
-                    }
-
-                    $afterBefore = $l != 0 ? array_slice($tempReplacement, 0, $l) : [];
-
-                    // Merge shared direct relationships.
-                    $mergedBefore = $this->mergeDirectRelationships($afterBefore, $nonBreakableBefore);
-
                     $result = array_merge(
                         $before,
-                        $mergedBefore,
-                        $replacement,
+                        $k > 0 ? array_slice($new, $k) : $new,
                         $after
                     );
 
@@ -443,22 +350,14 @@ class Compiler
                     $out[] = $result;
 
                     // recursively check for more matches
-                    $this->matchExtends($result, $out, count($before) + count($mergedBefore), false);
+                    $this->matchExtends($result, $out, $i, false);
 
                     // selector sequence merging
                     if (! empty($before) && count($new) > 1) {
-                        $sharedParts = $k > 0 ? array_slice($before, 0, $k) : [];
-                        $postSharedParts = $k > 0 ? array_slice($before, $k) : $before;
-
-                        list($injectBetweenSharedParts, $nonBreakable2) = $this->extractRelationshipFromFragment($afterBefore);
-
                         $result2 = array_merge(
-                            $sharedParts,
-                            $injectBetweenSharedParts,
-                            $postSharedParts,
-                            $nonBreakable2,
-                            $nonBreakableBefore,
-                            $replacement,
+                            array_slice($new, 0, -1),
+                            $k > 0 ? array_slice($before, $k) : $before,
+                            array_slice($new, -1),
                             $after
                         );
 
@@ -479,8 +378,8 @@ class Compiler
      */
     protected function matchExtendsSingle($rawSingle, &$outOrigin)
     {
-        $counts = [];
-        $single = [];
+        $counts = array();
+        $single = array();
 
         foreach ($rawSingle as $part) {
             // matches Number
@@ -495,13 +394,6 @@ class Compiler
             }
         }
 
-        $extendingDecoratedTag = false;
-
-        if (count($single) > 1) {
-            $matches = null;
-            $extendingDecoratedTag = preg_match('/^[a-z0-9]+$/i', $single[0], $matches) ? $matches[0] : false;
-        }
-
         foreach ($single as $part) {
             if (isset($this->extendsMap[$part])) {
                 foreach ($this->extendsMap[$part] as $idx) {
@@ -510,19 +402,17 @@ class Compiler
             }
         }
 
-        $outOrigin = [];
+        $outOrigin = array();
         $found = false;
 
         foreach ($counts as $idx => $count) {
-            list($target, $origin, /* $block */) = $this->extends[$idx];
+            list($target, $origin) = $this->extends[$idx];
 
             // check count
             if ($count !== count($target)) {
                 continue;
             }
 
-            $this->extends[$idx][3] = true;
-
             $rem = array_diff($single, $target);
 
             foreach ($origin as $j => $new) {
@@ -531,21 +421,7 @@ class Compiler
                     return false;
                 }
 
-                $replacement = end($new);
-
-                // Extending a decorated tag with another tag is not possible.
-                if ($extendingDecoratedTag && $replacement[0] != $extendingDecoratedTag &&
-                    preg_match('/^[a-z0-9]+$/i', $replacement[0])
-                ) {
-                    unset($origin[$j]);
-                    continue;
-                }
-
-                $combined = $this->combineSelectorSingle($replacement, $rem);
-
-                if (count(array_diff($combined, $origin[$j][count($origin[$j]) - 1]))) {
-                    $origin[$j][count($origin[$j]) - 1] = $combined;
-                }
+                $origin[$j][count($origin[$j]) - 1] = $this->combineSelectorSingle(end($new), $rem);
             }
 
             $outOrigin = array_merge($outOrigin, $origin);
@@ -556,39 +432,6 @@ class Compiler
         return $found;
     }
 
-
-    /**
-     * Extract a relationship from the fragment.
-     *
-     * When extracting the last portion of a selector we will be left with a
-     * fragment which may end with a direction relationship combinator. This
-     * method will extract the relationship fragment and return it along side
-     * the rest.
-     *
-     * @param array $fragment The selector fragment maybe ending with a direction relationship combinator.
-     * @return array The selector without the relationship fragment if any, the relationship fragment.
-     */
-    protected function extractRelationshipFromFragment(array $fragment)
-    {
-        $parents = [];
-        $children = [];
-        $j = $i = count($fragment);
-
-        for (;;) {
-            $children = $j != $i ? array_slice($fragment, $j, $i - $j) : [];
-            $parents = array_slice($fragment, 0, $j);
-            $slice = end($parents);
-
-            if (empty($slice) || ! $this->isImmediateRelationshipCombinator($slice[0])) {
-                break;
-            }
-
-            $j -= 2;
-        }
-
-        return [$parents, $children];
-    }
-
     /**
      * Combine selector single
      *
@@ -599,28 +442,21 @@ class Compiler
      */
     protected function combineSelectorSingle($base, $other)
     {
-        $tag = [];
-        $out = [];
-        $wasTag = true;
+        $tag = null;
+        $out = array();
 
-        foreach ([$base, $other] as $single) {
+        foreach (array($base, $other) as $single) {
             foreach ($single as $part) {
-                if (preg_match('/^[\[.:#]/', $part)) {
-                    $out[] = $part;
-                    $wasTag = false;
-                } elseif (preg_match('/^[^_-]/', $part)) {
-                    $tag[] = $part;
-                    $wasTag = true;
-                } elseif ($wasTag) {
-                    $tag[count($tag) - 1] .= $part;
+                if (preg_match('/^[^\[.#:]/', $part)) {
+                    $tag = $part;
                 } else {
-                    $out[count($out) - 1] .= $part;
+                    $out[= $part;
                 }
             }
         }
 
-        if (count($tag)) {
-            array_unshift($out, $tag[0]);
+        if ($tag) {
+            array_unshift($out, $tag);
         }
 
         return $out;
@@ -629,16 +465,16 @@ class Compiler
     /**
      * Compile media
      *
-     * @param \Leafo\ScssPhp\Block $media
+     * @param \stdClass $media
      */
-    protected function compileMedia(Block $media)
+    protected function compileMedia($media)
     {
         $this->pushEnv($media);
 
         $mediaQuery = $this->compileMediaQuery($this->multiplyMedia($this->env));
 
         if (! empty($mediaQuery)) {
-            $this->scope = $this->makeOutputBlock(Type::T_MEDIA, [$mediaQuery]);
+            $this->scope = $this->makeOutputBlock('media', array($mediaQuery));
 
             $parentScope = $this->mediaParent($this->scope);
             $parentScope->children[] = $this->scope;
@@ -649,30 +485,21 @@ class Compiler
             foreach ($media->children as $child) {
                 $type = $child[0];
 
-                if ($type !== Type::T_BLOCK &&
-                    $type !== Type::T_MEDIA &&
-                    $type !== Type::T_DIRECTIVE &&
-                    $type !== Type::T_IMPORT
-                ) {
+                if ($type !== 'block' && $type !== 'media' && $type !== 'directive') {
                     $needsWrap = true;
                     break;
                 }
             }
 
             if ($needsWrap) {
-                $wrapped = new Block;
-                $wrapped->sourceIndex  = $media->sourceIndex;
-                $wrapped->sourceLine   = $media->sourceLine;
-                $wrapped->sourceColumn = $media->sourceColumn;
-                $wrapped->selectors    = [];
-                $wrapped->comments     = [];
-                $wrapped->parent       = $media;
-                $wrapped->children     = $media->children;
-
-                $media->children = [[Type::T_BLOCK, $wrapped]];
+                $wrapped = (object)array(
+                    'selectors' => array(),
+                    'children' => $media->children
+                );
+                $media->children = array(array('block', $wrapped));
             }
 
-            $this->compileChildrenNoReturn($media->children, $this->scope);
+            $this->compileChildren($media->children, $this->scope);
 
             $this->scope = $this->scope->parent;
         }
@@ -683,14 +510,14 @@ class Compiler
     /**
      * Media parent
      *
-     * @param \Leafo\ScssPhp\Formatter\OutputBlock $scope
+     * @param \stdClass $scope
      *
-     * @return \Leafo\ScssPhp\Formatter\OutputBlock
+     * @return \stdClass
      */
-    protected function mediaParent(OutputBlock $scope)
+    protected function mediaParent($scope)
     {
         while (! empty($scope->parent)) {
-            if (! empty($scope->type) && $scope->type !== Type::T_MEDIA) {
+            if (! empty($scope->type) && $scope->type !== 'media') {
                 break;
             }
 
@@ -700,281 +527,22 @@ class Compiler
         return $scope;
     }
 
-    /**
-     * Compile directive
-     *
-     * @param \Leafo\ScssPhp\Block $block
-     */
-    protected function compileDirective(Block $block)
-    {
-        $s = '@' . $block->name;
-
-        if (! empty($block->value)) {
-            $s .= ' ' . $this->compileValue($block->value);
-        }
-
-        if ($block->name === 'keyframes' || substr($block->name, -10) === '-keyframes') {
-            $this->compileKeyframeBlock($block, [$s]);
-        } else {
-            $this->compileNestedBlock($block, [$s]);
-        }
-    }
-
-    /**
-     * Compile at-root
-     *
-     * @param \Leafo\ScssPhp\Block $block
-     */
-    protected function compileAtRoot(Block $block)
-    {
-        $env     = $this->pushEnv($block);
-        $envs    = $this->compactEnv($env);
-        $without = isset($block->with) ? $this->compileWith($block->with) : self::WITH_RULE;
-
-        // wrap inline selector
-        if ($block->selector) {
-            $wrapped = new Block;
-            $wrapped->sourceIndex  = $block->sourceIndex;
-            $wrapped->sourceLine   = $block->sourceLine;
-            $wrapped->sourceColumn = $block->sourceColumn;
-            $wrapped->selectors    = $block->selector;
-            $wrapped->comments     = [];
-            $wrapped->parent       = $block;
-            $wrapped->children     = $block->children;
-
-            $block->children = [[Type::T_BLOCK, $wrapped]];
-        }
-
-        $this->env = $this->filterWithout($envs, $without);
-        $newBlock  = $this->spliceTree($envs, $block, $without);
-
-        $saveScope   = $this->scope;
-        $this->scope = $this->rootBlock;
-
-        $this->compileChild($newBlock, $this->scope);
-
-        $this->scope = $saveScope;
-        $this->env   = $this->extractEnv($envs);
-
-        $this->popEnv();
-    }
-
-    /**
-     * Splice parse tree
-     *
-     * @param array                $envs
-     * @param \Leafo\ScssPhp\Block $block
-     * @param integer              $without
-     *
-     * @return array
-     */
-    private function spliceTree($envs, Block $block, $without)
-    {
-        $newBlock = null;
-
-        foreach ($envs as $e) {
-            if (! isset($e->block)) {
-                continue;
-            }
-
-            if ($e->block === $block) {
-                continue;
-            }
-
-            if (isset($e->block->type) && $e->block->type === Type::T_AT_ROOT) {
-                continue;
-            }
-
-            if ($e->block && $this->isWithout($without, $e->block)) {
-                continue;
-            }
-
-            $b = new Block;
-            $b->sourceIndex  = $e->block->sourceIndex;
-            $b->sourceLine   = $e->block->sourceLine;
-            $b->sourceColumn = $e->block->sourceColumn;
-            $b->selectors    = [];
-            $b->comments     = $e->block->comments;
-            $b->parent       = null;
-
-            if ($newBlock) {
-                $type = isset($newBlock->type) ? $newBlock->type : Type::T_BLOCK;
-
-                $b->children = [[$type, $newBlock]];
-
-                $newBlock->parent = $b;
-            } elseif (count($block->children)) {
-                foreach ($block->children as $child) {
-                    if ($child[0] === Type::T_BLOCK) {
-                        $child[1]->parent = $b;
-                    }
-                }
-
-                $b->children = $block->children;
-            }
-
-            if (isset($e->block->type)) {
-                $b->type = $e->block->type;
-            }
-
-            if (isset($e->block->name)) {
-                $b->name = $e->block->name;
-            }
-
-            if (isset($e->block->queryList)) {
-                $b->queryList = $e->block->queryList;
-            }
-
-            if (isset($e->block->value)) {
-                $b->value = $e->block->value;
-            }
-
-            $newBlock = $b;
-        }
-
-        $type = isset($newBlock->type) ? $newBlock->type : Type::T_BLOCK;
-
-        return [$type, $newBlock];
-    }
-
-    /**
-     * Compile @at-root's with: inclusion / without: exclusion into filter flags
-     *
-     * @param array $with
-     *
-     * @return integer
-     */
-    private function compileWith($with)
-    {
-        static $mapping = [
-            'rule'     => self::WITH_RULE,
-            'media'    => self::WITH_MEDIA,
-            'supports' => self::WITH_SUPPORTS,
-            'all'      => self::WITH_ALL,
-        ];
-
-        // exclude selectors by default
-        $without = self::WITH_RULE;
-
-        if ($this->libMapHasKey([$with, self::$with])) {
-            $without = self::WITH_ALL;
-
-            $list = $this->coerceList($this->libMapGet([$with, self::$with]));
-
-            foreach ($list[2] as $item) {
-                $keyword = $this->compileStringContent($this->coerceString($item));
-
-                if (array_key_exists($keyword, $mapping)) {
-                    $without &= ~($mapping[$keyword]);
-                }
-            }
-        }
-
-        if ($this->libMapHasKey([$with, self::$without])) {
-            $without = 0;
-
-            $list = $this->coerceList($this->libMapGet([$with, self::$without]));
-
-            foreach ($list[2] as $item) {
-                $keyword = $this->compileStringContent($this->coerceString($item));
-
-                if (array_key_exists($keyword, $mapping)) {
-                    $without |= $mapping[$keyword];
-                }
-            }
-        }
-
-        return $without;
-    }
-
-    /**
-     * Filter env stack
-     *
-     * @param array   $envs
-     * @param integer $without
-     *
-     * @return \Leafo\ScssPhp\Compiler\Environment
-     */
-    private function filterWithout($envs, $without)
-    {
-        $filtered = [];
-
-        foreach ($envs as $e) {
-            if ($e->block && $this->isWithout($without, $e->block)) {
-                continue;
-            }
-
-            $filtered[] = $e;
-        }
-
-        return $this->extractEnv($filtered);
-    }
-
-    /**
-     * Filter WITH rules
-     *
-     * @param integer              $without
-     * @param \Leafo\ScssPhp\Block $block
-     *
-     * @return boolean
-     */
-    private function isWithout($without, Block $block)
-    {
-        if ((($without & self::WITH_RULE) && isset($block->selectors)) ||
-            (($without & self::WITH_MEDIA) &&
-                isset($block->type) && $block->type === Type::T_MEDIA) ||
-            (($without & self::WITH_SUPPORTS) &&
-                isset($block->type) && $block->type === Type::T_DIRECTIVE &&
-                isset($block->name) && $block->name === 'supports')
-        ) {
-            return true;
-        }
-
-        return false;
-    }
-
-    /**
-     * Compile keyframe block
-     *
-     * @param \Leafo\ScssPhp\Block $block
-     * @param array                $selectors
-     */
-    protected function compileKeyframeBlock(Block $block, $selectors)
-    {
-        $env = $this->pushEnv($block);
-
-        $envs = $this->compactEnv($env);
-
-        $this->env = $this->extractEnv(array_filter($envs, function (Environment $e) {
-            return ! isset($e->block->selectors);
-        }));
-
-        $this->scope = $this->makeOutputBlock($block->type, $selectors);
-        $this->scope->depth = 1;
-        $this->scope->parent->children[] = $this->scope;
-
-        $this->compileChildrenNoReturn($block->children, $this->scope);
-
-        $this->scope = $this->scope->parent;
-        $this->env   = $this->extractEnv($envs);
-
-        $this->popEnv();
-    }
-
     /**
      * Compile nested block
      *
-     * @param \Leafo\ScssPhp\Block $block
-     * @param array                $selectors
+     * @todo refactor compileNestedBlock and compileMedia into same thing?
+     *
+     * @param \stdClass $block
+     * @param array     $selectors
      */
-    protected function compileNestedBlock(Block $block, $selectors)
+    protected function compileNestedBlock($block, $selectors)
     {
         $this->pushEnv($block);
 
         $this->scope = $this->makeOutputBlock($block->type, $selectors);
         $this->scope->parent->children[] = $this->scope;
 
-        $this->compileChildrenNoReturn($block->children, $this->scope);
+        $this->compileChildren($block->children, $this->scope);
 
         $this->scope = $this->scope->parent;
 
@@ -997,21 +565,22 @@ class Compiler
      *
      * @see Compiler::compileChild()
      *
-     * @param \Leafo\ScssPhp\Block $block
+     * @param \stdClass $block
      */
-    protected function compileBlock(Block $block)
+    protected function compileBlock($block)
     {
         $env = $this->pushEnv($block);
+
         $env->selectors = $this->evalSelectors($block->selectors);
 
-        $out = $this->makeOutputBlock(null);
+        $out = $this->makeOutputBlock(null, $this->multiplySelectors($env));
 
         if (isset($this->lineNumberStyle) && count($env->selectors) && count($block->children)) {
-            $annotation = $this->makeOutputBlock(Type::T_COMMENT);
+            $annotation = $this->makeOutputBlock('comment');
             $annotation->depth = 0;
 
-            $file = $this->sourceNames[$block->sourceIndex];
-            $line = $block->sourceLine;
+            $file = $block->sourceParser->getSourceName();
+            $line = $block->sourceParser->getLineNo($block->sourcePosition);
 
             switch ($this->lineNumberStyle) {
                 case self::LINE_COMMENTS:
@@ -1029,11 +598,7 @@ class Compiler
 
         $this->scope->children[] = $out;
 
-        if (count($block->children)) {
-            $out->selectors = $this->multiplySelectors($env);
-
-            $this->compileChildrenNoReturn($block->children, $out);
-        }
+        $this->compileChildren($block->children, $out);
 
         $this->formatter->stripSemicolon($out->lines);
 
@@ -1047,7 +612,7 @@ class Compiler
      */
     protected function compileComment($block)
     {
-        $out = $this->makeOutputBlock(Type::T_COMMENT);
+        $out = $this->makeOutputBlock('comment');
         $out->lines[] = $block[1];
         $this->scope->children[] = $out;
     }
@@ -1063,15 +628,15 @@ class Compiler
     {
         $this->shouldEvaluate = false;
 
-        $selectors = array_map([$this, 'evalSelector'], $selectors);
+        $selectors = array_map(array($this, 'evalSelector'), $selectors);
 
         // after evaluating interpolates, we might need a second pass
         if ($this->shouldEvaluate) {
             $buffer = $this->collapseSelectors($selectors);
-            $parser = $this->parserFactory(__METHOD__);
+            $parser = new Parser(__METHOD__, false);
 
             if ($parser->parseSelector($buffer, $newSelectors)) {
-                $selectors = array_map([$this, 'evalSelector'], $newSelectors);
+                $selectors = array_map(array($this, 'evalSelector'), $newSelectors);
             }
         }
 
@@ -1087,7 +652,7 @@ class Compiler
      */
     protected function evalSelector($selector)
     {
-        return array_map([$this, 'evalSelectorPart'], $selector);
+        return array_map(array($this, 'evalSelectorPart'), $selector);
     }
 
     /**
@@ -1100,7 +665,7 @@ class Compiler
     protected function evalSelectorPart($part)
     {
         foreach ($part as &$p) {
-            if (is_array($p) && ($p[0] === Type::T_INTERPOLATE || $p[0] === Type::T_STRING)) {
+            if (is_array($p) && ($p[0] === 'interpolate' || $p[0] === 'string')) {
                 $p = $this->compileValue($p);
 
                 // force re-evaluation
@@ -1127,7 +692,7 @@ class Compiler
      */
     protected function collapseSelectors($selectors)
     {
-        $parts = [];
+        $parts = array();
 
         foreach ($selectors as $selector) {
             $output = '';
@@ -1154,7 +719,7 @@ class Compiler
      */
     protected function flattenSelectorSingle($single)
     {
-        $joined = [];
+        $joined = array();
 
         foreach ($single as $part) {
             if (empty($joined) ||
@@ -1191,7 +756,7 @@ class Compiler
         return implode(
             ' ',
             array_map(
-                [$this, 'compileSelectorPart'],
+                array($this, 'compileSelectorPart'),
                 $selector
             )
         );
@@ -1212,7 +777,7 @@ class Compiler
             }
 
             switch ($p[0]) {
-                case Type::T_SELF:
+                case 'self':
                     $p = '&';
                     break;
 
@@ -1240,7 +805,7 @@ class Compiler
 
         foreach ($selector as $parts) {
             foreach ($parts as $part) {
-                if (strlen($part) && '%' === $part[0]) {
+                if ('%' === $part[0]) {
                     return true;
                 }
             }
@@ -1250,14 +815,14 @@ class Compiler
     }
 
     /**
-     * Compile children and return result
+     * Compile children
      *
-     * @param array                                $stms
-     * @param \Leafo\ScssPhp\Formatter\OutputBlock $out
+     * @param array $stms
+     * @param array $out
      *
      * @return array
      */
-    protected function compileChildren($stms, OutputBlock $out)
+    protected function compileChildren($stms, $out)
     {
         foreach ($stms as $stm) {
             $ret = $this->compileChild($stm, $out);
@@ -1268,27 +833,6 @@ class Compiler
         }
     }
 
-    /**
-     * Compile children and throw exception if unexpected @return
-     *
-     * @param array                                $stms
-     * @param \Leafo\ScssPhp\Formatter\OutputBlock $out
-     *
-     * @throws \Exception
-     */
-    protected function compileChildrenNoReturn($stms, OutputBlock $out)
-    {
-        foreach ($stms as $stm) {
-            $ret = $this->compileChild($stm, $out);
-
-            if (isset($ret)) {
-                $this->throwError('@return may only be used within a function');
-
-                return;
-            }
-        }
-    }
-
     /**
      * Compile media query
      *
@@ -1303,26 +847,26 @@ class Compiler
 
         foreach ($queryList as $query) {
             $type = null;
-            $parts = [];
+            $parts = array();
 
             foreach ($query as $q) {
                 switch ($q[0]) {
-                    case Type::T_MEDIA_TYPE:
+                    case 'mediaType':
                         if ($type) {
                             $type = $this->mergeMediaTypes(
                                 $type,
-                                array_map([$this, 'compileValue'], array_slice($q, 1))
+                                array_map(array($this, 'compileValue'), array_slice($q, 1))
                             );
 
                             if (empty($type)) { // merge failed
                                 return null;
                             }
                         } else {
-                            $type = array_map([$this, 'compileValue'], array_slice($q, 1));
+                            $type = array_map(array($this, 'compileValue'), array_slice($q, 1));
                         }
                         break;
 
-                    case Type::T_MEDIA_EXPRESSION:
+                    case 'mediaExp':
                         if (isset($q[2])) {
                             $parts[] = '('
                                 . $this->compileValue($q[1])
@@ -1335,10 +879,6 @@ class Compiler
                                 . ')';
                         }
                         break;
-
-                    case Type::T_MEDIA_VALUE:
-                        $parts[] = $this->compileValue($q[1]);
-                        break;
                 }
             }
 
@@ -1361,37 +901,6 @@ class Compiler
         return $out;
     }
 
-    protected function mergeDirectRelationships($selectors1, $selectors2)
-    {
-        if (empty($selectors1) || empty($selectors2)) {
-            return array_merge($selectors1, $selectors2);
-        }
-
-        $part1 = end($selectors1);
-        $part2 = end($selectors2);
-
-        if (! $this->isImmediateRelationshipCombinator($part1[0]) || $part1 !== $part2) {
-            return array_merge($selectors1, $selectors2);
-        }
-
-        $merged = [];
-
-        do {
-            $part1 = array_pop($selectors1);
-            $part2 = array_pop($selectors2);
-
-            if ($this->isImmediateRelationshipCombinator($part1[0]) && $part1 !== $part2) {
-                $merged = array_merge($selectors1, [$part1], $selectors2, [$part2], $merged);
-                break;
-            }
-
-            array_unshift($merged, $part1);
-            array_unshift($merged, [array_pop($selectors1)[0] . array_pop($selectors2)[0]]);
-        } while (! empty($selectors1) && ! empty($selectors2));
-
-        return $merged;
-    }
-
     /**
      * Merge media types
      *
@@ -1430,24 +939,24 @@ class Compiler
             $t2 = strtolower($type2[0]);
         }
 
-        if (($m1 === Type::T_NOT) ^ ($m2 === Type::T_NOT)) {
+        if (($m1 === 'not') ^ ($m2 === 'not')) {
             if ($t1 === $t2) {
                 return null;
             }
 
-            return [
-                $m1 === Type::T_NOT ? $m2 : $m1,
-                $m1 === Type::T_NOT ? $t2 : $t1,
-            ];
+            return array(
+                $m1 === 'not' ? $m2 : $m1,
+                $m1 === 'not' ? $t2 : $t1
+            );
         }
 
-        if ($m1 === Type::T_NOT && $m2 === Type::T_NOT) {
+        if ($m1 === 'not' && $m2 === 'not') {
             // CSS has no way of representing "neither screen nor print"
             if ($t1 !== $t2) {
                 return null;
             }
 
-            return [Type::T_NOT, $t1];
+            return array('not', $t1);
         }
 
         if ($t1 !== $t2) {
@@ -1455,28 +964,24 @@ class Compiler
         }
 
         // t1 == t2, neither m1 nor m2 are "not"
-        return [empty($m1)? $m2 : $m1, $t1];
+        return array(empty($m1)? $m2 : $m1, $t1);
     }
 
     /**
      * Compile import; returns true if the value was something that could be imported
      *
-     * @param array   $rawPath
-     * @param array   $out
-     * @param boolean $once
+     * @param array $rawPath
+     * @param array $out
      *
      * @return boolean
      */
-    protected function compileImport($rawPath, $out, $once = false)
+    protected function compileImport($rawPath, $out)
     {
-        if ($rawPath[0] === Type::T_STRING) {
+        if ($rawPath[0] === 'string') {
             $path = $this->compileStringContent($rawPath);
 
             if ($path = $this->findImport($path)) {
-                if (! $once || ! in_array($path, $this->importedFiles)) {
-                    $this->importFile($path, $out);
-                    $this->importedFiles[] = $path;
-                }
+                $this->importFile($path, $out);
 
                 return true;
             }
@@ -1484,14 +989,14 @@ class Compiler
             return false;
         }
 
-        if ($rawPath[0] === Type::T_LIST) {
+        if ($rawPath[0] === 'list') {
             // handle a list of strings
             if (count($rawPath[2]) === 0) {
                 return false;
             }
 
             foreach ($rawPath[2] as $path) {
-                if ($path[0] !== Type::T_STRING) {
+                if ($path[0] !== 'string') {
                     return false;
                 }
             }
@@ -1509,55 +1014,48 @@ class Compiler
     /**
      * Compile child; returns a value to halt execution
      *
-     * @param array                                $child
-     * @param \Leafo\ScssPhp\Formatter\OutputBlock $out
+     * @param array     $child
+     * @param \stdClass $out
      *
      * @return array
      */
-    protected function compileChild($child, OutputBlock $out)
+    protected function compileChild($child, $out)
     {
-        $this->sourceIndex  = isset($child[Parser::SOURCE_INDEX]) ? $child[Parser::SOURCE_INDEX] : null;
-        $this->sourceLine   = isset($child[Parser::SOURCE_LINE]) ? $child[Parser::SOURCE_LINE] : -1;
-        $this->sourceColumn = isset($child[Parser::SOURCE_COLUMN]) ? $child[Parser::SOURCE_COLUMN] : -1;
+        $this->sourcePos = isset($child[Parser::SOURCE_POSITION]) ? $child[Parser::SOURCE_POSITION] : -1;
+        $this->sourceParser = isset($child[Parser::SOURCE_PARSER]) ? $child[Parser::SOURCE_PARSER] : $this->parser;
 
         switch ($child[0]) {
-            case Type::T_SCSSPHP_IMPORT_ONCE:
+            case 'import':
                 list(, $rawPath) = $child;
 
                 $rawPath = $this->reduce($rawPath);
 
-                if (! $this->compileImport($rawPath, $out, true)) {
+                if (! $this->compileImport($rawPath, $out)) {
                     $out->lines[] = '@import ' . $this->compileValue($rawPath) . ';';
                 }
                 break;
 
-            case Type::T_IMPORT:
-                list(, $rawPath) = $child;
+            case 'directive':
+                list(, $directive) = $child;
 
-                $rawPath = $this->reduce($rawPath);
+                $s = '@' . $directive->name;
 
-                if (! $this->compileImport($rawPath, $out)) {
-                    $out->lines[] = '@import ' . $this->compileValue($rawPath) . ';';
+                if (! empty($directive->value)) {
+                    $s .= ' ' . $this->compileValue($directive->value);
                 }
-                break;
-
-            case Type::T_DIRECTIVE:
-                $this->compileDirective($child[1]);
-                break;
 
-            case Type::T_AT_ROOT:
-                $this->compileAtRoot($child[1]);
+                $this->compileNestedBlock($directive, array($s));
                 break;
 
-            case Type::T_MEDIA:
+            case 'media':
                 $this->compileMedia($child[1]);
                 break;
 
-            case Type::T_BLOCK:
+            case 'block':
                 $this->compileBlock($child[1]);
                 break;
 
-            case Type::T_CHARSET:
+            case 'charset':
                 if (! $this->charsetSeen) {
                     $this->charsetSeen = true;
 
@@ -1565,13 +1063,13 @@ class Compiler
                 }
                 break;
 
-            case Type::T_ASSIGN:
+            case 'assign':
                 list(, $name, $value) = $child;
 
-                if ($name[0] === Type::T_VARIABLE) {
-                    $flags = isset($child[3]) ? $child[3] : [];
-                    $isDefault = in_array('!default', $flags);
-                    $isGlobal = in_array('!global', $flags);
+                if ($name[0] === 'var') {
+                    $flag = isset($child[3]) ? $child[3] : null;
+                    $isDefault = $flag === '!default';
+                    $isGlobal = $flag === '!global';
 
                     if ($isGlobal) {
                         $this->set($name[1], $this->reduce($value), false, $this->rootEnv);
@@ -1592,11 +1090,11 @@ class Compiler
 
                 // handle shorthand syntax: size / line-height
                 if ($compiledName === 'font') {
-                    if ($value[0] === Type::T_EXPRESSION && $value[1] === '/') {
+                    if ($value[0] === 'exp' && $value[1] === '/') {
                         $value = $this->expToString($value);
-                    } elseif ($value[0] === Type::T_LIST) {
+                    } elseif ($value[0] === 'list') {
                         foreach ($value[2] as &$item) {
-                            if ($item[0] === Type::T_EXPRESSION && $item[1] === '/') {
+                            if ($item[0] === 'exp' && $item[1] === '/') {
                                 $item = $this->expToString($item);
                             }
                         }
@@ -1605,10 +1103,10 @@ class Compiler
 
                 // if the value reduces to null from something else then
                 // the property should be discarded
-                if ($value[0] !== Type::T_NULL) {
+                if ($value[0] !== 'null') {
                     $value = $this->reduce($value);
 
-                    if ($value[0] === Type::T_NULL || $value === self::$nullString) {
+                    if ($value[0] === 'null') {
                         break;
                     }
                 }
@@ -1621,8 +1119,8 @@ class Compiler
                 );
                 break;
 
-            case Type::T_COMMENT:
-                if ($out->type === Type::T_ROOT) {
+            case 'comment':
+                if ($out->type === 'root') {
                     $this->compileComment($child);
                     break;
                 }
@@ -1630,29 +1128,26 @@ class Compiler
                 $out->lines[] = $child[1];
                 break;
 
-            case Type::T_MIXIN:
-            case Type::T_FUNCTION:
+            case 'mixin':
+            case 'function':
                 list(, $block) = $child;
 
                 $this->set(self::$namespaces[$block->type] . $block->name, $block);
                 break;
 
-            case Type::T_EXTEND:
+            case 'extend':
                 list(, $selectors) = $child;
 
                 foreach ($selectors as $sel) {
-                    $results = $this->evalSelectors([$sel]);
+                    // only use the first one
+                    $result = $this->evalSelectors(array($sel));
+                    $result = current($result[0]);
 
-                    foreach ($results as $result) {
-                        // only use the first one
-                        $result = current($result);
-
-                        $this->pushExtends($result, $out->selectors, $child);
-                    }
+                    $this->pushExtends($result, $out->selectors);
                 }
                 break;
 
-            case Type::T_IF:
+            case 'if':
                 list(, $if) = $child;
 
                 if ($this->isTruthy($this->reduce($if->cond, true))) {
@@ -1660,15 +1155,18 @@ class Compiler
                 }
 
                 foreach ($if->cases as $case) {
-                    if ($case->type === Type::T_ELSE ||
-                        $case->type === Type::T_ELSEIF && $this->isTruthy($this->reduce($case->cond))
+                    if ($case->type === 'else' ||
+                        $case->type === 'elseif' && $this->isTruthy($this->reduce($case->cond))
                     ) {
                         return $this->compileChildren($case->children, $out);
                     }
                 }
                 break;
 
-            case Type::T_EACH:
+            case 'return':
+                return $this->reduce($child[1], true);
+
+            case 'each':
                 list(, $each) = $child;
 
                 $list = $this->coerceList($this->reduce($each->list));
@@ -1689,40 +1187,28 @@ class Compiler
                     $ret = $this->compileChildren($each->children, $out);
 
                     if ($ret) {
-                        if ($ret[0] !== Type::T_CONTROL) {
-                            $this->popEnv();
-
-                            return $ret;
-                        }
+                        $this->popEnv();
 
-                        if ($ret[1]) {
-                            break;
-                        }
+                        return $ret;
                     }
                 }
 
                 $this->popEnv();
                 break;
 
-            case Type::T_WHILE:
+            case 'while':
                 list(, $while) = $child;
 
                 while ($this->isTruthy($this->reduce($while->cond, true))) {
                     $ret = $this->compileChildren($while->children, $out);
 
                     if ($ret) {
-                        if ($ret[0] !== Type::T_CONTROL) {
-                            return $ret;
-                        }
-
-                        if ($ret[1]) {
-                            break;
-                        }
+                        return $ret;
                     }
                 }
                 break;
 
-            case Type::T_FOR:
+            case 'for':
                 list(, $for) = $child;
 
                 $start = $this->reduce($for->start, true);
@@ -1731,63 +1217,46 @@ class Compiler
                 $end = $end[1];
                 $d = $start < $end ? 1 : -1;
 
-                for (;;) {
+                while (true) {
                     if ((! $for->until && $start - $d == $end) ||
                         ($for->until && $start == $end)
                     ) {
                         break;
                     }
 
-                    $this->set($for->var, new Node\Number($start, ''));
+                    $this->set($for->var, array('number', $start, ''));
                     $start += $d;
 
                     $ret = $this->compileChildren($for->children, $out);
 
                     if ($ret) {
-                        if ($ret[0] !== Type::T_CONTROL) {
-                            return $ret;
-                        }
-
-                        if ($ret[1]) {
-                            break;
-                        }
+                        return $ret;
                     }
                 }
                 break;
 
-            case Type::T_BREAK:
-                return [Type::T_CONTROL, true];
-
-            case Type::T_CONTINUE:
-                return [Type::T_CONTROL, false];
-
-            case Type::T_RETURN:
-                return $this->reduce($child[1], true);
-
-            case Type::T_NESTED_PROPERTY:
+            case 'nestedprop':
                 list(, $prop) = $child;
 
-                $prefixed = [];
+                $prefixed = array();
                 $prefix = $this->compileValue($prop->prefix) . '-';
 
                 foreach ($prop->children as $child) {
-                    switch ($child[0]) {
-                        case Type::T_ASSIGN:
-                            array_unshift($child[1][2], $prefix);
-                            break;
-
-                        case Type::T_NESTED_PROPERTY:
-                            array_unshift($child[1]->prefix[2], $prefix);
-                            break;
+                    if ($child[0] === 'assign') {
+                        array_unshift($child[1][2], $prefix);
+                    }
+
+                    if ($child[0] === 'nestedprop') {
+                        array_unshift($child[1]->prefix[2], $prefix);
                     }
 
                     $prefixed[] = $child;
                 }
 
-                $this->compileChildrenNoReturn($prefixed, $out);
+                $this->compileChildren($prefixed, $out);
                 break;
 
-            case Type::T_INCLUDE:
+            case 'include':
                 // including a mixin
                 list(, $name, $argValues, $content) = $child;
 
@@ -1795,22 +1264,18 @@ class Compiler
 
                 if (! $mixin) {
                     $this->throwError("Undefined mixin $name");
-                    break;
                 }
 
-                $callingScope = $this->getStoreEnv();
+                $callingScope = $this->env;
 
                 // push scope, apply args
                 $this->pushEnv();
                 $this->env->depth--;
 
-                $storeEnv = $this->storeEnv;
-                $this->storeEnv = $this->env;
-
                 if (isset($content)) {
                     $content->scope = $callingScope;
 
-                    $this->setRaw(self::$namespaces['special'] . 'content', $content, $this->env);
+                    $this->setRaw(self::$namespaces['special'] . 'content', $content);
                 }
 
                 if (isset($mixin->args)) {
@@ -1819,60 +1284,58 @@ class Compiler
 
                 $this->env->marker = 'mixin';
 
-                $this->compileChildrenNoReturn($mixin->children, $out);
-
-                $this->storeEnv = $storeEnv;
+                foreach ($mixin->children as $child) {
+                    $this->compileChild($child, $out);
+                }
 
                 $this->popEnv();
                 break;
 
-            case Type::T_MIXIN_CONTENT:
-                $content = $this->get(self::$namespaces['special'] . 'content', false, $this->getStoreEnv())
-                         ?: $this->get(self::$namespaces['special'] . 'content', false, $this->env);
+            case 'mixin_content':
+                $content = $this->get(self::$namespaces['special'] . 'content', false);
 
                 if (! $content) {
-                    $content = new \stdClass();
-                    $content->scope = new \stdClass();
-                    $content->children = $this->storeEnv->parent->block->children;
+                    $this->throwError('Expected @content inside of mixin');
+                }
+
+                if (! isset($content->children)) {
                     break;
                 }
 
-                $storeEnv = $this->storeEnv;
                 $this->storeEnv = $content->scope;
 
-                $this->compileChildrenNoReturn($content->children, $out);
+                foreach ($content->children as $child) {
+                    $this->compileChild($child, $out);
+                }
+
+                $this->storeEnv = null;
 
-                $this->storeEnv = $storeEnv;
                 break;
 
-            case Type::T_DEBUG:
+            case 'debug':
                 list(, $value) = $child;
 
-                $line = $this->sourceLine;
+                $line = $this->parser->getLineNo($this->sourcePos);
                 $value = $this->compileValue($this->reduce($value, true));
                 fwrite($this->stderr, "Line $line DEBUG: $value\n");
                 break;
 
-            case Type::T_WARN:
+            case 'warn':
                 list(, $value) = $child;
 
-                $line = $this->sourceLine;
+                $line = $this->parser->getLineNo($this->sourcePos);
                 $value = $this->compileValue($this->reduce($value, true));
-                fwrite($this->stderr, "Line $line WARN: $value\n");
+                echo "Line $line WARN: $value\n";
                 break;
 
-            case Type::T_ERROR:
+            case 'error':
                 list(, $value) = $child;
 
-                $line = $this->sourceLine;
+                $line = $this->parser->getLineNo($this->sourcePos);
                 $value = $this->compileValue($this->reduce($value, true));
                 $this->throwError("Line $line ERROR: $value\n");
                 break;
 
-            case Type::T_CONTROL:
-                $this->throwError('@break/@continue not permitted in this scope');
-                break;
-
             default:
                 $this->throwError("unknown child type: $child[0]");
         }
@@ -1887,9 +1350,9 @@ class Compiler
      */
     protected function expToString($exp)
     {
-        list(, $op, $left, $right, /* $inParens */, $whiteLeft, $whiteRight) = $exp;
+        list(, $op, $left, $right, $inParens, $whiteLeft, $whiteRight) = $exp;
 
-        $content = [$this->reduce($left)];
+        $content = array($this->reduce($left));
 
         if ($whiteLeft) {
             $content[] = ' ';
@@ -1903,7 +1366,7 @@ class Compiler
 
         $content[] = $this->reduce($right);
 
-        return [Type::T_STRING, '', $content];
+        return array('string', '', $content);
     }
 
     /**
@@ -1918,18 +1381,6 @@ class Compiler
         return $value !== self::$false && $value !== self::$null;
     }
 
-    /**
-     * Is the value a direct relationship combinator?
-     *
-     * @param string $value
-     *
-     * @return bool
-     */
-    protected function isImmediateRelationshipCombinator($value)
-    {
-        return $value === '>' || $value === '+' || $value === '~';
-    }
-
     /**
      * Should $value cause its operand to eval
      *
@@ -1940,14 +1391,14 @@ class Compiler
     protected function shouldEval($value)
     {
         switch ($value[0]) {
-            case Type::T_EXPRESSION:
+            case 'exp':
                 if ($value[1] === '/') {
                     return $this->shouldEval($value[2], $value[3]);
                 }
 
                 // fall-thru
-            case Type::T_VARIABLE:
-            case Type::T_FUNCTION_CALL:
+            case 'var':
+            case 'fncall':
                 return true;
         }
 
@@ -1967,23 +1418,17 @@ class Compiler
         list($type) = $value;
 
         switch ($type) {
-            case Type::T_EXPRESSION:
+            case 'exp':
                 list(, $op, $left, $right, $inParens) = $value;
 
                 $opName = isset(self::$operatorNames[$op]) ? self::$operatorNames[$op] : $op;
                 $inExp = $inExp || $this->shouldEval($left) || $this->shouldEval($right);
 
                 $left = $this->reduce($left, true);
+                $right = $this->reduce($right, true);
 
-                if ($op !== 'and' && $op !== 'or') {
-                    $right = $this->reduce($right, true);
-                }
-
-                // special case: looks like css shorthand
-                if ($opName == 'div' && ! $inParens && ! $inExp && isset($right[2])
-                    && (($right[0] !== Type::T_NUMBER && $right[2] != '')
-                    || ($right[0] === Type::T_NUMBER && ! $right->unitless()))
-                ) {
+                // special case: looks like css short-hand
+                if ($opName === 'div' && ! $inParens && ! $inExp && isset($right[2]) && $right[2] !== '') {
                     return $this->expToString($value);
                 }
 
@@ -2003,49 +1448,47 @@ class Compiler
                 // 3. op[op name]
                 $fn = "op${ucOpName}${ucLType}${ucRType}";
 
-                if (is_callable([$this, $fn]) ||
+                if (is_callable(array($this, $fn)) ||
                     (($fn = "op${ucLType}${ucRType}") &&
-                        is_callable([$this, $fn]) &&
+                        is_callable(array($this, $fn)) &&
                         $passOp = true) ||
                     (($fn = "op${ucOpName}") &&
-                        is_callable([$this, $fn]) &&
+                        is_callable(array($this, $fn)) &&
                         $genOp = true)
                 ) {
-                    $coerceUnit = false;
+                    $unitChange = false;
 
                     if (! isset($genOp) &&
-                        $left[0] === Type::T_NUMBER && $right[0] === Type::T_NUMBER
+                        $left[0] === 'number' && $right[0] === 'number'
                     ) {
-                        $coerceUnit = true;
-
-                        switch ($opName) {
-                            case 'mul':
-                                $targetUnit = $left[2];
-
-                                foreach ($right[2] as $unit => $exp) {
-                                    $targetUnit[$unit] = (isset($targetUnit[$unit]) ? $targetUnit[$unit] : 0) + $exp;
-                                }
-                                break;
+                        if ($opName === 'mod' && $right[2] !== '') {
+                            $this->throwError("Cannot modulo by a number with units: $right[1]$right[2].");
+                        }
 
-                            case 'div':
-                                $targetUnit = $left[2];
+                        $unitChange = true;
+                        $emptyUnit = $left[2] === '' || $right[2] === '';
+                        $targetUnit = '' !== $left[2] ? $left[2] : $right[2];
 
-                                foreach ($right[2] as $unit => $exp) {
-                                    $targetUnit[$unit] = (isset($targetUnit[$unit]) ? $targetUnit[$unit] : 0) - $exp;
-                                }
-                                break;
+                        if ($opName !== 'mul') {
+                            $left[2] = '' !== $left[2] ? $left[2] : $targetUnit;
+                            $right[2] = '' !== $right[2] ? $right[2] : $targetUnit;
+                        }
 
-                            case 'mod':
-                                $targetUnit = $left[2];
-                                break;
+                        if ($opName !== 'mod') {
+                            $left = $this->normalizeNumber($left);
+                            $right = $this->normalizeNumber($right);
+                        }
 
-                            default:
-                                $targetUnit = $left->unitless() ? $right[2] : $left[2];
+                        if ($opName === 'div' && ! $emptyUnit && $left[2] === $right[2]) {
+                            $targetUnit = '';
                         }
 
-                        if (! $left->unitless() && ! $right->unitless()) {
-                            $left = $left->normalize();
-                            $right = $right->normalize();
+                        if ($opName === 'mul') {
+                            $left[2] = '' !== $left[2] ? $left[2] : $right[2];
+                            $right[2] = '' !== $right[2] ? $right[2] : $left[2];
+                        } elseif ($opName === 'div' && $left[2] === $right[2]) {
+                            $left[2] = '';
+                            $right[2] = '';
                         }
                     }
 
@@ -2058,8 +1501,8 @@ class Compiler
                     }
 
                     if (isset($out)) {
-                        if ($coerceUnit && $out[0] === Type::T_NUMBER) {
-                            $out = $out->coerce($targetUnit);
+                        if ($unitChange && $out[0] === 'number') {
+                            $out = $this->coerceUnit($out, $targetUnit);
                         }
 
                         return $out;
@@ -2068,25 +1511,27 @@ class Compiler
 
                 return $this->expToString($value);
 
-            case Type::T_UNARY:
+            case 'unary':
                 list(, $op, $exp, $inParens) = $value;
 
                 $inExp = $inExp || $this->shouldEval($exp);
                 $exp = $this->reduce($exp);
 
-                if ($exp[0] === Type::T_NUMBER) {
+                if ($exp[0] === 'number') {
                     switch ($op) {
                         case '+':
-                            return new Node\Number($exp[1], $exp[2]);
+                            return $exp;
 
                         case '-':
-                            return new Node\Number(-$exp[1], $exp[2]);
+                            $exp[1] *= -1;
+
+                            return $exp;
                     }
                 }
 
                 if ($op === 'not') {
                     if ($inExp || $inParens) {
-                        if ($exp === self::$false || $exp === self::$null) {
+                        if ($exp === self::$false) {
                             return self::$true;
                         }
 
@@ -2096,21 +1541,21 @@ class Compiler
                     $op = $op . ' ';
                 }
 
-                return [Type::T_STRING, '', [$op, $exp]];
+                return array('string', '', array($op, $exp));
 
-            case Type::T_VARIABLE:
+            case 'var':
                 list(, $name) = $value;
 
                 return $this->reduce($this->get($name));
 
-            case Type::T_LIST:
+            case 'list':
                 foreach ($value[2] as &$item) {
                     $item = $this->reduce($item);
                 }
 
                 return $value;
 
-            case Type::T_MAP:
+            case 'map':
                 foreach ($value[1] as &$item) {
                     $item = $this->reduce($item);
                 }
@@ -2121,60 +1566,66 @@ class Compiler
 
                 return $value;
 
-            case Type::T_STRING:
+            case 'string':
                 foreach ($value[2] as &$item) {
-                    if (is_array($item) || $item instanceof \ArrayAccess) {
+                    if (is_array($item)) {
                         $item = $this->reduce($item);
                     }
                 }
 
                 return $value;
 
-            case Type::T_INTERPOLATE:
+            case 'interpolate':
                 $value[1] = $this->reduce($value[1]);
 
                 return $value;
 
-            case Type::T_FUNCTION_CALL:
+            case 'fncall':
                 list(, $name, $argValues) = $value;
 
-                return $this->fncall($name, $argValues);
+                // user defined function?
+                $func = $this->get(self::$namespaces['function'] . $name, false);
 
-            default:
-                return $value;
-        }
-    }
+                if ($func) {
+                    $this->pushEnv();
 
-    /**
-     * Function caller
-     *
-     * @param string $name
-     * @param array  $argValues
-     *
-     * @return array|null
-     */
-    private function fncall($name, $argValues)
-    {
-        // SCSS @function
-        if ($this->callScssFunction($name, $argValues, $returnValue)) {
-            return $returnValue;
-        }
+                    // set the args
+                    if (isset($func->args)) {
+                        $this->applyArguments($func->args, $argValues);
+                    }
 
-        // native PHP functions
-        if ($this->callNativeFunction($name, $argValues, $returnValue)) {
-            return $returnValue;
-        }
+                    // throw away lines and children
+                    $tmp = (object)array(
+                        'lines' => array(),
+                        'children' => array()
+                    );
 
-        // for CSS functions, simply flatten the arguments into a list
-        $listArgs = [];
+                    $ret = $this->compileChildren($func->children, $tmp);
 
-        foreach ((array) $argValues as $arg) {
-            if (empty($arg[0])) {
-                $listArgs[] = $this->reduce($arg[1]);
-            }
-        }
+                    $this->popEnv();
 
-        return [Type::T_FUNCTION, $name, [Type::T_LIST, ',', $listArgs]];
+                    return ! isset($ret) ? self::$defaultValue : $ret;
+                }
+
+                // built in function
+                if ($this->callBuiltin($name, $argValues, $returnValue)) {
+                    return $returnValue;
+                }
+
+                // need to flatten the arguments into a list
+                $listArgs = array();
+
+                foreach ((array)$argValues as $arg) {
+                    if (empty($arg[0])) {
+                        $listArgs[] = $this->reduce($arg[1]);
+                    }
+                }
+
+                return array('function', $name, array('list', ',', $listArgs));
+
+            default:
+                return $value;
+        }
     }
 
     /**
@@ -2202,11 +1653,11 @@ class Compiler
         list($type) = $value;
 
         switch ($type) {
-            case Type::T_LIST:
+            case 'list':
                 $value = $this->extractInterpolation($value);
 
-                if ($value[0] !== Type::T_LIST) {
-                    return [Type::T_KEYWORD, $this->compileValue($value)];
+                if ($value[0] !== 'list') {
+                    return array('keyword', $this->compileValue($value));
                 }
 
                 foreach ($value[2] as $key => $item) {
@@ -2215,20 +1666,37 @@ class Compiler
 
                 return $value;
 
-            case Type::T_STRING:
-                return [$type, '"', [$this->compileStringContent($value)]];
-
-            case Type::T_NUMBER:
-                return $value->normalize();
+            case 'string':
+                return array($type, '"', $this->compileStringContent($value));
 
-            case Type::T_INTERPOLATE:
-                return [Type::T_KEYWORD, $this->compileValue($value)];
+            case 'number':
+                return $this->normalizeNumber($value);
 
             default:
                 return $value;
         }
     }
 
+    /**
+     * Normalize number; just does physical lengths for now
+     *
+     * @param array $number
+     *
+     * @return array
+     */
+    protected function normalizeNumber($number)
+    {
+        list(, $value, $unit) = $number;
+
+        if (isset(self::$unitTable['in'][$unit])) {
+            $conv = self::$unitTable['in'][$unit];
+
+            return array('number', $value / $conv, 'in');
+        }
+
+        return $number;
+    }
+
     /**
      * Add numbers
      *
@@ -2239,7 +1707,7 @@ class Compiler
      */
     protected function opAddNumberNumber($left, $right)
     {
-        return new Node\Number($left[1] + $right[1], $left[2]);
+        return array('number', $left[1] + $right[1], $left[2]);
     }
 
     /**
@@ -2252,7 +1720,7 @@ class Compiler
      */
     protected function opMulNumberNumber($left, $right)
     {
-        return new Node\Number($left[1] * $right[1], $left[2]);
+        return array('number', $left[1] * $right[1], $left[2]);
     }
 
     /**
@@ -2265,7 +1733,7 @@ class Compiler
      */
     protected function opSubNumberNumber($left, $right)
     {
-        return new Node\Number($left[1] - $right[1], $left[2]);
+        return array('number', $left[1] - $right[1], $left[2]);
     }
 
     /**
@@ -2279,10 +1747,10 @@ class Compiler
     protected function opDivNumberNumber($left, $right)
     {
         if ($right[1] == 0) {
-            return [Type::T_STRING, '', [$left[1] . $left[2] . '/' . $right[1] . $right[2]]];
+            $this->throwError('Division by zero');
         }
 
-        return new Node\Number($left[1] / $right[1], $left[2]);
+        return array('number', $left[1] / $right[1], $left[2]);
     }
 
     /**
@@ -2295,7 +1763,7 @@ class Compiler
      */
     protected function opModNumberNumber($left, $right)
     {
-        return new Node\Number($left[1] % $right[1], $left[2]);
+        return array('number', $left[1] % $right[1], $left[2]);
     }
 
     /**
@@ -2309,7 +1777,7 @@ class Compiler
     protected function opAdd($left, $right)
     {
         if ($strLeft = $this->coerceString($left)) {
-            if ($right[0] === Type::T_STRING) {
+            if ($right[0] === 'string') {
                 $right[1] = '';
             }
 
@@ -2319,7 +1787,7 @@ class Compiler
         }
 
         if ($strRight = $this->coerceString($right)) {
-            if ($left[0] === Type::T_STRING) {
+            if ($left[0] === 'string') {
                 $left[1] = '';
             }
 
@@ -2344,8 +1812,8 @@ class Compiler
             return;
         }
 
-        if ($left !== self::$false and $left !== self::$null) {
-            return $this->reduce($right, true);
+        if ($left !== self::$false) {
+            return $right;
         }
 
         return $left;
@@ -2366,11 +1834,11 @@ class Compiler
             return;
         }
 
-        if ($left !== self::$false and $left !== self::$null) {
+        if ($left !== self::$false) {
             return $left;
         }
 
-        return $this->reduce($right, true);
+        return $right;
     }
 
     /**
@@ -2384,9 +1852,9 @@ class Compiler
      */
     protected function opColorColor($op, $left, $right)
     {
-        $out = [Type::T_COLOR];
+        $out = array('color');
 
-        foreach ([1, 2, 3] as $i) {
+        foreach (range(1, 3) as $i) {
             $lval = isset($left[$i]) ? $left[$i] : 0;
             $rval = isset($right[$i]) ? $right[$i] : 0;
 
@@ -2410,7 +1878,6 @@ class Compiler
                 case '/':
                     if ($rval == 0) {
                         $this->throwError("color: Can't divide by zero");
-                        break 2;
                     }
 
                     $out[] = (int) ($lval / $rval);
@@ -2424,7 +1891,6 @@ class Compiler
 
                 default:
                     $this->throwError("color: unknown op $op");
-                    break 2;
             }
         }
 
@@ -2453,7 +1919,7 @@ class Compiler
         return $this->opColorColor(
             $op,
             $left,
-            [Type::T_COLOR, $value, $value, $value]
+            array('color', $value, $value, $value)
         );
     }
 
@@ -2472,7 +1938,7 @@ class Compiler
 
         return $this->opColorColor(
             $op,
-            [Type::T_COLOR, $value, $value, $value],
+            array('color', $value, $value, $value),
             $right
         );
     }
@@ -2571,21 +2037,6 @@ class Compiler
         return $this->toBool($left[1] < $right[1]);
     }
 
-    /**
-     * Three-way comparison, aka spaceship operator
-     *
-     * @param array $left
-     * @param array $right
-     *
-     * @return array
-     */
-    protected function opCmpNumberNumber($left, $right)
-    {
-        $n = $left[1] - $right[1];
-
-        return new Node\Number($n ? $n / abs($n) : 0, '');
-    }
-
     /**
      * Cast to boolean
      *
@@ -2624,10 +2075,10 @@ class Compiler
         list($type) = $value;
 
         switch ($type) {
-            case Type::T_KEYWORD:
+            case 'keyword':
                 return $value[1];
 
-            case Type::T_COLOR:
+            case 'color':
                 // [1] - red component (either number for a %)
                 // [2] - green component
                 // [3] - blue component
@@ -2651,46 +2102,42 @@ class Compiler
 
                 return $h;
 
-            case Type::T_NUMBER:
-                return $value->output($this);
+            case 'number':
+                return round($value[1], $this->numberPrecision) . $value[2];
 
-            case Type::T_STRING:
+            case 'string':
                 return $value[1] . $this->compileStringContent($value) . $value[1];
 
-            case Type::T_FUNCTION:
+            case 'function':
                 $args = ! empty($value[2]) ? $this->compileValue($value[2]) : '';
 
                 return "$value[1]($args)";
 
-            case Type::T_LIST:
+            case 'list':
                 $value = $this->extractInterpolation($value);
 
-                if ($value[0] !== Type::T_LIST) {
+                if ($value[0] !== 'list') {
                     return $this->compileValue($value);
                 }
 
                 list(, $delim, $items) = $value;
 
-                if ($delim !== ' ') {
-                    $delim .= ' ';
-                }
-
-                $filtered = [];
+                $filtered = array();
 
                 foreach ($items as $item) {
-                    if ($item[0] === Type::T_NULL) {
+                    if ($item[0] === 'null') {
                         continue;
                     }
 
                     $filtered[] = $this->compileValue($item);
                 }
 
-                return implode("$delim", $filtered);
+                return implode("$delim ", $filtered);
 
-            case Type::T_MAP:
+            case 'map':
                 $keys = $value[1];
                 $values = $value[2];
-                $filtered = [];
+                $filtered = array();
 
                 for ($i = 0, $s = count($keys); $i < $s; $i++) {
                     $filtered[$this->compileValue($keys[$i])] = $this->compileValue($values[$i]);
@@ -2702,7 +2149,7 @@ class Compiler
 
                 return '(' . implode(', ', $filtered) . ')';
 
-            case Type::T_INTERPOLATED:
+            case 'interpolated':
                 // node created by extractInterpolation
                 list(, $interpolate, $left, $right) = $value;
                 list(,, $whiteLeft, $whiteRight) = $interpolate;
@@ -2715,25 +2162,24 @@ class Compiler
 
                 return $left . $this->compileValue($interpolate) . $right;
 
-            case Type::T_INTERPOLATE:
+            case 'interpolate':
                 // raw parse node
                 list(, $exp) = $value;
 
                 // strip quotes if it's a string
                 $reduced = $this->reduce($exp);
-
                 switch ($reduced[0]) {
-                    case Type::T_STRING:
-                        $reduced = [Type::T_KEYWORD, $this->compileStringContent($reduced)];
+                    case 'string':
+                        $reduced = array('keyword', $this->compileStringContent($reduced));
                         break;
 
-                    case Type::T_NULL:
-                        $reduced = [Type::T_KEYWORD, ''];
+                    case 'null':
+                        $reduced = array('keyword', '');
                 }
 
                 return $this->compileValue($reduced);
 
-            case Type::T_NULL:
+            case 'null':
                 return 'null';
 
             default:
@@ -2762,10 +2208,10 @@ class Compiler
      */
     protected function compileStringContent($string)
     {
-        $parts = [];
+        $parts = array();
 
         foreach ($string[2] as $part) {
-            if (is_array($part) || $part instanceof \ArrayAccess) {
+            if (is_array($part)) {
                 $parts[] = $this->compileValue($part);
             } else {
                 $parts[] = $part;
@@ -2787,11 +2233,11 @@ class Compiler
         $items = $list[2];
 
         foreach ($items as $i => $item) {
-            if ($item[0] === Type::T_INTERPOLATE) {
-                $before = [Type::T_LIST, $list[1], array_slice($items, 0, $i)];
-                $after  = [Type::T_LIST, $list[1], array_slice($items, $i + 1)];
+            if ($item[0] === 'interpolate') {
+                $before = array('list', $list[1], array_slice($items, 0, $i));
+                $after = array('list', $list[1], array_slice($items, $i + 1));
 
-                return [Type::T_INTERPOLATED, $item, $before, $after];
+                return array('interpolated', $item, $before, $after);
             }
         }
 
@@ -2801,22 +2247,27 @@ class Compiler
     /**
      * Find the final set of selectors
      *
-     * @param \Leafo\ScssPhp\Compiler\Environment $env
+     * @param \stdClass $env
      *
      * @return array
      */
-    protected function multiplySelectors(Environment $env)
+    protected function multiplySelectors($env)
     {
-        $envs            = $this->compactEnv($env);
-        $selectors       = [];
-        $parentSelectors = [[]];
+        $envs = array();
 
-        while ($env = array_pop($envs)) {
-            if (empty($env->selectors)) {
-                continue;
+        while (null !== $env) {
+            if (empty($env->selectors)) {
+                $envs[] = $env;
             }
 
-            $selectors = [];
+            $env = $env->parent;
+        };
+
+        $selectors = array();
+        $parentSelectors = array(array());
+
+        while ($env = array_pop($envs)) {
+            $selectors = array();
 
             foreach ($env->selectors as $selector) {
                 foreach ($parentSelectors as $parent) {
@@ -2841,10 +2292,10 @@ class Compiler
     protected function joinSelectors($parent, $child)
     {
         $setSelf = false;
-        $out = [];
+        $out = array();
 
         foreach ($child as $part) {
-            $newPart = [];
+            $newPart = array();
 
             foreach ($part as $p) {
                 if ($p === self::$selfSelector) {
@@ -2853,7 +2304,7 @@ class Compiler
                     foreach ($parent as $i => $parentPart) {
                         if ($i > 0) {
                             $out[] = $newPart;
-                            $newPart = [];
+                            $newPart = array();
                         }
 
                         foreach ($parentPart as $pp) {
@@ -2874,15 +2325,15 @@ class Compiler
     /**
      * Multiply media
      *
-     * @param \Leafo\ScssPhp\Compiler\Environment $env
-     * @param array                               $childQueries
+     * @param \stdClass $env
+     * @param array     $childQueries
      *
      * @return array
      */
-    protected function multiplyMedia(Environment $env = null, $childQueries = null)
+    protected function multiplyMedia($env, $childQueries = null)
     {
         if (! isset($env) ||
-            ! empty($env->block->type) && $env->block->type !== Type::T_MEDIA
+            ! empty($env->block->type) && $env->block->type !== 'media'
         ) {
             return $childQueries;
         }
@@ -2892,15 +2343,12 @@ class Compiler
             return $this->multiplyMedia($env->parent, $childQueries);
         }
 
-        $parentQueries = isset($env->block->queryList)
-            ? $env->block->queryList
-            : [[[Type::T_MEDIA_VALUE, $env->block->value]]];
-
+        $parentQueries = $env->block->queryList;
         if ($childQueries === null) {
             $childQueries = $parentQueries;
         } else {
             $originalQueries = $childQueries;
-            $childQueries = [];
+            $childQueries = array();
 
             foreach ($parentQueries as $parentQuery) {
                 foreach ($originalQueries as $childQuery) {
@@ -2912,53 +2360,20 @@ class Compiler
         return $this->multiplyMedia($env->parent, $childQueries);
     }
 
-    /**
-     * Convert env linked list to stack
-     *
-     * @param \Leafo\ScssPhp\Compiler\Environment $env
-     *
-     * @return array
-     */
-    private function compactEnv(Environment $env)
-    {
-        for ($envs = []; $env; $env = $env->parent) {
-            $envs[] = $env;
-        }
-
-        return $envs;
-    }
-
-    /**
-     * Convert env stack to singly linked list
-     *
-     * @param array $envs
-     *
-     * @return \Leafo\ScssPhp\Compiler\Environment
-     */
-    private function extractEnv($envs)
-    {
-        for ($env = null; $e = array_pop($envs);) {
-            $e->parent = $env;
-            $env = $e;
-        }
-
-        return $env;
-    }
-
     /**
      * Push environment
      *
-     * @param \Leafo\ScssPhp\Block $block
+     * @param \stdClass $block
      *
-     * @return \Leafo\ScssPhp\Compiler\Environment
+     * @return \stdClass
      */
-    protected function pushEnv(Block $block = null)
+    protected function pushEnv($block = null)
     {
-        $env = new Environment;
+        $env = new \stdClass;
         $env->parent = $this->env;
-        $env->store  = [];
-        $env->block  = $block;
-        $env->depth  = isset($this->env->depth) ? $this->env->depth + 1 : 0;
+        $env->store = array();
+        $env->block = $block;
+        $env->depth = isset($this->env->depth) ? $this->env->depth + 1 : 0;
 
         $this->env = $env;
 
@@ -2970,13 +2385,16 @@ class Compiler
      */
     protected function popEnv()
     {
+        $env = $this->env;
         $this->env = $this->env->parent;
+
+        return $env;
     }
 
     /**
      * Get store environment
      *
-     * @return \Leafo\ScssPhp\Compiler\Environment
+     * @return \stdClass
      */
     protected function getStoreEnv()
     {
@@ -2986,19 +2404,15 @@ class Compiler
     /**
      * Set variable
      *
-     * @param string                              $name
-     * @param mixed                               $value
-     * @param boolean                             $shadow
-     * @param \Leafo\ScssPhp\Compiler\Environment $env
+     * @param string    $name
+     * @param mixed     $value
+     * @param boolean   $shadow
+     * @param \stdClass $env
      */
-    protected function set($name, $value, $shadow = false, Environment $env = null)
+    protected function set($name, $value, $shadow = false, $env = null)
     {
         $name = $this->normalizeName($name);
 
-        if (! isset($env)) {
-            $env = $this->getStoreEnv();
-        }
-
         if ($shadow) {
             $this->setRaw($name, $value, $env);
         } else {
@@ -3009,12 +2423,16 @@ class Compiler
     /**
      * Set existing variable
      *
-     * @param string                              $name
-     * @param mixed                               $value
-     * @param \Leafo\ScssPhp\Compiler\Environment $env
+     * @param string    $name
+     * @param mixed     $value
+     * @param \stdClass $env
      */
-    protected function setExisting($name, $value, Environment $env)
+    protected function setExisting($name, $value, $env = null)
     {
+        if (! isset($env)) {
+            $env = $this->getStoreEnv();
+        }
+
         $storeEnv = $env;
 
         $hasNamespace = $name[0] === '^' || $name[0] === '@' || $name[0] === '%';
@@ -3043,12 +2461,16 @@ class Compiler
     /**
      * Set raw variable
      *
-     * @param string                              $name
-     * @param mixed                               $value
-     * @param \Leafo\ScssPhp\Compiler\Environment $env
+     * @param string    $name
+     * @param mixed     $value
+     * @param \stdClass $env
      */
-    protected function setRaw($name, $value, Environment $env)
+    protected function setRaw($name, $value, $env = null)
     {
+        if (! isset($env)) {
+            $env = $this->getStoreEnv();
+        }
+
         $env->store[$name] = $value;
     }
 
@@ -3057,36 +2479,28 @@ class Compiler
      *
      * @api
      *
-     * @param string                              $name
-     * @param boolean                             $shouldThrow
-     * @param \Leafo\ScssPhp\Compiler\Environment $env
+     * @param string    $name
+     * @param boolean   $shouldThrow
+     * @param \stdClass $env
      *
      * @return mixed
      */
-    public function get($name, $shouldThrow = true, Environment $env = null)
+    public function get($name, $shouldThrow = true, $env = null)
     {
-        $normalizedName = $this->normalizeName($name);
-        $specialContentKey = self::$namespaces['special'] . 'content';
+        $name = $this->normalizeName($name);
 
         if (! isset($env)) {
             $env = $this->getStoreEnv();
         }
 
-        $nextIsRoot = false;
-        $hasNamespace = $normalizedName[0] === '^' || $normalizedName[0] === '@' || $normalizedName[0] === '%';
+        $hasNamespace = $name[0] === '^' || $name[0] === '@' || $name[0] === '%';
 
         for (;;) {
-            if (array_key_exists($normalizedName, $env->store)) {
-                return $env->store[$normalizedName];
+            if (array_key_exists($name, $env->store)) {
+                return $env->store[$name];
             }
 
             if (! $hasNamespace && isset($env->marker)) {
-                if (! $nextIsRoot && ! empty($env->store[$specialContentKey])) {
-                    $env = $env->store[$specialContentKey]->scope;
-                    $nextIsRoot = true;
-                    continue;
-                }
-
                 $env = $this->rootEnv;
                 continue;
             }
@@ -3108,12 +2522,12 @@ class Compiler
     /**
      * Has variable?
      *
-     * @param string                              $name
-     * @param \Leafo\ScssPhp\Compiler\Environment $env
+     * @param string    $name
+     * @param \stdClass $env
      *
      * @return boolean
      */
-    protected function has($name, Environment $env = null)
+    protected function has($name, $env = null)
     {
         return $this->get($name, false, $env) !== null;
     }
@@ -3129,7 +2543,7 @@ class Compiler
             return;
         }
 
-        $parser = $this->parserFactory(__METHOD__);
+        $parser = new Parser(__METHOD__, false);
 
         foreach ($args as $name => $strValue) {
             if ($name[0] === '$') {
@@ -3168,32 +2582,6 @@ class Compiler
         unset($this->registeredVars[$name]);
     }
 
-    /**
-     * Returns list of variables
-     *
-     * @api
-     *
-     * @return array
-     */
-    public function getVariables()
-    {
-        return $this->registeredVars;
-    }
-
-    /**
-     * Adds to list of parsed files
-     *
-     * @api
-     *
-     * @param string $path
-     */
-    public function addParsedFile($path)
-    {
-        if (isset($path) && file_exists($path)) {
-            $this->parsedFiles[realpath($path)] = filemtime($path);
-        }
-    }
-
     /**
      * Returns list of parsed files
      *
@@ -3229,7 +2617,7 @@ class Compiler
      */
     public function setImportPaths($path)
     {
-        $this->importPaths = (array) $path;
+        $this->importPaths = (array)$path;
     }
 
     /**
@@ -3241,7 +2629,7 @@ class Compiler
      */
     public function setNumberPrecision($numberPrecision)
     {
-        Node\Number::$precision = $numberPrecision;
+        $this->numberPrecision = $numberPrecision;
     }
 
     /**
@@ -3275,11 +2663,10 @@ class Compiler
      *
      * @param string   $name
      * @param callable $func
-     * @param array    $prototype
      */
-    public function registerFunction($name, $func, $prototype = null)
+    public function registerFunction($name, $func)
     {
-        $this->userFunctions[$this->normalizeName($name)] = [$func, $prototype];
+        $this->userFunctions[$this->normalizeName($name)] = $func;
     }
 
     /**
@@ -3294,18 +2681,6 @@ class Compiler
         unset($this->userFunctions[$this->normalizeName($name)]);
     }
 
-    /**
-     * Add feature
-     *
-     * @api
-     *
-     * @param string $name
-     */
-    public function addFeature($name)
-    {
-        $this->registeredFeatures[$name] = true;
-    }
-
     /**
      * Import file
      *
@@ -3322,16 +2697,17 @@ class Compiler
 
             $tree = $this->importCache[$realPath];
         } else {
-            $code   = file_get_contents($path);
-            $parser = $this->parserFactory($path);
-            $tree   = $parser->parse($code);
+            $code = file_get_contents($path);
+            $parser = new Parser($path, false);
+            $tree = $parser->parse($code);
 
+            $this->parsedFiles[$realPath] = filemtime($path);
             $this->importCache[$realPath] = $tree;
         }
 
         $pi = pathinfo($path);
         array_unshift($this->importPaths, $pi['dirname']);
-        $this->compileChildrenNoReturn($tree->children, $out);
+        $this->compileChildren($tree->children, $out);
         array_shift($this->importPaths);
     }
 
@@ -3346,21 +2722,21 @@ class Compiler
      */
     public function findImport($url)
     {
-        $urls = [];
+        $urls = array();
 
         // for "normal" scss imports (ignore vanilla css and external requests)
         if (! preg_match('/\.css$|^https?:\/\//', $url)) {
             // try both normal and the _partial filename
-            $urls = [$url, preg_replace('/[^\/]+$/', '_\0', $url)];
+            $urls = array($url, preg_replace('/[^\/]+$/', '_\0', $url));
         }
 
         foreach ($this->importPaths as $dir) {
             if (is_string($dir)) {
                 // check urls for normal import paths
                 foreach ($urls as $full) {
-                    $full = $dir
-                        . (! empty($dir) && substr($dir, -1) !== '/' ? '/' : '')
-                        $full;
+                    $full = $dir .
+                        (! empty($dir) && substr($dir, -1) !== '/' ? '/' : '') .
+                        $full;
 
                     if ($this->fileExists($file = $full . '.scss') ||
                         $this->fileExists($file = $full)
@@ -3370,7 +2746,7 @@ class Compiler
                 }
             } elseif (is_callable($dir)) {
                 // check custom callback for import path
-                $file = call_user_func($dir, $url);
+                $file = call_user_func($dir, $url, $this);
 
                 if ($file !== null) {
                     return $file;
@@ -3381,32 +2757,6 @@ class Compiler
         return null;
     }
 
-    /**
-     * Set encoding
-     *
-     * @api
-     *
-     * @param string $encoding
-     */
-    public function setEncoding($encoding)
-    {
-        $this->encoding = $encoding;
-    }
-
-    /**
-     * Ignore errors?
-     *
-     * @api
-     *
-     * @param boolean $ignoreErrors
-     *
-     * @return \Leafo\ScssPhp\Compiler
-     */
-    public function setIgnoreErrors($ignoreErrors)
-    {
-        $this->ignoreErrors = $ignoreErrors;
-    }
-
     /**
      * Throw error (exception)
      *
@@ -3414,22 +2764,19 @@ class Compiler
      *
      * @param string $msg Message with optional sprintf()-style vararg parameters
      *
-     * @throws \Leafo\ScssPhp\Exception\CompilerException
+     * @throws \Exception
      */
     public function throwError($msg)
     {
-        if ($this->ignoreErrors) {
-            return;
-        }
-
         if (func_num_args() > 1) {
             $msg = call_user_func_array('sprintf', func_get_args());
         }
 
-        $line = $this->sourceLine;
-        $msg = "$msg: line: $line";
+        if ($this->sourcePos >= 0 && isset($this->sourceParser)) {
+            $this->sourceParser->throwParseError($msg, $this->sourcePos);
+        }
 
-        throw new CompilerException($msg);
+        throw new \Exception($msg);
     }
 
     /**
@@ -3439,14 +2786,17 @@ class Compiler
      *
      * @throws \Exception
      */
-    protected function handleImportLoop($name)
+    private function handleImportLoop($name)
     {
         for ($env = $this->env; $env; $env = $env->parent) {
-            $file = $this->sourceNames[$env->block->sourceIndex];
+            $file = $env->block->sourceParser->getSourceName();
 
             if (realpath($file) === $name) {
-                $this->throwError('An @import loop has been found: %s imports %s', $file, basename($file));
-                break;
+                $this->throwError(
+                    'An @import loop has been found: %s imports %s',
+                    $this->env->block->sourceParser->getSourceName(),
+                    basename($file)
+                );
             }
         }
     }
@@ -3463,51 +2813,6 @@ class Compiler
         return is_file($name);
     }
 
-    /**
-     * Call SCSS @function
-     *
-     * @param string $name
-     * @param array  $args
-     * @param array  $returnValue
-     *
-     * @return boolean Returns true if returnValue is set; otherwise, false
-     */
-    protected function callScssFunction($name, $argValues, &$returnValue)
-    {
-        $func = $this->get(self::$namespaces['function'] . $name, false);
-
-        if (! $func) {
-            return false;
-        }
-
-        $this->pushEnv();
-
-        $storeEnv = $this->storeEnv;
-        $this->storeEnv = $this->env;
-
-        // set the args
-        if (isset($func->args)) {
-            $this->applyArguments($func->args, $argValues);
-        }
-
-        // throw away lines and children
-        $tmp = new OutputBlock;
-        $tmp->lines    = [];
-        $tmp->children = [];
-
-        $this->env->marker = 'function';
-
-        $ret = $this->compileChildren($func->children, $tmp);
-
-        $this->storeEnv = $storeEnv;
-
-        $this->popEnv();
-
-        $returnValue = ! isset($ret) ? self::$defaultValue : $ret;
-
-        return true;
-    }
-
     /**
      * Call built-in and registered (PHP) functions
      *
@@ -3517,38 +2822,44 @@ class Compiler
      *
      * @return boolean Returns true if returnValue is set; otherwise, false
      */
-    protected function callNativeFunction($name, $args, &$returnValue)
+    protected function callBuiltin($name, $args, &$returnValue)
     {
         // try a lib function
         $name = $this->normalizeName($name);
 
         if (isset($this->userFunctions[$name])) {
             // see if we can find a user function
-            list($f, $prototype) = $this->userFunctions[$name];
-        } elseif (($f = $this->getBuiltinFunction($name)) && is_callable($f)) {
-            $libName   = $f[1];
-            $prototype = isset(self::$$libName) ? self::$$libName : null;
+            $fn = $this->userFunctions[$name];
+
+            foreach ($args as &$val) {
+                $val = $this->reduce($val[1], true);
+            }
+
+            $returnValue = call_user_func($fn, $args, $this);
         } else {
-            return false;
-        }
+            $f = $this->getBuiltinFunction($name);
+
+            if (is_callable($f)) {
+                $libName = $f[1];
+
+                $prototype = isset(self::$$libName) ? self::$$libName : null;
+                $sorted = $this->sortArgs($prototype, $args);
 
-        list($sorted, $kwargs) = $this->sortArgs($prototype, $args);
+                foreach ($sorted as &$val) {
+                    $val = $this->reduce($val, true);
+                }
 
-        if ($name !== 'if' && $name !== 'call') {
-            foreach ($sorted as &$val) {
-                $val = $this->reduce($val, true);
+                $returnValue = call_user_func($f, $sorted, $this);
             }
         }
 
-        $returnValue = call_user_func($f, $sorted, $kwargs);
+        if (isset($returnValue)) {
+            $returnValue = $this->coerceValue($returnValue);
 
-        if (! isset($returnValue)) {
-            return false;
+            return true;
         }
 
-        $returnValue = $this->coerceValue($returnValue);
-
-        return true;
+        return false;
     }
 
     /**
@@ -3568,12 +2879,14 @@ class Compiler
             ucfirst($name)
         );
 
-        return [$this, $libName];
+        return array($this, $libName);
     }
 
     /**
      * Sorts keyword arguments
      *
+     * @todo Merge with applyArguments()?
+     *
      * @param array $prototype
      * @param array $args
      *
@@ -3581,10 +2894,9 @@ class Compiler
      */
     protected function sortArgs($prototype, $args)
     {
-        $keyArgs = [];
-        $posArgs = [];
+        $keyArgs = array();
+        $posArgs = array();
 
-        // separate positional and keyword arguments
         foreach ($args as $arg) {
             list($key, $value) = $arg;
 
@@ -3598,22 +2910,33 @@ class Compiler
         }
 
         if (! isset($prototype)) {
-            return [$posArgs, $keyArgs];
+            return $posArgs;
         }
 
-        // copy positional args
-        $finalArgs = array_pad($posArgs, count($prototype), null);
+        $finalArgs = array();
 
-        // overwrite positional args with keyword args
         foreach ($prototype as $i => $names) {
-            foreach ((array) $names as $name) {
+            if (isset($posArgs[$i])) {
+                $finalArgs[] = $posArgs[$i];
+                continue;
+            }
+
+            $set = false;
+
+            foreach ((array)$names as $name) {
                 if (isset($keyArgs[$name])) {
-                    $finalArgs[$i] = $keyArgs[$name];
+                    $finalArgs[] = $keyArgs[$name];
+                    $set = true;
+                    break;
                 }
             }
+
+            if (! $set) {
+                $finalArgs[] = null;
+            }
         }
 
-        return [$finalArgs, $keyArgs];
+        return $finalArgs;
     }
 
     /**
@@ -3628,22 +2951,22 @@ class Compiler
     {
         $storeEnv = $this->getStoreEnv();
 
-        $env = new Environment;
+        $env = new \stdClass;
         $env->store = $storeEnv->store;
 
         $hasVariable = false;
-        $args = [];
+        $args = array();
 
         foreach ($argDef as $i => $arg) {
             list($name, $default, $isVariable) = $argDef[$i];
 
-            $args[$name] = [$i, $name, $default, $isVariable];
+            $args[$name] = array($i, $name, $default, $isVariable);
             $hasVariable |= $isVariable;
         }
 
-        $keywordArgs = [];
-        $deferredKeywordArgs = [];
-        $remaining = [];
+        $keywordArgs = array();
+        $deferredKeywordArgs = array();
+        $remaining = array();
 
         // assign the keyword args
         foreach ((array) $argValues as $arg) {
@@ -3653,33 +2976,19 @@ class Compiler
                         $deferredKeywordArgs[$arg[0][1]] = $arg[1];
                     } else {
                         $this->throwError("Mixin or function doesn't have an argument named $%s.", $arg[0][1]);
-                        break;
                     }
                 } elseif ($args[$arg[0][1]][0] < count($remaining)) {
                     $this->throwError("The argument $%s was passed both by position and by name.", $arg[0][1]);
-                    break;
                 } else {
                     $keywordArgs[$arg[0][1]] = $arg[1];
                 }
             } elseif (count($keywordArgs)) {
                 $this->throwError('Positional arguments must come before keyword arguments.');
-                break;
             } elseif ($arg[2] === true) {
                 $val = $this->reduce($arg[1], true);
 
-                if ($val[0] === Type::T_LIST) {
+                if ($val[0] === 'list') {
                     foreach ($val[2] as $name => $item) {
-                        if (! is_numeric($name)) {
-                            $keywordArgs[$name] = $item;
-                        } else {
-                            $remaining[] = $item;
-                        }
-                    }
-                } elseif ($val[0] === Type::T_MAP) {
-                    foreach ($val[1] as $i => $name) {
-                        $name = $this->compileStringContent($this->coerceString($name));
-                        $item = $val[2][$i];
-
                         if (! is_numeric($name)) {
                             $keywordArgs[$name] = $item;
                         } else {
@@ -3698,7 +3007,7 @@ class Compiler
             list($i, $name, $default, $isVariable) = $arg;
 
             if ($isVariable) {
-                $val = [Type::T_LIST, ',', [], $isVariable];
+                $val = array('list', ',', array(), $isVariable);
 
                 for ($count = count($remaining); $i < $count; $i++) {
                     $val[2][] = $remaining[$i];
@@ -3715,7 +3024,6 @@ class Compiler
                 continue;
             } else {
                 $this->throwError("Missing argument $name");
-                break;
             }
 
             $this->set($name, $this->reduce($val, true), true, $env);
@@ -3743,50 +3051,46 @@ class Compiler
      */
     private function coerceValue($value)
     {
-        if (is_array($value) || $value instanceof \ArrayAccess) {
+        if (is_array($value)) {
             return $value;
         }
 
         if (is_bool($value)) {
-            return $this->toBool($value);
+            return $value ? self::$true : self::$false;
         }
 
         if ($value === null) {
-            return self::$null;
+            $value = self::$null;
         }
 
         if (is_numeric($value)) {
-            return new Node\Number($value, '');
+            return array('number', $value, '');
         }
 
         if ($value === '') {
             return self::$emptyString;
         }
 
-        if (preg_match('/^(#([0-9a-f]{6})|#([0-9a-f]{3}))$/i', $value, $m)) {
-            $color = [Type::T_COLOR];
-
-            if (isset($m[3])) {
-                $num = hexdec($m[3]);
-
-                foreach ([3, 2, 1] as $i) {
-                    $t = $num & 0xf;
-                    $color[$i] = $t << 4 | $t;
-                    $num >>= 4;
-                }
-            } else {
-                $num = hexdec($m[2]);
+        return array('keyword', $value);
+    }
 
-                foreach ([3, 2, 1] as $i) {
-                    $color[$i] = $num & 0xff;
-                    $num >>= 8;
-                }
-            }
+    /**
+     * Coerce unit on number to be normalized
+     *
+     * @param array  $number
+     * @param string $unit
+     *
+     * @return array
+     */
+    protected function coerceUnit($number, $unit)
+    {
+        list(, $value, $baseUnit) = $number;
 
-            return $color;
+        if (isset(self::$unitTable[$baseUnit][$unit])) {
+            $value = $value * self::$unitTable[$baseUnit][$unit];
         }
 
-        return [Type::T_KEYWORD, $value];
+        return array('number', $value, $unit);
     }
 
     /**
@@ -3798,7 +3102,7 @@ class Compiler
      */
     protected function coerceMap($item)
     {
-        if ($item[0] === Type::T_MAP) {
+        if ($item[0] === 'map') {
             return $item;
         }
 
@@ -3806,7 +3110,7 @@ class Compiler
             return self::$emptyMap;
         }
 
-        return [Type::T_MAP, [$item], [self::$null]];
+        return array('map', array($item), array(self::$null));
     }
 
     /**
@@ -3818,30 +3122,26 @@ class Compiler
      */
     protected function coerceList($item, $delim = ',')
     {
-        if (isset($item) && $item[0] === Type::T_LIST) {
+        if (isset($item) && $item[0] === 'list') {
             return $item;
         }
 
-        if (isset($item) && $item[0] === Type::T_MAP) {
+        if (isset($item) && $item[0] === 'map') {
             $keys = $item[1];
             $values = $item[2];
-            $list = [];
+            $list = array();
 
             for ($i = 0, $s = count($keys); $i < $s; $i++) {
                 $key = $keys[$i];
                 $value = $values[$i];
 
-                $list[] = [
-                    Type::T_LIST,
-                    '',
-                    [[Type::T_KEYWORD, $this->compileStringContent($this->coerceString($key))], $value]
-                ];
+                $list[] = array('list', '', array(array('keyword', $this->compileValue($key)), $value));
             }
 
-            return [Type::T_LIST, ',', $list];
+            return array('list', ',', $list);
         }
 
-        return [Type::T_LIST, $delim, ! isset($item) ? []: [$item]];
+        return array('list', $delim, ! isset($item) ? array(): array($item));
     }
 
     /**
@@ -3870,18 +3170,18 @@ class Compiler
     protected function coerceColor($value)
     {
         switch ($value[0]) {
-            case Type::T_COLOR:
+            case 'color':
                 return $value;
 
-            case Type::T_KEYWORD:
+            case 'keyword':
                 $name = strtolower($value[1]);
 
                 if (isset(Colors::$cssColors[$name])) {
                     $rgba = explode(',', Colors::$cssColors[$name]);
 
                     return isset($rgba[3])
-                        ? [Type::T_COLOR, (int) $rgba[0], (int) $rgba[1], (int) $rgba[2], (int) $rgba[3]]
-                        : [Type::T_COLOR, (int) $rgba[0], (int) $rgba[1], (int) $rgba[2]];
+                        ? array('color', (int) $rgba[0], (int) $rgba[1], (int) $rgba[2], (int) $rgba[3])
+                        : array('color', (int) $rgba[0], (int) $rgba[1], (int) $rgba[2]);
                 }
 
                 return null;
@@ -3899,11 +3199,11 @@ class Compiler
      */
     protected function coerceString($value)
     {
-        if ($value[0] === Type::T_STRING) {
+        if ($value[0] === 'string') {
             return $value;
         }
 
-        return [Type::T_STRING, '', [$this->compileValue($value)]];
+        return array('string', '', array($this->compileValue($value)));
     }
 
     /**
@@ -3915,8 +3215,8 @@ class Compiler
      */
     protected function coercePercent($value)
     {
-        if ($value[0] === Type::T_NUMBER) {
-            if (! empty($value[2]['%'])) {
+        if ($value[0] === 'number') {
+            if ($value[2] === '%') {
                 return $value[1] / 100;
             }
 
@@ -3941,7 +3241,7 @@ class Compiler
     {
         $value = $this->coerceMap($value);
 
-        if ($value[0] !== Type::T_MAP) {
+        if ($value[0] !== 'map') {
             $this->throwError('expecting map');
         }
 
@@ -3961,7 +3261,7 @@ class Compiler
      */
     public function assertList($value)
     {
-        if ($value[0] !== Type::T_LIST) {
+        if ($value[0] !== 'list') {
             $this->throwError('expecting list');
         }
 
@@ -4001,7 +3301,7 @@ class Compiler
      */
     public function assertNumber($value)
     {
-        if ($value[0] !== Type::T_NUMBER) {
+        if ($value[0] !== 'number') {
             $this->throwError('expecting number');
         }
 
@@ -4017,7 +3317,7 @@ class Compiler
      */
     protected function fixColor($c)
     {
-        foreach ([1, 2, 3] as $i) {
+        foreach (range(1, 3) as $i) {
             if ($c[$i] < 0) {
                 $c[$i] = 0;
             }
@@ -4067,7 +3367,7 @@ class Compiler
             }
         }
 
-        return [Type::T_HSL, fmod($h, 360), $s * 100, $l / 5.1];
+        return array('hsl', fmod($h, 360), $s * 100, $l / 5.1);
     }
 
     /**
@@ -4130,68 +3430,43 @@ class Compiler
         $g = $this->hueToRGB($m1, $m2, $h) * 255;
         $b = $this->hueToRGB($m1, $m2, $h - 1/3) * 255;
 
-        $out = [Type::T_COLOR, $r, $g, $b];
+        $out = array('color', $r, $g, $b);
 
         return $out;
     }
 
     // Built in functions
 
-    //protected static $libCall = ['name', 'args...'];
-    protected function libCall($args, $kwargs)
-    {
-        $name = $this->compileStringContent($this->coerceString($this->reduce(array_shift($args), true)));
-
-        $args = array_map(
-            function ($a) {
-                return [null, $a, false];
-            },
-            $args
-        );
-
-        if (count($kwargs)) {
-            foreach ($kwargs as $key => $value) {
-                $args[] = [[Type::T_VARIABLE, $key], $value, false];
-            }
-        }
-
-        return $this->reduce([Type::T_FUNCTION_CALL, $name, $args]);
-    }
-
-    protected static $libIf = ['condition', 'if-true', 'if-false'];
+    protected static $libIf = array('condition', 'if-true', 'if-false');
     protected function libIf($args)
     {
         list($cond, $t, $f) = $args;
 
-        if (! $this->isTruthy($this->reduce($cond, true))) {
-            return $this->reduce($f, true);
+        if (! $this->isTruthy($cond)) {
+            return $f;
         }
 
-        return $this->reduce($t, true);
+        return $t;
     }
 
-    protected static $libIndex = ['list', 'value'];
+    protected static $libIndex = array('list', 'value');
     protected function libIndex($args)
     {
         list($list, $value) = $args;
 
-        if ($value[0] === Type::T_MAP) {
+        if ($value[0] === 'map') {
             return self::$null;
         }
 
-        if ($list[0] === Type::T_MAP ||
-            $list[0] === Type::T_STRING ||
-            $list[0] === Type::T_KEYWORD ||
-            $list[0] === Type::T_INTERPOLATE
-        ) {
+        if ($list[0] === 'map') {
             $list = $this->coerceList($list, ' ');
         }
 
-        if ($list[0] !== Type::T_LIST) {
+        if ($list[0] !== 'list') {
             return self::$null;
         }
 
-        $values = [];
+        $values = array();
 
         foreach ($list[2] as $item) {
             $values[] = $this->normalizeValue($item);
@@ -4202,20 +3477,23 @@ class Compiler
         return false === $key ? self::$null : $key + 1;
     }
 
-    protected static $libRgb = ['red', 'green', 'blue'];
+    protected static $libRgb = array('red', 'green', 'blue');
     protected function libRgb($args)
     {
         list($r, $g, $b) = $args;
 
-        return [Type::T_COLOR, $r[1], $g[1], $b[1]];
+        return array('color', $r[1], $g[1], $b[1]);
     }
 
-    protected static $libRgba = [
-        ['red', 'color'],
-        'green', 'blue', 'alpha'];
+    protected static $libRgba = array(
+        array('red', 'color'),
+        'green', 'blue', 'alpha');
     protected function libRgba($args)
     {
         if ($color = $this->coerceColor($args[0])) {
+            // workaround https://github.com/facebook/hhvm/issues/5457
+            reset($args);
+
             $num = ! isset($args[1]) ? $args[3] : $args[1];
             $alpha = $this->assertNumber($num);
             $color[4] = $alpha;
@@ -4225,7 +3503,7 @@ class Compiler
 
         list($r, $g, $b, $a) = $args;
 
-        return [Type::T_COLOR, $r[1], $g[1], $b[1], $a[1]];
+        return array('color', $r[1], $g[1], $b[1], $a[1]);
     }
 
     // helper function for adjust_color, change_color, and scale_color
@@ -4233,7 +3511,10 @@ class Compiler
     {
         $color = $this->assertColor($args[0]);
 
-        foreach ([1, 2, 3, 7] as $i) {
+        // workaround https://github.com/facebook/hhvm/issues/5457
+        reset($args);
+
+        foreach (array(1, 2, 3, 7) as $i) {
             if (isset($args[$i])) {
                 $val = $this->assertNumber($args[$i]);
                 $ii = $i === 7 ? 4 : $i; // alpha
@@ -4244,7 +3525,7 @@ class Compiler
         if (isset($args[4]) || isset($args[5]) || isset($args[6])) {
             $hsl = $this->toHSL($color[1], $color[2], $color[3]);
 
-            foreach ([4, 5, 6] as $i) {
+            foreach (array(4, 5, 6) as $i) {
                 if (isset($args[$i])) {
                     $val = $this->assertNumber($args[$i]);
                     $hsl[$i - 3] = call_user_func($fn, $hsl[$i - 3], $val, $i);
@@ -4263,10 +3544,10 @@ class Compiler
         return $color;
     }
 
-    protected static $libAdjustColor = [
+    protected static $libAdjustColor = array(
         'color', 'red', 'green', 'blue',
         'hue', 'saturation', 'lightness', 'alpha'
-    ];
+    );
     protected function libAdjustColor($args)
     {
         return $this->alterColor($args, function ($base, $alter, $i) {
@@ -4274,10 +3555,10 @@ class Compiler
         });
     }
 
-    protected static $libChangeColor = [
+    protected static $libChangeColor = array(
         'color', 'red', 'green', 'blue',
         'hue', 'saturation', 'lightness', 'alpha'
-    ];
+    );
     protected function libChangeColor($args)
     {
         return $this->alterColor($args, function ($base, $alter, $i) {
@@ -4285,10 +3566,10 @@ class Compiler
         });
     }
 
-    protected static $libScaleColor = [
+    protected static $libScaleColor = array(
         'color', 'red', 'green', 'blue',
         'hue', 'saturation', 'lightness', 'alpha'
-    ];
+    );
     protected function libScaleColor($args)
     {
         return $this->alterColor($args, function ($base, $scale, $i) {
@@ -4324,16 +3605,16 @@ class Compiler
         });
     }
 
-    protected static $libIeHexStr = ['color'];
+    protected static $libIeHexStr = array('color');
     protected function libIeHexStr($args)
     {
         $color = $this->coerceColor($args[0]);
-        $color[4] = isset($color[4]) ? round(255 * $color[4]) : 255;
+        $color[4] = isset($color[4]) ? round(255*$color[4]) : 255;
 
         return sprintf('#%02X%02X%02X%02X', $color[4], $color[1], $color[2], $color[3]);
     }
 
-    protected static $libRed = ['color'];
+    protected static $libRed = array('color');
     protected function libRed($args)
     {
         $color = $this->coerceColor($args[0]);
@@ -4341,7 +3622,7 @@ class Compiler
         return $color[1];
     }
 
-    protected static $libGreen = ['color'];
+    protected static $libGreen = array('color');
     protected function libGreen($args)
     {
         $color = $this->coerceColor($args[0]);
@@ -4349,7 +3630,7 @@ class Compiler
         return $color[2];
     }
 
-    protected static $libBlue = ['color'];
+    protected static $libBlue = array('color');
     protected function libBlue($args)
     {
         $color = $this->coerceColor($args[0]);
@@ -4357,7 +3638,7 @@ class Compiler
         return $color[3];
     }
 
-    protected static $libAlpha = ['color'];
+    protected static $libAlpha = array('color');
     protected function libAlpha($args)
     {
         if ($color = $this->coerceColor($args[0])) {
@@ -4368,12 +3649,12 @@ class Compiler
         return null;
     }
 
-    protected static $libOpacity = ['color'];
+    protected static $libOpacity = array('color');
     protected function libOpacity($args)
     {
         $value = $args[0];
 
-        if ($value[0] === Type::T_NUMBER) {
+        if ($value[0] === 'number') {
             return null;
         }
 
@@ -4381,7 +3662,7 @@ class Compiler
     }
 
     // mix two colors
-    protected static $libMix = ['color-1', 'color-2', 'weight'];
+    protected static $libMix = array('color-1', 'color-2', 'weight');
     protected function libMix($args)
     {
         list($first, $second, $weight) = $args;
@@ -4404,11 +3685,11 @@ class Compiler
         $w1 = (($w * $a === -1 ? $w : ($w + $a) / (1 + $w * $a)) + 1) / 2.0;
         $w2 = 1.0 - $w1;
 
-        $new = [Type::T_COLOR,
+        $new = array('color',
             $w1 * $first[1] + $w2 * $second[1],
             $w1 * $first[2] + $w2 * $second[2],
             $w1 * $first[3] + $w2 * $second[3],
-        ];
+        );
 
         if ($firstAlpha != 1.0 || $secondAlpha != 1.0) {
             $new[] = $firstAlpha * $weight + $secondAlpha * ($weight - 1);
@@ -4417,7 +3698,7 @@ class Compiler
         return $this->fixColor($new);
     }
 
-    protected static $libHsl = ['hue', 'saturation', 'lightness'];
+    protected static $libHsl = array('hue', 'saturation', 'lightness');
     protected function libHsl($args)
     {
         list($h, $s, $l) = $args;
@@ -4425,7 +3706,8 @@ class Compiler
         return $this->toRGB($h[1], $s[1], $l[1]);
     }
 
-    protected static $libHsla = ['hue', 'saturation', 'lightness', 'alpha'];
+    protected static $libHsla = array('hue', 'saturation',
+        'lightness', 'alpha');
     protected function libHsla($args)
     {
         list($h, $s, $l, $a) = $args;
@@ -4436,31 +3718,31 @@ class Compiler
         return $color;
     }
 
-    protected static $libHue = ['color'];
+    protected static $libHue = array('color');
     protected function libHue($args)
     {
         $color = $this->assertColor($args[0]);
         $hsl = $this->toHSL($color[1], $color[2], $color[3]);
 
-        return new Node\Number($hsl[1], 'deg');
+        return array('number', $hsl[1], 'deg');
     }
 
-    protected static $libSaturation = ['color'];
+    protected static $libSaturation = array('color');
     protected function libSaturation($args)
     {
         $color = $this->assertColor($args[0]);
         $hsl = $this->toHSL($color[1], $color[2], $color[3]);
 
-        return new Node\Number($hsl[2], '%');
+        return array('number', $hsl[2], '%');
     }
 
-    protected static $libLightness = ['color'];
+    protected static $libLightness = array('color');
     protected function libLightness($args)
     {
         $color = $this->assertColor($args[0]);
         $hsl = $this->toHSL($color[1], $color[2], $color[3]);
 
-        return new Node\Number($hsl[3], '%');
+        return array('number', $hsl[3], '%');
     }
 
     protected function adjustHsl($color, $idx, $amount)
@@ -4476,7 +3758,7 @@ class Compiler
         return $out;
     }
 
-    protected static $libAdjustHue = ['color', 'degrees'];
+    protected static $libAdjustHue = array('color', 'degrees');
     protected function libAdjustHue($args)
     {
         $color = $this->assertColor($args[0]);
@@ -4485,7 +3767,7 @@ class Compiler
         return $this->adjustHsl($color, 1, $degrees);
     }
 
-    protected static $libLighten = ['color', 'amount'];
+    protected static $libLighten = array('color', 'amount');
     protected function libLighten($args)
     {
         $color = $this->assertColor($args[0]);
@@ -4494,7 +3776,7 @@ class Compiler
         return $this->adjustHsl($color, 3, $amount);
     }
 
-    protected static $libDarken = ['color', 'amount'];
+    protected static $libDarken = array('color', 'amount');
     protected function libDarken($args)
     {
         $color = $this->assertColor($args[0]);
@@ -4503,54 +3785,54 @@ class Compiler
         return $this->adjustHsl($color, 3, -$amount);
     }
 
-    protected static $libSaturate = ['color', 'amount'];
+    protected static $libSaturate = array('color', 'amount');
     protected function libSaturate($args)
     {
         $value = $args[0];
 
-        if ($value[0] === Type::T_NUMBER) {
+        if ($value[0] === 'number') {
             return null;
         }
 
         $color = $this->assertColor($value);
-        $amount = 100 * $this->coercePercent($args[1]);
+        $amount = 100*$this->coercePercent($args[1]);
 
         return $this->adjustHsl($color, 2, $amount);
     }
 
-    protected static $libDesaturate = ['color', 'amount'];
+    protected static $libDesaturate = array('color', 'amount');
     protected function libDesaturate($args)
     {
         $color = $this->assertColor($args[0]);
-        $amount = 100 * $this->coercePercent($args[1]);
+        $amount = 100*$this->coercePercent($args[1]);
 
         return $this->adjustHsl($color, 2, -$amount);
     }
 
-    protected static $libGrayscale = ['color'];
+    protected static $libGrayscale = array('color');
     protected function libGrayscale($args)
     {
         $value = $args[0];
 
-        if ($value[0] === Type::T_NUMBER) {
+        if ($value[0] === 'number') {
             return null;
         }
 
         return $this->adjustHsl($this->assertColor($value), 2, -100);
     }
 
-    protected static $libComplement = ['color'];
+    protected static $libComplement = array('color');
     protected function libComplement($args)
     {
         return $this->adjustHsl($this->assertColor($args[0]), 1, 180);
     }
 
-    protected static $libInvert = ['color'];
+    protected static $libInvert = array('color');
     protected function libInvert($args)
     {
         $value = $args[0];
 
-        if ($value[0] === Type::T_NUMBER) {
+        if ($value[0] === 'number') {
             return null;
         }
 
@@ -4563,7 +3845,7 @@ class Compiler
     }
 
     // increases opacity by amount
-    protected static $libOpacify = ['color', 'amount'];
+    protected static $libOpacify = array('color', 'amount');
     protected function libOpacify($args)
     {
         $color = $this->assertColor($args[0]);
@@ -4575,14 +3857,14 @@ class Compiler
         return $color;
     }
 
-    protected static $libFadeIn = ['color', 'amount'];
+    protected static $libFadeIn = array('color', 'amount');
     protected function libFadeIn($args)
     {
         return $this->libOpacify($args);
     }
 
     // decreases opacity by amount
-    protected static $libTransparentize = ['color', 'amount'];
+    protected static $libTransparentize = array('color', 'amount');
     protected function libTransparentize($args)
     {
         $color = $this->assertColor($args[0]);
@@ -4594,43 +3876,45 @@ class Compiler
         return $color;
     }
 
-    protected static $libFadeOut = ['color', 'amount'];
+    protected static $libFadeOut = array('color', 'amount');
     protected function libFadeOut($args)
     {
         return $this->libTransparentize($args);
     }
 
-    protected static $libUnquote = ['string'];
+    protected static $libUnquote = array('string');
     protected function libUnquote($args)
     {
         $str = $args[0];
 
-        if ($str[0] === Type::T_STRING) {
+        if ($str[0] === 'string') {
             $str[1] = '';
         }
 
         return $str;
     }
 
-    protected static $libQuote = ['string'];
+    protected static $libQuote = array('string');
     protected function libQuote($args)
     {
         $value = $args[0];
 
-        if ($value[0] === Type::T_STRING && ! empty($value[1])) {
+        if ($value[0] === 'string' && ! empty($value[1])) {
             return $value;
         }
 
-        return [Type::T_STRING, '"', [$value]];
+        return array('string', '"', array($value));
     }
 
-    protected static $libPercentage = ['value'];
+    protected static $libPercentage = array('value');
     protected function libPercentage($args)
     {
-        return new Node\Number($this->coercePercent($args[0]) * 100, '%');
+        return array('number',
+            $this->coercePercent($args[0]) * 100,
+            '%');
     }
 
-    protected static $libRound = ['value'];
+    protected static $libRound = array('value');
     protected function libRound($args)
     {
         $num = $args[0];
@@ -4639,7 +3923,7 @@ class Compiler
         return $num;
     }
 
-    protected static $libFloor = ['value'];
+    protected static $libFloor = array('value');
     protected function libFloor($args)
     {
         $num = $args[0];
@@ -4648,7 +3932,7 @@ class Compiler
         return $num;
     }
 
-    protected static $libCeil = ['value'];
+    protected static $libCeil = array('value');
     protected function libCeil($args)
     {
         $num = $args[0];
@@ -4657,7 +3941,7 @@ class Compiler
         return $num;
     }
 
-    protected static $libAbs = ['value'];
+    protected static $libAbs = array('value');
     protected function libAbs($args)
     {
         $num = $args[0];
@@ -4673,7 +3957,7 @@ class Compiler
 
         foreach ($numbers as $key => $number) {
             if (null === $min || $number[1] <= $min[1]) {
-                $min = [$key, $number[1]];
+                $min = array($key, $number[1]);
             }
         }
 
@@ -4687,7 +3971,7 @@ class Compiler
 
         foreach ($numbers as $key => $number) {
             if (null === $max || $number[1] >= $max[1]) {
-                $max = [$key, $number[1]];
+                $max = array($key, $number[1]);
             }
         }
 
@@ -4705,22 +3989,20 @@ class Compiler
     {
         $unit = null;
         $originalUnit = null;
-        $numbers = [];
+        $numbers = array();
 
         foreach ($args as $key => $item) {
-            if ($item[0] !== Type::T_NUMBER) {
+            if ('number' !== $item[0]) {
                 $this->throwError('%s is not a number', $item[0]);
-                break;
             }
 
-            $number = $item->normalize();
+            $number = $this->normalizeNumber($item);
 
             if (null === $unit) {
                 $unit = $number[2];
-                $originalUnit = $item->unitStr();
+                $originalUnit = $item[2];
             } elseif ($unit !== $number[2]) {
-                $this->throwError('Incompatible units: "%s" and "%s".', $originalUnit, $item->unitStr());
-                break;
+                $this->throwError('Incompatible units: "%s" and "%s".', $originalUnit, $item[2]);
             }
 
             $numbers[$key] = $number;
@@ -4729,7 +4011,7 @@ class Compiler
         return $numbers;
     }
 
-    protected static $libLength = ['list'];
+    protected static $libLength = array('list');
     protected function libLength($args)
     {
         $list = $this->coerceList($args[0]);
@@ -4737,7 +4019,8 @@ class Compiler
         return count($list[2]);
     }
 
-    //protected static $libListSeparator = ['list...'];
+    // TODO: need a way to declare this built-in as varargs
+    //protected static $libListSeparator = array('list...');
     protected function libListSeparator($args)
     {
         if (count($args) > 1) {
@@ -4757,37 +4040,23 @@ class Compiler
         return 'space';
     }
 
-    protected static $libNth = ['list', 'n'];
+    protected static $libNth = array('list', 'n');
     protected function libNth($args)
     {
         $list = $this->coerceList($args[0]);
-        $n = $this->assertNumber($args[1]);
-
-        if ($n > 0) {
-            $n--;
-        } elseif ($n < 0) {
-            $n += count($list[2]);
-        }
+        $n = $this->assertNumber($args[1]) - 1;
 
         return isset($list[2][$n]) ? $list[2][$n] : self::$defaultValue;
     }
 
-    protected static $libSetNth = ['list', 'n', 'value'];
+    protected static $libSetNth = array('list', 'n', 'value');
     protected function libSetNth($args)
     {
         $list = $this->coerceList($args[0]);
-        $n = $this->assertNumber($args[1]);
-
-        if ($n > 0) {
-            $n--;
-        } elseif ($n < 0) {
-            $n += count($list[2]);
-        }
+        $n = $this->assertNumber($args[1]) - 1;
 
         if (! isset($list[2][$n])) {
             $this->throwError('Invalid argument for "n"');
-
-            return;
         }
 
         $list[2][$n] = $args[2];
@@ -4795,14 +4064,15 @@ class Compiler
         return $list;
     }
 
-    protected static $libMapGet = ['map', 'key'];
+    protected static $libMapGet = array('map', 'key');
     protected function libMapGet($args)
     {
         $map = $this->assertMap($args[0]);
+
         $key = $this->compileStringContent($this->coerceString($args[1]));
 
         for ($i = count($map[1]) - 1; $i >= 0; $i--) {
-            if ($key === $this->compileStringContent($this->coerceString($map[1][$i]))) {
+            if ($key === $this->compileValue($map[1][$i])) {
                 return $map[2][$i];
             }
         }
@@ -4810,32 +4080,35 @@ class Compiler
         return self::$null;
     }
 
-    protected static $libMapKeys = ['map'];
+    protected static $libMapKeys = array('map');
     protected function libMapKeys($args)
     {
         $map = $this->assertMap($args[0]);
+
         $keys = $map[1];
 
-        return [Type::T_LIST, ',', $keys];
+        return array('list', ',', $keys);
     }
 
-    protected static $libMapValues = ['map'];
+    protected static $libMapValues = array('map');
     protected function libMapValues($args)
     {
         $map = $this->assertMap($args[0]);
+
         $values = $map[2];
 
-        return [Type::T_LIST, ',', $values];
+        return array('list', ',', $values);
     }
 
-    protected static $libMapRemove = ['map', 'key'];
+    protected static $libMapRemove = array('map', 'key');
     protected function libMapRemove($args)
     {
         $map = $this->assertMap($args[0]);
+
         $key = $this->compileStringContent($this->coerceString($args[1]));
 
         for ($i = count($map[1]) - 1; $i >= 0; $i--) {
-            if ($key === $this->compileStringContent($this->coerceString($map[1][$i]))) {
+            if ($key === $this->compileValue($map[1][$i])) {
                 array_splice($map[1], $i, 1);
                 array_splice($map[2], $i, 1);
             }
@@ -4844,44 +4117,29 @@ class Compiler
         return $map;
     }
 
-    protected static $libMapHasKey = ['map', 'key'];
+    protected static $libMapHasKey = array('map', 'key');
     protected function libMapHasKey($args)
     {
         $map = $this->assertMap($args[0]);
+
         $key = $this->compileStringContent($this->coerceString($args[1]));
 
         for ($i = count($map[1]) - 1; $i >= 0; $i--) {
-            if ($key === $this->compileStringContent($this->coerceString($map[1][$i]))) {
-                return true;
+            if ($key === $this->compileValue($map[1][$i])) {
+                return self::$true;
             }
         }
 
-        return false;
+        return self::$false;
     }
 
-    protected static $libMapMerge = ['map-1', 'map-2'];
+    protected static $libMapMerge = array('map-1', 'map-2');
     protected function libMapMerge($args)
     {
         $map1 = $this->assertMap($args[0]);
         $map2 = $this->assertMap($args[1]);
 
-        return [Type::T_MAP, array_merge($map1[1], $map2[1]), array_merge($map1[2], $map2[2])];
-    }
-
-    protected static $libKeywords = ['args'];
-    protected function libKeywords($args)
-    {
-        $this->assertList($args[0]);
-
-        $keys = [];
-        $values = [];
-
-        foreach ($args[0][2] as $name => $arg) {
-            $keys[] = [Type::T_KEYWORD, $name];
-            $values[] = $arg;
-        }
-
-        return [Type::T_MAP, $keys, $values];
+        return array('map', array_merge($map1[1], $map2[1]), array_merge($map1[2], $map2[2]));
     }
 
     protected function listSeparatorForJoin($list1, $sep)
@@ -4902,7 +4160,7 @@ class Compiler
         }
     }
 
-    protected static $libJoin = ['list1', 'list2', 'separator'];
+    protected static $libJoin = array('list1', 'list2', 'separator');
     protected function libJoin($args)
     {
         list($list1, $list2, $sep) = $args;
@@ -4911,10 +4169,10 @@ class Compiler
         $list2 = $this->coerceList($list2, ' ');
         $sep = $this->listSeparatorForJoin($list1, $sep);
 
-        return [Type::T_LIST, $sep, array_merge($list1[2], $list2[2])];
+        return array('list', $sep, array_merge($list1[2], $list2[2]));
     }
 
-    protected static $libAppend = ['list', 'val', 'separator'];
+    protected static $libAppend = array('list', 'val', 'separator');
     protected function libAppend($args)
     {
         list($list1, $value, $sep) = $args;
@@ -4922,7 +4180,7 @@ class Compiler
         $list1 = $this->coerceList($list1, ' ');
         $sep = $this->listSeparatorForJoin($list1, $sep);
 
-        return [Type::T_LIST, $sep, array_merge($list1[2], [$value])];
+        return array('list', $sep, array_merge($list1[2], array($value)));
     }
 
     protected function libZip($args)
@@ -4931,11 +4189,11 @@ class Compiler
             $this->assertList($arg);
         }
 
-        $lists = [];
+        $lists = array();
         $firstList = array_shift($args);
 
         foreach ($firstList[2] as $key => $item) {
-            $list = [Type::T_LIST, '', [$item]];
+            $list = array('list', '', array($item));
 
             foreach ($args as $arg) {
                 if (isset($arg[2][$key])) {
@@ -4944,20 +4202,19 @@ class Compiler
                     break 2;
                 }
             }
-
             $lists[] = $list;
         }
 
-        return [Type::T_LIST, ',', $lists];
+        return array('list', ',', $lists);
     }
 
-    protected static $libTypeOf = ['value'];
+    protected static $libTypeOf = array('value');
     protected function libTypeOf($args)
     {
         $value = $args[0];
 
         switch ($value[0]) {
-            case Type::T_KEYWORD:
+            case 'keyword':
                 if ($value === self::$true || $value === self::$false) {
                     return 'bool';
                 }
@@ -4967,10 +4224,10 @@ class Compiler
                 }
 
                 // fall-thru
-            case Type::T_FUNCTION:
+            case 'function':
                 return 'string';
 
-            case Type::T_LIST:
+            case 'list':
                 if (isset($value[3]) && $value[3]) {
                     return 'arglist';
                 }
@@ -4981,46 +4238,42 @@ class Compiler
         }
     }
 
-    protected static $libUnit = ['number'];
+    protected static $libUnit = array('number');
     protected function libUnit($args)
     {
         $num = $args[0];
 
-        if ($num[0] === Type::T_NUMBER) {
-            return [Type::T_STRING, '"', [$num->unitStr()]];
+        if ($num[0] === 'number') {
+            return array('string', '"', array($num[2]));
         }
 
         return '';
     }
 
-    protected static $libUnitless = ['number'];
+    protected static $libUnitless = array('number');
     protected function libUnitless($args)
     {
         $value = $args[0];
 
-        return $value[0] === Type::T_NUMBER && $value->unitless();
+        return $value[0] === 'number' && empty($value[2]);
     }
 
-    protected static $libComparable = ['number-1', 'number-2'];
+    protected static $libComparable = array('number-1', 'number-2');
     protected function libComparable($args)
     {
         list($number1, $number2) = $args;
 
-        if (! isset($number1[0]) || $number1[0] !== Type::T_NUMBER ||
-            ! isset($number2[0]) || $number2[0] !== Type::T_NUMBER
-        ) {
+        if (! isset($number1[0]) || $number1[0] !== 'number' || ! isset($number2[0]) || $number2[0] !== 'number') {
             $this->throwError('Invalid argument(s) for "comparable"');
-
-            return;
         }
 
-        $number1 = $number1->normalize();
-        $number2 = $number2->normalize();
+        $number1 = $this->normalizeNumber($number1);
+        $number2 = $this->normalizeNumber($number2);
 
-        return $number1[2] === $number2[2] || $number1->unitless() || $number2->unitless();
+        return $number1[2] === $number2[2] || $number1[2] === '' || $number2[2] === '';
     }
 
-    protected static $libStrIndex = ['string', 'substring'];
+    protected static $libStrIndex = array('string', 'substring');
     protected function libStrIndex($args)
     {
         $string = $this->coerceString($args[0]);
@@ -5031,10 +4284,10 @@ class Compiler
 
         $result = strpos($stringContent, $substringContent);
 
-        return $result === false ? self::$null : new Node\Number($result + 1, '');
+        return $result === false ? self::$null : array('number', $result + 1, '');
     }
 
-    protected static $libStrInsert = ['string', 'insert', 'index'];
+    protected static $libStrInsert = array('string', 'insert', 'index');
     protected function libStrInsert($args)
     {
         $string = $this->coerceString($args[0]);
@@ -5045,80 +4298,74 @@ class Compiler
 
         list(, $index) = $args[2];
 
-        $string[2] = [substr_replace($stringContent, $insertContent, $index - 1, 0)];
+        $string[2] = array(substr_replace($stringContent, $insertContent, $index - 1, 0));
 
         return $string;
     }
 
-    protected static $libStrLength = ['string'];
+    protected static $libStrLength = array('string');
     protected function libStrLength($args)
     {
         $string = $this->coerceString($args[0]);
         $stringContent = $this->compileStringContent($string);
 
-        return new Node\Number(strlen($stringContent), '');
+        return array('number', strlen($stringContent), '');
     }
 
-    protected static $libStrSlice = ['string', 'start-at', 'end-at'];
+    protected static $libStrSlice = array('string', 'start-at', 'end-at');
     protected function libStrSlice($args)
     {
-        if (isset($args[2]) && $args[2][1] == 0) {
-            return self::$nullString;
+        if ($args[2][1] == 0) {
+            return self::$emptyString;
         }
 
         $string = $this->coerceString($args[0]);
         $stringContent = $this->compileStringContent($string);
 
-        $start = (int) $args[1][1];
+        $start = (int) $args[1][1] ?: 1;
+        $end = (int) $args[2][1];
 
-        if ($start > 0) {
-            $start--;
-        }
-
-        $end    = (int) $args[2][1];
-        $length = $end < 0 ? $end + 1 : ($end > 0 ? $end - $start : $end);
-
-        $string[2] = $length
-            ? [substr($stringContent, $start, $length)]
-            : [substr($stringContent, $start)];
+        $string[2] = array(substr($stringContent, $start - 1, $end < 0 ? $end : $end - $start + 1));
 
         return $string;
     }
 
-    protected static $libToLowerCase = ['string'];
+    protected static $libToLowerCase = array('string');
     protected function libToLowerCase($args)
     {
         $string = $this->coerceString($args[0]);
         $stringContent = $this->compileStringContent($string);
 
-        $string[2] = [mb_strtolower($stringContent)];
+        $string[2] = array(mb_strtolower($stringContent));
 
         return $string;
     }
 
-    protected static $libToUpperCase = ['string'];
+    protected static $libToUpperCase = array('string');
     protected function libToUpperCase($args)
     {
         $string = $this->coerceString($args[0]);
         $stringContent = $this->compileStringContent($string);
 
-        $string[2] = [mb_strtoupper($stringContent)];
+        $string[2] = array(mb_strtoupper($stringContent));
 
         return $string;
     }
 
-    protected static $libFeatureExists = ['feature'];
+    protected static $libFeatureExists = array('feature');
     protected function libFeatureExists($args)
     {
-        $string = $this->coerceString($args[0]);
-        $name = $this->compileStringContent($string);
-
-        return $this->toBool(
-            array_key_exists($name, $this->registeredFeatures) ? $this->registeredFeatures[$name] : false
-        );
+        /*
+         * The following features not not (yet) supported:
+         * - global-variable-shadowing
+         * - extend-selector-pseudoclass
+         * - units-level-3
+         * - at-error
+         */
+        return self::$false;
     }
 
-    protected static $libFunctionExists = ['name'];
+    protected static $libFunctionExists = array('name');
     protected function libFunctionExists($args)
     {
         $string = $this->coerceString($args[0]);
@@ -5126,13 +4373,13 @@ class Compiler
 
         // user defined functions
         if ($this->has(self::$namespaces['function'] . $name)) {
-            return true;
+            return self::$true;
         }
 
         $name = $this->normalizeName($name);
 
         if (isset($this->userFunctions[$name])) {
-            return true;
+            return self::$true;
         }
 
         // built-in functions
@@ -5141,31 +4388,31 @@ class Compiler
         return $this->toBool(is_callable($f));
     }
 
-    protected static $libGlobalVariableExists = ['name'];
+    protected static $libGlobalVariableExists = array('name');
     protected function libGlobalVariableExists($args)
     {
         $string = $this->coerceString($args[0]);
         $name = $this->compileStringContent($string);
 
-        return $this->has($name, $this->rootEnv);
+        return $this->has($name, $this->rootEnv) ? self::$true : self::$false;
     }
 
-    protected static $libMixinExists = ['name'];
+    protected static $libMixinExists = array('name');
     protected function libMixinExists($args)
     {
         $string = $this->coerceString($args[0]);
         $name = $this->compileStringContent($string);
 
-        return $this->has(self::$namespaces['mixin'] . $name);
+        return $this->has(self::$namespaces['mixin'] . $name) ? self::$true : self::$false;
     }
 
-    protected static $libVariableExists = ['name'];
+    protected static $libVariableExists = array('name');
     protected function libVariableExists($args)
     {
         $string = $this->coerceString($args[0]);
         $name = $this->compileStringContent($string);
 
-        return $this->has($name);
+        return $this->has($name) ? self::$true : self::$false;
     }
 
     /**
@@ -5175,12 +4422,11 @@ class Compiler
      */
     protected function libCounter($args)
     {
-        $list = array_map([$this, 'compileValue'], $args);
+        $list = array_map(array($this, 'compileValue'), $args);
 
-        return [Type::T_STRING, '', ['counter(' . implode(',', $list) . ')']];
+        return array('string', '', array('counter(' . implode(',', $list) . ')'));
     }
 
-    protected static $libRandom = ['limit'];
     protected function libRandom($args)
     {
         if (isset($args[0])) {
@@ -5188,14 +4434,12 @@ class Compiler
 
             if ($n < 1) {
                 $this->throwError("limit must be greater than or equal to 1");
-
-                return;
             }
 
-            return new Node\Number(mt_rand(1, $n), '');
+            return array('number', mt_rand(1, $n), '');
         }
 
-        return new Node\Number(mt_rand(1, mt_getrandmax()), '');
+        return array('number', mt_rand(1, mt_getrandmax()), '');
     }
 
     protected function libUniqueId()
@@ -5208,16 +4452,6 @@ class Compiler
 
         $id += mt_rand(0, 10) + 1;
 
-        return [Type::T_STRING, '', ['u' . str_pad(base_convert($id, 10, 36), 8, '0', STR_PAD_LEFT)]];
-    }
-
-    protected static $libInspect = ['value'];
-    protected function libInspect($args)
-    {
-        if ($args[0] === self::$null) {
-            return [Type::T_KEYWORD, 'null'];
-        }
-
-        return $args[0];
+        return array('string', '', array('u' . str_pad(base_convert($id, 10, 36), 8, '0', STR_PAD_LEFT)));
     }
 }
diff --git a/wcfsetup/install/files/lib/system/style/scssphp/src/Compiler/Environment.php b/wcfsetup/install/files/lib/system/style/scssphp/src/Compiler/Environment.php
deleted file mode 100644 (file)
index d44bff7..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-<?php
-/**
- * SCSSPHP
- *
- * @copyright 2012-2015 Leaf Corcoran
- *
- * @license http://opensource.org/licenses/MIT MIT
- *
- * @link http://leafo.github.io/scssphp
- */
-
-namespace Leafo\ScssPhp\Compiler;
-
-/**
- * Compiler environment
- *
- * @author Anthon Pang <anthon.pang@gmail.com>
- */
-class Environment
-{
-    /**
-     * @var \Leafo\ScssPhp\Block
-     */
-    public $block;
-
-    /**
-     * @var \Leafo\ScssPhp\Compiler\Environment
-     */
-    public $parent;
-
-    /**
-     * @var array
-     */
-    public $store;
-
-    /**
-     * @var integer
-     */
-    public $depth;
-}
diff --git a/wcfsetup/install/files/lib/system/style/scssphp/src/Exception/CompilerException.php b/wcfsetup/install/files/lib/system/style/scssphp/src/Exception/CompilerException.php
deleted file mode 100644 (file)
index 777e40a..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-<?php
-/**
- * SCSSPHP
- *
- * @copyright 2012-2015 Leaf Corcoran
- *
- * @license http://opensource.org/licenses/MIT MIT
- *
- * @link http://leafo.github.io/scssphp
- */
-
-namespace Leafo\ScssPhp\Exception;
-
-/**
- * Compiler exception
- *
- * @author Oleksandr Savchenko <traveltino@gmail.com>
- */
-class CompilerException extends \Exception
-{
-}
diff --git a/wcfsetup/install/files/lib/system/style/scssphp/src/Exception/ParserException.php b/wcfsetup/install/files/lib/system/style/scssphp/src/Exception/ParserException.php
deleted file mode 100644 (file)
index fbe6388..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-<?php
-/**
- * SCSSPHP
- *
- * @copyright 2012-2015 Leaf Corcoran
- *
- * @license http://opensource.org/licenses/MIT MIT
- *
- * @link http://leafo.github.io/scssphp
- */
-
-namespace Leafo\ScssPhp\Exception;
-
-/**
- * Parser Exception
- *
- * @author Oleksandr Savchenko <traveltino@gmail.com>
- */
-class ParserException extends \Exception
-{
-}
diff --git a/wcfsetup/install/files/lib/system/style/scssphp/src/Exception/ServerException.php b/wcfsetup/install/files/lib/system/style/scssphp/src/Exception/ServerException.php
deleted file mode 100644 (file)
index 5a878d2..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-<?php
-/**
- * SCSSPHP
- *
- * @copyright 2012-2015 Leaf Corcoran
- *
- * @license http://opensource.org/licenses/MIT MIT
- *
- * @link http://leafo.github.io/scssphp
- */
-
-namespace Leafo\ScssPhp\Exception;
-
-/**
- * Server Exception
- *
- * @author Anthon Pang <anthon.pang@gmail.com>
- */
-class ServerException extends \Exception
-{
-}
index 2770bb2daa037f5fcd2d2a4b48838473e9989a68..5b035e1c2b0088a07fea808ea66fe9886386c80c 100644 (file)
 
 namespace Leafo\ScssPhp;
 
-use Leafo\ScssPhp\Formatter\OutputBlock;
-
 /**
- * Base formatter
+ * SCSS base formatter
  *
  * @author Leaf Corcoran <leafot@gmail.com>
  */
@@ -55,33 +53,23 @@ abstract class Formatter
      */
     public $assignSeparator;
 
-    /**
-     * @var boolea
-     */
-    public $keepSemicolons;
-
-    /**
-     * Initialize formatter
-     *
-     * @api
-     */
     abstract public function __construct();
 
     /**
      * Return indentation (whitespace)
      *
+     * @param integer $n
+     *
      * @return string
      */
-    protected function indentStr()
+    protected function indentStr($n = 0)
     {
-        return '';
+        return str_repeat($this->indentChar, max($this->indentLevel + $n, 0));
     }
 
     /**
      * Return property assignment
      *
-     * @api
-     *
      * @param string $name
      * @param mixed  $value
      *
@@ -95,34 +83,21 @@ abstract class Formatter
     /**
      * Strip semi-colon appended by property(); it's a separator, not a terminator
      *
-     * @api
-     *
      * @param array $lines
      */
     public function stripSemicolon(&$lines)
     {
-        if ($this->keepSemicolons) {
-            return;
-        }
-
-        if (($count = count($lines))
-            && substr($lines[$count - 1], -1) === ';'
-        ) {
-            $lines[$count - 1] = substr($lines[$count - 1], 0, -1);
-        }
     }
 
     /**
      * Output lines inside a block
      *
-     * @param \Leafo\ScssPhp\Formatter\OutputBlock $block
+     * @param string    $inner
+     * @param \stdClass $block
      */
-    protected function blockLines(OutputBlock $block)
+    protected function blockLines($inner, $block)
     {
-        $inner = $this->indentStr();
-
         $glue = $this->break . $inner;
-
         echo $inner . implode($glue, $block->lines);
 
         if (! empty($block->children)) {
@@ -130,57 +105,35 @@ abstract class Formatter
         }
     }
 
-    /**
-     * Output block selectors
-     *
-     * @param \Leafo\ScssPhp\Formatter\OutputBlock $block
-     */
-    protected function blockSelectors(OutputBlock $block)
-    {
-        $inner = $this->indentStr();
-
-        echo $inner
-            . implode($this->tagSeparator, $block->selectors)
-            . $this->open . $this->break;
-    }
-
-    /**
-     * Output block children
-     *
-     * @param \Leafo\ScssPhp\Formatter\OutputBlock $block
-     */
-    protected function blockChildren(OutputBlock $block)
-    {
-        foreach ($block->children as $child) {
-            $this->block($child);
-        }
-    }
-
     /**
      * Output non-empty block
      *
-     * @param \Leafo\ScssPhp\Formatter\OutputBlock $block
+     * @param \stdClass $block
      */
-    protected function block(OutputBlock $block)
+    protected function block($block)
     {
         if (empty($block->lines) && empty($block->children)) {
             return;
         }
 
-        $pre = $this->indentStr();
+        $inner = $pre = $this->indentStr();
 
         if (! empty($block->selectors)) {
-            $this->blockSelectors($block);
+            echo $pre
+                . implode($this->tagSeparator, $block->selectors)
+                . $this->open . $this->break;
 
             $this->indentLevel++;
+
+            $inner = $this->indentStr();
         }
 
         if (! empty($block->lines)) {
-            $this->blockLines($block);
+            $this->blockLines($inner, $block);
         }
 
-        if (! empty($block->children)) {
-            $this->blockChildren($block);
+        foreach ($block->children as $child) {
+            $this->block($child);
         }
 
         if (! empty($block->selectors)) {
@@ -197,13 +150,11 @@ abstract class Formatter
     /**
      * Entry point to formatting a block
      *
-     * @api
-     *
-     * @param \Leafo\ScssPhp\Formatter\OutputBlock $block An abstract syntax tree
+     * @param \stdClass $block An abstract syntax tree
      *
      * @return string
      */
-    public function format(OutputBlock $block)
+    public function format($block)
     {
         ob_start();
 
index 94abe329504c1974f5a4c08712e212845f5e5a0b..29874c96c2e2c63e75615ef5e589c894784fe9e5 100644 (file)
@@ -14,7 +14,7 @@ namespace Leafo\ScssPhp\Formatter;
 use Leafo\ScssPhp\Formatter;
 
 /**
- * Compact formatter
+ * SCSS compact formatter
  *
  * @author Leaf Corcoran <leafot@gmail.com>
  */
@@ -32,13 +32,12 @@ class Compact extends Formatter
         $this->close = "}\n\n";
         $this->tagSeparator = ',';
         $this->assignSeparator = ':';
-        $this->keepSemicolons = true;
     }
 
     /**
      * {@inheritdoc}
      */
-    public function indentStr()
+    public function indentStr($n = 0)
     {
         return ' ';
     }
index df7bc75fb38ac3bb2f68a2eaea30fd0dc33841e9..c95b68b3246c5b873edc65a1f81eddb0e7e70b83 100644 (file)
 namespace Leafo\ScssPhp\Formatter;
 
 use Leafo\ScssPhp\Formatter;
-use Leafo\ScssPhp\Formatter\OutputBlock;
 
 /**
- * Compressed formatter
+ * SCSS compressed formatter
  *
  * @author Leaf Corcoran <leafot@gmail.com>
  */
@@ -33,16 +32,33 @@ class Compressed extends Formatter
         $this->close = '}';
         $this->tagSeparator = ',';
         $this->assignSeparator = ':';
-        $this->keepSemicolons = false;
     }
 
     /**
      * {@inheritdoc}
      */
-    public function blockLines(OutputBlock $block)
+    public function indentStr($n = 0)
     {
-        $inner = $this->indentStr();
+        return '';
+    }
 
+    /**
+     * {@inheritdoc}
+     */
+    public function stripSemicolon(&$lines)
+    {
+        if (($count = count($lines))
+            && substr($lines[$count - 1], -1) === ';'
+        ) {
+            $lines[$count - 1] = substr($lines[$count - 1], 0, -1);
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function blockLines($inner, $block)
+    {
         $glue = $this->break . $inner;
 
         foreach ($block->lines as $index => $line) {
@@ -59,4 +75,15 @@ class Compressed extends Formatter
             echo $this->break;
         }
     }
+
+    /**
+     * {@inherit}
+     */
+    public function format($block)
+    {
+        return parent::format($block);
+
+        // TODO: we need to fix the 2 "compressed" tests where the "close" is applied
+        return trim(str_replace(';}', '}', parent::format($block)));
+    }
 }
index ccba1333bf6d6fccce73ea7b069fad6f2277e599..1227c12279c3a925adf422dff01f1b4fa2332729 100644 (file)
 namespace Leafo\ScssPhp\Formatter;
 
 use Leafo\ScssPhp\Formatter;
-use Leafo\ScssPhp\Formatter\OutputBlock;
 
 /**
- * Crunched formatter
+ * SCSS crunched formatter
  *
  * @author Anthon Pang <anthon.pang@gmail.com>
  */
@@ -33,16 +32,33 @@ class Crunched extends Formatter
         $this->close = '}';
         $this->tagSeparator = ',';
         $this->assignSeparator = ':';
-        $this->keepSemicolons = false;
     }
 
     /**
      * {@inheritdoc}
      */
-    public function blockLines(OutputBlock $block)
+    public function indentStr($n = 0)
     {
-        $inner = $this->indentStr();
+        return '';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function stripSemicolon(&$lines)
+    {
+        if (($count = count($lines))
+            && substr($lines[$count - 1], -1) === ';'
+        ) {
+            $lines[$count - 1] = substr($lines[$count - 1], 0, -1);
+        }
+    }
 
+    /**
+     * {@inheritdoc}
+     */
+    public function blockLines($inner, $block)
+    {
         $glue = $this->break . $inner;
 
         foreach ($block->lines as $index => $line) {
diff --git a/wcfsetup/install/files/lib/system/style/scssphp/src/Formatter/Debug.php b/wcfsetup/install/files/lib/system/style/scssphp/src/Formatter/Debug.php
deleted file mode 100644 (file)
index 855742e..0000000
+++ /dev/null
@@ -1,119 +0,0 @@
-<?php
-/**
- * SCSSPHP
- *
- * @copyright 2012-2015 Leaf Corcoran
- *
- * @license http://opensource.org/licenses/MIT MIT
- *
- * @link http://leafo.github.io/scssphp
- */
-
-namespace Leafo\ScssPhp\Formatter;
-
-use Leafo\ScssPhp\Formatter;
-use Leafo\ScssPhp\Formatter\OutputBlock;
-
-/**
- * Debug formatter
- *
- * @author Anthon Pang <anthon.pang@gmail.com>
- */
-class Debug extends Formatter
-{
-    /**
-     * {@inheritdoc}
-     */
-    public function __construct()
-    {
-        $this->indentLevel = 0;
-        $this->indentChar = '';
-        $this->break = "\n";
-        $this->open = ' {';
-        $this->close = ' }';
-        $this->tagSeparator = ', ';
-        $this->assignSeparator = ': ';
-        $this->keepSemicolons = true;
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    protected function indentStr()
-    {
-        return str_repeat('  ', $this->indentLevel);
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    protected function blockLines(OutputBlock $block)
-    {
-        $indent = $this->indentStr();
-
-        if (empty($block->lines)) {
-            echo "{$indent}block->lines: []\n";
-
-            return;
-        }
-
-        foreach ($block->lines as $index => $line) {
-            echo "{$indent}block->lines[{$index}]: $line\n";
-        }
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    protected function blockSelectors(OutputBlock $block)
-    {
-        $indent = $this->indentStr();
-
-        if (empty($block->selectors)) {
-            echo "{$indent}block->selectors: []\n";
-
-            return;
-        }
-
-        foreach ($block->selectors as $index => $selector) {
-            echo "{$indent}block->selectors[{$index}]: $selector\n";
-        }
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    protected function blockChildren(OutputBlock $block)
-    {
-        $indent = $this->indentStr();
-
-        if (empty($block->children)) {
-            echo "{$indent}block->children: []\n";
-
-            return;
-        }
-
-        $this->indentLevel++;
-
-        foreach ($block->children as $i => $child) {
-            $this->block($child);
-        }
-
-        $this->indentLevel--;
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    protected function block(OutputBlock $block)
-    {
-        $indent = $this->indentStr();
-
-        echo "{$indent}block->type: {$block->type}\n" .
-             "{$indent}block->depth: {$block->depth}\n";
-
-        $this->blockSelectors($block);
-        $this->blockLines($block);
-        $this->blockChildren($block);
-    }
-}
index 54db742ffdb874cb23d4b70b9307d6f190e38ad5..fbf85c1e1875af7f9bac917e30e5e92a5817afe0 100644 (file)
 namespace Leafo\ScssPhp\Formatter;
 
 use Leafo\ScssPhp\Formatter;
-use Leafo\ScssPhp\Formatter\OutputBlock;
 
 /**
- * Expanded formatter
+ * SCSS expanded formatter
  *
  * @author Leaf Corcoran <leafot@gmail.com>
  */
@@ -33,24 +32,13 @@ class Expanded extends Formatter
         $this->close = '}';
         $this->tagSeparator = ', ';
         $this->assignSeparator = ': ';
-        $this->keepSemicolons = true;
     }
 
     /**
      * {@inheritdoc}
      */
-    protected function indentStr()
+    protected function blockLines($inner, $block)
     {
-        return str_repeat($this->indentChar, $this->indentLevel);
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    protected function blockLines(OutputBlock $block)
-    {
-        $inner = $this->indentStr();
-
         $glue = $this->break . $inner;
 
         foreach ($block->lines as $index => $line) {
index 9fdb4dd0ab6b10a34932b2fa1928b349d1723014..bd2c4d213645b4f60b869ddd6acb1883f367ebe8 100644 (file)
 namespace Leafo\ScssPhp\Formatter;
 
 use Leafo\ScssPhp\Formatter;
-use Leafo\ScssPhp\Formatter\OutputBlock;
 
 /**
- * Nested formatter
+ * SCSS nested formatter
  *
  * @author Leaf Corcoran <leafot@gmail.com>
  */
 class Nested extends Formatter
 {
-    /**
-     * @var integer
-     */
-    private $depth;
-
     /**
      * {@inheritdoc}
      */
@@ -38,26 +32,55 @@ class Nested extends Formatter
         $this->close = ' }';
         $this->tagSeparator = ', ';
         $this->assignSeparator = ': ';
-        $this->keepSemicolons = true;
     }
 
     /**
-     * {@inheritdoc}
+     * Adjust the depths of all children, depth first
+     *
+     * @param \stdClass $block
      */
-    protected function indentStr()
+    public function adjustAllChildren($block)
     {
-        $n = $this->depth - 1;
+        // flatten empty nested blocks
+        $children = array();
+        foreach ($block->children as $i => $child) {
+            if (empty($child->lines) && empty($child->children)) {
+                if (isset($block->children[$i + 1])) {
+                    $block->children[$i + 1]->depth = $child->depth;
+                }
+                continue;
+            }
+            $children[] = $child;
+        }
+
+        $count = count($children);
+        for ($i = 0; $i < $count; $i++) {
+            $depth = $children[$i]->depth;
+            $j = $i + 1;
+            if (isset($children[$j]) && $depth < $children[$j]->depth) {
+                $childDepth = $children[$j]->depth;
+                for (; $j < $count; $j++) {
+                    if ($depth < $children[$j]->depth && $childDepth >= $children[$j]->depth) {
+                        $children[$j]->depth = $depth + 1;
+                    }
+                }
+            }
+        }
+
+        $block->children = $children;
 
-        return str_repeat($this->indentChar, max($this->indentLevel + $n, 0));
+        // make relative to parent
+        foreach ($block->children as $child) {
+            $this->adjustAllChildren($child);
+            $child->depth = $child->depth - $block->depth;
+        }
     }
 
     /**
      * {@inheritdoc}
      */
-    protected function blockLines(OutputBlock $block)
+    protected function blockLines($inner, $block)
     {
-        $inner = $this->indentStr();
-
         $glue = $this->break . $inner;
 
         foreach ($block->lines as $index => $line) {
@@ -76,20 +99,25 @@ class Nested extends Formatter
     /**
      * {@inheritdoc}
      */
-    protected function blockSelectors(OutputBlock $block)
+    protected function block($block)
     {
-        $inner = $this->indentStr();
+        if ($block->type === 'root') {
+            $this->adjustAllChildren($block);
+        }
 
-        echo $inner
-            . implode($this->tagSeparator, $block->selectors)
-            . $this->open . $this->break;
-    }
+        $inner = $pre = $this->indentStr($block->depth - 1);
+        if (! empty($block->selectors)) {
+            echo $pre .
+                implode($this->tagSeparator, $block->selectors) .
+                $this->open . $this->break;
+            $this->indentLevel++;
+            $inner = $this->indentStr($block->depth - 1);
+        }
+
+        if (! empty($block->lines)) {
+            $this->blockLines($inner, $block);
+        }
 
-    /**
-     * {@inheritdoc}
-     */
-    protected function blockChildren(OutputBlock $block)
-    {
         foreach ($block->children as $i => $child) {
             $this->block($child);
 
@@ -98,47 +126,15 @@ class Nested extends Formatter
 
                 if (isset($block->children[$i + 1])) {
                     $next = $block->children[$i + 1];
-
                     if ($next->depth === max($block->depth, 1) && $child->depth >= $next->depth) {
                         echo $this->break;
                     }
                 }
             }
         }
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    protected function block(OutputBlock $block)
-    {
-        if ($block->type === 'root') {
-            $this->adjustAllChildren($block);
-        }
-
-        if (empty($block->lines) && empty($block->children)) {
-            return;
-        }
-
-        $this->depth = $block->depth;
-
-        if (! empty($block->selectors)) {
-            $this->blockSelectors($block);
-
-            $this->indentLevel++;
-        }
-
-        if (! empty($block->lines)) {
-            $this->blockLines($block);
-        }
-
-        if (! empty($block->children)) {
-            $this->blockChildren($block);
-        }
 
         if (! empty($block->selectors)) {
             $this->indentLevel--;
-
             echo $this->close;
         }
 
@@ -146,53 +142,4 @@ class Nested extends Formatter
             echo $this->break;
         }
     }
-
-    /**
-     * Adjust the depths of all children, depth first
-     *
-     * @param \Leafo\ScssPhp\Formatter\OutputBlock $block
-     */
-    private function adjustAllChildren(OutputBlock $block)
-    {
-        // flatten empty nested blocks
-        $children = [];
-
-        foreach ($block->children as $i => $child) {
-            if (empty($child->lines) && empty($child->children)) {
-                if (isset($block->children[$i + 1])) {
-                    $block->children[$i + 1]->depth = $child->depth;
-                }
-
-                continue;
-            }
-
-            $children[] = $child;
-        }
-
-        $count = count($children);
-
-        for ($i = 0; $i < $count; $i++) {
-            $depth = $children[$i]->depth;
-            $j = $i + 1;
-
-            if (isset($children[$j]) && $depth < $children[$j]->depth) {
-                $childDepth = $children[$j]->depth;
-
-                for (; $j < $count; $j++) {
-                    if ($depth < $children[$j]->depth && $childDepth >= $children[$j]->depth) {
-                        $children[$j]->depth = $depth + 1;
-                    }
-                }
-            }
-        }
-
-        $block->children = $children;
-
-        // make relative to parent
-        foreach ($block->children as $child) {
-            $this->adjustAllChildren($child);
-
-            $child->depth = $child->depth - $block->depth;
-        }
-    }
 }
diff --git a/wcfsetup/install/files/lib/system/style/scssphp/src/Formatter/OutputBlock.php b/wcfsetup/install/files/lib/system/style/scssphp/src/Formatter/OutputBlock.php
deleted file mode 100644 (file)
index bb8d99b..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-<?php
-/**
- * SCSSPHP
- *
- * @copyright 2012-2015 Leaf Corcoran
- *
- * @license http://opensource.org/licenses/MIT MIT
- *
- * @link http://leafo.github.io/scssphp
- */
-
-namespace Leafo\ScssPhp\Formatter;
-
-/**
- * Output block
- *
- * @author Anthon Pang <anthon.pang@gmail.com>
- */
-class OutputBlock
-{
-    /**
-     * @var string
-     */
-    public $type;
-
-    /**
-     * @var integer
-     */
-    public $depth;
-
-    /**
-     * @var array
-     */
-    public $selectors;
-
-    /**
-     * @var array
-     */
-    public $lines;
-
-    /**
-     * @var array
-     */
-    public $children;
-
-    /**
-     * @var \Leafo\ScssPhp\Formatter\OutputBlock
-     */
-    public $parent;
-}
diff --git a/wcfsetup/install/files/lib/system/style/scssphp/src/Node.php b/wcfsetup/install/files/lib/system/style/scssphp/src/Node.php
deleted file mode 100644 (file)
index e6ed178..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-<?php
-/**
- * SCSSPHP
- *
- * @copyright 2012-2015 Leaf Corcoran
- *
- * @license http://opensource.org/licenses/MIT MIT
- *
- * @link http://leafo.github.io/scssphp
- */
-
-namespace Leafo\ScssPhp;
-
-/**
- * Base node
- *
- * @author Anthon Pang <anthon.pang@gmail.com>
- */
-abstract class Node
-{
-    /**
-     * @var string
-     */
-    public $type;
-
-    /**
-     * @var integer
-     */
-    public $sourceIndex;
-
-    /**
-     * @var integer
-     */
-    public $sourceLine;
-
-    /**
-     * @var integer
-     */
-    public $sourceColumn;
-}
diff --git a/wcfsetup/install/files/lib/system/style/scssphp/src/Node/Number.php b/wcfsetup/install/files/lib/system/style/scssphp/src/Node/Number.php
deleted file mode 100644 (file)
index a803a6c..0000000
+++ /dev/null
@@ -1,329 +0,0 @@
-<?php
-/**
- * SCSSPHP
- *
- * @copyright 2012-2015 Leaf Corcoran
- *
- * @license http://opensource.org/licenses/MIT MIT
- *
- * @link http://leafo.github.io/scssphp
- */
-
-namespace Leafo\ScssPhp\Node;
-
-use Leafo\ScssPhp\Compiler;
-use Leafo\ScssPhp\Node;
-use Leafo\ScssPhp\Type;
-
-/**
- * Dimension + optional units
- *
- * {@internal
- *     This is a work-in-progress.
- *
- *     The \ArrayAccess interface is temporary until the migration is complete.
- * }}
- *
- * @author Anthon Pang <anthon.pang@gmail.com>
- */
-class Number extends Node implements \ArrayAccess
-{
-    /**
-     * @var integer
-     */
-    static public $precision = 5;
-
-    /**
-     * @see http://www.w3.org/TR/2012/WD-css3-values-20120308/
-     *
-     * @var array
-     */
-    static protected $unitTable = [
-        'in' => [
-            'in' => 1,
-            'pc' => 6,
-            'pt' => 72,
-            'px' => 96,
-            'cm' => 2.54,
-            'mm' => 25.4,
-            'q'  => 101.6,
-        ],
-        'turn' => [
-            'deg'  => 360,
-            'grad' => 400,
-            'rad'  => 6.28318530717958647692528676, // 2 * M_PI
-            'turn' => 1,
-        ],
-        's' => [
-            's'  => 1,
-            'ms' => 1000,
-        ],
-        'Hz' => [
-            'Hz'  => 1,
-            'kHz' => 0.001,
-        ],
-        'dpi' => [
-            'dpi'  => 1,
-            'dpcm' => 2.54,
-            'dppx' => 96,
-        ],
-    ];
-
-    /**
-     * @var integer|float
-     */
-    public $dimension;
-
-    /**
-     * @var array
-     */
-    public $units;
-
-    /**
-     * Initialize number
-     *
-     * @param mixed $dimension
-     * @param mixed $initialUnit
-     */
-    public function __construct($dimension, $initialUnit)
-    {
-        $this->type      = Type::T_NUMBER;
-        $this->dimension = $dimension;
-        $this->units     = is_array($initialUnit)
-            ? $initialUnit
-            : ($initialUnit ? [$initialUnit => 1]
-                            : []);
-    }
-
-    /**
-     * Coerce number to target units
-     *
-     * @param array $units
-     *
-     * @return \Leafo\ScssPhp\Node\Number
-     */
-    public function coerce($units)
-    {
-        if ($this->unitless()) {
-            return new Number($this->dimension, $units);
-        }
-
-        $dimension = $this->dimension;
-
-        foreach (self::$unitTable['in'] as $unit => $conv) {
-            $from       = isset($this->units[$unit]) ? $this->units[$unit] : 0;
-            $to         = isset($units[$unit]) ? $units[$unit] : 0;
-            $factor     = pow($conv, $from - $to);
-            $dimension /= $factor;
-        }
-
-        return new Number($dimension, $units);
-    }
-
-    /**
-     * Normalize number
-     *
-     * @return \Leafo\ScssPhp\Node\Number
-     */
-    public function normalize()
-    {
-        $dimension = $this->dimension;
-        $units     = [];
-
-        $this->normalizeUnits($dimension, $units, 'in');
-
-        return new Number($dimension, $units);
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function offsetExists($offset)
-    {
-        if ($offset === -3) {
-            return $this->sourceColumn !== null;
-        }
-
-        if ($offset === -2) {
-            return $this->sourceLine !== null;
-        }
-
-        if ($offset === -1
-            || $offset === 0
-            || $offset === 1
-            || $offset === 2
-        ) {
-            return true;
-        }
-
-        return false;
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function offsetGet($offset)
-    {
-        switch ($offset) {
-            case -3:
-                return $this->sourceColumn;
-
-            case -2:
-                return $this->sourceLine;
-
-            case -1:
-                return $this->sourceIndex;
-
-            case 0:
-                return $this->type;
-
-            case 1:
-                return $this->dimension;
-
-            case 2:
-                return $this->units;
-        }
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function offsetSet($offset, $value)
-    {
-        if ($offset === 1) {
-            $this->dimension = $value;
-        } elseif ($offset === 2) {
-            $this->units = $value;
-        } elseif ($offset == -1) {
-            $this->sourceIndex = $value;
-        } elseif ($offset == -2) {
-            $this->sourceLine = $value;
-        } elseif ($offset == -3) {
-            $this->sourceColumn = $value;
-        }
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function offsetUnset($offset)
-    {
-        if ($offset === 1) {
-            $this->dimension = null;
-        } elseif ($offset === 2) {
-            $this->units = null;
-        } elseif ($offset === -1) {
-            $this->sourceIndex = null;
-        } elseif ($offset === -2) {
-            $this->sourceLine = null;
-        } elseif ($offset === -3) {
-            $this->sourceColumn = null;
-        }
-    }
-
-    /**
-     * Returns true if the number is unitless
-     *
-     * @return boolean
-     */
-    public function unitless()
-    {
-        return ! array_sum($this->units);
-    }
-
-    /**
-     * Returns unit(s) as the product of numerator units divided by the product of denominator units
-     *
-     * @return string
-     */
-    public function unitStr()
-    {
-        $numerators   = [];
-        $denominators = [];
-
-        foreach ($this->units as $unit => $unitSize) {
-            if ($unitSize > 0) {
-                $numerators = array_pad($numerators, count($numerators) + $unitSize, $unit);
-                continue;
-            }
-
-            if ($unitSize < 0) {
-                $denominators = array_pad($denominators, count($denominators) + $unitSize, $unit);
-                continue;
-            }
-        }
-
-        return implode('*', $numerators) . (count($denominators) ? '/' . implode('*', $denominators) : '');
-    }
-
-    /**
-     * Output number
-     *
-     * @param \Leafo\ScssPhp\Compiler $compiler
-     *
-     * @return string
-     */
-    public function output(Compiler $compiler = null)
-    {
-        $dimension = round($this->dimension, self::$precision);
-
-        $units = array_filter($this->units, function ($unitSize) {
-            return $unitSize;
-        });
-
-        if (count($units) > 1 && array_sum($units) === 0) {
-            $dimension = $this->dimension;
-            $units     = [];
-
-            $this->normalizeUnits($dimension, $units, 'in');
-
-            $dimension = round($dimension, self::$precision);
-            $units     = array_filter($units, function ($unitSize) {
-                return $unitSize;
-            });
-        }
-
-        $unitSize = array_sum($units);
-
-        if ($compiler && ($unitSize > 1 || $unitSize < 0 || count($units) > 1)) {
-            $compiler->throwError((string) $dimension . $this->unitStr() . " isn't a valid CSS value.");
-        }
-
-        reset($units);
-        list($unit, ) = each($units);
-
-        return (string) $dimension . $unit;
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function __toString()
-    {
-        return $this->output();
-    }
-
-    /**
-     * Normalize units
-     *
-     * @param integer|float $dimension
-     * @param array         $units
-     * @param string        $baseUnit
-     */
-    private function normalizeUnits(&$dimension, &$units, $baseUnit = 'in')
-    {
-        $dimension = $this->dimension;
-        $units     = [];
-
-        foreach ($this->units as $unit => $exp) {
-            if (isset(self::$unitTable[$baseUnit][$unit])) {
-                $factor = pow(self::$unitTable[$baseUnit][$unit], $exp);
-
-                $unit = $baseUnit;
-                $dimension /= $factor;
-            }
-
-            $units[$unit] = $exp + (isset($units[$unit]) ? $units[$unit] : 0);
-        }
-    }
-}
index ed0621eae1b9d5682a3753b7577a51a9ddd5e82f..8b27a27e4ec0c1e540b0fa13fab6faace3d9c572 100644 (file)
 
 namespace Leafo\ScssPhp;
 
-use Leafo\ScssPhp\Block;
 use Leafo\ScssPhp\Compiler;
-use Leafo\ScssPhp\Exception\ParserException;
-use Leafo\ScssPhp\Node;
-use Leafo\ScssPhp\Type;
 
 /**
- * Parser
+ * SCSS parser
  *
  * @author Leaf Corcoran <leafot@gmail.com>
  */
 class Parser
 {
-    const SOURCE_INDEX  = -1;
-    const SOURCE_LINE   = -2;
-    const SOURCE_COLUMN = -3;
+    const SOURCE_POSITION = -1;
+    const SOURCE_PARSER   = -2;
 
     /**
      * @var array
      */
-    protected static $precedence = [
-        '='   => 0,
-        'or'  => 1,
+    protected static $precedence = array(
+        '=' => 0,
+        'or' => 1,
         'and' => 2,
-        '=='  => 3,
-        '!='  => 3,
-        '<=>' => 3,
-        '<='  => 4,
-        '>='  => 4,
-        '<'   => 4,
-        '>'   => 4,
-        '+'   => 5,
-        '-'   => 5,
-        '*'   => 6,
-        '/'   => 6,
-        '%'   => 6,
-    ];
-
-    protected static $commentPattern;
-    protected static $operatorPattern;
+        '==' => 3,
+        '!=' => 3,
+        '<=' => 4,
+        '>=' => 4,
+        '<' => 4,
+        '>' => 4,
+        '+' => 5,
+        '-' => 5,
+        '*' => 6,
+        '/' => 6,
+        '%' => 6,
+    );
+
+    /**
+     * @var array
+     */
+    protected static $operators = array(
+        '+',
+        '-',
+        '*',
+        '/',
+        '%',
+        '==',
+        '!=',
+        '<=',
+        '>=',
+        '<',
+        '>',
+        'and',
+        'or',
+    );
+
+    protected static $operatorStr;
     protected static $whitePattern;
+    protected static $commentMulti;
+
+    protected static $commentSingle = '//';
+    protected static $commentMultiLeft = '/*';
+    protected static $commentMultiRight = '*/';
 
     private $sourceName;
-    private $sourceIndex;
-    private $sourcePositions;
+    private $rootParser;
     private $charset;
     private $count;
     private $env;
     private $inParens;
     private $eatWhiteDefault;
     private $buffer;
-    private $utf8;
-    private $encoding;
-    private $patternModifiers;
 
     /**
      * Constructor
      *
-     * @api
-     *
      * @param string  $sourceName
-     * @param integer $sourceIndex
-     * @param string  $encoding
+     * @param boolean $rootParser
      */
-    public function __construct($sourceName, $sourceIndex = 0, $encoding = 'utf-8')
+    public function __construct($sourceName = null, $rootParser = true)
     {
-        $this->sourceName       = $sourceName ?: '(stdin)';
-        $this->sourceIndex      = $sourceIndex;
-        $this->charset          = null;
-        $this->utf8             = ! $encoding || strtolower($encoding) === 'utf-8';
-        $this->patternModifiers = $this->utf8 ? 'Aisu' : 'Ais';
+        $this->sourceName = $sourceName;
+        $this->rootParser = $rootParser;
+        $this->charset = null;
 
-        if (empty(self::$operatorPattern)) {
-            self::$operatorPattern = '([*\/%+-]|[!=]\=|\>\=?|\<\=\>|\<\=?|and|or)';
+        if (empty(self::$operatorStr)) {
+            self::$operatorStr = $this->makeOperatorStr(self::$operators);
 
-            $commentSingle      = '\/\/';
-            $commentMultiLeft   = '\/\*';
-            $commentMultiRight  = '\*\/';
-
-            self::$commentPattern = $commentMultiLeft . '.*?' . $commentMultiRight;
-            self::$whitePattern = $this->utf8
-                ? '/' . $commentSingle . '[^\n]*\s*|(' . self::$commentPattern . ')\s*|\s+/AisuS'
-                : '/' . $commentSingle . '[^\n]*\s*|(' . self::$commentPattern . ')\s*|\s+/AisS';
+            $commentSingle = $this->pregQuote(self::$commentSingle);
+            $commentMultiLeft = $this->pregQuote(self::$commentMultiLeft);
+            $commentMultiRight = $this->pregQuote(self::$commentMultiRight);
+            self::$commentMulti = $commentMultiLeft . '.*?' . $commentMultiRight;
+            self::$whitePattern = '/' . $commentSingle . '[^\n]*\s*|(' . self::$commentMulti . ')\s*|\s+/Ais';
         }
     }
 
     /**
-     * Get source file name
+     * Make operator regex
      *
-     * @api
+     * @param array $operators
      *
      * @return string
      */
-    public function getSourceName()
-    {
-        return $this->sourceName;
-    }
-
-    /**
-     * Throw parser error
-     *
-     * @api
-     *
-     * @param string $msg
-     *
-     * @throws \Leafo\ScssPhp\Exception\ParserException
-     */
-    public function throwParseError($msg = 'parse error')
+    protected static function makeOperatorStr($operators)
     {
-        list($line, /* $column */) = $this->getSourcePosition($this->count);
-
-        $loc = empty($this->sourceName) ? "line: $line" : "$this->sourceName on line $line";
-
-        if ($this->peek("(.*?)(\n|$)", $m, $this->count)) {
-            throw new ParserException("$msg: failed at `$m[1]` $loc");
-        }
-
-        throw new ParserException("$msg: $loc");
+        return '('
+            . implode('|', array_map(array('Leafo\ScssPhp\Parser', 'pregQuote'), $operators))
+            . ')';
     }
 
     /**
      * Parser buffer
      *
-     * @api
+     * @param string $buffer;
      *
-     * @param string $buffer
-     *
-     * @return \Leafo\ScssPhp\Block
+     * @return \stdClass
      */
     public function parse($buffer)
     {
-        // strip BOM (byte order marker)
-        if (substr($buffer, 0, 3) === "\xef\xbb\xbf") {
-            $buffer = substr($buffer, 3);
-        }
-
-        $this->buffer          = rtrim($buffer, "\x00..\x1f");
         $this->count           = 0;
         $this->env             = null;
         $this->inParens        = false;
         $this->eatWhiteDefault = true;
-
-        $this->saveEncoding();
-        $this->extractLineNumbers($buffer);
+        $this->buffer          = $buffer;
 
         $this->pushBlock(null); // root block
+
         $this->whitespace();
         $this->pushBlock(null);
         $this->popBlock();
 
-        while ($this->parseChunk()) {
+        while (false !== $this->parseChunk()) {
             ;
         }
 
@@ -179,16 +155,12 @@ class Parser
 
         $this->env->isRoot    = true;
 
-        $this->restoreEncoding();
-
         return $this->env;
     }
 
     /**
      * Parse a value or value list
      *
-     * @api
-     *
      * @param string $buffer
      * @param string $out
      *
@@ -202,20 +174,12 @@ class Parser
         $this->eatWhiteDefault = true;
         $this->buffer          = (string) $buffer;
 
-        $this->saveEncoding();
-
-        $list = $this->valueList($out);
-
-        $this->restoreEncoding();
-
-        return $list;
+        return $this->valueList($out);
     }
 
     /**
      * Parse a selector or selector list
      *
-     * @api
-     *
      * @param string $buffer
      * @param string $out
      *
@@ -229,13 +193,7 @@ class Parser
         $this->eatWhiteDefault = true;
         $this->buffer          = (string) $buffer;
 
-        $this->saveEncoding();
-
-        $selector = $this->selectors($out);
-
-        $this->restoreEncoding();
-
-        return $selector;
+        return $this->selectors($out);
     }
 
     /**
@@ -283,22 +241,8 @@ class Parser
 
         // the directives
         if (isset($this->buffer[$this->count]) && $this->buffer[$this->count] === '@') {
-            if ($this->literal('@at-root') &&
-                ($this->selectors($selector) || true) &&
-                ($this->map($with) || true) &&
-                $this->literal('{')
-            ) {
-                $atRoot = $this->pushSpecialBlock(Type::T_AT_ROOT, $s);
-                $atRoot->selector = $selector;
-                $atRoot->with = $with;
-
-                return true;
-            }
-
-            $this->seek($s);
-
             if ($this->literal('@media') && $this->mediaQueryList($mediaQueryList) && $this->literal('{')) {
-                $media = $this->pushSpecialBlock(Type::T_MEDIA, $s);
+                $media = $this->pushSpecialBlock('media', $s);
                 $media->queryList = $mediaQueryList[2];
 
                 return true;
@@ -311,7 +255,7 @@ class Parser
                 ($this->argumentDef($args) || true) &&
                 $this->literal('{')
             ) {
-                $mixin = $this->pushSpecialBlock(Type::T_MIXIN, $s);
+                $mixin = $this->pushSpecialBlock('mixin', $s);
                 $mixin->name = $mixinName;
                 $mixin->args = $args;
 
@@ -328,10 +272,11 @@ class Parser
                 ($this->end() ||
                     $this->literal('{') && $hasBlock = true)
             ) {
-                $child = [Type::T_INCLUDE, $mixinName, isset($argValues) ? $argValues : null, null];
+                $child = array('include',
+                    $mixinName, isset($argValues) ? $argValues : null, null);
 
                 if (! empty($hasBlock)) {
-                    $include = $this->pushSpecialBlock(Type::T_INCLUDE, $s);
+                    $include = $this->pushSpecialBlock('include', $s);
                     $include->child = $child;
                 } else {
                     $this->append($child, $s);
@@ -342,22 +287,11 @@ class Parser
 
             $this->seek($s);
 
-            if ($this->literal('@scssphp-import-once') &&
-                $this->valueList($importPath) &&
-                $this->end()
-            ) {
-                $this->append([Type::T_SCSSPHP_IMPORT_ONCE, $importPath], $s);
-
-                return true;
-            }
-
-            $this->seek($s);
-
             if ($this->literal('@import') &&
                 $this->valueList($importPath) &&
                 $this->end()
             ) {
-                $this->append([Type::T_IMPORT, $importPath], $s);
+                $this->append(array('import', $importPath), $s);
 
                 return true;
             }
@@ -368,7 +302,7 @@ class Parser
                 $this->url($importPath) &&
                 $this->end()
             ) {
-                $this->append([Type::T_IMPORT, $importPath], $s);
+                $this->append(array('import', $importPath), $s);
 
                 return true;
             }
@@ -376,12 +310,10 @@ class Parser
             $this->seek($s);
 
             if ($this->literal('@extend') &&
-                $this->selectors($selectors) &&
+                $this->selectors($selector) &&
                 $this->end()
             ) {
-                // check for '!flag'
-                $optional = $this->stripOptionalFlag($selectors);
-                $this->append([Type::T_EXTEND, $selectors, $optional], $s);
+                $this->append(array('extend', $selector), $s);
 
                 return true;
             }
@@ -393,7 +325,7 @@ class Parser
                 $this->argumentDef($args) &&
                 $this->literal('{')
             ) {
-                $func = $this->pushSpecialBlock(Type::T_FUNCTION, $s);
+                $func = $this->pushSpecialBlock('function', $s);
                 $func->name = $fnName;
                 $func->args = $args;
 
@@ -402,25 +334,8 @@ class Parser
 
             $this->seek($s);
 
-            if ($this->literal('@break') && $this->end()) {
-                $this->append([Type::T_BREAK], $s);
-
-                return true;
-            }
-
-            $this->seek($s);
-
-            if ($this->literal('@continue') && $this->end()) {
-                $this->append([Type::T_CONTINUE], $s);
-
-                return true;
-            }
-
-            $this->seek($s);
-
-
-            if ($this->literal('@return') && ($this->valueList($retVal) || true) && $this->end()) {
-                $this->append([Type::T_RETURN, isset($retVal) ? $retVal : [Type::T_NULL]], $s);
+            if ($this->literal('@return') && $this->valueList($retVal) && $this->end()) {
+                $this->append(array('return', $retVal), $s);
 
                 return true;
             }
@@ -433,7 +348,7 @@ class Parser
                 $this->valueList($list) &&
                 $this->literal('{')
             ) {
-                $each = $this->pushSpecialBlock(Type::T_EACH, $s);
+                $each = $this->pushSpecialBlock('each', $s);
 
                 foreach ($varNames[2] as $varName) {
                     $each->vars[] = $varName[1];
@@ -450,7 +365,7 @@ class Parser
                 $this->expression($cond) &&
                 $this->literal('{')
             ) {
-                $while = $this->pushSpecialBlock(Type::T_WHILE, $s);
+                $while = $this->pushSpecialBlock('while', $s);
                 $while->cond = $cond;
 
                 return true;
@@ -467,7 +382,7 @@ class Parser
                 $this->expression($end) &&
                 $this->literal('{')
             ) {
-                $for = $this->pushSpecialBlock(Type::T_FOR, $s);
+                $for = $this->pushSpecialBlock('for', $s);
                 $for->var = $varName[1];
                 $for->start = $start;
                 $for->end = $end;
@@ -479,9 +394,9 @@ class Parser
             $this->seek($s);
 
             if ($this->literal('@if') && $this->valueList($cond) && $this->literal('{')) {
-                $if = $this->pushSpecialBlock(Type::T_IF, $s);
+                $if = $this->pushSpecialBlock('if', $s);
                 $if->cond = $cond;
-                $if->cases = [];
+                $if->cases = array();
 
                 return true;
             }
@@ -492,7 +407,7 @@ class Parser
                 $this->valueList($value) &&
                 $this->end()
             ) {
-                $this->append([Type::T_DEBUG, $value], $s);
+                $this->append(array('debug', $value), $s);
 
                 return true;
             }
@@ -503,7 +418,7 @@ class Parser
                 $this->valueList($value) &&
                 $this->end()
             ) {
-                $this->append([Type::T_WARN, $value], $s);
+                $this->append(array('warn', $value), $s);
 
                 return true;
             }
@@ -514,7 +429,7 @@ class Parser
                 $this->valueList($value) &&
                 $this->end()
             ) {
-                $this->append([Type::T_ERROR, $value], $s);
+                $this->append(array('error', $value), $s);
 
                 return true;
             }
@@ -522,7 +437,7 @@ class Parser
             $this->seek($s);
 
             if ($this->literal('@content') && $this->end()) {
-                $this->append([Type::T_MIXIN_CONTENT], $s);
+                $this->append(array('mixin_content'), $s);
 
                 return true;
             }
@@ -531,14 +446,14 @@ class Parser
 
             $last = $this->last();
 
-            if (isset($last) && $last[0] === Type::T_IF) {
+            if (isset($last) && $last[0] === 'if') {
                 list(, $if) = $last;
 
                 if ($this->literal('@else')) {
                     if ($this->literal('{')) {
-                        $else = $this->pushSpecialBlock(Type::T_ELSE, $s);
+                        $else = $this->pushSpecialBlock('else', $s);
                     } elseif ($this->literal('if') && $this->valueList($cond) && $this->literal('{')) {
-                        $else = $this->pushSpecialBlock(Type::T_ELSEIF, $s);
+                        $else = $this->pushSpecialBlock('elseif', $s);
                         $else->cond = $cond;
                     }
 
@@ -555,17 +470,16 @@ class Parser
 
             // only retain the first @charset directive encountered
             if ($this->literal('@charset') &&
-                $this->valueList($charset) &&
-                $this->end()
+                $this->valueList($charset) && $this->end()
             ) {
                 if (! isset($this->charset)) {
-                    $statement = [Type::T_CHARSET, $charset];
+                    $statement = array('charset', $charset);
 
-                    list($line, $column) = $this->getSourcePosition($s);
+                    $statement[self::SOURCE_POSITION] = $s;
 
-                    $statement[self::SOURCE_LINE]   = $line;
-                    $statement[self::SOURCE_COLUMN] = $column;
-                    $statement[self::SOURCE_INDEX]  = $this->sourceIndex;
+                    if (! $this->rootParser) {
+                        $statement[self::SOURCE_PARSER] = $this;
+                    }
 
                     $this->charset = $statement;
                 }
@@ -576,17 +490,12 @@ class Parser
             $this->seek($s);
 
             // doesn't match built in directive, do generic one
-            if ($this->literal('@', false) &&
-                $this->keyword($dirName) &&
+            if ($this->literal('@', false) && $this->keyword($dirName) &&
                 ($this->variable($dirValue) || $this->openString('{', $dirValue) || true) &&
                 $this->literal('{')
             ) {
-                if ($dirName === 'media') {
-                    $directive = $this->pushSpecialBlock(Type::T_MEDIA, $s);
-                } else {
-                    $directive = $this->pushSpecialBlock(Type::T_DIRECTIVE, $s);
-                    $directive->name = $dirName;
-                }
+                $directive = $this->pushSpecialBlock('directive', $s);
+                $directive->name = $dirName;
 
                 if (isset($dirValue)) {
                     $directive->value = $dirValue;
@@ -607,8 +516,8 @@ class Parser
             $this->valueList($value) &&
             $this->end()
         ) {
-            $name = [Type::T_STRING, '', [$name]];
-            $this->append([Type::T_ASSIGN, $name, $value], $s);
+            $name = array('string', '', array($name));
+            $this->append(array('assign', $name, $value), $s);
 
             return true;
         }
@@ -618,12 +527,11 @@ class Parser
         // variable assigns
         if ($this->variable($name) &&
             $this->literal(':') &&
-            $this->valueList($value) &&
-            $this->end()
+            $this->valueList($value) && $this->end()
         ) {
             // check for '!flag'
-            $assignmentFlags = $this->stripAssignmentFlags($value);
-            $this->append([Type::T_ASSIGN, $name, $value, $assignmentFlags], $s);
+            $assignmentFlag = $this->stripAssignmentFlag($value);
+            $this->append(array('assign', $name, $value, $assignmentFlag), $s);
 
             return true;
         }
@@ -637,7 +545,7 @@ class Parser
 
         // opening css block
         if ($this->selectors($selectors) && $this->literal('{')) {
-            $this->pushBlock($selectors, $s);
+            $b = $this->pushBlock($selectors, $s);
 
             return true;
         }
@@ -649,12 +557,12 @@ class Parser
             $foundSomething = false;
 
             if ($this->valueList($value)) {
-                $this->append([Type::T_ASSIGN, $name, $value], $s);
+                $this->append(array('assign', $name, $value), $s);
                 $foundSomething = true;
             }
 
             if ($this->literal('{')) {
-                $propBlock = $this->pushSpecialBlock(Type::T_NESTED_PROPERTY, $s);
+                $propBlock = $this->pushSpecialBlock('nestedprop', $s);
                 $propBlock->prefix = $name;
                 $foundSomething = true;
             } elseif ($foundSomething) {
@@ -672,14 +580,14 @@ class Parser
         if ($this->literal('}')) {
             $block = $this->popBlock();
 
-            if (isset($block->type) && $block->type === Type::T_INCLUDE) {
+            if (isset($block->type) && $block->type === 'include') {
                 $include = $block->child;
                 unset($block->child);
                 $include[3] = $block;
                 $this->append($include, $s);
             } elseif (empty($block->dontAppend)) {
-                $type = isset($block->type) ? $block->type : Type::T_BLOCK;
-                $this->append([$type, $block], $s);
+                $type = isset($block->type) ? $block->type : 'block';
+                $this->append(array($type, $block), $s);
             }
 
             return true;
@@ -695,35 +603,90 @@ class Parser
         return false;
     }
 
+    /**
+     * Strip assignment flag from the list
+     *
+     * @param array $value
+     *
+     * @return string
+     */
+    protected function stripAssignmentFlag(&$value)
+    {
+        $token = &$value;
+
+        for ($token = &$value; $token[0] === 'list' && ($s = count($token[2])); $token = &$lastNode) {
+            $lastNode = &$token[2][$s - 1];
+
+            if ($lastNode[0] === 'keyword' && in_array($lastNode[1], array('!default', '!global'))) {
+                array_pop($token[2]);
+
+                $token = $this->flattenList($token);
+
+                return $lastNode[1];
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Match literal string
+     *
+     * @param string  $what
+     * @param boolean $eatWhitespace
+     *
+     * @return boolean
+     */
+    protected function literal($what, $eatWhitespace = null)
+    {
+        if (! isset($eatWhitespace)) {
+            $eatWhitespace = $this->eatWhiteDefault;
+        }
+
+        // shortcut on single letter
+        if (! isset($what[1]) && isset($this->buffer[$this->count])) {
+            if ($this->buffer[$this->count] === $what) {
+                if (! $eatWhitespace) {
+                    $this->count++;
+
+                    return true;
+                }
+                // goes below...
+            } else {
+                return false;
+            }
+        }
+
+        return $this->match($this->pregQuote($what), $m, $eatWhitespace);
+    }
+
     /**
      * Push block onto parse tree
      *
      * @param array   $selectors
      * @param integer $pos
      *
-     * @return \Leafo\ScssPhp\Block
+     * @return \stdClass
      */
     protected function pushBlock($selectors, $pos = 0)
     {
-        list($line, $column) = $this->getSourcePosition($pos);
+        $b = new \stdClass;
+        $b->parent = $this->env; // not sure if we need this yet
 
-        $b = new Block;
-        $b->sourceLine   = $line;
-        $b->sourceColumn = $column;
-        $b->sourceIndex  = $this->sourceIndex;
-        $b->selectors    = $selectors;
-        $b->comments     = [];
-        $b->parent       = $this->env;
+        $b->sourcePosition = $pos;
+        $b->sourceParser = $this;
+        $b->selectors = $selectors;
+        $b->comments = array();
 
         if (! $this->env) {
-            $b->children = [];
+            $b->children = array();
         } elseif (empty($this->env->children)) {
             $this->env->children = $this->env->comments;
-            $b->children = [];
-            $this->env->comments = [];
+            $b->children = array();
+            $this->env->comments = array();
         } else {
             $b->children = $this->env->comments;
-            $this->env->comments = [];
+            $this->env->comments = array();
         }
 
         $this->env = $b;
@@ -737,7 +700,7 @@ class Parser
      * @param string  $type
      * @param integer $pos
      *
-     * @return \Leafo\ScssPhp\Block
+     * @return \stdClass
      */
     protected function pushSpecialBlock($type, $pos)
     {
@@ -750,7 +713,7 @@ class Parser
     /**
      * Pop scope and return last block
      *
-     * @return \Leafo\ScssPhp\Block
+     * @return \stdClass
      *
      * @throws \Exception
      */
@@ -775,304 +738,144 @@ class Parser
     }
 
     /**
-     * Peek input stream
+     * Append comment to current block
      *
-     * @param string  $regex
-     * @param array   $out
-     * @param integer $from
+     * @param array $comment
+     */
+    protected function appendComment($comment)
+    {
+        $comment[1] = substr(preg_replace(array('/^\s+/m', '/^(.)/m'), array('', ' \1'), $comment[1]), 1);
+
+        $this->env->comments[] = $comment;
+    }
+
+    /**
+     * Append statement to current block
      *
-     * @return integer
+     * @param array   $statement
+     * @param integer $pos
      */
-    protected function peek($regex, &$out, $from = null)
+    protected function append($statement, $pos = null)
     {
-        if (! isset($from)) {
-            $from = $this->count;
+        if ($pos !== null) {
+            $statement[self::SOURCE_POSITION] = $pos;
+
+            if (! $this->rootParser) {
+                $statement[self::SOURCE_PARSER] = $this;
+            }
         }
 
-        $r = '/' . $regex . '/' . $this->patternModifiers;
-        $result = preg_match($r, $this->buffer, $out, null, $from);
+        $this->env->children[] = $statement;
 
-        return $result;
+        $comments = $this->env->comments;
+
+        if (count($comments)) {
+            $this->env->children = array_merge($this->env->children, $comments);
+            $this->env->comments = array();
+        }
     }
 
     /**
-     * Seek to position in input stream (or return current position in input stream)
-     *
-     * @param integer $where
+     * Returns last child was appended
      *
-     * @return integer
+     * @return array|null
      */
-    protected function seek($where = null)
+    protected function last()
     {
-        if ($where === null) {
-            return $this->count;
-        }
-
-        $this->count = $where;
+        $i = count($this->env->children) - 1;
 
-        return true;
+        if (isset($this->env->children[$i])) {
+            return $this->env->children[$i];
+        }
     }
 
     /**
-     * Match string looking for either ending delim, escape, or string interpolation
+     * Parse media query list
      *
-     * {@internal This is a workaround for preg_match's 250K string match limit. }}
+     * @param array $out
      *
-     * @param array  $m     Matches (passed by reference)
-     * @param string $delim Delimeter
+     * @return boolean
+     */
+    protected function mediaQueryList(&$out)
+    {
+        return $this->genericList($out, 'mediaQuery', ',', false);
+    }
+
+    /**
+     * Parse media query
      *
-     * @return boolean True if match; false otherwise
+     * @param array $out
+     *
+     * @return boolean
      */
-    protected function matchString(&$m, $delim)
+    protected function mediaQuery(&$out)
     {
-        $token = null;
+        $s = $this->seek();
 
-        $end = strlen($this->buffer);
+        $expressions = null;
+        $parts = array();
 
-        // look for either ending delim, escape, or string interpolation
-        foreach (['#{', '\\', $delim] as $lookahead) {
-            $pos = strpos($this->buffer, $lookahead, $this->count);
+        if (($this->literal('only') && ($only = true) || $this->literal('not') && ($not = true) || true) &&
+            $this->mixedKeyword($mediaType)
+        ) {
+            $prop = array('mediaType');
 
-            if ($pos !== false && $pos < $end) {
-                $end = $pos;
-                $token = $lookahead;
+            if (isset($only)) {
+                $prop[] = array('keyword', 'only');
+            }
+
+            if (isset($not)) {
+                $prop[] = array('keyword', 'not');
+            }
+
+            $media = array('list', '', array());
+
+            foreach ((array)$mediaType as $type) {
+                if (is_array($type)) {
+                    $media[2][] = $type;
+                } else {
+                    $media[2][] = array('keyword', $type);
+                }
             }
+
+            $prop[] = $media;
+            $parts[] = $prop;
         }
 
-        if (! isset($token)) {
-            return false;
+        if (empty($parts) || $this->literal('and')) {
+            $this->genericList($expressions, 'mediaExpression', 'and', false);
+
+            if (is_array($expressions)) {
+                $parts = array_merge($parts, $expressions[2]);
+            }
         }
 
-        $match = substr($this->buffer, $this->count, $end - $this->count);
-        $m = [
-            $match . $token,
-            $match,
-            $token
-        ];
-        $this->count = $end + strlen($token);
+        $out = $parts;
 
         return true;
     }
 
     /**
-     * Try to match something on head of buffer
+     * Parse media expression
      *
-     * @param string  $regex
-     * @param array   $out
-     * @param boolean $eatWhitespace
+     * @param array $out
      *
      * @return boolean
      */
-    protected function match($regex, &$out, $eatWhitespace = null)
+    protected function mediaExpression(&$out)
     {
-        if (! isset($eatWhitespace)) {
-            $eatWhitespace = $this->eatWhiteDefault;
-        }
-
-        $r = '/' . $regex . '/' . $this->patternModifiers;
+        $s = $this->seek();
+        $value = null;
 
-        if (preg_match($r, $this->buffer, $out, null, $this->count)) {
-            $this->count += strlen($out[0]);
+        if ($this->literal('(') &&
+            $this->expression($feature) &&
+            ($this->literal(':') && $this->expression($value) || true) &&
+            $this->literal(')')
+        ) {
+            $out = array('mediaExp', $feature);
 
-            if ($eatWhitespace) {
-                $this->whitespace();
-            }
-
-            return true;
-        }
-
-        return false;
-    }
-
-    /**
-     * Match literal string
-     *
-     * @param string  $what
-     * @param boolean $eatWhitespace
-     *
-     * @return boolean
-     */
-    protected function literal($what, $eatWhitespace = null)
-    {
-        if (! isset($eatWhitespace)) {
-            $eatWhitespace = $this->eatWhiteDefault;
-        }
-
-        $len = strlen($what);
-
-        if (strcasecmp(substr($this->buffer, $this->count, $len), $what) === 0) {
-            $this->count += $len;
-
-            if ($eatWhitespace) {
-                $this->whitespace();
-            }
-
-            return true;
-        }
-
-        return false;
-    }
-
-    /**
-     * Match some whitespace
-     *
-     * @return boolean
-     */
-    protected function whitespace()
-    {
-        $gotWhite = false;
-
-        while (preg_match(self::$whitePattern, $this->buffer, $m, null, $this->count)) {
-            if (isset($m[1]) && empty($this->commentsSeen[$this->count])) {
-                $this->appendComment([Type::T_COMMENT, $m[1]]);
-
-                $this->commentsSeen[$this->count] = true;
-            }
-
-            $this->count += strlen($m[0]);
-            $gotWhite = true;
-        }
-
-        return $gotWhite;
-    }
-
-    /**
-     * Append comment to current block
-     *
-     * @param array $comment
-     */
-    protected function appendComment($comment)
-    {
-        $comment[1] = substr(preg_replace(['/^\s+/m', '/^(.)/m'], ['', ' \1'], $comment[1]), 1);
-
-        $this->env->comments[] = $comment;
-    }
-
-    /**
-     * Append statement to current block
-     *
-     * @param array   $statement
-     * @param integer $pos
-     */
-    protected function append($statement, $pos = null)
-    {
-        if ($pos !== null) {
-            list($line, $column) = $this->getSourcePosition($pos);
-
-            $statement[self::SOURCE_LINE]   = $line;
-            $statement[self::SOURCE_COLUMN] = $column;
-            $statement[self::SOURCE_INDEX]  = $this->sourceIndex;
-        }
-
-        $this->env->children[] = $statement;
-
-        $comments = $this->env->comments;
-
-        if (count($comments)) {
-            $this->env->children = array_merge($this->env->children, $comments);
-            $this->env->comments = [];
-        }
-    }
-
-    /**
-     * Returns last child was appended
-     *
-     * @return array|null
-     */
-    protected function last()
-    {
-        $i = count($this->env->children) - 1;
-
-        if (isset($this->env->children[$i])) {
-            return $this->env->children[$i];
-        }
-    }
-
-    /**
-     * Parse media query list
-     *
-     * @param array $out
-     *
-     * @return boolean
-     */
-    protected function mediaQueryList(&$out)
-    {
-        return $this->genericList($out, 'mediaQuery', ',', false);
-    }
-
-    /**
-     * Parse media query
-     *
-     * @param array $out
-     *
-     * @return boolean
-     */
-    protected function mediaQuery(&$out)
-    {
-        $expressions = null;
-        $parts = [];
-
-        if (($this->literal('only') && ($only = true) || $this->literal('not') && ($not = true) || true) &&
-            $this->mixedKeyword($mediaType)
-        ) {
-            $prop = [Type::T_MEDIA_TYPE];
-
-            if (isset($only)) {
-                $prop[] = [Type::T_KEYWORD, 'only'];
-            }
-
-            if (isset($not)) {
-                $prop[] = [Type::T_KEYWORD, 'not'];
-            }
-
-            $media = [Type::T_LIST, '', []];
-
-            foreach ((array) $mediaType as $type) {
-                if (is_array($type)) {
-                    $media[2][] = $type;
-                } else {
-                    $media[2][] = [Type::T_KEYWORD, $type];
-                }
-            }
-
-            $prop[]  = $media;
-            $parts[] = $prop;
-        }
-
-        if (empty($parts) || $this->literal('and')) {
-            $this->genericList($expressions, 'mediaExpression', 'and', false);
-
-            if (is_array($expressions)) {
-                $parts = array_merge($parts, $expressions[2]);
-            }
-        }
-
-        $out = $parts;
-
-        return true;
-    }
-
-    /**
-     * Parse media expression
-     *
-     * @param array $out
-     *
-     * @return boolean
-     */
-    protected function mediaExpression(&$out)
-    {
-        $s = $this->seek();
-        $value = null;
-
-        if ($this->literal('(') &&
-            $this->expression($feature) &&
-            ($this->literal(':') && $this->expression($value) || true) &&
-            $this->literal(')')
-        ) {
-            $out = [Type::T_MEDIA_EXPRESSION, $feature];
-
-            if ($value) {
-                $out[] = $value;
+            if ($value) {
+                $out[] = $value;
             }
 
             return true;
@@ -1120,7 +923,7 @@ class Parser
         }
 
         if ($this->genericList($value, 'expression')) {
-            $out = [$keyword, $value, false];
+            $out = array($keyword, $value, false);
             $s = $this->seek();
 
             if ($this->literal('...')) {
@@ -1136,7 +939,7 @@ class Parser
     }
 
     /**
-     * Parse comma separated value list
+     * Parse list
      *
      * @param string $out
      *
@@ -1148,7 +951,7 @@ class Parser
     }
 
     /**
-     * Parse space separated value list
+     * Parse space list
      *
      * @param array $out
      *
@@ -1172,7 +975,7 @@ class Parser
     protected function genericList(&$out, $parseItem, $delim = '', $flatten = true)
     {
         $s = $this->seek();
-        $items = [];
+        $items = array();
 
         while ($this->$parseItem($value)) {
             $items[] = $value;
@@ -1193,7 +996,7 @@ class Parser
         if ($flatten && count($items) === 1) {
             $out = $items[0];
         } else {
-            $out = [Type::T_LIST, $delim, $items];
+            $out = array('list', $delim, $items);
         }
 
         return true;
@@ -1212,12 +1015,12 @@ class Parser
 
         if ($this->literal('(')) {
             if ($this->literal(')')) {
-                $out = [Type::T_LIST, '', []];
+                $out = array('list', '', array());
 
                 return true;
             }
 
-            if ($this->valueList($out) && $this->literal(')') && $out[0] === Type::T_LIST) {
+            if ($this->valueList($out) && $this->literal(')') && $out[0] === 'list') {
                 return true;
             }
 
@@ -1249,13 +1052,13 @@ class Parser
      */
     protected function expHelper($lhs, $minP)
     {
-        $operators = self::$operatorPattern;
+        $opstr = self::$operatorStr;
 
         $ss = $this->seek();
         $whiteBefore = isset($this->buffer[$this->count - 1]) &&
             ctype_space($this->buffer[$this->count - 1]);
 
-        while ($this->match($operators, $m, false) && self::$precedence[$m[1]] >= $minP) {
+        while ($this->match($opstr, $m, false) && self::$precedence[$m[1]] >= $minP) {
             $whiteAfter = isset($this->buffer[$this->count]) &&
                 ctype_space($this->buffer[$this->count]);
             $varAfter = isset($this->buffer[$this->count]) &&
@@ -1275,11 +1078,11 @@ class Parser
             }
 
             // peek and see if rhs belongs to next operator
-            if ($this->peek($operators, $next) && self::$precedence[$next[1]] > self::$precedence[$op]) {
+            if ($this->peek($opstr, $next) && self::$precedence[$next[1]] > self::$precedence[$op]) {
                 $rhs = $this->expHelper($rhs, self::$precedence[$next[1]]);
             }
 
-            $lhs = [Type::T_EXPRESSION, $op, $lhs, $rhs, $this->inParens, $whiteBefore, $whiteAfter];
+            $lhs = array('exp', $op, $lhs, $rhs, $this->inParens, $whiteBefore, $whiteAfter);
             $ss = $this->seek();
             $whiteBefore = isset($this->buffer[$this->count - 1]) &&
                 ctype_space($this->buffer[$this->count - 1]);
@@ -1302,15 +1105,7 @@ class Parser
         $s = $this->seek();
 
         if ($this->literal('not', false) && $this->whitespace() && $this->value($inner)) {
-            $out = [Type::T_UNARY, 'not', $inner, $this->inParens];
-
-            return true;
-        }
-
-        $this->seek($s);
-
-        if ($this->literal('not', false) && $this->parenValue($inner)) {
-            $out = [Type::T_UNARY, 'not', $inner, $this->inParens];
+            $out = array('unary', 'not', $inner, $this->inParens);
 
             return true;
         }
@@ -1318,7 +1113,7 @@ class Parser
         $this->seek($s);
 
         if ($this->literal('+') && $this->value($inner)) {
-            $out = [Type::T_UNARY, '+', $inner, $this->inParens];
+            $out = array('unary', '+', $inner, $this->inParens);
 
             return true;
         }
@@ -1331,7 +1126,7 @@ class Parser
             $this->unit($inner) ||
             $this->parenValue($inner))
         ) {
-            $out = [Type::T_UNARY, '-', $inner, $this->inParens];
+            $out = array('unary', '-', $inner, $this->inParens);
 
             return true;
         }
@@ -1352,9 +1147,9 @@ class Parser
 
         if ($this->keyword($keyword)) {
             if ($keyword === 'null') {
-                $out = [Type::T_NULL];
+                $out = array('null');
             } else {
-                $out = [Type::T_KEYWORD, $keyword];
+                $out = array('keyword', $keyword);
             }
 
             return true;
@@ -1378,7 +1173,7 @@ class Parser
 
         if ($this->literal('(')) {
             if ($this->literal(')')) {
-                $out = [Type::T_LIST, '', []];
+                $out = array('list', '', array());
 
                 return true;
             }
@@ -1417,9 +1212,9 @@ class Parser
             $this->openString(')', $args, '(');
 
             if ($this->literal(')')) {
-                $out = [Type::T_STRING, '', [
+                $out = array('string', '', array(
                     'progid:', $fn, '(', $args, ')'
-                ]];
+                ));
 
                 return true;
             }
@@ -1445,7 +1240,7 @@ class Parser
             $this->literal('(')
         ) {
             if ($name === 'alpha' && $this->argumentList($args)) {
-                $func = [Type::T_FUNCTION, $name, [Type::T_STRING, '', $args]];
+                $func = array('function', $name, array('string', '', $args));
 
                 return true;
             }
@@ -1454,7 +1249,7 @@ class Parser
                 $ss = $this->seek();
 
                 if ($this->argValues($args) && $this->literal(')')) {
-                    $func = [Type::T_FUNCTION_CALL, $name, $args];
+                    $func = array('fncall', $name, $args);
 
                     return true;
                 }
@@ -1462,16 +1257,16 @@ class Parser
                 $this->seek($ss);
             }
 
-            if (($this->openString(')', $str, '(') || true) &&
+            if (($this->openString(')', $str, '(') || true ) &&
                 $this->literal(')')
             ) {
-                $args = [];
+                $args = array();
 
                 if (! empty($str)) {
-                    $args[] = [null, [Type::T_STRING, '', [$str]]];
+                    $args[] = array(null, array('string', '', array($str)));
                 }
 
-                $func = [Type::T_FUNCTION_CALL, $name, $args];
+                $func = array('fncall', $name, $args);
 
                 return true;
             }
@@ -1494,11 +1289,13 @@ class Parser
         $s = $this->seek();
         $this->literal('(');
 
-        $args = [];
+        $args = array();
 
         while ($this->keyword($var)) {
+            $ss = $this->seek();
+
             if ($this->literal('=') && $this->expression($exp)) {
-                $args[] = [Type::T_STRING, '', [$var . '=']];
+                $args[] = array('string', '', array($var . '='));
                 $arg = $exp;
             } else {
                 break;
@@ -1510,7 +1307,7 @@ class Parser
                 break;
             }
 
-            $args[] = [Type::T_STRING, '', [', ']];
+            $args[] = array('string', '', array(', '));
         }
 
         if (! $this->literal(')') || ! count($args)) {
@@ -1536,10 +1333,10 @@ class Parser
         $s = $this->seek();
         $this->literal('(');
 
-        $args = [];
+        $args = array();
 
         while ($this->variable($var)) {
-            $arg = [$var[1], null, false];
+            $arg = array($var[1], null, false);
 
             $ss = $this->seek();
 
@@ -1593,15 +1390,13 @@ class Parser
     {
         $s = $this->seek();
 
-        if (! $this->literal('(')) {
-            return false;
-        }
+        $this->literal('(');
 
-        $keys = [];
-        $values = [];
+        $keys = array();
+        $values = array();
 
-        while ($this->genericList($key, 'expression') && $this->literal(':') &&
-            $this->genericList($value, 'expression')
+        while ($this->genericList($key, 'expression') && $this->literal(':')
+            && $this->genericList($value, 'expression')
         ) {
             $keys[] = $key;
             $values[] = $value;
@@ -1617,7 +1412,7 @@ class Parser
             return false;
         }
 
-        $out = [Type::T_MAP, $keys, $values];
+        $out = array('map', $keys, $values);
 
         return true;
     }
@@ -1631,24 +1426,24 @@ class Parser
      */
     protected function color(&$out)
     {
-        $color = [Type::T_COLOR];
+        $color = array('color');
 
         if ($this->match('(#([0-9a-f]{6})|#([0-9a-f]{3}))', $m)) {
             if (isset($m[3])) {
-                $num = hexdec($m[3]);
-
-                foreach ([3, 2, 1] as $i) {
-                    $t = $num & 0xf;
-                    $color[$i] = $t << 4 | $t;
-                    $num >>= 4;
-                }
+                $num = $m[3];
+                $width = 16;
             } else {
-                $num = hexdec($m[2]);
+                $num = $m[2];
+                $width = 256;
+            }
 
-                foreach ([3, 2, 1] as $i) {
-                    $color[$i] = $num & 0xff;
-                    $num >>= 8;
-                }
+            $num = hexdec($num);
+
+            foreach (array(3, 2, 1) as $i) {
+                $t = $num % $width;
+                $num /= $width;
+
+                $color[$i] = $t * (256/$width) + $t * floor(16/$width);
             }
 
             $out = $color;
@@ -1669,7 +1464,7 @@ class Parser
     protected function unit(&$unit)
     {
         if ($this->match('([0-9]*(\.)?[0-9]+)([%a-zA-Z]+)?', $m)) {
-            $unit = new Node\Number($m[1], empty($m[3]) ? '' : $m[3]);
+            $unit = array('number', $m[1], empty($m[3]) ? '' : $m[3]);
 
             return true;
         }
@@ -1690,39 +1485,33 @@ class Parser
 
         if ($this->literal('"', false)) {
             $delim = '"';
-        } elseif ($this->literal("'", false)) {
-            $delim = "'";
+        } elseif ($this->literal('\'', false)) {
+            $delim = '\'';
         } else {
             return false;
         }
 
-        $content = [];
+        $content = array();
         $oldWhite = $this->eatWhiteDefault;
         $this->eatWhiteDefault = false;
-        $hasInterpolation = false;
 
         while ($this->matchString($m, $delim)) {
-            if ($m[1] !== '') {
-                $content[] = $m[1];
-            }
+            $content[] = $m[1];
 
             if ($m[2] === '#{') {
                 $this->count -= strlen($m[2]);
 
                 if ($this->interpolation($inter, false)) {
                     $content[] = $inter;
-                    $hasInterpolation = true;
                 } else {
                     $this->count += strlen($m[2]);
                     $content[] = '#{'; // ignore it
                 }
             } elseif ($m[2] === '\\') {
-                if ($this->literal('"', false)) {
-                    $content[] = $m[2] . '"';
-                } elseif ($this->literal("'", false)) {
-                    $content[] = $m[2] . "'";
-                } else {
-                    $content[] = $m[2];
+                $content[] = $m[2];
+
+                if ($this->literal($delim, false)) {
+                    $content[] = $delim;
                 }
             } else {
                 $this->count -= strlen($delim);
@@ -1733,19 +1522,7 @@ class Parser
         $this->eatWhiteDefault = $oldWhite;
 
         if ($this->literal($delim)) {
-            if ($hasInterpolation) {
-                $delim = '"';
-
-                foreach ($content as &$string) {
-                    if ($string === "\\'") {
-                        $string = "'";
-                    } elseif ($string === '\\"') {
-                        $string = '"';
-                    }
-                }
-            }
-
-            $out = [Type::T_STRING, $delim, $content];
+            $out = array('string', $delim, $content);
 
             return true;
         }
@@ -1764,12 +1541,14 @@ class Parser
      */
     protected function mixedKeyword(&$out)
     {
-        $parts = [];
+        $s = $this->seek();
+
+        $parts = array();
 
         $oldWhite = $this->eatWhiteDefault;
         $this->eatWhiteDefault = false;
 
-        for (;;) {
+        while (true) {
             if ($this->keyword($key)) {
                 $parts[] = $key;
                 continue;
@@ -1812,11 +1591,15 @@ class Parser
         $oldWhite = $this->eatWhiteDefault;
         $this->eatWhiteDefault = false;
 
-        $patt = '(.*?)([\'"]|#\{|' . $this->pregQuote($end) . '|' . self::$commentPattern . ')';
+        $stop = array('\'', '"', '#{', $end);
+        $stop = array_map(array($this, 'pregQuote'), $stop);
+        $stop[] = self::$commentMulti;
+
+        $patt = '(.*?)(' . implode('|', $stop) . ')';
 
         $nestingLevel = 0;
 
-        $content = [];
+        $content = array();
 
         while ($this->match($patt, $m, false)) {
             if (isset($m[1]) && $m[1] !== '') {
@@ -1835,7 +1618,7 @@ class Parser
                 break;
             }
 
-            if (($tok === "'" || $tok === '"') && $this->string($str)) {
+            if (($tok === '\'' || $tok === '"') && $this->string($str)) {
                 $content[] = $str;
                 continue;
             }
@@ -1860,7 +1643,7 @@ class Parser
             $content[count($content) - 1] = rtrim(end($content));
         }
 
-        $out = [Type::T_STRING, '', $content];
+        $out = array('string', '', $content);
 
         return true;
     }
@@ -1881,6 +1664,8 @@ class Parser
         $s = $this->seek();
 
         if ($this->literal('#{') && $this->valueList($value) && $this->literal('}', false)) {
+            // TODO: don't error if out of bounds
+
             if ($lookWhite) {
                 $left = preg_match('/\s/', $this->buffer[$s - 1]) ? ' ' : '';
                 $right = preg_match('/\s/', $this->buffer[$this->count]) ? ' ': '';
@@ -1888,19 +1673,17 @@ class Parser
                 $left = $right = false;
             }
 
-            $out = [Type::T_INTERPOLATE, $value, $left, $right];
+            $out = array('interpolate', $value, $left, $right);
             $this->eatWhiteDefault = $oldWhite;
 
             if ($this->eatWhiteDefault) {
                 $this->whitespace();
             }
-
             return true;
         }
 
         $this->seek($s);
         $this->eatWhiteDefault = $oldWhite;
-
         return false;
     }
 
@@ -1913,29 +1696,23 @@ class Parser
      */
     protected function propertyName(&$out)
     {
-        $parts = [];
+        $s = $this->seek();
+        $parts = array();
 
         $oldWhite = $this->eatWhiteDefault;
         $this->eatWhiteDefault = false;
 
-        for (;;) {
+        while (true) {
             if ($this->interpolation($inter)) {
                 $parts[] = $inter;
-                continue;
-            }
-
-            if ($this->keyword($text)) {
+            } elseif ($this->keyword($text)) {
                 $parts[] = $text;
-                continue;
-            }
-
-            if (count($parts) === 0 && $this->match('[:.#]', $m, false)) {
+            } elseif (count($parts) === 0 && $this->match('[:.#]', $m, false)) {
                 // css hacks
                 $parts[] = $m[0];
-                continue;
+            } else {
+                break;
             }
-
-            break;
         }
 
         $this->eatWhiteDefault = $oldWhite;
@@ -1960,7 +1737,7 @@ class Parser
 
         $this->whitespace(); // get any extra whitespace
 
-        $out = [Type::T_STRING, '', $parts];
+        $out = array('string', '', $parts);
 
         return true;
     }
@@ -1975,7 +1752,7 @@ class Parser
     protected function selectors(&$out)
     {
         $s = $this->seek();
-        $selectors = [];
+        $selectors = array();
 
         while ($this->selector($sel)) {
             $selectors[] = $sel;
@@ -2009,26 +1786,20 @@ class Parser
      */
     protected function selector(&$out)
     {
-        $selector = [];
+        $selector = array();
 
-        for (;;) {
+        while (true) {
             if ($this->match('[>+~]+', $m)) {
-                $selector[] = [$m[0]];
-                continue;
-            }
-
-            if ($this->selectorSingle($part)) {
+                $selector[] = array($m[0]);
+            } elseif ($this->selectorSingle($part)) {
                 $selector[] = $part;
                 $this->match('\s+', $m);
-                continue;
-            }
-
-            if ($this->match('\/[^\/]+\/', $m)) {
-                $selector[] = [$m[0]];
-                continue;
+            } elseif ($this->match('\/[^\/]+\/', $m)) {
+                $selector[] = array($m[0]);
+            } else {
+                break;
             }
 
-            break;
         }
 
         if (count($selector) === 0) {
@@ -2055,13 +1826,13 @@ class Parser
         $oldWhite = $this->eatWhiteDefault;
         $this->eatWhiteDefault = false;
 
-        $parts = [];
+        $parts = array();
 
         if ($this->literal('*', false)) {
             $parts[] = '*';
         }
 
-        for (;;) {
+        while (true) {
             // see if we can stop early
             if ($this->match('\s*[{,]', $m)) {
                 $this->count--;
@@ -2129,7 +1900,7 @@ class Parser
                 $ss = $this->seek();
 
                 if ($this->literal('(') &&
-                    ($this->openString(')', $str, '(') || true) &&
+                    ($this->openString(')', $str, '(') || true ) &&
                     $this->literal(')')
                 ) {
                     $parts[] = '(';
@@ -2149,22 +1920,59 @@ class Parser
             $this->seek($s);
 
             // attribute selector
-            if ($this->literal('[') &&
-               ($this->openString(']', $str, '[') || true) &&
-               $this->literal(']')
-            ) {
-                $parts[] = '[';
+            // TODO: replace with open string?
+            if ($this->literal('[', false)) {
+                $attrParts = array('[');
+
+                // keyword, string, operator
+                while (true) {
+                    if ($this->literal(']', false)) {
+                        $this->count--;
+                        break; // get out early
+                    }
 
-                if (! empty($str)) {
-                    $parts[] = $str;
+                    if ($this->match('\s+', $m)) {
+                        $attrParts[] = ' ';
+                        continue;
+                    }
+
+                    if ($this->string($str)) {
+                        $attrParts[] = $str;
+                        continue;
+                    }
+
+                    if ($this->keyword($word)) {
+                        $attrParts[] = $word;
+                        continue;
+                    }
+
+                    if ($this->interpolation($inter, false)) {
+                        $attrParts[] = $inter;
+                        continue;
+                    }
+
+                    // operator, handles attr namespace too
+                    if ($this->match('[|-~\$\*\^=]+', $m)) {
+                        $attrParts[] = $m[0];
+                        continue;
+                    }
+
+                    break;
                 }
 
-                $parts[] = ']';
+                if ($this->literal(']', false)) {
+                    $attrParts[] = ']';
 
-                continue;
-            }
+                    foreach ($attrParts as $part) {
+                        $parts[] = $part;
+                    }
 
-            $this->seek($s);
+                    continue;
+                }
+
+                $this->seek($s);
+                // TODO: should just break here?
+            }
 
             break;
         }
@@ -2192,7 +2000,7 @@ class Parser
         $s = $this->seek();
 
         if ($this->literal('$', false) && $this->keyword($name)) {
-            $out = [Type::T_VARIABLE, $name];
+            $out = array('var', $name);
 
             return true;
         }
@@ -2213,9 +2021,7 @@ class Parser
     protected function keyword(&$word, $eatWhitespace = null)
     {
         if ($this->match(
-            $this->utf8
-                ? '(([\pL\w_\-\*!"\']|[\\\\].)([\pL\w\-_"\']|[\\\\].)*)'
-                : '(([\w_\-\*!"\']|[\\\\].)([\w\-_"\']|[\\\\].)*)',
+            '(([\w_\-\*!"\']|[\\\\].)([\w\-_"\']|[\\\\].)*)',
             $m,
             $eatWhitespace
         )) {
@@ -2236,12 +2042,7 @@ class Parser
      */
     protected function placeholder(&$placeholder)
     {
-        if ($this->match(
-            $this->utf8
-                ? '([\pL\w\-_]+|#[{][$][\pL\w\-_]+[}])'
-                : '([\w\-_]+|#[{][$][\w\-_]+[}])',
-            $m
-        )) {
+        if ($this->match('([\w\-_]+|#[{][$][\w\-_]+[}])', $m)) {
             $placeholder = $m[1];
 
             return true;
@@ -2260,7 +2061,7 @@ class Parser
     protected function url(&$out)
     {
         if ($this->match('(url\(\s*(["\']?)([^)]+)\2\s*\))', $m)) {
-            $out = [Type::T_STRING, '', ['url(' . $m[2] . $m[3] . $m[2] . ')']];
+            $out = array('string', '', array('url(' . $m[2] . $m[3] . $m[2] . ')'));
 
             return true;
         }
@@ -2288,200 +2089,254 @@ class Parser
     }
 
     /**
-     * Strip assignment flag from the list
-     *
-     * @param array $value
+     * @deprecated
      *
-     * @return array
+     * {@internal
+     *     advance counter to next occurrence of $what
+     *     $until - don't include $what in advance
+     *     $allowNewline, if string, will be used as valid char set
+     * }}
      */
-    protected function stripAssignmentFlags(&$value)
+    protected function to($what, &$out, $until = false, $allowNewline = false)
     {
-        $flags = [];
-
-        for ($token = &$value; $token[0] === Type::T_LIST && ($s = count($token[2])); $token = &$lastNode) {
-            $lastNode = &$token[2][$s - 1];
-
-            while ($lastNode[0] === Type::T_KEYWORD && in_array($lastNode[1], ['!default', '!global'])) {
-                array_pop($token[2]);
-
-                $node = end($token[2]);
-
-                $token = $this->flattenList($token);
+        if (is_string($allowNewline)) {
+            $validChars = $allowNewline;
+        } else {
+            $validChars = $allowNewline ? '.' : "[^\n]";
+        }
 
-                $flags[] = $lastNode[1];
+        if (! $this->match('(' . $validChars . '*?)' . $this->pregQuote($what), $m, ! $until)) {
+            return false;
+        }
 
-                $lastNode = $node;
-            }
+        if ($until) {
+            $this->count -= strlen($what); // give back $what
         }
 
-        return $flags;
+        $out = $m[1];
+
+        return true;
     }
 
     /**
-     * Strip optional flag from selector list
+     * Throw parser error
      *
-     * @param array $selectors
+     * @param string  $msg
+     * @param integer $count
      *
-     * @return string
+     * @throws \Exception
      */
-    protected function stripOptionalFlag(&$selectors)
+    public function throwParseError($msg = 'parse error', $count = null)
     {
-        $optional = false;
+        $count = ! isset($count) ? $this->count : $count;
 
-        $selector = end($selectors);
-        $part = end($selector);
+        $line = $this->getLineNo($count);
 
-        if ($part === ['!optional']) {
-            array_pop($selectors[count($selectors) - 1]);
+        if (! empty($this->sourceName)) {
+            $loc = "$this->sourceName on line $line";
+        } else {
+            $loc = "line: $line";
+        }
 
-            $optional = true;
+        if ($this->peek("(.*?)(\n|$)", $m, $count)) {
+            throw new \Exception("$msg: failed at `$m[1]` $loc");
         }
 
-        return $optional;
+        throw new \Exception("$msg: $loc");
     }
 
     /**
-     * Turn list of length 1 into value type
-     *
-     * @param array $value
+     * Get source file name
      *
-     * @return array
+     * @return string
      */
-    protected function flattenList($value)
+    public function getSourceName()
     {
-        if ($value[0] === Type::T_LIST && count($value[2]) === 1) {
-            return $this->flattenList($value[2][0]);
-        }
+        return $this->sourceName;
+    }
 
-        return $value;
+    /**
+     * Get source line number (given character position in the buffer)
+     *
+     * @param integer $pos
+     *
+     * @return integer
+     */
+    public function getLineNo($pos)
+    {
+        return 1 + substr_count(substr($this->buffer, 0, $pos), "\n");
     }
 
     /**
-     * @deprecated
+     * Match string looking for either ending delim, escape, or string interpolation
      *
-     * {@internal
-     *     advance counter to next occurrence of $what
-     *     $until - don't include $what in advance
-     *     $allowNewline, if string, will be used as valid char set
-     * }}
+     * {@internal This is a workaround for preg_match's 250K string match limit. }}
+     *
+     * @param array  $m     Matches (passed by reference)
+     * @param string $delim Delimeter
+     *
+     * @return boolean True if match; false otherwise
      */
-    protected function to($what, &$out, $until = false, $allowNewline = false)
+    protected function matchString(&$m, $delim)
     {
-        if (is_string($allowNewline)) {
-            $validChars = $allowNewline;
-        } else {
-            $validChars = $allowNewline ? '.' : "[^\n]";
-        }
+        $token = null;
 
-        if (! $this->match('(' . $validChars . '*?)' . $this->pregQuote($what), $m, ! $until)) {
-            return false;
+        $end = strlen($this->buffer);
+
+        // look for either ending delim, escape, or string interpolation
+        foreach (array('#{', '\\', $delim) as $lookahead) {
+            $pos = strpos($this->buffer, $lookahead, $this->count);
+
+            if ($pos !== false && $pos < $end) {
+                $end = $pos;
+                $token = $lookahead;
+            }
         }
 
-        if ($until) {
-            $this->count -= strlen($what); // give back $what
+        if (! isset($token)) {
+            return false;
         }
 
-        $out = $m[1];
+        $match = substr($this->buffer, $this->count, $end - $this->count);
+        $m = array(
+            $match . $token,
+            $match,
+            $token
+        );
+        $this->count = $end + strlen($token);
 
         return true;
     }
 
     /**
-     * @deprecated
+     * Try to match something on head of buffer
+     *
+     * @param string  $regex
+     * @param array   $out
+     * @param boolean $eatWhitespace
+     *
+     * @return boolean
      */
-    protected function show()
+    protected function match($regex, &$out, $eatWhitespace = null)
     {
-        if ($this->peek("(.*?)(\n|$)", $m, $this->count)) {
-            return $m[1];
+        if (! isset($eatWhitespace)) {
+            $eatWhitespace = $this->eatWhiteDefault;
         }
 
-        return '';
+        $r = '/' . $regex . '/Ais';
+
+        if (preg_match($r, $this->buffer, $out, null, $this->count)) {
+            $this->count += strlen($out[0]);
+
+            if ($eatWhitespace) {
+                $this->whitespace();
+            }
+
+            return true;
+        }
+
+        return false;
     }
 
     /**
-     * Quote regular expression
-     *
-     * @param string $what
+     * Match some whitespace
      *
-     * @return string
+     * @return boolean
      */
-    private function pregQuote($what)
+    protected function whitespace()
     {
-        return preg_quote($what, '/');
+        $gotWhite = false;
+
+        while (preg_match(self::$whitePattern, $this->buffer, $m, null, $this->count)) {
+            if (isset($m[1]) && empty($this->commentsSeen[$this->count])) {
+                $this->appendComment(array('comment', $m[1]));
+
+                $this->commentsSeen[$this->count] = true;
+            }
+
+            $this->count += strlen($m[0]);
+            $gotWhite = true;
+        }
+
+        return $gotWhite;
     }
 
     /**
-     * Extract line numbers from buffer
+     * Peek input stream
      *
-     * @param string $buffer
+     * @param string  $regex
+     * @param array   $out
+     * @param integer $from
+     *
+     * @return integer
      */
-    private function extractLineNumbers($buffer)
+    protected function peek($regex, &$out, $from = null)
     {
-        $this->sourcePositions = [0 => 0];
-        $prev = 0;
-
-        while (($pos = strpos($buffer, "\n", $prev)) !== false) {
-            $this->sourcePositions[] = $pos;
-            $prev = $pos + 1;
+        if (! isset($from)) {
+            $from = $this->count;
         }
 
-        $this->sourcePositions[] = strlen($buffer);
+        $r = '/' . $regex . '/Ais';
+        $result = preg_match($r, $this->buffer, $out, null, $from);
 
-        if (substr($buffer, -1) !== "\n") {
-            $this->sourcePositions[] = strlen($buffer) + 1;
-        }
+        return $result;
     }
 
     /**
-     * Get source line number and column (given character position in the buffer)
+     * Seek to position in input stream (or return current position in input stream)
      *
-     * @param integer $pos
+     * @param integer $where
      *
      * @return integer
      */
-    private function getSourcePosition($pos)
+    protected function seek($where = null)
     {
-        $low = 0;
-        $high = count($this->sourcePositions);
-
-        while ($low < $high) {
-            $mid = (int) (($high + $low) / 2);
-
-            if ($pos < $this->sourcePositions[$mid]) {
-                $high = $mid - 1;
-                continue;
-            }
-
-            if ($pos >= $this->sourcePositions[$mid + 1]) {
-                $low = $mid + 1;
-                continue;
-            }
-
-            return [$mid + 1, $pos - $this->sourcePositions[$mid]];
+        if ($where === null) {
+            return $this->count;
         }
 
-        return [$low + 1, $pos - $this->sourcePositions[$low]];
+        $this->count = $where;
+
+        return true;
     }
 
     /**
-     * Save internal encoding
+     * Quote regular expression
+     *
+     * @param string $what
+     *
+     * @return string
      */
-    private function saveEncoding()
+    public static function pregQuote($what)
     {
-        if (ini_get('mbstring.func_overload') & 2) {
-            $this->encoding = mb_internal_encoding();
+        return preg_quote($what, '/');
+    }
 
-            mb_internal_encoding('iso-8859-1');
+    /**
+     * @deprecated
+     */
+    protected function show()
+    {
+        if ($this->peek("(.*?)(\n|$)", $m, $this->count)) {
+            return $m[1];
         }
+
+        return '';
     }
 
     /**
-     * Restore internal encoding
+     * Turn list of length 1 into value type
+     *
+     * @param array $value
+     *
+     * @return array
      */
-    private function restoreEncoding()
+    protected function flattenList($value)
     {
-        if ($this->encoding) {
-            mb_internal_encoding($this->encoding);
+        if ($value[0] === 'list' && count($value[2]) === 1) {
+            return $this->flattenList($value[2][0]);
         }
+
+        return $value;
     }
 }
index 221655cad8cad0459b0030d84666fd642f446568..5bd9bef0c8f8a07c09139ce7e30a86ab9b0a1029 100644 (file)
 namespace Leafo\ScssPhp;
 
 use Leafo\ScssPhp\Compiler;
-use Leafo\ScssPhp\Exception\ServerException;
 use Leafo\ScssPhp\Version;
 
 /**
- * Server
+ * SCSS server
  *
  * @author Leaf Corcoran <leafot@gmail.com>
  */
@@ -116,12 +115,13 @@ class Server
     /**
      * Determine whether .scss file needs to be re-compiled.
      *
+     * @param string $in   Input path
      * @param string $out  Output path
      * @param string $etag ETag
      *
      * @return boolean True if compile required.
      */
-    protected function needsCompile($out, &$etag)
+    protected function needsCompile($in, $out, &$etag)
     {
         if (! is_file($out)) {
             return true;
@@ -129,25 +129,21 @@ class Server
 
         $mtime = filemtime($out);
 
+        if (filemtime($in) > $mtime) {
+            return true;
+        }
+
         $metadataName = $this->metadataName($out);
 
         if (is_readable($metadataName)) {
             $metadata = unserialize(file_get_contents($metadataName));
 
-            foreach ($metadata['imports'] as $import => $originalMtime) {
-                $currentMtime = filemtime($import);
-
-                if ($currentMtime !== $originalMtime || $currentMtime > $mtime) {
+            foreach ($metadata['imports'] as $import => $importMtime) {
+                if ($importMtime > $mtime) {
                     return true;
                 }
             }
 
-            $metaVars = crc32(serialize($this->scss->getVariables()));
-
-            if ($metaVars !== $metadata['vars']) {
-                return true;
-            }
-
             $etag = $metadata['etag'];
 
             return false;
@@ -207,21 +203,20 @@ class Server
         $elapsed = round((microtime(true) - $start), 4);
 
         $v    = Version::VERSION;
-        $t    = date('r');
+        $t    = @date('r');
         $css  = "/* compiled by scssphp $v on $t (${elapsed}s) */\n\n" . $css;
         $etag = md5($css);
 
         file_put_contents($out, $css);
         file_put_contents(
             $this->metadataName($out),
-            serialize([
+            serialize(array(
                 'etag'    => $etag,
                 'imports' => $this->scss->getParsedFiles(),
-                'vars'    => crc32(serialize($this->scss->getVariables())),
-            ])
+            ))
         );
 
-        return [$css, $etag];
+        return array($css, $etag);
     }
 
     /**
@@ -231,11 +226,11 @@ class Server
      *
      * @return string
      */
-    protected function createErrorCSS(\Exception $error)
+    protected function createErrorCSS($error)
     {
         $message = str_replace(
-            ["'", "\n"],
-            ["\\'", "\\A"],
+            array("'", "\n"),
+            array("\\'", "\\A"),
             $error->getfile() . ":\n\n" . $error->getMessage()
         );
 
@@ -269,13 +264,11 @@ class Server
      * @param string $out Output file (.css) optional
      *
      * @return string|bool
-     *
-     * @throws \Leafo\ScssPhp\Exception\ServerException
      */
     public function compileFile($in, $out = null)
     {
         if (! is_readable($in)) {
-            throw new ServerException('load error: failed to find ' . $in);
+            throw new \Exception('load error: failed to find ' . $in);
         }
 
         $pi = pathinfo($in);
@@ -325,7 +318,7 @@ class Server
             $output = $this->cacheName($salt . $input);
             $etag = $noneMatch = trim($this->getIfNoneMatchHeader(), '"');
 
-            if ($this->needsCompile($output, $etag)) {
+            if ($this->needsCompile($input, $output, $etag)) {
                 try {
                     list($css, $etag) = $this->compile($input, $output);
 
@@ -336,6 +329,7 @@ class Server
                     header('ETag: "' . $etag . '"');
 
                     echo $css;
+
                 } catch (\Exception $e) {
                     if ($this->showErrorsAsCSS) {
                         header('Content-type: text/css');
@@ -347,6 +341,7 @@ class Server
 
                         echo 'Parse error: ' . $e->getMessage() . "\n";
                     }
+
                 }
 
                 return;
@@ -365,7 +360,7 @@ class Server
             $modifiedSince = $this->getIfModifiedSinceHeader();
             $mtime = filemtime($output);
 
-            if (strtotime($modifiedSince) === $mtime) {
+            if (@strtotime($modifiedSince) === $mtime) {
                 header($protocol . ' 304 Not Modified');
 
                 return;
@@ -395,19 +390,19 @@ class Server
      *
      * @return string Compiled CSS results
      *
-     * @throws \Leafo\ScssPhp\Exception\ServerException
+     * @throws \Exception
      */
     public function checkedCachedCompile($in, $out, $force = false)
     {
         if (! is_file($in) || ! is_readable($in)) {
-            throw new ServerException('Invalid or unreadable input file specified.');
+            throw new \Exception('Invalid or unreadable input file specified.');
         }
 
         if (is_dir($out) || ! is_writable(file_exists($out) ? $out : dirname($out))) {
-            throw new ServerException('Invalid or unwritable output file specified.');
+            throw new \Exception('Invalid or unwritable output file specified.');
         }
 
-        if ($force || $this->needsCompile($out, $etag)) {
+        if ($force || $this->needsCompile($in, $out, $etag)) {
             list($css, $etag) = $this->compile($in, $out);
         } else {
             $css = file_get_contents($out);
@@ -444,10 +439,6 @@ class Server
 
         $this->scss = $scss;
         $this->showErrorsAsCSS = false;
-
-        if (! ini_get('date.timezone')) {
-            date_default_timezone_set('UTC');
-        }
     }
 
     /**
diff --git a/wcfsetup/install/files/lib/system/style/scssphp/src/Type.php b/wcfsetup/install/files/lib/system/style/scssphp/src/Type.php
deleted file mode 100644 (file)
index 8c3886c..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-<?php
-/**
- * SCSSPHP
- *
- * @copyright 2012-2015 Leaf Corcoran
- *
- * @license http://opensource.org/licenses/MIT MIT
- *
- * @link http://leafo.github.io/scssphp
- */
-
-namespace Leafo\ScssPhp;
-
-/**
- * Block/node types
- *
- * @author Anthon Pang <anthon.pang@gmail.com>
- */
-class Type
-{
-    const T_ASSIGN = 'assign';
-    const T_AT_ROOT = 'at-root';
-    const T_BLOCK = 'block';
-    const T_BREAK = 'break';
-    const T_CHARSET = 'charset';
-    const T_COLOR = 'color';
-    const T_COMMENT = 'comment';
-    const T_CONTINUE = 'continue';
-    const T_CONTROL = 'control';
-    const T_DEBUG = 'debug';
-    const T_DIRECTIVE = 'directive';
-    const T_EACH = 'each';
-    const T_ELSE = 'else';
-    const T_ELSEIF = 'elseif';
-    const T_ERROR = 'error';
-    const T_EXPRESSION = 'exp';
-    const T_EXTEND = 'extend';
-    const T_FOR = 'for';
-    const T_FUNCTION = 'function';
-    const T_FUNCTION_CALL = 'fncall';
-    const T_HSL = 'hsl';
-    const T_IF = 'if';
-    const T_IMPORT = 'import';
-    const T_INCLUDE = 'include';
-    const T_INTERPOLATE = 'interpolate';
-    const T_INTERPOLATED = 'interpolated';
-    const T_KEYWORD = 'keyword';
-    const T_LIST = 'list';
-    const T_MAP = 'map';
-    const T_MEDIA = 'media';
-    const T_MEDIA_EXPRESSION = 'mediaExp';
-    const T_MEDIA_TYPE = 'mediaType';
-    const T_MEDIA_VALUE = 'mediaValue';
-    const T_MIXIN = 'mixin';
-    const T_MIXIN_CONTENT = 'mixin_content';
-    const T_NESTED_PROPERTY = 'nestedprop';
-    const T_NOT = 'not';
-    const T_NULL = 'null';
-    const T_NUMBER = 'number';
-    const T_RETURN = 'return';
-    const T_ROOT = 'root';
-    const T_SCSSPHP_IMPORT_ONCE = 'scssphp-import-once';
-    const T_SELF = 'self';
-    const T_STRING = 'string';
-    const T_UNARY = 'unary';
-    const T_VARIABLE = 'var';
-    const T_WARN = 'warn';
-    const T_WHILE = 'while';
-}
index 9f47c1d79762b9b48f7045ba4a3037752c40c1bf..714acdb8951ca37b93c6d6c6a3be58e7ba206149 100644 (file)
@@ -14,7 +14,7 @@ namespace Leafo\ScssPhp;
 use Leafo\ScssPhp\Base\Range;
 
 /**
- * Utilties
+ * SCSS utilties
  *
  * @author Anthon Pang <anthon.pang@gmail.com>
  */
index 80cdeee5adf3dabd4d9554fc18c3af758bd08688..c650a26dbe4e5928b71a0b07d7a3c978f12df05f 100644 (file)
@@ -18,5 +18,5 @@ namespace Leafo\ScssPhp;
  */
 class Version
 {
-    const VERSION = 'v0.6.6';
+    const VERSION = 'v0.3.0';
 }