From 9227d4cebddd56e631550d04d95e45c96888eaaa Mon Sep 17 00:00:00 2001 From: jason_alan_kennedy Date: Wed, 19 Aug 2015 22:49:08 +0000 Subject: [PATCH 001/484] Add gitattributes w/ export-ignore This will allow for a production ready version of scssphp when archived/exported for release(s) and still allowing a development version within the repository. The `export-ignore` excludes un-needed files and directories when a branch is archived for release but these files are still included when the repo is cloned/forked. This would be very useful because releases are intended for production use and when users are using a package (via composer or zip), they most likely are not interested in downloading the source code as a whole into production, thus saving server disk space and meeting security protocol. Think of it as a .gitignore for releases. - e.g. /tests folder, or your .travis.yml file, etc. will not be included in the release zip(s) See: [git-scm.com - Exporting-Your-Repository](http://git-scm.com/book/en/v2/Customizing-Git-Git-Attributes#Exporting-Your-Repository) --- .gitattributes | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..35c78edd --- /dev/null +++ b/.gitattributes @@ -0,0 +1,12 @@ +# Ignore un-needed files and directories for export of production releases +# Keeps src/ +# +# See: http://git-scm.com/book/en/v2/Customizing-Git-Git-Attributes#Exporting-Your-Repository + +tests export-ignore +.gitattributes export-ignore +.gitignore export-ignore +.travis.yml export-ignore +box.json.dist export-ignore +Makefile export-ignore +phpunit.xml.dist export-ignore \ No newline at end of file From aa5089c142c754613f490d0afa223d870c8972e9 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Mon, 24 Aug 2015 10:42:18 -0400 Subject: [PATCH 002/484] composer.json: satis support to exclude files from dist archives --- .gitattributes | 11 +++-------- composer.json | 13 ++++++++++++- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/.gitattributes b/.gitattributes index 35c78edd..a116ff23 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,12 +1,7 @@ -# Ignore un-needed files and directories for export of production releases -# Keeps src/ -# -# See: http://git-scm.com/book/en/v2/Customizing-Git-Git-Attributes#Exporting-Your-Repository - -tests export-ignore +Makefile export-ignore .gitattributes export-ignore .gitignore export-ignore .travis.yml export-ignore box.json.dist export-ignore -Makefile export-ignore -phpunit.xml.dist export-ignore \ No newline at end of file +phpunit.xml.dist export-ignore +tests/ export-ignore diff --git a/composer.json b/composer.json index 3c6ef843..c63339c3 100644 --- a/composer.json +++ b/composer.json @@ -29,5 +29,16 @@ "phpunit/phpunit": "~3.7", "kherge/box": "~2.5" }, - "bin": ["bin/pscss"] + "bin": ["bin/pscss"], + "archive": { + "exclude": [ + "/Makefile", + "/.gitattributes", + "/.gitignore", + "/.travis.yml", + "/box.json.dist", + "/phpunit.xml.dist", + "/tests" + ] + } } From cb81ccee517d347aecb80e6a469785971ae0f828 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 25 Aug 2015 11:29:01 -0400 Subject: [PATCH 003/484] detect @import loop; fixes #315 --- src/Compiler.php | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/Compiler.php b/src/Compiler.php index be7b0ff6..41598a57 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -2649,6 +2649,8 @@ protected function importFile($path, $out) $realPath = realpath($path); if (isset($this->importCache[$realPath])) { + $this->handleImportLoop($realPath); + $tree = $this->importCache[$realPath]; } else { $code = file_get_contents($path); @@ -2733,6 +2735,28 @@ public function throwError($msg) throw new \Exception($msg); } + /** + * Handle import loop + * + * @param string $name + * + * @throws \Exception + */ + private function handleImportLoop($name) + { + for ($env = $this->env; $env; $env = $env->parent) { + $file = $env->block->sourceParser->getSourceName(); + + if (realpath($file) === $name) { + $this->throwError( + 'An @import loop has been found: %s imports %s', + $this->env->block->sourceParser->getSourceName(), + basename($file) + ); + } + } + } + /** * Does file exist? * From 7df0b8c0d14cdb99fc4a0f32d919faabdd2dda64 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 25 Aug 2015 11:37:56 -0400 Subject: [PATCH 004/484] Bump version to 0.2.0 --- src/Version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Version.php b/src/Version.php index 0f419fa9..5d3ce31e 100644 --- a/src/Version.php +++ b/src/Version.php @@ -18,5 +18,5 @@ */ class Version { - const VERSION = 'v0.1.10'; + const VERSION = 'v0.2.0'; } From caae09d55e3af9b30cae0bb973d755e5ddf78b1a Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 25 Aug 2015 12:15:39 -0400 Subject: [PATCH 005/484] phpcs: fix source indentation --- src/Compiler.php | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 41598a57..9ecfffbf 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -2744,17 +2744,17 @@ public function throwError($msg) */ private function handleImportLoop($name) { - for ($env = $this->env; $env; $env = $env->parent) { - $file = $env->block->sourceParser->getSourceName(); - - if (realpath($file) === $name) { - $this->throwError( - 'An @import loop has been found: %s imports %s', - $this->env->block->sourceParser->getSourceName(), - basename($file) - ); - } - } + for ($env = $this->env; $env; $env = $env->parent) { + $file = $env->block->sourceParser->getSourceName(); + + if (realpath($file) === $name) { + $this->throwError( + 'An @import loop has been found: %s imports %s', + $this->env->block->sourceParser->getSourceName(), + basename($file) + ); + } + } } /** From 323d86e11490123fcfa4052fce6e45ffeaca7866 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Fri, 28 Aug 2015 10:19:02 -0400 Subject: [PATCH 006/484] add failing test from #316 --- tests/FailingTest.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/FailingTest.php b/tests/FailingTest.php index f6ec0ffc..850513a2 100644 --- a/tests/FailingTest.php +++ b/tests/FailingTest.php @@ -153,6 +153,17 @@ public function provideFailing() transform: scale(0.5); } to { transform: scale(1); } } +END_OF_EXPECTED + ), + array( + '#316 - swallowed comma before interpolation', <<<'END_OF_SCSS' +foo, #{x, y} { + color: #abc; +} +END_OF_SCSS + , << Date: Fri, 28 Aug 2015 10:36:50 -0400 Subject: [PATCH 007/484] collapseSelector regression; fixes #316 --- src/Compiler.php | 22 ++++++++++++++-------- tests/FailingTest.php | 11 ----------- tests/inputs/interpolation.scss | 4 ++++ tests/outputs/interpolation.css | 3 +++ tests/outputs_numbered/interpolation.css | 3 +++ 5 files changed, 24 insertions(+), 19 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 9ecfffbf..0b84398d 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -680,16 +680,22 @@ protected function evalSelectorPart($part) */ protected function collapseSelectors($selectors) { - $output = ''; + $parts = array(); - array_walk_recursive( - $selectors, - function ($value, $key) use (&$output) { - $output .= $value; - } - ); + foreach ($selectors as $selector) { + $output = ''; + + array_walk_recursive( + $selector, + function ($value, $key) use (&$output) { + $output .= $value; + } + ); + + $parts[] = $output; + } - return $output; + return implode(', ', $parts);; } /** diff --git a/tests/FailingTest.php b/tests/FailingTest.php index 850513a2..f6ec0ffc 100644 --- a/tests/FailingTest.php +++ b/tests/FailingTest.php @@ -153,17 +153,6 @@ public function provideFailing() transform: scale(0.5); } to { transform: scale(1); } } -END_OF_EXPECTED - ), - array( - '#316 - swallowed comma before interpolation', <<<'END_OF_SCSS' -foo, #{x, y} { - color: #abc; -} -END_OF_SCSS - , << Date: Thu, 3 Sep 2015 10:17:37 -0400 Subject: [PATCH 008/484] fix map-get(null) --- src/Compiler.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 0b84398d..e0ac8da7 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -2159,9 +2159,9 @@ public function compileValue($value) $reduced = $this->reduce($exp); switch ($reduced[0]) { case 'string': - $reduced = array('keyword', - $this->compileStringContent($reduced)); + $reduced = array('keyword', $this->compileStringContent($reduced)); break; + case 'null': $reduced = array('keyword', ''); } @@ -3145,6 +3145,12 @@ protected function coerceString($value) case 'keyword': return array('string', '', array($value[1])); + + case 'number': + return array('string', '', array(round($value[1], $this->numberPrecision) . $value[2])); + + case 'null': + return array('string', '', array('')); } return null; From 10a9e9976e530de3dfed01b3fb9c49bdb54bc9b5 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Thu, 3 Sep 2015 10:37:04 -0400 Subject: [PATCH 009/484] add tests --- src/Compiler.php | 2 +- tests/inputs/map.scss | 1 + tests/outputs_numbered/map.css | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index e0ac8da7..d8145dbf 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -4027,7 +4027,7 @@ protected function libMapGet($args) } } - return self::$defaultValue; + return self::$null; } protected static $libMapKeys = array('map'); diff --git a/tests/inputs/map.scss b/tests/inputs/map.scss index 91fe1ad5..8284151f 100644 --- a/tests/inputs/map.scss +++ b/tests/inputs/map.scss @@ -16,6 +16,7 @@ div { baz: map_merge($map, $map2); foo: map_remove($map2, color); bar: if(map_has_key($map, color), true, false); + suppress: map_get($map, null); } // List functions diff --git a/tests/outputs_numbered/map.css b/tests/outputs_numbered/map.css index 124ace48..48658216 100644 --- a/tests/outputs_numbered/map.css +++ b/tests/outputs_numbered/map.css @@ -7,7 +7,7 @@ div { baz: (color: #fff, color2: red, 'color3': #0f0, length: 40em); foo: (length: 40em); bar: true; } -/* line 22, inputs/map.scss */ +/* line 23, inputs/map.scss */ div { foo: color black; bar: color; } From 8da198fc641d61b1f37ed341d055b9e7df9525aa Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Thu, 3 Sep 2015 10:47:48 -0400 Subject: [PATCH 010/484] Add failing test for #318 --- tests/FailingTest.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/FailingTest.php b/tests/FailingTest.php index f6ec0ffc..c3e228c7 100644 --- a/tests/FailingTest.php +++ b/tests/FailingTest.php @@ -153,6 +153,18 @@ public function provideFailing() transform: scale(0.5); } to { transform: scale(1); } } +END_OF_EXPECTED + ), + array( + '#318 - BEM + parent selector @extend', <<<'END_OF_SCSS' +.foo{ + &__bar{background:red;} +} +input{@extend .foo__bar;} +END_OF_SCSS + , << Date: Thu, 3 Sep 2015 11:10:13 -0400 Subject: [PATCH 011/484] add partial test for #295 --- tests/FailingTest.php | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/FailingTest.php b/tests/FailingTest.php index c3e228c7..ac66c41e 100644 --- a/tests/FailingTest.php +++ b/tests/FailingTest.php @@ -153,6 +153,23 @@ public function provideFailing() transform: scale(0.5); } to { transform: scale(1); } } +END_OF_EXPECTED + ), + array( + '#295 - variable scope bug', <<<'END_OF_SCSS' +@mixin example { + $color: red; +} + +A { + $color: green; + @include example; + color: $color; +} +END_OF_SCSS + , << Date: Thu, 3 Sep 2015 18:18:43 -0400 Subject: [PATCH 012/484] minor refactoring --- src/Compiler.php | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index d8145dbf..52278566 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -3136,24 +3136,11 @@ protected function coerceColor($value) */ protected function coerceString($value) { - switch ($value[0]) { - case 'string': - return $value; - - case 'function': - return array('string', '', array($value[1] . '(' . $this->flattenList($value[2]) . ')')); - - case 'keyword': - return array('string', '', array($value[1])); - - case 'number': - return array('string', '', array(round($value[1], $this->numberPrecision) . $value[2])); - - case 'null': - return array('string', '', array('')); + if ($value[0] === 'string') { + return $value; } - return null; + return array('string', '', array($this->compileValue($value))); } /** From 5245d42cc93b1c50a3e2af2eda97f7026a80f960 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sat, 5 Sep 2015 18:09:03 -0400 Subject: [PATCH 013/484] internals: cleanup --- src/Compiler.php | 170 ++++++++++++++++++++------------------- src/Formatter/Nested.php | 6 +- src/Parser.php | 48 +++++------ 3 files changed, 113 insertions(+), 111 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 52278566..eb74c5c8 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -295,7 +295,7 @@ protected function flattenSelectors($block, $parentKey = null) $block->selectors[] = $this->compileSelector($selector); } - if ($placeholderSelector && 0 == count($block->selectors) && null !== $parentKey) { + if ($placeholderSelector && 0 === count($block->selectors) && null !== $parentKey) { unset($block->parent->children[$parentKey]); return; @@ -343,7 +343,7 @@ protected function matchExtends($selector, &$out, $from = 0, $initial = true) $after ); - if ($result == $selector) { + if ($result === $selector) { continue; } @@ -399,7 +399,7 @@ protected function matchExtendsSingle($single, &$outOrigin) list($target, $origin) = $this->extends[$idx]; // check count - if ($count != count($target)) { + if ($count !== count($target)) { continue; } @@ -507,7 +507,7 @@ protected function compileMedia($media) protected function mediaParent($scope) { while (! empty($scope->parent)) { - if (! empty($scope->type) && $scope->type != 'media') { + if (! empty($scope->type) && $scope->type !== 'media') { break; } @@ -793,7 +793,7 @@ protected function hasSelectorPlaceholder($selector) foreach ($selector as $parts) { foreach ($parts as $part) { - if ('%' == $part[0]) { + if ('%' === $part[0]) { return true; } } @@ -927,27 +927,27 @@ protected function mergeMediaTypes($type1, $type2) $t2 = strtolower($type2[0]); } - if (($m1 == 'not') ^ ($m2 == 'not')) { - if ($t1 == $t2) { + if (($m1 === 'not') ^ ($m2 === 'not')) { + if ($t1 === $t2) { return null; } return array( - $m1 == 'not' ? $m2 : $m1, - $m1 == 'not' ? $t2 : $t1 + $m1 === 'not' ? $m2 : $m1, + $m1 === 'not' ? $t2 : $t1 ); } - if ($m1 == 'not' && $m2 == 'not') { + if ($m1 === 'not' && $m2 === 'not') { // CSS has no way of representing "neither screen nor print" - if ($t1 != $t2) { + if ($t1 !== $t2) { return null; } return array('not', $t1); } - if ($t1 != $t2) { + if ($t1 !== $t2) { return null; } @@ -965,7 +965,7 @@ protected function mergeMediaTypes($type1, $type2) */ protected function compileImport($rawPath, $out) { - if ($rawPath[0] == 'string') { + if ($rawPath[0] === 'string') { $path = $this->compileStringContent($rawPath); if ($path = $this->findImport($path)) { @@ -977,14 +977,14 @@ protected function compileImport($rawPath, $out) return false; } - if ($rawPath[0] == 'list') { + if ($rawPath[0] === 'list') { // handle a list of strings - if (count($rawPath[2]) == 0) { + if (count($rawPath[2]) === 0) { return false; } foreach ($rawPath[2] as $path) { - if ($path[0] != 'string') { + if ($path[0] !== 'string') { return false; } } @@ -1054,7 +1054,7 @@ protected function compileChild($child, $out) case 'assign': list(, $name, $value) = $child; - if ($name[0] == 'var') { + if ($name[0] === 'var') { $flag = isset($child[3]) ? $child[3] : null; $isDefault = $flag === '!default'; $isGlobal = $flag === '!global'; @@ -1067,7 +1067,7 @@ protected function compileChild($child, $out) if ($isDefault) { $existingValue = $this->get($name[1], true); - $shouldSet = $existingValue === true || $existingValue == self::$null; + $shouldSet = $existingValue === true || $existingValue === self::$null; } if (! $isDefault || $shouldSet) { @@ -1093,10 +1093,10 @@ protected function compileChild($child, $out) // if the value reduces to null from something else then // the property should be discarded - if ($value[0] != 'null') { + if ($value[0] !== 'null') { $value = $this->reduce($value); - if ($value[0] == 'null') { + if ($value[0] === 'null') { break; } } @@ -1110,7 +1110,7 @@ protected function compileChild($child, $out) break; case 'comment': - if ($out->type == 'root') { + if ($out->type === 'root') { $this->compileComment($child); break; } @@ -1131,7 +1131,9 @@ protected function compileChild($child, $out) foreach ($selectors as $sel) { // only use the first one $result = $this->evalSelectors(array($sel)); - $this->pushExtends(current($result[0]), $out->selectors); + $result = current($result[0]); + + $this->pushExtends($result, $out->selectors); } break; @@ -1143,8 +1145,8 @@ protected function compileChild($child, $out) } foreach ($if->cases as $case) { - if ($case->type == 'else' || - $case->type == '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); } @@ -1162,7 +1164,7 @@ protected function compileChild($child, $out) foreach ($list[2] as $item) { $this->pushEnv(); - if (count($each->vars) == 1) { + if (count($each->vars) === 1) { $this->set($each->vars[0], $item); } else { list(,, $values) = $this->coerceList($item); @@ -1228,11 +1230,11 @@ protected function compileChild($child, $out) $prefix = $this->compileValue($prop->prefix) . '-'; foreach ($prop->children as $child) { - if ($child[0] == 'assign') { + if ($child[0] === 'assign') { array_unshift($child[1][2], $prefix); } - if ($child[0] == 'nestedprop') { + if ($child[0] === 'nestedprop') { array_unshift($child[1]->prefix[2], $prefix); } @@ -1367,7 +1369,7 @@ protected function expToString($exp) */ protected function isTruthy($value) { - return $value != self::$false && $value != self::$null; + return $value !== self::$false && $value !== self::$null; } /** @@ -1381,7 +1383,7 @@ protected function shouldEval($value) { switch ($value[0]) { case 'exp': - if ($value[1] == '/') { + if ($value[1] === '/') { return $this->shouldEval($value[2], $value[3]); } @@ -1417,7 +1419,7 @@ protected function reduce($value, $inExp = false) $right = $this->reduce($right, true); // special case: looks like css short-hand - if ($opName == 'div' && ! $inParens && ! $inExp && isset($right[2]) && $right[2] != '') { + if ($opName === 'div' && ! $inParens && ! $inExp && isset($right[2]) && $right[2] !== '') { return $this->expToString($value); } @@ -1448,34 +1450,34 @@ protected function reduce($value, $inExp = false) $unitChange = false; if (! isset($genOp) && - $left[0] == 'number' && $right[0] == 'number' + $left[0] === 'number' && $right[0] === 'number' ) { - if ($opName == 'mod' && $right[2] != '') { + if ($opName === 'mod' && $right[2] !== '') { $this->throwError("Cannot modulo by a number with units: $right[1]$right[2]."); } $unitChange = true; - $emptyUnit = $left[2] == '' || $right[2] == ''; - $targetUnit = '' != $left[2] ? $left[2] : $right[2]; + $emptyUnit = $left[2] === '' || $right[2] === ''; + $targetUnit = '' !== $left[2] ? $left[2] : $right[2]; - if ($opName != 'mul') { - $left[2] = '' != $left[2] ? $left[2] : $targetUnit; - $right[2] = '' != $right[2] ? $right[2] : $targetUnit; + if ($opName !== 'mul') { + $left[2] = '' !== $left[2] ? $left[2] : $targetUnit; + $right[2] = '' !== $right[2] ? $right[2] : $targetUnit; } - if ($opName != 'mod') { + if ($opName !== 'mod') { $left = $this->normalizeNumber($left); $right = $this->normalizeNumber($right); } - if ($opName == 'div' && ! $emptyUnit && $left[2] == $right[2]) { + if ($opName === 'div' && ! $emptyUnit && $left[2] === $right[2]) { $targetUnit = ''; } - 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]) { + 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] = ''; } @@ -1490,7 +1492,7 @@ protected function reduce($value, $inExp = false) } if (isset($out)) { - if ($unitChange && $out[0] == 'number') { + if ($unitChange && $out[0] === 'number') { $out = $this->coerceUnit($out, $targetUnit); } @@ -1506,7 +1508,7 @@ protected function reduce($value, $inExp = false) $inExp = $inExp || $this->shouldEval($exp); $exp = $this->reduce($exp); - if ($exp[0] == 'number') { + if ($exp[0] === 'number') { switch ($op) { case '+': return $exp; @@ -1518,9 +1520,9 @@ protected function reduce($value, $inExp = false) } } - if ($op == 'not') { + if ($op === 'not') { if ($inExp || $inParens) { - if ($exp == self::$false) { + if ($exp === self::$false) { return self::$true; } @@ -1645,7 +1647,7 @@ public function normalizeValue($value) case 'list': $value = $this->extractInterpolation($value); - if ($value[0] != 'list') { + if ($value[0] !== 'list') { return array('keyword', $this->compileValue($value)); } @@ -1766,7 +1768,7 @@ protected function opModNumberNumber($left, $right) protected function opAdd($left, $right) { if ($strLeft = $this->coerceString($left)) { - if ($right[0] == 'string') { + if ($right[0] === 'string') { $right[1] = ''; } @@ -1776,7 +1778,7 @@ protected function opAdd($left, $right) } if ($strRight = $this->coerceString($right)) { - if ($left[0] == 'string') { + if ($left[0] === 'string') { $left[1] = ''; } @@ -1801,7 +1803,7 @@ protected function opAnd($left, $right, $shouldEval) return; } - if ($left != self::$false) { + if ($left !== self::$false) { return $right; } @@ -1823,7 +1825,7 @@ protected function opOr($left, $right, $shouldEval) return; } - if ($left != self::$false) { + if ($left !== self::$false) { return $left; } @@ -1950,7 +1952,7 @@ protected function opEq($left, $right) $right = $this->compileValue($rStr); } - return $this->toBool($left == $right); + return $this->toBool($left === $right); } /** @@ -1971,7 +1973,7 @@ protected function opNeq($left, $right) $right = $this->compileValue($rStr); } - return $this->toBool($left != $right); + return $this->toBool($left !== $right); } /** @@ -2078,7 +2080,7 @@ public function compileValue($value) $g = round($g); $b = round($b); - if (count($value) == 5 && $value[4] != 1) { // rgba + if (count($value) === 5 && $value[4] !== 1) { // rgba return 'rgba(' . $r . ', ' . $g . ', ' . $b . ', ' . $value[4] . ')'; } @@ -2105,7 +2107,7 @@ public function compileValue($value) case 'list': $value = $this->extractInterpolation($value); - if ($value[0] != 'list') { + if ($value[0] !== 'list') { return $this->compileValue($value); } @@ -2114,7 +2116,7 @@ public function compileValue($value) $filtered = array(); foreach ($items as $item) { - if ($item[0] == 'null') { + if ($item[0] === 'null') { continue; } @@ -2222,7 +2224,7 @@ protected function extractInterpolation($list) $items = $list[2]; foreach ($items as $i => $item) { - if ($item[0] == 'interpolate') { + if ($item[0] === 'interpolate') { $before = array('list', $list[1], array_slice($items, 0, $i)); $after = array('list', $list[1], array_slice($items, $i + 1)); @@ -2287,7 +2289,7 @@ protected function joinSelectors($parent, $child) $newPart = array(); foreach ($part as $p) { - if ($p == self::$selfSelector) { + if ($p === self::$selfSelector) { $setSelf = true; foreach ($parent as $i => $parentPart) { @@ -2322,7 +2324,7 @@ protected function joinSelectors($parent, $child) protected function multiplyMedia($env, $childQueries = null) { if (! isset($env) || - ! empty($env->block->type) && $env->block->type != 'media' + ! empty($env->block->type) && $env->block->type !== 'media' ) { return $childQueries; } @@ -2333,7 +2335,7 @@ protected function multiplyMedia($env, $childQueries = null) } $parentQueries = $env->block->queryList; - if ($childQueries == null) { + if ($childQueries === null) { $childQueries = $parentQueries; } else { $originalQueries = $childQueries; @@ -2697,7 +2699,7 @@ public function findImport($url) // check urls for normal import paths foreach ($urls as $full) { $full = $dir . - (! empty($dir) && substr($dir, -1) != '/' ? '/' : '') . + (! empty($dir) && substr($dir, -1) !== '/' ? '/' : '') . $full; if ($this->fileExists($file = $full . '.scss') || @@ -2953,10 +2955,10 @@ protected function applyArguments($argDef, $argValues) } } elseif (count($keywordArgs)) { $this->throwError('Positional arguments must come before keyword arguments.'); - } elseif ($arg[2] == true) { + } elseif ($arg[2] === true) { $val = $this->reduce($arg[1], true); - if ($val[0] == 'list') { + if ($val[0] === 'list') { foreach ($val[2] as $name => $item) { if (! is_numeric($name)) { $keywordArgs[$name] = $item; @@ -3043,7 +3045,7 @@ protected function coerceMap($item) return $item; } - if ($item == self::$emptyList) { + if ($item === self::$emptyList) { return self::$emptyMap; } @@ -3059,11 +3061,11 @@ protected function coerceMap($item) */ protected function coerceList($item, $delim = ',') { - if (isset($item) && $item[0] == 'list') { + if (isset($item) && $item[0] === 'list') { return $item; } - if (isset($item) && $item[0] == 'map') { + if (isset($item) && $item[0] === 'map') { $keys = $item[1]; $values = $item[2]; $list = array(); @@ -3152,8 +3154,8 @@ protected function coerceString($value) */ protected function coercePercent($value) { - if ($value[0] == 'number') { - if ($value[2] == '%') { + if ($value[0] === 'number') { + if ($value[2] === '%') { return $value[1] / 100; } @@ -3178,7 +3180,7 @@ public function assertMap($value) { $value = $this->coerceMap($value); - if ($value[0] != 'map') { + if ($value[0] !== 'map') { $this->throwError('expecting map'); } @@ -3198,7 +3200,7 @@ public function assertMap($value) */ public function assertList($value) { - if ($value[0] != 'list') { + if ($value[0] !== 'list') { $this->throwError('expecting list'); } @@ -3238,7 +3240,7 @@ public function assertColor($value) */ public function assertNumber($value) { - if ($value[0] != 'number') { + if ($value[0] !== 'number') { $this->throwError('expecting number'); } @@ -3286,7 +3288,7 @@ public function toHSL($red, $green, $blue) $l = $min + $max; $d = $max - $min; - if ((int) $d == 0) { + if ((int) $d === 0) { $h = $s = 0; } else { if ($l < 255) { @@ -3391,7 +3393,7 @@ protected function libIndex($args) { list($list, $value) = $args; - if ($value[0] == 'map') { + if ($value[0] === 'map') { return self::$null; } @@ -3454,7 +3456,7 @@ protected function alterColor($args, $fn) foreach (array(1, 2, 3, 7) as $i) { if (isset($args[$i])) { $val = $this->assertNumber($args[$i]); - $ii = $i == 7 ? 4 : $i; // alpha + $ii = $i === 7 ? 4 : $i; // alpha $color[$ii] = call_user_func($fn, isset($color[$ii]) ? $color[$ii] : 0, $val, $i); } } @@ -3619,7 +3621,7 @@ protected function libMix($args) $w = $weight * 2 - 1; $a = $firstAlpha - $secondAlpha; - $w1 = (($w * $a == -1 ? $w : ($w + $a) / (1 + $w * $a)) + 1) / 2.0; + $w1 = (($w * $a === -1 ? $w : ($w + $a) / (1 + $w * $a)) + 1) / 2.0; $w2 = 1.0 - $w1; $new = array('color', @@ -3824,7 +3826,7 @@ protected function libUnquote($args) { $str = $args[0]; - if ($str[0] == 'string') { + if ($str[0] === 'string') { $str[1] = ''; } @@ -3836,7 +3838,7 @@ protected function libQuote($args) { $value = $args[0]; - if ($value[0] == 'string' && ! empty($value[1])) { + if ($value[0] === 'string' && ! empty($value[1])) { return $value; } @@ -3929,7 +3931,7 @@ protected function getNormalizedNumbers($args) $numbers = array(); foreach ($args as $key => $item) { - if ('number' != $item[0]) { + if ('number' !== $item[0]) { $this->throwError('%s is not a number', $item[0]); } @@ -4152,7 +4154,7 @@ protected function libTypeOf($args) switch ($value[0]) { case 'keyword': - if ($value == self::$true || $value == self::$false) { + if ($value === self::$true || $value === self::$false) { return 'bool'; } @@ -4180,7 +4182,7 @@ protected function libUnit($args) { $num = $args[0]; - if ($num[0] == 'number') { + if ($num[0] === 'number') { return array('string', '"', array($num[2])); } @@ -4192,7 +4194,7 @@ protected function libUnitless($args) { $value = $args[0]; - return $value[0] == 'number' && empty($value[2]); + return $value[0] === 'number' && empty($value[2]); } protected static $libComparable = array('number-1', 'number-2'); @@ -4200,14 +4202,14 @@ protected function libComparable($args) { list($number1, $number2) = $args; - if (! isset($number1[0]) || $number1[0] != 'number' || ! isset($number2[0]) || $number2[0] != 'number') { + if (! isset($number1[0]) || $number1[0] !== 'number' || ! isset($number2[0]) || $number2[0] !== 'number') { $this->throwError('Invalid argument(s) for "comparable"'); } $number1 = $this->normalizeNumber($number1); $number2 = $this->normalizeNumber($number2); - return $number1[2] == $number2[2] || $number1[2] == '' || $number2[2] == ''; + return $number1[2] === $number2[2] || $number1[2] === '' || $number2[2] === ''; } protected static $libStrIndex = array('string', 'substring'); diff --git a/src/Formatter/Nested.php b/src/Formatter/Nested.php index 1bad2f27..bd2c4d21 100644 --- a/src/Formatter/Nested.php +++ b/src/Formatter/Nested.php @@ -101,7 +101,7 @@ protected function blockLines($inner, $block) */ protected function block($block) { - if ($block->type == 'root') { + if ($block->type === 'root') { $this->adjustAllChildren($block); } @@ -126,7 +126,7 @@ protected function block($block) if (isset($block->children[$i + 1])) { $next = $block->children[$i + 1]; - if ($next->depth == max($block->depth, 1) && $child->depth >= $next->depth) { + if ($next->depth === max($block->depth, 1) && $child->depth >= $next->depth) { echo $this->break; } } @@ -138,7 +138,7 @@ protected function block($block) echo $this->close; } - if ($block->type == 'root') { + if ($block->type === 'root') { echo $this->break; } } diff --git a/src/Parser.php b/src/Parser.php index bb085a5f..16b4b177 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -141,7 +141,7 @@ public function parse($buffer) ; } - if ($this->count != strlen($this->buffer)) { + if ($this->count !== strlen($this->buffer)) { $this->throwParseError(); } @@ -240,7 +240,7 @@ protected function parseChunk() $s = $this->seek(); // the directives - if (isset($this->buffer[$this->count]) && $this->buffer[$this->count] == '@') { + if (isset($this->buffer[$this->count]) && $this->buffer[$this->count] === '@') { if ($this->literal('@media') && $this->mediaQueryList($mediaQueryList) && $this->literal('{')) { $media = $this->pushSpecialBlock('media', $s); $media->queryList = $mediaQueryList[2]; @@ -446,7 +446,7 @@ protected function parseChunk() $last = $this->last(); - if (isset($last) && $last[0] == 'if') { + if (isset($last) && $last[0] === 'if') { list(, $if) = $last; if ($this->literal('@else')) { @@ -580,7 +580,7 @@ protected function parseChunk() if ($this->literal('}')) { $block = $this->popBlock(); - if (isset($block->type) && $block->type == 'include') { + if (isset($block->type) && $block->type === 'include') { $include = $block->child; unset($block->child); $include[3] = $block; @@ -645,7 +645,7 @@ protected function literal($what, $eatWhitespace = null) // shortcut on single letter if (! isset($what[1]) && isset($this->buffer[$this->count])) { - if ($this->buffer[$this->count] == $what) { + if ($this->buffer[$this->count] === $what) { if (! $eatWhitespace) { $this->count++; @@ -987,13 +987,13 @@ protected function genericList(&$out, $parseItem, $delim = '', $flatten = true) } } - if (count($items) == 0) { + if (count($items) === 0) { $this->seek($s); return false; } - if ($flatten && count($items) == 1) { + if ($flatten && count($items) === 1) { $out = $items[0]; } else { $out = array('list', $delim, $items); @@ -1146,7 +1146,7 @@ protected function value(&$out) } if ($this->keyword($keyword)) { - if ($keyword == 'null') { + if ($keyword === 'null') { $out = array('null'); } else { $out = array('keyword', $keyword); @@ -1239,13 +1239,13 @@ protected function func(&$func) if ($this->keyword($name, false) && $this->literal('(') ) { - if ($name == 'alpha' && $this->argumentList($args)) { + if ($name === 'alpha' && $this->argumentList($args)) { $func = array('function', $name, array('string', '', $args)); return true; } - if ($name != 'expression' && ! preg_match('/^(-[a-z]+-)?calc$/', $name)) { + if ($name !== 'expression' && ! preg_match('/^(-[a-z]+-)?calc$/', $name)) { $ss = $this->seek(); if ($this->argValues($args) && $this->literal(')')) { @@ -1498,7 +1498,7 @@ protected function string(&$out) while ($this->matchString($m, $delim)) { $content[] = $m[1]; - if ($m[2] == '#{') { + if ($m[2] === '#{') { $this->count -= strlen($m[2]); if ($this->interpolation($inter, false)) { @@ -1507,7 +1507,7 @@ protected function string(&$out) $this->count += strlen($m[2]); $content[] = '#{'; // ignore it } - } elseif ($m[2] == '\\') { + } elseif ($m[2] === '\\') { $content[] = $m[2]; if ($this->literal($delim, false)) { @@ -1564,7 +1564,7 @@ protected function mixedKeyword(&$out) $this->eatWhiteDefault = $oldWhite; - if (count($parts) == 0) { + if (count($parts) === 0) { return false; } @@ -1614,16 +1614,16 @@ protected function openString($end, &$out, $nestingOpen = null) $this->count-= strlen($tok); - if ($tok == $end && ! $nestingLevel--) { + if ($tok === $end && ! $nestingLevel--) { break; } - if (($tok == '\'' || $tok == '"') && $this->string($str)) { + if (($tok === '\'' || $tok === '"') && $this->string($str)) { $content[] = $str; continue; } - if ($tok == '#{' && $this->interpolation($inter)) { + if ($tok === '#{' && $this->interpolation($inter)) { $content[] = $inter; continue; } @@ -1634,7 +1634,7 @@ protected function openString($end, &$out, $nestingOpen = null) $this->eatWhiteDefault = $oldWhite; - if (count($content) == 0) { + if (count($content) === 0) { return false; } @@ -1707,7 +1707,7 @@ protected function propertyName(&$out) $parts[] = $inter; } elseif ($this->keyword($text)) { $parts[] = $text; - } elseif (count($parts) == 0 && $this->match('[:.#]', $m, false)) { + } elseif (count($parts) === 0 && $this->match('[:.#]', $m, false)) { // css hacks $parts[] = $m[0]; } else { @@ -1717,7 +1717,7 @@ protected function propertyName(&$out) $this->eatWhiteDefault = $oldWhite; - if (count($parts) == 0) { + if (count($parts) === 0) { return false; } @@ -1766,7 +1766,7 @@ protected function selectors(&$out) } } - if (count($selectors) == 0) { + if (count($selectors) === 0) { $this->seek($s); return false; @@ -1802,7 +1802,7 @@ protected function selector(&$out) } - if (count($selector) == 0) { + if (count($selector) === 0) { return false; } @@ -1979,7 +1979,7 @@ protected function selectorSingle(&$out) $this->eatWhiteDefault = $oldWhite; - if (count($parts) == 0) { + if (count($parts) === 0) { return false; } @@ -2080,7 +2080,7 @@ protected function end() return true; } - if ($this->count == strlen($this->buffer) || $this->buffer[$this->count] == '}') { + if ($this->count === strlen($this->buffer) || $this->buffer[$this->count] === '}') { // if there is end of file or a closing block next then we don't need a ; return true; } @@ -2333,7 +2333,7 @@ protected function show() */ protected function flattenList($value) { - if ($value[0] == 'list' && count($value[2]) == 1) { + if ($value[0] === 'list' && count($value[2]) === 1) { return $this->flattenList($value[2][0]); } From 064ac6d572adca51c3df72a300c6fe4b197b18e5 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sat, 5 Sep 2015 18:15:49 -0400 Subject: [PATCH 014/484] fix nested_function_def from scss_test.rb --- src/Compiler.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index eb74c5c8..a211f686 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -2424,10 +2424,10 @@ protected function setExisting($name, $value, $env = null) $env = $this->getStoreEnv(); } - if (isset($env->store[$name]) || ! isset($env->parent)) { - $env->store[$name] = $value; - } else { + if (! isset($env->store[$name]) && isset($env->parent) && $this->has($name, $env->parent)) { $this->setExisting($name, $value, $env->parent); + } else { + $env->store[$name] = $value; } } From 9c769106419797d9b39213b2e69fe740f00e83fc Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sat, 5 Sep 2015 19:11:19 -0400 Subject: [PATCH 015/484] fixes #319 - don't throw exception in Compiler injectVariables by coercing values --- src/Compiler.php | 45 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index a211f686..9faf8bdb 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -695,7 +695,7 @@ function ($value, $key) use (&$output) { $parts[] = $output; } - return implode(', ', $parts);; + return implode(', ', $parts); } /** @@ -2515,7 +2515,7 @@ protected function injectVariables(array $args) } if (! $parser->parseValue($strValue, $value)) { - throw new \Exception("failed to parse passed in variable $name: $strValue"); + $value = $this->coerceValue($strValue); } $this->set($name, $value); @@ -2818,14 +2818,7 @@ protected function callBuiltin($name, $args, &$returnValue) } if (isset($returnValue)) { - // coerce a php value into a scss one - if (is_numeric($returnValue)) { - $returnValue = array('number', $returnValue, ''); - } elseif (is_bool($returnValue)) { - $returnValue = $returnValue ? self::$true : self::$false; - } elseif (! is_array($returnValue)) { - $returnValue = array('keyword', $returnValue); - } + $returnValue = $this->coerceValue($returnValue); return true; } @@ -3013,6 +3006,38 @@ protected function applyArguments($argDef, $argValues) } } + /** + * Coerce a php value into a scss one + * + * @param mixed $value + * + * @return array + */ + private function coerceValue($value) + { + if (is_array($value)) { + return $value; + } + + if (is_bool($value)) { + return $value ? self::$true : self::$false; + } + + if ($value === null) { + $value = self::$null; + } + + if (is_numeric($value)) { + return array('number', $value, ''); + } + + if ($value === '') { + return self::$emptyString; + } + + return array('keyword', $value); + } + /** * Coerce unit on number to be normalized * From 94efd300fd5ac35ffe39e538eba6727e2e6e5fef Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sat, 5 Sep 2015 21:19:45 -0400 Subject: [PATCH 016/484] internals: unreachable code --- src/Compiler.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 9faf8bdb..bdfb1bf5 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -381,10 +381,6 @@ protected function matchExtendsSingle($single, &$outOrigin) $counts = array(); foreach ($single as $part) { - if (! is_string($part)) { - return false; // hmm - } - if (isset($this->extendsMap[$part])) { foreach ($this->extendsMap[$part] as $idx) { $counts[$idx] = isset($counts[$idx]) ? $counts[$idx] + 1 : 1; From 72475fabb53bb1fc79bed0b3a975f4362b36a891 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sat, 5 Sep 2015 22:19:51 -0400 Subject: [PATCH 017/484] fixes #318 (more or less) --- src/Compiler.php | 13 +++++++++++-- tests/FailingTest.php | 12 ------------ tests/inputs/extends.scss | 5 +++++ tests/outputs/extends.css | 2 ++ tests/outputs_numbered/extends.css | 7 +++++++ 5 files changed, 25 insertions(+), 14 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index bdfb1bf5..1b6f5034 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -371,14 +371,23 @@ protected function matchExtends($selector, &$out, $from = 0, $initial = true) /** * Match extends single * - * @param array $single + * @param array $rawSingle * @param array $outOrigin * * @return boolean */ - protected function matchExtendsSingle($single, &$outOrigin) + protected function matchExtendsSingle($rawSingle, &$outOrigin) { $counts = array(); + $single = array(); + + foreach ($rawSingle as $part) { + if (! preg_match('/^[\[.:#%]/', $part) && count($single)) { + $single[count($single) - 1] .= $part; + } else { + $single[] = $part; + } + } foreach ($single as $part) { if (isset($this->extendsMap[$part])) { diff --git a/tests/FailingTest.php b/tests/FailingTest.php index ac66c41e..cf4677d3 100644 --- a/tests/FailingTest.php +++ b/tests/FailingTest.php @@ -170,18 +170,6 @@ public function provideFailing() , << Date: Sat, 5 Sep 2015 23:15:44 -0400 Subject: [PATCH 018/484] Bump version to 0.2.1 --- src/Version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Version.php b/src/Version.php index 5d3ce31e..32f0bf74 100644 --- a/src/Version.php +++ b/src/Version.php @@ -18,5 +18,5 @@ */ class Version { - const VERSION = 'v0.2.0'; + const VERSION = 'v0.2.1'; } From 3fb23ddc146d85244d0f1ca3d23d83a298bb8f90 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sat, 5 Sep 2015 23:18:49 -0400 Subject: [PATCH 019/484] fixes #320 - Compiler getParsedFiles() compat-buster --- src/Compiler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compiler.php b/src/Compiler.php index 1b6f5034..e30f6306 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -2669,8 +2669,8 @@ protected function importFile($path, $out) $code = file_get_contents($path); $parser = new Parser($path, false); $tree = $parser->parse($code); - $this->parsedFiles[] = $path; + $this->parsedFiles[realPath] = filemtime($path); $this->importCache[$realPath] = $tree; } From 1dbd589e19d6978ff0e007a4d89c254748d2cc82 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sun, 6 Sep 2015 00:02:08 -0400 Subject: [PATCH 020/484] fix unit test --- src/Server.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Server.php b/src/Server.php index c2304dd4..5bd9bef0 100644 --- a/src/Server.php +++ b/src/Server.php @@ -138,8 +138,8 @@ protected function needsCompile($in, $out, &$etag) if (is_readable($metadataName)) { $metadata = unserialize(file_get_contents($metadataName)); - foreach ($metadata['imports'] as $import) { - if (filemtime($import) > $mtime) { + foreach ($metadata['imports'] as $import => $importMtime) { + if ($importMtime > $mtime) { return true; } } From e86ae9c10bd04107e9e102839deebe0f6a33c071 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sun, 6 Sep 2015 03:05:54 -0400 Subject: [PATCH 021/484] fix @each scope --- src/Compiler.php | 15 ++++++++++----- tests/inputs/looping.scss | 4 ++++ tests/outputs/looping.css | 3 ++- tests/outputs_numbered/looping.css | 15 ++++++++------- 4 files changed, 24 insertions(+), 13 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index e30f6306..598171d6 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -536,9 +536,11 @@ protected function compileNestedBlock($block, $selectors) $this->scope = $this->makeOutputBlock($block->type, $selectors); $this->scope->parent->children[] = $this->scope; + $this->compileChildren($block->children, $this->scope); $this->scope = $this->scope->parent; + $this->popEnv(); } @@ -1166,26 +1168,29 @@ protected function compileChild($child, $out) $list = $this->coerceList($this->reduce($each->list)); - foreach ($list[2] as $item) { - $this->pushEnv(); + $this->pushEnv(); + foreach ($list[2] as $item) { if (count($each->vars) === 1) { - $this->set($each->vars[0], $item); + $this->set($each->vars[0], $item, true); } else { list(,, $values) = $this->coerceList($item); foreach ($each->vars as $i => $var) { - $this->set($var, isset($values[$i]) ? $values[$i] : self::$null); + $this->set($var, isset($values[$i]) ? $values[$i] : self::$null, true); } } $ret = $this->compileChildren($each->children, $out); - $this->popEnv(); if ($ret) { + $this->popEnv(); + return $ret; } } + + $this->popEnv(); break; case 'while': diff --git a/tests/inputs/looping.scss b/tests/inputs/looping.scss index d0325dc0..dd96b12c 100644 --- a/tests/inputs/looping.scss +++ b/tests/inputs/looping.scss @@ -1,5 +1,7 @@ div { + $var: red; + @each $var in what is this { color: $var; } @@ -32,6 +34,8 @@ div { border: $nonexistent; } } + + color: $var; } diff --git a/tests/outputs/looping.css b/tests/outputs/looping.css index 2836f70d..1dd81d68 100644 --- a/tests/outputs/looping.css +++ b/tests/outputs/looping.css @@ -16,7 +16,8 @@ div { background: this this; background: what what; background: is is; - background: this this; } + background: this this; + color: red; } div h1 { font-size: 2em; } div h2 { diff --git a/tests/outputs_numbered/looping.css b/tests/outputs_numbered/looping.css index 98838286..921e978f 100644 --- a/tests/outputs_numbered/looping.css +++ b/tests/outputs_numbered/looping.css @@ -17,17 +17,18 @@ div { background: this this; background: what what; background: is is; - background: this this; } -/* line 30, inputs/looping.scss */ + background: this this; + color: red; } +/* line 32, inputs/looping.scss */ div h1 { font-size: 2em; } -/* line 30, inputs/looping.scss */ +/* line 32, inputs/looping.scss */ div h2 { font-size: 1.5em; } -/* line 30, inputs/looping.scss */ +/* line 32, inputs/looping.scss */ div h3 { font-size: 1.2em; } -/* line 38, inputs/looping.scss */ +/* line 42, inputs/looping.scss */ span { color: 0; color: 1; @@ -40,7 +41,7 @@ span { color: 8; color: 9; color: 10; } -/* line 46, inputs/looping.scss */ +/* line 50, inputs/looping.scss */ pre { color: 1; color: 2; @@ -59,7 +60,7 @@ pre { cool: 5; cool: 4; cool: 3; } -/* line 81, inputs/looping.scss */ +/* line 85, inputs/looping.scss */ div { a: false; b: true; From da33429c3525c0cf5858ef01bf0ad1df113b7924 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sun, 6 Sep 2015 06:28:05 -0400 Subject: [PATCH 022/484] internals: remove recursion from Compiler setExisting() and get() --- src/Compiler.php | 62 +++++++++++++++++++++++++++++------------------- 1 file changed, 37 insertions(+), 25 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 598171d6..7a2a8cd5 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -1068,14 +1068,12 @@ protected function compileChild($child, $out) if ($isGlobal) { $this->set($name[1], $this->reduce($value), false, $this->rootEnv); - break; } - if ($isDefault) { - $existingValue = $this->get($name[1], true); - $shouldSet = $existingValue === true || $existingValue === self::$null; - } + $shouldSet = $isDefault && + (($result = $this->get($name[1], false)) === null + || $result === self::$null); if (! $isDefault || $shouldSet) { $this->set($name[1], $this->reduce($value)); @@ -1230,7 +1228,6 @@ protected function compileChild($child, $out) return $ret; } } - break; case 'nestedprop': @@ -1290,9 +1287,9 @@ protected function compileChild($child, $out) break; case 'mixin_content': - $content = $this->get(self::$namespaces['special'] . 'content'); + $content = $this->get(self::$namespaces['special'] . 'content', false); - if (! isset($content)) { + if (! $content) { $this->throwError('Expected @content inside of mixin'); } @@ -2434,11 +2431,22 @@ protected function setExisting($name, $value, $env = null) $env = $this->getStoreEnv(); } - if (! isset($env->store[$name]) && isset($env->parent) && $this->has($name, $env->parent)) { - $this->setExisting($name, $value, $env->parent); - } else { - $env->store[$name] = $value; + $storeEnv = $env; + + for (;;) { + if (array_key_exists($name, $env->store)) { + break; + } + + if (! isset($env->parent)) { + $env = $storeEnv; + break; + } + + $env = $env->parent; } + + $env->store[$name] = $value; } /** @@ -2463,12 +2471,12 @@ protected function setRaw($name, $value, $env = null) * @api * * @param string $name - * @param mixed $defaultValue + * @param boolean $shouldThrow * @param \stdClass $env * * @return mixed */ - public function get($name, $defaultValue = null, $env = null) + public function get($name, $shouldThrow = true, $env = null) { $name = $this->normalizeName($name); @@ -2476,19 +2484,25 @@ public function get($name, $defaultValue = null, $env = null) $env = $this->getStoreEnv(); } - if (! isset($defaultValue)) { - $defaultValue = self::$defaultValue; - } + $hasNamespace = $name[0] === '^' || $name[0] === '@' || $name[0] === '%'; + + for (;;) { + if (array_key_exists($name, $env->store)) { + return $env->store[$name]; + } - if (isset($env->store[$name])) { - return $env->store[$name]; + if (! isset($env->parent)) { + break; + } + + $env = $env->parent; } - if (isset($env->parent)) { - return $this->get($name, $defaultValue, $env->parent); + if ($shouldThrow) { + $this->throwError('undefined variable $' . $name); } - return $defaultValue; // found nothing + // found nothing } /** @@ -2501,9 +2515,7 @@ public function get($name, $defaultValue = null, $env = null) */ protected function has($name, $env = null) { - $value = $this->get($name, false, $env); - - return $value !== false; + return $this->get($name, false, $env) !== null; } /** From f88ecf10d3e1a460a9e04100756243251409f0e5 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sun, 6 Sep 2015 06:37:35 -0400 Subject: [PATCH 023/484] fix fix --- src/Compiler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compiler.php b/src/Compiler.php index 7a2a8cd5..16ed3bf8 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -2687,7 +2687,7 @@ protected function importFile($path, $out) $parser = new Parser($path, false); $tree = $parser->parse($code); - $this->parsedFiles[realPath] = filemtime($path); + $this->parsedFiles[$realPath] = filemtime($path); $this->importCache[$realPath] = $tree; } From 73c403dccbbab3f5ba8a9257bd624d0e322923b2 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sun, 6 Sep 2015 18:04:22 -0400 Subject: [PATCH 024/484] fixes #295 - multiple variable scoping bugs --- src/Compiler.php | 17 ++++++++++++++++- tests/ExceptionTest.php | 26 ++++++++++++++++++++++++++ tests/FailingTest.php | 17 ----------------- tests/inputs/variables.scss | 10 ++++++++++ tests/outputs/variables.css | 3 +++ tests/outputs_numbered/variables.css | 3 +++ 6 files changed, 58 insertions(+), 18 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 16ed3bf8..f02a4b7e 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -1272,6 +1272,7 @@ protected function compileChild($child, $out) if (isset($content)) { $content->scope = $callingScope; + $this->setRaw(self::$namespaces['special'] . 'content', $content); } @@ -1279,6 +1280,8 @@ protected function compileChild($child, $out) $this->applyArguments($mixin->args, $argValues); } + $this->env->marker = 'mixin'; + foreach ($mixin->children as $child) { $this->compileChild($child, $out); } @@ -2433,11 +2436,18 @@ protected function setExisting($name, $value, $env = null) $storeEnv = $env; + $hasNamespace = $name[0] === '^' || $name[0] === '@' || $name[0] === '%'; + for (;;) { if (array_key_exists($name, $env->store)) { break; } + if (! $hasNamespace && isset($env->marker)) { + $env = $storeEnv; + break; + } + if (! isset($env->parent)) { $env = $storeEnv; break; @@ -2491,6 +2501,11 @@ public function get($name, $shouldThrow = true, $env = null) return $env->store[$name]; } + if (! $hasNamespace && isset($env->marker)) { + $env = $this->rootEnv; + continue; + } + if (! isset($env->parent)) { break; } @@ -2499,7 +2514,7 @@ public function get($name, $shouldThrow = true, $env = null) } if ($shouldThrow) { - $this->throwError('undefined variable $' . $name); + $this->throwError("Undefined variable \$$name"); } // found nothing diff --git a/tests/ExceptionTest.php b/tests/ExceptionTest.php index 50255f40..12ec165f 100644 --- a/tests/ExceptionTest.php +++ b/tests/ExceptionTest.php @@ -103,6 +103,32 @@ public function provideScss() , 'expecting color' ), + array(<<<'END_OF_SCSS' +BODY { + DIV { + $bg: red; + } + + background: $bg; +} +END_OF_SCSS + , + 'Undefined variable $bg' + ), + array(<<<'END_OF_SCSS' +@mixin example { + background: $bg; +} + +P { + $bg: red; + + @include example; +} +END_OF_SCSS + , + 'Undefined variable $bg' + ), ); } diff --git a/tests/FailingTest.php b/tests/FailingTest.php index cf4677d3..f6ec0ffc 100644 --- a/tests/FailingTest.php +++ b/tests/FailingTest.php @@ -153,23 +153,6 @@ public function provideFailing() transform: scale(0.5); } to { transform: scale(1); } } -END_OF_EXPECTED - ), - array( - '#295 - variable scope bug', <<<'END_OF_SCSS' -@mixin example { - $color: red; -} - -A { - $color: green; - @include example; - color: $color; -} -END_OF_SCSS - , << Date: Sun, 6 Sep 2015 20:27:37 -0400 Subject: [PATCH 025/484] Reverts 94efd300; fixes #323 --- src/Compiler.php | 5 +++++ tests/inputs/directives.scss | 17 +++++++++++++++++ tests/outputs/directives.css | 10 ++++++++++ tests/outputs_numbered/directives.css | 12 ++++++++++++ 4 files changed, 44 insertions(+) diff --git a/src/Compiler.php b/src/Compiler.php index f02a4b7e..ac7b56fe 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -382,6 +382,11 @@ protected function matchExtendsSingle($rawSingle, &$outOrigin) $single = array(); foreach ($rawSingle as $part) { + // matches Number + if (! is_string($part)) { + return false; + } + if (! preg_match('/^[\[.:#%]/', $part) && count($single)) { $single[count($single) - 1] .= $part; } else { diff --git a/tests/inputs/directives.scss b/tests/inputs/directives.scss index 10e39422..14b89cb7 100644 --- a/tests/inputs/directives.scss +++ b/tests/inputs/directives.scss @@ -108,3 +108,20 @@ div { { body { color: purple; background: yellow; } } + +@keyframes anim-rotate { + 0% { + transform: rotate(0); + } + 100% { + transform: rotate(360deg); + } +} + +.bold { + font-weight: 700; +} + +.icon-ajax { + @extend .bold; +} diff --git a/tests/outputs/directives.css b/tests/outputs/directives.css index 2b715582..4a1ced6e 100644 --- a/tests/outputs/directives.css +++ b/tests/outputs/directives.css @@ -75,3 +75,13 @@ div { body { color: purple; background: yellow; } } + +@keyframes anim-rotate { + 0% { + transform: rotate(0); } + + 100% { + transform: rotate(360deg); } } + +.bold, .icon-ajax { + font-weight: 700; } diff --git a/tests/outputs_numbered/directives.css b/tests/outputs_numbered/directives.css index 7de43a31..9b77350e 100644 --- a/tests/outputs_numbered/directives.css +++ b/tests/outputs_numbered/directives.css @@ -83,3 +83,15 @@ to { body { color: purple; background: yellow; } } + +@keyframes anim-rotate { +/* line 113, inputs/directives.scss */ +0% { + transform: rotate(0); } +/* line 116, inputs/directives.scss */ +100% { + transform: rotate(360deg); } } +/* line 121, inputs/directives.scss */ +.bold, .icon-ajax { + font-weight: 700; } +/* line 125, inputs/directives.scss */ From 4bcaed23664897db093a50e99b93e078f3a31a2d Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sun, 6 Sep 2015 21:04:49 -0400 Subject: [PATCH 026/484] bump version to 0.3.0 --- src/Version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Version.php b/src/Version.php index 32f0bf74..c650a26d 100644 --- a/src/Version.php +++ b/src/Version.php @@ -18,5 +18,5 @@ */ class Version { - const VERSION = 'v0.2.1'; + const VERSION = 'v0.3.0'; } From e688a85898c3a9e33199276252cf0ed4b1073640 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sun, 6 Sep 2015 22:46:01 -0400 Subject: [PATCH 027/484] temporary: partially revert 73c403dc to work with bootstrap v4-dev --- src/Compiler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compiler.php b/src/Compiler.php index ac7b56fe..a95e69f3 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -1285,7 +1285,7 @@ protected function compileChild($child, $out) $this->applyArguments($mixin->args, $argValues); } - $this->env->marker = 'mixin'; + //$this->env->marker = 'mixin'; foreach ($mixin->children as $child) { $this->compileChild($child, $out); From 92514e267989690264312553828e70fa127a191c Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Thu, 10 Sep 2015 11:29:51 -0400 Subject: [PATCH 028/484] Remove deprecated "strict" option from phpunit.xml.dist --- phpunit.xml.dist | 1 - 1 file changed, 1 deletion(-) diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 57219d25..c007930c 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -4,7 +4,6 @@ Date: Fri, 11 Sep 2015 21:41:46 -0400 Subject: [PATCH 029/484] Fix docblock --- src/Parser.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Parser.php b/src/Parser.php index 16b4b177..8b27a27e 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -697,7 +697,7 @@ protected function pushBlock($selectors, $pos = 0) /** * Push special (named) block onto parse tree * - * @param array $selectors + * @param string $type * @param integer $pos * * @return \stdClass From 2977aa444415787e931c048989b8c9195fd5b344 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Fri, 11 Sep 2015 21:42:27 -0400 Subject: [PATCH 030/484] Fixes #324 bootstrap v4-dev regression --- src/Compiler.php | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index a95e69f3..bef345bc 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -1270,10 +1270,7 @@ protected function compileChild($child, $out) // push scope, apply args $this->pushEnv(); - - if ($this->env->depth > 0) { - $this->env->depth--; - } + $this->env->depth--; if (isset($content)) { $content->scope = $callingScope; @@ -1285,7 +1282,7 @@ protected function compileChild($child, $out) $this->applyArguments($mixin->args, $argValues); } - //$this->env->marker = 'mixin'; + $this->env->marker = 'mixin'; foreach ($mixin->children as $child) { $this->compileChild($child, $out); @@ -1305,17 +1302,14 @@ protected function compileChild($child, $out) break; } - $strongTypes = array('include', 'block', 'for', 'while'); + $this->storeEnv = $content->scope; foreach ($content->children as $child) { - $this->storeEnv = (in_array($child[0], $strongTypes)) - ? null - : $content->scope; - $this->compileChild($child, $out); } - unset($this->storeEnv); + $this->storeEnv = null; + break; case 'debug': From 18fccbd309f250fd5fe34cd9c6d6f5da67aed958 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Fri, 11 Sep 2015 23:11:09 -0400 Subject: [PATCH 031/484] Bump version to 0.3.1 --- src/Version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Version.php b/src/Version.php index c650a26d..efd45ca0 100644 --- a/src/Version.php +++ b/src/Version.php @@ -18,5 +18,5 @@ */ class Version { - const VERSION = 'v0.3.0'; + const VERSION = 'v0.3.1'; } From 18773ccdff5d322665567d85930d8f2a48a3c60f Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sun, 13 Sep 2015 12:02:56 -0400 Subject: [PATCH 032/484] internals: adjustAllChildren() scope --- src/Formatter/Nested.php | 95 ++++++++++++++++++++++------------------ 1 file changed, 53 insertions(+), 42 deletions(-) diff --git a/src/Formatter/Nested.php b/src/Formatter/Nested.php index bd2c4d21..1f709b95 100644 --- a/src/Formatter/Nested.php +++ b/src/Formatter/Nested.php @@ -34,48 +34,6 @@ public function __construct() $this->assignSeparator = ': '; } - /** - * Adjust the depths of all children, depth first - * - * @param \stdClass $block - */ - public function adjustAllChildren($block) - { - // 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; - - // make relative to parent - foreach ($block->children as $child) { - $this->adjustAllChildren($child); - $child->depth = $child->depth - $block->depth; - } - } - /** * {@inheritdoc} */ @@ -110,7 +68,9 @@ protected function block($block) echo $pre . implode($this->tagSeparator, $block->selectors) . $this->open . $this->break; + $this->indentLevel++; + $inner = $this->indentStr($block->depth - 1); } @@ -126,6 +86,7 @@ protected function block($block) 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; } @@ -135,6 +96,7 @@ protected function block($block) if (! empty($block->selectors)) { $this->indentLevel--; + echo $this->close; } @@ -142,4 +104,53 @@ protected function block($block) echo $this->break; } } + + /** + * Adjust the depths of all children, depth first + * + * @param \stdClass $block + */ + private function adjustAllChildren($block) + { + // 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; + + // make relative to parent + foreach ($block->children as $child) { + $this->adjustAllChildren($child); + + $child->depth = $child->depth - $block->depth; + } + } } From f9431a4c8aa0aacb2c369c1f583e3349093d06eb Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sun, 13 Sep 2015 15:11:48 -0400 Subject: [PATCH 033/484] internals: add debug formatter --- scss.inc.php | 1 + src/Compiler.php | 8 ++-- src/Formatter.php | 30 +++++++++----- src/Formatter/Compressed.php | 4 +- src/Formatter/Crunched.php | 4 +- src/Formatter/Debug.php | 77 ++++++++++++++++++++++++++++++++++++ src/Formatter/Expanded.php | 4 +- src/Formatter/Nested.php | 25 ++++++++---- 8 files changed, 130 insertions(+), 23 deletions(-) create mode 100644 src/Formatter/Debug.php diff --git a/scss.inc.php b/scss.inc.php index 51ae57e5..da641f7c 100644 --- a/scss.inc.php +++ b/scss.inc.php @@ -11,6 +11,7 @@ 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/Parser.php'; diff --git a/src/Compiler.php b/src/Compiler.php index bef345bc..620821c9 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -1303,11 +1303,13 @@ protected function compileChild($child, $out) } $this->storeEnv = $content->scope; + //$this->pushEnv(); foreach ($content->children as $child) { $this->compileChild($child, $out); } + //$this->popEnv(); $this->storeEnv = null; break; @@ -2734,9 +2736,9 @@ public function findImport($url) 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) diff --git a/src/Formatter.php b/src/Formatter.php index 5b035e1c..f08cdfe7 100644 --- a/src/Formatter.php +++ b/src/Formatter.php @@ -92,12 +92,14 @@ public function stripSemicolon(&$lines) /** * Output lines inside a block * - * @param string $inner * @param \stdClass $block */ - protected function blockLines($inner, $block) + protected function blockLines($block) { + $inner = $this->indentStr(); + $glue = $this->break . $inner; + echo $inner . implode($glue, $block->lines); if (! empty($block->children)) { @@ -105,6 +107,20 @@ protected function blockLines($inner, $block) } } + /** + * Output block selectors + * + * @param \stdClass $block + */ + protected function blockSelectors($block) + { + $inner = $this->indentStr(); + + echo $inner + . implode($this->tagSeparator, $block->selectors) + . $this->open . $this->break; + } + /** * Output non-empty block * @@ -116,20 +132,16 @@ protected function block($block) return; } - $inner = $pre = $this->indentStr(); + $pre = $this->indentStr(); if (! empty($block->selectors)) { - echo $pre - . implode($this->tagSeparator, $block->selectors) - . $this->open . $this->break; + $this->blockSelectors($block); $this->indentLevel++; - - $inner = $this->indentStr(); } if (! empty($block->lines)) { - $this->blockLines($inner, $block); + $this->blockLines($block); } foreach ($block->children as $child) { diff --git a/src/Formatter/Compressed.php b/src/Formatter/Compressed.php index c95b68b3..2ab2eaae 100644 --- a/src/Formatter/Compressed.php +++ b/src/Formatter/Compressed.php @@ -57,8 +57,10 @@ public function stripSemicolon(&$lines) /** * {@inheritdoc} */ - public function blockLines($inner, $block) + public function blockLines($block) { + $inner = $this->indentStr(); + $glue = $this->break . $inner; foreach ($block->lines as $index => $line) { diff --git a/src/Formatter/Crunched.php b/src/Formatter/Crunched.php index 1227c122..ad52c464 100644 --- a/src/Formatter/Crunched.php +++ b/src/Formatter/Crunched.php @@ -57,8 +57,10 @@ public function stripSemicolon(&$lines) /** * {@inheritdoc} */ - public function blockLines($inner, $block) + public function blockLines($block) { + $inner = $this->indentStr(); + $glue = $this->break . $inner; foreach ($block->lines as $index => $line) { diff --git a/src/Formatter/Debug.php b/src/Formatter/Debug.php new file mode 100644 index 00000000..7e227b48 --- /dev/null +++ b/src/Formatter/Debug.php @@ -0,0 +1,77 @@ + + */ +class Debug extends Formatter +{ + /** + * {@inheritdoc} + */ + public function __construct() + { + $this->indentLevel = 0; + $this->indentChar = ''; + $this->break = "\n"; + $this->open = ' {'; + $this->close = ' }'; + $this->tagSeparator = ', '; + $this->assignSeparator = ': '; + } + + /** + * {@inheritdoc} + */ + protected function blockLines($block) + { + foreach ($block->lines as $index => $line) { + echo "block->lines[{$index}]: $line\n"; + } + } + + /** + * {@inheritdoc} + */ + protected function blockSelectors($block) + { + foreach ($block->selectors as $index => $selector) { + echo "block->selectors[{$index}]: $selector\n"; + } + } + + /** + * {@inheritdoc} + */ + protected function block($block) + { + echo "block->type: {$block->type}\n" . + "block->depth: {$block->depth}\n"; + + if (! empty($block->selectors)) { + $this->blockSelectors($block); + } + + if (! empty($block->lines)) { + $this->blockLines($block); + } + + foreach ($block->children as $i => $child) { + $this->block($child); + } + } +} diff --git a/src/Formatter/Expanded.php b/src/Formatter/Expanded.php index fbf85c1e..89ecfffb 100644 --- a/src/Formatter/Expanded.php +++ b/src/Formatter/Expanded.php @@ -37,8 +37,10 @@ public function __construct() /** * {@inheritdoc} */ - protected function blockLines($inner, $block) + protected function blockLines($block) { + $inner = $this->indentStr(); + $glue = $this->break . $inner; foreach ($block->lines as $index => $line) { diff --git a/src/Formatter/Nested.php b/src/Formatter/Nested.php index 1f709b95..bf2c4a67 100644 --- a/src/Formatter/Nested.php +++ b/src/Formatter/Nested.php @@ -37,8 +37,10 @@ public function __construct() /** * {@inheritdoc} */ - protected function blockLines($inner, $block) + protected function blockLines($block) { + $inner = $this->indentStr($block->depth - 1); + $glue = $this->break . $inner; foreach ($block->lines as $index => $line) { @@ -54,6 +56,18 @@ protected function blockLines($inner, $block) } } + /** + * {@inheritdoc} + */ + protected function blockSelectors($block) + { + $inner = $this->indentStr($block->depth - 1); + + echo $inner + . implode($this->tagSeparator, $block->selectors) + . $this->open . $this->break; + } + /** * {@inheritdoc} */ @@ -63,19 +77,14 @@ protected function block($block) $this->adjustAllChildren($block); } - $inner = $pre = $this->indentStr($block->depth - 1); if (! empty($block->selectors)) { - echo $pre . - implode($this->tagSeparator, $block->selectors) . - $this->open . $this->break; + $this->blockSelectors($block); $this->indentLevel++; - - $inner = $this->indentStr($block->depth - 1); } if (! empty($block->lines)) { - $this->blockLines($inner, $block); + $this->blockLines($block); } foreach ($block->children as $i => $child) { From 108219502ea7ec4664c37eb149c1c33e175cd433 Mon Sep 17 00:00:00 2001 From: Andre Mohren Date: Tue, 22 Sep 2015 09:05:02 +0200 Subject: [PATCH 034/484] @importOnce support - while not an official feature, this allows modules to specify dependencies which have to be loaded if not already loaded without loading them twice. --- src/Compiler.php | 58 ++++++++++++++++++++++++++++++++++++++++++++++++ src/Parser.php | 11 +++++++++ 2 files changed, 69 insertions(+) diff --git a/src/Compiler.php b/src/Compiler.php index 620821c9..c46a217e 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -107,6 +107,7 @@ class Compiler protected $importPaths = array(''); protected $importCache = array(); + protected $importedFiles = array(); protected $userFunctions = array(); protected $registeredVars = array(); @@ -982,6 +983,53 @@ protected function compileImport($rawPath, $out) if ($path = $this->findImport($path)) { $this->importFile($path, $out); + $this->importedFiles[] = $path; + + return true; + } + + return false; + } + + if ($rawPath[0] === 'list') { + // handle a list of strings + if (count($rawPath[2]) === 0) { + return false; + } + + foreach ($rawPath[2] as $path) { + if ($path[0] !== 'string') { + return false; + } + } + + foreach ($rawPath[2] as $path) { + $this->compileImport($path, $out); + } + + return true; + } + + return false; + } + + /** + * Compile importOnce; returns true if the value was something that could be imported + * + * @param array $rawPath + * @param array $out + * + * @return boolean + */ + protected function compileImportOnce($rawPath, $out) + { + if ($rawPath[0] === 'string') { + $path = $this->compileStringContent($rawPath); + if ($path = $this->findImport($path)) { + if (!in_array($path, $this->importedFiles)) { + $this->importFile($path, $out); + $this->importedFiles[] = $path; + } return true; } @@ -1025,6 +1073,16 @@ protected function compileChild($child, $out) $this->sourceParser = isset($child[Parser::SOURCE_PARSER]) ? $child[Parser::SOURCE_PARSER] : $this->parser; switch ($child[0]) { + case 'importOnce': + list(, $rawPath) = $child; + + $rawPath = $this->reduce($rawPath); + + if (! $this->compileImportOnce($rawPath, $out)) { + $out->lines[] = '@importOnce ' . $this->compileValue($rawPath) . ';'; + } + break; + case 'import': list(, $rawPath) = $child; diff --git a/src/Parser.php b/src/Parser.php index 8b27a27e..0cf6a3cc 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -287,6 +287,17 @@ protected function parseChunk() $this->seek($s); + if ($this->literal('@importOnce') && + $this->valueList($importPath) && + $this->end() + ) { + $this->append(array('importOnce', $importPath), $s); + + return true; + } + + $this->seek($s); + if ($this->literal('@import') && $this->valueList($importPath) && $this->end() From b6c40f2238a41aacb9fe47daf38a41b58e99c4c0 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 22 Sep 2015 15:58:26 -0400 Subject: [PATCH 035/484] add #336 test --- tests/FailingTest.php | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tests/FailingTest.php b/tests/FailingTest.php index f6ec0ffc..858a0ad5 100644 --- a/tests/FailingTest.php +++ b/tests/FailingTest.php @@ -153,6 +153,31 @@ public function provideFailing() transform: scale(0.5); } to { transform: scale(1); } } +END_OF_EXPECTED + ), + array( + '#336 - @extend with interpolation of list', <<<'END_OF_SCSS' +.selector1 { + color: blue; +} + +.selector2 { + background: #ccc; +} + +$var: ".selector1, .selector2"; + +.master-class { + // interpolation of the variable is needed since we have a selector in $var + @extend #{$var}; +} +END_OF_SCSS + , << Date: Wed, 23 Sep 2015 08:35:48 +0200 Subject: [PATCH 036/484] Renamed @importOnce to @scssphp-import-once to follow sass naming conventions and use vendor-prefixes. --- src/Compiler.php | 4 ++-- src/Parser.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index c46a217e..6346e28e 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -1073,13 +1073,13 @@ protected function compileChild($child, $out) $this->sourceParser = isset($child[Parser::SOURCE_PARSER]) ? $child[Parser::SOURCE_PARSER] : $this->parser; switch ($child[0]) { - case 'importOnce': + case 'scssphp-import-once': list(, $rawPath) = $child; $rawPath = $this->reduce($rawPath); if (! $this->compileImportOnce($rawPath, $out)) { - $out->lines[] = '@importOnce ' . $this->compileValue($rawPath) . ';'; + $out->lines[] = '@scssphp-import-once ' . $this->compileValue($rawPath) . ';'; } break; diff --git a/src/Parser.php b/src/Parser.php index 0cf6a3cc..f74fdbd8 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -287,11 +287,11 @@ protected function parseChunk() $this->seek($s); - if ($this->literal('@importOnce') && + if ($this->literal('@scssphp-import-once') && $this->valueList($importPath) && $this->end() ) { - $this->append(array('importOnce', $importPath), $s); + $this->append(array('scssphp-import-once', $importPath), $s); return true; } From d1c5336236ca7498ef6eefd69d293696c35c6cd1 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 23 Sep 2015 13:11:43 -0400 Subject: [PATCH 037/484] fixes #336 --- src/Compiler.php | 11 +++++++---- tests/FailingTest.php | 25 ------------------------- tests/inputs/extends.scss | 15 +++++++++++++++ tests/outputs/extends.css | 6 ++++++ tests/outputs_numbered/extends.css | 8 ++++++++ 5 files changed, 36 insertions(+), 29 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 620821c9..010f430d 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -1139,11 +1139,14 @@ protected function compileChild($child, $out) list(, $selectors) = $child; foreach ($selectors as $sel) { - // only use the first one - $result = $this->evalSelectors(array($sel)); - $result = current($result[0]); + $results = $this->evalSelectors(array($sel)); - $this->pushExtends($result, $out->selectors); + foreach ($results as $result) { + // only use the first one + $result = current($result); + + $this->pushExtends($result, $out->selectors); + } } break; diff --git a/tests/FailingTest.php b/tests/FailingTest.php index 858a0ad5..f6ec0ffc 100644 --- a/tests/FailingTest.php +++ b/tests/FailingTest.php @@ -153,31 +153,6 @@ public function provideFailing() transform: scale(0.5); } to { transform: scale(1); } } -END_OF_EXPECTED - ), - array( - '#336 - @extend with interpolation of list', <<<'END_OF_SCSS' -.selector1 { - color: blue; -} - -.selector2 { - background: #ccc; -} - -$var: ".selector1, .selector2"; - -.master-class { - // interpolation of the variable is needed since we have a selector in $var - @extend #{$var}; -} -END_OF_SCSS - , << Date: Mon, 21 Sep 2015 15:48:42 -0400 Subject: [PATCH 038/484] More efficient solution to adding base scss file into allParseFiles variable pushing logic into new public method for Compiler --- src/Compiler.php | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/Compiler.php b/src/Compiler.php index 010f430d..e706b240 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -161,6 +161,8 @@ public function compile($code, $name = null) $this->formatter = new $this->formatter(); + $this->addParsedFile($name); + $this->rootEnv = $this->pushEnv($tree); $this->injectVariables($this->registeredVars); $this->compileRoot($tree); @@ -2587,6 +2589,19 @@ public function unsetVariable($name) unset($this->registeredVars[$name]); } + /** + * Adds to list of parsed files + * + * @api + * + * @param string $file + */ + public function addParsedFile($file) { + if (!empty($file)) { + $this->parsedFiles[realpath($file)] = filemtime($file); + } + } + /** * Returns list of parsed files * @@ -2706,7 +2721,7 @@ protected function importFile($path, $out) $parser = new Parser($path, false); $tree = $parser->parse($code); - $this->parsedFiles[$realPath] = filemtime($path); + $this->addParsedFile($path); $this->importCache[$realPath] = $tree; } From 7db57eb7bc3c80a418d066524bd9e5d8b294b8eb Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 23 Sep 2015 14:17:32 -0400 Subject: [PATCH 039/484] fixes #339; closes #340 --- bin/pscss | 4 ++-- src/Compiler.php | 19 ++++++++++--------- src/Parser.php | 2 +- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/bin/pscss b/bin/pscss index 8a21393e..f4b7ba8f 100755 --- a/bin/pscss +++ b/bin/pscss @@ -146,7 +146,7 @@ if ($inputFile) { } if ($dumpTree) { - $parser = new Parser($inputFile ?: 'STDIN'); + $parser = new Parser($inputFile); print_r(json_decode(json_encode($parser->parse($data)), true)); @@ -175,7 +175,7 @@ if ($style) { $scss->setFormatter('Leafo\\ScssPhp\\Formatter\\' . ucfirst($style)); } -echo $scss->compile($data, $inputFile ?: 'STDIN'); +echo $scss->compile($data, $inputFile); if ($changeDir) { chdir($oldWorkingDir); diff --git a/src/Compiler.php b/src/Compiler.php index e706b240..2488cebd 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -136,11 +136,11 @@ class Compiler * @api * * @param string $code - * @param string $name + * @param string $path * * @return string */ - public function compile($code, $name = null) + public function compile($code, $path = null) { $this->indentLevel = -1; $this->commentsSeen = array(); @@ -155,13 +155,13 @@ public function compile($code, $name = null) $locale = setlocale(LC_NUMERIC, 0); setlocale(LC_NUMERIC, 'C'); - $this->parser = new Parser($name); + $this->parser = new Parser($path); $tree = $this->parser->parse($code); $this->formatter = new $this->formatter(); - $this->addParsedFile($name); + $this->addParsedFile($path); $this->rootEnv = $this->pushEnv($tree); $this->injectVariables($this->registeredVars); @@ -2594,13 +2594,14 @@ public function unsetVariable($name) * * @api * - * @param string $file + * @param string $path */ - public function addParsedFile($file) { - if (!empty($file)) { - $this->parsedFiles[realpath($file)] = filemtime($file); + public function addParsedFile($path) + { + if (isset($path)) { + $this->parsedFiles[realpath($path)] = filemtime($path); } - } + } /** * Returns list of parsed files diff --git a/src/Parser.php b/src/Parser.php index 8b27a27e..80926060 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -87,7 +87,7 @@ class Parser */ public function __construct($sourceName = null, $rootParser = true) { - $this->sourceName = $sourceName; + $this->sourceName = $sourceName ?: '(stdin)'; $this->rootParser = $rootParser; $this->charset = null; From ad9875274d83e3387d62cffc6ac856837a5dc8f7 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 23 Sep 2015 22:34:22 -0400 Subject: [PATCH 040/484] Parser: parent is needed --- src/Parser.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Parser.php b/src/Parser.php index 80926060..815ea09d 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -671,7 +671,7 @@ protected function literal($what, $eatWhitespace = null) protected function pushBlock($selectors, $pos = 0) { $b = new \stdClass; - $b->parent = $this->env; // not sure if we need this yet + $b->parent = $this->env; $b->sourcePosition = $pos; $b->sourceParser = $this; From ceadeabdb9112996d45939d94ed4d7f38c281237 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 23 Sep 2015 22:47:57 -0400 Subject: [PATCH 041/484] Formatter: factoring out blockChildren() --- src/Formatter.php | 16 ++++++++++++++-- src/Formatter/Debug.php | 38 +++++++++++++++++++++++++++++--------- src/Formatter/Nested.php | 38 ++++++++++++++++++++++++-------------- 3 files changed, 67 insertions(+), 25 deletions(-) diff --git a/src/Formatter.php b/src/Formatter.php index f08cdfe7..c341f1d7 100644 --- a/src/Formatter.php +++ b/src/Formatter.php @@ -121,6 +121,18 @@ protected function blockSelectors($block) . $this->open . $this->break; } + /** + * Output block children + * + * @param \stdClass $block + */ + protected function blockChildren($block) + { + foreach ($block->children as $child) { + $this->block($child); + } + } + /** * Output non-empty block * @@ -144,8 +156,8 @@ protected function block($block) $this->blockLines($block); } - foreach ($block->children as $child) { - $this->block($child); + if (! empty($block->children)) { + $this->blockChildren($block); } if (! empty($block->selectors)) { diff --git a/src/Formatter/Debug.php b/src/Formatter/Debug.php index 7e227b48..1aae1dab 100644 --- a/src/Formatter/Debug.php +++ b/src/Formatter/Debug.php @@ -39,6 +39,12 @@ public function __construct() */ protected function blockLines($block) { + if (empty($block->lines)) { + echo "block->lines: []\n"; + + return; + } + foreach ($block->lines as $index => $line) { echo "block->lines[{$index}]: $line\n"; } @@ -49,6 +55,12 @@ protected function blockLines($block) */ protected function blockSelectors($block) { + if (empty($block->selectors)) { + echo "block->selectors: []\n"; + + return; + } + foreach ($block->selectors as $index => $selector) { echo "block->selectors[{$index}]: $selector\n"; } @@ -57,21 +69,29 @@ protected function blockSelectors($block) /** * {@inheritdoc} */ - protected function block($block) + protected function blockChildren($block) { - echo "block->type: {$block->type}\n" . - "block->depth: {$block->depth}\n"; - - if (! empty($block->selectors)) { - $this->blockSelectors($block); - } + if (empty($block->children)) { + echo "block->children: []\n"; - if (! empty($block->lines)) { - $this->blockLines($block); + return; } foreach ($block->children as $i => $child) { $this->block($child); } } + + /** + * {@inheritdoc} + */ + protected function block($block) + { + echo "block->type: {$block->type}\n" . + "block->depth: {$block->depth}\n"; + + $this->blockSelectors($block); + $this->blockLines($block); + $this->blockChildren($block); + } } diff --git a/src/Formatter/Nested.php b/src/Formatter/Nested.php index bf2c4a67..3897419b 100644 --- a/src/Formatter/Nested.php +++ b/src/Formatter/Nested.php @@ -68,6 +68,28 @@ protected function blockSelectors($block) . $this->open . $this->break; } + /** + * {@inheritdoc} + */ + protected function blockChildren($block) + { + foreach ($block->children as $i => $child) { + $this->block($child); + + if ($i < count($block->children) - 1) { + echo $this->break; + + 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} */ @@ -87,20 +109,8 @@ protected function block($block) $this->blockLines($block); } - foreach ($block->children as $i => $child) { - $this->block($child); - - if ($i < count($block->children) - 1) { - echo $this->break; - - 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; - } - } - } + if (! empty($block->children)) { + $this->blockChildren($block); } if (! empty($block->selectors)) { From f50a8cefd1dff241841e0c3ef66aecf17e9b6b6f Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 23 Sep 2015 22:52:02 -0400 Subject: [PATCH 042/484] Compiler: cleanup --- src/Compiler.php | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 2488cebd..45c4c1aa 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -93,7 +93,7 @@ class Compiler 'mm' => 25.4, 'px' => 96, 'q' => 101.6, - ) + ), ); static public $true = array('keyword', 'true'); @@ -494,10 +494,11 @@ protected function compileMedia($media) } if ($needsWrap) { - $wrapped = (object)array( + $wrapped = (object) array( 'selectors' => array(), - 'children' => $media->children + 'children' => $media->children, ); + $media->children = array(array('block', $wrapped)); } @@ -819,8 +820,8 @@ protected function hasSelectorPlaceholder($selector) /** * Compile children * - * @param array $stms - * @param array $out + * @param array $stms + * @param \stdClass $out * * @return array */ @@ -948,7 +949,7 @@ protected function mergeMediaTypes($type1, $type2) return array( $m1 === 'not' ? $m2 : $m1, - $m1 === 'not' ? $t2 : $t1 + $m1 === 'not' ? $t2 : $t1, ); } @@ -1602,9 +1603,9 @@ protected function reduce($value, $inExp = false) } // throw away lines and children - $tmp = (object)array( + $tmp = (object) array( 'lines' => array(), - 'children' => array() + 'children' => array(), ); $ret = $this->compileChildren($func->children, $tmp); From fb8ae6392338a1bcf7fa7dfef3730e578957f667 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 23 Sep 2015 22:55:30 -0400 Subject: [PATCH 043/484] Compiler: compileBlock() optimization - calls multiplySelectors() only if block has children --- src/Compiler.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 45c4c1aa..3641ac26 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -576,7 +576,7 @@ protected function compileBlock($block) $env->selectors = $this->evalSelectors($block->selectors); - $out = $this->makeOutputBlock(null, $this->multiplySelectors($env)); + $out = $this->makeOutputBlock(null); if (isset($this->lineNumberStyle) && count($env->selectors) && count($block->children)) { $annotation = $this->makeOutputBlock('comment'); @@ -601,7 +601,11 @@ protected function compileBlock($block) $this->scope->children[] = $out; - $this->compileChildren($block->children, $out); + if (! empty($block->children)) { + $out->selectors = $this->multiplySelectors($env); + + $this->compileChildren($block->children, $out); + } $this->formatter->stripSemicolon($out->lines); From 4faf52646b9edd1f4f0b9939669983cbe723cfad Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 23 Sep 2015 23:02:34 -0400 Subject: [PATCH 044/484] code tweak --- src/Compiler.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 3641ac26..47da5a8b 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -533,8 +533,6 @@ protected function mediaParent($scope) /** * Compile nested block * - * @todo refactor compileNestedBlock and compileMedia into same thing? - * * @param \stdClass $block * @param array $selectors */ @@ -601,7 +599,7 @@ protected function compileBlock($block) $this->scope->children[] = $out; - if (! empty($block->children)) { + if (count($block->children)) { $out->selectors = $this->multiplySelectors($env); $this->compileChildren($block->children, $out); From 01a63e3e300ab8badfdb3f6ff288c9b29c97f491 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Thu, 24 Sep 2015 00:26:57 -0400 Subject: [PATCH 045/484] Use original test case --- tests/FailingTest.php | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/FailingTest.php b/tests/FailingTest.php index f6ec0ffc..bd68a99b 100644 --- a/tests/FailingTest.php +++ b/tests/FailingTest.php @@ -133,6 +133,17 @@ public function provideFailing() & { color:blue; } + +@-webkit-keyframes zoomer { + from { + transform:scale(0.5); + } + + to { + transform:scale(1); + } +} + @keyframes zoomer { from { transform:scale(0.5); @@ -148,6 +159,13 @@ public function provideFailing() , << Date: Thu, 24 Sep 2015 00:33:28 -0400 Subject: [PATCH 046/484] code tweak --- src/Parser.php | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/Parser.php b/src/Parser.php index 815ea09d..1a092a9b 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -470,7 +470,8 @@ protected function parseChunk() // 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 = array('charset', $charset); @@ -490,7 +491,8 @@ protected function parseChunk() $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('{') ) { @@ -527,7 +529,8 @@ protected function parseChunk() // variable assigns if ($this->variable($name) && $this->literal(':') && - $this->valueList($value) && $this->end() + $this->valueList($value) && + $this->end() ) { // check for '!flag' $assignmentFlag = $this->stripAssignmentFlag($value); @@ -1395,8 +1398,8 @@ protected function map(&$out) $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; From 6163f8646a74caa5515b3b30373aedea6b5d86a6 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Thu, 24 Sep 2015 02:52:40 -0400 Subject: [PATCH 047/484] loop review --- src/Compiler.php | 10 +++------- src/Parser.php | 12 ++++++------ 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 47da5a8b..554bb966 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -2263,15 +2263,11 @@ protected function extractInterpolation($list) */ protected function multiplySelectors($env) { - $envs = array(); - - while (null !== $env) { - if (! empty($env->selectors)) { + for ($envs = array(); $env; $env = $env->parent) { + if (count($env->selectors)) { $envs[] = $env; } - - $env = $env->parent; - }; + } $selectors = array(); $parentSelectors = array(array()); diff --git a/src/Parser.php b/src/Parser.php index 1a092a9b..9b6a8dfa 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -137,7 +137,7 @@ public function parse($buffer) $this->pushBlock(null); $this->popBlock(); - while (false !== $this->parseChunk()) { + while ($this->parseChunk()) { ; } @@ -1551,7 +1551,7 @@ protected function mixedKeyword(&$out) $oldWhite = $this->eatWhiteDefault; $this->eatWhiteDefault = false; - while (true) { + for (;;) { if ($this->keyword($key)) { $parts[] = $key; continue; @@ -1705,7 +1705,7 @@ protected function propertyName(&$out) $oldWhite = $this->eatWhiteDefault; $this->eatWhiteDefault = false; - while (true) { + for (;;) { if ($this->interpolation($inter)) { $parts[] = $inter; } elseif ($this->keyword($text)) { @@ -1791,7 +1791,7 @@ protected function selector(&$out) { $selector = array(); - while (true) { + for (;;) { if ($this->match('[>+~]+', $m)) { $selector[] = array($m[0]); } elseif ($this->selectorSingle($part)) { @@ -1835,7 +1835,7 @@ protected function selectorSingle(&$out) $parts[] = '*'; } - while (true) { + for (;;) { // see if we can stop early if ($this->match('\s*[{,]', $m)) { $this->count--; @@ -1928,7 +1928,7 @@ protected function selectorSingle(&$out) $attrParts = array('['); // keyword, string, operator - while (true) { + for (;;) { if ($this->literal(']', false)) { $this->count--; break; // get out early From a8e987c70fde8eab8b53b2369beb703234ee05b6 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Fri, 25 Sep 2015 00:49:04 -0400 Subject: [PATCH 048/484] fixes #281 --- src/Compiler.php | 82 ++++++++++++++++++++++++--- tests/FailingTest.php | 47 --------------- tests/inputs/directives.scss | 28 +++++++++ tests/outputs/directives.css | 15 +++++ tests/outputs_numbered/directives.css | 21 +++++++ 5 files changed, 137 insertions(+), 56 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 554bb966..a91db34b 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -530,6 +530,34 @@ protected function mediaParent($scope) return $scope; } + /** + * Compile keyframe block + * + * @param \stdClass $block + * @param array $selectors + */ + protected function compileKeyframeBlock($block, $selectors) + { + $env = $this->pushEnv($block); + + $envs = $this->compactEnv($env); + + $this->env = $this->extractEnv(array_filter($envs, function ($e) { + return ! isset($e->block->selectors); + })); + + $this->scope = $this->makeOutputBlock($block->type, $selectors); + $this->scope->depth = 1; + $this->scope->parent->children[] = $this->scope; + + $this->compileChildren($block->children, $this->scope); + + $this->scope = $this->scope->parent; + $this->env = $this->extractEnv($envs); + + $this->popEnv(); + } + /** * Compile nested block * @@ -1049,7 +1077,11 @@ protected function compileChild($child, $out) $s .= ' ' . $this->compileValue($directive->value); } - $this->compileNestedBlock($directive, array($s)); + if ($directive->name === 'keyframes' || substr($directive->name, -10) === '-keyframes') { + $this->compileKeyframeBlock($directive, array($s)); + } else { + $this->compileNestedBlock($directive, array($s)); + } break; case 'media': @@ -2245,7 +2277,7 @@ protected function extractInterpolation($list) foreach ($items as $i => $item) { if ($item[0] === 'interpolate') { $before = array('list', $list[1], array_slice($items, 0, $i)); - $after = array('list', $list[1], array_slice($items, $i + 1)); + $after = array('list', $list[1], array_slice($items, $i + 1)); return array('interpolated', $item, $before, $after); } @@ -2263,16 +2295,15 @@ protected function extractInterpolation($list) */ protected function multiplySelectors($env) { - for ($envs = array(); $env; $env = $env->parent) { - if (count($env->selectors)) { - $envs[] = $env; - } - } - - $selectors = array(); + $envs = $this->compactEnv($env); + $selectors = array(); $parentSelectors = array(array()); while ($env = array_pop($envs)) { + if (empty($env->selectors)) { + continue; + } + $selectors = array(); foreach ($env->selectors as $selector) { @@ -2366,6 +2397,39 @@ protected function multiplyMedia($env, $childQueries = null) return $this->multiplyMedia($env->parent, $childQueries); } + /** + * Convert env linked list to stack + * + * @param \stdClass $env + * + * @return array + */ + private function compactEnv($env) + { + for ($envs = array(); $env; $env = $env->parent) { + $envs[] = $env; + } + + return $envs; + } + + /** + * Convert env stack to singly linked list + * + * @param array $envs + * + * @return \stdClass + */ + private function extractEnv($envs) + { + for ($env = null; $e = array_pop($envs);) { + $e->parent = $env; + $env = $e; + } + + return $env; + } + /** * Push environment * diff --git a/tests/FailingTest.php b/tests/FailingTest.php index bd68a99b..7a074a77 100644 --- a/tests/FailingTest.php +++ b/tests/FailingTest.php @@ -124,53 +124,6 @@ public function provideFailing() small { font-weight: italic; } -END_OF_EXPECTED - ), - array( - '#281 - nested animation selector', <<<'END_OF_SCSS' -.custom-selector { - -& { - color:blue; -} - -@-webkit-keyframes zoomer { - from { - transform:scale(0.5); - } - - to { - transform:scale(1); - } -} - -@keyframes zoomer { - from { - transform:scale(0.5); - } - - to { - transform:scale(1); - } -} - -} -END_OF_SCSS - , << Date: Mon, 20 Jul 2015 20:03:46 -0400 Subject: [PATCH 049/484] partial @at-root support (#193) --- src/Compiler.php | 34 ++++++++++++++++++++++++++++++ src/Parser.php | 18 +++++++++++++++- tests/inputs/at_root.scss | 24 +++++++++++++++++++++ tests/outputs/at_root.css | 15 +++++++++++++ tests/outputs_numbered/at_root.css | 19 +++++++++++++++++ 5 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 tests/inputs/at_root.scss create mode 100644 tests/outputs/at_root.css create mode 100644 tests/outputs_numbered/at_root.css diff --git a/src/Compiler.php b/src/Compiler.php index a91db34b..e578b8fa 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -530,6 +530,36 @@ protected function mediaParent($scope) return $scope; } + /** + * Compile at-root + * + * @param \stdClass $block + */ + protected function compileAtRoot($block) + { + $env = $this->pushEnv($block); + + $envs = $this->compactEnv($env); + + if (isset($block->with)) { + // @todo move outside of nested directives, e.g., (without: all), (without: media supports), (with: rule) + } else { + // exclude selectors by default + $this->env->parent = $this->rootEnv; + } + + $this->scope = $this->makeOutputBlock('at-root'); + $this->scope->depth = 1; + $this->scope->parent->children[] = $this->scope; + + $this->compileChildren($block->children, $this->scope); + + $this->scope = $this->scope->parent; + $this->env = $this->extractEnv($envs); + + $this->popEnv(); + } + /** * Compile keyframe block * @@ -1084,6 +1114,10 @@ protected function compileChild($child, $out) } break; + case 'at-root': + $this->compileAtRoot($child[1]); + break; + case 'media': $this->compileMedia($child[1]); break; diff --git a/src/Parser.php b/src/Parser.php index 9b6a8dfa..5e7370b2 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -241,6 +241,20 @@ protected function parseChunk() // 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('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('media', $s); $media->queryList = $mediaQueryList[2]; @@ -1393,7 +1407,9 @@ protected function map(&$out) { $s = $this->seek(); - $this->literal('('); + if (! $this->literal('(')) { + return false; + } $keys = array(); $values = array(); diff --git a/tests/inputs/at_root.scss b/tests/inputs/at_root.scss new file mode 100644 index 00000000..aa4f6584 --- /dev/null +++ b/tests/inputs/at_root.scss @@ -0,0 +1,24 @@ +.first { + color: red; +} +body{ +.second { + color: white; + + @at-root { + .nested1 { + color: blue; + @at-root { + .nested2 { + color: yellow; + } + } + color: orange; + } + } + color: black; +} +} +.third { + color: green; +} diff --git a/tests/outputs/at_root.css b/tests/outputs/at_root.css new file mode 100644 index 00000000..86edb772 --- /dev/null +++ b/tests/outputs/at_root.css @@ -0,0 +1,15 @@ +.first { + color: red; } + +body .second { + color: white; + color: black; } + + .nested1 { + color: blue; + color: orange; } + .nested2 { + color: yellow; } + +.third { + color: green; } diff --git a/tests/outputs_numbered/at_root.css b/tests/outputs_numbered/at_root.css new file mode 100644 index 00000000..873218f2 --- /dev/null +++ b/tests/outputs_numbered/at_root.css @@ -0,0 +1,19 @@ +/* line 1, inputs/at_root.scss */ +.first { + color: red; } +/* line 4, inputs/at_root.scss */ +/* line 5, inputs/at_root.scss */ + body .second { + color: white; + color: black; } + +/* line 9, inputs/at_root.scss */ +.nested1 { +color: blue; +color: orange; } +/* line 12, inputs/at_root.scss */ +.nested2 { +color: yellow; } +/* line 22, inputs/at_root.scss */ +.third { + color: green; } From 9992440b533b97e9d6b22f78bfb0d9c79f8cd70c Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Fri, 25 Sep 2015 03:58:17 -0400 Subject: [PATCH 050/484] internals: formatter indentStr() --- src/Compiler.php | 3 --- src/Formatter.php | 6 ++---- src/Formatter/Compact.php | 2 +- src/Formatter/Compressed.php | 8 -------- src/Formatter/Crunched.php | 8 -------- src/Formatter/Expanded.php | 8 ++++++++ src/Formatter/Nested.php | 21 +++++++++++++++++++-- 7 files changed, 30 insertions(+), 26 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index e578b8fa..5dca4476 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -629,7 +629,6 @@ protected function compileNestedBlock($block, $selectors) protected function compileBlock($block) { $env = $this->pushEnv($block); - $env->selectors = $this->evalSelectors($block->selectors); $out = $this->makeOutputBlock(null); @@ -1377,13 +1376,11 @@ protected function compileChild($child, $out) } $this->storeEnv = $content->scope; - //$this->pushEnv(); foreach ($content->children as $child) { $this->compileChild($child, $out); } - //$this->popEnv(); $this->storeEnv = null; break; diff --git a/src/Formatter.php b/src/Formatter.php index c341f1d7..90f79e90 100644 --- a/src/Formatter.php +++ b/src/Formatter.php @@ -58,13 +58,11 @@ abstract public function __construct(); /** * Return indentation (whitespace) * - * @param integer $n - * * @return string */ - protected function indentStr($n = 0) + protected function indentStr() { - return str_repeat($this->indentChar, max($this->indentLevel + $n, 0)); + return ''; } /** diff --git a/src/Formatter/Compact.php b/src/Formatter/Compact.php index 29874c96..eda6ec24 100644 --- a/src/Formatter/Compact.php +++ b/src/Formatter/Compact.php @@ -37,7 +37,7 @@ public function __construct() /** * {@inheritdoc} */ - public function indentStr($n = 0) + public function indentStr() { return ' '; } diff --git a/src/Formatter/Compressed.php b/src/Formatter/Compressed.php index 2ab2eaae..21def797 100644 --- a/src/Formatter/Compressed.php +++ b/src/Formatter/Compressed.php @@ -34,14 +34,6 @@ public function __construct() $this->assignSeparator = ':'; } - /** - * {@inheritdoc} - */ - public function indentStr($n = 0) - { - return ''; - } - /** * {@inheritdoc} */ diff --git a/src/Formatter/Crunched.php b/src/Formatter/Crunched.php index ad52c464..1e930b4f 100644 --- a/src/Formatter/Crunched.php +++ b/src/Formatter/Crunched.php @@ -34,14 +34,6 @@ public function __construct() $this->assignSeparator = ':'; } - /** - * {@inheritdoc} - */ - public function indentStr($n = 0) - { - return ''; - } - /** * {@inheritdoc} */ diff --git a/src/Formatter/Expanded.php b/src/Formatter/Expanded.php index 89ecfffb..25e6639d 100644 --- a/src/Formatter/Expanded.php +++ b/src/Formatter/Expanded.php @@ -34,6 +34,14 @@ public function __construct() $this->assignSeparator = ': '; } + /** + * {@inheritdoc} + */ + protected function indentStr() + { + return str_repeat($this->indentChar, $this->indentLevel); + } + /** * {@inheritdoc} */ diff --git a/src/Formatter/Nested.php b/src/Formatter/Nested.php index 3897419b..45866ca9 100644 --- a/src/Formatter/Nested.php +++ b/src/Formatter/Nested.php @@ -20,6 +20,11 @@ */ class Nested extends Formatter { + /** + * @var integer + */ + private $depth; + /** * {@inheritdoc} */ @@ -34,12 +39,22 @@ public function __construct() $this->assignSeparator = ': '; } + /** + * {@inheritdoc} + */ + protected function indentStr() + { + $n = $this->depth - 1; + + return str_repeat($this->indentChar, max($this->indentLevel + $n, 0)); + } + /** * {@inheritdoc} */ protected function blockLines($block) { - $inner = $this->indentStr($block->depth - 1); + $inner = $this->indentStr(); $glue = $this->break . $inner; @@ -61,7 +76,7 @@ protected function blockLines($block) */ protected function blockSelectors($block) { - $inner = $this->indentStr($block->depth - 1); + $inner = $this->indentStr(); echo $inner . implode($this->tagSeparator, $block->selectors) @@ -99,6 +114,8 @@ protected function block($block) $this->adjustAllChildren($block); } + $this->depth = $block->depth; + if (! empty($block->selectors)) { $this->blockSelectors($block); From 2356983ad1234972014eed68a23b5b3c1a47b53f Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sun, 27 Sep 2015 17:29:50 -0400 Subject: [PATCH 051/484] @at-root inline selector --- src/Compiler.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/Compiler.php b/src/Compiler.php index 5dca4476..def09836 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -552,6 +552,20 @@ protected function compileAtRoot($block) $this->scope->depth = 1; $this->scope->parent->children[] = $this->scope; + // wrap inline selector + if ($block->selector) { + $wrapped = (object) array( + 'parent' => $block, + 'sourcePosition' => $block->sourcePosition, + 'sourceParser' => $block->sourceParser, + 'selectors' => $block->selector, + 'comments' => array(), + 'children' => $block->children, + ); + + $block->children = array(array('block', $wrapped)); + } + $this->compileChildren($block->children, $this->scope); $this->scope = $this->scope->parent; From ab533fd945c59001b99c183f577a94dd375abbe3 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sat, 3 Oct 2015 19:14:32 -0400 Subject: [PATCH 052/484] fixes #343 --- src/Compiler.php | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index def09836..0413d4c4 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -2255,6 +2255,7 @@ public function compileValue($value) // strip quotes if it's a string $reduced = $this->reduce($exp); + switch ($reduced[0]) { case 'string': $reduced = array('keyword', $this->compileStringContent($reduced)); @@ -3835,8 +3836,7 @@ protected function libHsl($args) return $this->toRGB($h[1], $s[1], $l[1]); } - protected static $libHsla = array('hue', 'saturation', - 'lightness', 'alpha'); + protected static $libHsla = array('hue', 'saturation', 'lightness', 'alpha'); protected function libHsla($args) { list($h, $s, $l, $a) = $args; @@ -4197,11 +4197,10 @@ protected function libSetNth($args) 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->compileValue($map[1][$i])) { + if ($key === $this->compileStringContent($this->coerceString($map[1][$i]))) { return $map[2][$i]; } } @@ -4213,7 +4212,6 @@ protected function libMapGet($args) protected function libMapKeys($args) { $map = $this->assertMap($args[0]); - $keys = $map[1]; return array('list', ',', $keys); @@ -4223,7 +4221,6 @@ protected function libMapKeys($args) protected function libMapValues($args) { $map = $this->assertMap($args[0]); - $values = $map[2]; return array('list', ',', $values); @@ -4233,11 +4230,10 @@ protected function libMapValues($args) 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->compileValue($map[1][$i])) { + if ($key === $this->compileStringContent($this->coerceString($map[1][$i]))) { array_splice($map[1], $i, 1); array_splice($map[2], $i, 1); } @@ -4250,11 +4246,10 @@ protected function libMapRemove($args) 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->compileValue($map[1][$i])) { + if ($key === $this->compileStringContent($this->coerceString($map[1][$i]))) { return self::$true; } } @@ -4331,6 +4326,7 @@ protected function libZip($args) break 2; } } + $lists[] = $list; } From 5de607bc5ea7a62740be49368d62ef6c34af4ed7 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sat, 3 Oct 2015 20:56:34 -0400 Subject: [PATCH 053/484] not(value) === not (value) --- src/Parser.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Parser.php b/src/Parser.php index 5e7370b2..d2b4b99a 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -1129,6 +1129,14 @@ protected function value(&$out) $this->seek($s); + if ($this->literal('not', false) && $this->parenValue($inner)) { + $out = array('unary', 'not', $inner, $this->inParens); + + return true; + } + + $this->seek($s); + if ($this->literal('+') && $this->value($inner)) { $out = array('unary', '+', $inner, $this->inParens); From a891c167987bb7453508cf1eba0271190c49d39a Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sat, 3 Oct 2015 22:10:02 -0400 Subject: [PATCH 054/484] fix short circuit evaluation -- it should delay evaluation of right side --- src/Compiler.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 0413d4c4..880b946a 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -1512,7 +1512,10 @@ protected function reduce($value, $inExp = false) $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 short-hand if ($opName === 'div' && ! $inParens && ! $inExp && isset($right[2]) && $right[2] !== '') { @@ -1900,7 +1903,7 @@ protected function opAnd($left, $right, $shouldEval) } if ($left !== self::$false) { - return $right; + return $this->reduce($right, true); } return $left; @@ -1925,7 +1928,7 @@ protected function opOr($left, $right, $shouldEval) return $left; } - return $right; + return $this->reduce($right, true); } /** From 4ed0142ff2ddabe1be2c2305dd21b8076a3bd746 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sun, 4 Oct 2015 10:22:03 -0400 Subject: [PATCH 055/484] Bump version to 0.3.2 --- src/Version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Version.php b/src/Version.php index efd45ca0..fffc751b 100644 --- a/src/Version.php +++ b/src/Version.php @@ -18,5 +18,5 @@ */ class Version { - const VERSION = 'v0.3.1'; + const VERSION = 'v0.3.2'; } From 93f4d16398d73538a37a172c738440da2ff61cc2 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sun, 4 Oct 2015 10:41:29 -0400 Subject: [PATCH 056/484] fixes #345 --- src/Compiler.php | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 880b946a..8b4de85c 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -2964,8 +2964,10 @@ protected function callBuiltin($name, $args, &$returnValue) // see if we can find a user function $fn = $this->userFunctions[$name]; - foreach ($args as &$val) { - $val = $this->reduce($val[1], true); + if ($name !== 'if') { + foreach ($args as &$val) { + $val = $this->reduce($val[1], true); + } } $returnValue = call_user_func($fn, $args, $this); @@ -2978,8 +2980,10 @@ protected function callBuiltin($name, $args, &$returnValue) $prototype = isset(self::$$libName) ? self::$$libName : null; $sorted = $this->sortArgs($prototype, $args); - foreach ($sorted as &$val) { - $val = $this->reduce($val, true); + if ($name !== 'if') { + foreach ($sorted as &$val) { + $val = $this->reduce($val, true); + } } $returnValue = call_user_func($f, $sorted, $this); @@ -3575,11 +3579,11 @@ protected function libIf($args) { list($cond, $t, $f) = $args; - if (! $this->isTruthy($cond)) { - return $f; + if (! $this->isTruthy($this->reduce($cond, true))) { + return $this->reduce($f, true); } - return $t; + return $this->reduce($t, true); } protected static $libIndex = array('list', 'value'); From 14d7295ac3defadbf5b8aa767ed132867bf83108 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sun, 4 Oct 2015 10:45:13 -0400 Subject: [PATCH 057/484] add unit tests --- tests/inputs/short_circuit.scss | 3 +++ tests/outputs/short_circuit.css | 1 + tests/outputs_numbered/short_circuit.css | 1 + 3 files changed, 5 insertions(+) create mode 100644 tests/inputs/short_circuit.scss create mode 100644 tests/outputs/short_circuit.css create mode 100644 tests/outputs_numbered/short_circuit.css diff --git a/tests/inputs/short_circuit.scss b/tests/inputs/short_circuit.scss new file mode 100644 index 00000000..84f317a7 --- /dev/null +++ b/tests/inputs/short_circuit.scss @@ -0,0 +1,3 @@ +$test: if(true, 1, $undefined); +$test: false and $undefined; +$test: true or $undefined; diff --git a/tests/outputs/short_circuit.css b/tests/outputs/short_circuit.css new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/tests/outputs/short_circuit.css @@ -0,0 +1 @@ + diff --git a/tests/outputs_numbered/short_circuit.css b/tests/outputs_numbered/short_circuit.css new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/tests/outputs_numbered/short_circuit.css @@ -0,0 +1 @@ + From c0ffbc6d5b65772ee1c101e8b2e7ae1716630490 Mon Sep 17 00:00:00 2001 From: hitok Date: Sun, 4 Oct 2015 10:55:16 -0400 Subject: [PATCH 058/484] fixes #344 --- src/Compiler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compiler.php b/src/Compiler.php index 8b4de85c..1176470f 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -487,7 +487,7 @@ protected function compileMedia($media) foreach ($media->children as $child) { $type = $child[0]; - if ($type !== 'block' && $type !== 'media' && $type !== 'directive') { + if ($type !== 'block' && $type !== 'media' && $type !== 'directive' && $type !== 'import') { $needsWrap = true; break; } From 27b70ca5e3cc73014d4cae859a52f1567a461266 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sun, 4 Oct 2015 12:22:47 -0400 Subject: [PATCH 059/484] #302 - partial call() support; doesn't support kwargs yet --- src/Compiler.php | 12 ++++++++++-- tests/inputs/builtins.scss | 6 ++++++ tests/outputs/builtins.css | 4 ++++ tests/outputs_numbered/builtins.css | 4 ++++ 4 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 1176470f..b783c4f1 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -2964,7 +2964,7 @@ protected function callBuiltin($name, $args, &$returnValue) // see if we can find a user function $fn = $this->userFunctions[$name]; - if ($name !== 'if') { + if ($name !== 'if' && $name !== 'call') { foreach ($args as &$val) { $val = $this->reduce($val[1], true); } @@ -2980,7 +2980,7 @@ protected function callBuiltin($name, $args, &$returnValue) $prototype = isset(self::$$libName) ? self::$$libName : null; $sorted = $this->sortArgs($prototype, $args); - if ($name !== 'if') { + if ($name !== 'if' && $name !== 'call') { foreach ($sorted as &$val) { $val = $this->reduce($val, true); } @@ -3574,6 +3574,14 @@ public function toRGB($hue, $saturation, $lightness) // Built in functions + //protected static $libCall = array('name', 'args...'); + protected function libCall($args) + { + $name = $this->compileStringContent($this->coerceString($this->reduce(array_shift($args), true))); + + return $this->reduce(array('fncall', $name, array_map(function ($a) { return array(null, $a); }, $args))); + } + protected static $libIf = array('condition', 'if-true', 'if-false'); protected function libIf($args) { diff --git a/tests/inputs/builtins.scss b/tests/inputs/builtins.scss index 37487a08..1b8bffb4 100644 --- a/tests/inputs/builtins.scss +++ b/tests/inputs/builtins.scss @@ -185,3 +185,9 @@ $a-false-value: false; c: variable-exists(nonexistent); } +$fn: nth; +div.call-tests { + a: call(rgb, 10, 100, 255); + //b: call(scale-color, #0a64ff, $lightness: -10%); + c: call($fn, (a b c), 2); +} diff --git a/tests/outputs/builtins.css b/tests/outputs/builtins.css index 84c6de27..7778777e 100644 --- a/tests/outputs/builtins.css +++ b/tests/outputs/builtins.css @@ -133,3 +133,7 @@ a: true; b: true; c: false; } + +div.call-tests { + a: #0a64ff; + c: b; } diff --git a/tests/outputs_numbered/builtins.css b/tests/outputs_numbered/builtins.css index 954e77d4..b62b42c6 100644 --- a/tests/outputs_numbered/builtins.css +++ b/tests/outputs_numbered/builtins.css @@ -134,3 +134,7 @@ a: true; b: true; c: false; } +/* line 189, inputs/builtins.scss */ +div.call-tests { + a: #0a64ff; + c: b; } From 033dd027dfc8e1905ce782a687160d66d3bd65ee Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sun, 4 Oct 2015 12:25:29 -0400 Subject: [PATCH 060/484] phpcs --- src/Compiler.php | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/Compiler.php b/src/Compiler.php index b783c4f1..a916f85c 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -3579,7 +3579,18 @@ protected function libCall($args) { $name = $this->compileStringContent($this->coerceString($this->reduce(array_shift($args), true))); - return $this->reduce(array('fncall', $name, array_map(function ($a) { return array(null, $a); }, $args))); + return $this->reduce( + array( + 'fncall', + $name, + array_map( + function ($a) { + return array(null, $a); + }, + $args + ) + ) + ); } protected static $libIf = array('condition', 'if-true', 'if-false'); From 2c5b40e9787879e5c50887e214c0ab5ffa53043c Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Mon, 5 Oct 2015 14:05:53 -0400 Subject: [PATCH 061/484] Compiler: coerceList(map) fixes #343 --- src/Compiler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compiler.php b/src/Compiler.php index a916f85c..8cf5fac0 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -3272,7 +3272,7 @@ protected function coerceList($item, $delim = ',') $key = $keys[$i]; $value = $values[$i]; - $list[] = array('list', '', array(array('keyword', $this->compileValue($key)), $value)); + $list[] = array('list', '', array(array('keyword', $this->compileStringContent($this->coerceString($key))), $value)); } return array('list', ',', $list); From d8ee4e9b77e55b891ec975a37c487c890f8c482e Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Mon, 5 Oct 2015 14:08:27 -0400 Subject: [PATCH 062/484] update unit tests --- src/Compiler.php | 6 +++++- tests/inputs/map.scss | 8 ++++++++ tests/outputs/map.css | 2 ++ tests/outputs_numbered/map.css | 3 +++ 4 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/Compiler.php b/src/Compiler.php index 8cf5fac0..43a30bb4 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -3272,7 +3272,11 @@ protected function coerceList($item, $delim = ',') $key = $keys[$i]; $value = $values[$i]; - $list[] = array('list', '', array(array('keyword', $this->compileStringContent($this->coerceString($key))), $value)); + $list[] = array( + 'list', + '', + array(array('keyword', $this->compileStringContent($this->coerceString($key))), $value) + ); } return array('list', ',', $list); diff --git a/tests/inputs/map.scss b/tests/inputs/map.scss index 8284151f..ecaf29d1 100644 --- a/tests/inputs/map.scss +++ b/tests/inputs/map.scss @@ -24,3 +24,11 @@ div { foo: nth($map, 1); bar: nth(nth($map, 1), 1); } + +$color: ("black" : #000000); + +@each $color_name, $color_value in $color { + .#{$color_name} { + background-color: $color_value !important; + } +} diff --git a/tests/outputs/map.css b/tests/outputs/map.css index 5beeeef5..5dad9b77 100644 --- a/tests/outputs/map.css +++ b/tests/outputs/map.css @@ -10,3 +10,5 @@ div { div { foo: color black; bar: color; } + .black { + background-color: #000 !important; } diff --git a/tests/outputs_numbered/map.css b/tests/outputs_numbered/map.css index 48658216..81366a16 100644 --- a/tests/outputs_numbered/map.css +++ b/tests/outputs_numbered/map.css @@ -11,3 +11,6 @@ div { div { foo: color black; bar: color; } +/* line 31, inputs/map.scss */ +.black { + background-color: #000 !important; } From 73c9f33ad92f68e0472e245c6adcc28ad8bc179c Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Mon, 5 Oct 2015 14:45:32 -0400 Subject: [PATCH 063/484] ignore /frameworks/ folder --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 4ffba003..dbff826d 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ pscss.phar /sass/ /compass/ /vendor/ +/frameworks/ From ce2607c66985a38cece865e91a1962cb0ccbac56 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Mon, 5 Oct 2015 20:18:35 -0400 Subject: [PATCH 064/484] add unit tests for #346 --- tests/inputs/map.scss | 27 ++++++++++++++++++++++++--- tests/outputs/map.css | 6 ++++++ tests/outputs_numbered/map.css | 6 ++++++ 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/tests/inputs/map.scss b/tests/inputs/map.scss index ecaf29d1..0fb6be19 100644 --- a/tests/inputs/map.scss +++ b/tests/inputs/map.scss @@ -28,7 +28,28 @@ div { $color: ("black" : #000000); @each $color_name, $color_value in $color { - .#{$color_name} { - background-color: $color_value !important; - } + .#{$color_name} { + background-color: $color_value !important; + } +} + +$args: ('a': 1, 'b': 2); + +@mixin output($args) { + @each $k, $v in $args { + #{$k}: $v; + } +} + +@mixin output-varargs( + $a, + $b +) { + color: $a; + background-color: $b; +} + +div { + @include output($args); + @include output-varargs($args...); } diff --git a/tests/outputs/map.css b/tests/outputs/map.css index 5dad9b77..9c38094a 100644 --- a/tests/outputs/map.css +++ b/tests/outputs/map.css @@ -12,3 +12,9 @@ div { bar: color; } .black { background-color: #000 !important; } + +div { + a: 1; + b: 2; + color: 1; + background-color: 2; } diff --git a/tests/outputs_numbered/map.css b/tests/outputs_numbered/map.css index 81366a16..16de52b9 100644 --- a/tests/outputs_numbered/map.css +++ b/tests/outputs_numbered/map.css @@ -14,3 +14,9 @@ div { /* line 31, inputs/map.scss */ .black { background-color: #000 !important; } +/* line 52, inputs/map.scss */ +div { + a: 1; + b: 2; + color: 1; + background-color: 2; } From 8e9d689603c6ec31c0e8074d81bf9a6cc2425c3c Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Mon, 5 Oct 2015 20:32:18 -0400 Subject: [PATCH 065/484] fixes #346 --- src/Compiler.php | 11 +++++++++++ src/Parser.php | 1 + 2 files changed, 12 insertions(+) diff --git a/src/Compiler.php b/src/Compiler.php index 43a30bb4..d2901119 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -3126,6 +3126,17 @@ protected function applyArguments($argDef, $argValues) if ($val[0] === 'list') { foreach ($val[2] as $name => $item) { + if (! is_numeric($name)) { + $keywordArgs[$name] = $item; + } else { + $remaining[] = $item; + } + } + } elseif ($val[0] === '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 { diff --git a/src/Parser.php b/src/Parser.php index d2b4b99a..5312a64d 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -668,6 +668,7 @@ protected function literal($what, $eatWhitespace = null) return true; } + // goes below... } else { return false; From 12f27ee83a3493d9e235e15a0682d7c26d00a877 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Mon, 5 Oct 2015 21:31:15 -0400 Subject: [PATCH 066/484] fixes #347 --- src/Compiler.php | 14 ++++++++++++-- tests/inputs/list.scss | 1 + tests/outputs/list.css | 3 ++- tests/outputs_numbered/list.css | 5 +++-- 4 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index d2901119..a8b6b2c3 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -2212,6 +2212,10 @@ public function compileValue($value) list(, $delim, $items) = $value; + if ($delim !== ' ') { + $delim .= ' '; + } + $filtered = array(); foreach ($items as $item) { @@ -2222,7 +2226,7 @@ public function compileValue($value) $filtered[] = $this->compileValue($item); } - return implode("$delim ", $filtered); + return implode("$delim", $filtered); case 'map': $keys = $value[1]; @@ -4214,7 +4218,13 @@ protected function libListSeparator($args) protected function libNth($args) { $list = $this->coerceList($args[0]); - $n = $this->assertNumber($args[1]) - 1; + $n = $this->assertNumber($args[1]); + + if ($n > 0) { + $n--; + } elseif ($n < 0) { + $n += count($list[2]); + } return isset($list[2][$n]) ? $list[2][$n] : self::$defaultValue; } diff --git a/tests/inputs/list.scss b/tests/inputs/list.scss index 483292d3..8d290e9f 100644 --- a/tests/inputs/list.scss +++ b/tests/inputs/list.scss @@ -25,6 +25,7 @@ div { a: nth($list, 1); b: nth($list, 2); c: nth($list, 3); + d: nth($list, -1); } div { diff --git a/tests/outputs/list.css b/tests/outputs/list.css index aed03cea..7358b340 100644 --- a/tests/outputs/list.css +++ b/tests/outputs/list.css @@ -13,7 +13,8 @@ div { div { a: 10px; b: -20px; - c: 30px; } + c: 30px; + d: 30px; } div { x: space; diff --git a/tests/outputs_numbered/list.css b/tests/outputs_numbered/list.css index 90cc8576..d80d6150 100644 --- a/tests/outputs_numbered/list.css +++ b/tests/outputs_numbered/list.css @@ -14,8 +14,9 @@ div { div { a: 10px; b: -20px; - c: 30px; } -/* line 30, inputs/list.scss */ + c: 30px; + d: 30px; } +/* line 31, inputs/list.scss */ div { x: space; y: comma; From a763e28ed14cd5d5478d754cece0b9ad726b9661 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Mon, 5 Oct 2015 21:34:27 -0400 Subject: [PATCH 067/484] refs #347 - fixes negative index with set-nth() too --- src/Compiler.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Compiler.php b/src/Compiler.php index a8b6b2c3..68ef704f 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -4233,7 +4233,13 @@ protected function libNth($args) protected function libSetNth($args) { $list = $this->coerceList($args[0]); - $n = $this->assertNumber($args[1]) - 1; + $n = $this->assertNumber($args[1]); + + if ($n > 0) { + $n--; + } elseif ($n < 0) { + $n += count($list[2]); + } if (! isset($list[2][$n])) { $this->throwError('Invalid argument for "n"'); From d88972e42dee2a9c16368ab973d052f99c0412e0 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 6 Oct 2015 21:03:56 -0400 Subject: [PATCH 068/484] refs #349 - nested mixin @content --- src/Compiler.php | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 68ef704f..300e1e31 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -1362,7 +1362,7 @@ protected function compileChild($child, $out) if (isset($content)) { $content->scope = $callingScope; - $this->setRaw(self::$namespaces['special'] . 'content', $content); + $this->setRaw(self::$namespaces['special'] . 'content', $content, $this->env); } if (isset($mixin->args)) { @@ -1379,13 +1379,15 @@ protected function compileChild($child, $out) break; case 'mixin_content': - $content = $this->get(self::$namespaces['special'] . 'content', false); + $content = $this->get(self::$namespaces['special'] . 'content', false, $this->env); if (! $content) { $this->throwError('Expected @content inside of mixin'); } - if (! isset($content->children)) { + if (! isset($content->children) || + $this->storeEnv === $content->scope + ) { break; } @@ -2536,6 +2538,10 @@ 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 { @@ -2550,12 +2556,8 @@ protected function set($name, $value, $shadow = false, $env = null) * @param mixed $value * @param \stdClass $env */ - protected function setExisting($name, $value, $env = null) + protected function setExisting($name, $value, $env) { - if (! isset($env)) { - $env = $this->getStoreEnv(); - } - $storeEnv = $env; $hasNamespace = $name[0] === '^' || $name[0] === '@' || $name[0] === '%'; @@ -2588,12 +2590,8 @@ protected function setExisting($name, $value, $env = null) * @param mixed $value * @param \stdClass $env */ - protected function setRaw($name, $value, $env = null) + protected function setRaw($name, $value, $env) { - if (! isset($env)) { - $env = $this->getStoreEnv(); - } - $env->store[$name] = $value; } From 00088be53b3a4917b7d9398be65facbffec521a0 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Fri, 9 Oct 2015 00:27:56 -0400 Subject: [PATCH 069/484] add tests --- src/Compiler.php | 8 +++----- tests/inputs/content.scss | 20 ++++++++++++++++++++ tests/outputs/content.css | 3 +++ tests/outputs_numbered/content.css | 5 +++++ 4 files changed, 31 insertions(+), 5 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 300e1e31..ca6df8fb 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -1362,7 +1362,7 @@ protected function compileChild($child, $out) if (isset($content)) { $content->scope = $callingScope; - $this->setRaw(self::$namespaces['special'] . 'content', $content, $this->env); + $this->setRaw(self::$namespaces['special'] . 'content', $content, $this->getStoreEnv()); } if (isset($mixin->args)) { @@ -1379,15 +1379,13 @@ protected function compileChild($child, $out) break; case 'mixin_content': - $content = $this->get(self::$namespaces['special'] . 'content', false, $this->env); + $content = $this->get(self::$namespaces['special'] . 'content', false, $this->getStoreEnv()); if (! $content) { $this->throwError('Expected @content inside of mixin'); } - if (! isset($content->children) || - $this->storeEnv === $content->scope - ) { + if (! isset($content->children)) { break; } diff --git a/tests/inputs/content.scss b/tests/inputs/content.scss index 55ca04e8..a56acae3 100644 --- a/tests/inputs/content.scss +++ b/tests/inputs/content.scss @@ -59,3 +59,23 @@ $color: white; $i: $i + 1; } } + +@mixin nested { + * body { + @content; + } +} + +@mixin top($args) { + * html { + @include nested($args) { + @content; + } + } +} + +@include top('hello' 'world') { + #logo { + background-image: url(/logo.gif); + } +} diff --git a/tests/outputs/content.css b/tests/outputs/content.css index 6657a216..89a1ad9e 100644 --- a/tests/outputs/content.css +++ b/tests/outputs/content.css @@ -27,3 +27,6 @@ width: 100%; } .grid-2 { width: 100%; } } + +* html * body #logo { + background-image: url(/logo.gif); } diff --git a/tests/outputs_numbered/content.css b/tests/outputs_numbered/content.css index 016b8946..7948df35 100644 --- a/tests/outputs_numbered/content.css +++ b/tests/outputs_numbered/content.css @@ -34,3 +34,8 @@ /* line 58, inputs/content.scss */ .grid-2 { width: 100%; } } +/* line 70, inputs/content.scss */ +/* line 64, inputs/content.scss */ + /* line 78, inputs/content.scss */ + * html * body #logo { + background-image: url(/logo.gif); } From f30b7221bf7d38ab64c306d85c16a51f19e5bbf6 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 13 Oct 2015 13:31:57 -0400 Subject: [PATCH 070/484] partial revert 00088be5; fixes #350 --- src/Server.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Server.php b/src/Server.php index 5bd9bef0..d85a9190 100644 --- a/src/Server.php +++ b/src/Server.php @@ -139,7 +139,7 @@ protected function needsCompile($in, $out, &$etag) $metadata = unserialize(file_get_contents($metadataName)); foreach ($metadata['imports'] as $import => $importMtime) { - if ($importMtime > $mtime) { + if (filemtime($import) > $mtime) { return true; } } From e5c40d02d9c5d287337e9ca316c76725deaf1a63 Mon Sep 17 00:00:00 2001 From: jk Date: Tue, 13 Oct 2015 22:39:39 +0200 Subject: [PATCH 071/484] quick check to needsCompile for changed registeredVars with a crc32 hash --- src/Compiler.php | 12 ++++++++++++ src/Server.php | 6 ++++++ 2 files changed, 18 insertions(+) diff --git a/src/Compiler.php b/src/Compiler.php index ca6df8fb..d14e0875 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -2701,6 +2701,18 @@ public function unsetVariable($name) unset($this->registeredVars[$name]); } + /** + * Returns list of variables + * + * @api + * + * @return array + */ + public function getVariables() + { + return $this->registeredVars; + } + /** * Adds to list of parsed files * diff --git a/src/Server.php b/src/Server.php index d85a9190..6b2a7003 100644 --- a/src/Server.php +++ b/src/Server.php @@ -143,6 +143,11 @@ protected function needsCompile($in, $out, &$etag) return true; } } + + $metaVars = crc32(serialize($this->scss->getVariables())); + if ($metaVars!=$metadata['vars']) { + return true; + } $etag = $metadata['etag']; @@ -213,6 +218,7 @@ protected function compile($in, $out) serialize(array( 'etag' => $etag, 'imports' => $this->scss->getParsedFiles(), + 'vars' => crc32(serialize($this->scss->getVariables())), )) ); From fed68815b1b6e7d2c7cb056303333c12f7940eaa Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 14 Oct 2015 13:11:34 -0400 Subject: [PATCH 072/484] refs #333 - phpcs --- Makefile | 5 ++++- src/Compiler.php | 2 +- src/Server.php | 7 ++++--- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index c608f8e1..0d64cb3c 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,8 @@ -test: +tests: vendor/bin/phpunit --colors tests +standard: + vendor/bin/phpcs --standard=PSR2 bin src tests *.php + phar: php -dphar.readonly=0 vendor/bin/box build -v diff --git a/src/Compiler.php b/src/Compiler.php index d14e0875..6ef519b0 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -1379,7 +1379,7 @@ protected function compileChild($child, $out) break; case 'mixin_content': - $content = $this->get(self::$namespaces['special'] . 'content', false, $this->getStoreEnv()); + $content = $this->get(self::$namespaces['special'] . 'content', false, $this->getStoreEnv()); if (! $content) { $this->throwError('Expected @content inside of mixin'); diff --git a/src/Server.php b/src/Server.php index 6b2a7003..ff0d7285 100644 --- a/src/Server.php +++ b/src/Server.php @@ -143,9 +143,10 @@ protected function needsCompile($in, $out, &$etag) return true; } } - + $metaVars = crc32(serialize($this->scss->getVariables())); - if ($metaVars!=$metadata['vars']) { + + if ($metaVars !== $metadata['vars']) { return true; } @@ -218,7 +219,7 @@ protected function compile($in, $out) serialize(array( 'etag' => $etag, 'imports' => $this->scss->getParsedFiles(), - 'vars' => crc32(serialize($this->scss->getVariables())), + 'vars' => crc32(serialize($this->scss->getVariables())), )) ); From 408e75e82135ac600ff7a203ebcfec50d314800e Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 14 Oct 2015 13:17:21 -0400 Subject: [PATCH 073/484] compat tests --- Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Makefile b/Makefile index 0d64cb3c..d08ee190 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,9 @@ tests: vendor/bin/phpunit --colors tests +compat: + TEST_SCSS_COMPAT=1 vendor/bin/phpunit --colors tests | tail -2 + standard: vendor/bin/phpcs --standard=PSR2 bin src tests *.php From fbf5864004eb188571a7f78db65b921175a307e1 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 14 Oct 2015 15:13:05 -0400 Subject: [PATCH 074/484] add .editorconfig (per #353) --- .editorconfig | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..31dd6aa4 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,14 @@ +root = true + +[*] +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +charset = utf-8 + +[Makefile] +indent_style = tab + +[*.php] +indent_style = space +indent_size = 4 From ef676cd8b6c83c2acdda43bd5701d776f6f0fea7 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Fri, 16 Oct 2015 17:03:27 -0400 Subject: [PATCH 075/484] Compiler: addFeature(); fixes #342 --- src/Compiler.php | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 6ef519b0..c1eb83e4 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -109,6 +109,12 @@ class Compiler protected $importCache = array(); protected $userFunctions = array(); protected $registeredVars = array(); + protected $features = array( + 'extend-selector-pseudoclass' => false, + 'at-error' => true, + 'units-level-3' => false, + 'global-variable-shadowing' => false, + ); protected $numberPrecision = 5; protected $lineNumberStyle = null; @@ -2826,6 +2832,18 @@ public function unregisterFunction($name) unset($this->userFunctions[$this->normalizeName($name)]); } + /** + * Add feature + * + * @api + * + * @param string $name + */ + public function addFeature($name) + { + $this->features[$name] = true; + } + /** * Import file * @@ -4545,14 +4563,10 @@ protected function libToUpperCase($args) protected static $libFeatureExists = array('feature'); protected function libFeatureExists($args) { - /* - * The following features not not (yet) supported: - * - global-variable-shadowing - * - extend-selector-pseudoclass - * - units-level-3 - * - at-error - */ - return self::$false; + $string = $this->coerceString($args[0]); + $name = $this->compileStringContent($string); + + return $this->toBool(array_key_exists($name, $this->feature) ? $this->features[$name] : false); } protected static $libFunctionExists = array('name'); From 16bd233cfa821fbbb972892ad1815d62d9d44eb4 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Fri, 16 Oct 2015 17:14:33 -0400 Subject: [PATCH 076/484] Server: handle more file timestamp edge cases --- src/Server.php | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/Server.php b/src/Server.php index ff0d7285..9095e357 100644 --- a/src/Server.php +++ b/src/Server.php @@ -129,17 +129,15 @@ protected function needsCompile($in, $out, &$etag) $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 => $importMtime) { - if (filemtime($import) > $mtime) { + foreach ($metadata['imports'] as $import => $originalMtime) { + $currentMtime = filemtime($import); + + if ($currentMtime !== $originalMtime || $currentMtime > $mtime) { return true; } } From 6252bedccc3d1c46e4b7075d3512ec4eaa2a9fc0 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Fri, 16 Oct 2015 18:14:52 -0400 Subject: [PATCH 077/484] phpcs --- src/Compiler.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Compiler.php b/src/Compiler.php index c1eb83e4..7a3ff57b 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -2440,6 +2440,7 @@ protected function multiplyMedia($env, $childQueries = null) } $parentQueries = $env->block->queryList; + if ($childQueries === null) { $childQueries = $parentQueries; } else { From c968e998869b285a671dcbc5c27682fed0040bd5 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 21 Oct 2015 12:44:18 -0400 Subject: [PATCH 078/484] ignore .editorconfig in git archives --- .gitattributes | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitattributes b/.gitattributes index a116ff23..73d1a258 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,4 +1,5 @@ Makefile export-ignore +.editorconfig export-ignore .gitattributes export-ignore .gitignore export-ignore .travis.yml export-ignore From 3301bc53f51d449e25fdf904a3b4939f33a5a911 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 21 Oct 2015 16:14:32 -0400 Subject: [PATCH 079/484] declare rootEnv and rootBlock; rename features to registeredFeatures --- src/Compiler.php | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 7a3ff57b..f4765e79 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -109,7 +109,7 @@ class Compiler protected $importCache = array(); protected $userFunctions = array(); protected $registeredVars = array(); - protected $features = array( + protected $registeredFeatures = array( 'extend-selector-pseudoclass' => false, 'at-error' => true, 'units-level-3' => false, @@ -121,6 +121,9 @@ class Compiler protected $formatter = 'Leafo\ScssPhp\Formatter\Nested'; + protected $rootEnv; + protected $rootBlock; + private $indentLevel; private $commentsSeen; private $extends; @@ -252,7 +255,7 @@ protected function makeOutputBlock($type, $selectors = null) */ protected function compileRoot($rootBlock) { - $this->scope = $this->makeOutputBlock('root'); + $this->rootBlock = $this->scope = $this->makeOutputBlock('root'); $this->compileChildren($rootBlock->children, $this->scope); $this->flattenSelectors($this->scope); @@ -2842,7 +2845,7 @@ public function unregisterFunction($name) */ public function addFeature($name) { - $this->features[$name] = true; + $this->registeredFeatures[$name] = true; } /** @@ -4567,7 +4570,7 @@ protected function libFeatureExists($args) $string = $this->coerceString($args[0]); $name = $this->compileStringContent($string); - return $this->toBool(array_key_exists($name, $this->feature) ? $this->features[$name] : false); + return $this->toBool(array_key_exists($name, $this->registeredFeatures) ? $this->registeredFeatures[$name] : false); } protected static $libFunctionExists = array('name'); From 9c488fd601d253e279f6b12fe59486c63250aeec Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 21 Oct 2015 22:18:08 -0400 Subject: [PATCH 080/484] refs #328 - change sourceParser to an index, minimizing self-referencing recursion in the parse tree --- src/Compiler.php | 83 ++++++++++++++++++++++++++++-------------------- src/Parser.php | 21 ++++++------ 2 files changed, 61 insertions(+), 43 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index f4765e79..529fdac0 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -133,7 +133,8 @@ class Compiler private $scope; private $parser; private $sourcePos; - private $sourceParser; + private $sourceParsers; + private $sourceIndex; private $storeEnv; private $charsetSeen; private $stderr; @@ -151,27 +152,25 @@ class Compiler */ public function compile($code, $path = 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->parser = new Parser($path); - + $this->indentLevel = -1; + $this->commentsSeen = array(); + $this->extends = array(); + $this->extendsMap = array(); + $this->parsedFiles = array(); + $this->sourceParsers = array(); + $this->sourceIndex = null; + $this->env = null; + $this->scope = null; + $this->stderr = fopen('php://stderr', 'w'); + + $this->parser = $this->parserFactory($path); $tree = $this->parser->parse($code); $this->formatter = new $this->formatter(); - $this->addParsedFile($path); - $this->rootEnv = $this->pushEnv($tree); $this->injectVariables($this->registeredVars); $this->compileRoot($tree); @@ -184,6 +183,24 @@ public function compile($code, $path = null) return $out; } + /** + * Instantiate parser + * + * @param string $path + * @param boolean $isRoot + * + * @return \Leafo\Parser + */ + private function parserFactory($path, $isRoot = true) + { + $parser = new Parser($path, count($this->sourceParsers), $isRoot); + + $this->sourceParsers[] = $parser; + $this->addParsedFile($path); + + return $parser; + } + /** * Is self extend? * @@ -566,7 +583,7 @@ protected function compileAtRoot($block) $wrapped = (object) array( 'parent' => $block, 'sourcePosition' => $block->sourcePosition, - 'sourceParser' => $block->sourceParser, + 'sourceIndex' => $block->sourceIndex, 'selectors' => $block->selector, 'comments' => array(), 'children' => $block->children, @@ -660,8 +677,9 @@ protected function compileBlock($block) $annotation = $this->makeOutputBlock('comment'); $annotation->depth = 0; - $file = $block->sourceParser->getSourceName(); - $line = $block->sourceParser->getLineNo($block->sourcePosition); + $parser = $this->sourceParsers[$block->sourceIndex]; + $file = $parser->getSourceName(); + $line = $parser->getLineNo($block->sourcePosition); switch ($this->lineNumberStyle) { case self::LINE_COMMENTS: @@ -718,7 +736,7 @@ protected function evalSelectors($selectors) // after evaluating interpolates, we might need a second pass if ($this->shouldEvaluate) { $buffer = $this->collapseSelectors($selectors); - $parser = new Parser(__METHOD__, false); + $parser = $this->parserFactory(__METHOD__, false); if ($parser->parseSelector($buffer, $newSelectors)) { $selectors = array_map(array($this, 'evalSelector'), $newSelectors); @@ -1106,8 +1124,8 @@ protected function compileImport($rawPath, $out) */ protected function compileChild($child, $out) { + $this->sourceIndex = isset($child[Parser::SOURCE_INDEX]) ? $child[Parser::SOURCE_INDEX] : null; $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 'import': @@ -2672,7 +2690,7 @@ protected function injectVariables(array $args) return; } - $parser = new Parser(__METHOD__, false); + $parser = $this->parserFactory(__METHOD__, false); foreach ($args as $name => $strValue) { if ($name[0] === '$') { @@ -2732,7 +2750,7 @@ public function getVariables() */ public function addParsedFile($path) { - if (isset($path)) { + if (isset($path) && file_exists($path)) { $this->parsedFiles[realpath($path)] = filemtime($path); } } @@ -2864,11 +2882,10 @@ protected function importFile($path, $out) $tree = $this->importCache[$realPath]; } else { - $code = file_get_contents($path); - $parser = new Parser($path, false); - $tree = $parser->parse($code); + $code = file_get_contents($path); + $parser = $this->parserFactory($path, false); + $tree = $parser->parse($code); - $this->addParsedFile($path); $this->importCache[$realPath] = $tree; } @@ -2939,8 +2956,9 @@ public function throwError($msg) $msg = call_user_func_array('sprintf', func_get_args()); } - if ($this->sourcePos >= 0 && isset($this->sourceParser)) { - $this->sourceParser->throwParseError($msg, $this->sourcePos); + if ($this->sourcePos >= 0 && isset($this->sourceIndex)) { + $parser = $this->sourceParsers[$this->sourceIndex]; + $parser->throwParseError($msg, $this->sourcePos); } throw new \Exception($msg); @@ -2956,14 +2974,11 @@ public function throwError($msg) private function handleImportLoop($name) { for ($env = $this->env; $env; $env = $env->parent) { - $file = $env->block->sourceParser->getSourceName(); + $parser = $this->sourceParsers[$env->block->sourceIndex]; + $file = $parser->getSourceName(); if (realpath($file) === $name) { - $this->throwError( - 'An @import loop has been found: %s imports %s', - $this->env->block->sourceParser->getSourceName(), - basename($file) - ); + $this->throwError('An @import loop has been found: %s imports %s', $file, basename($file)); } } } diff --git a/src/Parser.php b/src/Parser.php index 5312a64d..b2028f80 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -20,8 +20,8 @@ */ class Parser { - const SOURCE_POSITION = -1; - const SOURCE_PARSER = -2; + const SOURCE_INDEX = -1; + const SOURCE_POSITION = -2; /** * @var array @@ -71,6 +71,7 @@ class Parser protected static $commentMultiRight = '*/'; private $sourceName; + private $sourceIndex; private $rootParser; private $charset; private $count; @@ -83,13 +84,15 @@ class Parser * Constructor * * @param string $sourceName + * @param integer $sourceIndex * @param boolean $rootParser */ - public function __construct($sourceName = null, $rootParser = true) + public function __construct($sourceName = null, $sourceIndex = null, $rootParser = true) { - $this->sourceName = $sourceName ?: '(stdin)'; - $this->rootParser = $rootParser; - $this->charset = null; + $this->sourceName = $sourceName ?: '(stdin)'; + $this->sourceIndex = $sourceIndex; + $this->rootParser = $rootParser; + $this->charset = null; if (empty(self::$operatorStr)) { self::$operatorStr = $this->makeOperatorStr(self::$operators); @@ -493,7 +496,7 @@ protected function parseChunk() $statement[self::SOURCE_POSITION] = $s; if (! $this->rootParser) { - $statement[self::SOURCE_PARSER] = $this; + $statement[self::SOURCE_INDEX] = $this->sourceIndex; } $this->charset = $statement; @@ -692,7 +695,7 @@ protected function pushBlock($selectors, $pos = 0) $b->parent = $this->env; $b->sourcePosition = $pos; - $b->sourceParser = $this; + $b->sourceIndex = $this->sourceIndex; $b->selectors = $selectors; $b->comments = array(); @@ -779,7 +782,7 @@ protected function append($statement, $pos = null) $statement[self::SOURCE_POSITION] = $pos; if (! $this->rootParser) { - $statement[self::SOURCE_PARSER] = $this; + $statement[self::SOURCE_INDEX] = $this->sourceIndex; } } From 0424688cb34e94e49098cff3159985c73f1c3b26 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 21 Oct 2015 22:19:37 -0400 Subject: [PATCH 081/484] phpcs --- src/Compiler.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Compiler.php b/src/Compiler.php index 529fdac0..68dca586 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -4585,7 +4585,9 @@ 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); + return $this->toBool( + array_key_exists($name, $this->registeredFeatures) ? $this->registeredFeatures[$name] : false + ); } protected static $libFunctionExists = array('name'); From 604bf3b91caeb14b7d1676ec146b6e8a9b31ca3b Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Thu, 22 Oct 2015 00:23:21 -0400 Subject: [PATCH 082/484] Nested formatter output optimization --- src/Formatter/Nested.php | 4 ++++ tests/outputs/short_circuit.css | 1 - tests/outputs_numbered/short_circuit.css | 1 - 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Formatter/Nested.php b/src/Formatter/Nested.php index 45866ca9..c8e3decd 100644 --- a/src/Formatter/Nested.php +++ b/src/Formatter/Nested.php @@ -114,6 +114,10 @@ protected function block($block) $this->adjustAllChildren($block); } + if (empty($block->lines) && empty($block->children)) { + return; + } + $this->depth = $block->depth; if (! empty($block->selectors)) { diff --git a/tests/outputs/short_circuit.css b/tests/outputs/short_circuit.css index 8b137891..e69de29b 100644 --- a/tests/outputs/short_circuit.css +++ b/tests/outputs/short_circuit.css @@ -1 +0,0 @@ - diff --git a/tests/outputs_numbered/short_circuit.css b/tests/outputs_numbered/short_circuit.css index 8b137891..e69de29b 100644 --- a/tests/outputs_numbered/short_circuit.css +++ b/tests/outputs_numbered/short_circuit.css @@ -1 +0,0 @@ - From 81ec424d1f8561e07039435a13506032700bff81 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Thu, 22 Oct 2015 01:07:12 -0400 Subject: [PATCH 083/484] Debug Formatter - add indent --- src/Formatter/Debug.php | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/src/Formatter/Debug.php b/src/Formatter/Debug.php index 1aae1dab..7082909d 100644 --- a/src/Formatter/Debug.php +++ b/src/Formatter/Debug.php @@ -34,19 +34,29 @@ public function __construct() $this->assignSeparator = ': '; } + /** + * {@inheritdoc} + */ + protected function indentStr() + { + return str_repeat(' ', $this->indentLevel); + } + /** * {@inheritdoc} */ protected function blockLines($block) { + $indent = $this->indentStr(); + if (empty($block->lines)) { - echo "block->lines: []\n"; + echo "{$indent}block->lines: []\n"; return; } foreach ($block->lines as $index => $line) { - echo "block->lines[{$index}]: $line\n"; + echo "{$indent}block->lines[{$index}]: $line\n"; } } @@ -55,14 +65,16 @@ protected function blockLines($block) */ protected function blockSelectors($block) { + $indent = $this->indentStr(); + if (empty($block->selectors)) { - echo "block->selectors: []\n"; + echo "{$indent}block->selectors: []\n"; return; } foreach ($block->selectors as $index => $selector) { - echo "block->selectors[{$index}]: $selector\n"; + echo "{$indent}block->selectors[{$index}]: $selector\n"; } } @@ -71,15 +83,21 @@ protected function blockSelectors($block) */ protected function blockChildren($block) { + $indent = $this->indentStr(); + if (empty($block->children)) { - echo "block->children: []\n"; + echo "{$indent}block->children: []\n"; return; } + $this->indentLevel++; + foreach ($block->children as $i => $child) { $this->block($child); } + + $this->indentLevel--; } /** @@ -87,8 +105,10 @@ protected function blockChildren($block) */ protected function block($block) { - echo "block->type: {$block->type}\n" . - "block->depth: {$block->depth}\n"; + $indent = $this->indentStr(); + + echo "{$indent}block->type: {$block->type}\n" . + "{$indent}block->depth: {$block->depth}\n"; $this->blockSelectors($block); $this->blockLines($block); From 8c10ce2e233330c4c85d1d122778ae0b286ceaaa Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Thu, 22 Oct 2015 01:44:03 -0400 Subject: [PATCH 084/484] internals: Parser::$rootParser removal --- src/Compiler.php | 15 +++++++-------- src/Parser.php | 15 +++------------ 2 files changed, 10 insertions(+), 20 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 68dca586..97667ff3 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -186,14 +186,13 @@ public function compile($code, $path = null) /** * Instantiate parser * - * @param string $path - * @param boolean $isRoot + * @param string $path * - * @return \Leafo\Parser + * @return \Leafo\ScssPhp\Parser */ - private function parserFactory($path, $isRoot = true) + private function parserFactory($path) { - $parser = new Parser($path, count($this->sourceParsers), $isRoot); + $parser = new Parser($path, count($this->sourceParsers)); $this->sourceParsers[] = $parser; $this->addParsedFile($path); @@ -736,7 +735,7 @@ protected function evalSelectors($selectors) // after evaluating interpolates, we might need a second pass if ($this->shouldEvaluate) { $buffer = $this->collapseSelectors($selectors); - $parser = $this->parserFactory(__METHOD__, false); + $parser = $this->parserFactory(__METHOD__); if ($parser->parseSelector($buffer, $newSelectors)) { $selectors = array_map(array($this, 'evalSelector'), $newSelectors); @@ -2690,7 +2689,7 @@ protected function injectVariables(array $args) return; } - $parser = $this->parserFactory(__METHOD__, false); + $parser = $this->parserFactory(__METHOD__); foreach ($args as $name => $strValue) { if ($name[0] === '$') { @@ -2883,7 +2882,7 @@ protected function importFile($path, $out) $tree = $this->importCache[$realPath]; } else { $code = file_get_contents($path); - $parser = $this->parserFactory($path, false); + $parser = $this->parserFactory($path); $tree = $parser->parse($code); $this->importCache[$realPath] = $tree; diff --git a/src/Parser.php b/src/Parser.php index b2028f80..14198a0f 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -72,7 +72,6 @@ class Parser private $sourceName; private $sourceIndex; - private $rootParser; private $charset; private $count; private $env; @@ -85,13 +84,11 @@ class Parser * * @param string $sourceName * @param integer $sourceIndex - * @param boolean $rootParser */ - public function __construct($sourceName = null, $sourceIndex = null, $rootParser = true) + public function __construct($sourceName, $sourceIndex = 0) { $this->sourceName = $sourceName ?: '(stdin)'; $this->sourceIndex = $sourceIndex; - $this->rootParser = $rootParser; $this->charset = null; if (empty(self::$operatorStr)) { @@ -494,10 +491,7 @@ protected function parseChunk() $statement = array('charset', $charset); $statement[self::SOURCE_POSITION] = $s; - - if (! $this->rootParser) { - $statement[self::SOURCE_INDEX] = $this->sourceIndex; - } + $statement[self::SOURCE_INDEX] = $this->sourceIndex; $this->charset = $statement; } @@ -780,10 +774,7 @@ protected function append($statement, $pos = null) { if ($pos !== null) { $statement[self::SOURCE_POSITION] = $pos; - - if (! $this->rootParser) { - $statement[self::SOURCE_INDEX] = $this->sourceIndex; - } + $statement[self::SOURCE_INDEX] = $this->sourceIndex; } $this->env->children[] = $statement; From 375e39768e598a31c95935d2544b229fc3d1560b Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Fri, 23 Oct 2015 12:36:44 -0400 Subject: [PATCH 085/484] Compiler: refactor compileDirective() --- src/Compiler.php | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 97667ff3..d541207e 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -555,6 +555,26 @@ protected function mediaParent($scope) return $scope; } + /** + * Compile directive + * + * @param \stdClass $block + */ + protected function compileDirective($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, array($s)); + } else { + $this->compileNestedBlock($block, array($s)); + } + } + /** * Compile at-root * @@ -1138,19 +1158,7 @@ protected function compileChild($child, $out) break; case 'directive': - list(, $directive) = $child; - - $s = '@' . $directive->name; - - if (! empty($directive->value)) { - $s .= ' ' . $this->compileValue($directive->value); - } - - if ($directive->name === 'keyframes' || substr($directive->name, -10) === '-keyframes') { - $this->compileKeyframeBlock($directive, array($s)); - } else { - $this->compileNestedBlock($directive, array($s)); - } + $this->compileDirective($child[1]); break; case 'at-root': From 1995cc145d1e776ba38e3eae1e18211a43f0ba56 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sat, 26 Sep 2015 21:16:18 -0400 Subject: [PATCH 086/484] Compiler: add spliceTree() to flesh out @at-root support; fixes #193, #354 --- src/Compiler.php | 233 ++++++++++++++++++++++++++--- tests/inputs/at_root.scss | 147 ++++++++++++++++-- tests/outputs/at_root.css | 73 ++++++++- tests/outputs_numbered/at_root.css | 118 +++++++++++++-- 4 files changed, 517 insertions(+), 54 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index d541207e..8171ff06 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -53,6 +53,11 @@ 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 */ @@ -104,6 +109,8 @@ class Compiler static public $emptyList = array('list', '', array()); static public $emptyMap = array('map', array(), array()); static public $emptyString = array('string', '"', array()); + static public $with = array('keyword', 'with'); + static public $without = array('keyword', 'without'); protected $importPaths = array(''); protected $importCache = array(); @@ -582,20 +589,9 @@ protected function compileDirective($block) */ protected function compileAtRoot($block) { - $env = $this->pushEnv($block); - - $envs = $this->compactEnv($env); - - if (isset($block->with)) { - // @todo move outside of nested directives, e.g., (without: all), (without: media supports), (with: rule) - } else { - // exclude selectors by default - $this->env->parent = $this->rootEnv; - } - - $this->scope = $this->makeOutputBlock('at-root'); - $this->scope->depth = 1; - $this->scope->parent->children[] = $this->scope; + $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) { @@ -611,14 +607,207 @@ protected function compileAtRoot($block) $block->children = array(array('block', $wrapped)); } - $this->compileChildren($block->children, $this->scope); + $this->env = $this->filterWithout($envs, $without); + $newBlock = $this->spliceTree($envs, $block, $without); - $this->scope = $this->scope->parent; + $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 \stdClass $block + * @param integer $without + * + * @return \stdClass + */ + private function spliceTree($envs, $block, $without) + { + $newBlock = null; + + foreach ($envs as $e) { + if (! isset($e->block)) { + continue; + } + + if (isset($e->block) && $e->block === $block) { + continue; + } + + if (isset($e->block->type) && $e->block->type === 'at-root') { + continue; + } + + if (($without & self::WITH_RULE) && isset($e->block->selectors)) { + continue; + } + + if (($without & self::WITH_MEDIA) && + isset($e->block->type) && $e->block->type === 'media' + ) { + continue; + } + + if (($without & self::WITH_SUPPORTS) && + isset($e->block->type) && $e->block->type === 'directive' && + isset($e->block->name) && $e->block->name === 'supports' + ) { + continue; + } + + $b = new \stdClass; + + if (isset($e->block->sourcePosition)) { + $b->sourcePosition = $e->block->sourcePosition; + } + + if (isset($e->block->sourceIndex)) { + $b->sourceIndex = $e->block->sourceIndex; + } + + $b->selectors = array(); + + if (isset($e->block->comments)) { + $b->comments = $e->block->comments; + } + + 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; + } + + if ($newBlock) { + $type = isset($newBlock->type) ? $newBlock->type : 'block'; + + $b->children = array(array($type, $newBlock)); + + $newBlock->parent = $b; + } elseif (count($block->children)) { + foreach ($block->children as $child) { + if ($child[0] === 'block') { + $child[1]->parent = $b; + } + } + + $b->children = $block->children; + } + + $b->parent = null; + + $newBlock = $b; + } + + $type = isset($newBlock->type) ? $newBlock->type : 'block'; + + return array($type, $newBlock); + } + + /** + * Compile @at-root's with: inclusion / without: exclusion into filter flags + * + * @param array $with + * + * @return integer + */ + private function compileWith($with) + { + static $mapping = array( + '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(array($with, self::$with))) { + $without = self::WITH_ALL; + + $list = $this->coerceList($this->libMapGet(array($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(array($with, self::$without))) { + $without = 0; + + $list = $this->coerceList($this->libMapGet(array($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 \stdClass + */ + private function filterWithout($envs, $without) + { + $filtered = array(); + + foreach ($envs as $e) { + if (($without & self::WITH_RULE) && isset($e->block->selectors)) { + continue; + } + + if (($without & self::WITH_MEDIA) && + isset($e->block->type) && $e->block->type === 'media' + ) { + continue; + } + + if (($without & self::WITH_SUPPORTS) && + isset($e->block->type) && $e->block->type === 'directive' && + isset($e->block->name) && $e->block->name === 'supports' + ) { + continue; + } + + $filtered[] = $e; + } + + return $this->extractEnv($filtered); + } + /** * Compile keyframe block * @@ -4135,9 +4324,7 @@ protected function libQuote($args) protected static $libPercentage = array('value'); protected function libPercentage($args) { - return array('number', - $this->coercePercent($args[0]) * 100, - '%'); + return array('number', $this->coercePercent($args[0]) * 100, '%'); } protected static $libRound = array('value'); @@ -4359,11 +4546,11 @@ protected function libMapHasKey($args) for ($i = count($map[1]) - 1; $i >= 0; $i--) { if ($key === $this->compileStringContent($this->coerceString($map[1][$i]))) { - return self::$true; + return true; } } - return self::$false; + return false; } protected static $libMapMerge = array('map-1', 'map-2'); @@ -4605,13 +4792,13 @@ protected function libFunctionExists($args) // user defined functions if ($this->has(self::$namespaces['function'] . $name)) { - return self::$true; + return true; } $name = $this->normalizeName($name); if (isset($this->userFunctions[$name])) { - return self::$true; + return true; } // built-in functions diff --git a/tests/inputs/at_root.scss b/tests/inputs/at_root.scss index aa4f6584..1f00c727 100644 --- a/tests/inputs/at_root.scss +++ b/tests/inputs/at_root.scss @@ -1,24 +1,149 @@ +.parent-inline-selector { + color: white; + @at-root .child { color: black; } +} + +.parent-block { + color: white; + @at-root { + .child1 { color: green; } + .child2 { color: blue; } + } + .step-child { color: black; } +} + .first { color: red; } body{ -.second { - color: white; + .second { + color: white; - @at-root { - .nested1 { - color: blue; - @at-root { - .nested2 { - color: yellow; + @at-root { + .nested1 { + color: blue; + @at-root { + .nested2 { + color: yellow; + } } + color: orange; } - color: orange; } + color: black; } - color: black; -} } .third { color: green; } + +@media print { + .page { + width: 8in; + @at-root (without: media) { + color: red; + } + } +} + +.my-widget { + @media (min-width: 300px) { + .inside-mq { + inside-style: mq; + } + @at-root (without: media){ + //this without:media tells the screen what to bust out of + .outside-mq { + outside-style: mq; + } + } + // using (without:all) forces all nested selectors outside of all media + // queries, hence color:blue only in the .outside-everything class in generated css + @at-root (without:all){ + .outside-class { + color: blue; + } + } + + // using the (with:media) keeps .with-only inside media query hence .with-only selector + // inside @media ()min-width:300px + @at-root (with: media) { + .with-only { + // do this ONLY inside mq + color: pink; + } + } + } +} + +// without: rule - default - outside of all css rules, but inside directives +@media screen and (max-width:320px) { + .foo { + margin: 0; + + @at-root (without: rule) { + .bar { + padding: 0; + } + } + @at-root (without: media rule) { + .baar { + padding: 0; + } + } + @at-root (without: media) { + .barr { + padding: 0; + } + } + } +} + +// with: rule - outside of all directives but preserve any css rules +@media screen and (max-width:640px) { + .foo { + @supports ( display: flex ) { + @at-root (with: rule) { + .bar { + width: 0; + } + } + @at-root (with: supports) { + .baz { + height: 0; + } + } + @at-root (with: media) { + .qux { + margin: 0; + } + } + @at-root (with: media rule) { + .quux { + padding: 0; + } + } + @at-root (with: rule) { + .quix { + padding: 0; + } + } + } + } +} + +$table-padding: 10px !default; + +@mixin table($padding: $table-padding) { + @at-root { + tbody { + padding: $padding; + } + } +} +.test{ + .test2{ + padding: 0px; + @include table; + } +} diff --git a/tests/outputs/at_root.css b/tests/outputs/at_root.css index 86edb772..5383eaab 100644 --- a/tests/outputs/at_root.css +++ b/tests/outputs/at_root.css @@ -1,15 +1,76 @@ +.parent-inline-selector { + color: white; } + .child { + color: black; } + +.parent-block { + color: white; } + .child1 { + color: green; } + .child2 { + color: blue; } + .parent-block .step-child { + color: black; } + .first { color: red; } body .second { color: white; color: black; } - - .nested1 { - color: blue; - color: orange; } - .nested2 { - color: yellow; } + .nested1 { + color: blue; + color: orange; } + .nested2 { + color: yellow; } .third { color: green; } + +@media print { + .page { + width: 8in; } } + .page { + color: red; } + +@media (min-width: 300px) { + .my-widget .inside-mq { + inside-style: mq; } } + .my-widget .outside-mq { + outside-style: mq; } + .outside-class { + color: blue; } + @media (min-width: 300px) { + .with-only { + color: pink; } } + +@media screen and (max-width: 320px) { + .foo { + margin: 0; } } + @media screen and (max-width: 320px) { + .bar { + padding: 0; } } + .baar { + padding: 0; } + .foo .barr { + padding: 0; } + + + .foo .bar { + width: 0; } + @supports ( display: flex ) { + .baz { + height: 0; } } + @media screen and (max-width: 640px) { + .qux { + margin: 0; } } + @media screen and (max-width: 640px) { + .foo .quux { + padding: 0; } } + .foo .quix { + padding: 0; } + +.test .test2 { + padding: 0px; } + tbody { + padding: 10px; } diff --git a/tests/outputs_numbered/at_root.css b/tests/outputs_numbered/at_root.css index 873218f2..2ac5a07e 100644 --- a/tests/outputs_numbered/at_root.css +++ b/tests/outputs_numbered/at_root.css @@ -1,19 +1,109 @@ /* line 1, inputs/at_root.scss */ +.parent-inline-selector { + color: white; } + /* line 3, inputs/at_root.scss */ + .child { + color: black; } +/* line 6, inputs/at_root.scss */ +.parent-block { + color: white; } + /* line 9, inputs/at_root.scss */ + .child1 { + color: green; } +/* line 10, inputs/at_root.scss */ +.child2 { + color: blue; } +/* line 12, inputs/at_root.scss */ +.parent-block .step-child { + color: black; } +/* line 15, inputs/at_root.scss */ .first { color: red; } -/* line 4, inputs/at_root.scss */ -/* line 5, inputs/at_root.scss */ - body .second { - color: white; - color: black; } - -/* line 9, inputs/at_root.scss */ -.nested1 { -color: blue; -color: orange; } -/* line 12, inputs/at_root.scss */ -.nested2 { -color: yellow; } -/* line 22, inputs/at_root.scss */ +/* line 18, inputs/at_root.scss */ +/* line 19, inputs/at_root.scss */ + +body .second { + color: white; + color: black; } + +/* line 23, inputs/at_root.scss */ + .nested1 { + color: blue; + color: orange; } + /* line 26, inputs/at_root.scss */ + .nested2 { + color: yellow; } +/* line 36, inputs/at_root.scss */ .third { color: green; } + +@media print { +/* line 41, inputs/at_root.scss */ +.page { + width: 8in; } } + +.page { + color: red; } +/* line 49, inputs/at_root.scss */ +@media (min-width: 300px) { + /* line 51, inputs/at_root.scss */ + .my-widget .inside-mq { + inside-style: mq; } } + /* line 56, inputs/at_root.scss */ + .my-widget .outside-mq { + outside-style: mq; } + /* line 63, inputs/at_root.scss */ + .outside-class { + color: blue; } + @media (min-width: 300px) { +/* line 71, inputs/at_root.scss */ +.with-only { +color: pink; } } + +@media screen and (max-width: 320px) { +/* line 81, inputs/at_root.scss */ +.foo { + margin: 0; } } + +@media screen and (max-width: 320px) { +/* line 85, inputs/at_root.scss */ +.bar { + padding: 0; } } + +/* line 90, inputs/at_root.scss */ + .baar { + padding: 0; } + +/* line 95, inputs/at_root.scss */ + .foo .barr { + padding: 0; } + +@media screen and (max-width: 640px) { +/* line 104, inputs/at_root.scss */ } + /* line 107, inputs/at_root.scss */ + .foo .bar { + width: 0; } + @supports ( display: flex ) { +/* line 112, inputs/at_root.scss */ +.baz { +height: 0; } } + @media screen and (max-width: 640px) { +/* line 117, inputs/at_root.scss */ +.qux { +margin: 0; } } + @media screen and (max-width: 640px) { + /* line 122, inputs/at_root.scss */ + .foo .quux { + padding: 0; } } + /* line 127, inputs/at_root.scss */ + .foo .quix { + padding: 0; } +/* line 144, inputs/at_root.scss */ +/* line 145, inputs/at_root.scss */ + +.test .test2 { + padding: 0px; } + +/* line 139, inputs/at_root.scss */ + tbody { + padding: 10px; } From e89391b29b5eb66e211ce4d20ccb2bb519137c08 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Fri, 23 Oct 2015 13:15:30 -0400 Subject: [PATCH 087/484] Bump version for next release --- src/Version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Version.php b/src/Version.php index fffc751b..f5769285 100644 --- a/src/Version.php +++ b/src/Version.php @@ -18,5 +18,5 @@ */ class Version { - const VERSION = 'v0.3.2'; + const VERSION = 'v0.3.3'; } From a8df1b945732bc4fadffa4f09d9d39af955113f6 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Fri, 23 Oct 2015 20:01:14 -0400 Subject: [PATCH 088/484] Compiler: index($list, $value) problem when $list is a string/keyword/interpolate (fixes #362) --- src/Compiler.php | 5 ++++- tests/inputs/builtins.scss | 1 + tests/outputs/builtins.css | 1 + tests/outputs_numbered/builtins.css | 13 +++++++------ 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 8171ff06..8f0a0d89 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -1985,6 +1985,9 @@ public function normalizeValue($value) case 'number': return $this->normalizeNumber($value); + case 'interpolate': + return array('keyword', $this->compileValue($value)); + default: return $value; } @@ -3874,7 +3877,7 @@ protected function libIndex($args) return self::$null; } - if ($list[0] === 'map') { + if ($list[0] === 'map' || $list[0] === 'string' || $list[0] === 'keyword' || $list[0] === 'interpolate') { $list = $this->coerceList($list, ' '); } diff --git a/tests/inputs/builtins.scss b/tests/inputs/builtins.scss index 1b8bffb4..07a571c7 100644 --- a/tests/inputs/builtins.scss +++ b/tests/inputs/builtins.scss @@ -119,6 +119,7 @@ index: index(1px 3px + 3px, 4+2px); $var: oo; index: index(foo bar, f#{$var}); + display: index(nest, nest); $yes: one, two, three; $no: great job; diff --git a/tests/outputs/builtins.css b/tests/outputs/builtins.css index 7778777e..811700b7 100644 --- a/tests/outputs/builtins.css +++ b/tests/outputs/builtins.css @@ -83,6 +83,7 @@ index: 2; index: 2; index: 1; + display: 1; world: one, two, three, great, job; world: one, two, three, great job; cool: one two three great job; diff --git a/tests/outputs_numbered/builtins.css b/tests/outputs_numbered/builtins.css index b62b42c6..a256ec0f 100644 --- a/tests/outputs_numbered/builtins.css +++ b/tests/outputs_numbered/builtins.css @@ -84,13 +84,14 @@ index: 2; index: 2; index: 1; + display: 1; world: one, two, three, great, job; world: one, two, three, great job; cool: one two three great job; cool: great job one two three; zip: 1px solid, 2px dashed; zip: 1px solid red, 2px dashed green; } -/* line 135, inputs/builtins.scss */ +/* line 136, inputs/builtins.scss */ #introspection { t: number; t: string; @@ -110,31 +111,31 @@ c: true; c: false; c: true; } -/* line 159, inputs/builtins.scss */ +/* line 160, inputs/builtins.scss */ #if { color: yes; color: no; color: yes; color: yes; } -/* line 166, inputs/builtins.scss */ +/* line 167, inputs/builtins.scss */ .transparent { r: 0; g: 0; b: 0; a: 0; } -/* line 173, inputs/builtins.scss */ +/* line 174, inputs/builtins.scss */ .alpha { a: 1; a: 1; a: 1; a: 0.5; a: alpha(currentColor); } -/* line 182, inputs/builtins.scss */ +/* line 183, inputs/builtins.scss */ #exists { a: true; b: true; c: false; } -/* line 189, inputs/builtins.scss */ +/* line 190, inputs/builtins.scss */ div.call-tests { a: #0a64ff; c: b; } From 3644540d4d9c6de75bf0d31b0a91e39710dc25d8 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Fri, 23 Oct 2015 22:54:19 -0400 Subject: [PATCH 089/484] Rewrite generic media directive as @media directive; fixes #364 --- src/Compiler.php | 8 +++++++- src/Parser.php | 8 ++++++-- tests/inputs/mixins.scss | 9 +++++++++ tests/outputs/mixins.css | 4 ++++ tests/outputs_numbered/mixins.css | 4 ++++ 5 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 8f0a0d89..ed2ca8a7 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -505,7 +505,13 @@ protected function compileMedia($media) { $this->pushEnv($media); - $mediaQuery = $this->compileMediaQuery($this->multiplyMedia($this->env)); + $queryList = $this->multiplyMedia($this->env); + + $mediaQuery = $queryList + ? $this->compileMediaQuery($queryList) + : (isset($media->value) + ? '@media ' . $this->compileStringContent($media->value) + : null); if (! empty($mediaQuery)) { $this->scope = $this->makeOutputBlock('media', array($mediaQuery)); diff --git a/src/Parser.php b/src/Parser.php index 14198a0f..d193849f 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -507,8 +507,12 @@ protected function parseChunk() ($this->variable($dirValue) || $this->openString('{', $dirValue) || true) && $this->literal('{') ) { - $directive = $this->pushSpecialBlock('directive', $s); - $directive->name = $dirName; + if ($dirName === 'media') { + $directive = $this->pushSpecialBlock('media', $s); + } else { + $directive = $this->pushSpecialBlock('directive', $s); + $directive->name = $dirName; + } if (isset($dirValue)) { $directive->value = $dirValue; diff --git a/tests/inputs/mixins.scss b/tests/inputs/mixins.scss index 1b6550cf..63f9007b 100644 --- a/tests/inputs/mixins.scss +++ b/tests/inputs/mixins.scss @@ -181,3 +181,12 @@ div.parameter-name-scope { 0% { color: green; } 100% { color: red; } } + +@mixin test-mixin($color: #000) { + @media screen and (min-width:0\0) { + color: $color; + } +} +.test{ + @include test-mixin(); +} diff --git a/tests/outputs/mixins.css b/tests/outputs/mixins.css index 55feee4e..e2004a07 100644 --- a/tests/outputs/mixins.css +++ b/tests/outputs/mixins.css @@ -91,3 +91,7 @@ div.parameter-name-scope { 100% { color: red; } } + +@media screen and (min-width:0\0) { + .test { + color: #000; } } diff --git a/tests/outputs_numbered/mixins.css b/tests/outputs_numbered/mixins.css index b16f853b..2df05142 100644 --- a/tests/outputs_numbered/mixins.css +++ b/tests/outputs_numbered/mixins.css @@ -104,3 +104,7 @@ div.parameter-name-scope { /* line 182, inputs/mixins.scss */ 100% { color: red; } } +/* line 190, inputs/mixins.scss */ +@media screen and (min-width:0\0) { + .test { + color: #000; } } From 0d1855985c37833731c5dd703a6d69b950e6c2ab Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Fri, 23 Oct 2015 23:23:47 -0400 Subject: [PATCH 090/484] code tweaks --- src/Compiler.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index ed2ca8a7..4fc0bade 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -505,13 +505,7 @@ protected function compileMedia($media) { $this->pushEnv($media); - $queryList = $this->multiplyMedia($this->env); - - $mediaQuery = $queryList - ? $this->compileMediaQuery($queryList) - : (isset($media->value) - ? '@media ' . $this->compileStringContent($media->value) - : null); + $mediaQuery = $this->compileMediaQuery($this->multiplyMedia($this->env)); if (! empty($mediaQuery)) { $this->scope = $this->makeOutputBlock('media', array($mediaQuery)); @@ -1196,6 +1190,10 @@ protected function compileMediaQuery($queryList) . ')'; } break; + + case 'mediaValue': + $parts[] = $this->compileValue($q[1]); + break; } } @@ -2665,7 +2663,9 @@ protected function multiplyMedia($env, $childQueries = null) return $this->multiplyMedia($env->parent, $childQueries); } - $parentQueries = $env->block->queryList; + $parentQueries = isset($env->block->queryList) + ? $env->block->queryList + : array(array(array('mediaValue', $env->block->value))); if ($childQueries === null) { $childQueries = $parentQueries; From ebb6269951efb2c0f2cd0cd2d950ff83ef34b7e2 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sat, 24 Oct 2015 16:06:58 -0400 Subject: [PATCH 091/484] fix str-slice() when end-at <= 0; fixes #366 --- src/Compiler.php | 4 ++-- tests/inputs/builtins.scss | 2 ++ tests/outputs/builtins.css | 1 + tests/outputs_numbered/builtins.css | 17 +++++++++-------- 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 4fc0bade..044fff34 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -4746,7 +4746,7 @@ protected function libStrLength($args) protected function libStrSlice($args) { if ($args[2][1] == 0) { - return self::$emptyString; + return self::$null; } $string = $this->coerceString($args[0]); @@ -4755,7 +4755,7 @@ protected function libStrSlice($args) $start = (int) $args[1][1] ?: 1; $end = (int) $args[2][1]; - $string[2] = array(substr($stringContent, $start - 1, $end < 0 ? $end : $end - $start + 1)); + $string[2] = array(substr($stringContent, $start - 1, ($end < 0 ? $end : $end - $start) + 1)); return $string; } diff --git a/tests/inputs/builtins.scss b/tests/inputs/builtins.scss index 07a571c7..3c898a30 100644 --- a/tests/inputs/builtins.scss +++ b/tests/inputs/builtins.scss @@ -80,6 +80,8 @@ color: str_insert(string, insert, 2); color: str_length(string); color: str_slice(string, 2, 4); + color: str-slice(string, 2, 0); + color: str-slice(string, 2, -2); color: to_lower_case('StRiNg'); color: to_upper_case(StRiNg); } diff --git a/tests/outputs/builtins.css b/tests/outputs/builtins.css index 811700b7..ff6f6f9c 100644 --- a/tests/outputs/builtins.css +++ b/tests/outputs/builtins.css @@ -55,6 +55,7 @@ color: sinserttring; color: 6; color: tri; + color: trin; color: 'string'; color: STRING; } diff --git a/tests/outputs_numbered/builtins.css b/tests/outputs_numbered/builtins.css index a256ec0f..d0e22471 100644 --- a/tests/outputs_numbered/builtins.css +++ b/tests/outputs_numbered/builtins.css @@ -56,9 +56,10 @@ color: sinserttring; color: 6; color: tri; + color: trin; color: 'string'; color: STRING; } -/* line 87, inputs/builtins.scss */ +/* line 89, inputs/builtins.scss */ #number { color: 250%; color: 3; @@ -69,7 +70,7 @@ width: 200%; bottom: 10px; padding: 3em 1in 96px 72pt; } -/* line 100, inputs/builtins.scss */ +/* line 102, inputs/builtins.scss */ #list { len: 3; len: 1; @@ -91,7 +92,7 @@ cool: great job one two three; zip: 1px solid, 2px dashed; zip: 1px solid red, 2px dashed green; } -/* line 136, inputs/builtins.scss */ +/* line 138, inputs/builtins.scss */ #introspection { t: number; t: string; @@ -111,31 +112,31 @@ c: true; c: false; c: true; } -/* line 160, inputs/builtins.scss */ +/* line 162, inputs/builtins.scss */ #if { color: yes; color: no; color: yes; color: yes; } -/* line 167, inputs/builtins.scss */ +/* line 169, inputs/builtins.scss */ .transparent { r: 0; g: 0; b: 0; a: 0; } -/* line 174, inputs/builtins.scss */ +/* line 176, inputs/builtins.scss */ .alpha { a: 1; a: 1; a: 1; a: 0.5; a: alpha(currentColor); } -/* line 183, inputs/builtins.scss */ +/* line 185, inputs/builtins.scss */ #exists { a: true; b: true; c: false; } -/* line 190, inputs/builtins.scss */ +/* line 192, inputs/builtins.scss */ div.call-tests { a: #0a64ff; c: b; } From 1cbf6723ae5e991a3f919b3b5ee98a49b461f855 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Thu, 29 Oct 2015 13:19:33 -0400 Subject: [PATCH 092/484] refs #260 - output literal string instead of throwing exception --- src/Compiler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compiler.php b/src/Compiler.php index 044fff34..3acc1f05 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -2067,7 +2067,7 @@ protected function opSubNumberNumber($left, $right) protected function opDivNumberNumber($left, $right) { if ($right[1] == 0) { - $this->throwError('Division by zero'); + return array('string', '', $left[1] . $left[2] . '/' . $right[1] . $right[2]); } return array('number', $left[1] / $right[1], $left[2]); From 237644f1d5f53643a5872326b534778769c755c3 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Thu, 29 Oct 2015 23:29:12 -0400 Subject: [PATCH 093/484] refs #260 update unit tests --- src/Compiler.php | 4 ++-- tests/ExceptionTest.php | 6 ------ 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 3acc1f05..09918cf8 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -1984,7 +1984,7 @@ public function normalizeValue($value) return $value; case 'string': - return array($type, '"', $this->compileStringContent($value)); + return array($type, '"', array($this->compileStringContent($value))); case 'number': return $this->normalizeNumber($value); @@ -2067,7 +2067,7 @@ protected function opSubNumberNumber($left, $right) protected function opDivNumberNumber($left, $right) { if ($right[1] == 0) { - return array('string', '', $left[1] . $left[2] . '/' . $right[1] . $right[2]); + return array('string', '', array($left[1] . $left[2] . '/' . $right[1] . $right[2])); } return array('number', $left[1] / $right[1], $left[2]); diff --git a/tests/ExceptionTest.php b/tests/ExceptionTest.php index 12ec165f..4df1bae3 100644 --- a/tests/ExceptionTest.php +++ b/tests/ExceptionTest.php @@ -71,12 +71,6 @@ public function provideScss() 'color: Can\'t divide by zero' ), array(<<<'END_OF_SCSS' -.test { left: 300px/0; } -END_OF_SCSS - , - 'Division by zero' - ), - array(<<<'END_OF_SCSS' .test { @include foo(); } From 35999817cf080c724635fd89714c914dd68fb1d6 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sat, 24 Oct 2015 18:18:12 -0400 Subject: [PATCH 094/484] Add scope tests from #367 --- tests/inputs/content.scss | 18 ++++++++++++++++++ tests/outputs/content.css | 3 +++ tests/outputs_numbered/content.css | 3 +++ 3 files changed, 24 insertions(+) diff --git a/tests/inputs/content.scss b/tests/inputs/content.scss index a56acae3..7fef7800 100644 --- a/tests/inputs/content.scss +++ b/tests/inputs/content.scss @@ -79,3 +79,21 @@ $color: white; background-image: url(/logo.gif); } } + +@mixin foo { + @content; +} + +@mixin bar { + @content; +} + +A { + @include foo { + $top: 10px; + + @include bar { + top: $top; + } + } +} diff --git a/tests/outputs/content.css b/tests/outputs/content.css index 89a1ad9e..93ed1372 100644 --- a/tests/outputs/content.css +++ b/tests/outputs/content.css @@ -30,3 +30,6 @@ * html * body #logo { background-image: url(/logo.gif); } + +A { + top: 10px; } diff --git a/tests/outputs_numbered/content.css b/tests/outputs_numbered/content.css index 7948df35..ace5a98a 100644 --- a/tests/outputs_numbered/content.css +++ b/tests/outputs_numbered/content.css @@ -39,3 +39,6 @@ /* line 78, inputs/content.scss */ * html * body #logo { background-image: url(/logo.gif); } +/* line 91, inputs/content.scss */ +A { + top: 10px; } From 09cd9403ae28df53936940865ae34b022e4726be Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Mon, 26 Oct 2015 11:23:29 -0400 Subject: [PATCH 095/484] Add scope tests from #365 --- tests/inputs/functions.scss | 19 +++++++++++++++++++ tests/outputs/functions.css | 6 ++++++ tests/outputs_numbered/functions.css | 6 ++++++ 3 files changed, 31 insertions(+) diff --git a/tests/inputs/functions.scss b/tests/inputs/functions.scss index c0204040..4886a6e5 100644 --- a/tests/inputs/functions.scss +++ b/tests/inputs/functions.scss @@ -95,3 +95,22 @@ div { p{ color: test("a", "s", "d", "f"); } + +@function test-function() { + $str: 'test-function'; + @return $str; +} + +@mixin test-mixin { + $str: 'test-mixin'; + display: $str; + $new-bp: test-function(); + display: $str; +} + +$str: 'global'; +.test{ + display: $str; + @include test-mixin; + display: $str; +} diff --git a/tests/outputs/functions.css b/tests/outputs/functions.css index a0130b62..9604accb 100644 --- a/tests/outputs/functions.css +++ b/tests/outputs/functions.css @@ -26,3 +26,9 @@ div { p { color: arglist; } + +.test { + display: 'global'; + display: 'test-mixin'; + display: 'test-mixin'; + display: 'global'; } diff --git a/tests/outputs_numbered/functions.css b/tests/outputs_numbered/functions.css index 8fdcdecc..4949511a 100644 --- a/tests/outputs_numbered/functions.css +++ b/tests/outputs_numbered/functions.css @@ -28,3 +28,9 @@ div { /* line 95, inputs/functions.scss */ p { color: arglist; } +/* line 112, inputs/functions.scss */ +.test { + display: 'global'; + display: 'test-mixin'; + display: 'test-mixin'; + display: 'global'; } From 1e1b0be6d9d546384cf9e81fcabd887d57e4ff27 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Fri, 30 Oct 2015 13:49:01 -0400 Subject: [PATCH 096/484] fixes #295, #365, #367 - multiple scope compatibility issues --- src/Compiler.php | 55 ++++++++++++++++++++++++++++++------------------ 1 file changed, 35 insertions(+), 20 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 09918cf8..ba21a31b 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -171,6 +171,7 @@ public function compile($code, $path = null) $this->sourceIndex = null; $this->env = null; $this->scope = null; + $this->storeEnv = null; $this->stderr = fopen('php://stderr', 'w'); $this->parser = $this->parserFactory($path); @@ -280,7 +281,7 @@ protected function compileRoot($rootBlock) { $this->rootBlock = $this->scope = $this->makeOutputBlock('root'); - $this->compileChildren($rootBlock->children, $this->scope); + $this->compileChildrenNoReturn($rootBlock->children, $this->scope); $this->flattenSelectors($this->scope); } @@ -534,7 +535,7 @@ protected function compileMedia($media) $media->children = array(array('block', $wrapped)); } - $this->compileChildren($media->children, $this->scope); + $this->compileChildrenNoReturn($media->children, $this->scope); $this->scope = $this->scope->parent; } @@ -828,7 +829,7 @@ protected function compileKeyframeBlock($block, $selectors) $this->scope->depth = 1; $this->scope->parent->children[] = $this->scope; - $this->compileChildren($block->children, $this->scope); + $this->compileChildrenNoReturn($block->children, $this->scope); $this->scope = $this->scope->parent; $this->env = $this->extractEnv($envs); @@ -849,7 +850,7 @@ protected function compileNestedBlock($block, $selectors) $this->scope = $this->makeOutputBlock($block->type, $selectors); $this->scope->parent->children[] = $this->scope; - $this->compileChildren($block->children, $this->scope); + $this->compileChildrenNoReturn($block->children, $this->scope); $this->scope = $this->scope->parent; @@ -908,7 +909,7 @@ protected function compileBlock($block) if (count($block->children)) { $out->selectors = $this->multiplySelectors($env); - $this->compileChildren($block->children, $out); + $this->compileChildrenNoReturn($block->children, $out); } $this->formatter->stripSemicolon($out->lines); @@ -1126,7 +1127,7 @@ protected function hasSelectorPlaceholder($selector) } /** - * Compile children + * Compile children and return result * * @param array $stms * @param \stdClass $out @@ -1144,6 +1145,25 @@ protected function compileChildren($stms, $out) } } + /** + * Compile children and throw exception if unexpected @return + * + * @param array $stms + * @param \stdClass $out + * + * @throws \Exception + */ + protected function compileChildrenNoReturn($stms, $out) + { + foreach ($stms as $stm) { + $ret = $this->compileChild($stm, $out); + + if (isset($ret)) { + $this->throwError('@return may only be used within a function'); + } + } + } + /** * Compile media query * @@ -1567,7 +1587,7 @@ protected function compileChild($child, $out) $prefixed[] = $child; } - $this->compileChildren($prefixed, $out); + $this->compileChildrenNoReturn($prefixed, $out); break; case 'include': @@ -1587,7 +1607,7 @@ protected function compileChild($child, $out) $this->env->depth--; if (isset($content)) { - $content->scope = $callingScope; + $content->scope = $this->storeEnv ? $this->storeEnv : $callingScope; $this->setRaw(self::$namespaces['special'] . 'content', $content, $this->getStoreEnv()); } @@ -1598,9 +1618,7 @@ protected function compileChild($child, $out) $this->env->marker = 'mixin'; - foreach ($mixin->children as $child) { - $this->compileChild($child, $out); - } + $this->compileChildrenNoReturn($mixin->children, $out); $this->popEnv(); break; @@ -1616,14 +1634,12 @@ protected function compileChild($child, $out) break; } + $storeEnv = $this->storeEnv; $this->storeEnv = $content->scope; - foreach ($content->children as $child) { - $this->compileChild($child, $out); - } - - $this->storeEnv = null; + $this->compileChildrenNoReturn($content->children, $out); + $this->storeEnv = $storeEnv; break; case 'debug': @@ -1917,6 +1933,8 @@ protected function reduce($value, $inExp = false) 'children' => array(), ); + $this->env->marker = 'function'; + $ret = $this->compileChildren($func->children, $tmp); $this->popEnv(); @@ -2741,10 +2759,7 @@ protected function pushEnv($block = null) */ protected function popEnv() { - $env = $this->env; $this->env = $this->env->parent; - - return $env; } /** @@ -3096,7 +3111,7 @@ protected function importFile($path, $out) $pi = pathinfo($path); array_unshift($this->importPaths, $pi['dirname']); - $this->compileChildren($tree->children, $out); + $this->compileChildrenNoReturn($tree->children, $out); array_shift($this->importPaths); } From 59112355fa985c8b420cd3320d0d12e88112969e Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Fri, 30 Oct 2015 14:31:40 -0400 Subject: [PATCH 097/484] Compiler: refactor to use getStoreEnv() method --- src/Compiler.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index ba21a31b..3958470f 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -1600,14 +1600,14 @@ protected function compileChild($child, $out) $this->throwError("Undefined mixin $name"); } - $callingScope = $this->env; + $callingScope = $this->getStoreEnv(); // push scope, apply args $this->pushEnv(); $this->env->depth--; if (isset($content)) { - $content->scope = $this->storeEnv ? $this->storeEnv : $callingScope; + $content->scope = $callingScope; $this->setRaw(self::$namespaces['special'] . 'content', $content, $this->getStoreEnv()); } From 6118d5fff7df236fdb9336a3953f23c2cac9e482 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Thu, 5 Nov 2015 10:50:26 -0500 Subject: [PATCH 098/484] refactor fncall --- src/Compiler.php | 97 +++++++++++++++++++++++++++--------------------- 1 file changed, 55 insertions(+), 42 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 3958470f..dd1d7c2d 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -1916,51 +1916,64 @@ protected function reduce($value, $inExp = false) case 'fncall': list(, $name, $argValues) = $value; - // user defined function? - $func = $this->get(self::$namespaces['function'] . $name, false); + return $this->fncall($name, $argValues); - if ($func) { - $this->pushEnv(); + default: + return $value; + } + } - // set the args - if (isset($func->args)) { - $this->applyArguments($func->args, $argValues); - } + /** + * Function caller + * + * @param string $name + * @param array $argValues + * + * @return array|null + */ + private function fncall($name, $argValues) + { + // user defined function? + $func = $this->get(self::$namespaces['function'] . $name, false); - // throw away lines and children - $tmp = (object) array( - 'lines' => array(), - 'children' => array(), - ); + if ($func) { + $this->pushEnv(); - $this->env->marker = 'function'; + // set the args + if (isset($func->args)) { + $this->applyArguments($func->args, $argValues); + } - $ret = $this->compileChildren($func->children, $tmp); + // throw away lines and children + $tmp = (object) array( + 'lines' => array(), + 'children' => array(), + ); - $this->popEnv(); + $this->env->marker = 'function'; - return ! isset($ret) ? self::$defaultValue : $ret; - } + $ret = $this->compileChildren($func->children, $tmp); - // built in function - if ($this->callBuiltin($name, $argValues, $returnValue)) { - return $returnValue; - } + $this->popEnv(); - // need to flatten the arguments into a list - $listArgs = array(); + return ! isset($ret) ? self::$defaultValue : $ret; + } - foreach ((array)$argValues as $arg) { - if (empty($arg[0])) { - $listArgs[] = $this->reduce($arg[1]); - } - } + // built in function + if ($this->callBuiltin($name, $argValues, $returnValue)) { + return $returnValue; + } - return array('function', $name, array('list', ',', $listArgs)); + // need to flatten the arguments into a list + $listArgs = array(); - default: - return $value; + foreach ((array) $argValues as $arg) { + if (empty($arg[0])) { + $listArgs[] = $this->reduce($arg[1]); + } } + + return array('function', $name, array('list', ',', $listArgs)); } /** @@ -3010,7 +3023,7 @@ public function addImportPath($path) */ public function setImportPaths($path) { - $this->importPaths = (array)$path; + $this->importPaths = (array) $path; } /** @@ -3150,7 +3163,7 @@ public function findImport($url) } } elseif (is_callable($dir)) { // check custom callback for import path - $file = call_user_func($dir, $url, $this); + $file = call_user_func($dir, $url); if ($file !== null) { return $file; @@ -3239,7 +3252,7 @@ protected function callBuiltin($name, $args, &$returnValue) } } - $returnValue = call_user_func($fn, $args, $this); + $returnValue = call_user_func($fn, $args); } else { $f = $this->getBuiltinFunction($name); @@ -3255,17 +3268,17 @@ protected function callBuiltin($name, $args, &$returnValue) } } - $returnValue = call_user_func($f, $sorted, $this); + $returnValue = call_user_func($f, $sorted); } } - if (isset($returnValue)) { - $returnValue = $this->coerceValue($returnValue); - - return true; + if (! isset($returnValue)) { + return false; } - return false; + $returnValue = $this->coerceValue($returnValue); + + return true; } /** @@ -3329,7 +3342,7 @@ protected function sortArgs($prototype, $args) $set = false; - foreach ((array)$names as $name) { + foreach ((array) $names as $name) { if (isset($keyArgs[$name])) { $finalArgs[] = $keyArgs[$name]; $set = true; From fb09e949c709f39cf6de682c864665006ed12c22 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Thu, 5 Nov 2015 10:50:38 -0500 Subject: [PATCH 099/484] add kwargs unit test --- tests/inputs/builtins.scss | 2 +- tests/outputs/builtins.css | 1 + tests/outputs_numbered/builtins.css | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/inputs/builtins.scss b/tests/inputs/builtins.scss index 3c898a30..7f54d41e 100644 --- a/tests/inputs/builtins.scss +++ b/tests/inputs/builtins.scss @@ -191,6 +191,6 @@ $a-false-value: false; $fn: nth; div.call-tests { a: call(rgb, 10, 100, 255); - //b: call(scale-color, #0a64ff, $lightness: -10%); + b: call(scale-color, #0a64ff, $lightness: -10%); c: call($fn, (a b c), 2); } diff --git a/tests/outputs/builtins.css b/tests/outputs/builtins.css index ff6f6f9c..f7bc1f12 100644 --- a/tests/outputs/builtins.css +++ b/tests/outputs/builtins.css @@ -138,4 +138,5 @@ div.call-tests { a: #0a64ff; + b: #0058ef; c: b; } diff --git a/tests/outputs_numbered/builtins.css b/tests/outputs_numbered/builtins.css index d0e22471..08cbb0be 100644 --- a/tests/outputs_numbered/builtins.css +++ b/tests/outputs_numbered/builtins.css @@ -139,4 +139,5 @@ /* line 192, inputs/builtins.scss */ div.call-tests { a: #0a64ff; + b: #0058ef; c: b; } From ee89fefa5f1954ba5795d2c8292c47021b45054e Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Thu, 5 Nov 2015 12:23:09 -0500 Subject: [PATCH 100/484] Further refactoring of fncall --- src/Compiler.php | 83 ++++++++++++++++++++++++++++++------------------ 1 file changed, 52 insertions(+), 31 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index dd1d7c2d..e05160d3 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -1933,38 +1933,17 @@ protected function reduce($value, $inExp = false) */ private function fncall($name, $argValues) { - // user defined function? - $func = $this->get(self::$namespaces['function'] . $name, false); - - if ($func) { - $this->pushEnv(); - - // set the args - if (isset($func->args)) { - $this->applyArguments($func->args, $argValues); - } - - // throw away lines and children - $tmp = (object) array( - 'lines' => array(), - 'children' => array(), - ); - - $this->env->marker = 'function'; - - $ret = $this->compileChildren($func->children, $tmp); - - $this->popEnv(); - - return ! isset($ret) ? self::$defaultValue : $ret; + // SCSS @function + if ($this->callScssFunction($name, $argValues, $returnValue)) { + return $returnValue; } - // built in function - if ($this->callBuiltin($name, $argValues, $returnValue)) { + // native PHP functions + if ($this->callNativeFunction($name, $argValues, $returnValue)) { return $returnValue; } - // need to flatten the arguments into a list + // for CSS functions, simply flatten the arguments into a list $listArgs = array(); foreach ((array) $argValues as $arg) { @@ -3069,10 +3048,11 @@ public function setLineNumberStyle($lineNumberStyle) * * @param string $name * @param callable $func + * @param array $prototype */ - public function registerFunction($name, $func) + public function registerFunction($name, $func, $prototype = null) { - $this->userFunctions[$this->normalizeName($name)] = $func; + $this->userFunctions[$this->normalizeName($name)] = array($func, $prototype); } /** @@ -3228,6 +3208,47 @@ protected function fileExists($name) 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(); + + // set the args + if (isset($func->args)) { + $this->applyArguments($func->args, $argValues); + } + + // throw away lines and children + $tmp = (object) array( + 'lines' => array(), + 'children' => array(), + ); + + $this->env->marker = 'function'; + + $ret = $this->compileChildren($func->children, $tmp); + + $this->popEnv(); + + $returnValue = ! isset($ret) ? self::$defaultValue : $ret; + + return true; + } + /** * Call built-in and registered (PHP) functions * @@ -3237,14 +3258,14 @@ protected function fileExists($name) * * @return boolean Returns true if returnValue is set; otherwise, false */ - protected function callBuiltin($name, $args, &$returnValue) + protected function callNativeFunction($name, $args, &$returnValue) { // try a lib function $name = $this->normalizeName($name); if (isset($this->userFunctions[$name])) { // see if we can find a user function - $fn = $this->userFunctions[$name]; + list($fn, $prototype) = $this->userFunctions[$name]; if ($name !== 'if' && $name !== 'call') { foreach ($args as &$val) { From d37386bb4bfdc248ea6c0a9417becdf7b63f67b1 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Thu, 5 Nov 2015 13:10:31 -0500 Subject: [PATCH 101/484] Refactoring callNativeFunction --- src/Compiler.php | 58 ++++++++++++++++-------------------------------- 1 file changed, 19 insertions(+), 39 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index e05160d3..fabc33c6 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -3265,34 +3265,24 @@ protected function callNativeFunction($name, $args, &$returnValue) if (isset($this->userFunctions[$name])) { // see if we can find a user function - list($fn, $prototype) = $this->userFunctions[$name]; - - if ($name !== 'if' && $name !== 'call') { - foreach ($args as &$val) { - $val = $this->reduce($val[1], true); - } - } - - $returnValue = call_user_func($fn, $args); + list($f, $prototype) = $this->userFunctions[$name]; + } elseif (($f = $this->getBuiltinFunction($name)) && is_callable($f)) { + $libName = $f[1]; + $prototype = isset(self::$$libName) ? self::$$libName : null; } else { - $f = $this->getBuiltinFunction($name); - - if (is_callable($f)) { - $libName = $f[1]; - - $prototype = isset(self::$$libName) ? self::$$libName : null; - $sorted = $this->sortArgs($prototype, $args); + return false; + } - if ($name !== 'if' && $name !== 'call') { - foreach ($sorted as &$val) { - $val = $this->reduce($val, true); - } - } + list($sorted, $kwargs) = $this->sortArgs($prototype, $args); - $returnValue = call_user_func($f, $sorted); + if ($name !== 'if' && $name !== 'call') { + foreach ($sorted as &$val) { + $val = $this->reduce($val, true); } } + $returnValue = call_user_func($f, $sorted, $kwargs); + if (! isset($returnValue)) { return false; } @@ -3337,6 +3327,7 @@ protected function sortArgs($prototype, $args) $keyArgs = array(); $posArgs = array(); + // separate positional and keyword arguments foreach ($args as $arg) { list($key, $value) = $arg; @@ -3350,33 +3341,22 @@ protected function sortArgs($prototype, $args) } if (! isset($prototype)) { - return $posArgs; + return array($posArgs, $keyArgs); } - $finalArgs = array(); + // copy positional args + $finalArgs = array_pad($posArgs, count($prototype), null); + // overwrite positional args with keyword args foreach ($prototype as $i => $names) { - if (isset($posArgs[$i])) { - $finalArgs[] = $posArgs[$i]; - continue; - } - - $set = false; - foreach ((array) $names as $name) { if (isset($keyArgs[$name])) { - $finalArgs[] = $keyArgs[$name]; - $set = true; - break; + $finalArgs[$i] = $keyArgs[$name]; } } - - if (! $set) { - $finalArgs[] = null; - } } - return $finalArgs; + return array($finalArgs, $keyArgs); } /** From e641a274a76a58e4e23126b90cd99f3eb88e5178 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Thu, 5 Nov 2015 13:54:14 -0500 Subject: [PATCH 102/484] Pass kwargs to built-ins --- src/Compiler.php | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index fabc33c6..baebdf35 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -3873,22 +3873,24 @@ public function toRGB($hue, $saturation, $lightness) // Built in functions //protected static $libCall = array('name', 'args...'); - protected function libCall($args) + protected function libCall($args, $kwargs) { $name = $this->compileStringContent($this->coerceString($this->reduce(array_shift($args), true))); - return $this->reduce( - array( - 'fncall', - $name, - array_map( - function ($a) { - return array(null, $a); - }, - $args - ) - ) + $args = array_map( + function ($a) { + return array(null, $a, false); + }, + $args ); + + if (count($kwargs)) { + foreach ($kwargs as $key => $value) { + $args[] = array(array('var', $key), $value, false); + } + } + + return $this->reduce(array('fncall', $name, $args)); } protected static $libIf = array('condition', 'if-true', 'if-false'); From f8baeab695bcd55c99a086bb1a4f567f14866f67 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Thu, 5 Nov 2015 14:03:42 -0500 Subject: [PATCH 103/484] fixes #143 - adds unit test for named arguments passed to user registered function --- tests/ApiTest.php | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/ApiTest.php b/tests/ApiTest.php index c4f18ba7..ca5f3a28 100644 --- a/tests/ApiTest.php +++ b/tests/ApiTest.php @@ -38,6 +38,22 @@ public function testUserFunction() ); } + public function testUserFunctionKwargs() + { + $this->scss->registerFunction( + 'divide', + function ($args, $kwargs) { + return $kwargs['dividend'][1] / $kwargs['divisor'][1]; + }, + array('dividend', 'divisor') + ); + + $this->assertEquals( + 'result: 15;', + $this->compile('result: divide($divisor: 2, $dividend: 30);') + ); + } + public function testImportMissing() { $this->assertEquals( From 22921dfb0b72b06991eff07c4a8a68117f363a31 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Thu, 5 Nov 2015 14:54:00 -0500 Subject: [PATCH 104/484] update docblocks --- src/Colors.php | 2 ++ src/Formatter.php | 11 +++++++++++ 2 files changed, 13 insertions(+) diff --git a/src/Colors.php b/src/Colors.php index dd32be21..0227fbe0 100644 --- a/src/Colors.php +++ b/src/Colors.php @@ -22,6 +22,8 @@ class Colors * CSS Colors * * @see http://www.w3.org/TR/css3-color + * + * @var array */ public static $cssColors = array( 'aliceblue' => '240,248,255', diff --git a/src/Formatter.php b/src/Formatter.php index 90f79e90..bf62c4c6 100644 --- a/src/Formatter.php +++ b/src/Formatter.php @@ -53,6 +53,11 @@ abstract class Formatter */ public $assignSeparator; + /** + * Initialize formatter + * + * @api + */ abstract public function __construct(); /** @@ -68,6 +73,8 @@ protected function indentStr() /** * Return property assignment * + * @api + * * @param string $name * @param mixed $value * @@ -81,6 +88,8 @@ public function property($name, $value) /** * Strip semi-colon appended by property(); it's a separator, not a terminator * + * @api + * * @param array $lines */ public function stripSemicolon(&$lines) @@ -172,6 +181,8 @@ protected function block($block) /** * Entry point to formatting a block * + * @api + * * @param \stdClass $block An abstract syntax tree * * @return string From eb5f43fc140b49d4209e8df7852720dcfec9efca Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Thu, 5 Nov 2015 18:47:37 -0500 Subject: [PATCH 105/484] Convert stdClass to more explicit abstractions --- scss.inc.php | 3 + src/Block.php | 72 +++++++++++++ src/Compiler.php | 196 +++++++++++++++++----------------- src/Compiler/Environment.php | 42 ++++++++ src/Formatter.php | 22 ++-- src/Formatter/Compressed.php | 5 +- src/Formatter/Crunched.php | 3 +- src/Formatter/Debug.php | 9 +- src/Formatter/Expanded.php | 3 +- src/Formatter/Nested.php | 13 +-- src/Formatter/OutputBlock.php | 50 +++++++++ src/Parser.php | 11 +- 12 files changed, 302 insertions(+), 127 deletions(-) create mode 100644 src/Block.php create mode 100644 src/Compiler/Environment.php create mode 100644 src/Formatter/OutputBlock.php diff --git a/scss.inc.php b/scss.inc.php index da641f7c..4c142973 100644 --- a/scss.inc.php +++ b/scss.inc.php @@ -5,8 +5,10 @@ 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/Formatter.php'; include_once __DIR__ . '/src/Formatter/Compact.php'; include_once __DIR__ . '/src/Formatter/Compressed.php'; @@ -14,6 +16,7 @@ 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/Parser.php'; include_once __DIR__ . '/src/Util.php'; include_once __DIR__ . '/src/Version.php'; diff --git a/src/Block.php b/src/Block.php new file mode 100644 index 00000000..9e3483ba --- /dev/null +++ b/src/Block.php @@ -0,0 +1,72 @@ + + */ +class Block +{ + const T_MEDIA = 'media'; + const T_MIXIN = 'mixin'; + const T_INCLUDE = 'include'; + const T_FUNCTION = 'function'; + const T_EACH = 'each'; + const T_WHILE = 'while'; + const T_FOR = 'for'; + const T_IF = 'if'; + const T_ELSE = 'else'; + const T_ELSEIF = 'elseif'; + const T_DIRECTIVE = 'directive'; + const T_NESTED_PROPERTY = 'nestedprop'; + const T_BLOCK = 'block'; + const T_ROOT = 'root'; + const T_NULL = null; + const T_COMMENT = 'comment'; + + /** + * @var string + */ + public $type; + + /** + * @var \Leafo\ScssPhp\Block + */ + public $parent; + + /** + * @var integer + */ + public $sourcePosition; + + /** + * @var integer + */ + public $sourceIndex; + + /** + * @var array + */ + public $selectors; + + /** + * @var array + */ + public $comments; + + /** + * @var array + */ + public $children; +} diff --git a/src/Compiler.php b/src/Compiler.php index baebdf35..110f4c7b 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -12,7 +12,10 @@ namespace Leafo\ScssPhp; use Leafo\ScssPhp\Base\Range; +use Leafo\ScssPhp\Block; use Leafo\ScssPhp\Colors; +use Leafo\ScssPhp\Compiler\Environment; +use Leafo\ScssPhp\Formatter\OutputBlock; use Leafo\ScssPhp\Parser; use Leafo\ScssPhp\Util; @@ -257,17 +260,17 @@ protected function pushExtends($target, $origin) * @param string $type * @param array $selectors * - * @return \stdClass + * @return \Leafo\ScssPhp\Formatter\OutputBlock */ protected function makeOutputBlock($type, $selectors = null) { - $out = new \stdClass; - $out->type = $type; - $out->lines = array(); - $out->children = array(); - $out->parent = $this->scope; + $out = new OutputBlock; + $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; } @@ -275,9 +278,9 @@ protected function makeOutputBlock($type, $selectors = null) /** * Compile root * - * @param \stdClass $rootBlock + * @param \Leafo\ScssPhp\Block $rootBlock */ - protected function compileRoot($rootBlock) + protected function compileRoot(Block $rootBlock) { $this->rootBlock = $this->scope = $this->makeOutputBlock('root'); @@ -288,10 +291,10 @@ protected function compileRoot($rootBlock) /** * Flatten selectors * - * @param \stdClass $block - * @parent string $parentKey + * @param \Leafo\ScssPhp\Formatter\OutputBlock $block + * @param string $parentKey */ - protected function flattenSelectors($block, $parentKey = null) + protected function flattenSelectors(OutputBlock $block, $parentKey = null) { if ($block->selectors) { $selectors = array(); @@ -500,9 +503,9 @@ protected function combineSelectorSingle($base, $other) /** * Compile media * - * @param \stdClass $media + * @param \Leafo\ScssPhp\Block $media */ - protected function compileMedia($media) + protected function compileMedia(Block $media) { $this->pushEnv($media); @@ -527,10 +530,9 @@ protected function compileMedia($media) } if ($needsWrap) { - $wrapped = (object) array( - 'selectors' => array(), - 'children' => $media->children, - ); + $wrapped = new Block; + $wrapped->selectors = array(); + $wrapped->children = $media->children; $media->children = array(array('block', $wrapped)); } @@ -546,11 +548,11 @@ protected function compileMedia($media) /** * Media parent * - * @param \stdClass $scope + * @param \Leafo\ScssPhp\Formatter\OutputBlock $scope * - * @return \stdClass + * @return \Leafo\ScssPhp\Formatter\OutputBlock */ - protected function mediaParent($scope) + protected function mediaParent(OutputBlock $scope) { while (! empty($scope->parent)) { if (! empty($scope->type) && $scope->type !== 'media') { @@ -566,9 +568,9 @@ protected function mediaParent($scope) /** * Compile directive * - * @param \stdClass $block + * @param \Leafo\ScssPhp\Block $block */ - protected function compileDirective($block) + protected function compileDirective(Block $block) { $s = '@' . $block->name; @@ -586,9 +588,9 @@ protected function compileDirective($block) /** * Compile at-root * - * @param \stdClass $block + * @param \Leafo\ScssPhp\Block $block */ - protected function compileAtRoot($block) + protected function compileAtRoot(Block $block) { $env = $this->pushEnv($block); $envs = $this->compactEnv($env); @@ -596,14 +598,13 @@ protected function compileAtRoot($block) // wrap inline selector if ($block->selector) { - $wrapped = (object) array( - 'parent' => $block, - 'sourcePosition' => $block->sourcePosition, - 'sourceIndex' => $block->sourceIndex, - 'selectors' => $block->selector, - 'comments' => array(), - 'children' => $block->children, - ); + $wrapped = new Block; + $wrapped->parent = $block; + $wrapped->sourcePosition = $block->sourcePosition; + $wrapped->sourceIndex = $block->sourceIndex; + $wrapped->selectors = $block->selector; + $wrapped->comments = array(); + $wrapped->children = $block->children; $block->children = array(array('block', $wrapped)); } @@ -625,13 +626,13 @@ protected function compileAtRoot($block) /** * Splice parse tree * - * @param array $envs - * @param \stdClass $block - * @param integer $without + * @param array $envs + * @param \Leafo\ScssPhp\Block $block + * @param integer $without * - * @return \stdClass + * @return array */ - private function spliceTree($envs, $block, $without) + private function spliceTree($envs, Block $block, $without) { $newBlock = null; @@ -665,7 +666,7 @@ private function spliceTree($envs, $block, $without) continue; } - $b = new \stdClass; + $b = new Block; if (isset($e->block->sourcePosition)) { $b->sourcePosition = $e->block->sourcePosition; @@ -779,7 +780,7 @@ private function compileWith($with) * @param array $envs * @param integer $without * - * @return \stdClass + * @return \Leafo\ScssPhp\Compiler\Environment */ private function filterWithout($envs, $without) { @@ -812,10 +813,10 @@ private function filterWithout($envs, $without) /** * Compile keyframe block * - * @param \stdClass $block - * @param array $selectors + * @param \Leafo\ScssPhp\Block $block + * @param array $selectors */ - protected function compileKeyframeBlock($block, $selectors) + protected function compileKeyframeBlock(Block $block, $selectors) { $env = $this->pushEnv($block); @@ -840,10 +841,10 @@ protected function compileKeyframeBlock($block, $selectors) /** * Compile nested block * - * @param \stdClass $block - * @param array $selectors + * @param \Leafo\ScssPhp\Block $block + * @param array $selectors */ - protected function compileNestedBlock($block, $selectors) + protected function compileNestedBlock(Block $block, $selectors) { $this->pushEnv($block); @@ -873,9 +874,9 @@ protected function compileNestedBlock($block, $selectors) * * @see Compiler::compileChild() * - * @param \stdClass $block + * @param \Leafo\ScssPhp\Block $block */ - protected function compileBlock($block) + protected function compileBlock(Block $block) { $env = $this->pushEnv($block); $env->selectors = $this->evalSelectors($block->selectors); @@ -1129,12 +1130,12 @@ protected function hasSelectorPlaceholder($selector) /** * Compile children and return result * - * @param array $stms - * @param \stdClass $out + * @param array $stms + * @param \Leafo\ScssPhp\Formatter\OutputBlock $out * * @return array */ - protected function compileChildren($stms, $out) + protected function compileChildren($stms, OutputBlock $out) { foreach ($stms as $stm) { $ret = $this->compileChild($stm, $out); @@ -1148,12 +1149,12 @@ protected function compileChildren($stms, $out) /** * Compile children and throw exception if unexpected @return * - * @param array $stms - * @param \stdClass $out + * @param array $stms + * @param \Leafo\ScssPhp\Formatter\OutputBlock $out * * @throws \Exception */ - protected function compileChildrenNoReturn($stms, $out) + protected function compileChildrenNoReturn($stms, OutputBlock $out) { foreach ($stms as $stm) { $ret = $this->compileChild($stm, $out); @@ -1349,12 +1350,12 @@ protected function compileImport($rawPath, $out) /** * Compile child; returns a value to halt execution * - * @param array $child - * @param \stdClass $out + * @param array $child + * @param \Leafo\ScssPhp\Formatter\OutputBlock $out * * @return array */ - protected function compileChild($child, $out) + protected function compileChild($child, OutputBlock $out) { $this->sourceIndex = isset($child[Parser::SOURCE_INDEX]) ? $child[Parser::SOURCE_INDEX] : null; $this->sourcePos = isset($child[Parser::SOURCE_POSITION]) ? $child[Parser::SOURCE_POSITION] : -1; @@ -2582,11 +2583,11 @@ protected function extractInterpolation($list) /** * Find the final set of selectors * - * @param \stdClass $env + * @param \Leafo\ScssPhp\Compiler\Environment $env * * @return array */ - protected function multiplySelectors($env) + protected function multiplySelectors(Environment $env) { $envs = $this->compactEnv($env); $selectors = array(); @@ -2655,12 +2656,12 @@ protected function joinSelectors($parent, $child) /** * Multiply media * - * @param \stdClass $env - * @param array $childQueries + * @param \Leafo\ScssPhp\Compiler\Environment $env + * @param array $childQueries * * @return array */ - protected function multiplyMedia($env, $childQueries = null) + protected function multiplyMedia(Environment $env = null, $childQueries = null) { if (! isset($env) || ! empty($env->block->type) && $env->block->type !== 'media' @@ -2696,11 +2697,11 @@ protected function multiplyMedia($env, $childQueries = null) /** * Convert env linked list to stack * - * @param \stdClass $env + * @param \Leafo\ScssPhp\Compiler\Environment $env * * @return array */ - private function compactEnv($env) + private function compactEnv(Environment $env) { for ($envs = array(); $env; $env = $env->parent) { $envs[] = $env; @@ -2714,7 +2715,7 @@ private function compactEnv($env) * * @param array $envs * - * @return \stdClass + * @return \Leafo\ScssPhp\Compiler\Environment */ private function extractEnv($envs) { @@ -2729,17 +2730,17 @@ private function extractEnv($envs) /** * Push environment * - * @param \stdClass $block + * @param \Leafo\ScssPhp\Block $block * - * @return \stdClass + * @return \Leafo\ScssPhp\Compiler\Environment */ - protected function pushEnv($block = null) + protected function pushEnv(Block $block = null) { - $env = new \stdClass; + $env = new Environment; $env->parent = $this->env; - $env->store = array(); - $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; @@ -2757,7 +2758,7 @@ protected function popEnv() /** * Get store environment * - * @return \stdClass + * @return \Leafo\ScssPhp\Compiler\Environment */ protected function getStoreEnv() { @@ -2767,12 +2768,12 @@ protected function getStoreEnv() /** * Set variable * - * @param string $name - * @param mixed $value - * @param boolean $shadow - * @param \stdClass $env + * @param string $name + * @param mixed $value + * @param boolean $shadow + * @param \Leafo\ScssPhp\Compiler\Environment $env */ - protected function set($name, $value, $shadow = false, $env = null) + protected function set($name, $value, $shadow = false, Environment $env = null) { $name = $this->normalizeName($name); @@ -2790,11 +2791,11 @@ protected function set($name, $value, $shadow = false, $env = null) /** * Set existing variable * - * @param string $name - * @param mixed $value - * @param \stdClass $env + * @param string $name + * @param mixed $value + * @param \Leafo\ScssPhp\Compiler\Environment $env */ - protected function setExisting($name, $value, $env) + protected function setExisting($name, $value, Environment $env) { $storeEnv = $env; @@ -2824,11 +2825,11 @@ protected function setExisting($name, $value, $env) /** * Set raw variable * - * @param string $name - * @param mixed $value - * @param \stdClass $env + * @param string $name + * @param mixed $value + * @param \Leafo\ScssPhp\Compiler\Environment $env */ - protected function setRaw($name, $value, $env) + protected function setRaw($name, $value, Environment $env) { $env->store[$name] = $value; } @@ -2838,13 +2839,13 @@ protected function setRaw($name, $value, $env) * * @api * - * @param string $name - * @param boolean $shouldThrow - * @param \stdClass $env + * @param string $name + * @param boolean $shouldThrow + * @param \Leafo\ScssPhp\Compiler\Environment $env * * @return mixed */ - public function get($name, $shouldThrow = true, $env = null) + public function get($name, $shouldThrow = true, Environment $env = null) { $name = $this->normalizeName($name); @@ -2881,12 +2882,12 @@ public function get($name, $shouldThrow = true, $env = null) /** * Has variable? * - * @param string $name - * @param \stdClass $env + * @param string $name + * @param \Leafo\ScssPhp\Compiler\Environment $env * * @return boolean */ - protected function has($name, $env = null) + protected function has($name, Environment $env = null) { return $this->get($name, false, $env) !== null; } @@ -3233,10 +3234,9 @@ protected function callScssFunction($name, $argValues, &$returnValue) } // throw away lines and children - $tmp = (object) array( - 'lines' => array(), - 'children' => array(), - ); + $tmp = new OutputBlock; + $tmp->lines = array(); + $tmp->children = array(); $this->env->marker = 'function'; @@ -3371,7 +3371,7 @@ protected function applyArguments($argDef, $argValues) { $storeEnv = $this->getStoreEnv(); - $env = new \stdClass; + $env = new Environment; $env->store = $storeEnv->store; $hasVariable = false; diff --git a/src/Compiler/Environment.php b/src/Compiler/Environment.php new file mode 100644 index 00000000..f929c74c --- /dev/null +++ b/src/Compiler/Environment.php @@ -0,0 +1,42 @@ + + */ +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/src/Formatter.php b/src/Formatter.php index bf62c4c6..88b7d867 100644 --- a/src/Formatter.php +++ b/src/Formatter.php @@ -11,6 +11,8 @@ namespace Leafo\ScssPhp; +use Leafo\ScssPhp\Formatter\OutputBlock; + /** * SCSS base formatter * @@ -99,9 +101,9 @@ public function stripSemicolon(&$lines) /** * Output lines inside a block * - * @param \stdClass $block + * @param \Leafo\ScssPhp\Formatter\OutputBlock $block */ - protected function blockLines($block) + protected function blockLines(OutputBlock $block) { $inner = $this->indentStr(); @@ -117,9 +119,9 @@ protected function blockLines($block) /** * Output block selectors * - * @param \stdClass $block + * @param \Leafo\ScssPhp\Formatter\OutputBlock $block */ - protected function blockSelectors($block) + protected function blockSelectors(OutputBlock $block) { $inner = $this->indentStr(); @@ -131,9 +133,9 @@ protected function blockSelectors($block) /** * Output block children * - * @param \stdClass $block + * @param \Leafo\ScssPhp\Formatter\OutputBlock $block */ - protected function blockChildren($block) + protected function blockChildren(OutputBlock $block) { foreach ($block->children as $child) { $this->block($child); @@ -143,9 +145,9 @@ protected function blockChildren($block) /** * Output non-empty block * - * @param \stdClass $block + * @param \Leafo\ScssPhp\Formatter\OutputBlock $block */ - protected function block($block) + protected function block(OutputBlock $block) { if (empty($block->lines) && empty($block->children)) { return; @@ -183,11 +185,11 @@ protected function block($block) * * @api * - * @param \stdClass $block An abstract syntax tree + * @param \Leafo\ScssPhp\Formatter\OutputBlock $block An abstract syntax tree * * @return string */ - public function format($block) + public function format(OutputBlock $block) { ob_start(); diff --git a/src/Formatter/Compressed.php b/src/Formatter/Compressed.php index 21def797..c51bb26b 100644 --- a/src/Formatter/Compressed.php +++ b/src/Formatter/Compressed.php @@ -12,6 +12,7 @@ namespace Leafo\ScssPhp\Formatter; use Leafo\ScssPhp\Formatter; +use Leafo\ScssPhp\Formatter\OutputBlock; /** * SCSS compressed formatter @@ -49,7 +50,7 @@ public function stripSemicolon(&$lines) /** * {@inheritdoc} */ - public function blockLines($block) + public function blockLines(OutputBlock $block) { $inner = $this->indentStr(); @@ -73,7 +74,7 @@ public function blockLines($block) /** * {@inherit} */ - public function format($block) + public function format(OutputBlock $block) { return parent::format($block); diff --git a/src/Formatter/Crunched.php b/src/Formatter/Crunched.php index 1e930b4f..29120cc9 100644 --- a/src/Formatter/Crunched.php +++ b/src/Formatter/Crunched.php @@ -12,6 +12,7 @@ namespace Leafo\ScssPhp\Formatter; use Leafo\ScssPhp\Formatter; +use Leafo\ScssPhp\Formatter\OutputBlock; /** * SCSS crunched formatter @@ -49,7 +50,7 @@ public function stripSemicolon(&$lines) /** * {@inheritdoc} */ - public function blockLines($block) + public function blockLines(OutputBlock $block) { $inner = $this->indentStr(); diff --git a/src/Formatter/Debug.php b/src/Formatter/Debug.php index 7082909d..e330b2d6 100644 --- a/src/Formatter/Debug.php +++ b/src/Formatter/Debug.php @@ -12,6 +12,7 @@ namespace Leafo\ScssPhp\Formatter; use Leafo\ScssPhp\Formatter; +use Leafo\ScssPhp\Formatter\OutputBlock; /** * SCSS debug formatter @@ -45,7 +46,7 @@ protected function indentStr() /** * {@inheritdoc} */ - protected function blockLines($block) + protected function blockLines(OutputBlock $block) { $indent = $this->indentStr(); @@ -63,7 +64,7 @@ protected function blockLines($block) /** * {@inheritdoc} */ - protected function blockSelectors($block) + protected function blockSelectors(OutputBlock $block) { $indent = $this->indentStr(); @@ -81,7 +82,7 @@ protected function blockSelectors($block) /** * {@inheritdoc} */ - protected function blockChildren($block) + protected function blockChildren(OutputBlock $block) { $indent = $this->indentStr(); @@ -103,7 +104,7 @@ protected function blockChildren($block) /** * {@inheritdoc} */ - protected function block($block) + protected function block(OutputBlock $block) { $indent = $this->indentStr(); diff --git a/src/Formatter/Expanded.php b/src/Formatter/Expanded.php index 25e6639d..477318b4 100644 --- a/src/Formatter/Expanded.php +++ b/src/Formatter/Expanded.php @@ -12,6 +12,7 @@ namespace Leafo\ScssPhp\Formatter; use Leafo\ScssPhp\Formatter; +use Leafo\ScssPhp\Formatter\OutputBlock; /** * SCSS expanded formatter @@ -45,7 +46,7 @@ protected function indentStr() /** * {@inheritdoc} */ - protected function blockLines($block) + protected function blockLines(OutputBlock $block) { $inner = $this->indentStr(); diff --git a/src/Formatter/Nested.php b/src/Formatter/Nested.php index c8e3decd..8040adab 100644 --- a/src/Formatter/Nested.php +++ b/src/Formatter/Nested.php @@ -12,6 +12,7 @@ namespace Leafo\ScssPhp\Formatter; use Leafo\ScssPhp\Formatter; +use Leafo\ScssPhp\Formatter\OutputBlock; /** * SCSS nested formatter @@ -52,7 +53,7 @@ protected function indentStr() /** * {@inheritdoc} */ - protected function blockLines($block) + protected function blockLines(OutputBlock $block) { $inner = $this->indentStr(); @@ -74,7 +75,7 @@ protected function blockLines($block) /** * {@inheritdoc} */ - protected function blockSelectors($block) + protected function blockSelectors(OutputBlock $block) { $inner = $this->indentStr(); @@ -86,7 +87,7 @@ protected function blockSelectors($block) /** * {@inheritdoc} */ - protected function blockChildren($block) + protected function blockChildren(OutputBlock $block) { foreach ($block->children as $i => $child) { $this->block($child); @@ -108,7 +109,7 @@ protected function blockChildren($block) /** * {@inheritdoc} */ - protected function block($block) + protected function block(OutputBlock $block) { if ($block->type === 'root') { $this->adjustAllChildren($block); @@ -148,9 +149,9 @@ protected function block($block) /** * Adjust the depths of all children, depth first * - * @param \stdClass $block + * @param \Leafo\ScssPhp\Formatter\OutputBlock $block */ - private function adjustAllChildren($block) + private function adjustAllChildren(OutputBlock $block) { // flatten empty nested blocks $children = array(); diff --git a/src/Formatter/OutputBlock.php b/src/Formatter/OutputBlock.php new file mode 100644 index 00000000..bfc383e2 --- /dev/null +++ b/src/Formatter/OutputBlock.php @@ -0,0 +1,50 @@ + + */ +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/src/Parser.php b/src/Parser.php index d193849f..f0e486d7 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -11,6 +11,7 @@ namespace Leafo\ScssPhp; +use Leafo\ScssPhp\Block; use Leafo\ScssPhp\Compiler; /** @@ -121,7 +122,7 @@ protected static function makeOperatorStr($operators) * * @param string $buffer; * - * @return \stdClass + * @return \Leafo\ScssPhp\Block */ public function parse($buffer) { @@ -685,11 +686,11 @@ protected function literal($what, $eatWhitespace = null) * @param array $selectors * @param integer $pos * - * @return \stdClass + * @return \Leafo\ScssPhp\Block */ protected function pushBlock($selectors, $pos = 0) { - $b = new \stdClass; + $b = new Block; $b->parent = $this->env; $b->sourcePosition = $pos; @@ -719,7 +720,7 @@ protected function pushBlock($selectors, $pos = 0) * @param string $type * @param integer $pos * - * @return \stdClass + * @return \Leafo\ScssPhp\Block */ protected function pushSpecialBlock($type, $pos) { @@ -732,7 +733,7 @@ protected function pushSpecialBlock($type, $pos) /** * Pop scope and return last block * - * @return \stdClass + * @return \Leafo\ScssPhp\Block * * @throws \Exception */ From e0f519b2c1abe1b85fe35d405987561e9e802148 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Thu, 5 Nov 2015 22:03:24 -0500 Subject: [PATCH 106/484] Add (parser) node types --- scss.inc.php | 2 + src/Block.php | 17 -- src/Compiler.php | 397 ++++++++++++++++++++++++----------------------- src/Node.php | 21 +++ src/Parser.php | 137 ++++++++-------- src/Type.php | 65 ++++++++ 6 files changed, 357 insertions(+), 282 deletions(-) create mode 100644 src/Node.php create mode 100644 src/Type.php diff --git a/scss.inc.php b/scss.inc.php index 4c142973..a8fe358d 100644 --- a/scss.inc.php +++ b/scss.inc.php @@ -17,7 +17,9 @@ 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/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'; diff --git a/src/Block.php b/src/Block.php index 9e3483ba..16f4a968 100644 --- a/src/Block.php +++ b/src/Block.php @@ -18,23 +18,6 @@ */ class Block { - const T_MEDIA = 'media'; - const T_MIXIN = 'mixin'; - const T_INCLUDE = 'include'; - const T_FUNCTION = 'function'; - const T_EACH = 'each'; - const T_WHILE = 'while'; - const T_FOR = 'for'; - const T_IF = 'if'; - const T_ELSE = 'else'; - const T_ELSEIF = 'elseif'; - const T_DIRECTIVE = 'directive'; - const T_NESTED_PROPERTY = 'nestedprop'; - const T_BLOCK = 'block'; - const T_ROOT = 'root'; - const T_NULL = null; - const T_COMMENT = 'comment'; - /** * @var string */ diff --git a/src/Compiler.php b/src/Compiler.php index 110f4c7b..8991d05f 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -16,6 +16,7 @@ use Leafo\ScssPhp\Colors; use Leafo\ScssPhp\Compiler\Environment; use Leafo\ScssPhp\Formatter\OutputBlock; +use Leafo\ScssPhp\Type; use Leafo\ScssPhp\Parser; use Leafo\ScssPhp\Util; @@ -104,16 +105,16 @@ class Compiler ), ); - 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()); - static public $with = array('keyword', 'with'); - static public $without = array('keyword', 'without'); + static public $true = array(Type::T_KEYWORD, 'true'); + static public $false = array(Type::T_KEYWORD, 'false'); + static public $null = array(Type::T_NULL); + static public $defaultValue = array(Type::T_KEYWORD, ''); + static public $selfSelector = array(Type::T_SELF); + static public $emptyList = array(Type::T_LIST, '', array()); + static public $emptyMap = array(Type::T_MAP, array(), array()); + static public $emptyString = array(Type::T_STRING, '"', array()); + static public $with = array(Type::T_KEYWORD, 'with'); + static public $without = array(Type::T_KEYWORD, 'without'); protected $importPaths = array(''); protected $importCache = array(); @@ -282,7 +283,7 @@ protected function makeOutputBlock($type, $selectors = null) */ protected function compileRoot(Block $rootBlock) { - $this->rootBlock = $this->scope = $this->makeOutputBlock('root'); + $this->rootBlock = $this->scope = $this->makeOutputBlock(Type::T_ROOT); $this->compileChildrenNoReturn($rootBlock->children, $this->scope); $this->flattenSelectors($this->scope); @@ -314,7 +315,9 @@ protected function flattenSelectors(OutputBlock $block, $parentKey = null) array_walk($selectors, function (&$value) { $value = json_encode($value); }); + $selectors = array_unique($selectors); + array_walk($selectors, function (&$value) { $value = json_decode($value); }); @@ -512,7 +515,7 @@ protected function compileMedia(Block $media) $mediaQuery = $this->compileMediaQuery($this->multiplyMedia($this->env)); if (! empty($mediaQuery)) { - $this->scope = $this->makeOutputBlock('media', array($mediaQuery)); + $this->scope = $this->makeOutputBlock(Type::T_MEDIA, array($mediaQuery)); $parentScope = $this->mediaParent($this->scope); $parentScope->children[] = $this->scope; @@ -523,7 +526,7 @@ protected function compileMedia(Block $media) foreach ($media->children as $child) { $type = $child[0]; - if ($type !== 'block' && $type !== 'media' && $type !== 'directive' && $type !== 'import') { + if ($type !== Type::T_BLOCK && $type !== Type::T_MEDIA && $type !== Type::T_DIRECTIVE && $type !== Type::T_IMPORT) { $needsWrap = true; break; } @@ -534,7 +537,7 @@ protected function compileMedia(Block $media) $wrapped->selectors = array(); $wrapped->children = $media->children; - $media->children = array(array('block', $wrapped)); + $media->children = array(array(Type::T_BLOCK, $wrapped)); } $this->compileChildrenNoReturn($media->children, $this->scope); @@ -555,7 +558,7 @@ protected function compileMedia(Block $media) protected function mediaParent(OutputBlock $scope) { while (! empty($scope->parent)) { - if (! empty($scope->type) && $scope->type !== 'media') { + if (! empty($scope->type) && $scope->type !== Type::T_MEDIA) { break; } @@ -606,7 +609,7 @@ protected function compileAtRoot(Block $block) $wrapped->comments = array(); $wrapped->children = $block->children; - $block->children = array(array('block', $wrapped)); + $block->children = array(array(Type::T_BLOCK, $wrapped)); } $this->env = $this->filterWithout($envs, $without); @@ -645,7 +648,7 @@ private function spliceTree($envs, Block $block, $without) continue; } - if (isset($e->block->type) && $e->block->type === 'at-root') { + if (isset($e->block->type) && $e->block->type === Type::T_AT_ROOT) { continue; } @@ -654,13 +657,13 @@ private function spliceTree($envs, Block $block, $without) } if (($without & self::WITH_MEDIA) && - isset($e->block->type) && $e->block->type === 'media' + isset($e->block->type) && $e->block->type === Type::T_MEDIA ) { continue; } if (($without & self::WITH_SUPPORTS) && - isset($e->block->type) && $e->block->type === 'directive' && + isset($e->block->type) && $e->block->type === Type::T_DIRECTIVE && isset($e->block->name) && $e->block->name === 'supports' ) { continue; @@ -699,14 +702,14 @@ private function spliceTree($envs, Block $block, $without) } if ($newBlock) { - $type = isset($newBlock->type) ? $newBlock->type : 'block'; + $type = isset($newBlock->type) ? $newBlock->type : Type::T_BLOCK; $b->children = array(array($type, $newBlock)); $newBlock->parent = $b; } elseif (count($block->children)) { foreach ($block->children as $child) { - if ($child[0] === 'block') { + if ($child[0] === Type::T_BLOCK) { $child[1]->parent = $b; } } @@ -719,7 +722,7 @@ private function spliceTree($envs, Block $block, $without) $newBlock = $b; } - $type = isset($newBlock->type) ? $newBlock->type : 'block'; + $type = isset($newBlock->type) ? $newBlock->type : Type::T_BLOCK; return array($type, $newBlock); } @@ -792,13 +795,13 @@ private function filterWithout($envs, $without) } if (($without & self::WITH_MEDIA) && - isset($e->block->type) && $e->block->type === 'media' + isset($e->block->type) && $e->block->type === Type::T_MEDIA ) { continue; } if (($without & self::WITH_SUPPORTS) && - isset($e->block->type) && $e->block->type === 'directive' && + isset($e->block->type) && $e->block->type === Type::T_DIRECTIVE && isset($e->block->name) && $e->block->name === 'supports' ) { continue; @@ -884,7 +887,7 @@ protected function compileBlock(Block $block) $out = $this->makeOutputBlock(null); if (isset($this->lineNumberStyle) && count($env->selectors) && count($block->children)) { - $annotation = $this->makeOutputBlock('comment'); + $annotation = $this->makeOutputBlock(Type::T_COMMENT); $annotation->depth = 0; $parser = $this->sourceParsers[$block->sourceIndex]; @@ -925,7 +928,7 @@ protected function compileBlock(Block $block) */ protected function compileComment($block) { - $out = $this->makeOutputBlock('comment'); + $out = $this->makeOutputBlock(Type::T_COMMENT); $out->lines[] = $block[1]; $this->scope->children[] = $out; } @@ -978,7 +981,7 @@ protected function evalSelector($selector) protected function evalSelectorPart($part) { foreach ($part as &$p) { - if (is_array($p) && ($p[0] === 'interpolate' || $p[0] === 'string')) { + if (is_array($p) && ($p[0] === Type::T_INTERPOLATE || $p[0] === Type::T_STRING)) { $p = $this->compileValue($p); // force re-evaluation @@ -1090,7 +1093,7 @@ protected function compileSelectorPart($piece) } switch ($p[0]) { - case 'self': + case Type::T_SELF: $p = '&'; break; @@ -1183,7 +1186,7 @@ protected function compileMediaQuery($queryList) foreach ($query as $q) { switch ($q[0]) { - case 'mediaType': + case Type::T_MEDIA_TYPE: if ($type) { $type = $this->mergeMediaTypes( $type, @@ -1198,7 +1201,7 @@ protected function compileMediaQuery($queryList) } break; - case 'mediaExp': + case Type::T_MEDIA_EXPRESSION: if (isset($q[2])) { $parts[] = '(' . $this->compileValue($q[1]) @@ -1212,7 +1215,7 @@ protected function compileMediaQuery($queryList) } break; - case 'mediaValue': + case Type::T_MEDIA_VALUE: $parts[] = $this->compileValue($q[1]); break; } @@ -1275,24 +1278,24 @@ protected function mergeMediaTypes($type1, $type2) $t2 = strtolower($type2[0]); } - if (($m1 === 'not') ^ ($m2 === 'not')) { + if (($m1 === Type::T_NOT) ^ ($m2 === Type::T_NOT)) { if ($t1 === $t2) { return null; } return array( - $m1 === 'not' ? $m2 : $m1, - $m1 === 'not' ? $t2 : $t1, + $m1 === Type::T_NOT ? $m2 : $m1, + $m1 === Type::T_NOT ? $t2 : $t1, ); } - if ($m1 === 'not' && $m2 === 'not') { + if ($m1 === Type::T_NOT && $m2 === Type::T_NOT) { // CSS has no way of representing "neither screen nor print" if ($t1 !== $t2) { return null; } - return array('not', $t1); + return array(Type::T_NOT, $t1); } if ($t1 !== $t2) { @@ -1313,7 +1316,7 @@ protected function mergeMediaTypes($type1, $type2) */ protected function compileImport($rawPath, $out) { - if ($rawPath[0] === 'string') { + if ($rawPath[0] === Type::T_STRING) { $path = $this->compileStringContent($rawPath); if ($path = $this->findImport($path)) { @@ -1325,14 +1328,14 @@ protected function compileImport($rawPath, $out) return false; } - if ($rawPath[0] === 'list') { + if ($rawPath[0] === Type::T_LIST) { // handle a list of strings if (count($rawPath[2]) === 0) { return false; } foreach ($rawPath[2] as $path) { - if ($path[0] !== 'string') { + if ($path[0] !== Type::T_STRING) { return false; } } @@ -1361,7 +1364,7 @@ protected function compileChild($child, OutputBlock $out) $this->sourcePos = isset($child[Parser::SOURCE_POSITION]) ? $child[Parser::SOURCE_POSITION] : -1; switch ($child[0]) { - case 'import': + case Type::T_IMPORT: list(, $rawPath) = $child; $rawPath = $this->reduce($rawPath); @@ -1371,23 +1374,23 @@ protected function compileChild($child, OutputBlock $out) } break; - case 'directive': + case Type::T_DIRECTIVE: $this->compileDirective($child[1]); break; - case 'at-root': + case Type::T_AT_ROOT: $this->compileAtRoot($child[1]); break; - case 'media': + case Type::T_MEDIA: $this->compileMedia($child[1]); break; - case 'block': + case Type::T_BLOCK: $this->compileBlock($child[1]); break; - case 'charset': + case Type::T_CHARSET: if (! $this->charsetSeen) { $this->charsetSeen = true; @@ -1395,10 +1398,10 @@ protected function compileChild($child, OutputBlock $out) } break; - case 'assign': + case Type::T_ASSIGN: list(, $name, $value) = $child; - if ($name[0] === 'var') { + if ($name[0] === Type::T_VARIABLE) { $flag = isset($child[3]) ? $child[3] : null; $isDefault = $flag === '!default'; $isGlobal = $flag === '!global'; @@ -1422,11 +1425,11 @@ protected function compileChild($child, OutputBlock $out) // handle shorthand syntax: size / line-height if ($compiledName === 'font') { - if ($value[0] === 'exp' && $value[1] === '/') { + if ($value[0] === Type::T_EXPRESSION && $value[1] === '/') { $value = $this->expToString($value); - } elseif ($value[0] === 'list') { + } elseif ($value[0] === Type::T_LIST) { foreach ($value[2] as &$item) { - if ($item[0] === 'exp' && $item[1] === '/') { + if ($item[0] === Type::T_EXPRESSION && $item[1] === '/') { $item = $this->expToString($item); } } @@ -1435,10 +1438,10 @@ protected function compileChild($child, OutputBlock $out) // if the value reduces to null from something else then // the property should be discarded - if ($value[0] !== 'null') { + if ($value[0] !== Type::T_NULL) { $value = $this->reduce($value); - if ($value[0] === 'null') { + if ($value[0] === Type::T_NULL) { break; } } @@ -1451,8 +1454,8 @@ protected function compileChild($child, OutputBlock $out) ); break; - case 'comment': - if ($out->type === 'root') { + case Type::T_COMMENT: + if ($out->type === Type::T_ROOT) { $this->compileComment($child); break; } @@ -1460,14 +1463,14 @@ protected function compileChild($child, OutputBlock $out) $out->lines[] = $child[1]; break; - case 'mixin': - case 'function': + case Type::T_MIXIN: + case Type::T_FUNCTION: list(, $block) = $child; $this->set(self::$namespaces[$block->type] . $block->name, $block); break; - case 'extend': + case Type::T_EXTEND: list(, $selectors) = $child; foreach ($selectors as $sel) { @@ -1482,7 +1485,7 @@ protected function compileChild($child, OutputBlock $out) } break; - case 'if': + case Type::T_IF: list(, $if) = $child; if ($this->isTruthy($this->reduce($if->cond, true))) { @@ -1490,18 +1493,18 @@ protected function compileChild($child, OutputBlock $out) } foreach ($if->cases as $case) { - if ($case->type === 'else' || - $case->type === 'elseif' && $this->isTruthy($this->reduce($case->cond)) + if ($case->type === Type::T_ELSE || + $case->type === Type::T_ELSEIF && $this->isTruthy($this->reduce($case->cond)) ) { return $this->compileChildren($case->children, $out); } } break; - case 'return': + case Type::T_RETURN: return $this->reduce($child[1], true); - case 'each': + case Type::T_EACH: list(, $each) = $child; $list = $this->coerceList($this->reduce($each->list)); @@ -1531,7 +1534,7 @@ protected function compileChild($child, OutputBlock $out) $this->popEnv(); break; - case 'while': + case Type::T_WHILE: list(, $while) = $child; while ($this->isTruthy($this->reduce($while->cond, true))) { @@ -1543,7 +1546,7 @@ protected function compileChild($child, OutputBlock $out) } break; - case 'for': + case Type::T_FOR: list(, $for) = $child; $start = $this->reduce($for->start, true); @@ -1559,7 +1562,7 @@ protected function compileChild($child, OutputBlock $out) break; } - $this->set($for->var, array('number', $start, '')); + $this->set($for->var, array(Type::T_NUMBER, $start, '')); $start += $d; $ret = $this->compileChildren($for->children, $out); @@ -1570,18 +1573,18 @@ protected function compileChild($child, OutputBlock $out) } break; - case 'nestedprop': + case Type::T_NESTED_PROPERTY: list(, $prop) = $child; $prefixed = array(); $prefix = $this->compileValue($prop->prefix) . '-'; foreach ($prop->children as $child) { - if ($child[0] === 'assign') { + if ($child[0] === Type::T_ASSIGN) { array_unshift($child[1][2], $prefix); } - if ($child[0] === 'nestedprop') { + if ($child[0] === Type::T_NESTED_PROPERTY) { array_unshift($child[1]->prefix[2], $prefix); } @@ -1591,7 +1594,7 @@ protected function compileChild($child, OutputBlock $out) $this->compileChildrenNoReturn($prefixed, $out); break; - case 'include': + case Type::T_INCLUDE: // including a mixin list(, $name, $argValues, $content) = $child; @@ -1624,7 +1627,7 @@ protected function compileChild($child, OutputBlock $out) $this->popEnv(); break; - case 'mixin_content': + case Type::T_MIXIN_CONTENT: $content = $this->get(self::$namespaces['special'] . 'content', false, $this->getStoreEnv()); if (! $content) { @@ -1643,7 +1646,7 @@ protected function compileChild($child, OutputBlock $out) $this->storeEnv = $storeEnv; break; - case 'debug': + case Type::T_DEBUG: list(, $value) = $child; $line = $this->parser->getLineNo($this->sourcePos); @@ -1651,7 +1654,7 @@ protected function compileChild($child, OutputBlock $out) fwrite($this->stderr, "Line $line DEBUG: $value\n"); break; - case 'warn': + case Type::T_WARN: list(, $value) = $child; $line = $this->parser->getLineNo($this->sourcePos); @@ -1659,7 +1662,7 @@ protected function compileChild($child, OutputBlock $out) echo "Line $line WARN: $value\n"; break; - case 'error': + case Type::T_ERROR: list(, $value) = $child; $line = $this->parser->getLineNo($this->sourcePos); @@ -1697,7 +1700,7 @@ protected function expToString($exp) $content[] = $this->reduce($right); - return array('string', '', $content); + return array(Type::T_STRING, '', $content); } /** @@ -1722,14 +1725,14 @@ protected function isTruthy($value) protected function shouldEval($value) { switch ($value[0]) { - case 'exp': + case Type::T_EXPRESSION: if ($value[1] === '/') { return $this->shouldEval($value[2], $value[3]); } // fall-thru - case 'var': - case 'fncall': + case Type::T_VARIABLE: + case Type::T_FUNCTION_CALL: return true; } @@ -1749,7 +1752,7 @@ protected function reduce($value, $inExp = false) list($type) = $value; switch ($type) { - case 'exp': + case Type::T_EXPRESSION: list(, $op, $left, $right, $inParens) = $value; $opName = isset(self::$operatorNames[$op]) ? self::$operatorNames[$op] : $op; @@ -1793,7 +1796,7 @@ protected function reduce($value, $inExp = false) $unitChange = false; if (! isset($genOp) && - $left[0] === 'number' && $right[0] === 'number' + $left[0] === Type::T_NUMBER && $right[0] === Type::T_NUMBER ) { if ($opName === 'mod' && $right[2] !== '') { $this->throwError("Cannot modulo by a number with units: $right[1]$right[2]."); @@ -1835,7 +1838,7 @@ protected function reduce($value, $inExp = false) } if (isset($out)) { - if ($unitChange && $out[0] === 'number') { + if ($unitChange && $out[0] === Type::T_NUMBER) { $out = $this->coerceUnit($out, $targetUnit); } @@ -1845,13 +1848,13 @@ protected function reduce($value, $inExp = false) return $this->expToString($value); - case 'unary': + case Type::T_UNARY: list(, $op, $exp, $inParens) = $value; $inExp = $inExp || $this->shouldEval($exp); $exp = $this->reduce($exp); - if ($exp[0] === 'number') { + if ($exp[0] === Type::T_NUMBER) { switch ($op) { case '+': return $exp; @@ -1875,21 +1878,21 @@ protected function reduce($value, $inExp = false) $op = $op . ' '; } - return array('string', '', array($op, $exp)); + return array(Type::T_STRING, '', array($op, $exp)); - case 'var': + case Type::T_VARIABLE: list(, $name) = $value; return $this->reduce($this->get($name)); - case 'list': + case Type::T_LIST: foreach ($value[2] as &$item) { $item = $this->reduce($item); } return $value; - case 'map': + case Type::T_MAP: foreach ($value[1] as &$item) { $item = $this->reduce($item); } @@ -1900,7 +1903,7 @@ protected function reduce($value, $inExp = false) return $value; - case 'string': + case Type::T_STRING: foreach ($value[2] as &$item) { if (is_array($item)) { $item = $this->reduce($item); @@ -1909,12 +1912,12 @@ protected function reduce($value, $inExp = false) return $value; - case 'interpolate': + case Type::T_INTERPOLATE: $value[1] = $this->reduce($value[1]); return $value; - case 'fncall': + case Type::T_FUNCTION_CALL: list(, $name, $argValues) = $value; return $this->fncall($name, $argValues); @@ -1953,7 +1956,7 @@ private function fncall($name, $argValues) } } - return array('function', $name, array('list', ',', $listArgs)); + return array(Type::T_FUNCTION, $name, array(Type::T_LIST, ',', $listArgs)); } /** @@ -1981,11 +1984,11 @@ public function normalizeValue($value) list($type) = $value; switch ($type) { - case 'list': + case Type::T_LIST: $value = $this->extractInterpolation($value); - if ($value[0] !== 'list') { - return array('keyword', $this->compileValue($value)); + if ($value[0] !== Type::T_LIST) { + return array(Type::T_KEYWORD, $this->compileValue($value)); } foreach ($value[2] as $key => $item) { @@ -1994,14 +1997,14 @@ public function normalizeValue($value) return $value; - case 'string': + case Type::T_STRING: return array($type, '"', array($this->compileStringContent($value))); - case 'number': + case Type::T_NUMBER: return $this->normalizeNumber($value); - case 'interpolate': - return array('keyword', $this->compileValue($value)); + case Type::T_INTERPOLATE: + return array(Type::T_KEYWORD, $this->compileValue($value)); default: return $value; @@ -2022,7 +2025,7 @@ protected function normalizeNumber($number) if (isset(self::$unitTable['in'][$unit])) { $conv = self::$unitTable['in'][$unit]; - return array('number', $value / $conv, 'in'); + return array(Type::T_NUMBER, $value / $conv, 'in'); } return $number; @@ -2038,7 +2041,7 @@ protected function normalizeNumber($number) */ protected function opAddNumberNumber($left, $right) { - return array('number', $left[1] + $right[1], $left[2]); + return array(Type::T_NUMBER, $left[1] + $right[1], $left[2]); } /** @@ -2051,7 +2054,7 @@ protected function opAddNumberNumber($left, $right) */ protected function opMulNumberNumber($left, $right) { - return array('number', $left[1] * $right[1], $left[2]); + return array(Type::T_NUMBER, $left[1] * $right[1], $left[2]); } /** @@ -2064,7 +2067,7 @@ protected function opMulNumberNumber($left, $right) */ protected function opSubNumberNumber($left, $right) { - return array('number', $left[1] - $right[1], $left[2]); + return array(Type::T_NUMBER, $left[1] - $right[1], $left[2]); } /** @@ -2078,10 +2081,10 @@ protected function opSubNumberNumber($left, $right) protected function opDivNumberNumber($left, $right) { if ($right[1] == 0) { - return array('string', '', array($left[1] . $left[2] . '/' . $right[1] . $right[2])); + return array(Type::T_STRING, '', array($left[1] . $left[2] . '/' . $right[1] . $right[2])); } - return array('number', $left[1] / $right[1], $left[2]); + return array(Type::T_NUMBER, $left[1] / $right[1], $left[2]); } /** @@ -2094,7 +2097,7 @@ protected function opDivNumberNumber($left, $right) */ protected function opModNumberNumber($left, $right) { - return array('number', $left[1] % $right[1], $left[2]); + return array(Type::T_NUMBER, $left[1] % $right[1], $left[2]); } /** @@ -2108,7 +2111,7 @@ protected function opModNumberNumber($left, $right) protected function opAdd($left, $right) { if ($strLeft = $this->coerceString($left)) { - if ($right[0] === 'string') { + if ($right[0] === Type::T_STRING) { $right[1] = ''; } @@ -2118,7 +2121,7 @@ protected function opAdd($left, $right) } if ($strRight = $this->coerceString($right)) { - if ($left[0] === 'string') { + if ($left[0] === Type::T_STRING) { $left[1] = ''; } @@ -2183,7 +2186,7 @@ protected function opOr($left, $right, $shouldEval) */ protected function opColorColor($op, $left, $right) { - $out = array('color'); + $out = array(Type::T_COLOR); foreach (range(1, 3) as $i) { $lval = isset($left[$i]) ? $left[$i] : 0; @@ -2250,7 +2253,7 @@ protected function opColorNumber($op, $left, $right) return $this->opColorColor( $op, $left, - array('color', $value, $value, $value) + array(Type::T_COLOR, $value, $value, $value) ); } @@ -2269,7 +2272,7 @@ protected function opNumberColor($op, $left, $right) return $this->opColorColor( $op, - array('color', $value, $value, $value), + array(Type::T_COLOR, $value, $value, $value), $right ); } @@ -2406,10 +2409,10 @@ public function compileValue($value) list($type) = $value; switch ($type) { - case 'keyword': + case Type::T_KEYWORD: return $value[1]; - case 'color': + case Type::T_COLOR: // [1] - red component (either number for a %) // [2] - green component // [3] - blue component @@ -2433,21 +2436,21 @@ public function compileValue($value) return $h; - case 'number': + case Type::T_NUMBER: return round($value[1], $this->numberPrecision) . $value[2]; - case 'string': + case Type::T_STRING: return $value[1] . $this->compileStringContent($value) . $value[1]; - case 'function': + case Type::T_FUNCTION: $args = ! empty($value[2]) ? $this->compileValue($value[2]) : ''; return "$value[1]($args)"; - case 'list': + case Type::T_LIST: $value = $this->extractInterpolation($value); - if ($value[0] !== 'list') { + if ($value[0] !== Type::T_LIST) { return $this->compileValue($value); } @@ -2460,7 +2463,7 @@ public function compileValue($value) $filtered = array(); foreach ($items as $item) { - if ($item[0] === 'null') { + if ($item[0] === Type::T_NULL) { continue; } @@ -2469,7 +2472,7 @@ public function compileValue($value) return implode("$delim", $filtered); - case 'map': + case Type::T_MAP: $keys = $value[1]; $values = $value[2]; $filtered = array(); @@ -2484,7 +2487,7 @@ public function compileValue($value) return '(' . implode(', ', $filtered) . ')'; - case 'interpolated': + case Type::T_INTERPOLATED: // node created by extractInterpolation list(, $interpolate, $left, $right) = $value; list(,, $whiteLeft, $whiteRight) = $interpolate; @@ -2497,7 +2500,7 @@ public function compileValue($value) return $left . $this->compileValue($interpolate) . $right; - case 'interpolate': + case Type::T_INTERPOLATE: // raw parse node list(, $exp) = $value; @@ -2505,17 +2508,17 @@ public function compileValue($value) $reduced = $this->reduce($exp); switch ($reduced[0]) { - case 'string': - $reduced = array('keyword', $this->compileStringContent($reduced)); + case Type::T_STRING: + $reduced = array(Type::T_KEYWORD, $this->compileStringContent($reduced)); break; - case 'null': - $reduced = array('keyword', ''); + case Type::T_NULL: + $reduced = array(Type::T_KEYWORD, ''); } return $this->compileValue($reduced); - case 'null': + case Type::T_NULL: return 'null'; default: @@ -2569,11 +2572,11 @@ protected function extractInterpolation($list) $items = $list[2]; foreach ($items as $i => $item) { - if ($item[0] === 'interpolate') { - $before = array('list', $list[1], array_slice($items, 0, $i)); - $after = array('list', $list[1], array_slice($items, $i + 1)); + if ($item[0] === Type::T_INTERPOLATE) { + $before = array(Type::T_LIST, $list[1], array_slice($items, 0, $i)); + $after = array(Type::T_LIST, $list[1], array_slice($items, $i + 1)); - return array('interpolated', $item, $before, $after); + return array(Type::T_INTERPOLATED, $item, $before, $after); } } @@ -2664,7 +2667,7 @@ protected function joinSelectors($parent, $child) protected function multiplyMedia(Environment $env = null, $childQueries = null) { if (! isset($env) || - ! empty($env->block->type) && $env->block->type !== 'media' + ! empty($env->block->type) && $env->block->type !== Type::T_MEDIA ) { return $childQueries; } @@ -2676,7 +2679,7 @@ protected function multiplyMedia(Environment $env = null, $childQueries = null) $parentQueries = isset($env->block->queryList) ? $env->block->queryList - : array(array(array('mediaValue', $env->block->value))); + : array(array(array(Type::T_MEDIA_VALUE, $env->block->value))); if ($childQueries === null) { $childQueries = $parentQueries; @@ -3407,7 +3410,7 @@ protected function applyArguments($argDef, $argValues) } elseif ($arg[2] === true) { $val = $this->reduce($arg[1], true); - if ($val[0] === 'list') { + if ($val[0] === Type::T_LIST) { foreach ($val[2] as $name => $item) { if (! is_numeric($name)) { $keywordArgs[$name] = $item; @@ -3415,7 +3418,7 @@ protected function applyArguments($argDef, $argValues) $remaining[] = $item; } } - } elseif ($val[0] === 'map') { + } elseif ($val[0] === Type::T_MAP) { foreach ($val[1] as $i => $name) { $name = $this->compileStringContent($this->coerceString($name)); $item = $val[2][$i]; @@ -3438,7 +3441,7 @@ protected function applyArguments($argDef, $argValues) list($i, $name, $default, $isVariable) = $arg; if ($isVariable) { - $val = array('list', ',', array(), $isVariable); + $val = array(Type::T_LIST, ',', array(), $isVariable); for ($count = count($remaining); $i < $count; $i++) { $val[2][] = $remaining[$i]; @@ -3495,14 +3498,14 @@ private function coerceValue($value) } if (is_numeric($value)) { - return array('number', $value, ''); + return array(Type::T_NUMBER, $value, ''); } if ($value === '') { return self::$emptyString; } - return array('keyword', $value); + return array(Type::T_KEYWORD, $value); } /** @@ -3521,7 +3524,7 @@ protected function coerceUnit($number, $unit) $value = $value * self::$unitTable[$baseUnit][$unit]; } - return array('number', $value, $unit); + return array(Type::T_NUMBER, $value, $unit); } /** @@ -3533,7 +3536,7 @@ protected function coerceUnit($number, $unit) */ protected function coerceMap($item) { - if ($item[0] === 'map') { + if ($item[0] === Type::T_MAP) { return $item; } @@ -3541,7 +3544,7 @@ protected function coerceMap($item) return self::$emptyMap; } - return array('map', array($item), array(self::$null)); + return array(Type::T_MAP, array($item), array(self::$null)); } /** @@ -3553,11 +3556,11 @@ protected function coerceMap($item) */ protected function coerceList($item, $delim = ',') { - if (isset($item) && $item[0] === 'list') { + if (isset($item) && $item[0] === Type::T_LIST) { return $item; } - if (isset($item) && $item[0] === 'map') { + if (isset($item) && $item[0] === Type::T_MAP) { $keys = $item[1]; $values = $item[2]; $list = array(); @@ -3567,16 +3570,16 @@ protected function coerceList($item, $delim = ',') $value = $values[$i]; $list[] = array( - 'list', + Type::T_LIST, '', - array(array('keyword', $this->compileStringContent($this->coerceString($key))), $value) + array(array(Type::T_KEYWORD, $this->compileStringContent($this->coerceString($key))), $value) ); } - return array('list', ',', $list); + return array(Type::T_LIST, ',', $list); } - return array('list', $delim, ! isset($item) ? array(): array($item)); + return array(Type::T_LIST, $delim, ! isset($item) ? array(): array($item)); } /** @@ -3605,18 +3608,18 @@ protected function coerceForExpression($value) protected function coerceColor($value) { switch ($value[0]) { - case 'color': + case Type::T_COLOR: return $value; - case 'keyword': + case Type::T_KEYWORD: $name = strtolower($value[1]); if (isset(Colors::$cssColors[$name])) { $rgba = explode(',', Colors::$cssColors[$name]); return isset($rgba[3]) - ? 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]); + ? array(Type::T_COLOR, (int) $rgba[0], (int) $rgba[1], (int) $rgba[2], (int) $rgba[3]) + : array(Type::T_COLOR, (int) $rgba[0], (int) $rgba[1], (int) $rgba[2]); } return null; @@ -3634,11 +3637,11 @@ protected function coerceColor($value) */ protected function coerceString($value) { - if ($value[0] === 'string') { + if ($value[0] === Type::T_STRING) { return $value; } - return array('string', '', array($this->compileValue($value))); + return array(Type::T_STRING, '', array($this->compileValue($value))); } /** @@ -3650,7 +3653,7 @@ protected function coerceString($value) */ protected function coercePercent($value) { - if ($value[0] === 'number') { + if ($value[0] === Type::T_NUMBER) { if ($value[2] === '%') { return $value[1] / 100; } @@ -3676,7 +3679,7 @@ public function assertMap($value) { $value = $this->coerceMap($value); - if ($value[0] !== 'map') { + if ($value[0] !== Type::T_MAP) { $this->throwError('expecting map'); } @@ -3696,7 +3699,7 @@ public function assertMap($value) */ public function assertList($value) { - if ($value[0] !== 'list') { + if ($value[0] !== Type::T_LIST) { $this->throwError('expecting list'); } @@ -3736,7 +3739,7 @@ public function assertColor($value) */ public function assertNumber($value) { - if ($value[0] !== 'number') { + if ($value[0] !== Type::T_NUMBER) { $this->throwError('expecting number'); } @@ -3802,7 +3805,7 @@ public function toHSL($red, $green, $blue) } } - return array('hsl', fmod($h, 360), $s * 100, $l / 5.1); + return array(Type::T_HSL, fmod($h, 360), $s * 100, $l / 5.1); } /** @@ -3865,7 +3868,7 @@ public function toRGB($hue, $saturation, $lightness) $g = $this->hueToRGB($m1, $m2, $h) * 255; $b = $this->hueToRGB($m1, $m2, $h - 1/3) * 255; - $out = array('color', $r, $g, $b); + $out = array(Type::T_COLOR, $r, $g, $b); return $out; } @@ -3886,11 +3889,11 @@ function ($a) { if (count($kwargs)) { foreach ($kwargs as $key => $value) { - $args[] = array(array('var', $key), $value, false); + $args[] = array(array(Type::T_VARIABLE, $key), $value, false); } } - return $this->reduce(array('fncall', $name, $args)); + return $this->reduce(array(Type::T_FUNCTION_CALL, $name, $args)); } protected static $libIf = array('condition', 'if-true', 'if-false'); @@ -3910,15 +3913,15 @@ protected function libIndex($args) { list($list, $value) = $args; - if ($value[0] === 'map') { + if ($value[0] === Type::T_MAP) { return self::$null; } - if ($list[0] === 'map' || $list[0] === 'string' || $list[0] === 'keyword' || $list[0] === 'interpolate') { + if ($list[0] === Type::T_MAP || $list[0] === Type::T_STRING || $list[0] === Type::T_KEYWORD || $list[0] === Type::T_INTERPOLATE) { $list = $this->coerceList($list, ' '); } - if ($list[0] !== 'list') { + if ($list[0] !== Type::T_LIST) { return self::$null; } @@ -3938,7 +3941,7 @@ protected function libRgb($args) { list($r, $g, $b) = $args; - return array('color', $r[1], $g[1], $b[1]); + return array(Type::T_COLOR, $r[1], $g[1], $b[1]); } protected static $libRgba = array( @@ -3959,7 +3962,7 @@ protected function libRgba($args) list($r, $g, $b, $a) = $args; - return array('color', $r[1], $g[1], $b[1], $a[1]); + return array(Type::T_COLOR, $r[1], $g[1], $b[1], $a[1]); } // helper function for adjust_color, change_color, and scale_color @@ -4110,7 +4113,7 @@ protected function libOpacity($args) { $value = $args[0]; - if ($value[0] === 'number') { + if ($value[0] === Type::T_NUMBER) { return null; } @@ -4141,7 +4144,7 @@ protected function libMix($args) $w1 = (($w * $a === -1 ? $w : ($w + $a) / (1 + $w * $a)) + 1) / 2.0; $w2 = 1.0 - $w1; - $new = array('color', + $new = array(Type::T_COLOR, $w1 * $first[1] + $w2 * $second[1], $w1 * $first[2] + $w2 * $second[2], $w1 * $first[3] + $w2 * $second[3], @@ -4179,7 +4182,7 @@ protected function libHue($args) $color = $this->assertColor($args[0]); $hsl = $this->toHSL($color[1], $color[2], $color[3]); - return array('number', $hsl[1], 'deg'); + return array(Type::T_NUMBER, $hsl[1], 'deg'); } protected static $libSaturation = array('color'); @@ -4188,7 +4191,7 @@ protected function libSaturation($args) $color = $this->assertColor($args[0]); $hsl = $this->toHSL($color[1], $color[2], $color[3]); - return array('number', $hsl[2], '%'); + return array(Type::T_NUMBER, $hsl[2], '%'); } protected static $libLightness = array('color'); @@ -4197,7 +4200,7 @@ protected function libLightness($args) $color = $this->assertColor($args[0]); $hsl = $this->toHSL($color[1], $color[2], $color[3]); - return array('number', $hsl[3], '%'); + return array(Type::T_NUMBER, $hsl[3], '%'); } protected function adjustHsl($color, $idx, $amount) @@ -4245,7 +4248,7 @@ protected function libSaturate($args) { $value = $args[0]; - if ($value[0] === 'number') { + if ($value[0] === Type::T_NUMBER) { return null; } @@ -4269,7 +4272,7 @@ protected function libGrayscale($args) { $value = $args[0]; - if ($value[0] === 'number') { + if ($value[0] === Type::T_NUMBER) { return null; } @@ -4287,7 +4290,7 @@ protected function libInvert($args) { $value = $args[0]; - if ($value[0] === 'number') { + if ($value[0] === Type::T_NUMBER) { return null; } @@ -4342,7 +4345,7 @@ protected function libUnquote($args) { $str = $args[0]; - if ($str[0] === 'string') { + if ($str[0] === Type::T_STRING) { $str[1] = ''; } @@ -4354,17 +4357,17 @@ protected function libQuote($args) { $value = $args[0]; - if ($value[0] === 'string' && ! empty($value[1])) { + if ($value[0] === Type::T_STRING && ! empty($value[1])) { return $value; } - return array('string', '"', array($value)); + return array(Type::T_STRING, '"', array($value)); } protected static $libPercentage = array('value'); protected function libPercentage($args) { - return array('number', $this->coercePercent($args[0]) * 100, '%'); + return array(Type::T_NUMBER, $this->coercePercent($args[0]) * 100, '%'); } protected static $libRound = array('value'); @@ -4445,7 +4448,7 @@ protected function getNormalizedNumbers($args) $numbers = array(); foreach ($args as $key => $item) { - if ('number' !== $item[0]) { + if ($item[0] !== Type::T_NUMBER) { $this->throwError('%s is not a number', $item[0]); } @@ -4550,7 +4553,7 @@ protected function libMapKeys($args) $map = $this->assertMap($args[0]); $keys = $map[1]; - return array('list', ',', $keys); + return array(Type::T_LIST, ',', $keys); } protected static $libMapValues = array('map'); @@ -4559,7 +4562,7 @@ protected function libMapValues($args) $map = $this->assertMap($args[0]); $values = $map[2]; - return array('list', ',', $values); + return array(Type::T_LIST, ',', $values); } protected static $libMapRemove = array('map', 'key'); @@ -4599,7 +4602,7 @@ protected function libMapMerge($args) $map1 = $this->assertMap($args[0]); $map2 = $this->assertMap($args[1]); - return array('map', array_merge($map1[1], $map2[1]), array_merge($map1[2], $map2[2])); + return array(Type::T_MAP, array_merge($map1[1], $map2[1]), array_merge($map1[2], $map2[2])); } protected function listSeparatorForJoin($list1, $sep) @@ -4629,7 +4632,7 @@ protected function libJoin($args) $list2 = $this->coerceList($list2, ' '); $sep = $this->listSeparatorForJoin($list1, $sep); - return array('list', $sep, array_merge($list1[2], $list2[2])); + return array(Type::T_LIST, $sep, array_merge($list1[2], $list2[2])); } protected static $libAppend = array('list', 'val', 'separator'); @@ -4640,7 +4643,7 @@ protected function libAppend($args) $list1 = $this->coerceList($list1, ' '); $sep = $this->listSeparatorForJoin($list1, $sep); - return array('list', $sep, array_merge($list1[2], array($value))); + return array(Type::T_LIST, $sep, array_merge($list1[2], array($value))); } protected function libZip($args) @@ -4653,7 +4656,7 @@ protected function libZip($args) $firstList = array_shift($args); foreach ($firstList[2] as $key => $item) { - $list = array('list', '', array($item)); + $list = array(Type::T_LIST, '', array($item)); foreach ($args as $arg) { if (isset($arg[2][$key])) { @@ -4666,7 +4669,7 @@ protected function libZip($args) $lists[] = $list; } - return array('list', ',', $lists); + return array(Type::T_LIST, ',', $lists); } protected static $libTypeOf = array('value'); @@ -4675,7 +4678,7 @@ protected function libTypeOf($args) $value = $args[0]; switch ($value[0]) { - case 'keyword': + case Type::T_KEYWORD: if ($value === self::$true || $value === self::$false) { return 'bool'; } @@ -4685,10 +4688,10 @@ protected function libTypeOf($args) } // fall-thru - case 'function': + case Type::T_FUNCTION: return 'string'; - case 'list': + case Type::T_LIST: if (isset($value[3]) && $value[3]) { return 'arglist'; } @@ -4704,8 +4707,8 @@ protected function libUnit($args) { $num = $args[0]; - if ($num[0] === 'number') { - return array('string', '"', array($num[2])); + if ($num[0] === Type::T_NUMBER) { + return array(Type::T_STRING, '"', array($num[2])); } return ''; @@ -4716,7 +4719,7 @@ protected function libUnitless($args) { $value = $args[0]; - return $value[0] === 'number' && empty($value[2]); + return $value[0] === Type::T_NUMBER && empty($value[2]); } protected static $libComparable = array('number-1', 'number-2'); @@ -4724,7 +4727,7 @@ protected function libComparable($args) { list($number1, $number2) = $args; - if (! isset($number1[0]) || $number1[0] !== 'number' || ! isset($number2[0]) || $number2[0] !== 'number') { + if (! isset($number1[0]) || $number1[0] !== Type::T_NUMBER || ! isset($number2[0]) || $number2[0] !== Type::T_NUMBER) { $this->throwError('Invalid argument(s) for "comparable"'); } @@ -4745,7 +4748,7 @@ protected function libStrIndex($args) $result = strpos($stringContent, $substringContent); - return $result === false ? self::$null : array('number', $result + 1, ''); + return $result === false ? self::$null : array(Type::T_NUMBER, $result + 1, ''); } protected static $libStrInsert = array('string', 'insert', 'index'); @@ -4770,7 +4773,7 @@ protected function libStrLength($args) $string = $this->coerceString($args[0]); $stringContent = $this->compileStringContent($string); - return array('number', strlen($stringContent), ''); + return array(Type::T_NUMBER, strlen($stringContent), ''); } protected static $libStrSlice = array('string', 'start-at', 'end-at'); @@ -4883,7 +4886,7 @@ protected function libCounter($args) { $list = array_map(array($this, 'compileValue'), $args); - return array('string', '', array('counter(' . implode(',', $list) . ')')); + return array(Type::T_STRING, '', array('counter(' . implode(',', $list) . ')')); } protected function libRandom($args) @@ -4895,10 +4898,10 @@ protected function libRandom($args) $this->throwError("limit must be greater than or equal to 1"); } - return array('number', mt_rand(1, $n), ''); + return array(Type::T_NUMBER, mt_rand(1, $n), ''); } - return array('number', mt_rand(1, mt_getrandmax()), ''); + return array(Type::T_NUMBER, mt_rand(1, mt_getrandmax()), ''); } protected function libUniqueId() @@ -4911,6 +4914,6 @@ protected function libUniqueId() $id += mt_rand(0, 10) + 1; - return array('string', '', array('u' . str_pad(base_convert($id, 10, 36), 8, '0', STR_PAD_LEFT))); + return array(Type::T_STRING, '', array('u' . str_pad(base_convert($id, 10, 36), 8, '0', STR_PAD_LEFT))); } } diff --git a/src/Node.php b/src/Node.php new file mode 100644 index 00000000..107ee574 --- /dev/null +++ b/src/Node.php @@ -0,0 +1,21 @@ + + */ +abstract class Node +{ +} diff --git a/src/Parser.php b/src/Parser.php index f0e486d7..bbfbfd2d 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -13,6 +13,7 @@ use Leafo\ScssPhp\Block; use Leafo\ScssPhp\Compiler; +use Leafo\ScssPhp\Type; /** * SCSS parser @@ -247,7 +248,7 @@ protected function parseChunk() ($this->map($with) || true) && $this->literal('{') ) { - $atRoot = $this->pushSpecialBlock('at-root', $s); + $atRoot = $this->pushSpecialBlock(Type::T_AT_ROOT, $s); $atRoot->selector = $selector; $atRoot->with = $with; @@ -257,7 +258,7 @@ protected function parseChunk() $this->seek($s); if ($this->literal('@media') && $this->mediaQueryList($mediaQueryList) && $this->literal('{')) { - $media = $this->pushSpecialBlock('media', $s); + $media = $this->pushSpecialBlock(Type::T_MEDIA, $s); $media->queryList = $mediaQueryList[2]; return true; @@ -270,7 +271,7 @@ protected function parseChunk() ($this->argumentDef($args) || true) && $this->literal('{') ) { - $mixin = $this->pushSpecialBlock('mixin', $s); + $mixin = $this->pushSpecialBlock(Type::T_MIXIN, $s); $mixin->name = $mixinName; $mixin->args = $args; @@ -287,11 +288,11 @@ protected function parseChunk() ($this->end() || $this->literal('{') && $hasBlock = true) ) { - $child = array('include', + $child = array(Type::T_INCLUDE, $mixinName, isset($argValues) ? $argValues : null, null); if (! empty($hasBlock)) { - $include = $this->pushSpecialBlock('include', $s); + $include = $this->pushSpecialBlock(Type::T_INCLUDE, $s); $include->child = $child; } else { $this->append($child, $s); @@ -306,7 +307,7 @@ protected function parseChunk() $this->valueList($importPath) && $this->end() ) { - $this->append(array('import', $importPath), $s); + $this->append(array(Type::T_IMPORT, $importPath), $s); return true; } @@ -317,7 +318,7 @@ protected function parseChunk() $this->url($importPath) && $this->end() ) { - $this->append(array('import', $importPath), $s); + $this->append(array(Type::T_IMPORT, $importPath), $s); return true; } @@ -328,7 +329,7 @@ protected function parseChunk() $this->selectors($selector) && $this->end() ) { - $this->append(array('extend', $selector), $s); + $this->append(array(Type::T_EXTEND, $selector), $s); return true; } @@ -340,7 +341,7 @@ protected function parseChunk() $this->argumentDef($args) && $this->literal('{') ) { - $func = $this->pushSpecialBlock('function', $s); + $func = $this->pushSpecialBlock(Type::T_FUNCTION, $s); $func->name = $fnName; $func->args = $args; @@ -350,7 +351,7 @@ protected function parseChunk() $this->seek($s); if ($this->literal('@return') && $this->valueList($retVal) && $this->end()) { - $this->append(array('return', $retVal), $s); + $this->append(array(Type::T_RETURN, $retVal), $s); return true; } @@ -363,7 +364,7 @@ protected function parseChunk() $this->valueList($list) && $this->literal('{') ) { - $each = $this->pushSpecialBlock('each', $s); + $each = $this->pushSpecialBlock(Type::T_EACH, $s); foreach ($varNames[2] as $varName) { $each->vars[] = $varName[1]; @@ -380,7 +381,7 @@ protected function parseChunk() $this->expression($cond) && $this->literal('{') ) { - $while = $this->pushSpecialBlock('while', $s); + $while = $this->pushSpecialBlock(Type::T_WHILE, $s); $while->cond = $cond; return true; @@ -397,7 +398,7 @@ protected function parseChunk() $this->expression($end) && $this->literal('{') ) { - $for = $this->pushSpecialBlock('for', $s); + $for = $this->pushSpecialBlock(Type::T_FOR, $s); $for->var = $varName[1]; $for->start = $start; $for->end = $end; @@ -409,7 +410,7 @@ protected function parseChunk() $this->seek($s); if ($this->literal('@if') && $this->valueList($cond) && $this->literal('{')) { - $if = $this->pushSpecialBlock('if', $s); + $if = $this->pushSpecialBlock(Type::T_IF, $s); $if->cond = $cond; $if->cases = array(); @@ -422,7 +423,7 @@ protected function parseChunk() $this->valueList($value) && $this->end() ) { - $this->append(array('debug', $value), $s); + $this->append(array(Type::T_DEBUG, $value), $s); return true; } @@ -433,7 +434,7 @@ protected function parseChunk() $this->valueList($value) && $this->end() ) { - $this->append(array('warn', $value), $s); + $this->append(array(Type::T_WARN, $value), $s); return true; } @@ -444,7 +445,7 @@ protected function parseChunk() $this->valueList($value) && $this->end() ) { - $this->append(array('error', $value), $s); + $this->append(array(Type::T_ERROR, $value), $s); return true; } @@ -452,7 +453,7 @@ protected function parseChunk() $this->seek($s); if ($this->literal('@content') && $this->end()) { - $this->append(array('mixin_content'), $s); + $this->append(array(Type::T_MIXIN_CONTENT), $s); return true; } @@ -461,14 +462,14 @@ protected function parseChunk() $last = $this->last(); - if (isset($last) && $last[0] === 'if') { + if (isset($last) && $last[0] === Type::T_IF) { list(, $if) = $last; if ($this->literal('@else')) { if ($this->literal('{')) { - $else = $this->pushSpecialBlock('else', $s); + $else = $this->pushSpecialBlock(Type::T_ELSE, $s); } elseif ($this->literal('if') && $this->valueList($cond) && $this->literal('{')) { - $else = $this->pushSpecialBlock('elseif', $s); + $else = $this->pushSpecialBlock(Type::T_ELSEIF, $s); $else->cond = $cond; } @@ -489,7 +490,7 @@ protected function parseChunk() $this->end() ) { if (! isset($this->charset)) { - $statement = array('charset', $charset); + $statement = array(Type::T_CHARSET, $charset); $statement[self::SOURCE_POSITION] = $s; $statement[self::SOURCE_INDEX] = $this->sourceIndex; @@ -509,9 +510,9 @@ protected function parseChunk() $this->literal('{') ) { if ($dirName === 'media') { - $directive = $this->pushSpecialBlock('media', $s); + $directive = $this->pushSpecialBlock(Type::T_MEDIA, $s); } else { - $directive = $this->pushSpecialBlock('directive', $s); + $directive = $this->pushSpecialBlock(Type::T_DIRECTIVE, $s); $directive->name = $dirName; } @@ -534,8 +535,8 @@ protected function parseChunk() $this->valueList($value) && $this->end() ) { - $name = array('string', '', array($name)); - $this->append(array('assign', $name, $value), $s); + $name = array(Type::T_STRING, '', array($name)); + $this->append(array(Type::T_ASSIGN, $name, $value), $s); return true; } @@ -550,7 +551,7 @@ protected function parseChunk() ) { // check for '!flag' $assignmentFlag = $this->stripAssignmentFlag($value); - $this->append(array('assign', $name, $value, $assignmentFlag), $s); + $this->append(array(Type::T_ASSIGN, $name, $value, $assignmentFlag), $s); return true; } @@ -576,12 +577,12 @@ protected function parseChunk() $foundSomething = false; if ($this->valueList($value)) { - $this->append(array('assign', $name, $value), $s); + $this->append(array(Type::T_ASSIGN, $name, $value), $s); $foundSomething = true; } if ($this->literal('{')) { - $propBlock = $this->pushSpecialBlock('nestedprop', $s); + $propBlock = $this->pushSpecialBlock(Type::T_NESTED_PROPERTY, $s); $propBlock->prefix = $name; $foundSomething = true; } elseif ($foundSomething) { @@ -599,13 +600,13 @@ protected function parseChunk() if ($this->literal('}')) { $block = $this->popBlock(); - if (isset($block->type) && $block->type === 'include') { + if (isset($block->type) && $block->type === Type::T_INCLUDE) { $include = $block->child; unset($block->child); $include[3] = $block; $this->append($include, $s); } elseif (empty($block->dontAppend)) { - $type = isset($block->type) ? $block->type : 'block'; + $type = isset($block->type) ? $block->type : Type::T_BLOCK; $this->append(array($type, $block), $s); } @@ -633,10 +634,10 @@ protected function stripAssignmentFlag(&$value) { $token = &$value; - for ($token = &$value; $token[0] === 'list' && ($s = count($token[2])); $token = &$lastNode) { + for ($token = &$value; $token[0] === Type::T_LIST && ($s = count($token[2])); $token = &$lastNode) { $lastNode = &$token[2][$s - 1]; - if ($lastNode[0] === 'keyword' && in_array($lastNode[1], array('!default', '!global'))) { + if ($lastNode[0] === Type::T_KEYWORD && in_array($lastNode[1], array('!default', '!global'))) { array_pop($token[2]); $token = $this->flattenList($token); @@ -835,23 +836,23 @@ protected function mediaQuery(&$out) if (($this->literal('only') && ($only = true) || $this->literal('not') && ($not = true) || true) && $this->mixedKeyword($mediaType) ) { - $prop = array('mediaType'); + $prop = array(Type::T_MEDIA_TYPE); if (isset($only)) { - $prop[] = array('keyword', 'only'); + $prop[] = array(Type::T_KEYWORD, 'only'); } if (isset($not)) { - $prop[] = array('keyword', 'not'); + $prop[] = array(Type::T_KEYWORD, 'not'); } - $media = array('list', '', array()); + $media = array(Type::T_LIST, '', array()); foreach ((array)$mediaType as $type) { if (is_array($type)) { $media[2][] = $type; } else { - $media[2][] = array('keyword', $type); + $media[2][] = array(Type::T_KEYWORD, $type); } } @@ -889,7 +890,7 @@ protected function mediaExpression(&$out) ($this->literal(':') && $this->expression($value) || true) && $this->literal(')') ) { - $out = array('mediaExp', $feature); + $out = array(Type::T_MEDIA_EXPRESSION, $feature); if ($value) { $out[] = $value; @@ -1013,7 +1014,7 @@ protected function genericList(&$out, $parseItem, $delim = '', $flatten = true) if ($flatten && count($items) === 1) { $out = $items[0]; } else { - $out = array('list', $delim, $items); + $out = array(Type::T_LIST, $delim, $items); } return true; @@ -1032,12 +1033,12 @@ protected function expression(&$out) if ($this->literal('(')) { if ($this->literal(')')) { - $out = array('list', '', array()); + $out = array(Type::T_LIST, '', array()); return true; } - if ($this->valueList($out) && $this->literal(')') && $out[0] === 'list') { + if ($this->valueList($out) && $this->literal(')') && $out[0] === Type::T_LIST) { return true; } @@ -1099,7 +1100,7 @@ protected function expHelper($lhs, $minP) $rhs = $this->expHelper($rhs, self::$precedence[$next[1]]); } - $lhs = array('exp', $op, $lhs, $rhs, $this->inParens, $whiteBefore, $whiteAfter); + $lhs = array(Type::T_EXPRESSION, $op, $lhs, $rhs, $this->inParens, $whiteBefore, $whiteAfter); $ss = $this->seek(); $whiteBefore = isset($this->buffer[$this->count - 1]) && ctype_space($this->buffer[$this->count - 1]); @@ -1122,7 +1123,7 @@ protected function value(&$out) $s = $this->seek(); if ($this->literal('not', false) && $this->whitespace() && $this->value($inner)) { - $out = array('unary', 'not', $inner, $this->inParens); + $out = array(Type::T_UNARY, 'not', $inner, $this->inParens); return true; } @@ -1130,7 +1131,7 @@ protected function value(&$out) $this->seek($s); if ($this->literal('not', false) && $this->parenValue($inner)) { - $out = array('unary', 'not', $inner, $this->inParens); + $out = array(Type::T_UNARY, 'not', $inner, $this->inParens); return true; } @@ -1138,7 +1139,7 @@ protected function value(&$out) $this->seek($s); if ($this->literal('+') && $this->value($inner)) { - $out = array('unary', '+', $inner, $this->inParens); + $out = array(Type::T_UNARY, '+', $inner, $this->inParens); return true; } @@ -1151,7 +1152,7 @@ protected function value(&$out) $this->unit($inner) || $this->parenValue($inner)) ) { - $out = array('unary', '-', $inner, $this->inParens); + $out = array(Type::T_UNARY, '-', $inner, $this->inParens); return true; } @@ -1172,9 +1173,9 @@ protected function value(&$out) if ($this->keyword($keyword)) { if ($keyword === 'null') { - $out = array('null'); + $out = array(Type::T_NULL); } else { - $out = array('keyword', $keyword); + $out = array(Type::T_KEYWORD, $keyword); } return true; @@ -1198,7 +1199,7 @@ protected function parenValue(&$out) if ($this->literal('(')) { if ($this->literal(')')) { - $out = array('list', '', array()); + $out = array(Type::T_LIST, '', array()); return true; } @@ -1237,7 +1238,7 @@ protected function progid(&$out) $this->openString(')', $args, '('); if ($this->literal(')')) { - $out = array('string', '', array( + $out = array(Type::T_STRING, '', array( 'progid:', $fn, '(', $args, ')' )); @@ -1265,7 +1266,7 @@ protected function func(&$func) $this->literal('(') ) { if ($name === 'alpha' && $this->argumentList($args)) { - $func = array('function', $name, array('string', '', $args)); + $func = array(Type::T_FUNCTION, $name, array(Type::T_STRING, '', $args)); return true; } @@ -1274,7 +1275,7 @@ protected function func(&$func) $ss = $this->seek(); if ($this->argValues($args) && $this->literal(')')) { - $func = array('fncall', $name, $args); + $func = array(Type::T_FUNCTION_CALL, $name, $args); return true; } @@ -1288,10 +1289,10 @@ protected function func(&$func) $args = array(); if (! empty($str)) { - $args[] = array(null, array('string', '', array($str))); + $args[] = array(null, array(Type::T_STRING, '', array($str))); } - $func = array('fncall', $name, $args); + $func = array(Type::T_FUNCTION_CALL, $name, $args); return true; } @@ -1320,7 +1321,7 @@ protected function argumentList(&$out) $ss = $this->seek(); if ($this->literal('=') && $this->expression($exp)) { - $args[] = array('string', '', array($var . '=')); + $args[] = array(Type::T_STRING, '', array($var . '=')); $arg = $exp; } else { break; @@ -1332,7 +1333,7 @@ protected function argumentList(&$out) break; } - $args[] = array('string', '', array(', ')); + $args[] = array(Type::T_STRING, '', array(', ')); } if (! $this->literal(')') || ! count($args)) { @@ -1439,7 +1440,7 @@ protected function map(&$out) return false; } - $out = array('map', $keys, $values); + $out = array(Type::T_MAP, $keys, $values); return true; } @@ -1453,7 +1454,7 @@ protected function map(&$out) */ protected function color(&$out) { - $color = array('color'); + $color = array(Type::T_COLOR); if ($this->match('(#([0-9a-f]{6})|#([0-9a-f]{3}))', $m)) { if (isset($m[3])) { @@ -1491,7 +1492,7 @@ protected function color(&$out) protected function unit(&$unit) { if ($this->match('([0-9]*(\.)?[0-9]+)([%a-zA-Z]+)?', $m)) { - $unit = array('number', $m[1], empty($m[3]) ? '' : $m[3]); + $unit = array(Type::T_NUMBER, $m[1], empty($m[3]) ? '' : $m[3]); return true; } @@ -1549,7 +1550,7 @@ protected function string(&$out) $this->eatWhiteDefault = $oldWhite; if ($this->literal($delim)) { - $out = array('string', $delim, $content); + $out = array(Type::T_STRING, $delim, $content); return true; } @@ -1670,7 +1671,7 @@ protected function openString($end, &$out, $nestingOpen = null) $content[count($content) - 1] = rtrim(end($content)); } - $out = array('string', '', $content); + $out = array(Type::T_STRING, '', $content); return true; } @@ -1700,7 +1701,7 @@ protected function interpolation(&$out, $lookWhite = true) $left = $right = false; } - $out = array('interpolate', $value, $left, $right); + $out = array(Type::T_INTERPOLATE, $value, $left, $right); $this->eatWhiteDefault = $oldWhite; if ($this->eatWhiteDefault) { @@ -1764,7 +1765,7 @@ protected function propertyName(&$out) $this->whitespace(); // get any extra whitespace - $out = array('string', '', $parts); + $out = array(Type::T_STRING, '', $parts); return true; } @@ -2027,7 +2028,7 @@ protected function variable(&$out) $s = $this->seek(); if ($this->literal('$', false) && $this->keyword($name)) { - $out = array('var', $name); + $out = array(Type::T_VARIABLE, $name); return true; } @@ -2088,7 +2089,7 @@ protected function placeholder(&$placeholder) protected function url(&$out) { if ($this->match('(url\(\s*(["\']?)([^)]+)\2\s*\))', $m)) { - $out = array('string', '', array('url(' . $m[2] . $m[3] . $m[2] . ')')); + $out = array(Type::T_STRING, '', array('url(' . $m[2] . $m[3] . $m[2] . ')')); return true; } @@ -2276,7 +2277,7 @@ protected function whitespace() 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->appendComment(array(Type::T_COMMENT, $m[1])); $this->commentsSeen[$this->count] = true; } @@ -2360,7 +2361,7 @@ protected function show() */ protected function flattenList($value) { - if ($value[0] === 'list' && count($value[2]) === 1) { + if ($value[0] === Type::T_LIST && count($value[2]) === 1) { return $this->flattenList($value[2][0]); } diff --git a/src/Type.php b/src/Type.php new file mode 100644 index 00000000..2402f7a4 --- /dev/null +++ b/src/Type.php @@ -0,0 +1,65 @@ + + */ +class Type +{ + const T_ASSIGN = 'assign'; + const T_AT_ROOT = 'at-root'; + const T_BLOCK = 'block'; + const T_CHARSET = 'charset'; + const T_COLOR = 'color'; + const T_COMMENT = 'comment'; + 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_SELF = 'self'; + const T_STRING = 'string'; + const T_UNARY = 'unary'; + const T_VARIABLE = 'var'; + const T_WARN = 'warn'; + const T_WHILE = 'while'; +} From 63ffea74d80047f79ea95679fce83d8cab370432 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Fri, 6 Nov 2015 10:00:40 -0500 Subject: [PATCH 107/484] Reorganize methods in Parser --- src/Parser.php | 574 +++++++++++++++++++++++++------------------------ 1 file changed, 294 insertions(+), 280 deletions(-) diff --git a/src/Parser.php b/src/Parser.php index bbfbfd2d..1b3498b4 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -84,6 +84,8 @@ class Parser /** * Constructor * + * @api + * * @param string $sourceName * @param integer $sourceIndex */ @@ -105,23 +107,66 @@ public function __construct($sourceName, $sourceIndex = 0) } /** - * Make operator regex + * Get source file name * - * @param array $operators + * @api * * @return string */ - protected static function makeOperatorStr($operators) + public function getSourceName() { - return '(' - . implode('|', array_map(array('Leafo\ScssPhp\Parser', 'pregQuote'), $operators)) - . ')'; + return $this->sourceName; + } + + /** + * Get source line number (given character position in the buffer) + * + * @api + * + * @param integer $pos + * + * @return integer + */ + public function getLineNo($pos) + { + return 1 + substr_count(substr($this->buffer, 0, $pos), "\n"); + } + + /** + * Throw parser error + * + * @api + * + * @param string $msg + * @param integer $count + * + * @throws \Exception + */ + public function throwParseError($msg = 'parse error', $count = null) + { + $count = ! isset($count) ? $this->count : $count; + + $line = $this->getLineNo($count); + + if (! empty($this->sourceName)) { + $loc = "$this->sourceName on line $line"; + } else { + $loc = "line: $line"; + } + + if ($this->peek("(.*?)(\n|$)", $m, $count)) { + throw new \Exception("$msg: failed at `$m[1]` $loc"); + } + + throw new \Exception("$msg: $loc"); } /** * Parser buffer * - * @param string $buffer; + * @api + * + * @param string $buffer * * @return \Leafo\ScssPhp\Block */ @@ -163,6 +208,8 @@ public function parse($buffer) /** * Parse a value or value list * + * @api + * * @param string $buffer * @param string $out * @@ -182,6 +229,8 @@ public function parseValue($buffer, &$out) /** * Parse a selector or selector list * + * @api + * * @param string $buffer * @param string $out * @@ -198,6 +247,32 @@ public function parseSelector($buffer, &$out) return $this->selectors($out); } + /** + * Quote regular expression + * + * @param string $what + * + * @return string + */ + public static function pregQuote($what) + { + return preg_quote($what, '/'); + } + + /** + * Make operator regex + * + * @param array $operators + * + * @return string + */ + protected static function makeOperatorStr($operators) + { + return '(' + . implode('|', array_map(array('Leafo\ScssPhp\Parser', 'pregQuote'), $operators)) + . ')'; + } + /** * Parse a single chunk off the head of the buffer and append it to the * current parse environment. @@ -623,64 +698,6 @@ protected function parseChunk() return false; } - /** - * Strip assignment flag from the list - * - * @param array $value - * - * @return string - */ - protected function stripAssignmentFlag(&$value) - { - $token = &$value; - - for ($token = &$value; $token[0] === Type::T_LIST && ($s = count($token[2])); $token = &$lastNode) { - $lastNode = &$token[2][$s - 1]; - - if ($lastNode[0] === Type::T_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 * @@ -740,22 +757,187 @@ protected function pushSpecialBlock($type, $pos) */ protected function popBlock() { - $block = $this->env; + $block = $this->env; + + if (empty($block->parent)) { + $this->throwParseError('unexpected }'); + } + + $this->env = $block->parent; + unset($block->parent); + + $comments = $block->comments; + if (count($comments)) { + $this->env->comments = $comments; + unset($block->comments); + } + + return $block; + } + + /** + * Peek input stream + * + * @param string $regex + * @param array $out + * @param integer $from + * + * @return integer + */ + protected function peek($regex, &$out, $from = null) + { + if (! isset($from)) { + $from = $this->count; + } + + $r = '/' . $regex . '/Ais'; + $result = preg_match($r, $this->buffer, $out, null, $from); + + return $result; + } + + /** + * Seek to position in input stream (or return current position in input stream) + * + * @param integer $where + * + * @return integer + */ + protected function seek($where = null) + { + if ($where === null) { + return $this->count; + } + + $this->count = $where; + + return true; + } + + /** + * Match string looking for either ending delim, escape, or string interpolation + * + * {@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 matchString(&$m, $delim) + { + $token = null; + + $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 (! isset($token)) { + return false; + } + + $match = substr($this->buffer, $this->count, $end - $this->count); + $m = array( + $match . $token, + $match, + $token + ); + $this->count = $end + strlen($token); + + return true; + } + + /** + * Try to match something on head of buffer + * + * @param string $regex + * @param array $out + * @param boolean $eatWhitespace + * + * @return boolean + */ + protected function match($regex, &$out, $eatWhitespace = null) + { + if (! isset($eatWhitespace)) { + $eatWhitespace = $this->eatWhiteDefault; + } + + $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; + } + + /** + * 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); + } + + /** + * Match some whitespace + * + * @return boolean + */ + protected function whitespace() + { + $gotWhite = false; - if (empty($block->parent)) { - $this->throwParseError('unexpected }'); - } + while (preg_match(self::$whitePattern, $this->buffer, $m, null, $this->count)) { + if (isset($m[1]) && empty($this->commentsSeen[$this->count])) { + $this->appendComment(array(Type::T_COMMENT, $m[1])); - $this->env = $block->parent; - unset($block->parent); + $this->commentsSeen[$this->count] = true; + } - $comments = $block->comments; - if (count($comments)) { - $this->env->comments = $comments; - unset($block->comments); + $this->count += strlen($m[0]); + $gotWhite = true; } - return $block; + return $gotWhite; } /** @@ -957,7 +1139,7 @@ protected function argValue(&$out) } /** - * Parse list + * Parse comma separated value list * * @param string $out * @@ -969,7 +1151,7 @@ protected function valueList(&$out) } /** - * Parse space list + * Parse space separated value list * * @param array $out * @@ -2117,229 +2299,77 @@ protected function end() } /** - * @deprecated - * - * {@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 to($what, &$out, $until = false, $allowNewline = false) - { - if (is_string($allowNewline)) { - $validChars = $allowNewline; - } else { - $validChars = $allowNewline ? '.' : "[^\n]"; - } - - if (! $this->match('(' . $validChars . '*?)' . $this->pregQuote($what), $m, ! $until)) { - return false; - } - - if ($until) { - $this->count -= strlen($what); // give back $what - } - - $out = $m[1]; - - return true; - } - - /** - * Throw parser error - * - * @param string $msg - * @param integer $count + * Strip assignment flag from the list * - * @throws \Exception - */ - public function throwParseError($msg = 'parse error', $count = null) - { - $count = ! isset($count) ? $this->count : $count; - - $line = $this->getLineNo($count); - - if (! empty($this->sourceName)) { - $loc = "$this->sourceName on line $line"; - } else { - $loc = "line: $line"; - } - - if ($this->peek("(.*?)(\n|$)", $m, $count)) { - throw new \Exception("$msg: failed at `$m[1]` $loc"); - } - - throw new \Exception("$msg: $loc"); - } - - /** - * Get source file name + * @param array $value * * @return string */ - public function getSourceName() - { - return $this->sourceName; - } - - /** - * Get source line number (given character position in the buffer) - * - * @param integer $pos - * - * @return integer - */ - public function getLineNo($pos) + protected function stripAssignmentFlag(&$value) { - return 1 + substr_count(substr($this->buffer, 0, $pos), "\n"); - } + $token = &$value; - /** - * Match string looking for either ending delim, escape, or string interpolation - * - * {@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 matchString(&$m, $delim) - { - $token = null; + for ($token = &$value; $token[0] === Type::T_LIST && ($s = count($token[2])); $token = &$lastNode) { + $lastNode = &$token[2][$s - 1]; - $end = strlen($this->buffer); + if ($lastNode[0] === Type::T_KEYWORD && in_array($lastNode[1], array('!default', '!global'))) { + array_pop($token[2]); - // look for either ending delim, escape, or string interpolation - foreach (array('#{', '\\', $delim) as $lookahead) { - $pos = strpos($this->buffer, $lookahead, $this->count); + $token = $this->flattenList($token); - if ($pos !== false && $pos < $end) { - $end = $pos; - $token = $lookahead; + return $lastNode[1]; } } - if (! isset($token)) { - return false; - } - - $match = substr($this->buffer, $this->count, $end - $this->count); - $m = array( - $match . $token, - $match, - $token - ); - $this->count = $end + strlen($token); - - return true; + return false; } /** - * Try to match something on head of buffer + * Turn list of length 1 into value type * - * @param string $regex - * @param array $out - * @param boolean $eatWhitespace + * @param array $value * - * @return boolean + * @return array */ - protected function match($regex, &$out, $eatWhitespace = null) + protected function flattenList($value) { - if (! isset($eatWhitespace)) { - $eatWhitespace = $this->eatWhiteDefault; - } - - $r = '/' . $regex . '/Ais'; - - if (preg_match($r, $this->buffer, $out, null, $this->count)) { - $this->count += strlen($out[0]); - - if ($eatWhitespace) { - $this->whitespace(); - } - - return true; + if ($value[0] === Type::T_LIST && count($value[2]) === 1) { + return $this->flattenList($value[2][0]); } - return false; + return $value; } /** - * Match some whitespace + * @deprecated * - * @return boolean + * {@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 whitespace() + protected function to($what, &$out, $until = false, $allowNewline = false) { - $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(Type::T_COMMENT, $m[1])); - - $this->commentsSeen[$this->count] = true; - } - - $this->count += strlen($m[0]); - $gotWhite = true; + if (is_string($allowNewline)) { + $validChars = $allowNewline; + } else { + $validChars = $allowNewline ? '.' : "[^\n]"; } - return $gotWhite; - } - - /** - * Peek input stream - * - * @param string $regex - * @param array $out - * @param integer $from - * - * @return integer - */ - protected function peek($regex, &$out, $from = null) - { - if (! isset($from)) { - $from = $this->count; + if (! $this->match('(' . $validChars . '*?)' . $this->pregQuote($what), $m, ! $until)) { + return false; } - $r = '/' . $regex . '/Ais'; - $result = preg_match($r, $this->buffer, $out, null, $from); - - return $result; - } - - /** - * Seek to position in input stream (or return current position in input stream) - * - * @param integer $where - * - * @return integer - */ - protected function seek($where = null) - { - if ($where === null) { - return $this->count; + if ($until) { + $this->count -= strlen($what); // give back $what } - $this->count = $where; + $out = $m[1]; return true; } - /** - * Quote regular expression - * - * @param string $what - * - * @return string - */ - public static function pregQuote($what) - { - return preg_quote($what, '/'); - } - /** * @deprecated */ @@ -2351,20 +2381,4 @@ protected function show() return ''; } - - /** - * Turn list of length 1 into value type - * - * @param array $value - * - * @return array - */ - protected function flattenList($value) - { - if ($value[0] === Type::T_LIST && count($value[2]) === 1) { - return $this->flattenList($value[2][0]); - } - - return $value; - } } From c876d82ea6c45775a5f5eb5149a9ac2bc9158730 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Fri, 6 Nov 2015 10:20:13 -0500 Subject: [PATCH 108/484] phpcs --- src/Compiler.php | 64 ++++++++++++++++++++++++++++-------------------- src/Parser.php | 49 ++++++++++++++++++------------------ 2 files changed, 61 insertions(+), 52 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 8991d05f..47a9c476 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -57,36 +57,36 @@ class Compiler const LINE_COMMENTS = 1; const DEBUG_INFO = 2; - const WITH_RULE = 1; - const WITH_MEDIA = 2; + const WITH_RULE = 1; + const WITH_MEDIA = 2; const WITH_SUPPORTS = 4; - const WITH_ALL = 7; + const WITH_ALL = 7; /** * @var array */ static protected $operatorNames = array( - '+' => 'add', - '-' => 'sub', - '*' => 'mul', - '/' => 'div', - '%' => 'mod', - - '==' => 'eq', - '!=' => 'neq', - '<' => 'lt', - '>' => 'gt', - - '<=' => 'lte', - '>=' => 'gte', + '+' => 'add', + '-' => 'sub', + '*' => 'mul', + '/' => 'div', + '%' => 'mod', + + '==' => 'eq', + '!=' => 'neq', + '<' => 'lt', + '>' => 'gt', + + '<=' => 'lte', + '>=' => 'gte', ); /** * @var array */ static protected $namespaces = array( - 'special' => '%', - 'mixin' => '@', + 'special' => '%', + 'mixin' => '@', 'function' => '^', ); @@ -122,9 +122,9 @@ class Compiler protected $registeredVars = array(); protected $registeredFeatures = array( 'extend-selector-pseudoclass' => false, - 'at-error' => true, - 'units-level-3' => false, - 'global-variable-shadowing' => false, + 'at-error' => true, + 'units-level-3' => false, + 'global-variable-shadowing' => false, ); protected $numberPrecision = 5; @@ -526,7 +526,11 @@ protected function compileMedia(Block $media) 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 !== Type::T_BLOCK && + $type !== Type::T_MEDIA && + $type !== Type::T_DIRECTIVE && + $type !== Type::T_IMPORT + ) { $needsWrap = true; break; } @@ -737,10 +741,10 @@ private function spliceTree($envs, Block $block, $without) private function compileWith($with) { static $mapping = array( - 'rule' => self::WITH_RULE, - 'media' => self::WITH_MEDIA, + 'rule' => self::WITH_RULE, + 'media' => self::WITH_MEDIA, 'supports' => self::WITH_SUPPORTS, - 'all' => self::WITH_ALL, + 'all' => self::WITH_ALL, ); // exclude selectors by default @@ -3917,7 +3921,11 @@ protected function libIndex($args) 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] === Type::T_MAP || + $list[0] === Type::T_STRING || + $list[0] === Type::T_KEYWORD || + $list[0] === Type::T_INTERPOLATE + ) { $list = $this->coerceList($list, ' '); } @@ -4727,7 +4735,9 @@ 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] !== Type::T_NUMBER || + ! isset($number2[0]) || $number2[0] !== Type::T_NUMBER + ) { $this->throwError('Invalid argument(s) for "comparable"'); } diff --git a/src/Parser.php b/src/Parser.php index 1b3498b4..205cda6d 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -29,20 +29,20 @@ class Parser * @var array */ protected static $precedence = array( - '=' => 0, - 'or' => 1, + '=' => 0, + 'or' => 1, 'and' => 2, - '==' => 3, - '!=' => 3, - '<=' => 4, - '>=' => 4, - '<' => 4, - '>' => 4, - '+' => 5, - '-' => 5, - '*' => 6, - '/' => 6, - '%' => 6, + '==' => 3, + '!=' => 3, + '<=' => 4, + '>=' => 4, + '<' => 4, + '>' => 4, + '+' => 5, + '-' => 5, + '*' => 6, + '/' => 6, + '%' => 6, ); /** @@ -98,9 +98,9 @@ public function __construct($sourceName, $sourceIndex = 0) if (empty(self::$operatorStr)) { self::$operatorStr = $this->makeOperatorStr(self::$operators); - $commentSingle = $this->pregQuote(self::$commentSingle); - $commentMultiLeft = $this->pregQuote(self::$commentMultiLeft); - $commentMultiRight = $this->pregQuote(self::$commentMultiRight); + $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'; } @@ -709,12 +709,11 @@ protected function parseChunk() protected function pushBlock($selectors, $pos = 0) { $b = new Block; - $b->parent = $this->env; - + $b->parent = $this->env; $b->sourcePosition = $pos; - $b->sourceIndex = $this->sourceIndex; - $b->selectors = $selectors; - $b->comments = array(); + $b->sourceIndex = $this->sourceIndex; + $b->selectors = $selectors; + $b->comments = array(); if (! $this->env) { $b->children = array(); @@ -962,7 +961,7 @@ protected function append($statement, $pos = null) { if ($pos !== null) { $statement[self::SOURCE_POSITION] = $pos; - $statement[self::SOURCE_INDEX] = $this->sourceIndex; + $statement[self::SOURCE_INDEX] = $this->sourceIndex; } $this->env->children[] = $statement; @@ -1038,7 +1037,7 @@ protected function mediaQuery(&$out) } } - $prop[] = $media; + $prop[] = $media; $parts[] = $prop; } @@ -1801,8 +1800,8 @@ protected function openString($end, &$out, $nestingOpen = null) $oldWhite = $this->eatWhiteDefault; $this->eatWhiteDefault = false; - $stop = array('\'', '"', '#{', $end); - $stop = array_map(array($this, 'pregQuote'), $stop); + $stop = array('\'', '"', '#{', $end); + $stop = array_map(array($this, 'pregQuote'), $stop); $stop[] = self::$commentMulti; $patt = '(.*?)(' . implode('|', $stop) . ')'; From e36c909f61afb4c92909073cad1317ca73602120 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Fri, 6 Nov 2015 11:14:57 -0500 Subject: [PATCH 109/484] internals: cleanup --- src/Compiler.php | 8 ++++---- src/Parser.php | 16 +--------------- 2 files changed, 5 insertions(+), 19 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 47a9c476..0f412c42 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -3494,7 +3494,7 @@ private function coerceValue($value) } if (is_bool($value)) { - return $value ? self::$true : self::$false; + return $this->toBool($value); } if ($value === null) { @@ -4866,7 +4866,7 @@ protected function libGlobalVariableExists($args) $string = $this->coerceString($args[0]); $name = $this->compileStringContent($string); - return $this->has($name, $this->rootEnv) ? self::$true : self::$false; + return $this->has($name, $this->rootEnv); } protected static $libMixinExists = array('name'); @@ -4875,7 +4875,7 @@ protected function libMixinExists($args) $string = $this->coerceString($args[0]); $name = $this->compileStringContent($string); - return $this->has(self::$namespaces['mixin'] . $name) ? self::$true : self::$false; + return $this->has(self::$namespaces['mixin'] . $name); } protected static $libVariableExists = array('name'); @@ -4884,7 +4884,7 @@ protected function libVariableExists($args) $string = $this->coerceString($args[0]); $name = $this->compileStringContent($string); - return $this->has($name) ? self::$true : self::$false; + return $this->has($name); } /** diff --git a/src/Parser.php b/src/Parser.php index 205cda6d..6729a68c 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -96,7 +96,7 @@ public function __construct($sourceName, $sourceIndex = 0) $this->charset = null; if (empty(self::$operatorStr)) { - self::$operatorStr = $this->makeOperatorStr(self::$operators); + self::$operatorStr = '(' . implode('|', array_map(array($this, 'pregQuote'), self::$operators)) . ')'; $commentSingle = $this->pregQuote(self::$commentSingle); $commentMultiLeft = $this->pregQuote(self::$commentMultiLeft); @@ -259,20 +259,6 @@ public static function pregQuote($what) return preg_quote($what, '/'); } - /** - * Make operator regex - * - * @param array $operators - * - * @return string - */ - protected static function makeOperatorStr($operators) - { - return '(' - . implode('|', array_map(array('Leafo\ScssPhp\Parser', 'pregQuote'), $operators)) - . ')'; - } - /** * Parse a single chunk off the head of the buffer and append it to the * current parse environment. From c9fdc3d0945b76ea77e90b0ef590ea6dc55ac68e Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Fri, 6 Nov 2015 11:28:11 -0500 Subject: [PATCH 110/484] Experimental features: spaceship <=> operator, @break, @continue --- src/Compiler.php | 58 ++++++++++++++++++++--- src/Parser.php | 23 +++++++++- src/Type.php | 3 ++ tests/inputs/looping.scss | 63 +++++++++++++++++++++++++ tests/inputs/operators.scss | 4 ++ tests/outputs/looping.css | 21 +++++++++ tests/outputs/operators.css | 5 +- tests/outputs_numbered/looping.css | 21 +++++++++ tests/outputs_numbered/operators.css | 69 +++++++++++++++------------- 9 files changed, 224 insertions(+), 43 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 0f412c42..68be1725 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -79,6 +79,7 @@ class Compiler '<=' => 'lte', '>=' => 'gte', + '<=>' => 'cmp', ); /** @@ -1505,9 +1506,6 @@ protected function compileChild($child, OutputBlock $out) } break; - case Type::T_RETURN: - return $this->reduce($child[1], true); - case Type::T_EACH: list(, $each) = $child; @@ -1529,9 +1527,15 @@ protected function compileChild($child, OutputBlock $out) $ret = $this->compileChildren($each->children, $out); if ($ret) { - $this->popEnv(); + if ($ret[0] !== Type::T_CONTROL) { + $this->popEnv(); + + return $ret; + } - return $ret; + if ($ret[1]) { + break; + } } } @@ -1545,7 +1549,13 @@ protected function compileChild($child, OutputBlock $out) $ret = $this->compileChildren($while->children, $out); if ($ret) { - return $ret; + if ($ret[0] !== Type::T_CONTROL) { + return $ret; + } + + if ($ret[1]) { + break; + } } } break; @@ -1572,11 +1582,26 @@ protected function compileChild($child, OutputBlock $out) $ret = $this->compileChildren($for->children, $out); if ($ret) { - return $ret; + if ($ret[0] !== Type::T_CONTROL) { + return $ret; + } + + if ($ret[1]) { + break; + } } } break; + case Type::T_BREAK: + return array(Type::T_CONTROL, true); + + case Type::T_CONTINUE: + return array(Type::T_CONTROL, false); + + case Type::T_RETURN: + return $this->reduce($child[1], true); + case Type::T_NESTED_PROPERTY: list(, $prop) = $child; @@ -1674,6 +1699,10 @@ protected function compileChild($child, OutputBlock $out) $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]"); } @@ -2375,6 +2404,21 @@ protected function opLtNumberNumber($left, $right) 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 array(Type::T_NUMBER, $n ? $n / abs($n) : 0, ''); + } + /** * Cast to boolean * diff --git a/src/Parser.php b/src/Parser.php index 6729a68c..573f5c30 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -34,6 +34,7 @@ class Parser 'and' => 2, '==' => 3, '!=' => 3, + '<=>' => 3, '<=' => 4, '>=' => 4, '<' => 4, @@ -56,6 +57,7 @@ class Parser '%', '==', '!=', + '<=>', '<=', '>=', '<', @@ -411,8 +413,25 @@ protected function parseChunk() $this->seek($s); - if ($this->literal('@return') && $this->valueList($retVal) && $this->end()) { - $this->append(array(Type::T_RETURN, $retVal), $s); + if ($this->literal('@break') && $this->end()) { + $this->append(array(Type::T_BREAK), $s); + + return true; + } + + $this->seek($s); + + if ($this->literal('@continue') && $this->end()) { + $this->append(array(Type::T_CONTINUE), $s); + + return true; + } + + $this->seek($s); + + + if ($this->literal('@return') && ($this->valueList($retVal) || true) && $this->end()) { + $this->append(array(Type::T_RETURN, isset($retVal) ? $retVal : array(Type::T_NULL)), $s); return true; } diff --git a/src/Type.php b/src/Type.php index 2402f7a4..b750bbe4 100644 --- a/src/Type.php +++ b/src/Type.php @@ -21,9 +21,12 @@ 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'; diff --git a/tests/inputs/looping.scss b/tests/inputs/looping.scss index dd96b12c..665bce99 100644 --- a/tests/inputs/looping.scss +++ b/tests/inputs/looping.scss @@ -88,3 +88,66 @@ div { c: is-number("3"); c: is-number(5); } + +body.each { + @each $x in 1 2 3 4 5 { + @if $x == 2 or $x == 4 { + background-color: $x; + @continue; + } + + color: $x; + + @if $x == 5 { + @break; + } + @else { + @continue; + } + + color: "not reached"; + } +} + +body.for { + @for $x from 1 through 5 { + @if $x == 2 or $x == 4 { + background-color: $x; + @continue; + } + + color: $x; + + @if $x == 5 { + @break; + } + @else { + @continue; + } + + color: "not reached"; + } +} + +body.while { + $x: 0; + @while $x <= 5 { + $x: $x+1; + + @if $x == 2 or $x == 4 { + background-color: $x; + @continue; + } + + color: $x; + + @if $x == 5 { + @break; + } + @else { + @continue; + } + + color: "not reached"; + } +} diff --git a/tests/inputs/operators.scss b/tests/inputs/operators.scss index 98ae7cb0..2b4fea4f 100644 --- a/tests/inputs/operators.scss +++ b/tests/inputs/operators.scss @@ -19,6 +19,10 @@ div { color: 4 > 3; color: 4 < 3; color: what > 3; + + color: 0 <=> 0; + color: 1 <=> 2; + color: 2 <=> 1; } diff --git a/tests/outputs/looping.css b/tests/outputs/looping.css index 1dd81d68..2d313b1f 100644 --- a/tests/outputs/looping.css +++ b/tests/outputs/looping.css @@ -62,3 +62,24 @@ div { b: true; c: true; c: true; } + +body.each { + color: 1; + background-color: 2; + color: 3; + background-color: 4; + color: 5; } + +body.for { + color: 1; + background-color: 2; + color: 3; + background-color: 4; + color: 5; } + +body.while { + color: 1; + background-color: 2; + color: 3; + background-color: 4; + color: 5; } diff --git a/tests/outputs/operators.css b/tests/outputs/operators.css index c592c08e..3a236c57 100644 --- a/tests/outputs/operators.css +++ b/tests/outputs/operators.css @@ -14,7 +14,10 @@ div { color: true; color: true; color: false; - color: what > 3; } + color: what > 3; + color: 0; + color: -1; + color: 1; } #units { test: 2.5748in; diff --git a/tests/outputs_numbered/looping.css b/tests/outputs_numbered/looping.css index 921e978f..3ca7273c 100644 --- a/tests/outputs_numbered/looping.css +++ b/tests/outputs_numbered/looping.css @@ -66,3 +66,24 @@ div { b: true; c: true; c: true; } +/* line 92, inputs/looping.scss */ +body.each { + color: 1; + background-color: 2; + color: 3; + background-color: 4; + color: 5; } +/* line 112, inputs/looping.scss */ +body.for { + color: 1; + background-color: 2; + color: 3; + background-color: 4; + color: 5; } +/* line 132, inputs/looping.scss */ +body.while { + color: 1; + background-color: 2; + color: 3; + background-color: 4; + color: 5; } diff --git a/tests/outputs_numbered/operators.css b/tests/outputs_numbered/operators.css index c379809d..0d35202f 100644 --- a/tests/outputs_numbered/operators.css +++ b/tests/outputs_numbered/operators.css @@ -15,19 +15,22 @@ div { color: true; color: true; color: false; - color: what > 3; } -/* line 25, inputs/operators.scss */ + color: what > 3; + color: 0; + color: -1; + color: 1; } +/* line 29, inputs/operators.scss */ #units { test: 2.5748in; test: 13mm; test: 4em; test: 11mm; test: 1.1cm; } -/* line 33, inputs/operators.scss */ +/* line 37, inputs/operators.scss */ #modulo { test: 1; test: 1cm; } -/* line 38, inputs/operators.scss */ +/* line 42, inputs/operators.scss */ #colors { color: #ff0203; color: #fe0000; @@ -42,11 +45,11 @@ div { color: true; color: true; color: false; } -/* line 59, inputs/operators.scss */ +/* line 63, inputs/operators.scss */ #preserve { hello: what -going; hello: what - going; } -/* line 64, inputs/operators.scss */ +/* line 68, inputs/operators.scss */ #strings { hello: what -going; hello: whatgoing; @@ -56,12 +59,12 @@ div { hello: "whatgoing"; hello: goingwhat; hello: "whatwhat"; } -/* line 77, inputs/operators.scss */ +/* line 81, inputs/operators.scss */ #negation { a: -60; b: -90; b: -90; } -/* line 84, inputs/operators.scss */ +/* line 88, inputs/operators.scss */ #bools-fail { and: false and two; and: one and two; @@ -69,7 +72,7 @@ div { or: false or two; or: one or two; or: one or false; } -/* line 94, inputs/operators.scss */ +/* line 98, inputs/operators.scss */ #bools { and: false; and: two; @@ -77,7 +80,7 @@ div { or: two; or: one; or: one; } -/* line 105, inputs/operators.scss */ +/* line 109, inputs/operators.scss */ #nots-fail { not: false2; not: not false; @@ -85,7 +88,7 @@ div { not: not 1; not: not ""; not: not hello; } -/* line 114, inputs/operators.scss */ +/* line 118, inputs/operators.scss */ #nots { not: false2; not: true; @@ -93,7 +96,7 @@ div { not: false; not: false; not: false; } -/* line 123, inputs/operators.scss */ +/* line 127, inputs/operators.scss */ #string-test { str: true; str: false; @@ -101,66 +104,66 @@ div { str: true; str: xhellohellofalse; str: true; } -/* line 139, inputs/operators.scss */ +/* line 143, inputs/operators.scss */ #special { cancel-unit: 1; } -/* line 146, inputs/operators.scss */ +/* line 150, inputs/operators.scss */ .row .a { margin: -0.5em; } -/* line 147, inputs/operators.scss */ +/* line 151, inputs/operators.scss */ .row .b { margin: -0.5em; } -/* line 148, inputs/operators.scss */ +/* line 152, inputs/operators.scss */ .row .c { margin: -0.5em; } -/* line 149, inputs/operators.scss */ +/* line 153, inputs/operators.scss */ .row .d { margin: -0.5em; } -/* line 150, inputs/operators.scss */ +/* line 154, inputs/operators.scss */ .row .e { margin: 0 -0.5em; } -/* line 152, inputs/operators.scss */ +/* line 156, inputs/operators.scss */ .alt .a { margin: -0.5em; } -/* line 153, inputs/operators.scss */ +/* line 157, inputs/operators.scss */ .alt .b { margin: -0.5em; } -/* line 154, inputs/operators.scss */ +/* line 158, inputs/operators.scss */ .alt .c { margin: -0.5em; } -/* line 155, inputs/operators.scss */ +/* line 159, inputs/operators.scss */ .alt .d { margin: 0 -0.5em; } -/* line 156, inputs/operators.scss */ +/* line 160, inputs/operators.scss */ .alt .e { margin: 0 -0.5em; } -/* line 158, inputs/operators.scss */ +/* line 162, inputs/operators.scss */ .row .f { margin: -2em; } -/* line 159, inputs/operators.scss */ +/* line 163, inputs/operators.scss */ .row .g { margin: -2em; } -/* line 160, inputs/operators.scss */ +/* line 164, inputs/operators.scss */ .row .h { margin: -2em; } -/* line 161, inputs/operators.scss */ +/* line 165, inputs/operators.scss */ .row .i { margin: -2em; } -/* line 162, inputs/operators.scss */ +/* line 166, inputs/operators.scss */ .row .j { margin: 0 -2em; } -/* line 164, inputs/operators.scss */ +/* line 168, inputs/operators.scss */ .alt .f { margin: -2em; } -/* line 165, inputs/operators.scss */ +/* line 169, inputs/operators.scss */ .alt .g { margin: -2em; } -/* line 166, inputs/operators.scss */ +/* line 170, inputs/operators.scss */ .alt .h { margin: -2em; } -/* line 167, inputs/operators.scss */ +/* line 171, inputs/operators.scss */ .alt .i { margin: 0 -2em; } -/* line 168, inputs/operators.scss */ +/* line 172, inputs/operators.scss */ .alt .j { margin: 0 -2em; } From 7099e1f69dac59f34d2dec1aff54e55bb53911c1 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Fri, 6 Nov 2015 23:08:04 -0500 Subject: [PATCH 111/484] probably fixes #372 - strip unprintables from buffer --- src/Parser.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Parser.php b/src/Parser.php index 573f5c30..0a64e241 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -178,7 +178,7 @@ public function parse($buffer) $this->env = null; $this->inParens = false; $this->eatWhiteDefault = true; - $this->buffer = $buffer; + $this->buffer = rtrim($buffer, "\x00..\x1f"); $this->pushBlock(null); // root block From e485594b658cd10eec3b30e4ad3e7c20664c9c1e Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Fri, 6 Nov 2015 14:34:52 -0500 Subject: [PATCH 112/484] Node\Number abstraction --- scss.inc.php | 1 + src/Compiler.php | 159 ++++++++++------------------- src/Node.php | 14 +++ src/Node/Number.php | 236 ++++++++++++++++++++++++++++++++++++++++++++ src/Parser.php | 3 +- 5 files changed, 306 insertions(+), 107 deletions(-) create mode 100644 src/Node/Number.php diff --git a/scss.inc.php b/scss.inc.php index a8fe358d..d2871155 100644 --- a/scss.inc.php +++ b/scss.inc.php @@ -18,6 +18,7 @@ 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'; diff --git a/src/Compiler.php b/src/Compiler.php index 68be1725..8df83575 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -16,6 +16,7 @@ use Leafo\ScssPhp\Colors; use Leafo\ScssPhp\Compiler\Environment; use Leafo\ScssPhp\Formatter\OutputBlock; +use Leafo\ScssPhp\Node; use Leafo\ScssPhp\Type; use Leafo\ScssPhp\Parser; use Leafo\ScssPhp\Util; @@ -91,21 +92,6 @@ class Compiler 'function' => '^', ); - /** - * @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(Type::T_KEYWORD, 'true'); static public $false = array(Type::T_KEYWORD, 'false'); static public $null = array(Type::T_NULL); @@ -128,7 +114,6 @@ class Compiler 'global-variable-shadowing' => false, ); - protected $numberPrecision = 5; protected $lineNumberStyle = null; protected $formatter = 'Leafo\ScssPhp\Formatter\Nested'; @@ -314,13 +299,13 @@ protected function flattenSelectors(OutputBlock $block, $parentKey = null) // remove duplicates array_walk($selectors, function (&$value) { - $value = json_encode($value); + $value = serialize($value); }); $selectors = array_unique($selectors); array_walk($selectors, function (&$value) { - $value = json_decode($value); + $value = unserialize($value); }); } } @@ -1576,7 +1561,7 @@ protected function compileChild($child, OutputBlock $out) break; } - $this->set($for->var, array(Type::T_NUMBER, $start, '')); + $this->set($for->var, new Node\Number($start, '')); $start += $d; $ret = $this->compileChildren($for->children, $out); @@ -1797,8 +1782,11 @@ protected function reduce($value, $inExp = false) $right = $this->reduce($right, true); } - // special case: looks like css short-hand - if ($opName === 'div' && ! $inParens && ! $inExp && isset($right[2]) && $right[2] !== '') { + // 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())) + ) { return $this->expToString($value); } @@ -1831,22 +1819,22 @@ protected function reduce($value, $inExp = false) if (! isset($genOp) && $left[0] === Type::T_NUMBER && $right[0] === Type::T_NUMBER ) { - if ($opName === 'mod' && $right[2] !== '') { - $this->throwError("Cannot modulo by a number with units: $right[1]$right[2]."); + if ($opName === 'mod' && ! $right->unitless()) { + $this->throwError("Cannot modulo by a number with units: %s%s", $right[1], $right->unitStr()); } $unitChange = true; - $emptyUnit = $left[2] === '' || $right[2] === ''; - $targetUnit = '' !== $left[2] ? $left[2] : $right[2]; + $emptyUnit = $left->unitless() || $right->unitless(); + $targetUnit = $left->unitless() ? $right[2] : $left[2]; if ($opName !== 'mul') { - $left[2] = '' !== $left[2] ? $left[2] : $targetUnit; - $right[2] = '' !== $right[2] ? $right[2] : $targetUnit; + $left[2] = $left->unitless() ? $targetUnit : $left[2]; + $right[2] = $right->unitless() ? $targetUnit : $right[2]; } if ($opName !== 'mod') { - $left = $this->normalizeNumber($left); - $right = $this->normalizeNumber($right); + $left = $left->normalize(); + $right = $right->normalize(); } if ($opName === 'div' && ! $emptyUnit && $left[2] === $right[2]) { @@ -1854,8 +1842,8 @@ protected function reduce($value, $inExp = false) } if ($opName === 'mul') { - $left[2] = '' !== $left[2] ? $left[2] : $right[2]; - $right[2] = '' !== $right[2] ? $right[2] : $left[2]; + $left[2] = $left->unitless() ? $right[2] : $left[2]; + $right[2] = $right->unitless() ? $left[2] : $right[2]; } elseif ($opName === 'div' && $left[2] === $right[2]) { $left[2] = ''; $right[2] = ''; @@ -1872,7 +1860,7 @@ protected function reduce($value, $inExp = false) if (isset($out)) { if ($unitChange && $out[0] === Type::T_NUMBER) { - $out = $this->coerceUnit($out, $targetUnit); + $out = $out->coerce($targetUnit); } return $out; @@ -1890,12 +1878,10 @@ protected function reduce($value, $inExp = false) if ($exp[0] === Type::T_NUMBER) { switch ($op) { case '+': - return $exp; + return new Node\Number($exp[1], $exp[2]); case '-': - $exp[1] *= -1; - - return $exp; + return new Node\Number(-$exp[1], $exp[2]); } } @@ -1938,7 +1924,7 @@ protected function reduce($value, $inExp = false) case Type::T_STRING: foreach ($value[2] as &$item) { - if (is_array($item)) { + if (is_array($item) || $item instanceof \ArrayAccess) { $item = $this->reduce($item); } } @@ -2034,7 +2020,7 @@ public function normalizeValue($value) return array($type, '"', array($this->compileStringContent($value))); case Type::T_NUMBER: - return $this->normalizeNumber($value); + return $value->normalize(); case Type::T_INTERPOLATE: return array(Type::T_KEYWORD, $this->compileValue($value)); @@ -2044,26 +2030,6 @@ public function normalizeValue($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(Type::T_NUMBER, $value / $conv, 'in'); - } - - return $number; - } - /** * Add numbers * @@ -2074,7 +2040,7 @@ protected function normalizeNumber($number) */ protected function opAddNumberNumber($left, $right) { - return array(Type::T_NUMBER, $left[1] + $right[1], $left[2]); + return new Node\Number($left[1] + $right[1], $left[2]); } /** @@ -2087,7 +2053,7 @@ protected function opAddNumberNumber($left, $right) */ protected function opMulNumberNumber($left, $right) { - return array(Type::T_NUMBER, $left[1] * $right[1], $left[2]); + return new Node\Number($left[1] * $right[1], $left[2]); } /** @@ -2100,7 +2066,7 @@ protected function opMulNumberNumber($left, $right) */ protected function opSubNumberNumber($left, $right) { - return array(Type::T_NUMBER, $left[1] - $right[1], $left[2]); + return new Node\Number($left[1] - $right[1], $left[2]); } /** @@ -2117,7 +2083,7 @@ protected function opDivNumberNumber($left, $right) return array(Type::T_STRING, '', array($left[1] . $left[2] . '/' . $right[1] . $right[2])); } - return array(Type::T_NUMBER, $left[1] / $right[1], $left[2]); + return new Node\Number($left[1] / $right[1], $left[2]); } /** @@ -2130,7 +2096,7 @@ protected function opDivNumberNumber($left, $right) */ protected function opModNumberNumber($left, $right) { - return array(Type::T_NUMBER, $left[1] % $right[1], $left[2]); + return new Node\Number($left[1] % $right[1], $left[2]); } /** @@ -2416,7 +2382,7 @@ protected function opCmpNumberNumber($left, $right) { $n = $left[1] - $right[1]; - return array(Type::T_NUMBER, $n ? $n / abs($n) : 0, ''); + return new Node\Number($n ? $n / abs($n) : 0, ''); } /** @@ -2485,7 +2451,7 @@ public function compileValue($value) return $h; case Type::T_NUMBER: - return round($value[1], $this->numberPrecision) . $value[2]; + return (string) $value; case Type::T_STRING: return $value[1] . $this->compileStringContent($value) . $value[1]; @@ -2598,7 +2564,7 @@ protected function compileStringContent($string) $parts = array(); foreach ($string[2] as $part) { - if (is_array($part)) { + if (is_array($part) || $part instanceof \ArrayAccess) { $parts[] = $this->compileValue($part); } else { $parts[] = $part; @@ -3066,7 +3032,7 @@ public function setImportPaths($path) */ public function setNumberPrecision($numberPrecision) { - $this->numberPrecision = $numberPrecision; + Node\Number::$precision = $numberPrecision; } /** @@ -3533,7 +3499,7 @@ protected function applyArguments($argDef, $argValues) */ private function coerceValue($value) { - if (is_array($value)) { + if (is_array($value) || $value instanceof \ArrayAccess) { return $value; } @@ -3546,7 +3512,7 @@ private function coerceValue($value) } if (is_numeric($value)) { - return array(Type::T_NUMBER, $value, ''); + return new Node\Number($value, ''); } if ($value === '') { @@ -3556,25 +3522,6 @@ private function coerceValue($value) return array(Type::T_KEYWORD, $value); } - /** - * Coerce unit on number to be normalized - * - * @param array $number - * @param string $unit - * - * @return array - */ - protected function coerceUnit($number, $unit) - { - list(, $value, $baseUnit) = $number; - - if (isset(self::$unitTable[$baseUnit][$unit])) { - $value = $value * self::$unitTable[$baseUnit][$unit]; - } - - return array(Type::T_NUMBER, $value, $unit); - } - /** * Coerce something to map * @@ -4234,7 +4181,7 @@ protected function libHue($args) $color = $this->assertColor($args[0]); $hsl = $this->toHSL($color[1], $color[2], $color[3]); - return array(Type::T_NUMBER, $hsl[1], 'deg'); + return new Node\Number($hsl[1], 'deg'); } protected static $libSaturation = array('color'); @@ -4243,7 +4190,7 @@ protected function libSaturation($args) $color = $this->assertColor($args[0]); $hsl = $this->toHSL($color[1], $color[2], $color[3]); - return array(Type::T_NUMBER, $hsl[2], '%'); + return new Node\Number($hsl[2], '%'); } protected static $libLightness = array('color'); @@ -4252,7 +4199,7 @@ protected function libLightness($args) $color = $this->assertColor($args[0]); $hsl = $this->toHSL($color[1], $color[2], $color[3]); - return array(Type::T_NUMBER, $hsl[3], '%'); + return new Node\Number($hsl[3], '%'); } protected function adjustHsl($color, $idx, $amount) @@ -4305,7 +4252,7 @@ protected function libSaturate($args) } $color = $this->assertColor($value); - $amount = 100*$this->coercePercent($args[1]); + $amount = 100 * $this->coercePercent($args[1]); return $this->adjustHsl($color, 2, $amount); } @@ -4314,7 +4261,7 @@ protected function libSaturate($args) 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); } @@ -4419,7 +4366,7 @@ protected function libQuote($args) protected static $libPercentage = array('value'); protected function libPercentage($args) { - return array(Type::T_NUMBER, $this->coercePercent($args[0]) * 100, '%'); + return new Node\Number($this->coercePercent($args[0]) * 100, '%'); } protected static $libRound = array('value'); @@ -4504,13 +4451,13 @@ protected function getNormalizedNumbers($args) $this->throwError('%s is not a number', $item[0]); } - $number = $this->normalizeNumber($item); + $number = $item->normalize(); if (null === $unit) { $unit = $number[2]; - $originalUnit = $item[2]; + $originalUnit = $item->unitStr(); } elseif ($unit !== $number[2]) { - $this->throwError('Incompatible units: "%s" and "%s".', $originalUnit, $item[2]); + $this->throwError('Incompatible units: "%s" and "%s".', $originalUnit, $item->unitStr()); } $numbers[$key] = $number; @@ -4760,7 +4707,7 @@ protected function libUnit($args) $num = $args[0]; if ($num[0] === Type::T_NUMBER) { - return array(Type::T_STRING, '"', array($num[2])); + return array(Type::T_STRING, '"', array($num->unitStr())); } return ''; @@ -4771,7 +4718,7 @@ protected function libUnitless($args) { $value = $args[0]; - return $value[0] === Type::T_NUMBER && empty($value[2]); + return $value[0] === Type::T_NUMBER && $value->unitless(); } protected static $libComparable = array('number-1', 'number-2'); @@ -4785,10 +4732,10 @@ protected function libComparable($args) $this->throwError('Invalid argument(s) for "comparable"'); } - $number1 = $this->normalizeNumber($number1); - $number2 = $this->normalizeNumber($number2); + $number1 = $number1->normalize(); + $number2 = $number2->normalize(); - return $number1[2] === $number2[2] || $number1[2] === '' || $number2[2] === ''; + return $number1[2] === $number2[2] || $number1->unitless() || $number2->unitless(); } protected static $libStrIndex = array('string', 'substring'); @@ -4802,7 +4749,7 @@ protected function libStrIndex($args) $result = strpos($stringContent, $substringContent); - return $result === false ? self::$null : array(Type::T_NUMBER, $result + 1, ''); + return $result === false ? self::$null : new Node\Number($result + 1, ''); } protected static $libStrInsert = array('string', 'insert', 'index'); @@ -4827,7 +4774,7 @@ protected function libStrLength($args) $string = $this->coerceString($args[0]); $stringContent = $this->compileStringContent($string); - return array(Type::T_NUMBER, strlen($stringContent), ''); + return new Node\Number(strlen($stringContent), ''); } protected static $libStrSlice = array('string', 'start-at', 'end-at'); @@ -4952,10 +4899,10 @@ protected function libRandom($args) $this->throwError("limit must be greater than or equal to 1"); } - return array(Type::T_NUMBER, mt_rand(1, $n), ''); + return new Node\Number(mt_rand(1, $n), ''); } - return array(Type::T_NUMBER, mt_rand(1, mt_getrandmax()), ''); + return new Node\Number(mt_rand(1, mt_getrandmax()), ''); } protected function libUniqueId() diff --git a/src/Node.php b/src/Node.php index 107ee574..d09f597e 100644 --- a/src/Node.php +++ b/src/Node.php @@ -18,4 +18,18 @@ */ abstract class Node { + /** + * @var string + */ + public $type; + + /** + * @var integer + */ + public $sourcePosition; + + /** + * @var integer + */ + public $sourceIndex; } diff --git a/src/Node/Number.php b/src/Node/Number.php new file mode 100644 index 00000000..a0abcd2b --- /dev/null +++ b/src/Node/Number.php @@ -0,0 +1,236 @@ + + */ +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 = array( + 'in' => array( + 'in' => 1, + 'pc' => 6, + 'pt' => 72, + 'px' => 96, + 'cm' => 2.54, + 'mm' => 25.4, + 'q' => 101.6, + ), + 'turn' => array( + 'deg' => 180, + 'grad' => 200, + 'rad' => M_PI, + 'turn' => 0.5, + ), + 's' => array( + 's' => 1, + 'ms' => 1000, + ), + 'Hz' => array( + 'Hz' => 1, + 'kHz' => 0.001, + ), + 'dpi' => array( + 'dpi' => 1, + 'dpcm' => 2.54, + 'dppx' => 96, + ), + ); + + /** + * @var integer|float + */ + public $dimension; + + /** + * @var string + */ + public $units; + + /** + * Initialize number + * + * @param mixed $dimension + * @param string $initialUnit + */ + public function __construct($dimension, $initialUnit) + { + $this->type = Type::T_NUMBER; + $this->dimension = $dimension; + $this->units = $initialUnit; + } + + /** + * Coerce number to target units + * + * @param array $units + * + * @return \Leafo\ScssPhp\Node\Number + */ + public function coerce($units) + { + $value = $this->dimension; + + if (isset(self::$unitTable[$this->units][$units])) { + $value *= self::$unitTable[$this->units][$units]; + } + + return new Number($value, $units); + } + + /** + * Normalize number + * + * @return \Leafo\ScssPhp\Node\Number + */ + public function normalize() + { + if (isset(self::$unitTable['in'][$this->units])) { + $conv = self::$unitTable['in'][$this->units]; + + return new Number($this->dimension / $conv, 'in'); + } + + return new Number($this->dimension, $this->units); + } + + /** + * {@inheritdoc} + */ + public function offsetExists($offset) + { + if ($offset === -2) { + return $sourceIndex !== null; + } + + if ($offset === -1 + || $offset === 0 + || $offset === 1 + || $offset === 2 + ) { + return true; + } + + return false; + } + + /** + * {@inheritdoc} + */ + public function offsetGet($offset) + { + switch ($offset) { + case -2: + return $this->sourceIndex; + + case -1: + return $this->sourcePosition; + + 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->sourcePosition = $value; + } elseif ($offset == -2) { + $this->sourceIndex = $value; + } + } + + /** + * {@inheritdoc} + */ + public function offsetUnset($offset) + { + if ($offset === 1) { + $this->dimension = null; + } elseif ($offset === 2) { + $this->units = null; + } elseif ($offset === -1) { + $this->sourcePosition = null; + } elseif ($offset === -2) { + $this->sourceIndex = null; + } + } + + /** + * Returns true if the number is unitless + * + * @return boolean + */ + public function unitless() + { + return empty($this->units); + } + + /** + * Returns unit(s) as the product of numerator units divided by the product of denominator units + * + * @return string + */ + public function unitStr() + { + return $this->units; + } + + /** + * {@inheritdoc} + */ + public function __toString() + { + $value = round($this->dimension, self::$precision); + + if (empty($this->units)) { + return (string) $value; + } + + return (string) $value . $this->units; + } +} diff --git a/src/Parser.php b/src/Parser.php index 0a64e241..f613a6a6 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -13,6 +13,7 @@ use Leafo\ScssPhp\Block; use Leafo\ScssPhp\Compiler; +use Leafo\ScssPhp\Node; use Leafo\ScssPhp\Type; /** @@ -1678,7 +1679,7 @@ protected function color(&$out) protected function unit(&$unit) { if ($this->match('([0-9]*(\.)?[0-9]+)([%a-zA-Z]+)?', $m)) { - $unit = array(Type::T_NUMBER, $m[1], empty($m[3]) ? '' : $m[3]); + $unit = new Node\Number($m[1], empty($m[3]) ? '' : $m[3]); return true; } From 4d25d6dee8285b768ad6064c876184786b570565 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sat, 7 Nov 2015 12:20:56 -0500 Subject: [PATCH 113/484] phpcs --- src/Compiler.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Compiler.php b/src/Compiler.php index 8df83575..e547fa05 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -1820,7 +1820,11 @@ protected function reduce($value, $inExp = false) $left[0] === Type::T_NUMBER && $right[0] === Type::T_NUMBER ) { if ($opName === 'mod' && ! $right->unitless()) { - $this->throwError("Cannot modulo by a number with units: %s%s", $right[1], $right->unitStr()); + $this->throwError( + 'Cannot modulo by a number with units: %s%s', + $right[1], + $right->unitStr() + ); } $unitChange = true; From e969f521d3fdcc618cb23858bdd32853987d6fcb Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sat, 7 Nov 2015 12:42:27 -0500 Subject: [PATCH 114/484] update scss_test.rb from sass/sass --- tests/scss_test.rb | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/tests/scss_test.rb b/tests/scss_test.rb index de3e90e6..3d849d8a 100644 --- a/tests/scss_test.rb +++ b/tests/scss_test.rb @@ -457,6 +457,15 @@ def test_line_comment_in_script SCSS end + def test_static_hyphenated_unit + assert_equal < :compressed)) +@import url("fallback-layout.css") supports(not (display: flex)) +CSS +$display-type: flex; +@import url("fallback-layout.css") supports(not (display: #{$display-type})); +SASS + end + + def test_import_with_supports_clause + assert_equal(< :compressed)) +@import url("fallback-layout.css") supports(not (display: flex));.foo{bar:baz} +CSS +@import url("fallback-layout.css") supports(not (display: flex)); +.foo { bar: baz; } +SASS + end + end From bec3a7ac2182b830f8dedc8146ce09d20786c5fb Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sun, 8 Nov 2015 14:41:35 -0500 Subject: [PATCH 115/484] fixes #361 - remove deprecated classmap.php file --- box.json.dist | 1 - classmap.php | 63 --------------------------------------------------- composer.json | 1 - scss.inc.php | 1 - 4 files changed, 66 deletions(-) delete mode 100644 classmap.php diff --git a/box.json.dist b/box.json.dist index 9e61a65e..63d13baf 100644 --- a/box.json.dist +++ b/box.json.dist @@ -2,7 +2,6 @@ "chmod": "0755", "files": [ "LICENSE.md", - "classmap.php", "scss.inc.php" ], "directories": ["src"], diff --git a/classmap.php b/classmap.php deleted file mode 100644 index 2d8a52b8..00000000 --- a/classmap.php +++ /dev/null @@ -1,63 +0,0 @@ - Date: Sun, 8 Nov 2015 14:59:36 -0500 Subject: [PATCH 116/484] fixes #360 - raise php requirement to 5.4 --- .travis.yml | 1 - bin/pscss | 4 ++-- composer.json | 2 +- scss.inc.php | 4 ++-- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8c3a2996..aa251129 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,6 @@ language: php sudo: false php: - - 5.3 - 5.4 - 5.5 - 5.6 diff --git a/bin/pscss b/bin/pscss index f4b7ba8f..4dd5136a 100755 --- a/bin/pscss +++ b/bin/pscss @@ -12,8 +12,8 @@ error_reporting(E_ALL); -if (version_compare(PHP_VERSION, '5.3') < 0) { - die('Requires PHP 5.3 or above'); +if (version_compare(PHP_VERSION, '5.4') < 0) { + die('Requires PHP 5.4 or above'); } include __DIR__ . '/../scss.inc.php'; diff --git a/composer.json b/composer.json index 4a1a4e13..691a68a2 100644 --- a/composer.json +++ b/composer.json @@ -21,7 +21,7 @@ "psr-4": { "Leafo\\ScssPhp\\Test\\": "tests/" } }, "require": { - "php": ">=5.3.0" + "php": ">=5.4.0" }, "require-dev": { "squizlabs/php_codesniffer": "~2.3", diff --git a/scss.inc.php b/scss.inc.php index 4d2e6152..b7409a48 100644 --- a/scss.inc.php +++ b/scss.inc.php @@ -1,6 +1,6 @@ Date: Sun, 8 Nov 2015 16:15:14 -0500 Subject: [PATCH 117/484] prep for 0.4.0 release --- src/Version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Version.php b/src/Version.php index f5769285..9216885a 100644 --- a/src/Version.php +++ b/src/Version.php @@ -18,5 +18,5 @@ */ class Version { - const VERSION = 'v0.3.3'; + const VERSION = 'v0.4.0'; } From 61448e5b75020b496064b0a240e87fbe9767db39 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sun, 8 Nov 2015 17:16:36 -0500 Subject: [PATCH 118/484] internals: minor refactoring --- src/Compiler.php | 4 ++-- src/Parser.php | 55 ++++++++++++++++++++++++++++++------------------ 2 files changed, 37 insertions(+), 22 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index e547fa05..7cf11465 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -2191,7 +2191,7 @@ protected function opColorColor($op, $left, $right) { $out = array(Type::T_COLOR); - foreach (range(1, 3) as $i) { + foreach (array(1, 2, 3) as $i) { $lval = isset($left[$i]) ? $left[$i] : 0; $rval = isset($right[$i]) ? $right[$i] : 0; @@ -3754,7 +3754,7 @@ public function assertNumber($value) */ protected function fixColor($c) { - foreach (range(1, 3) as $i) { + foreach (array(1, 2, 3) as $i) { if ($c[$i] < 0) { $c[$i] = 0; } diff --git a/src/Parser.php b/src/Parser.php index f613a6a6..3679f6da 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -1645,20 +1645,20 @@ protected function color(&$out) if ($this->match('(#([0-9a-f]{6})|#([0-9a-f]{3}))', $m)) { if (isset($m[3])) { - $num = $m[3]; - $width = 16; - } else { - $num = $m[2]; - $width = 256; - } + $num = hexdec($m[3]); - $num = hexdec($num); - - foreach (array(3, 2, 1) as $i) { - $t = $num % $width; - $num /= $width; + foreach (array(3, 2, 1) as $i) { + $t = $num & 0xf; + $color[$i] = $t << 4 | $t; + $num >>= 4; + } + } else { + $num = hexdec($m[2]); - $color[$i] = $t * (256/$width) + $t * floor(16/$width); + foreach (array(3, 2, 1) as $i) { + $color[$i] = $num & 0xff; + $num >>= 8; + } } $out = $color; @@ -1894,11 +1894,13 @@ protected function interpolation(&$out, $lookWhite = true) if ($this->eatWhiteDefault) { $this->whitespace(); } + return true; } $this->seek($s); $this->eatWhiteDefault = $oldWhite; + return false; } @@ -1920,14 +1922,21 @@ protected function propertyName(&$out) for (;;) { if ($this->interpolation($inter)) { $parts[] = $inter; - } elseif ($this->keyword($text)) { + continue; + } + + if ($this->keyword($text)) { $parts[] = $text; - } elseif (count($parts) === 0 && $this->match('[:.#]', $m, false)) { + continue; + } + + if (count($parts) === 0 && $this->match('[:.#]', $m, false)) { // css hacks $parts[] = $m[0]; - } else { - break; + continue; } + + break; } $this->eatWhiteDefault = $oldWhite; @@ -2006,15 +2015,21 @@ protected function selector(&$out) for (;;) { if ($this->match('[>+~]+', $m)) { $selector[] = array($m[0]); - } elseif ($this->selectorSingle($part)) { + continue; + } + + if ($this->selectorSingle($part)) { $selector[] = $part; $this->match('\s+', $m); - } elseif ($this->match('\/[^\/]+\/', $m)) { + continue; + } + + if ($this->match('\/[^\/]+\/', $m)) { $selector[] = array($m[0]); - } else { - break; + continue; } + break; } if (count($selector) === 0) { From c96cd5fe32154e3e7dfa6a86fbcad125df2e01de Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sun, 8 Nov 2015 21:02:18 -0500 Subject: [PATCH 119/484] internals: reduce pregQuote() use --- src/Parser.php | 79 +++++++++++++++++--------------------------------- 1 file changed, 26 insertions(+), 53 deletions(-) diff --git a/src/Parser.php b/src/Parser.php index 3679f6da..cbf86829 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -47,33 +47,9 @@ class Parser '%' => 6, ); - /** - * @var array - */ - protected static $operators = array( - '+', - '-', - '*', - '/', - '%', - '==', - '!=', - '<=>', - '<=', - '>=', - '<', - '>', - 'and', - 'or', - ); - - protected static $operatorStr; + protected static $commentPattern; + protected static $operatorPattern; protected static $whitePattern; - protected static $commentMulti; - - protected static $commentSingle = '//'; - protected static $commentMultiLeft = '/*'; - protected static $commentMultiRight = '*/'; private $sourceName; private $sourceIndex; @@ -98,14 +74,15 @@ public function __construct($sourceName, $sourceIndex = 0) $this->sourceIndex = $sourceIndex; $this->charset = null; - if (empty(self::$operatorStr)) { - self::$operatorStr = '(' . implode('|', array_map(array($this, 'pregQuote'), self::$operators)) . ')'; + if (empty(self::$operatorPattern)) { + self::$operatorPattern = '([*\/%+-]|[!=]\=|\>\=?|\<\=\>|\<\=?|and|or)'; - $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'; + $commentSingle = '\/\/'; + $commentMultiLeft = '\/\*'; + $commentMultiRight = '\*\/'; + + self::$commentPattern = $commentMultiLeft . '.*?' . $commentMultiRight; + self::$whitePattern = '/' . $commentSingle . '[^\n]*\s*|(' . self::$commentPattern . ')\s*|\s+/Ais'; } } @@ -250,18 +227,6 @@ public function parseSelector($buffer, &$out) return $this->selectors($out); } - /** - * Quote regular expression - * - * @param string $what - * - * @return string - */ - public static function pregQuote($what) - { - return preg_quote($what, '/'); - } - /** * Parse a single chunk off the head of the buffer and append it to the * current parse environment. @@ -1257,13 +1222,13 @@ protected function expression(&$out) */ protected function expHelper($lhs, $minP) { - $opstr = self::$operatorStr; + $operators = self::$operatorPattern; $ss = $this->seek(); $whiteBefore = isset($this->buffer[$this->count - 1]) && ctype_space($this->buffer[$this->count - 1]); - while ($this->match($opstr, $m, false) && self::$precedence[$m[1]] >= $minP) { + while ($this->match($operators, $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]) && @@ -1283,7 +1248,7 @@ protected function expHelper($lhs, $minP) } // peek and see if rhs belongs to next operator - if ($this->peek($opstr, $next) && self::$precedence[$next[1]] > self::$precedence[$op]) { + if ($this->peek($operators, $next) && self::$precedence[$next[1]] > self::$precedence[$op]) { $rhs = $this->expHelper($rhs, self::$precedence[$next[1]]); } @@ -1806,11 +1771,7 @@ protected function openString($end, &$out, $nestingOpen = null) $oldWhite = $this->eatWhiteDefault; $this->eatWhiteDefault = false; - $stop = array('\'', '"', '#{', $end); - $stop = array_map(array($this, 'pregQuote'), $stop); - $stop[] = self::$commentMulti; - - $patt = '(.*?)(' . implode('|', $stop) . ')'; + $patt = '(.*?)([\'"]|#\{|' . $this->pregQuote($end) . '|' . self::$commentPattern . ')'; $nestingLevel = 0; @@ -2401,4 +2362,16 @@ protected function show() return ''; } + + /** + * Quote regular expression + * + * @param string $what + * + * @return string + */ + private function pregQuote($what) + { + return preg_quote($what, '/'); + } } From 1323ac596fc7443768c6ab3c7fd4342288b67ca0 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Mon, 9 Nov 2015 09:44:00 -0500 Subject: [PATCH 120/484] Revert "fixes #361 - remove deprecated classmap.php file" This reverts commit bec3a7ac2182b830f8dedc8146ce09d20786c5fb. --- box.json.dist | 1 + classmap.php | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++ composer.json | 1 + scss.inc.php | 1 + 4 files changed, 66 insertions(+) create mode 100644 classmap.php diff --git a/box.json.dist b/box.json.dist index 63d13baf..9e61a65e 100644 --- a/box.json.dist +++ b/box.json.dist @@ -2,6 +2,7 @@ "chmod": "0755", "files": [ "LICENSE.md", + "classmap.php", "scss.inc.php" ], "directories": ["src"], diff --git a/classmap.php b/classmap.php new file mode 100644 index 00000000..2d8a52b8 --- /dev/null +++ b/classmap.php @@ -0,0 +1,63 @@ + Date: Mon, 9 Nov 2015 09:44:09 -0500 Subject: [PATCH 121/484] Revert "fixes #360 - raise php requirement to 5.4" This reverts commit 90996f57ea08cf12003c4292714bc60dcf081ee7. --- .travis.yml | 1 + bin/pscss | 4 ++-- composer.json | 2 +- scss.inc.php | 4 ++-- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index aa251129..8c3a2996 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,6 +2,7 @@ language: php sudo: false php: + - 5.3 - 5.4 - 5.5 - 5.6 diff --git a/bin/pscss b/bin/pscss index 4dd5136a..f4b7ba8f 100755 --- a/bin/pscss +++ b/bin/pscss @@ -12,8 +12,8 @@ error_reporting(E_ALL); -if (version_compare(PHP_VERSION, '5.4') < 0) { - die('Requires PHP 5.4 or above'); +if (version_compare(PHP_VERSION, '5.3') < 0) { + die('Requires PHP 5.3 or above'); } include __DIR__ . '/../scss.inc.php'; diff --git a/composer.json b/composer.json index cd386f82..c63339c3 100644 --- a/composer.json +++ b/composer.json @@ -22,7 +22,7 @@ "psr-4": { "Leafo\\ScssPhp\\Test\\": "tests/" } }, "require": { - "php": ">=5.4.0" + "php": ">=5.3.0" }, "require-dev": { "squizlabs/php_codesniffer": "~2.3", diff --git a/scss.inc.php b/scss.inc.php index de741d06..d2871155 100644 --- a/scss.inc.php +++ b/scss.inc.php @@ -1,6 +1,6 @@ Date: Tue, 10 Nov 2015 15:04:59 -0500 Subject: [PATCH 122/484] fix: ! null === true --- src/Compiler.php | 2 +- tests/inputs/operators.scss | 1 + tests/outputs/operators.css | 3 +- tests/outputs_numbered/operators.css | 47 ++++++++++++++-------------- 4 files changed, 28 insertions(+), 25 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 7cf11465..b6f4953c 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -1891,7 +1891,7 @@ protected function reduce($value, $inExp = false) if ($op === 'not') { if ($inExp || $inParens) { - if ($exp === self::$false) { + if ($exp === self::$false || $exp === self::$null) { return self::$true; } diff --git a/tests/inputs/operators.scss b/tests/inputs/operators.scss index 2b4fea4f..0d881c09 100644 --- a/tests/inputs/operators.scss +++ b/tests/inputs/operators.scss @@ -122,6 +122,7 @@ div { not: (not 1); not: (not ""); not: (not hello); + not: (not null); } #string-test { diff --git a/tests/outputs/operators.css b/tests/outputs/operators.css index 3a236c57..3c63406e 100644 --- a/tests/outputs/operators.css +++ b/tests/outputs/operators.css @@ -94,7 +94,8 @@ div { not: false; not: false; not: false; - not: false; } + not: false; + not: true; } #string-test { str: true; diff --git a/tests/outputs_numbered/operators.css b/tests/outputs_numbered/operators.css index 0d35202f..5dc5bef8 100644 --- a/tests/outputs_numbered/operators.css +++ b/tests/outputs_numbered/operators.css @@ -95,8 +95,9 @@ div { not: false; not: false; not: false; - not: false; } -/* line 127, inputs/operators.scss */ + not: false; + not: true; } +/* line 128, inputs/operators.scss */ #string-test { str: true; str: false; @@ -104,66 +105,66 @@ div { str: true; str: xhellohellofalse; str: true; } -/* line 143, inputs/operators.scss */ +/* line 144, inputs/operators.scss */ #special { cancel-unit: 1; } -/* line 150, inputs/operators.scss */ +/* line 151, inputs/operators.scss */ .row .a { margin: -0.5em; } -/* line 151, inputs/operators.scss */ +/* line 152, inputs/operators.scss */ .row .b { margin: -0.5em; } -/* line 152, inputs/operators.scss */ +/* line 153, inputs/operators.scss */ .row .c { margin: -0.5em; } -/* line 153, inputs/operators.scss */ +/* line 154, inputs/operators.scss */ .row .d { margin: -0.5em; } -/* line 154, inputs/operators.scss */ +/* line 155, inputs/operators.scss */ .row .e { margin: 0 -0.5em; } -/* line 156, inputs/operators.scss */ +/* line 157, inputs/operators.scss */ .alt .a { margin: -0.5em; } -/* line 157, inputs/operators.scss */ +/* line 158, inputs/operators.scss */ .alt .b { margin: -0.5em; } -/* line 158, inputs/operators.scss */ +/* line 159, inputs/operators.scss */ .alt .c { margin: -0.5em; } -/* line 159, inputs/operators.scss */ +/* line 160, inputs/operators.scss */ .alt .d { margin: 0 -0.5em; } -/* line 160, inputs/operators.scss */ +/* line 161, inputs/operators.scss */ .alt .e { margin: 0 -0.5em; } -/* line 162, inputs/operators.scss */ +/* line 163, inputs/operators.scss */ .row .f { margin: -2em; } -/* line 163, inputs/operators.scss */ +/* line 164, inputs/operators.scss */ .row .g { margin: -2em; } -/* line 164, inputs/operators.scss */ +/* line 165, inputs/operators.scss */ .row .h { margin: -2em; } -/* line 165, inputs/operators.scss */ +/* line 166, inputs/operators.scss */ .row .i { margin: -2em; } -/* line 166, inputs/operators.scss */ +/* line 167, inputs/operators.scss */ .row .j { margin: 0 -2em; } -/* line 168, inputs/operators.scss */ +/* line 169, inputs/operators.scss */ .alt .f { margin: -2em; } -/* line 169, inputs/operators.scss */ +/* line 170, inputs/operators.scss */ .alt .g { margin: -2em; } -/* line 170, inputs/operators.scss */ +/* line 171, inputs/operators.scss */ .alt .h { margin: -2em; } -/* line 171, inputs/operators.scss */ +/* line 172, inputs/operators.scss */ .alt .i { margin: 0 -2em; } -/* line 172, inputs/operators.scss */ +/* line 173, inputs/operators.scss */ .alt .j { margin: 0 -2em; } From 61bf507f69ff91c1591629520b7c9665b66d514b Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 10 Nov 2015 17:18:45 -0500 Subject: [PATCH 123/484] str-slice() compatibility fixes --- src/Compiler.php | 36 +++++++++++++++++++++-------- tests/inputs/builtins.scss | 16 +++++++++++++ tests/outputs/builtins.css | 17 +++++++++++++- tests/outputs_numbered/builtins.css | 33 ++++++++++++++++++-------- 4 files changed, 82 insertions(+), 20 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index b6f4953c..3f0c3ac3 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -95,6 +95,7 @@ class Compiler static public $true = array(Type::T_KEYWORD, 'true'); static public $false = array(Type::T_KEYWORD, 'false'); static public $null = array(Type::T_NULL); + static public $nullString = array(Type::T_STRING, '', array()); static public $defaultValue = array(Type::T_KEYWORD, ''); static public $selfSelector = array(Type::T_SELF); static public $emptyList = array(Type::T_LIST, '', array()); @@ -1431,7 +1432,7 @@ protected function compileChild($child, OutputBlock $out) if ($value[0] !== Type::T_NULL) { $value = $this->reduce($value); - if ($value[0] === Type::T_NULL) { + if ($value[0] === Type::T_NULL || $value === self::$nullString) { break; } } @@ -1594,12 +1595,14 @@ protected function compileChild($child, OutputBlock $out) $prefix = $this->compileValue($prop->prefix) . '-'; foreach ($prop->children as $child) { - if ($child[0] === Type::T_ASSIGN) { - array_unshift($child[1][2], $prefix); - } + switch ($child[0]) { + case Type::T_ASSIGN: + array_unshift($child[1][2], $prefix); + break; - if ($child[0] === Type::T_NESTED_PROPERTY) { - array_unshift($child[1]->prefix[2], $prefix); + case Type::T_NESTED_PROPERTY: + array_unshift($child[1]->prefix[2], $prefix); + break; } $prefixed[] = $child; @@ -4784,17 +4787,30 @@ protected function libStrLength($args) protected static $libStrSlice = array('string', 'start-at', 'end-at'); protected function libStrSlice($args) { - if ($args[2][1] == 0) { - return self::$null; + if (isset($args[2]) && $args[2][1] == 0) { + return self::$nullString; } $string = $this->coerceString($args[0]); $stringContent = $this->compileStringContent($string); - $start = (int) $args[1][1] ?: 1; + $start = (int) $args[1][1]; + if ($start > 0) { + $start--; + } + $end = (int) $args[2][1]; + if ($end < 0) { + $end++; + } elseif ($end > 0) { + $end -= $start; + } + + if ($end === 0 || $end > strlen($stringContent)) { + $end = strlen($stringContent); + } - $string[2] = array(substr($stringContent, $start - 1, ($end < 0 ? $end : $end - $start) + 1)); + $string[2] = array(substr($stringContent, $start, $end)); return $string; } diff --git a/tests/inputs/builtins.scss b/tests/inputs/builtins.scss index 7f54d41e..32066b40 100644 --- a/tests/inputs/builtins.scss +++ b/tests/inputs/builtins.scss @@ -84,6 +84,22 @@ color: str-slice(string, 2, -2); color: to_lower_case('StRiNg'); color: to_upper_case(StRiNg); + color: str-slice(string, 0); + color: str-slice(string, 1); + color: str-slice(string, 2); + color: str-slice(string, 3); + color: str-slice(string, 4); + color: str-slice(string, 5); + color: str-slice(string, -1); + color: str-slice(string, -2); + color: str-slice(string, -3); + color: str-slice(string, -4); + color: str-slice(string, -5); + color: str-slice(string, 1, 0); + color: str-slice(string, 1, 1); + color: str-slice(string, 1, 2); + color: str-slice(string, 1, -1); + color: str-slice(string, 1, -2); } #number { diff --git a/tests/outputs/builtins.css b/tests/outputs/builtins.css index f7bc1f12..4d3ef6d8 100644 --- a/tests/outputs/builtins.css +++ b/tests/outputs/builtins.css @@ -57,7 +57,22 @@ color: tri; color: trin; color: 'string'; - color: STRING; } + color: STRING; + color: string; + color: string; + color: tring; + color: ring; + color: ing; + color: ng; + color: g; + color: ng; + color: ing; + color: ring; + color: tring; + color: s; + color: st; + color: string; + color: strin; } #number { color: 250%; diff --git a/tests/outputs_numbered/builtins.css b/tests/outputs_numbered/builtins.css index 08cbb0be..95bc7759 100644 --- a/tests/outputs_numbered/builtins.css +++ b/tests/outputs_numbered/builtins.css @@ -58,8 +58,23 @@ color: tri; color: trin; color: 'string'; - color: STRING; } -/* line 89, inputs/builtins.scss */ + color: STRING; + color: string; + color: string; + color: tring; + color: ring; + color: ing; + color: ng; + color: g; + color: ng; + color: ing; + color: ring; + color: tring; + color: s; + color: st; + color: string; + color: strin; } +/* line 105, inputs/builtins.scss */ #number { color: 250%; color: 3; @@ -70,7 +85,7 @@ width: 200%; bottom: 10px; padding: 3em 1in 96px 72pt; } -/* line 102, inputs/builtins.scss */ +/* line 118, inputs/builtins.scss */ #list { len: 3; len: 1; @@ -92,7 +107,7 @@ cool: great job one two three; zip: 1px solid, 2px dashed; zip: 1px solid red, 2px dashed green; } -/* line 138, inputs/builtins.scss */ +/* line 154, inputs/builtins.scss */ #introspection { t: number; t: string; @@ -112,31 +127,31 @@ c: true; c: false; c: true; } -/* line 162, inputs/builtins.scss */ +/* line 178, inputs/builtins.scss */ #if { color: yes; color: no; color: yes; color: yes; } -/* line 169, inputs/builtins.scss */ +/* line 185, inputs/builtins.scss */ .transparent { r: 0; g: 0; b: 0; a: 0; } -/* line 176, inputs/builtins.scss */ +/* line 192, inputs/builtins.scss */ .alpha { a: 1; a: 1; a: 1; a: 0.5; a: alpha(currentColor); } -/* line 185, inputs/builtins.scss */ +/* line 201, inputs/builtins.scss */ #exists { a: true; b: true; c: false; } -/* line 192, inputs/builtins.scss */ +/* line 208, inputs/builtins.scss */ div.call-tests { a: #0a64ff; b: #0058ef; From 8cb681f99c55c44a284142265b36b9c194aae2b0 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 10 Nov 2015 20:09:54 -0500 Subject: [PATCH 124/484] Remove hhvm support/hacks --- .travis.yml | 1 - src/Compiler.php | 6 ------ 2 files changed, 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8c3a2996..1c727fcd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,7 +7,6 @@ php: - 5.5 - 5.6 - nightly - - hhvm script: - phpunit tests diff --git a/src/Compiler.php b/src/Compiler.php index 3f0c3ac3..50cb6237 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -3956,9 +3956,6 @@ protected function libRgb($args) 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; @@ -3976,9 +3973,6 @@ protected function alterColor($args, $fn) { $color = $this->assertColor($args[0]); - // 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]); From 626cd788e350e9a7265750caa464af4e7c957d9c Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 10 Nov 2015 20:29:20 -0500 Subject: [PATCH 125/484] internals: code tweaks to libStrSlice() --- src/Compiler.php | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 50cb6237..96262659 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -4789,22 +4789,17 @@ protected function libStrSlice($args) $stringContent = $this->compileStringContent($string); $start = (int) $args[1][1]; + if ($start > 0) { $start--; } - $end = (int) $args[2][1]; - if ($end < 0) { - $end++; - } elseif ($end > 0) { - $end -= $start; - } - - if ($end === 0 || $end > strlen($stringContent)) { - $end = strlen($stringContent); - } + $end = (int) $args[2][1]; + $length = $end < 0 ? $end + 1 : ($end > 0 ? $end - $start : $end); - $string[2] = array(substr($stringContent, $start, $end)); + $string[2] = $length + ? array(substr($stringContent, $start, $length)) + : array(substr($stringContent, $start)); return $string; } From 91d857f8226a52bf1b62199d236a6dfc48df805a Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Mon, 9 Nov 2015 00:28:45 -0500 Subject: [PATCH 126/484] fixes #107 - incompatible units --- src/Compiler.php | 62 ++++++------ src/Node/Number.php | 136 +++++++++++++++++++++------ tests/ExceptionTest.php | 6 ++ tests/FailingTest.php | 14 --- tests/inputs/operators.scss | 20 +++- tests/outputs/operators.css | 7 +- tests/outputs_numbered/operators.css | 7 +- 7 files changed, 175 insertions(+), 77 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 96262659..1b666d08 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -1817,43 +1817,41 @@ protected function reduce($value, $inExp = false) is_callable(array($this, $fn)) && $genOp = true) ) { - $unitChange = false; + $coerceUnit = false; if (! isset($genOp) && $left[0] === Type::T_NUMBER && $right[0] === Type::T_NUMBER ) { - if ($opName === 'mod' && ! $right->unitless()) { - $this->throwError( - 'Cannot modulo by a number with units: %s%s', - $right[1], - $right->unitStr() - ); - } + $coerceUnit = true; - $unitChange = true; - $emptyUnit = $left->unitless() || $right->unitless(); - $targetUnit = $left->unitless() ? $right[2] : $left[2]; + switch ($opName) { + case 'mul': + $targetUnit = $left[2]; - if ($opName !== 'mul') { - $left[2] = $left->unitless() ? $targetUnit : $left[2]; - $right[2] = $right->unitless() ? $targetUnit : $right[2]; - } + foreach ($right[2] as $unit => $exp) { + @$targetUnit[$unit] += $exp; + } + break; - if ($opName !== 'mod') { - $left = $left->normalize(); - $right = $right->normalize(); - } + case 'div': + $targetUnit = $left[2]; - if ($opName === 'div' && ! $emptyUnit && $left[2] === $right[2]) { - $targetUnit = ''; + foreach ($right[2] as $unit => $exp) { + @$targetUnit[$unit] -= $exp; + } + break; + + case 'mod': + $targetUnit = $left[2]; + break; + + default: + $targetUnit = $left->unitless() ? $right[2] : $left[2]; } - if ($opName === 'mul') { - $left[2] = $left->unitless() ? $right[2] : $left[2]; - $right[2] = $right->unitless() ? $left[2] : $right[2]; - } elseif ($opName === 'div' && $left[2] === $right[2]) { - $left[2] = ''; - $right[2] = ''; + if (! $left->unitless() && ! $right->unitless()) { + $left = $left->normalize(); + $right = $right->normalize(); } } @@ -1866,7 +1864,7 @@ protected function reduce($value, $inExp = false) } if (isset($out)) { - if ($unitChange && $out[0] === Type::T_NUMBER) { + if ($coerceUnit && $out[0] === Type::T_NUMBER) { $out = $out->coerce($targetUnit); } @@ -2458,7 +2456,7 @@ public function compileValue($value) return $h; case Type::T_NUMBER: - return (string) $value; + return $value->output($this); case Type::T_STRING: return $value[1] . $this->compileStringContent($value) . $value[1]; @@ -3339,8 +3337,6 @@ function ($m) { /** * Sorts keyword arguments * - * @todo Merge with applyArguments()? - * * @param array $prototype * @param array $args * @@ -3656,7 +3652,7 @@ protected function coerceString($value) protected function coercePercent($value) { if ($value[0] === Type::T_NUMBER) { - if ($value[2] === '%') { + if (isset($value[2]['%'])) { return $value[1] / 100; } @@ -4068,7 +4064,7 @@ protected function libScaleColor($args) 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]); } diff --git a/src/Node/Number.php b/src/Node/Number.php index a0abcd2b..929d816d 100644 --- a/src/Node/Number.php +++ b/src/Node/Number.php @@ -11,6 +11,7 @@ namespace Leafo\ScssPhp\Node; +use Leafo\ScssPhp\Compiler; use Leafo\ScssPhp\Node; use Leafo\ScssPhp\Type; @@ -48,21 +49,21 @@ class Number extends Node implements \ArrayAccess 'q' => 101.6, ), 'turn' => array( - 'deg' => 180, - 'grad' => 200, - 'rad' => M_PI, - 'turn' => 0.5, + 'deg' => 360, + 'grad' => 400, + 'rad' => 6.28318530717958647692528676, // 2 * M_PI + 'turn' => 1, ), 's' => array( - 's' => 1, + 's' => 1, 'ms' => 1000, ), 'Hz' => array( - 'Hz' => 1, + 'Hz' => 1, 'kHz' => 0.001, ), 'dpi' => array( - 'dpi' => 1, + 'dpi' => 1, 'dpcm' => 2.54, 'dppx' => 96, ), @@ -74,21 +75,24 @@ class Number extends Node implements \ArrayAccess public $dimension; /** - * @var string + * @var array */ public $units; /** * Initialize number * - * @param mixed $dimension - * @param string $initialUnit + * @param mixed $dimension + * @param mixed $initialUnit */ public function __construct($dimension, $initialUnit) { $this->type = Type::T_NUMBER; $this->dimension = $dimension; - $this->units = $initialUnit; + $this->units = is_array($initialUnit) + ? $initialUnit + : ($initialUnit ? array($initialUnit => 1) + : array()); } /** @@ -100,13 +104,19 @@ public function __construct($dimension, $initialUnit) */ public function coerce($units) { - $value = $this->dimension; + if ($this->unitless()) { + return new Number($this->dimension, $units); + } + + $dimension = $this->dimension; - if (isset(self::$unitTable[$this->units][$units])) { - $value *= self::$unitTable[$this->units][$units]; + foreach (self::$unitTable['in'] as $unit => $conv) { + $exp = @$this->units[$unit] - @$units[$unit]; + $factor = pow($conv, $exp); + $dimension /= $factor; } - return new Number($value, $units); + return new Number($dimension, $units); } /** @@ -116,13 +126,12 @@ public function coerce($units) */ public function normalize() { - if (isset(self::$unitTable['in'][$this->units])) { - $conv = self::$unitTable['in'][$this->units]; + $dimension = $this->dimension; + $units = array(); - return new Number($this->dimension / $conv, 'in'); - } + $this->normalizeUnits($dimension, $units, 'in'); - return new Number($this->dimension, $this->units); + return new Number($dimension, $units); } /** @@ -207,7 +216,7 @@ public function offsetUnset($offset) */ public function unitless() { - return empty($this->units); + return ! array_sum($this->units); } /** @@ -217,7 +226,62 @@ public function unitless() */ public function unitStr() { - return $this->units; + $numerators = array(); + $denominators = array(); + + 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; + }); + + // @todo refactor normalize() + if (count($units) > 1 && array_sum($units) === 0) { + $dimension = $this->dimension; + $units = array(); + + $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; } /** @@ -225,12 +289,30 @@ public function unitStr() */ public function __toString() { - $value = round($this->dimension, self::$precision); + return $this->output(); + } - if (empty($this->units)) { - return (string) $value; - } + /** + * Normalize units + * + * @param integer|float $dimension + * @param array $units + * @param string $baseUnit + */ + private function normalizeUnits(&$dimension, &$units, $baseUnit = 'in') + { + $dimension = $this->dimension; + $units = array(); + + foreach ($this->units as $unit => $exp) { + if (isset(self::$unitTable[$baseUnit][$unit])) { + $factor = pow(self::$unitTable[$baseUnit][$unit], $exp); - return (string) $value . $this->units; + $unit = $baseUnit; + $dimension /= $factor; + } + + @$units[$unit] += $exp; + } } } diff --git a/tests/ExceptionTest.php b/tests/ExceptionTest.php index 4df1bae3..ea17abb5 100644 --- a/tests/ExceptionTest.php +++ b/tests/ExceptionTest.php @@ -123,6 +123,12 @@ public function provideScss() , 'Undefined variable $bg' ), + array(<<<'END_OF_SCSS' +div { bottom: (4/2px); } +END_OF_SCSS + , + 'isn\'t a valid CSS value' + ), ); } diff --git a/tests/FailingTest.php b/tests/FailingTest.php index 7a074a77..863ffe00 100644 --- a/tests/FailingTest.php +++ b/tests/FailingTest.php @@ -84,20 +84,6 @@ public function provideFailing() background: #eee; } .nav-bar > .item, header ul > .item, header ul > li { margin: 0 10px; } -END_OF_EXPECTED - ), - array( - '#107 - incompatible units (example 2)', <<<'END_OF_SCSS' -$gridRowWidth: 20px; - -.foo -{ -width: (2.5 / $gridRowWidth * 100px * 1% ); -} -END_OF_SCSS - , << Date: Sun, 8 Nov 2015 14:41:35 -0500 Subject: [PATCH 127/484] fixes #361 - remove deprecated classmap.php file --- box.json.dist | 1 - classmap.php | 63 --------------------------------------------------- composer.json | 1 - scss.inc.php | 1 - 4 files changed, 66 deletions(-) delete mode 100644 classmap.php diff --git a/box.json.dist b/box.json.dist index 9e61a65e..63d13baf 100644 --- a/box.json.dist +++ b/box.json.dist @@ -2,7 +2,6 @@ "chmod": "0755", "files": [ "LICENSE.md", - "classmap.php", "scss.inc.php" ], "directories": ["src"], diff --git a/classmap.php b/classmap.php deleted file mode 100644 index 2d8a52b8..00000000 --- a/classmap.php +++ /dev/null @@ -1,63 +0,0 @@ - Date: Sun, 8 Nov 2015 14:59:36 -0500 Subject: [PATCH 128/484] fixes #360 - raise php requirement to 5.4 --- .travis.yml | 1 - bin/pscss | 4 ++-- composer.json | 2 +- scss.inc.php | 4 ++-- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 1c727fcd..80b481f1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,6 @@ language: php sudo: false php: - - 5.3 - 5.4 - 5.5 - 5.6 diff --git a/bin/pscss b/bin/pscss index f4b7ba8f..4dd5136a 100755 --- a/bin/pscss +++ b/bin/pscss @@ -12,8 +12,8 @@ error_reporting(E_ALL); -if (version_compare(PHP_VERSION, '5.3') < 0) { - die('Requires PHP 5.3 or above'); +if (version_compare(PHP_VERSION, '5.4') < 0) { + die('Requires PHP 5.4 or above'); } include __DIR__ . '/../scss.inc.php'; diff --git a/composer.json b/composer.json index 4a1a4e13..691a68a2 100644 --- a/composer.json +++ b/composer.json @@ -21,7 +21,7 @@ "psr-4": { "Leafo\\ScssPhp\\Test\\": "tests/" } }, "require": { - "php": ">=5.3.0" + "php": ">=5.4.0" }, "require-dev": { "squizlabs/php_codesniffer": "~2.3", diff --git a/scss.inc.php b/scss.inc.php index 4d2e6152..b7409a48 100644 --- a/scss.inc.php +++ b/scss.inc.php @@ -1,6 +1,6 @@ Date: Wed, 11 Nov 2015 00:06:44 -0500 Subject: [PATCH 129/484] Bump version to 0.5.0 --- src/Version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Version.php b/src/Version.php index 9216885a..fb19a731 100644 --- a/src/Version.php +++ b/src/Version.php @@ -18,5 +18,5 @@ */ class Version { - const VERSION = 'v0.4.0'; + const VERSION = 'v0.5.0'; } From 27ac88271586743de9d3bb09e11a59607b8c2dff Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 11 Nov 2015 00:38:05 -0500 Subject: [PATCH 130/484] merges #341 - @scssphp-import-once --- src/Compiler.php | 57 ++++++------------------------------------------ src/Parser.php | 2 +- src/Type.php | 1 + 3 files changed, 9 insertions(+), 51 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 693359ab..15df21d6 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -1301,62 +1301,19 @@ protected function mergeMediaTypes($type1, $type2) /** * Compile import; returns true if the value was something that could be imported * - * @param array $rawPath - * @param array $out + * @param array $rawPath + * @param array $out + * @param boolean $once * * @return boolean */ - protected function compileImport($rawPath, $out) + protected function compileImport($rawPath, $out, $once = false) { if ($rawPath[0] === Type::T_STRING) { $path = $this->compileStringContent($rawPath); if ($path = $this->findImport($path)) { - $this->importFile($path, $out); - $this->importedFiles[] = $path; - - return true; - } - - return false; - } - - if ($rawPath[0] === 'list') { - // handle a list of strings - if (count($rawPath[2]) === 0) { - return false; - } - - foreach ($rawPath[2] as $path) { - if ($path[0] !== 'string') { - return false; - } - } - - foreach ($rawPath[2] as $path) { - $this->compileImport($path, $out); - } - - return true; - } - - return false; - } - - /** - * Compile importOnce; returns true if the value was something that could be imported - * - * @param array $rawPath - * @param array $out - * - * @return boolean - */ - protected function compileImportOnce($rawPath, $out) - { - if ($rawPath[0] === 'string') { - $path = $this->compileStringContent($rawPath); - if ($path = $this->findImport($path)) { - if (!in_array($path, $this->importedFiles)) { + if (! $once || ! in_array($path, $this->importedFiles)) { $this->importFile($path, $out); $this->importedFiles[] = $path; } @@ -1403,13 +1360,13 @@ protected function compileChild($child, OutputBlock $out) $this->sourcePos = isset($child[Parser::SOURCE_POSITION]) ? $child[Parser::SOURCE_POSITION] : -1; switch ($child[0]) { - case 'scssphp-import-once': + case Type::T_SCSSPHP_IMPORT_ONCE: list(, $rawPath) = $child; $rawPath = $this->reduce($rawPath); if (! $this->compileImportOnce($rawPath, $out)) { - $out->lines[] = '@scssphp-import-once ' . $this->compileValue($rawPath) . ';'; + $out->lines[] = '@import ' . $this->compileValue($rawPath) . ';'; } break; diff --git a/src/Parser.php b/src/Parser.php index 66e21a4f..9249878e 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -336,7 +336,7 @@ protected function parseChunk() $this->valueList($importPath) && $this->end() ) { - $this->append(array('scssphp-import-once', $importPath), $s); + $this->append(array(Type::T_SCSSPHP_IMPORT_ONCE, $importPath), $s); return true; } diff --git a/src/Type.php b/src/Type.php index b750bbe4..80b53258 100644 --- a/src/Type.php +++ b/src/Type.php @@ -59,6 +59,7 @@ class Type 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'; From cb0c57ec553da73f8f01255225c189c3fa05409a Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 11 Nov 2015 01:18:49 -0500 Subject: [PATCH 131/484] refs #341 - call refactored compileImport() --- src/Compiler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compiler.php b/src/Compiler.php index 15df21d6..56567e04 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -1365,7 +1365,7 @@ protected function compileChild($child, OutputBlock $out) $rawPath = $this->reduce($rawPath); - if (! $this->compileImportOnce($rawPath, $out)) { + if (! $this->compileImport($rawPath, $out, true)) { $out->lines[] = '@import ' . $this->compileValue($rawPath) . ';'; } break; From 9e0bda70b230418c854e85da4c8c29fcea8c96cc Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 11 Nov 2015 17:12:40 -0500 Subject: [PATCH 132/484] fixes #378 - avoid notices in custom error handlers that ignore @ operator --- src/Compiler.php | 4 ++-- src/Node/Number.php | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 56567e04..03190c71 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -1844,7 +1844,7 @@ protected function reduce($value, $inExp = false) $targetUnit = $left[2]; foreach ($right[2] as $unit => $exp) { - @$targetUnit[$unit] += $exp; + $targetUnit[$unit] = (isset($targetUnit[$unit]) ? $targetUnit[$unit] : 0) + $exp; } break; @@ -1852,7 +1852,7 @@ protected function reduce($value, $inExp = false) $targetUnit = $left[2]; foreach ($right[2] as $unit => $exp) { - @$targetUnit[$unit] -= $exp; + $targetUnit[$unit] = (isset($targetUnit[$unit]) ? $targetUnit[$unit] : 0) - $exp; } break; diff --git a/src/Node/Number.php b/src/Node/Number.php index 929d816d..3727a768 100644 --- a/src/Node/Number.php +++ b/src/Node/Number.php @@ -111,8 +111,9 @@ public function coerce($units) $dimension = $this->dimension; foreach (self::$unitTable['in'] as $unit => $conv) { - $exp = @$this->units[$unit] - @$units[$unit]; - $factor = pow($conv, $exp); + $from = isset($this->units[$unit]) ? $this->units[$unit] : 0; + $to = isset($units[$unit]) ? $units[$unit] : 0; + $factor = pow($conv, $from - $to); $dimension /= $factor; } @@ -312,7 +313,7 @@ private function normalizeUnits(&$dimension, &$units, $baseUnit = 'in') $dimension /= $factor; } - @$units[$unit] += $exp; + $units[$unit] = $exp + (isset($units[$unit]) ? $units[$unit] : 0); } } } From 251cd10c1c86d9df20d54d3856ba30acdbdaab5b Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 11 Nov 2015 17:15:07 -0500 Subject: [PATCH 133/484] bump version to 0.5.1 --- src/Version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Version.php b/src/Version.php index fb19a731..76f37eb3 100644 --- a/src/Version.php +++ b/src/Version.php @@ -18,5 +18,5 @@ */ class Version { - const VERSION = 'v0.5.0'; + const VERSION = 'v0.5.1'; } From cafe9b9113a7fefbdecba79ee7b58a5ff84627b4 Mon Sep 17 00:00:00 2001 From: Oleksandr Savchenko Date: Fri, 13 Nov 2015 10:15:46 +0200 Subject: [PATCH 134/484] refactor exceptions --- scss.inc.php | 2 ++ src/Compiler.php | 6 ++++-- src/Exception/CompilerException.php | 10 ++++++++++ src/Exception/ParseException.php | 10 ++++++++++ src/Parser.php | 7 ++++--- 5 files changed, 30 insertions(+), 5 deletions(-) create mode 100644 src/Exception/CompilerException.php create mode 100644 src/Exception/ParseException.php diff --git a/scss.inc.php b/scss.inc.php index b7409a48..86e41b83 100644 --- a/scss.inc.php +++ b/scss.inc.php @@ -9,6 +9,8 @@ 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/ParseException.php'; include_once __DIR__ . '/src/Formatter.php'; include_once __DIR__ . '/src/Formatter/Compact.php'; include_once __DIR__ . '/src/Formatter/Compressed.php'; diff --git a/src/Compiler.php b/src/Compiler.php index 03190c71..5d45a206 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -15,6 +15,7 @@ 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; @@ -3199,7 +3200,8 @@ public function findImport($url) * * @param string $msg Message with optional sprintf()-style vararg parameters * - * @throws \Exception + * @throws \Leafo\ScssPhp\Exception\CompilerException + * @throws \Leafo\ScssPhp\Exception\ParseException */ public function throwError($msg) { @@ -3212,7 +3214,7 @@ public function throwError($msg) $parser->throwParseError($msg, $this->sourcePos); } - throw new \Exception($msg); + throw new CompilerException($msg); } /** diff --git a/src/Exception/CompilerException.php b/src/Exception/CompilerException.php new file mode 100644 index 00000000..b52bbb95 --- /dev/null +++ b/src/Exception/CompilerException.php @@ -0,0 +1,10 @@ +peek("(.*?)(\n|$)", $m, $count)) { - throw new \Exception("$msg: failed at `$m[1]` $loc"); + throw new ParseException("$msg: failed at `$m[1]` $loc"); } - throw new \Exception("$msg: $loc"); + throw new ParseException("$msg: $loc"); } /** From e9f44a5d1c850ac33edb0d2226887a597455ee8d Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Fri, 20 Nov 2015 05:46:54 -0500 Subject: [PATCH 135/484] internals: improve consistency of new blocks --- src/Compiler.php | 55 +++++++++++++++++++++--------------------------- src/Parser.php | 2 +- 2 files changed, 25 insertions(+), 32 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 03190c71..7ed511dd 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -526,8 +526,12 @@ protected function compileMedia(Block $media) if ($needsWrap) { $wrapped = new Block; - $wrapped->selectors = array(); - $wrapped->children = $media->children; + $wrapped->sourcePosition = $media->sourcePosition; + $wrapped->sourceIndex = $media->sourceIndex; + $wrapped->selectors = array(); + $wrapped->comments = array(); + $wrapped->parent = $media; + $wrapped->children = $media->children; $media->children = array(array(Type::T_BLOCK, $wrapped)); } @@ -594,11 +598,11 @@ protected function compileAtRoot(Block $block) // wrap inline selector if ($block->selector) { $wrapped = new Block; - $wrapped->parent = $block; $wrapped->sourcePosition = $block->sourcePosition; $wrapped->sourceIndex = $block->sourceIndex; $wrapped->selectors = $block->selector; $wrapped->comments = array(); + $wrapped->parent = $block; $wrapped->children = $block->children; $block->children = array(array(Type::T_BLOCK, $wrapped)); @@ -636,7 +640,7 @@ private function spliceTree($envs, Block $block, $without) continue; } - if (isset($e->block) && $e->block === $block) { + if ($e->block === $block) { continue; } @@ -662,19 +666,26 @@ private function spliceTree($envs, Block $block, $without) } $b = new Block; + $b->sourcePosition = $e->block->sourcePosition; + $b->sourceIndex = $e->block->sourceIndex; + $b->selectors = array(); + $b->comments = $e->block->comments; + $b->parent = null; - if (isset($e->block->sourcePosition)) { - $b->sourcePosition = $e->block->sourcePosition; - } + if ($newBlock) { + $type = isset($newBlock->type) ? $newBlock->type : Type::T_BLOCK; - if (isset($e->block->sourceIndex)) { - $b->sourceIndex = $e->block->sourceIndex; - } + $b->children = array(array($type, $newBlock)); - $b->selectors = array(); + $newBlock->parent = $b; + } elseif (count($block->children)) { + foreach ($block->children as $child) { + if ($child[0] === Type::T_BLOCK) { + $child[1]->parent = $b; + } + } - if (isset($e->block->comments)) { - $b->comments = $e->block->comments; + $b->children = $block->children; } if (isset($e->block->type)) { @@ -693,24 +704,6 @@ private function spliceTree($envs, Block $block, $without) $b->value = $e->block->value; } - if ($newBlock) { - $type = isset($newBlock->type) ? $newBlock->type : Type::T_BLOCK; - - $b->children = array(array($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; - } - - $b->parent = null; - $newBlock = $b; } diff --git a/src/Parser.php b/src/Parser.php index 9249878e..afb6956f 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -691,11 +691,11 @@ protected function parseChunk() protected function pushBlock($selectors, $pos = 0) { $b = new Block; - $b->parent = $this->env; $b->sourcePosition = $pos; $b->sourceIndex = $this->sourceIndex; $b->selectors = $selectors; $b->comments = array(); + $b->parent = $this->env; if (! $this->env) { $b->children = array(); From d1d7f8211b3901a89aae2c0d2a1d5a20b2977a68 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sun, 22 Nov 2015 19:31:03 -0500 Subject: [PATCH 136/484] internals: reinitialize Compiler properties on subsequent compile() calls --- src/Compiler.php | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 7ed511dd..f6c999f4 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -154,24 +154,28 @@ public function compile($code, $path = null) $locale = setlocale(LC_NUMERIC, 0); setlocale(LC_NUMERIC, 'C'); - $this->indentLevel = -1; - $this->commentsSeen = array(); - $this->extends = array(); - $this->extendsMap = array(); - $this->parsedFiles = array(); - $this->sourceParsers = array(); - $this->sourceIndex = null; - $this->env = null; - $this->scope = null; - $this->storeEnv = null; - $this->stderr = fopen('php://stderr', 'w'); + $this->indentLevel = -1; + $this->commentsSeen = array(); + $this->extends = array(); + $this->extendsMap = array(); + $this->parsedFiles = array(); + $this->sourceParsers = array(); + $this->sourceIndex = null; + $this->sourcePos = 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); $tree = $this->parser->parse($code); $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(); From fe37dc2b4d83e8ac68acb2aa823b562829af6f58 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sat, 28 Nov 2015 15:28:00 -0500 Subject: [PATCH 137/484] Fixes #383; undefined sourceParsers when reusing a single Compiler instance --- src/Compiler.php | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index f6c999f4..76e70e8b 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -139,6 +139,15 @@ class Compiler private $stderr; private $shouldEvaluate; + /** + * Constructor + */ + public function __construct() + { + $this->parsedFiles = array(); + $this->sourceParsers = array(); + } + /** * Compile scss * @@ -158,8 +167,6 @@ public function compile($code, $path = null) $this->commentsSeen = array(); $this->extends = array(); $this->extendsMap = array(); - $this->parsedFiles = array(); - $this->sourceParsers = array(); $this->sourceIndex = null; $this->sourcePos = null; $this->env = null; From 1f5c1eeba750f09653432511876613eba3dd2242 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sat, 28 Nov 2015 21:43:31 -0500 Subject: [PATCH 138/484] fixes #385 - replace sourcePosition with sourceLine; also, eliminate sourceParsers[] --- src/Compiler.php | 71 +++++++++++++++--------------- src/Parser.php | 110 +++++++++++++++++++++++++++++------------------ 2 files changed, 102 insertions(+), 79 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 76e70e8b..77a4fa79 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -131,8 +131,8 @@ class Compiler private $env; private $scope; private $parser; - private $sourcePos; - private $sourceParsers; + private $sourceNames; + private $sourceLine; private $sourceIndex; private $storeEnv; private $charsetSeen; @@ -144,8 +144,8 @@ class Compiler */ public function __construct() { - $this->parsedFiles = array(); - $this->sourceParsers = array(); + $this->parsedFiles = array(); + $this->sourceNames = array(); } /** @@ -168,7 +168,7 @@ public function compile($code, $path = null) $this->extends = array(); $this->extendsMap = array(); $this->sourceIndex = null; - $this->sourcePos = null; + $this->sourceLine = null; $this->env = null; $this->scope = null; $this->storeEnv = null; @@ -178,6 +178,7 @@ public function compile($code, $path = null) $this->parser = $this->parserFactory($path); $tree = $this->parser->parse($code); + $this->parser = null; $this->formatter = new $this->formatter(); $this->rootBlock = null; @@ -203,9 +204,9 @@ public function compile($code, $path = null) */ private function parserFactory($path) { - $parser = new Parser($path, count($this->sourceParsers)); + $parser = new Parser($path, count($this->sourceNames)); - $this->sourceParsers[] = $parser; + $this->sourceNames[] = $path; $this->addParsedFile($path); return $parser; @@ -537,12 +538,12 @@ protected function compileMedia(Block $media) if ($needsWrap) { $wrapped = new Block; - $wrapped->sourcePosition = $media->sourcePosition; - $wrapped->sourceIndex = $media->sourceIndex; - $wrapped->selectors = array(); - $wrapped->comments = array(); - $wrapped->parent = $media; - $wrapped->children = $media->children; + $wrapped->sourceLine = $media->sourceLine; + $wrapped->sourceIndex = $media->sourceIndex; + $wrapped->selectors = array(); + $wrapped->comments = array(); + $wrapped->parent = $media; + $wrapped->children = $media->children; $media->children = array(array(Type::T_BLOCK, $wrapped)); } @@ -609,12 +610,12 @@ protected function compileAtRoot(Block $block) // wrap inline selector if ($block->selector) { $wrapped = new Block; - $wrapped->sourcePosition = $block->sourcePosition; - $wrapped->sourceIndex = $block->sourceIndex; - $wrapped->selectors = $block->selector; - $wrapped->comments = array(); - $wrapped->parent = $block; - $wrapped->children = $block->children; + $wrapped->sourceLine = $block->sourceLine; + $wrapped->sourceIndex = $block->sourceIndex; + $wrapped->selectors = $block->selector; + $wrapped->comments = array(); + $wrapped->parent = $block; + $wrapped->children = $block->children; $block->children = array(array(Type::T_BLOCK, $wrapped)); } @@ -677,11 +678,11 @@ private function spliceTree($envs, Block $block, $without) } $b = new Block; - $b->sourcePosition = $e->block->sourcePosition; - $b->sourceIndex = $e->block->sourceIndex; - $b->selectors = array(); - $b->comments = $e->block->comments; - $b->parent = null; + $b->sourceLine = $e->block->sourceLine; + $b->sourceIndex = $e->block->sourceIndex; + $b->selectors = array(); + $b->comments = $e->block->comments; + $b->parent = null; if ($newBlock) { $type = isset($newBlock->type) ? $newBlock->type : Type::T_BLOCK; @@ -886,9 +887,8 @@ protected function compileBlock(Block $block) $annotation = $this->makeOutputBlock(Type::T_COMMENT); $annotation->depth = 0; - $parser = $this->sourceParsers[$block->sourceIndex]; - $file = $parser->getSourceName(); - $line = $parser->getLineNo($block->sourcePosition); + $file = $this->sourceNames[$block->sourceIndex]; + $line = $block->sourceLine; switch ($this->lineNumberStyle) { case self::LINE_COMMENTS: @@ -1361,7 +1361,7 @@ protected function compileImport($rawPath, $out, $once = false) protected function compileChild($child, OutputBlock $out) { $this->sourceIndex = isset($child[Parser::SOURCE_INDEX]) ? $child[Parser::SOURCE_INDEX] : null; - $this->sourcePos = isset($child[Parser::SOURCE_POSITION]) ? $child[Parser::SOURCE_POSITION] : -1; + $this->sourceLine = isset($child[Parser::SOURCE_LINE]) ? $child[Parser::SOURCE_LINE] : -1; switch ($child[0]) { case Type::T_SCSSPHP_IMPORT_ONCE: @@ -1685,7 +1685,7 @@ protected function compileChild($child, OutputBlock $out) case Type::T_DEBUG: list(, $value) = $child; - $line = $this->parser->getLineNo($this->sourcePos); + $line = $this->sourceLine; $value = $this->compileValue($this->reduce($value, true)); fwrite($this->stderr, "Line $line DEBUG: $value\n"); break; @@ -1693,7 +1693,7 @@ protected function compileChild($child, OutputBlock $out) case Type::T_WARN: list(, $value) = $child; - $line = $this->parser->getLineNo($this->sourcePos); + $line = $this->sourceLine; $value = $this->compileValue($this->reduce($value, true)); echo "Line $line WARN: $value\n"; break; @@ -1701,7 +1701,7 @@ protected function compileChild($child, OutputBlock $out) case Type::T_ERROR: list(, $value) = $child; - $line = $this->parser->getLineNo($this->sourcePos); + $line = $this->sourceLine; $value = $this->compileValue($this->reduce($value, true)); $this->throwError("Line $line ERROR: $value\n"); break; @@ -3211,10 +3211,8 @@ public function throwError($msg) $msg = call_user_func_array('sprintf', func_get_args()); } - if ($this->sourcePos >= 0 && isset($this->sourceIndex)) { - $parser = $this->sourceParsers[$this->sourceIndex]; - $parser->throwParseError($msg, $this->sourcePos); - } + $line = $this->sourceLine; + $msg = "$msg: line: $line"; throw new \Exception($msg); } @@ -3229,8 +3227,7 @@ public function throwError($msg) private function handleImportLoop($name) { for ($env = $this->env; $env; $env = $env->parent) { - $parser = $this->sourceParsers[$env->block->sourceIndex]; - $file = $parser->getSourceName(); + $file = $this->sourceNames[$env->block->sourceIndex]; if (realpath($file) === $name) { $this->throwError('An @import loop has been found: %s imports %s', $file, basename($file)); diff --git a/src/Parser.php b/src/Parser.php index afb6956f..c072cc11 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -23,8 +23,8 @@ */ class Parser { - const SOURCE_INDEX = -1; - const SOURCE_POSITION = -2; + const SOURCE_INDEX = -1; + const SOURCE_LINE = -2; /** * @var array @@ -53,6 +53,7 @@ class Parser private $sourceName; private $sourceIndex; + private $sourcePositions; private $charset; private $count; private $env; @@ -98,47 +99,22 @@ public function getSourceName() return $this->sourceName; } - /** - * Get source line number (given character position in the buffer) - * - * @api - * - * @param integer $pos - * - * @return integer - */ - public function getLineNo($pos) - { - return 1 + substr_count(substr($this->buffer, 0, $pos), "\n"); - } - /** * Throw parser error * * @api * - * @param string $msg - * @param integer $count + * @param string $msg * * @throws \Exception */ - public function throwParseError($msg = 'parse error', $count = null) + public function throwParseError($msg = 'parse error') { - $count = ! isset($count) ? $this->count : $count; - - $line = $this->getLineNo($count); + $line = $this->getSourceLineNumber($this->count); + $loc = empty($this->sourceName) ? "line: $line" : "$this->sourceName on line $line"; - if (! empty($this->sourceName)) { - $loc = "$this->sourceName on line $line"; - } else { - $loc = "line: $line"; - } - - if ($this->peek("(.*?)(\n|$)", $m, $count)) { - throw new \Exception("$msg: failed at `$m[1]` $loc"); - } - - throw new \Exception("$msg: $loc"); + throw new \Exception("$msg: failed at `$m[1]` $loc"); + //throw new \Exception("$msg: $loc"); } /** @@ -158,8 +134,9 @@ public function parse($buffer) $this->eatWhiteDefault = true; $this->buffer = rtrim($buffer, "\x00..\x1f"); - $this->pushBlock(null); // root block + $this->extractLineNumbers($buffer); + $this->pushBlock(null); // root block $this->whitespace(); $this->pushBlock(null); $this->popBlock(); @@ -549,7 +526,7 @@ protected function parseChunk() if (! isset($this->charset)) { $statement = array(Type::T_CHARSET, $charset); - $statement[self::SOURCE_POSITION] = $s; + $statement[self::SOURCE_LINE] = $this->getSourceLineNumber($s); $statement[self::SOURCE_INDEX] = $this->sourceIndex; $this->charset = $statement; @@ -691,11 +668,11 @@ protected function parseChunk() protected function pushBlock($selectors, $pos = 0) { $b = new Block; - $b->sourcePosition = $pos; - $b->sourceIndex = $this->sourceIndex; - $b->selectors = $selectors; - $b->comments = array(); - $b->parent = $this->env; + $b->sourceLine = $this->getSourceLineNumber($pos); + $b->sourceIndex = $this->sourceIndex; + $b->selectors = $selectors; + $b->comments = array(); + $b->parent = $this->env; if (! $this->env) { $b->children = array(); @@ -942,8 +919,8 @@ protected function appendComment($comment) protected function append($statement, $pos = null) { if ($pos !== null) { - $statement[self::SOURCE_POSITION] = $pos; - $statement[self::SOURCE_INDEX] = $this->sourceIndex; + $statement[self::SOURCE_LINE] = $this->getSourceLineNumber($pos); + $statement[self::SOURCE_INDEX] = $this->sourceIndex; } $this->env->children[] = $statement; @@ -2385,4 +2362,53 @@ private function pregQuote($what) { return preg_quote($what, '/'); } + + /** + * Extract line numbers from buffer + * + * @param string $buffer + */ + private function extractLineNumbers($buffer) + { + $this->sourcePositions = array(0 => 0); + $pos = 0; + + while (($pos = strpos($buffer, "\n", $prev)) !== false) { + $this->sourcePositions[] = $pos; + $prev = $pos + 1; + } + + $this->sourcePositions[] = strlen($buffer); + } + + /** + * Get source line number (given character position in the buffer) + * + * @param integer $pos + * + * @return integer + */ + private function getSourceLineNumber($pos) + { + $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; + } + + return $low + 1; + } } From 33ad8789de77741798de76f946f5b9076c697cbc Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sat, 28 Nov 2015 21:55:02 -0500 Subject: [PATCH 139/484] Fix throwParseError() --- src/Parser.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Parser.php b/src/Parser.php index c072cc11..c5fc442c 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -113,8 +113,11 @@ public function throwParseError($msg = 'parse error') $line = $this->getSourceLineNumber($this->count); $loc = empty($this->sourceName) ? "line: $line" : "$this->sourceName on line $line"; - throw new \Exception("$msg: failed at `$m[1]` $loc"); - //throw new \Exception("$msg: $loc"); + if ($this->peek("(.*?)(\n|$)", $m, $this->count)) { + throw new \Exception("$msg: failed at `$m[1]` $loc"); + } + + throw new \Exception("$msg: $loc"); } /** @@ -2371,7 +2374,7 @@ private function pregQuote($what) private function extractLineNumbers($buffer) { $this->sourcePositions = array(0 => 0); - $pos = 0; + $prev = 0; while (($pos = strpos($buffer, "\n", $prev)) !== false) { $this->sourcePositions[] = $pos; From 113311ada3a824ebade489463968914e81fbf9ae Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sat, 28 Nov 2015 22:52:35 -0500 Subject: [PATCH 140/484] debug broken travis unit tests --- tests/ExceptionTest.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/ExceptionTest.php b/tests/ExceptionTest.php index ea17abb5..4406c523 100644 --- a/tests/ExceptionTest.php +++ b/tests/ExceptionTest.php @@ -36,9 +36,11 @@ public function testThrowError($scss, $expectedExceptionMessage) try { $this->compile($scss); } catch (\Exception $e) { - if (strpos($e->getMessage(), $expectedExceptionMessage) !== false) { - return; - }; + if (strpos($e->getMessage(), $expectedExceptionMessage) === false) { + $this->fail('Unexpected exception raised: ' . $e->getMessage() . ' vs ' . $expectedExceptionMessage); + } + + return; } $this->fail('Expected exception to be raised: ' . $expectedExceptionMessage); From 9e422ba034dd723ab36fc1d1295a6b27d8dc621a Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sat, 28 Nov 2015 22:58:44 -0500 Subject: [PATCH 141/484] more travis debugging --- src/Parser.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Parser.php b/src/Parser.php index c5fc442c..7596afb7 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -2382,6 +2382,10 @@ private function extractLineNumbers($buffer) } $this->sourcePositions[] = strlen($buffer); + + if (substr($buffer, -1) !== "\n") { + $this->sourcePositions[] = strlen(buffer) + 1; + } } /** From 1fd71902452efcabfe8a67b0a51fc37217745caf Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sat, 28 Nov 2015 22:59:52 -0500 Subject: [PATCH 142/484] fix typo --- src/Parser.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Parser.php b/src/Parser.php index 7596afb7..eca8d20d 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -2384,7 +2384,7 @@ private function extractLineNumbers($buffer) $this->sourcePositions[] = strlen($buffer); if (substr($buffer, -1) !== "\n") { - $this->sourcePositions[] = strlen(buffer) + 1; + $this->sourcePositions[] = strlen($buffer) + 1; } } From 62da8881d43562d9c47ad9c2434415d713b0c120 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sat, 28 Nov 2015 23:27:17 -0500 Subject: [PATCH 143/484] Add sourceColumn (needed for future sourcemap support) --- src/Block.php | 9 +++++++-- src/Compiler.php | 46 +++++++++++++++++++++++++-------------------- src/Node.php | 9 +++++++-- src/Node/Number.php | 25 +++++++++++++++++------- src/Parser.php | 43 ++++++++++++++++++++++++++---------------- 5 files changed, 85 insertions(+), 47 deletions(-) diff --git a/src/Block.php b/src/Block.php index 16f4a968..b44f4bd9 100644 --- a/src/Block.php +++ b/src/Block.php @@ -31,12 +31,17 @@ class Block /** * @var integer */ - public $sourcePosition; + public $sourceIndex; /** * @var integer */ - public $sourceIndex; + public $sourceLine; + + /** + * @var integer + */ + public $sourceColumn; /** * @var array diff --git a/src/Compiler.php b/src/Compiler.php index 77a4fa79..5ccf4fed 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -132,8 +132,9 @@ class Compiler private $scope; private $parser; private $sourceNames; - private $sourceLine; private $sourceIndex; + private $sourceLine; + private $sourceColumn; private $storeEnv; private $charsetSeen; private $stderr; @@ -169,6 +170,7 @@ public function compile($code, $path = null) $this->extendsMap = array(); $this->sourceIndex = null; $this->sourceLine = null; + $this->sourceColumn = null; $this->env = null; $this->scope = null; $this->storeEnv = null; @@ -538,12 +540,13 @@ protected function compileMedia(Block $media) if ($needsWrap) { $wrapped = new Block; - $wrapped->sourceLine = $media->sourceLine; - $wrapped->sourceIndex = $media->sourceIndex; - $wrapped->selectors = array(); - $wrapped->comments = array(); - $wrapped->parent = $media; - $wrapped->children = $media->children; + $wrapped->sourceIndex = $media->sourceIndex; + $wrapped->sourceLine = $media->sourceLine; + $wrapped->sourceColumn = $media->sourceColumn; + $wrapped->selectors = array(); + $wrapped->comments = array(); + $wrapped->parent = $media; + $wrapped->children = $media->children; $media->children = array(array(Type::T_BLOCK, $wrapped)); } @@ -610,12 +613,13 @@ protected function compileAtRoot(Block $block) // wrap inline selector if ($block->selector) { $wrapped = new Block; - $wrapped->sourceLine = $block->sourceLine; - $wrapped->sourceIndex = $block->sourceIndex; - $wrapped->selectors = $block->selector; - $wrapped->comments = array(); - $wrapped->parent = $block; - $wrapped->children = $block->children; + $wrapped->sourceIndex = $block->sourceIndex; + $wrapped->sourceLine = $block->sourceLine; + $wrapped->sourceColumn = $block->sourceColumn; + $wrapped->selectors = $block->selector; + $wrapped->comments = array(); + $wrapped->parent = $block; + $wrapped->children = $block->children; $block->children = array(array(Type::T_BLOCK, $wrapped)); } @@ -678,11 +682,12 @@ private function spliceTree($envs, Block $block, $without) } $b = new Block; - $b->sourceLine = $e->block->sourceLine; - $b->sourceIndex = $e->block->sourceIndex; - $b->selectors = array(); - $b->comments = $e->block->comments; - $b->parent = null; + $b->sourceIndex = $e->block->sourceIndex; + $b->sourceLine = $e->block->sourceLine; + $b->sourceColumn = $e->block->sourceColumn; + $b->selectors = array(); + $b->comments = $e->block->comments; + $b->parent = null; if ($newBlock) { $type = isset($newBlock->type) ? $newBlock->type : Type::T_BLOCK; @@ -1360,8 +1365,9 @@ protected function compileImport($rawPath, $out, $once = false) */ protected function compileChild($child, OutputBlock $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->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; switch ($child[0]) { case Type::T_SCSSPHP_IMPORT_ONCE: diff --git a/src/Node.php b/src/Node.php index d09f597e..2b55419e 100644 --- a/src/Node.php +++ b/src/Node.php @@ -26,10 +26,15 @@ abstract class Node /** * @var integer */ - public $sourcePosition; + public $sourceIndex; /** * @var integer */ - public $sourceIndex; + public $sourceLine; + + /** + * @var integer + */ + public $sourceColumn; } diff --git a/src/Node/Number.php b/src/Node/Number.php index 3727a768..f5356bfb 100644 --- a/src/Node/Number.php +++ b/src/Node/Number.php @@ -140,8 +140,12 @@ public function normalize() */ public function offsetExists($offset) { + if ($offset === -3) { + return $this->sourceColumn !== null; + } + if ($offset === -2) { - return $sourceIndex !== null; + return $this->sourceLine !== null; } if ($offset === -1 @@ -161,11 +165,14 @@ public function offsetExists($offset) public function offsetGet($offset) { switch ($offset) { + case -3: + return $this->sourceColumn; + case -2: - return $this->sourceIndex; + return $this->sourceLine; case -1: - return $this->sourcePosition; + return $this->sourceIndex; case 0: return $this->type; @@ -188,9 +195,11 @@ public function offsetSet($offset, $value) } elseif ($offset === 2) { $this->units = $value; } elseif ($offset == -1) { - $this->sourcePosition = $value; - } elseif ($offset == -2) { $this->sourceIndex = $value; + } elseif ($offset == -2) { + $this->sourceLine = $value; + } elseif ($offset == -3) { + $this->sourceColumn = $value; } } @@ -204,9 +213,11 @@ public function offsetUnset($offset) } elseif ($offset === 2) { $this->units = null; } elseif ($offset === -1) { - $this->sourcePosition = null; - } elseif ($offset === -2) { $this->sourceIndex = null; + } elseif ($offset === -2) { + $this->sourceLine = null; + } elseif ($offset === -3) { + $this->sourceColumn = null; } } diff --git a/src/Parser.php b/src/Parser.php index eca8d20d..5fe92e96 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -23,8 +23,9 @@ */ class Parser { - const SOURCE_INDEX = -1; - const SOURCE_LINE = -2; + const SOURCE_INDEX = -1; + const SOURCE_LINE = -2; + const SOURCE_COLUMN = -3; /** * @var array @@ -110,7 +111,8 @@ public function getSourceName() */ public function throwParseError($msg = 'parse error') { - $line = $this->getSourceLineNumber($this->count); + 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)) { @@ -529,8 +531,11 @@ protected function parseChunk() if (! isset($this->charset)) { $statement = array(Type::T_CHARSET, $charset); - $statement[self::SOURCE_LINE] = $this->getSourceLineNumber($s); - $statement[self::SOURCE_INDEX] = $this->sourceIndex; + list($line, $column) = $this->getSourcePosition($s); + + $statement[self::SOURCE_LINE] = $line; + $statement[self::SOURCE_COLUMN] = $column; + $statement[self::SOURCE_INDEX] = $this->sourceIndex; $this->charset = $statement; } @@ -670,12 +675,15 @@ protected function parseChunk() */ protected function pushBlock($selectors, $pos = 0) { + list($line, $column) = $this->getSourcePosition($pos); + $b = new Block; - $b->sourceLine = $this->getSourceLineNumber($pos); - $b->sourceIndex = $this->sourceIndex; - $b->selectors = $selectors; - $b->comments = array(); - $b->parent = $this->env; + $b->sourceLine = $line; + $b->sourceColumn = $column; + $b->sourceIndex = $this->sourceIndex; + $b->selectors = $selectors; + $b->comments = array(); + $b->parent = $this->env; if (! $this->env) { $b->children = array(); @@ -922,8 +930,11 @@ protected function appendComment($comment) protected function append($statement, $pos = null) { if ($pos !== null) { - $statement[self::SOURCE_LINE] = $this->getSourceLineNumber($pos); - $statement[self::SOURCE_INDEX] = $this->sourceIndex; + 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; @@ -2389,13 +2400,13 @@ private function extractLineNumbers($buffer) } /** - * Get source line number (given character position in the buffer) + * Get source line number and column (given character position in the buffer) * * @param integer $pos * * @return integer */ - private function getSourceLineNumber($pos) + private function getSourcePosition($pos) { $low = 0; $high = count($this->sourcePositions); @@ -2413,9 +2424,9 @@ private function getSourceLineNumber($pos) continue; } - return $mid + 1; + return array($mid + 1, $pos - $this->sourcePosition[$mid]); } - return $low + 1; + return array($low + 1, $pos - $this->sourcePosition[$low]); } } From 68881cf7d64e19f52f295d078694ab3da365609a Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sat, 28 Nov 2015 23:29:43 -0500 Subject: [PATCH 144/484] fix typo --- src/Parser.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Parser.php b/src/Parser.php index 5fe92e96..89c3a93d 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -2424,9 +2424,9 @@ private function getSourcePosition($pos) continue; } - return array($mid + 1, $pos - $this->sourcePosition[$mid]); + return array($mid + 1, $pos - $this->sourcePositions[$mid]); } - return array($low + 1, $pos - $this->sourcePosition[$low]); + return array($low + 1, $pos - $this->sourcePositions[$low]); } } From c727c14ddd0de785af5dcc26c90422f361adf5f3 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Thu, 3 Dec 2015 23:51:15 -0500 Subject: [PATCH 145/484] fix single quoting issue reported with zurb/foundation 6.0.4 --- src/Parser.php | 12 ++++++------ tests/inputs/builtins.scss | 5 +++++ tests/outputs/builtins.css | 3 +++ tests/outputs_numbered/builtins.css | 3 +++ 4 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/Parser.php b/src/Parser.php index 89c3a93d..f208585b 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -1667,8 +1667,8 @@ protected function string(&$out) if ($this->literal('"', false)) { $delim = '"'; - } elseif ($this->literal('\'', false)) { - $delim = '\''; + } elseif ($this->literal("'", false)) { + $delim = "'"; } else { return false; } @@ -1690,10 +1690,10 @@ protected function string(&$out) $content[] = '#{'; // ignore it } } elseif ($m[2] === '\\') { - $content[] = $m[2]; - if ($this->literal($delim, false)) { - $content[] = $delim; + $content[] = $delim === '"' ? $m[2] . $delim : $delim; + } else { + $content[] = $m[2]; } } else { $this->count -= strlen($delim); @@ -1796,7 +1796,7 @@ protected function openString($end, &$out, $nestingOpen = null) break; } - if (($tok === '\'' || $tok === '"') && $this->string($str)) { + if (($tok === "'" || $tok === '"') && $this->string($str)) { $content[] = $str; continue; } diff --git a/tests/inputs/builtins.scss b/tests/inputs/builtins.scss index 32066b40..70ec3012 100644 --- a/tests/inputs/builtins.scss +++ b/tests/inputs/builtins.scss @@ -210,3 +210,8 @@ div.call-tests { b: call(scale-color, #0a64ff, $lightness: -10%); c: call($fn, (a b c), 2); } + +$type: text; +div.unquote-test { + a: unquote('[type=\'#{$type}\']'); +} diff --git a/tests/outputs/builtins.css b/tests/outputs/builtins.css index 4d3ef6d8..fa74c1e2 100644 --- a/tests/outputs/builtins.css +++ b/tests/outputs/builtins.css @@ -155,3 +155,6 @@ div.call-tests { a: #0a64ff; b: #0058ef; c: b; } + +div.unquote-test { + a: [type='text']; } diff --git a/tests/outputs_numbered/builtins.css b/tests/outputs_numbered/builtins.css index 95bc7759..b0170c8d 100644 --- a/tests/outputs_numbered/builtins.css +++ b/tests/outputs_numbered/builtins.css @@ -156,3 +156,6 @@ div.call-tests { a: #0a64ff; b: #0058ef; c: b; } +/* line 215, inputs/builtins.scss */ +div.unquote-test { + a: [type='text']; } From 70e80a5f40c9e6e162c872384806292354b7ddc1 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Fri, 4 Dec 2015 00:42:37 -0500 Subject: [PATCH 146/484] more quoting issues --- src/Parser.php | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/src/Parser.php b/src/Parser.php index f208585b..d6cfb86e 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -1676,22 +1676,28 @@ protected function string(&$out) $content = array(); $oldWhite = $this->eatWhiteDefault; $this->eatWhiteDefault = false; + $hasInterpolation = false; while ($this->matchString($m, $delim)) { - $content[] = $m[1]; + if ($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($delim, false)) { - $content[] = $delim === '"' ? $m[2] . $delim : $delim; + if ($this->literal('"', false)) { + $content[] = $m[2] . '"'; + } elseif ($this->literal("'", false)) { + $content[] = $m[2] . "'"; } else { $content[] = $m[2]; } @@ -1704,6 +1710,18 @@ protected function string(&$out) $this->eatWhiteDefault = $oldWhite; if ($this->literal($delim)) { + if ($hasInterpolation) { + $delim = '"'; + + foreach ($content as &$string) { + if ($string === "\\'") { + $string = "'"; + } elseif ($string === '\\"') { + $string = '"'; + } + } + } + $out = array(Type::T_STRING, $delim, $content); return true; @@ -2394,9 +2412,9 @@ private function extractLineNumbers($buffer) $this->sourcePositions[] = strlen($buffer); - if (substr($buffer, -1) !== "\n") { + if (substr($buffer, -1) !== "\n") { $this->sourcePositions[] = strlen($buffer) + 1; - } + } } /** From e7d382952c4dffc29ae832a7a37ddc0f37e66e68 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Fri, 4 Dec 2015 00:49:08 -0500 Subject: [PATCH 147/484] Bump to version 0.6.0 --- src/Version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Version.php b/src/Version.php index 76f37eb3..a47062b1 100644 --- a/src/Version.php +++ b/src/Version.php @@ -18,5 +18,5 @@ */ class Version { - const VERSION = 'v0.5.1'; + const VERSION = 'v0.6.0'; } From 1fcb031ca1377449a0a0dbfd86d41f8e79e9c6ef Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sun, 6 Dec 2015 18:25:49 -0500 Subject: [PATCH 148/484] Update doc blocks --- scss.inc.php | 2 +- src/Base/Range.php | 2 +- src/Block.php | 2 +- src/Compiler.php | 7 +++---- src/Compiler/Environment.php | 2 +- src/Exception/CompileException.php | 21 +++++++++++++++++++++ src/Exception/CompilerException.php | 10 ---------- src/Exception/ParseException.php | 13 ++++++++++++- src/Formatter.php | 2 +- src/Formatter/Compact.php | 2 +- src/Formatter/Compressed.php | 2 +- src/Formatter/Crunched.php | 2 +- src/Formatter/Debug.php | 2 +- src/Formatter/Expanded.php | 2 +- src/Formatter/Nested.php | 2 +- src/Formatter/OutputBlock.php | 2 +- src/Node.php | 2 +- src/Node/Number.php | 2 +- src/Parser.php | 2 +- src/Server.php | 2 +- src/Type.php | 2 +- src/Util.php | 2 +- 22 files changed, 54 insertions(+), 33 deletions(-) create mode 100644 src/Exception/CompileException.php delete mode 100644 src/Exception/CompilerException.php diff --git a/scss.inc.php b/scss.inc.php index 86e41b83..aaeddcad 100644 --- a/scss.inc.php +++ b/scss.inc.php @@ -9,7 +9,7 @@ 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/CompileException.php'; include_once __DIR__ . '/src/Exception/ParseException.php'; include_once __DIR__ . '/src/Formatter.php'; include_once __DIR__ . '/src/Formatter/Compact.php'; diff --git a/src/Base/Range.php b/src/Base/Range.php index 76920556..a591d7b0 100644 --- a/src/Base/Range.php +++ b/src/Base/Range.php @@ -12,7 +12,7 @@ namespace Leafo\ScssPhp\Base; /** - * Range class + * Range * * @author Anthon Pang */ diff --git a/src/Block.php b/src/Block.php index b44f4bd9..6b972a1b 100644 --- a/src/Block.php +++ b/src/Block.php @@ -12,7 +12,7 @@ namespace Leafo\ScssPhp; /** - * SCSS block + * Block * * @author Anthon Pang */ diff --git a/src/Compiler.php b/src/Compiler.php index 02adcf36..f986c831 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -15,7 +15,7 @@ use Leafo\ScssPhp\Block; use Leafo\ScssPhp\Colors; use Leafo\ScssPhp\Compiler\Environment; -use Leafo\ScssPhp\Exception\CompilerException; +use Leafo\ScssPhp\Exception\CompileException; use Leafo\ScssPhp\Formatter\OutputBlock; use Leafo\ScssPhp\Node; use Leafo\ScssPhp\Type; @@ -3210,8 +3210,7 @@ public function findImport($url) * * @param string $msg Message with optional sprintf()-style vararg parameters * - * @throws \Leafo\ScssPhp\Exception\CompilerException - * @throws \Leafo\ScssPhp\Exception\ParseException + * @throws \Leafo\ScssPhp\Exception\CompileException */ public function throwError($msg) { @@ -3222,7 +3221,7 @@ public function throwError($msg) $line = $this->sourceLine; $msg = "$msg: line: $line"; - throw new CompilerException($msg); + throw new CompileException($msg); } /** diff --git a/src/Compiler/Environment.php b/src/Compiler/Environment.php index f929c74c..e6314cbe 100644 --- a/src/Compiler/Environment.php +++ b/src/Compiler/Environment.php @@ -14,7 +14,7 @@ use Leafo\ScssPhp\Block; /** - * SCSS compiler environment + * Compiler environment * * @author Anthon Pang */ diff --git a/src/Exception/CompileException.php b/src/Exception/CompileException.php new file mode 100644 index 00000000..dbb554ed --- /dev/null +++ b/src/Exception/CompileException.php @@ -0,0 +1,21 @@ + + */ +class CompileException extends \Exception +{ +} diff --git a/src/Exception/CompilerException.php b/src/Exception/CompilerException.php deleted file mode 100644 index b52bbb95..00000000 --- a/src/Exception/CompilerException.php +++ /dev/null @@ -1,10 +0,0 @@ - */ class ParseException extends \Exception { diff --git a/src/Formatter.php b/src/Formatter.php index 88b7d867..caa9d660 100644 --- a/src/Formatter.php +++ b/src/Formatter.php @@ -14,7 +14,7 @@ use Leafo\ScssPhp\Formatter\OutputBlock; /** - * SCSS base formatter + * Base formatter * * @author Leaf Corcoran */ diff --git a/src/Formatter/Compact.php b/src/Formatter/Compact.php index eda6ec24..721390d7 100644 --- a/src/Formatter/Compact.php +++ b/src/Formatter/Compact.php @@ -14,7 +14,7 @@ use Leafo\ScssPhp\Formatter; /** - * SCSS compact formatter + * Compact formatter * * @author Leaf Corcoran */ diff --git a/src/Formatter/Compressed.php b/src/Formatter/Compressed.php index c51bb26b..1e27240a 100644 --- a/src/Formatter/Compressed.php +++ b/src/Formatter/Compressed.php @@ -15,7 +15,7 @@ use Leafo\ScssPhp\Formatter\OutputBlock; /** - * SCSS compressed formatter + * Compressed formatter * * @author Leaf Corcoran */ diff --git a/src/Formatter/Crunched.php b/src/Formatter/Crunched.php index 29120cc9..5e80a824 100644 --- a/src/Formatter/Crunched.php +++ b/src/Formatter/Crunched.php @@ -15,7 +15,7 @@ use Leafo\ScssPhp\Formatter\OutputBlock; /** - * SCSS crunched formatter + * Crunched formatter * * @author Anthon Pang */ diff --git a/src/Formatter/Debug.php b/src/Formatter/Debug.php index e330b2d6..e4555bb3 100644 --- a/src/Formatter/Debug.php +++ b/src/Formatter/Debug.php @@ -15,7 +15,7 @@ use Leafo\ScssPhp\Formatter\OutputBlock; /** - * SCSS debug formatter + * Debug formatter * * @author Anthon Pang */ diff --git a/src/Formatter/Expanded.php b/src/Formatter/Expanded.php index 477318b4..b1a0bf24 100644 --- a/src/Formatter/Expanded.php +++ b/src/Formatter/Expanded.php @@ -15,7 +15,7 @@ use Leafo\ScssPhp\Formatter\OutputBlock; /** - * SCSS expanded formatter + * Expanded formatter * * @author Leaf Corcoran */ diff --git a/src/Formatter/Nested.php b/src/Formatter/Nested.php index 8040adab..6fab43dc 100644 --- a/src/Formatter/Nested.php +++ b/src/Formatter/Nested.php @@ -15,7 +15,7 @@ use Leafo\ScssPhp\Formatter\OutputBlock; /** - * SCSS nested formatter + * Nested formatter * * @author Leaf Corcoran */ diff --git a/src/Formatter/OutputBlock.php b/src/Formatter/OutputBlock.php index bfc383e2..bb8d99b4 100644 --- a/src/Formatter/OutputBlock.php +++ b/src/Formatter/OutputBlock.php @@ -12,7 +12,7 @@ namespace Leafo\ScssPhp\Formatter; /** - * SCSS output block + * Output block * * @author Anthon Pang */ diff --git a/src/Node.php b/src/Node.php index 2b55419e..e6ed178c 100644 --- a/src/Node.php +++ b/src/Node.php @@ -12,7 +12,7 @@ namespace Leafo\ScssPhp; /** - * SCSS node + * Base node * * @author Anthon Pang */ diff --git a/src/Node/Number.php b/src/Node/Number.php index f5356bfb..17e856db 100644 --- a/src/Node/Number.php +++ b/src/Node/Number.php @@ -16,7 +16,7 @@ use Leafo\ScssPhp\Type; /** - * SCSS dimension + optional units + * Dimension + optional units * * {@internal * This is a work-in-progress. diff --git a/src/Parser.php b/src/Parser.php index c7f3553c..f5789729 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -18,7 +18,7 @@ use Leafo\ScssPhp\Type; /** - * SCSS parser + * Parser * * @author Leaf Corcoran */ diff --git a/src/Server.php b/src/Server.php index 9095e357..de4e8830 100644 --- a/src/Server.php +++ b/src/Server.php @@ -15,7 +15,7 @@ use Leafo\ScssPhp\Version; /** - * SCSS server + * Server * * @author Leaf Corcoran */ diff --git a/src/Type.php b/src/Type.php index 80b53258..8c3886c8 100644 --- a/src/Type.php +++ b/src/Type.php @@ -12,7 +12,7 @@ namespace Leafo\ScssPhp; /** - * SCSS block/node types + * Block/node types * * @author Anthon Pang */ diff --git a/src/Util.php b/src/Util.php index 714acdb8..9f47c1d7 100644 --- a/src/Util.php +++ b/src/Util.php @@ -14,7 +14,7 @@ use Leafo\ScssPhp\Base\Range; /** - * SCSS utilties + * Utilties * * @author Anthon Pang */ From 038518b1dab60d39a96bfa8dd99dcd41d654679a Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sun, 6 Dec 2015 18:45:00 -0500 Subject: [PATCH 149/484] Add ServerException --- scss.inc.php | 5 +++-- src/Compiler.php | 6 +++--- ...ileException.php => CompilerException.php} | 2 +- ...ParseException.php => ParserException.php} | 4 ++-- src/Exception/ServerException.php | 21 +++++++++++++++++++ src/Parser.php | 8 +++---- src/Server.php | 15 ++++++------- 7 files changed, 42 insertions(+), 19 deletions(-) rename src/Exception/{CompileException.php => CompilerException.php} (87%) rename src/Exception/{ParseException.php => ParserException.php} (81%) create mode 100644 src/Exception/ServerException.php diff --git a/scss.inc.php b/scss.inc.php index aaeddcad..d3720eb7 100644 --- a/scss.inc.php +++ b/scss.inc.php @@ -9,8 +9,9 @@ include_once __DIR__ . '/src/Colors.php'; include_once __DIR__ . '/src/Compiler.php'; include_once __DIR__ . '/src/Compiler/Environment.php'; - include_once __DIR__ . '/src/Exception/CompileException.php'; - include_once __DIR__ . '/src/Exception/ParseException.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'; diff --git a/src/Compiler.php b/src/Compiler.php index f986c831..8f8ff830 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -15,7 +15,7 @@ use Leafo\ScssPhp\Block; use Leafo\ScssPhp\Colors; use Leafo\ScssPhp\Compiler\Environment; -use Leafo\ScssPhp\Exception\CompileException; +use Leafo\ScssPhp\Exception\CompilerException; use Leafo\ScssPhp\Formatter\OutputBlock; use Leafo\ScssPhp\Node; use Leafo\ScssPhp\Type; @@ -3210,7 +3210,7 @@ public function findImport($url) * * @param string $msg Message with optional sprintf()-style vararg parameters * - * @throws \Leafo\ScssPhp\Exception\CompileException + * @throws \Leafo\ScssPhp\Exception\CompilerException */ public function throwError($msg) { @@ -3221,7 +3221,7 @@ public function throwError($msg) $line = $this->sourceLine; $msg = "$msg: line: $line"; - throw new CompileException($msg); + throw new CompilerException($msg); } /** diff --git a/src/Exception/CompileException.php b/src/Exception/CompilerException.php similarity index 87% rename from src/Exception/CompileException.php rename to src/Exception/CompilerException.php index dbb554ed..777e40ac 100644 --- a/src/Exception/CompileException.php +++ b/src/Exception/CompilerException.php @@ -16,6 +16,6 @@ * * @author Oleksandr Savchenko */ -class CompileException extends \Exception +class CompilerException extends \Exception { } diff --git a/src/Exception/ParseException.php b/src/Exception/ParserException.php similarity index 81% rename from src/Exception/ParseException.php rename to src/Exception/ParserException.php index 5737e142..fbe6388f 100644 --- a/src/Exception/ParseException.php +++ b/src/Exception/ParserException.php @@ -12,10 +12,10 @@ namespace Leafo\ScssPhp\Exception; /** - * Parse Exception + * Parser Exception * * @author Oleksandr Savchenko */ -class ParseException extends \Exception +class ParserException extends \Exception { } diff --git a/src/Exception/ServerException.php b/src/Exception/ServerException.php new file mode 100644 index 00000000..5a878d2f --- /dev/null +++ b/src/Exception/ServerException.php @@ -0,0 +1,21 @@ + + */ +class ServerException extends \Exception +{ +} diff --git a/src/Parser.php b/src/Parser.php index f5789729..7bf6c8f3 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -13,7 +13,7 @@ use Leafo\ScssPhp\Block; use Leafo\ScssPhp\Compiler; -use Leafo\ScssPhp\Exception\ParseException; +use Leafo\ScssPhp\Exception\ParserException; use Leafo\ScssPhp\Node; use Leafo\ScssPhp\Type; @@ -108,7 +108,7 @@ public function getSourceName() * * @param string $msg * - * @throws \Leafo\ScssPhp\Exception\ParseException + * @throws \Leafo\ScssPhp\Exception\ParserException */ public function throwParseError($msg = 'parse error') { @@ -117,10 +117,10 @@ public function throwParseError($msg = 'parse error') $loc = empty($this->sourceName) ? "line: $line" : "$this->sourceName on line $line"; if ($this->peek("(.*?)(\n|$)", $m, $this->count)) { - throw new ParseException("$msg: failed at `$m[1]` $loc"); + throw new ParserException("$msg: failed at `$m[1]` $loc"); } - throw new ParseException("$msg: $loc"); + throw new ParserException("$msg: $loc"); } /** diff --git a/src/Server.php b/src/Server.php index de4e8830..cada00e0 100644 --- a/src/Server.php +++ b/src/Server.php @@ -12,6 +12,7 @@ namespace Leafo\ScssPhp; use Leafo\ScssPhp\Compiler; +use Leafo\ScssPhp\Exception\ServerException; use Leafo\ScssPhp\Version; /** @@ -231,7 +232,7 @@ protected function compile($in, $out) * * @return string */ - protected function createErrorCSS($error) + protected function createErrorCSS(\Exception $error) { $message = str_replace( array("'", "\n"), @@ -269,11 +270,13 @@ public function showErrorsAsCSS($show = true) * @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 \Exception('load error: failed to find ' . $in); + throw new ServerException('load error: failed to find ' . $in); } $pi = pathinfo($in); @@ -334,7 +337,6 @@ public function serve($salt = '') header('ETag: "' . $etag . '"'); echo $css; - } catch (\Exception $e) { if ($this->showErrorsAsCSS) { header('Content-type: text/css'); @@ -346,7 +348,6 @@ public function serve($salt = '') echo 'Parse error: ' . $e->getMessage() . "\n"; } - } return; @@ -395,16 +396,16 @@ public function serve($salt = '') * * @return string Compiled CSS results * - * @throws \Exception + * @throws \Leafo\ScssPhp\Exception\ServerException */ public function checkedCachedCompile($in, $out, $force = false) { if (! is_file($in) || ! is_readable($in)) { - throw new \Exception('Invalid or unreadable input file specified.'); + throw new ServerException('Invalid or unreadable input file specified.'); } if (is_dir($out) || ! is_writable(file_exists($out) ? $out : dirname($out))) { - throw new \Exception('Invalid or unwritable output file specified.'); + throw new ServerException('Invalid or unwritable output file specified.'); } if ($force || $this->needsCompile($in, $out, $etag)) { From 53e68de07034da565793495027c1dd27835a421c Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sun, 6 Dec 2015 20:15:59 -0500 Subject: [PATCH 150/484] add more failing tests --- tests/FailingTest.php | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/tests/FailingTest.php b/tests/FailingTest.php index 863ffe00..3cdf0a04 100644 --- a/tests/FailingTest.php +++ b/tests/FailingTest.php @@ -110,6 +110,32 @@ public function provideFailing() small { font-weight: italic; } +END_OF_EXPECTED + ), + array( + '#368 - self in selector', <<<'END_OF_SCSS' +.test { + &:last-child:not(+ &:first-child) { + padding-left: 10px; + } +} +END_OF_SCSS + , << Date: Fri, 13 Nov 2015 10:12:06 +0200 Subject: [PATCH 151/484] ignore errors --- src/Compiler.php | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/Compiler.php b/src/Compiler.php index 8f8ff830..cb25ba26 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -141,6 +141,11 @@ class Compiler private $stderr; private $shouldEvaluate; + /** + * @var boolean ignore SCSS errors or throw an error + */ + private $ignoreErrors; + /** * Constructor */ @@ -3214,6 +3219,10 @@ public function findImport($url) */ public function throwError($msg) { + if ($this->isIgnoreErrors()) { + return; + } + if (func_num_args() > 1) { $msg = call_user_func_array('sprintf', func_get_args()); } @@ -4945,4 +4954,30 @@ protected function libUniqueId() return array(Type::T_STRING, '', array('u' . str_pad(base_convert($id, 10, 36), 8, '0', STR_PAD_LEFT))); } + + /** + * is IgnoreErrors + * + * @return boolean + */ + private function isIgnoreErrors() + { + return $this->ignoreErrors; + } + + /** + * Set IgnoreErrors + * + * @api + * + * @param boolean $ignoreErrors + * + * @return \Leafo\ScssPhp\Compiler + */ + public function setIgnoreErrors($ignoreErrors) + { + $this->ignoreErrors = $ignoreErrors; + + return $this; + } } From 990fbea30cb55dc37f69e2b143d652f6b81dda12 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 8 Dec 2015 12:08:15 -0500 Subject: [PATCH 152/484] bin/pscss: add --continue-on-error --- bin/pscss | 27 +++++++++++++++++++-------- src/Compiler.php | 46 +++++++++++++++------------------------------- 2 files changed, 34 insertions(+), 39 deletions(-) diff --git a/bin/pscss b/bin/pscss index 4dd5136a..16a12039 100755 --- a/bin/pscss +++ b/bin/pscss @@ -30,6 +30,7 @@ $inputFile = null; $changeDir = false; $debugInfo = false; $lineNumbers = false; +$ignoreErrors = false; /** * Parse argument @@ -67,14 +68,15 @@ Usage: $exe [options] [input-file] Options include: - --debug-info Annotate selectors with CSS referring to the source file and line number - -h, --help Show this message - -f=format Set the output format (compact, compressed, crunched, expanded, or nested) - -i=path Set import path - --line-numbers Annotate selectors with comments referring to the source file and line number - -p=precision Set decimal number precision (default 5) - -T Dump formatted parse tree - -v, --version Print the version + -h, --help Show this message + --continue-on-error Continue compilation (as best as possible) when error encountered + --debug-info Annotate selectors with CSS referring to the source file and line number + -f=format Set the output format (compact, compressed, crunched, expanded, or nested) + -i=path Set import path + --line-numbers Annotate selectors with comments referring to the source file and line number + -p=precision Set decimal number precision (default 5) + -T Dump formatted parse tree + -v, --version Print the version EOT; exit($HELP); @@ -84,6 +86,11 @@ EOT; exit(Version::VERSION . "\n"); } + if ($argv[$i] === '--continue-on-error') { + $ignoreErrors = true; + continue; + } + if ($argv[$i] === '--debug-info') { $debugInfo = true; continue; @@ -163,6 +170,10 @@ if ($lineNumbers && $inputFile) { $scss->setLineNumberStyle(Compiler::LINE_COMMENTS); } +if ($ignoreErrors) { + $scss->setIgnoreErrors($ignoreErrors); +} + if ($loadPaths) { $scss->setImportPaths(explode(PATH_SEPARATOR, $loadPaths)); } diff --git a/src/Compiler.php b/src/Compiler.php index cb25ba26..6140ffef 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -140,10 +140,6 @@ class Compiler private $charsetSeen; private $stderr; private $shouldEvaluate; - - /** - * @var boolean ignore SCSS errors or throw an error - */ private $ignoreErrors; /** @@ -3208,6 +3204,20 @@ public function findImport($url) return null; } + /** + * Ignore errors? + * + * @api + * + * @param boolean $ignoreErrors + * + * @return \Leafo\ScssPhp\Compiler + */ + public function setIgnoreErrors($ignoreErrors) + { + $this->ignoreErrors = $ignoreErrors; + } + /** * Throw error (exception) * @@ -3219,7 +3229,7 @@ public function findImport($url) */ public function throwError($msg) { - if ($this->isIgnoreErrors()) { + if ($this->ignoreErrors) { return; } @@ -4954,30 +4964,4 @@ protected function libUniqueId() return array(Type::T_STRING, '', array('u' . str_pad(base_convert($id, 10, 36), 8, '0', STR_PAD_LEFT))); } - - /** - * is IgnoreErrors - * - * @return boolean - */ - private function isIgnoreErrors() - { - return $this->ignoreErrors; - } - - /** - * Set IgnoreErrors - * - * @api - * - * @param boolean $ignoreErrors - * - * @return \Leafo\ScssPhp\Compiler - */ - public function setIgnoreErrors($ignoreErrors) - { - $this->ignoreErrors = $ignoreErrors; - - return $this; - } } From 48ccb0dc4a6af0bd2654b28e2dd7ac4dbbc6e64e Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 8 Dec 2015 23:17:36 -0500 Subject: [PATCH 153/484] Refs #388 - yet another BEM fix --- src/Compiler.php | 6 +++++- tests/inputs/extends.scss | 12 ++++++++++++ tests/outputs/extends.css | 5 +++++ tests/outputs_numbered/extends.css | 9 +++++++++ 4 files changed, 31 insertions(+), 1 deletion(-) diff --git a/src/Compiler.php b/src/Compiler.php index 6140ffef..84d0b99f 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -466,7 +466,11 @@ protected function matchExtendsSingle($rawSingle, &$outOrigin) return false; } - $origin[$j][count($origin[$j]) - 1] = $this->combineSelectorSingle(end($new), $rem); + $combined = $this->combineSelectorSingle(end($new), $rem); + + if (count(array_diff($combined, $origin[$j][count($origin[$j]) - 1]))) { + $origin[$j][count($origin[$j]) - 1] = $combined; + } } $outOrigin = array_merge($outOrigin, $origin); diff --git a/tests/inputs/extends.scss b/tests/inputs/extends.scss index fe93cab8..315f25ea 100644 --- a/tests/inputs/extends.scss +++ b/tests/inputs/extends.scss @@ -269,3 +269,15 @@ $var: ".selector1, .selector2"; // interpolation of the variable is needed since we have a selector in $var @extend #{$var}; } + +// scss +.main { + .block__element { + font-size: 14px; + + &--modifier { + @extend .block__element; + font-weight: bold; + } + } +} diff --git a/tests/outputs/extends.css b/tests/outputs/extends.css index cd101158..82a65823 100644 --- a/tests/outputs/extends.css +++ b/tests/outputs/extends.css @@ -124,3 +124,8 @@ body .to-extend, body .test { .selector2, .master-class { background: #ccc; } + +.main .block__element, .main .block__element--modifier { + font-size: 14px; } + .main .block__element--modifier { + font-weight: bold; } diff --git a/tests/outputs_numbered/extends.css b/tests/outputs_numbered/extends.css index cfaa646b..935d1033 100644 --- a/tests/outputs_numbered/extends.css +++ b/tests/outputs_numbered/extends.css @@ -191,3 +191,12 @@ body .to-extend, body .test { .selector2, .master-class { background: #ccc; } /* line 268, inputs/extends.scss */ +/* line 274, inputs/extends.scss */ + +/* line 275, inputs/extends.scss */ + +.main .block__element, .main .block__element--modifier { + font-size: 14px; } +/* line 278, inputs/extends.scss */ +.main .block__element--modifier { + font-weight: bold; } From 9f71d901bf56a08072a11a59940bb7ab216bedee Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 9 Dec 2015 12:33:23 -0500 Subject: [PATCH 154/484] refs #379 - break/return if throwError returns (i.e., continue-on-error) --- src/Compiler.php | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 84d0b99f..9a0429d3 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -1171,6 +1171,8 @@ protected function compileChildrenNoReturn($stms, OutputBlock $out) if (isset($ret)) { $this->throwError('@return may only be used within a function'); + + return; } } } @@ -1650,6 +1652,7 @@ protected function compileChild($child, OutputBlock $out) if (! $mixin) { $this->throwError("Undefined mixin $name"); + break; } $callingScope = $this->getStoreEnv(); @@ -1680,9 +1683,6 @@ protected function compileChild($child, OutputBlock $out) if (! $content) { $this->throwError('Expected @content inside of mixin'); - } - - if (! isset($content->children)) { break; } @@ -2247,6 +2247,7 @@ protected function opColorColor($op, $left, $right) case '/': if ($rval == 0) { $this->throwError("color: Can't divide by zero"); + break 2; } $out[] = (int) ($lval / $rval); @@ -2260,6 +2261,7 @@ protected function opColorColor($op, $left, $right) default: $this->throwError("color: unknown op $op"); + break 2; } } @@ -3261,6 +3263,7 @@ private function handleImportLoop($name) if (realpath($file) === $name) { $this->throwError('An @import loop has been found: %s imports %s', $file, basename($file)); + break; } } } @@ -3462,14 +3465,17 @@ protected function applyArguments($argDef, $argValues) $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); @@ -3521,6 +3527,7 @@ protected function applyArguments($argDef, $argValues) continue; } else { $this->throwError("Missing argument $name"); + break; } $this->set($name, $this->reduce($val, true), true, $env); @@ -4492,6 +4499,7 @@ protected function getNormalizedNumbers($args) foreach ($args as $key => $item) { if ($item[0] !== Type::T_NUMBER) { $this->throwError('%s is not a number', $item[0]); + break; } $number = $item->normalize(); @@ -4501,6 +4509,7 @@ protected function getNormalizedNumbers($args) $originalUnit = $item->unitStr(); } elseif ($unit !== $number[2]) { $this->throwError('Incompatible units: "%s" and "%s".', $originalUnit, $item->unitStr()); + break; } $numbers[$key] = $number; @@ -4567,6 +4576,8 @@ protected function libSetNth($args) if (! isset($list[2][$n])) { $this->throwError('Invalid argument for "n"'); + + return; } $list[2][$n] = $args[2]; @@ -4773,6 +4784,8 @@ protected function libComparable($args) ! isset($number2[0]) || $number2[0] !== Type::T_NUMBER ) { $this->throwError('Invalid argument(s) for "comparable"'); + + return; } $number1 = $number1->normalize(); @@ -4948,6 +4961,8 @@ protected function libRandom($args) if ($n < 1) { $this->throwError("limit must be greater than or equal to 1"); + + return; } return new Node\Number(mt_rand(1, $n), ''); From f642956291be38aa6627c9fb61386160bc181e46 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 9 Dec 2015 13:29:03 -0500 Subject: [PATCH 155/484] fix another potential notice --- src/Compiler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compiler.php b/src/Compiler.php index 9a0429d3..a08f1ba6 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -1128,7 +1128,7 @@ protected function hasSelectorPlaceholder($selector) foreach ($selector as $parts) { foreach ($parts as $part) { - if ('%' === $part[0]) { + if (strlen($part) && '%' === $part[0]) { return true; } } From a070856a488e19df6b65e83b1ac5c71e37c19d83 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sun, 13 Dec 2015 01:24:06 -0500 Subject: [PATCH 156/484] fixes #304 - implement @extend !optional --- src/Compiler.php | 41 +++++++++++++++++++++++++----- src/Parser.php | 29 +++++++++++++++++++-- tests/ExceptionTest.php | 8 ++++++ tests/inputs/extends.scss | 4 +++ tests/outputs_numbered/extends.css | 1 + 5 files changed, 75 insertions(+), 8 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index a08f1ba6..5e6c11d5 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -238,17 +238,18 @@ protected function isSelfExtend($target, $origin) /** * Push extends * - * @param array $target - * @param array $origin + * @param array $target + * @param array $origin + * @param \stdClass $block */ - protected function pushExtends($target, $origin) + protected function pushExtends($target, $origin, $block) { if ($this->isSelfExtend($target, $origin)) { return; } $i = count($this->extends); - $this->extends[] = array($target, $origin); + $this->extends[] = array($target, $origin, $block); foreach ($target as $part) { if (isset($this->extendsMap[$part])) { @@ -291,6 +292,32 @@ protected function compileRoot(Block $rootBlock) $this->compileChildrenNoReturn($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."); + } } /** @@ -451,13 +478,15 @@ protected function matchExtendsSingle($rawSingle, &$outOrigin) $found = false; foreach ($counts as $idx => $count) { - list($target, $origin) = $this->extends[$idx]; + list($target, $origin, $block) = $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) { @@ -1504,7 +1533,7 @@ protected function compileChild($child, OutputBlock $out) // only use the first one $result = current($result); - $this->pushExtends($result, $out->selectors); + $this->pushExtends($result, $out->selectors, $child); } } break; diff --git a/src/Parser.php b/src/Parser.php index 7bf6c8f3..b543f182 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -349,10 +349,12 @@ protected function parseChunk() $this->seek($s); if ($this->literal('@extend') && - $this->selectors($selector) && + $this->selectors($selectors) && $this->end() ) { - $this->append(array(Type::T_EXTEND, $selector), $s); + // check for '!flag' + $optional = $this->stripOptionalFlag($selectors); + $this->append(array(Type::T_EXTEND, $selectors, $optional), $s); return true; } @@ -2326,6 +2328,29 @@ protected function stripAssignmentFlag(&$value) return false; } + /** + * Strip optional flag from selector list + * + * @param array $selectors + * + * @return string + */ + protected function stripOptionalFlag(&$selectors) + { + $optional = false; + + $selector = end($selectors); + $part = end($selector); + + if ($part === array('!optional')) { + array_pop($selectors[count($selectors) - 1]); + + $optional = true; + } + + return $optional; + } + /** * Turn list of length 1 into value type * diff --git a/tests/ExceptionTest.php b/tests/ExceptionTest.php index 4406c523..f5fa17a5 100644 --- a/tests/ExceptionTest.php +++ b/tests/ExceptionTest.php @@ -131,6 +131,14 @@ public function provideScss() , 'isn\'t a valid CSS value' ), + array(<<<'END_OF_SCSS' +a.important { + @extend .notice; +} +END_OF_SCSS + , + 'was not found' + ), ); } diff --git a/tests/inputs/extends.scss b/tests/inputs/extends.scss index 315f25ea..9da03502 100644 --- a/tests/inputs/extends.scss +++ b/tests/inputs/extends.scss @@ -281,3 +281,7 @@ $var: ".selector1, .selector2"; } } } + +a.important { + @extend .notice !optional; +} diff --git a/tests/outputs_numbered/extends.css b/tests/outputs_numbered/extends.css index 935d1033..5d76d9d3 100644 --- a/tests/outputs_numbered/extends.css +++ b/tests/outputs_numbered/extends.css @@ -200,3 +200,4 @@ body .to-extend, body .test { /* line 278, inputs/extends.scss */ .main .block__element--modifier { font-weight: bold; } +/* line 285, inputs/extends.scss */ From adfc2cf537e3fbe07d1d48fcf184c13460e1f218 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sun, 13 Dec 2015 02:30:32 -0500 Subject: [PATCH 157/484] fixes #301 - implement keywords() --- src/Compiler.php | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/Compiler.php b/src/Compiler.php index 5e6c11d5..2eac200b 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -4687,6 +4687,22 @@ protected function libMapMerge($args) return array(Type::T_MAP, array_merge($map1[1], $map2[1]), array_merge($map1[2], $map2[2])); } + protected static $libKeywords = array('args'); + protected function libKeywords($args) + { + $this->assertList($args[0]); + + $keys = array(); + $values = array(); + + foreach ($args[0][2] as $name => $arg) { + $keys[] = array(Type::T_KEYWORD, $name); + $values[] = $arg; + } + + return array(Type::T_MAP, $keys, $values); + } + protected function listSeparatorForJoin($list1, $sep) { if (! isset($sep)) { From fc8c73b7955b22b40792d716d42c8daa5b43a777 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sun, 13 Dec 2015 03:03:38 -0500 Subject: [PATCH 158/484] Bump version to v0.6.1 --- src/Version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Version.php b/src/Version.php index a47062b1..ef096e94 100644 --- a/src/Version.php +++ b/src/Version.php @@ -18,5 +18,5 @@ */ class Version { - const VERSION = 'v0.6.0'; + const VERSION = 'v0.6.1'; } From 317a7b31b49ae442cb70706456d36a5eef45d38b Mon Sep 17 00:00:00 2001 From: Marat Tanalin Date: Mon, 14 Dec 2015 01:10:30 +0300 Subject: [PATCH 159/484] Added `rebeccapurple` named color It is supported by all current major browsers. Some details: * https://drafts.csswg.org/css-color/#valdef-color-rebeccapurple * http://codepen.io/trezy/post/honoring-a-great-man --- src/Colors.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Colors.php b/src/Colors.php index 0227fbe0..d1a75949 100644 --- a/src/Colors.php +++ b/src/Colors.php @@ -145,6 +145,7 @@ 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', From d27d49229c17b42f4d9b055d41984b62df9fcbb3 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sun, 13 Dec 2015 20:03:25 -0500 Subject: [PATCH 160/484] phpcs tweak --- src/Colors.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Colors.php b/src/Colors.php index d1a75949..e3936a5f 100644 --- a/src/Colors.php +++ b/src/Colors.php @@ -174,6 +174,6 @@ class Colors 'white' => '255,255,255', 'whitesmoke' => '245,245,245', 'yellow' => '255,255,0', - 'yellowgreen' => '154,205,50' + 'yellowgreen' => '154,205,50', ); } From 3eaa3f8df021f3dcbe19f814540c941308a9668f Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sun, 13 Dec 2015 20:43:36 -0500 Subject: [PATCH 161/484] fixes #303 - implement inspect($value) built-in --- src/Compiler.php | 11 +++++++++++ tests/inputs/functions.scss | 14 ++++++++++++++ tests/outputs/functions.css | 13 +++++++++++++ tests/outputs_numbered/functions.css | 13 +++++++++++++ 4 files changed, 51 insertions(+) diff --git a/src/Compiler.php b/src/Compiler.php index 2eac200b..57ec4d65 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -4999,6 +4999,7 @@ protected function libCounter($args) return array(Type::T_STRING, '', array('counter(' . implode(',', $list) . ')')); } + protected static $libRandom = array('limit'); protected function libRandom($args) { if (isset($args[0])) { @@ -5028,4 +5029,14 @@ protected function libUniqueId() return array(Type::T_STRING, '', array('u' . str_pad(base_convert($id, 10, 36), 8, '0', STR_PAD_LEFT))); } + + protected static $libInspect = array('value'); + protected function libInspect($args) + { + if ($args[0] === self::$null) { + return array(Type::T_KEYWORD, 'null'); + } + + return $args[0]; + } } diff --git a/tests/inputs/functions.scss b/tests/inputs/functions.scss index 4886a6e5..bcceaffd 100644 --- a/tests/inputs/functions.scss +++ b/tests/inputs/functions.scss @@ -114,3 +114,17 @@ $str: 'global'; @include test-mixin; display: $str; } + +.test-inspect { + n: inspect(null); + b1: inspect(true); + b2: inspect(false); + n1: inspect(0); + s1: inspect(''); + s2: inspect('hello'); + l1: inspect(1 2); + l2: inspect((3 4)); + l3: inspect((5,6)); + l4: inspect((a: 1, b: 2)); + l5: inspect($value: (a: 1, b: 2)); +} diff --git a/tests/outputs/functions.css b/tests/outputs/functions.css index 9604accb..252e3909 100644 --- a/tests/outputs/functions.css +++ b/tests/outputs/functions.css @@ -32,3 +32,16 @@ p { display: 'test-mixin'; display: 'test-mixin'; display: 'global'; } + +.test-inspect { + n: null; + b1: true; + b2: false; + n1: 0; + s1: ''; + s2: 'hello'; + l1: 1 2; + l2: 3 4; + l3: 5, 6; + l4: (a: 1, b: 2); + l5: (a: 1, b: 2); } diff --git a/tests/outputs_numbered/functions.css b/tests/outputs_numbered/functions.css index 4949511a..9b525e8d 100644 --- a/tests/outputs_numbered/functions.css +++ b/tests/outputs_numbered/functions.css @@ -34,3 +34,16 @@ p { display: 'test-mixin'; display: 'test-mixin'; display: 'global'; } +/* line 118, inputs/functions.scss */ +.test-inspect { + n: null; + b1: true; + b2: false; + n1: 0; + s1: ''; + s2: 'hello'; + l1: 1 2; + l2: 3 4; + l3: 5, 6; + l4: (a: 1, b: 2); + l5: (a: 1, b: 2); } From 493e05a95431e0cbb3ed6cbca65e07e6e443e19e Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 15 Dec 2015 13:41:21 -0500 Subject: [PATCH 162/484] support unicode characters in scss indentifers --- src/Parser.php | 10 +++++----- tests/inputs/variables.scss | 7 +++++++ tests/outputs/variables.css | 3 +++ tests/outputs_numbered/variables.css | 3 +++ 4 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/Parser.php b/src/Parser.php index b543f182..a0986136 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -85,7 +85,7 @@ public function __construct($sourceName, $sourceIndex = 0) $commentMultiRight = '\*\/'; self::$commentPattern = $commentMultiLeft . '.*?' . $commentMultiRight; - self::$whitePattern = '/' . $commentSingle . '[^\n]*\s*|(' . self::$commentPattern . ')\s*|\s+/Ais'; + self::$whitePattern = '/' . $commentSingle . '[^\n]*\s*|(' . self::$commentPattern . ')\s*|\s+/AisuS'; } } @@ -762,7 +762,7 @@ protected function peek($regex, &$out, $from = null) $from = $this->count; } - $r = '/' . $regex . '/Ais'; + $r = '/' . $regex . '/Aisu'; $result = preg_match($r, $this->buffer, $out, null, $from); return $result; @@ -842,7 +842,7 @@ protected function match($regex, &$out, $eatWhitespace = null) $eatWhitespace = $this->eatWhiteDefault; } - $r = '/' . $regex . '/Ais'; + $r = '/' . $regex . '/Aisu'; if (preg_match($r, $this->buffer, $out, null, $this->count)) { $this->count += strlen($out[0]); @@ -2235,7 +2235,7 @@ protected function variable(&$out) protected function keyword(&$word, $eatWhitespace = null) { if ($this->match( - '(([\w_\-\*!"\']|[\\\\].)([\w\-_"\']|[\\\\].)*)', + '(([\pL\w_\-\*!"\']|[\\\\].)([\pL\w\-_"\']|[\\\\].)*)', $m, $eatWhitespace )) { @@ -2256,7 +2256,7 @@ protected function keyword(&$word, $eatWhitespace = null) */ protected function placeholder(&$placeholder) { - if ($this->match('([\w\-_]+|#[{][$][\w\-_]+[}])', $m)) { + if ($this->match('([\pL\w\-_]+|#[{][$][\pL\w\-_]+[}])', $m)) { $placeholder = $m[1]; return true; diff --git a/tests/inputs/variables.scss b/tests/inputs/variables.scss index 367416fc..53149e6e 100644 --- a/tests/inputs/variables.scss +++ b/tests/inputs/variables.scss @@ -73,3 +73,10 @@ A { @include example; color: $color; } + +$коммент : #000; + +body +{ + color: $коммент ; +} diff --git a/tests/outputs/variables.css b/tests/outputs/variables.css index 693ee6ef..ed3e6b21 100644 --- a/tests/outputs/variables.css +++ b/tests/outputs/variables.css @@ -31,3 +31,6 @@ body { A { color: green; } + +body { + color: #000; } diff --git a/tests/outputs_numbered/variables.css b/tests/outputs_numbered/variables.css index f341c52f..206fa249 100644 --- a/tests/outputs_numbered/variables.css +++ b/tests/outputs_numbered/variables.css @@ -34,3 +34,6 @@ body { /* line 71, inputs/variables.scss */ A { color: green; } +/* line 79, inputs/variables.scss */ +body { + color: #000; } From 4ac664e32bd68833e69d88f0f1568239f7731113 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 15 Dec 2015 13:51:36 -0500 Subject: [PATCH 163/484] tweak tests --- tests/inputs/comments.scss | 4 ++++ tests/inputs/variables.scss | 4 ++-- tests/outputs/comments.css | 1 + tests/outputs_numbered/comments.css | 1 + 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/inputs/comments.scss b/tests/inputs/comments.scss index 80ab6c73..3e641cc1 100644 --- a/tests/inputs/comments.scss +++ b/tests/inputs/comments.scss @@ -41,4 +41,8 @@ a { } /* comment 7 */ +/* коммент */ + +// коммент + // end diff --git a/tests/inputs/variables.scss b/tests/inputs/variables.scss index 53149e6e..5212c385 100644 --- a/tests/inputs/variables.scss +++ b/tests/inputs/variables.scss @@ -74,9 +74,9 @@ A { color: $color; } -$коммент : #000; +$цвет : #000; body { - color: $коммент ; + color: $цвет; } diff --git a/tests/outputs/comments.css b/tests/outputs/comments.css index 6535554c..f6f8d184 100644 --- a/tests/outputs/comments.css +++ b/tests/outputs/comments.css @@ -29,3 +29,4 @@ a { /* comment 5 */ /* comment 6 */ } /* comment 7 */ +/* коммент */ diff --git a/tests/outputs_numbered/comments.css b/tests/outputs_numbered/comments.css index 6226d416..3669d1d8 100644 --- a/tests/outputs_numbered/comments.css +++ b/tests/outputs_numbered/comments.css @@ -31,3 +31,4 @@ a { /* comment 5 */ /* comment 6 */ } /* comment 7 */ +/* коммент */ From dbca8da31c3cfd5cfd9c7b2c5a0a74310fc248ae Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 15 Dec 2015 16:56:25 -0500 Subject: [PATCH 164/484] fixes #395 - bin/pscss: add --iso8859-1 option; defaults to utf-8 (slower but more compatible with ruby scss) --- bin/pscss | 11 +++++++++++ src/Compiler.php | 15 ++++++++++++++- src/Parser.php | 24 ++++++++++++++++++------ 3 files changed, 43 insertions(+), 7 deletions(-) diff --git a/bin/pscss b/bin/pscss index 16a12039..3175ea0b 100755 --- a/bin/pscss +++ b/bin/pscss @@ -31,6 +31,7 @@ $changeDir = false; $debugInfo = false; $lineNumbers = false; $ignoreErrors = false; +$encoding = false; /** * Parse argument @@ -73,6 +74,7 @@ Options include: --debug-info Annotate selectors with CSS referring to the source file and line number -f=format Set the output format (compact, compressed, crunched, expanded, or nested) -i=path Set import path + --iso8859-1 Use iso8859-1 encoding instead of utf-8 (default utf-8) --line-numbers Annotate selectors with comments referring to the source file and line number -p=precision Set decimal number precision (default 5) -T Dump formatted parse tree @@ -96,6 +98,11 @@ EOT; continue; } + if ($argv[$i] === '--iso8859-1') { + $encoding = 'iso8859-1'; + continue; + } + if ($argv[$i] === '--line-numbers' || $argv[$i] === '--line-comments') { $lineNumbers = true; continue; @@ -186,6 +193,10 @@ if ($style) { $scss->setFormatter('Leafo\\ScssPhp\\Formatter\\' . ucfirst($style)); } +if ($encoding) { + $scss->setEncoding($encoding); +} + echo $scss->compile($data, $inputFile); if ($changeDir) { diff --git a/src/Compiler.php b/src/Compiler.php index 57ec4d65..f55e1d7c 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -117,6 +117,7 @@ class Compiler 'global-variable-shadowing' => false, ); + protected $encoding = null; protected $lineNumberStyle = null; protected $formatter = 'Leafo\ScssPhp\Formatter\Nested'; @@ -208,7 +209,7 @@ public function compile($code, $path = null) */ private function parserFactory($path) { - $parser = new Parser($path, count($this->sourceNames)); + $parser = new Parser($path, count($this->sourceNames), $this->encoding); $this->sourceNames[] = $path; $this->addParsedFile($path); @@ -3239,6 +3240,18 @@ public function findImport($url) return null; } + /** + * Set encoding + * + * @api + * + * @param string $encoding + */ + public function setEncoding($encoding) + { + $this->encoding = $encoding; + } + /** * Ignore errors? * diff --git a/src/Parser.php b/src/Parser.php index a0986136..ac542dc4 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -62,6 +62,7 @@ class Parser private $inParens; private $eatWhiteDefault; private $buffer; + private $utf8; /** * Constructor @@ -70,12 +71,14 @@ class Parser * * @param string $sourceName * @param integer $sourceIndex + * @param string $encoding */ - public function __construct($sourceName, $sourceIndex = 0) + public function __construct($sourceName, $sourceIndex = 0, $encoding = 'utf-8') { $this->sourceName = $sourceName ?: '(stdin)'; $this->sourceIndex = $sourceIndex; $this->charset = null; + $this->utf8 = ! $encoding || strtolower($encoding) === 'utf-8'; if (empty(self::$operatorPattern)) { self::$operatorPattern = '([*\/%+-]|[!=]\=|\>\=?|\<\=\>|\<\=?|and|or)'; @@ -85,7 +88,9 @@ public function __construct($sourceName, $sourceIndex = 0) $commentMultiRight = '\*\/'; self::$commentPattern = $commentMultiLeft . '.*?' . $commentMultiRight; - self::$whitePattern = '/' . $commentSingle . '[^\n]*\s*|(' . self::$commentPattern . ')\s*|\s+/AisuS'; + self::$whitePattern = $this->utf8 + ? '/' . $commentSingle . '[^\n]*\s*|(' . self::$commentPattern . ')\s*|\s+/AisuS' + : '/' . $commentSingle . '[^\n]*\s*|(' . self::$commentPattern . ')\s*|\s+/AisS'; } } @@ -762,7 +767,7 @@ protected function peek($regex, &$out, $from = null) $from = $this->count; } - $r = '/' . $regex . '/Aisu'; + $r = $this->utf8 ? '/' . $regex . '/Aisu' : '/' . $regex . '/Ais'; $result = preg_match($r, $this->buffer, $out, null, $from); return $result; @@ -842,7 +847,7 @@ protected function match($regex, &$out, $eatWhitespace = null) $eatWhitespace = $this->eatWhiteDefault; } - $r = '/' . $regex . '/Aisu'; + $r = $this->utf8 ? '/' . $regex . '/Aisu' : '/' . $regex . '/Ais'; if (preg_match($r, $this->buffer, $out, null, $this->count)) { $this->count += strlen($out[0]); @@ -2235,7 +2240,9 @@ protected function variable(&$out) protected function keyword(&$word, $eatWhitespace = null) { if ($this->match( - '(([\pL\w_\-\*!"\']|[\\\\].)([\pL\w\-_"\']|[\\\\].)*)', + $this->utf8 + ? '(([\pL\w_\-\*!"\']|[\\\\].)([\pL\w\-_"\']|[\\\\].)*)' + : '(([\w_\-\*!"\']|[\\\\].)([\w\-_"\']|[\\\\].)*)', $m, $eatWhitespace )) { @@ -2256,7 +2263,12 @@ protected function keyword(&$word, $eatWhitespace = null) */ protected function placeholder(&$placeholder) { - if ($this->match('([\pL\w\-_]+|#[{][$][\pL\w\-_]+[}])', $m)) { + if ($this->match( + $this->utf8 + ? '([\pL\w\-_]+|#[{][$][\pL\w\-_]+[}])' + : '([\w\-_]+|#[{][$][\w\-_]+[}])', + $m + )) { $placeholder = $m[1]; return true; From 361faee948a150d4bb3623a67bb8efb98de2d33d Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 15 Dec 2015 17:03:18 -0500 Subject: [PATCH 165/484] rename Makefile target (conflict with tests/ folder) --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index d08ee190..b42e4a2b 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -tests: +test: vendor/bin/phpunit --colors tests compat: From b948673438b95e4bbf3fc55d3c1fec2d6f1cd8f4 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 16 Dec 2015 00:09:30 -0500 Subject: [PATCH 166/484] change die() to throw exception instead --- scss.inc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scss.inc.php b/scss.inc.php index d3720eb7..b6892fec 100644 --- a/scss.inc.php +++ b/scss.inc.php @@ -1,6 +1,6 @@ Date: Wed, 16 Dec 2015 00:41:13 -0500 Subject: [PATCH 167/484] fixes #394 - parser exception with mbstring.func_overload=2 and mbstring.internal_encoding=utf-8 --- src/Parser.php | 44 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/src/Parser.php b/src/Parser.php index ac542dc4..68f205ae 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -63,6 +63,7 @@ class Parser private $eatWhiteDefault; private $buffer; private $utf8; + private $encoding; /** * Constructor @@ -119,7 +120,7 @@ public function throwParseError($msg = 'parse error') { list($line, $column) = $this->getSourcePosition($this->count); - $loc = empty($this->sourceName) ? "line: $line" : "$this->sourceName on line $line"; + $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"); @@ -145,6 +146,7 @@ public function parse($buffer) $this->eatWhiteDefault = true; $this->buffer = rtrim($buffer, "\x00..\x1f"); + $this->saveEncoding(); $this->extractLineNumbers($buffer); $this->pushBlock(null); // root block @@ -170,6 +172,8 @@ public function parse($buffer) $this->env->isRoot = true; + $this->restoreEncoding(); + return $this->env; } @@ -191,7 +195,13 @@ public function parseValue($buffer, &$out) $this->eatWhiteDefault = true; $this->buffer = (string) $buffer; - return $this->valueList($out); + $this->saveEncoding(); + + $list = $this->valueList($out); + + $this->restoreEncoding(); + + return $list; } /** @@ -212,7 +222,13 @@ public function parseSelector($buffer, &$out) $this->eatWhiteDefault = true; $this->buffer = (string) $buffer; - return $this->selectors($out); + $this->saveEncoding(); + + $selector = $this->selectors($out); + + $this->restoreEncoding(); + + return $selector; } /** @@ -2485,4 +2501,26 @@ private function getSourcePosition($pos) return array($low + 1, $pos - $this->sourcePositions[$low]); } + + /** + * Save internal encoding + */ + private function saveEncoding() + { + if (ini_get('mbstring.func_overload') & 2) { + $this->encoding = mb_internal_encoding(); + + mb_internal_encoding('iso-8859-1'); + } + } + + /** + * Restore internal encoding + */ + private function restoreEncoding() + { + if ($this->encoding) { + mb_internal_encoding($this->encoding); + } + } } From e7fa252c35a24d5a1beb418fc0f2360848edc812 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 16 Dec 2015 00:58:24 -0500 Subject: [PATCH 168/484] optimze attribute selector code --- src/Parser.php | 63 +++++++++++--------------------------------------- 1 file changed, 13 insertions(+), 50 deletions(-) diff --git a/src/Parser.php b/src/Parser.php index 68f205ae..5b72dafb 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -1461,7 +1461,7 @@ protected function func(&$func) $this->seek($ss); } - if (($this->openString(')', $str, '(') || true ) && + if (($this->openString(')', $str, '(') || true) && $this->literal(')') ) { $args = array(); @@ -2135,7 +2135,7 @@ protected function selectorSingle(&$out) $ss = $this->seek(); if ($this->literal('(') && - ($this->openString(')', $str, '(') || true ) && + ($this->openString(')', $str, '(') || true) && $this->literal(')') ) { $parts[] = '('; @@ -2155,60 +2155,23 @@ protected function selectorSingle(&$out) $this->seek($s); // attribute selector - // TODO: replace with open string? - if ($this->literal('[', false)) { - $attrParts = array('['); - - // keyword, string, operator - for (;;) { - if ($this->literal(']', false)) { - $this->count--; - break; // get out early - } - - 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; - } + if ($this->literal('[') && + ($this->openString(']', $str, '[') || true) && + $this->literal(']') + ) { + $parts[] = '['; - break; + if (! empty($str)) { + $parts[] = $str; } - if ($this->literal(']', false)) { - $attrParts[] = ']'; + $parts[] = ']'; - foreach ($attrParts as $part) { - $parts[] = $part; - } - - continue; - } - - $this->seek($s); - // TODO: should just break here? + continue; } + $this->seek($s); + break; } From 0f52e57cadd1d52a3d4c00ce27fd74f3df638dbc Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 16 Dec 2015 01:13:15 -0500 Subject: [PATCH 169/484] unused "use" --- src/Compiler/Environment.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Compiler/Environment.php b/src/Compiler/Environment.php index e6314cbe..d44bff76 100644 --- a/src/Compiler/Environment.php +++ b/src/Compiler/Environment.php @@ -11,8 +11,6 @@ namespace Leafo\ScssPhp\Compiler; -use Leafo\ScssPhp\Block; - /** * Compiler environment * From 7edbcf499498f5376fa82da26a76b34f955b59e6 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 16 Dec 2015 01:13:43 -0500 Subject: [PATCH 170/484] add typehint --- src/Compiler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compiler.php b/src/Compiler.php index f55e1d7c..524a3c41 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -863,7 +863,7 @@ protected function compileKeyframeBlock(Block $block, $selectors) $envs = $this->compactEnv($env); - $this->env = $this->extractEnv(array_filter($envs, function ($e) { + $this->env = $this->extractEnv(array_filter($envs, function (Environment $e) { return ! isset($e->block->selectors); })); From 5951a597fe330179009abbd49afe717c92f1ee5f Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 16 Dec 2015 01:14:08 -0500 Subject: [PATCH 171/484] unused variables --- src/Parser.php | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/Parser.php b/src/Parser.php index 5b72dafb..19bd9319 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -631,7 +631,7 @@ protected function parseChunk() // opening css block if ($this->selectors($selectors) && $this->literal('{')) { - $b = $this->pushBlock($selectors, $s); + $this->pushBlock($selectors, $s); return true; } @@ -1006,8 +1006,6 @@ protected function mediaQueryList(&$out) */ protected function mediaQuery(&$out) { - $s = $this->seek(); - $expressions = null; $parts = array(); @@ -1496,8 +1494,6 @@ protected function argumentList(&$out) $args = array(); while ($this->keyword($var)) { - $ss = $this->seek(); - if ($this->literal('=') && $this->expression($exp)) { $args[] = array(Type::T_STRING, '', array($var . '=')); $arg = $exp; @@ -1765,8 +1761,6 @@ protected function string(&$out) */ protected function mixedKeyword(&$out) { - $s = $this->seek(); - $parts = array(); $oldWhite = $this->eatWhiteDefault; @@ -1918,7 +1912,6 @@ protected function interpolation(&$out, $lookWhite = true) */ protected function propertyName(&$out) { - $s = $this->seek(); $parts = array(); $oldWhite = $this->eatWhiteDefault; From fc1b22ca74bae384d36e9a1e771d66153ef22f46 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 16 Dec 2015 15:19:58 -0500 Subject: [PATCH 172/484] Remove the compressed patch -- it no longer makes any difference --- src/Formatter/Compressed.php | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/Formatter/Compressed.php b/src/Formatter/Compressed.php index 1e27240a..a54b3e3a 100644 --- a/src/Formatter/Compressed.php +++ b/src/Formatter/Compressed.php @@ -70,15 +70,4 @@ public function blockLines(OutputBlock $block) echo $this->break; } } - - /** - * {@inherit} - */ - public function format(OutputBlock $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))); - } } From 8443d11ad3c54c44a94a28aee2761e649de9bfda Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 16 Dec 2015 15:21:30 -0500 Subject: [PATCH 173/484] remove orphaned @todo --- src/Node/Number.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Node/Number.php b/src/Node/Number.php index 17e856db..6549f495 100644 --- a/src/Node/Number.php +++ b/src/Node/Number.php @@ -271,7 +271,6 @@ public function output(Compiler $compiler = null) return $unitSize; }); - // @todo refactor normalize() if (count($units) > 1 && array_sum($units) === 0) { $dimension = $this->dimension; $units = array(); From 1a95d89f15ba4cf876b9c6f331c7abe9a0bbf024 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 16 Dec 2015 15:26:28 -0500 Subject: [PATCH 174/484] remove unused variable declarations --- src/Compiler.php | 4 ++-- src/Parser.php | 2 +- src/Server.php | 7 +++---- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 524a3c41..46ba6aa7 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -479,7 +479,7 @@ protected function matchExtendsSingle($rawSingle, &$outOrigin) $found = false; foreach ($counts as $idx => $count) { - list($target, $origin, $block) = $this->extends[$idx]; + list($target, $origin, /* $block */) = $this->extends[$idx]; // check count if ($count !== count($target)) { @@ -1766,7 +1766,7 @@ protected function compileChild($child, OutputBlock $out) */ protected function expToString($exp) { - list(, $op, $left, $right, $inParens, $whiteLeft, $whiteRight) = $exp; + list(, $op, $left, $right, /* $inParens */, $whiteLeft, $whiteRight) = $exp; $content = array($this->reduce($left)); diff --git a/src/Parser.php b/src/Parser.php index 19bd9319..cd815900 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -118,7 +118,7 @@ public function getSourceName() */ public function throwParseError($msg = 'parse error') { - list($line, $column) = $this->getSourcePosition($this->count); + list($line, /* $column */) = $this->getSourcePosition($this->count); $loc = empty($this->sourceName) ? "line: $line" : "$this->sourceName on line $line"; diff --git a/src/Server.php b/src/Server.php index cada00e0..cbbdae15 100644 --- a/src/Server.php +++ b/src/Server.php @@ -116,13 +116,12 @@ protected function metadataName($out) /** * 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($in, $out, &$etag) + protected function needsCompile($out, &$etag) { if (! is_file($out)) { return true; @@ -326,7 +325,7 @@ public function serve($salt = '') $output = $this->cacheName($salt . $input); $etag = $noneMatch = trim($this->getIfNoneMatchHeader(), '"'); - if ($this->needsCompile($input, $output, $etag)) { + if ($this->needsCompile($output, $etag)) { try { list($css, $etag) = $this->compile($input, $output); @@ -408,7 +407,7 @@ public function checkedCachedCompile($in, $out, $force = false) throw new ServerException('Invalid or unwritable output file specified.'); } - if ($force || $this->needsCompile($in, $out, $etag)) { + if ($force || $this->needsCompile($out, $etag)) { list($css, $etag) = $this->compile($in, $out); } else { $css = file_get_contents($out); From 1152ca9c27f65da8e31abf26e1dff7d961abbb9d Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 16 Dec 2015 15:29:01 -0500 Subject: [PATCH 175/484] remove more TODOs --- src/Compiler.php | 1 - src/Parser.php | 2 -- 2 files changed, 3 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 46ba6aa7..ecdb8c97 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -4568,7 +4568,6 @@ protected function libLength($args) return count($list[2]); } - // TODO: need a way to declare this built-in as varargs //protected static $libListSeparator = array('list...'); protected function libListSeparator($args) { diff --git a/src/Parser.php b/src/Parser.php index cd815900..562ea701 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -1878,8 +1878,6 @@ protected function interpolation(&$out, $lookWhite = true) $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]) ? ' ': ''; From 4c6e8be425c6cc53c70a641a8cc097ad06727826 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 16 Dec 2015 15:34:14 -0500 Subject: [PATCH 176/484] refactoring (for size) --- src/Formatter/Compressed.php | 30 ++---------------------------- 1 file changed, 2 insertions(+), 28 deletions(-) diff --git a/src/Formatter/Compressed.php b/src/Formatter/Compressed.php index a54b3e3a..4a2bb41a 100644 --- a/src/Formatter/Compressed.php +++ b/src/Formatter/Compressed.php @@ -11,7 +11,7 @@ namespace Leafo\ScssPhp\Formatter; -use Leafo\ScssPhp\Formatter; +use Leafo\ScssPhp\Formatter\Crunched; use Leafo\ScssPhp\Formatter\OutputBlock; /** @@ -19,34 +19,8 @@ * * @author Leaf Corcoran */ -class Compressed extends Formatter +class Compressed extends Crunched { - /** - * {@inheritdoc} - */ - public function __construct() - { - $this->indentLevel = 0; - $this->indentChar = ' '; - $this->break = ''; - $this->open = '{'; - $this->close = '}'; - $this->tagSeparator = ','; - $this->assignSeparator = ':'; - } - - /** - * {@inheritdoc} - */ - public function stripSemicolon(&$lines) - { - if (($count = count($lines)) - && substr($lines[$count - 1], -1) === ';' - ) { - $lines[$count - 1] = substr($lines[$count - 1], 0, -1); - } - } - /** * {@inheritdoc} */ From ca57dd3e57859de6599a2ed4a769591fc5ca0501 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 16 Dec 2015 15:53:30 -0500 Subject: [PATCH 177/484] Compiler: refactoring duplicated code --- src/Compiler.php | 55 +++++++++++++++++++++++------------------------- 1 file changed, 26 insertions(+), 29 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index ecdb8c97..93c581ba 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -697,23 +697,10 @@ private function spliceTree($envs, Block $block, $without) } if (isset($e->block->type) && $e->block->type === Type::T_AT_ROOT) { - continue; - } - - if (($without & self::WITH_RULE) && isset($e->block->selectors)) { - continue; + continue; } - if (($without & self::WITH_MEDIA) && - isset($e->block->type) && $e->block->type === Type::T_MEDIA - ) { - continue; - } - - if (($without & self::WITH_SUPPORTS) && - isset($e->block->type) && $e->block->type === Type::T_DIRECTIVE && - isset($e->block->name) && $e->block->name === 'supports' - ) { + if ($this->isWithout($without, $e->block)) { continue; } @@ -828,20 +815,7 @@ private function filterWithout($envs, $without) $filtered = array(); foreach ($envs as $e) { - if (($without & self::WITH_RULE) && isset($e->block->selectors)) { - continue; - } - - if (($without & self::WITH_MEDIA) && - isset($e->block->type) && $e->block->type === Type::T_MEDIA - ) { - continue; - } - - if (($without & self::WITH_SUPPORTS) && - isset($e->block->type) && $e->block->type === Type::T_DIRECTIVE && - isset($e->block->name) && $e->block->name === 'supports' - ) { + if ($this->isWithout($without, $e->block)) { continue; } @@ -851,6 +825,29 @@ private function filterWithout($envs, $without) return $this->extractEnv($filtered); } + /** + * Filter WITH rules + * + * @param integer $without + * @param \stdClass $block + * + * @return boolean + */ + private function isWithout($without, $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 * From 2996d07d16d53b2e6fe222b20a67e3322bbec43a Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 16 Dec 2015 15:57:07 -0500 Subject: [PATCH 178/484] Server: remove @ operator on date functions --- src/Server.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Server.php b/src/Server.php index cbbdae15..e34ff5c3 100644 --- a/src/Server.php +++ b/src/Server.php @@ -207,7 +207,7 @@ protected function compile($in, $out) $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); @@ -365,7 +365,7 @@ public function serve($salt = '') $modifiedSince = $this->getIfModifiedSinceHeader(); $mtime = filemtime($output); - if (@strtotime($modifiedSince) === $mtime) { + if (strtotime($modifiedSince) === $mtime) { header($protocol . ' 304 Not Modified'); return; @@ -444,6 +444,10 @@ public function __construct($dir, $cacheDir = null, $scss = null) $this->scss = $scss; $this->showErrorsAsCSS = false; + + if (! ini_get('date.timezone')) { + date_default_timezone_set('UTC'); + } } /** From 2e1f5bd062faa2c7cf23519d6d171287663bae59 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 16 Dec 2015 16:03:40 -0500 Subject: [PATCH 179/484] Compiler: add typehint to isWithout() --- src/Compiler.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 93c581ba..04c1af01 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -700,7 +700,7 @@ private function spliceTree($envs, Block $block, $without) continue; } - if ($this->isWithout($without, $e->block)) { + if ($e->block && $this->isWithout($without, $e->block)) { continue; } @@ -815,7 +815,7 @@ private function filterWithout($envs, $without) $filtered = array(); foreach ($envs as $e) { - if ($this->isWithout($without, $e->block)) { + if ($e->block && $this->isWithout($without, $e->block)) { continue; } @@ -828,12 +828,12 @@ private function filterWithout($envs, $without) /** * Filter WITH rules * - * @param integer $without - * @param \stdClass $block + * @param integer $without + * @param \Leafo\ScssPhp\Block $block * * @return boolean */ - private function isWithout($without, $block) + private function isWithout($without, Block $block) { if ((($without & self::WITH_RULE) && isset($block->selectors)) || (($without & self::WITH_MEDIA) && From 9177e859a01ca1395fbede153d1eb93ab86645d4 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 16 Dec 2015 16:22:06 -0500 Subject: [PATCH 180/484] composition via trait --- scss.inc.php | 1 + src/Formatter/Compressed.php | 6 ++-- src/Formatter/Crunched.php | 26 +---------------- src/Formatter/StripSemiColons.php | 46 +++++++++++++++++++++++++++++++ 4 files changed, 52 insertions(+), 27 deletions(-) create mode 100644 src/Formatter/StripSemiColons.php diff --git a/scss.inc.php b/scss.inc.php index b6892fec..ddfd1297 100644 --- a/scss.inc.php +++ b/scss.inc.php @@ -20,6 +20,7 @@ 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/Formatter/StripSemiColons.php'; include_once __DIR__ . '/src/Node.php'; include_once __DIR__ . '/src/Node/Number.php'; include_once __DIR__ . '/src/Parser.php'; diff --git a/src/Formatter/Compressed.php b/src/Formatter/Compressed.php index 4a2bb41a..af55da02 100644 --- a/src/Formatter/Compressed.php +++ b/src/Formatter/Compressed.php @@ -11,7 +11,7 @@ namespace Leafo\ScssPhp\Formatter; -use Leafo\ScssPhp\Formatter\Crunched; +use Leafo\ScssPhp\Formatter; use Leafo\ScssPhp\Formatter\OutputBlock; /** @@ -19,8 +19,10 @@ * * @author Leaf Corcoran */ -class Compressed extends Crunched +class Compressed extends Formatter { + use StripSemiColons; + /** * {@inheritdoc} */ diff --git a/src/Formatter/Crunched.php b/src/Formatter/Crunched.php index 5e80a824..c31cac56 100644 --- a/src/Formatter/Crunched.php +++ b/src/Formatter/Crunched.php @@ -21,31 +21,7 @@ */ class Crunched extends Formatter { - /** - * {@inheritdoc} - */ - public function __construct() - { - $this->indentLevel = 0; - $this->indentChar = ' '; - $this->break = ''; - $this->open = '{'; - $this->close = '}'; - $this->tagSeparator = ','; - $this->assignSeparator = ':'; - } - - /** - * {@inheritdoc} - */ - public function stripSemicolon(&$lines) - { - if (($count = count($lines)) - && substr($lines[$count - 1], -1) === ';' - ) { - $lines[$count - 1] = substr($lines[$count - 1], 0, -1); - } - } + use StripSemiColons; /** * {@inheritdoc} diff --git a/src/Formatter/StripSemiColons.php b/src/Formatter/StripSemiColons.php new file mode 100644 index 00000000..8e42c16e --- /dev/null +++ b/src/Formatter/StripSemiColons.php @@ -0,0 +1,46 @@ + + */ +trait StripSemiColons +{ + /** + * {@inheritdoc} + */ + public function __construct() + { + $this->indentLevel = 0; + $this->indentChar = ' '; + $this->break = ''; + $this->open = '{'; + $this->close = '}'; + $this->tagSeparator = ','; + $this->assignSeparator = ':'; + } + + /** + * {@inheritdoc} + */ + public function stripSemicolon(&$lines) + { + if (($count = count($lines)) + && substr($lines[$count - 1], -1) === ';' + ) { + $lines[$count - 1] = substr($lines[$count - 1], 0, -1); + } + } +} From 183412ca18111b2484bca0f2327df6ee8af97945 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 16 Dec 2015 16:22:20 -0500 Subject: [PATCH 181/484] errant tabs --- src/Compiler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compiler.php b/src/Compiler.php index 04c1af01..3039a704 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -697,7 +697,7 @@ private function spliceTree($envs, Block $block, $without) } if (isset($e->block->type) && $e->block->type === Type::T_AT_ROOT) { - continue; + continue; } if ($e->block && $this->isWithout($without, $e->block)) { From 7f2ae8d1a8d324052460be2061143a3663f1f479 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 16 Dec 2015 17:18:28 -0500 Subject: [PATCH 182/484] Update codesniffer to 2.5.0 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 691a68a2..112a6bde 100644 --- a/composer.json +++ b/composer.json @@ -24,7 +24,7 @@ "php": ">=5.4.0" }, "require-dev": { - "squizlabs/php_codesniffer": "~2.3", + "squizlabs/php_codesniffer": "~2.5", "phpunit/phpunit": "~3.7", "kherge/box": "~2.5" }, From 69e49077ed9d4c16d2bfd2b74cb6b9b6d00dcd3d Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 16 Dec 2015 17:19:22 -0500 Subject: [PATCH 183/484] php5.4: Use shorthand array() syntax --- src/Colors.php | 4 +- src/Compiler.php | 498 +++++++++++++++++++-------------------- src/Formatter/Nested.php | 2 +- src/Node/Number.php | 38 +-- src/Parser.php | 181 +++++++------- src/Server.php | 10 +- 6 files changed, 366 insertions(+), 367 deletions(-) diff --git a/src/Colors.php b/src/Colors.php index e3936a5f..ff48c199 100644 --- a/src/Colors.php +++ b/src/Colors.php @@ -25,7 +25,7 @@ class Colors * * @var array */ - public static $cssColors = array( + public static $cssColors = [ 'aliceblue' => '240,248,255', 'antiquewhite' => '250,235,215', 'aqua' => '0,255,255', @@ -175,5 +175,5 @@ class Colors 'whitesmoke' => '245,245,245', 'yellow' => '255,255,0', 'yellowgreen' => '154,205,50', - ); + ]; } diff --git a/src/Compiler.php b/src/Compiler.php index 3039a704..64dfb8b3 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -67,7 +67,7 @@ class Compiler /** * @var array */ - static protected $operatorNames = array( + static protected $operatorNames = [ '+' => 'add', '-' => 'sub', '*' => 'mul', @@ -82,40 +82,40 @@ class Compiler '<=' => 'lte', '>=' => 'gte', '<=>' => 'cmp', - ); + ]; /** * @var array */ - static protected $namespaces = array( + static protected $namespaces = [ 'special' => '%', 'mixin' => '@', 'function' => '^', - ); - - static public $true = array(Type::T_KEYWORD, 'true'); - static public $false = array(Type::T_KEYWORD, 'false'); - static public $null = array(Type::T_NULL); - static public $nullString = array(Type::T_STRING, '', array()); - static public $defaultValue = array(Type::T_KEYWORD, ''); - static public $selfSelector = array(Type::T_SELF); - static public $emptyList = array(Type::T_LIST, '', array()); - static public $emptyMap = array(Type::T_MAP, array(), array()); - static public $emptyString = array(Type::T_STRING, '"', array()); - static public $with = array(Type::T_KEYWORD, 'with'); - static public $without = array(Type::T_KEYWORD, 'without'); - - protected $importPaths = array(''); - protected $importCache = array(); - protected $importedFiles = array(); - protected $userFunctions = array(); - protected $registeredVars = array(); - protected $registeredFeatures = array( + ]; + + 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; protected $lineNumberStyle = null; @@ -148,8 +148,8 @@ class Compiler */ public function __construct() { - $this->parsedFiles = array(); - $this->sourceNames = array(); + $this->parsedFiles = []; + $this->sourceNames = []; } /** @@ -168,9 +168,9 @@ public function compile($code, $path = null) setlocale(LC_NUMERIC, 'C'); $this->indentLevel = -1; - $this->commentsSeen = array(); - $this->extends = array(); - $this->extendsMap = array(); + $this->commentsSeen = []; + $this->extends = []; + $this->extendsMap = []; $this->sourceIndex = null; $this->sourceLine = null; $this->sourceColumn = null; @@ -250,13 +250,13 @@ protected function pushExtends($target, $origin, $block) } $i = count($this->extends); - $this->extends[] = array($target, $origin, $block); + $this->extends[] = [$target, $origin, $block]; foreach ($target as $part) { if (isset($this->extendsMap[$part])) { $this->extendsMap[$part][] = $i; } else { - $this->extendsMap[$part] = array($i); + $this->extendsMap[$part] = [$i]; } } } @@ -273,8 +273,8 @@ protected function makeOutputBlock($type, $selectors = null) { $out = new OutputBlock; $out->type = $type; - $out->lines = array(); - $out->children = array(); + $out->lines = []; + $out->children = []; $out->parent = $this->scope; $out->selectors = $selectors; $out->depth = $this->env->depth; @@ -330,7 +330,7 @@ protected function missingSelectors() protected function flattenSelectors(OutputBlock $block, $parentKey = null) { if ($block->selectors) { - $selectors = array(); + $selectors = []; foreach ($block->selectors as $s) { $selectors[] = $s; @@ -356,7 +356,7 @@ protected function flattenSelectors(OutputBlock $block, $parentKey = null) } } - $block->selectors = array(); + $block->selectors = []; $placeholderSelector = false; foreach ($selectors as $selector) { @@ -451,8 +451,8 @@ protected function matchExtends($selector, &$out, $from = 0, $initial = true) */ protected function matchExtendsSingle($rawSingle, &$outOrigin) { - $counts = array(); - $single = array(); + $counts = []; + $single = []; foreach ($rawSingle as $part) { // matches Number @@ -475,7 +475,7 @@ protected function matchExtendsSingle($rawSingle, &$outOrigin) } } - $outOrigin = array(); + $outOrigin = []; $found = false; foreach ($counts as $idx => $count) { @@ -522,9 +522,9 @@ protected function matchExtendsSingle($rawSingle, &$outOrigin) protected function combineSelectorSingle($base, $other) { $tag = null; - $out = array(); + $out = []; - foreach (array($base, $other) as $single) { + foreach ([$base, $other] as $single) { foreach ($single as $part) { if (preg_match('/^[^\[.#:]/', $part)) { $tag = $part; @@ -553,7 +553,7 @@ protected function compileMedia(Block $media) $mediaQuery = $this->compileMediaQuery($this->multiplyMedia($this->env)); if (! empty($mediaQuery)) { - $this->scope = $this->makeOutputBlock(Type::T_MEDIA, array($mediaQuery)); + $this->scope = $this->makeOutputBlock(Type::T_MEDIA, [$mediaQuery]); $parentScope = $this->mediaParent($this->scope); $parentScope->children[] = $this->scope; @@ -579,12 +579,12 @@ protected function compileMedia(Block $media) $wrapped->sourceIndex = $media->sourceIndex; $wrapped->sourceLine = $media->sourceLine; $wrapped->sourceColumn = $media->sourceColumn; - $wrapped->selectors = array(); - $wrapped->comments = array(); + $wrapped->selectors = []; + $wrapped->comments = []; $wrapped->parent = $media; $wrapped->children = $media->children; - $media->children = array(array(Type::T_BLOCK, $wrapped)); + $media->children = [[Type::T_BLOCK, $wrapped]]; } $this->compileChildrenNoReturn($media->children, $this->scope); @@ -629,9 +629,9 @@ protected function compileDirective(Block $block) } if ($block->name === 'keyframes' || substr($block->name, -10) === '-keyframes') { - $this->compileKeyframeBlock($block, array($s)); + $this->compileKeyframeBlock($block, [$s]); } else { - $this->compileNestedBlock($block, array($s)); + $this->compileNestedBlock($block, [$s]); } } @@ -653,11 +653,11 @@ protected function compileAtRoot(Block $block) $wrapped->sourceLine = $block->sourceLine; $wrapped->sourceColumn = $block->sourceColumn; $wrapped->selectors = $block->selector; - $wrapped->comments = array(); + $wrapped->comments = []; $wrapped->parent = $block; $wrapped->children = $block->children; - $block->children = array(array(Type::T_BLOCK, $wrapped)); + $block->children = [[Type::T_BLOCK, $wrapped]]; } $this->env = $this->filterWithout($envs, $without); @@ -708,14 +708,14 @@ private function spliceTree($envs, Block $block, $without) $b->sourceIndex = $e->block->sourceIndex; $b->sourceLine = $e->block->sourceLine; $b->sourceColumn = $e->block->sourceColumn; - $b->selectors = array(); + $b->selectors = []; $b->comments = $e->block->comments; $b->parent = null; if ($newBlock) { $type = isset($newBlock->type) ? $newBlock->type : Type::T_BLOCK; - $b->children = array(array($type, $newBlock)); + $b->children = [[$type, $newBlock]]; $newBlock->parent = $b; } elseif (count($block->children)) { @@ -749,7 +749,7 @@ private function spliceTree($envs, Block $block, $without) $type = isset($newBlock->type) ? $newBlock->type : Type::T_BLOCK; - return array($type, $newBlock); + return [$type, $newBlock]; } /** @@ -761,20 +761,20 @@ private function spliceTree($envs, Block $block, $without) */ private function compileWith($with) { - static $mapping = array( + 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(array($with, self::$with))) { + if ($this->libMapHasKey([$with, self::$with])) { $without = self::WITH_ALL; - $list = $this->coerceList($this->libMapGet(array($with, self::$with))); + $list = $this->coerceList($this->libMapGet([$with, self::$with])); foreach ($list[2] as $item) { $keyword = $this->compileStringContent($this->coerceString($item)); @@ -785,10 +785,10 @@ private function compileWith($with) } } - if ($this->libMapHasKey(array($with, self::$without))) { + if ($this->libMapHasKey([$with, self::$without])) { $without = 0; - $list = $this->coerceList($this->libMapGet(array($with, self::$without))); + $list = $this->coerceList($this->libMapGet([$with, self::$without])); foreach ($list[2] as $item) { $keyword = $this->compileStringContent($this->coerceString($item)); @@ -812,7 +812,7 @@ private function compileWith($with) */ private function filterWithout($envs, $without) { - $filtered = array(); + $filtered = []; foreach ($envs as $e) { if ($e->block && $this->isWithout($without, $e->block)) { @@ -978,7 +978,7 @@ protected function evalSelectors($selectors) { $this->shouldEvaluate = false; - $selectors = array_map(array($this, 'evalSelector'), $selectors); + $selectors = array_map([$this, 'evalSelector'], $selectors); // after evaluating interpolates, we might need a second pass if ($this->shouldEvaluate) { @@ -986,7 +986,7 @@ protected function evalSelectors($selectors) $parser = $this->parserFactory(__METHOD__); if ($parser->parseSelector($buffer, $newSelectors)) { - $selectors = array_map(array($this, 'evalSelector'), $newSelectors); + $selectors = array_map([$this, 'evalSelector'], $newSelectors); } } @@ -1002,7 +1002,7 @@ protected function evalSelectors($selectors) */ protected function evalSelector($selector) { - return array_map(array($this, 'evalSelectorPart'), $selector); + return array_map([$this, 'evalSelectorPart'], $selector); } /** @@ -1042,7 +1042,7 @@ protected function evalSelectorPart($part) */ protected function collapseSelectors($selectors) { - $parts = array(); + $parts = []; foreach ($selectors as $selector) { $output = ''; @@ -1069,7 +1069,7 @@ function ($value, $key) use (&$output) { */ protected function flattenSelectorSingle($single) { - $joined = array(); + $joined = []; foreach ($single as $part) { if (empty($joined) || @@ -1106,7 +1106,7 @@ protected function compileSelector($selector) return implode( ' ', array_map( - array($this, 'compileSelectorPart'), + [$this, 'compileSelectorPart'], $selector ) ); @@ -1218,7 +1218,7 @@ protected function compileMediaQuery($queryList) foreach ($queryList as $query) { $type = null; - $parts = array(); + $parts = []; foreach ($query as $q) { switch ($q[0]) { @@ -1226,14 +1226,14 @@ protected function compileMediaQuery($queryList) if ($type) { $type = $this->mergeMediaTypes( $type, - array_map(array($this, 'compileValue'), array_slice($q, 1)) + array_map([$this, 'compileValue'], array_slice($q, 1)) ); if (empty($type)) { // merge failed return null; } } else { - $type = array_map(array($this, 'compileValue'), array_slice($q, 1)); + $type = array_map([$this, 'compileValue'], array_slice($q, 1)); } break; @@ -1319,10 +1319,10 @@ protected function mergeMediaTypes($type1, $type2) return null; } - return array( + return [ $m1 === Type::T_NOT ? $m2 : $m1, $m1 === Type::T_NOT ? $t2 : $t1, - ); + ]; } if ($m1 === Type::T_NOT && $m2 === Type::T_NOT) { @@ -1331,7 +1331,7 @@ protected function mergeMediaTypes($type1, $type2) return null; } - return array(Type::T_NOT, $t1); + return [Type::T_NOT, $t1]; } if ($t1 !== $t2) { @@ -1339,7 +1339,7 @@ protected function mergeMediaTypes($type1, $type2) } // t1 == t2, neither m1 nor m2 are "not" - return array(empty($m1)? $m2 : $m1, $t1); + return [empty($m1)? $m2 : $m1, $t1]; } /** @@ -1525,7 +1525,7 @@ protected function compileChild($child, OutputBlock $out) list(, $selectors) = $child; foreach ($selectors as $sel) { - $results = $this->evalSelectors(array($sel)); + $results = $this->evalSelectors([$sel]); foreach ($results as $result) { // only use the first one @@ -1640,10 +1640,10 @@ protected function compileChild($child, OutputBlock $out) break; case Type::T_BREAK: - return array(Type::T_CONTROL, true); + return [Type::T_CONTROL, true]; case Type::T_CONTINUE: - return array(Type::T_CONTROL, false); + return [Type::T_CONTROL, false]; case Type::T_RETURN: return $this->reduce($child[1], true); @@ -1651,7 +1651,7 @@ protected function compileChild($child, OutputBlock $out) case Type::T_NESTED_PROPERTY: list(, $prop) = $child; - $prefixed = array(); + $prefixed = []; $prefix = $this->compileValue($prop->prefix) . '-'; foreach ($prop->children as $child) { @@ -1765,7 +1765,7 @@ protected function expToString($exp) { list(, $op, $left, $right, /* $inParens */, $whiteLeft, $whiteRight) = $exp; - $content = array($this->reduce($left)); + $content = [$this->reduce($left)]; if ($whiteLeft) { $content[] = ' '; @@ -1779,7 +1779,7 @@ protected function expToString($exp) $content[] = $this->reduce($right); - return array(Type::T_STRING, '', $content); + return [Type::T_STRING, '', $content]; } /** @@ -1867,12 +1867,12 @@ protected function reduce($value, $inExp = false) // 3. op[op name] $fn = "op${ucOpName}${ucLType}${ucRType}"; - if (is_callable(array($this, $fn)) || + if (is_callable([$this, $fn]) || (($fn = "op${ucLType}${ucRType}") && - is_callable(array($this, $fn)) && + is_callable([$this, $fn]) && $passOp = true) || (($fn = "op${ucOpName}") && - is_callable(array($this, $fn)) && + is_callable([$this, $fn]) && $genOp = true) ) { $coerceUnit = false; @@ -1960,7 +1960,7 @@ protected function reduce($value, $inExp = false) $op = $op . ' '; } - return array(Type::T_STRING, '', array($op, $exp)); + return [Type::T_STRING, '', [$op, $exp]]; case Type::T_VARIABLE: list(, $name) = $value; @@ -2030,7 +2030,7 @@ private function fncall($name, $argValues) } // for CSS functions, simply flatten the arguments into a list - $listArgs = array(); + $listArgs = []; foreach ((array) $argValues as $arg) { if (empty($arg[0])) { @@ -2038,7 +2038,7 @@ private function fncall($name, $argValues) } } - return array(Type::T_FUNCTION, $name, array(Type::T_LIST, ',', $listArgs)); + return [Type::T_FUNCTION, $name, [Type::T_LIST, ',', $listArgs]]; } /** @@ -2070,7 +2070,7 @@ public function normalizeValue($value) $value = $this->extractInterpolation($value); if ($value[0] !== Type::T_LIST) { - return array(Type::T_KEYWORD, $this->compileValue($value)); + return [Type::T_KEYWORD, $this->compileValue($value)]; } foreach ($value[2] as $key => $item) { @@ -2080,13 +2080,13 @@ public function normalizeValue($value) return $value; case Type::T_STRING: - return array($type, '"', array($this->compileStringContent($value))); + return [$type, '"', [$this->compileStringContent($value)]]; case Type::T_NUMBER: return $value->normalize(); case Type::T_INTERPOLATE: - return array(Type::T_KEYWORD, $this->compileValue($value)); + return [Type::T_KEYWORD, $this->compileValue($value)]; default: return $value; @@ -2143,7 +2143,7 @@ protected function opSubNumberNumber($left, $right) protected function opDivNumberNumber($left, $right) { if ($right[1] == 0) { - return array(Type::T_STRING, '', array($left[1] . $left[2] . '/' . $right[1] . $right[2])); + return [Type::T_STRING, '', [$left[1] . $left[2] . '/' . $right[1] . $right[2]]]; } return new Node\Number($left[1] / $right[1], $left[2]); @@ -2248,9 +2248,9 @@ protected function opOr($left, $right, $shouldEval) */ protected function opColorColor($op, $left, $right) { - $out = array(Type::T_COLOR); + $out = [Type::T_COLOR]; - foreach (array(1, 2, 3) as $i) { + foreach ([1, 2, 3] as $i) { $lval = isset($left[$i]) ? $left[$i] : 0; $rval = isset($right[$i]) ? $right[$i] : 0; @@ -2317,7 +2317,7 @@ protected function opColorNumber($op, $left, $right) return $this->opColorColor( $op, $left, - array(Type::T_COLOR, $value, $value, $value) + [Type::T_COLOR, $value, $value, $value] ); } @@ -2336,7 +2336,7 @@ protected function opNumberColor($op, $left, $right) return $this->opColorColor( $op, - array(Type::T_COLOR, $value, $value, $value), + [Type::T_COLOR, $value, $value, $value], $right ); } @@ -2539,7 +2539,7 @@ public function compileValue($value) $delim .= ' '; } - $filtered = array(); + $filtered = []; foreach ($items as $item) { if ($item[0] === Type::T_NULL) { @@ -2554,7 +2554,7 @@ public function compileValue($value) case Type::T_MAP: $keys = $value[1]; $values = $value[2]; - $filtered = array(); + $filtered = []; for ($i = 0, $s = count($keys); $i < $s; $i++) { $filtered[$this->compileValue($keys[$i])] = $this->compileValue($values[$i]); @@ -2588,11 +2588,11 @@ public function compileValue($value) switch ($reduced[0]) { case Type::T_STRING: - $reduced = array(Type::T_KEYWORD, $this->compileStringContent($reduced)); + $reduced = [Type::T_KEYWORD, $this->compileStringContent($reduced)]; break; case Type::T_NULL: - $reduced = array(Type::T_KEYWORD, ''); + $reduced = [Type::T_KEYWORD, '']; } return $this->compileValue($reduced); @@ -2626,7 +2626,7 @@ protected function flattenList($list) */ protected function compileStringContent($string) { - $parts = array(); + $parts = []; foreach ($string[2] as $part) { if (is_array($part) || $part instanceof \ArrayAccess) { @@ -2652,10 +2652,10 @@ protected function extractInterpolation($list) foreach ($items as $i => $item) { if ($item[0] === Type::T_INTERPOLATE) { - $before = array(Type::T_LIST, $list[1], array_slice($items, 0, $i)); - $after = array(Type::T_LIST, $list[1], array_slice($items, $i + 1)); + $before = [Type::T_LIST, $list[1], array_slice($items, 0, $i)]; + $after = [Type::T_LIST, $list[1], array_slice($items, $i + 1)]; - return array(Type::T_INTERPOLATED, $item, $before, $after); + return [Type::T_INTERPOLATED, $item, $before, $after]; } } @@ -2672,15 +2672,15 @@ protected function extractInterpolation($list) protected function multiplySelectors(Environment $env) { $envs = $this->compactEnv($env); - $selectors = array(); - $parentSelectors = array(array()); + $selectors = []; + $parentSelectors = [[]]; while ($env = array_pop($envs)) { if (empty($env->selectors)) { continue; } - $selectors = array(); + $selectors = []; foreach ($env->selectors as $selector) { foreach ($parentSelectors as $parent) { @@ -2705,10 +2705,10 @@ protected function multiplySelectors(Environment $env) protected function joinSelectors($parent, $child) { $setSelf = false; - $out = array(); + $out = []; foreach ($child as $part) { - $newPart = array(); + $newPart = []; foreach ($part as $p) { if ($p === self::$selfSelector) { @@ -2717,7 +2717,7 @@ protected function joinSelectors($parent, $child) foreach ($parent as $i => $parentPart) { if ($i > 0) { $out[] = $newPart; - $newPart = array(); + $newPart = []; } foreach ($parentPart as $pp) { @@ -2758,13 +2758,13 @@ protected function multiplyMedia(Environment $env = null, $childQueries = null) $parentQueries = isset($env->block->queryList) ? $env->block->queryList - : array(array(array(Type::T_MEDIA_VALUE, $env->block->value))); + : [[[Type::T_MEDIA_VALUE, $env->block->value]]]; if ($childQueries === null) { $childQueries = $parentQueries; } else { $originalQueries = $childQueries; - $childQueries = array(); + $childQueries = []; foreach ($parentQueries as $parentQuery) { foreach ($originalQueries as $childQuery) { @@ -2785,7 +2785,7 @@ protected function multiplyMedia(Environment $env = null, $childQueries = null) */ private function compactEnv(Environment $env) { - for ($envs = array(); $env; $env = $env->parent) { + for ($envs = []; $env; $env = $env->parent) { $envs[] = $env; } @@ -2820,7 +2820,7 @@ protected function pushEnv(Block $block = null) { $env = new Environment; $env->parent = $this->env; - $env->store = array(); + $env->store = []; $env->block = $block; $env->depth = isset($this->env->depth) ? $this->env->depth + 1 : 0; @@ -3135,7 +3135,7 @@ public function setLineNumberStyle($lineNumberStyle) */ public function registerFunction($name, $func, $prototype = null) { - $this->userFunctions[$this->normalizeName($name)] = array($func, $prototype); + $this->userFunctions[$this->normalizeName($name)] = [$func, $prototype]; } /** @@ -3202,12 +3202,12 @@ protected function importFile($path, $out) */ public function findImport($url) { - $urls = array(); + $urls = []; // for "normal" scss imports (ignore vanilla css and external requests) if (! preg_match('/\.css$|^https?:\/\//', $url)) { // try both normal and the _partial filename - $urls = array($url, preg_replace('/[^\/]+$/', '_\0', $url)); + $urls = [$url, preg_replace('/[^\/]+$/', '_\0', $url)]; } foreach ($this->importPaths as $dir) { @@ -3345,8 +3345,8 @@ protected function callScssFunction($name, $argValues, &$returnValue) // throw away lines and children $tmp = new OutputBlock; - $tmp->lines = array(); - $tmp->children = array(); + $tmp->lines = []; + $tmp->children = []; $this->env->marker = 'function'; @@ -3419,7 +3419,7 @@ function ($m) { ucfirst($name) ); - return array($this, $libName); + return [$this, $libName]; } /** @@ -3432,8 +3432,8 @@ function ($m) { */ protected function sortArgs($prototype, $args) { - $keyArgs = array(); - $posArgs = array(); + $keyArgs = []; + $posArgs = []; // separate positional and keyword arguments foreach ($args as $arg) { @@ -3449,7 +3449,7 @@ protected function sortArgs($prototype, $args) } if (! isset($prototype)) { - return array($posArgs, $keyArgs); + return [$posArgs, $keyArgs]; } // copy positional args @@ -3464,7 +3464,7 @@ protected function sortArgs($prototype, $args) } } - return array($finalArgs, $keyArgs); + return [$finalArgs, $keyArgs]; } /** @@ -3483,18 +3483,18 @@ protected function applyArguments($argDef, $argValues) $env->store = $storeEnv->store; $hasVariable = false; - $args = array(); + $args = []; foreach ($argDef as $i => $arg) { list($name, $default, $isVariable) = $argDef[$i]; - $args[$name] = array($i, $name, $default, $isVariable); + $args[$name] = [$i, $name, $default, $isVariable]; $hasVariable |= $isVariable; } - $keywordArgs = array(); - $deferredKeywordArgs = array(); - $remaining = array(); + $keywordArgs = []; + $deferredKeywordArgs = []; + $remaining = []; // assign the keyword args foreach ((array) $argValues as $arg) { @@ -3549,7 +3549,7 @@ protected function applyArguments($argDef, $argValues) list($i, $name, $default, $isVariable) = $arg; if ($isVariable) { - $val = array(Type::T_LIST, ',', array(), $isVariable); + $val = [Type::T_LIST, ',', [], $isVariable]; for ($count = count($remaining); $i < $count; $i++) { $val[2][] = $remaining[$i]; @@ -3614,7 +3614,7 @@ private function coerceValue($value) return self::$emptyString; } - return array(Type::T_KEYWORD, $value); + return [Type::T_KEYWORD, $value]; } /** @@ -3634,7 +3634,7 @@ protected function coerceMap($item) return self::$emptyMap; } - return array(Type::T_MAP, array($item), array(self::$null)); + return [Type::T_MAP, [$item], [self::$null]]; } /** @@ -3653,23 +3653,23 @@ protected function coerceList($item, $delim = ',') if (isset($item) && $item[0] === Type::T_MAP) { $keys = $item[1]; $values = $item[2]; - $list = array(); + $list = []; for ($i = 0, $s = count($keys); $i < $s; $i++) { $key = $keys[$i]; $value = $values[$i]; - $list[] = array( + $list[] = [ Type::T_LIST, '', - array(array(Type::T_KEYWORD, $this->compileStringContent($this->coerceString($key))), $value) - ); + [[Type::T_KEYWORD, $this->compileStringContent($this->coerceString($key))], $value] + ]; } - return array(Type::T_LIST, ',', $list); + return [Type::T_LIST, ',', $list]; } - return array(Type::T_LIST, $delim, ! isset($item) ? array(): array($item)); + return [Type::T_LIST, $delim, ! isset($item) ? []: [$item]]; } /** @@ -3708,8 +3708,8 @@ protected function coerceColor($value) $rgba = explode(',', Colors::$cssColors[$name]); return isset($rgba[3]) - ? array(Type::T_COLOR, (int) $rgba[0], (int) $rgba[1], (int) $rgba[2], (int) $rgba[3]) - : array(Type::T_COLOR, (int) $rgba[0], (int) $rgba[1], (int) $rgba[2]); + ? [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]]; } return null; @@ -3731,7 +3731,7 @@ protected function coerceString($value) return $value; } - return array(Type::T_STRING, '', array($this->compileValue($value))); + return [Type::T_STRING, '', [$this->compileValue($value)]]; } /** @@ -3845,7 +3845,7 @@ public function assertNumber($value) */ protected function fixColor($c) { - foreach (array(1, 2, 3) as $i) { + foreach ([1, 2, 3] as $i) { if ($c[$i] < 0) { $c[$i] = 0; } @@ -3895,7 +3895,7 @@ public function toHSL($red, $green, $blue) } } - return array(Type::T_HSL, fmod($h, 360), $s * 100, $l / 5.1); + return [Type::T_HSL, fmod($h, 360), $s * 100, $l / 5.1]; } /** @@ -3958,35 +3958,35 @@ public function toRGB($hue, $saturation, $lightness) $g = $this->hueToRGB($m1, $m2, $h) * 255; $b = $this->hueToRGB($m1, $m2, $h - 1/3) * 255; - $out = array(Type::T_COLOR, $r, $g, $b); + $out = [Type::T_COLOR, $r, $g, $b]; return $out; } // Built in functions - //protected static $libCall = array('name', 'args...'); + //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 array(null, $a, false); + return [null, $a, false]; }, $args ); if (count($kwargs)) { foreach ($kwargs as $key => $value) { - $args[] = array(array(Type::T_VARIABLE, $key), $value, false); + $args[] = [[Type::T_VARIABLE, $key], $value, false]; } } - return $this->reduce(array(Type::T_FUNCTION_CALL, $name, $args)); + return $this->reduce([Type::T_FUNCTION_CALL, $name, $args]); } - protected static $libIf = array('condition', 'if-true', 'if-false'); + protected static $libIf = ['condition', 'if-true', 'if-false']; protected function libIf($args) { list($cond, $t, $f) = $args; @@ -3998,7 +3998,7 @@ protected function libIf($args) return $this->reduce($t, true); } - protected static $libIndex = array('list', 'value'); + protected static $libIndex = ['list', 'value']; protected function libIndex($args) { list($list, $value) = $args; @@ -4019,7 +4019,7 @@ protected function libIndex($args) return self::$null; } - $values = array(); + $values = []; foreach ($list[2] as $item) { $values[] = $this->normalizeValue($item); @@ -4030,17 +4030,17 @@ protected function libIndex($args) return false === $key ? self::$null : $key + 1; } - protected static $libRgb = array('red', 'green', 'blue'); + protected static $libRgb = ['red', 'green', 'blue']; protected function libRgb($args) { list($r, $g, $b) = $args; - return array(Type::T_COLOR, $r[1], $g[1], $b[1]); + return [Type::T_COLOR, $r[1], $g[1], $b[1]]; } - protected static $libRgba = array( - array('red', 'color'), - 'green', 'blue', 'alpha'); + protected static $libRgba = [ + ['red', 'color'], + 'green', 'blue', 'alpha']; protected function libRgba($args) { if ($color = $this->coerceColor($args[0])) { @@ -4053,7 +4053,7 @@ protected function libRgba($args) list($r, $g, $b, $a) = $args; - return array(Type::T_COLOR, $r[1], $g[1], $b[1], $a[1]); + return [Type::T_COLOR, $r[1], $g[1], $b[1], $a[1]]; } // helper function for adjust_color, change_color, and scale_color @@ -4061,7 +4061,7 @@ protected function alterColor($args, $fn) { $color = $this->assertColor($args[0]); - foreach (array(1, 2, 3, 7) as $i) { + foreach ([1, 2, 3, 7] as $i) { if (isset($args[$i])) { $val = $this->assertNumber($args[$i]); $ii = $i === 7 ? 4 : $i; // alpha @@ -4072,7 +4072,7 @@ protected function alterColor($args, $fn) if (isset($args[4]) || isset($args[5]) || isset($args[6])) { $hsl = $this->toHSL($color[1], $color[2], $color[3]); - foreach (array(4, 5, 6) as $i) { + foreach ([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); @@ -4091,10 +4091,10 @@ protected function alterColor($args, $fn) return $color; } - protected static $libAdjustColor = array( + protected static $libAdjustColor = [ 'color', 'red', 'green', 'blue', 'hue', 'saturation', 'lightness', 'alpha' - ); + ]; protected function libAdjustColor($args) { return $this->alterColor($args, function ($base, $alter, $i) { @@ -4102,10 +4102,10 @@ protected function libAdjustColor($args) }); } - protected static $libChangeColor = array( + protected static $libChangeColor = [ 'color', 'red', 'green', 'blue', 'hue', 'saturation', 'lightness', 'alpha' - ); + ]; protected function libChangeColor($args) { return $this->alterColor($args, function ($base, $alter, $i) { @@ -4113,10 +4113,10 @@ protected function libChangeColor($args) }); } - protected static $libScaleColor = array( + protected static $libScaleColor = [ 'color', 'red', 'green', 'blue', 'hue', 'saturation', 'lightness', 'alpha' - ); + ]; protected function libScaleColor($args) { return $this->alterColor($args, function ($base, $scale, $i) { @@ -4152,7 +4152,7 @@ protected function libScaleColor($args) }); } - protected static $libIeHexStr = array('color'); + protected static $libIeHexStr = ['color']; protected function libIeHexStr($args) { $color = $this->coerceColor($args[0]); @@ -4161,7 +4161,7 @@ protected function libIeHexStr($args) return sprintf('#%02X%02X%02X%02X', $color[4], $color[1], $color[2], $color[3]); } - protected static $libRed = array('color'); + protected static $libRed = ['color']; protected function libRed($args) { $color = $this->coerceColor($args[0]); @@ -4169,7 +4169,7 @@ protected function libRed($args) return $color[1]; } - protected static $libGreen = array('color'); + protected static $libGreen = ['color']; protected function libGreen($args) { $color = $this->coerceColor($args[0]); @@ -4177,7 +4177,7 @@ protected function libGreen($args) return $color[2]; } - protected static $libBlue = array('color'); + protected static $libBlue = ['color']; protected function libBlue($args) { $color = $this->coerceColor($args[0]); @@ -4185,7 +4185,7 @@ protected function libBlue($args) return $color[3]; } - protected static $libAlpha = array('color'); + protected static $libAlpha = ['color']; protected function libAlpha($args) { if ($color = $this->coerceColor($args[0])) { @@ -4196,7 +4196,7 @@ protected function libAlpha($args) return null; } - protected static $libOpacity = array('color'); + protected static $libOpacity = ['color']; protected function libOpacity($args) { $value = $args[0]; @@ -4209,7 +4209,7 @@ protected function libOpacity($args) } // mix two colors - protected static $libMix = array('color-1', 'color-2', 'weight'); + protected static $libMix = ['color-1', 'color-2', 'weight']; protected function libMix($args) { list($first, $second, $weight) = $args; @@ -4232,11 +4232,11 @@ protected function libMix($args) $w1 = (($w * $a === -1 ? $w : ($w + $a) / (1 + $w * $a)) + 1) / 2.0; $w2 = 1.0 - $w1; - $new = array(Type::T_COLOR, + $new = [Type::T_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); @@ -4245,7 +4245,7 @@ protected function libMix($args) return $this->fixColor($new); } - protected static $libHsl = array('hue', 'saturation', 'lightness'); + protected static $libHsl = ['hue', 'saturation', 'lightness']; protected function libHsl($args) { list($h, $s, $l) = $args; @@ -4253,7 +4253,7 @@ protected function libHsl($args) return $this->toRGB($h[1], $s[1], $l[1]); } - protected static $libHsla = array('hue', 'saturation', 'lightness', 'alpha'); + protected static $libHsla = ['hue', 'saturation', 'lightness', 'alpha']; protected function libHsla($args) { list($h, $s, $l, $a) = $args; @@ -4264,7 +4264,7 @@ protected function libHsla($args) return $color; } - protected static $libHue = array('color'); + protected static $libHue = ['color']; protected function libHue($args) { $color = $this->assertColor($args[0]); @@ -4273,7 +4273,7 @@ protected function libHue($args) return new Node\Number($hsl[1], 'deg'); } - protected static $libSaturation = array('color'); + protected static $libSaturation = ['color']; protected function libSaturation($args) { $color = $this->assertColor($args[0]); @@ -4282,7 +4282,7 @@ protected function libSaturation($args) return new Node\Number($hsl[2], '%'); } - protected static $libLightness = array('color'); + protected static $libLightness = ['color']; protected function libLightness($args) { $color = $this->assertColor($args[0]); @@ -4304,7 +4304,7 @@ protected function adjustHsl($color, $idx, $amount) return $out; } - protected static $libAdjustHue = array('color', 'degrees'); + protected static $libAdjustHue = ['color', 'degrees']; protected function libAdjustHue($args) { $color = $this->assertColor($args[0]); @@ -4313,7 +4313,7 @@ protected function libAdjustHue($args) return $this->adjustHsl($color, 1, $degrees); } - protected static $libLighten = array('color', 'amount'); + protected static $libLighten = ['color', 'amount']; protected function libLighten($args) { $color = $this->assertColor($args[0]); @@ -4322,7 +4322,7 @@ protected function libLighten($args) return $this->adjustHsl($color, 3, $amount); } - protected static $libDarken = array('color', 'amount'); + protected static $libDarken = ['color', 'amount']; protected function libDarken($args) { $color = $this->assertColor($args[0]); @@ -4331,7 +4331,7 @@ protected function libDarken($args) return $this->adjustHsl($color, 3, -$amount); } - protected static $libSaturate = array('color', 'amount'); + protected static $libSaturate = ['color', 'amount']; protected function libSaturate($args) { $value = $args[0]; @@ -4346,7 +4346,7 @@ protected function libSaturate($args) return $this->adjustHsl($color, 2, $amount); } - protected static $libDesaturate = array('color', 'amount'); + protected static $libDesaturate = ['color', 'amount']; protected function libDesaturate($args) { $color = $this->assertColor($args[0]); @@ -4355,7 +4355,7 @@ protected function libDesaturate($args) return $this->adjustHsl($color, 2, -$amount); } - protected static $libGrayscale = array('color'); + protected static $libGrayscale = ['color']; protected function libGrayscale($args) { $value = $args[0]; @@ -4367,13 +4367,13 @@ protected function libGrayscale($args) return $this->adjustHsl($this->assertColor($value), 2, -100); } - protected static $libComplement = array('color'); + protected static $libComplement = ['color']; protected function libComplement($args) { return $this->adjustHsl($this->assertColor($args[0]), 1, 180); } - protected static $libInvert = array('color'); + protected static $libInvert = ['color']; protected function libInvert($args) { $value = $args[0]; @@ -4391,7 +4391,7 @@ protected function libInvert($args) } // increases opacity by amount - protected static $libOpacify = array('color', 'amount'); + protected static $libOpacify = ['color', 'amount']; protected function libOpacify($args) { $color = $this->assertColor($args[0]); @@ -4403,14 +4403,14 @@ protected function libOpacify($args) return $color; } - protected static $libFadeIn = array('color', 'amount'); + protected static $libFadeIn = ['color', 'amount']; protected function libFadeIn($args) { return $this->libOpacify($args); } // decreases opacity by amount - protected static $libTransparentize = array('color', 'amount'); + protected static $libTransparentize = ['color', 'amount']; protected function libTransparentize($args) { $color = $this->assertColor($args[0]); @@ -4422,13 +4422,13 @@ protected function libTransparentize($args) return $color; } - protected static $libFadeOut = array('color', 'amount'); + protected static $libFadeOut = ['color', 'amount']; protected function libFadeOut($args) { return $this->libTransparentize($args); } - protected static $libUnquote = array('string'); + protected static $libUnquote = ['string']; protected function libUnquote($args) { $str = $args[0]; @@ -4440,7 +4440,7 @@ protected function libUnquote($args) return $str; } - protected static $libQuote = array('string'); + protected static $libQuote = ['string']; protected function libQuote($args) { $value = $args[0]; @@ -4449,16 +4449,16 @@ protected function libQuote($args) return $value; } - return array(Type::T_STRING, '"', array($value)); + return [Type::T_STRING, '"', [$value]]; } - protected static $libPercentage = array('value'); + protected static $libPercentage = ['value']; protected function libPercentage($args) { return new Node\Number($this->coercePercent($args[0]) * 100, '%'); } - protected static $libRound = array('value'); + protected static $libRound = ['value']; protected function libRound($args) { $num = $args[0]; @@ -4467,7 +4467,7 @@ protected function libRound($args) return $num; } - protected static $libFloor = array('value'); + protected static $libFloor = ['value']; protected function libFloor($args) { $num = $args[0]; @@ -4476,7 +4476,7 @@ protected function libFloor($args) return $num; } - protected static $libCeil = array('value'); + protected static $libCeil = ['value']; protected function libCeil($args) { $num = $args[0]; @@ -4485,7 +4485,7 @@ protected function libCeil($args) return $num; } - protected static $libAbs = array('value'); + protected static $libAbs = ['value']; protected function libAbs($args) { $num = $args[0]; @@ -4501,7 +4501,7 @@ protected function libMin($args) foreach ($numbers as $key => $number) { if (null === $min || $number[1] <= $min[1]) { - $min = array($key, $number[1]); + $min = [$key, $number[1]]; } } @@ -4515,7 +4515,7 @@ protected function libMax($args) foreach ($numbers as $key => $number) { if (null === $max || $number[1] >= $max[1]) { - $max = array($key, $number[1]); + $max = [$key, $number[1]]; } } @@ -4533,7 +4533,7 @@ protected function getNormalizedNumbers($args) { $unit = null; $originalUnit = null; - $numbers = array(); + $numbers = []; foreach ($args as $key => $item) { if ($item[0] !== Type::T_NUMBER) { @@ -4557,7 +4557,7 @@ protected function getNormalizedNumbers($args) return $numbers; } - protected static $libLength = array('list'); + protected static $libLength = ['list']; protected function libLength($args) { $list = $this->coerceList($args[0]); @@ -4565,7 +4565,7 @@ protected function libLength($args) return count($list[2]); } - //protected static $libListSeparator = array('list...'); + //protected static $libListSeparator = ['list...']; protected function libListSeparator($args) { if (count($args) > 1) { @@ -4585,7 +4585,7 @@ protected function libListSeparator($args) return 'space'; } - protected static $libNth = array('list', 'n'); + protected static $libNth = ['list', 'n']; protected function libNth($args) { $list = $this->coerceList($args[0]); @@ -4600,7 +4600,7 @@ protected function libNth($args) return isset($list[2][$n]) ? $list[2][$n] : self::$defaultValue; } - protected static $libSetNth = array('list', 'n', 'value'); + protected static $libSetNth = ['list', 'n', 'value']; protected function libSetNth($args) { $list = $this->coerceList($args[0]); @@ -4623,7 +4623,7 @@ protected function libSetNth($args) return $list; } - protected static $libMapGet = array('map', 'key'); + protected static $libMapGet = ['map', 'key']; protected function libMapGet($args) { $map = $this->assertMap($args[0]); @@ -4638,25 +4638,25 @@ protected function libMapGet($args) return self::$null; } - protected static $libMapKeys = array('map'); + protected static $libMapKeys = ['map']; protected function libMapKeys($args) { $map = $this->assertMap($args[0]); $keys = $map[1]; - return array(Type::T_LIST, ',', $keys); + return [Type::T_LIST, ',', $keys]; } - protected static $libMapValues = array('map'); + protected static $libMapValues = ['map']; protected function libMapValues($args) { $map = $this->assertMap($args[0]); $values = $map[2]; - return array(Type::T_LIST, ',', $values); + return [Type::T_LIST, ',', $values]; } - protected static $libMapRemove = array('map', 'key'); + protected static $libMapRemove = ['map', 'key']; protected function libMapRemove($args) { $map = $this->assertMap($args[0]); @@ -4672,7 +4672,7 @@ protected function libMapRemove($args) return $map; } - protected static $libMapHasKey = array('map', 'key'); + protected static $libMapHasKey = ['map', 'key']; protected function libMapHasKey($args) { $map = $this->assertMap($args[0]); @@ -4687,29 +4687,29 @@ protected function libMapHasKey($args) return false; } - protected static $libMapMerge = array('map-1', 'map-2'); + protected static $libMapMerge = ['map-1', 'map-2']; protected function libMapMerge($args) { $map1 = $this->assertMap($args[0]); $map2 = $this->assertMap($args[1]); - return array(Type::T_MAP, array_merge($map1[1], $map2[1]), array_merge($map1[2], $map2[2])); + return [Type::T_MAP, array_merge($map1[1], $map2[1]), array_merge($map1[2], $map2[2])]; } - protected static $libKeywords = array('args'); + protected static $libKeywords = ['args']; protected function libKeywords($args) { $this->assertList($args[0]); - $keys = array(); - $values = array(); + $keys = []; + $values = []; foreach ($args[0][2] as $name => $arg) { - $keys[] = array(Type::T_KEYWORD, $name); + $keys[] = [Type::T_KEYWORD, $name]; $values[] = $arg; } - return array(Type::T_MAP, $keys, $values); + return [Type::T_MAP, $keys, $values]; } protected function listSeparatorForJoin($list1, $sep) @@ -4730,7 +4730,7 @@ protected function listSeparatorForJoin($list1, $sep) } } - protected static $libJoin = array('list1', 'list2', 'separator'); + protected static $libJoin = ['list1', 'list2', 'separator']; protected function libJoin($args) { list($list1, $list2, $sep) = $args; @@ -4739,10 +4739,10 @@ protected function libJoin($args) $list2 = $this->coerceList($list2, ' '); $sep = $this->listSeparatorForJoin($list1, $sep); - return array(Type::T_LIST, $sep, array_merge($list1[2], $list2[2])); + return [Type::T_LIST, $sep, array_merge($list1[2], $list2[2])]; } - protected static $libAppend = array('list', 'val', 'separator'); + protected static $libAppend = ['list', 'val', 'separator']; protected function libAppend($args) { list($list1, $value, $sep) = $args; @@ -4750,7 +4750,7 @@ protected function libAppend($args) $list1 = $this->coerceList($list1, ' '); $sep = $this->listSeparatorForJoin($list1, $sep); - return array(Type::T_LIST, $sep, array_merge($list1[2], array($value))); + return [Type::T_LIST, $sep, array_merge($list1[2], [$value])]; } protected function libZip($args) @@ -4759,11 +4759,11 @@ protected function libZip($args) $this->assertList($arg); } - $lists = array(); + $lists = []; $firstList = array_shift($args); foreach ($firstList[2] as $key => $item) { - $list = array(Type::T_LIST, '', array($item)); + $list = [Type::T_LIST, '', [$item]]; foreach ($args as $arg) { if (isset($arg[2][$key])) { @@ -4776,10 +4776,10 @@ protected function libZip($args) $lists[] = $list; } - return array(Type::T_LIST, ',', $lists); + return [Type::T_LIST, ',', $lists]; } - protected static $libTypeOf = array('value'); + protected static $libTypeOf = ['value']; protected function libTypeOf($args) { $value = $args[0]; @@ -4809,19 +4809,19 @@ protected function libTypeOf($args) } } - protected static $libUnit = array('number'); + protected static $libUnit = ['number']; protected function libUnit($args) { $num = $args[0]; if ($num[0] === Type::T_NUMBER) { - return array(Type::T_STRING, '"', array($num->unitStr())); + return [Type::T_STRING, '"', [$num->unitStr()]]; } return ''; } - protected static $libUnitless = array('number'); + protected static $libUnitless = ['number']; protected function libUnitless($args) { $value = $args[0]; @@ -4829,7 +4829,7 @@ protected function libUnitless($args) return $value[0] === Type::T_NUMBER && $value->unitless(); } - protected static $libComparable = array('number-1', 'number-2'); + protected static $libComparable = ['number-1', 'number-2']; protected function libComparable($args) { list($number1, $number2) = $args; @@ -4848,7 +4848,7 @@ protected function libComparable($args) return $number1[2] === $number2[2] || $number1->unitless() || $number2->unitless(); } - protected static $libStrIndex = array('string', 'substring'); + protected static $libStrIndex = ['string', 'substring']; protected function libStrIndex($args) { $string = $this->coerceString($args[0]); @@ -4862,7 +4862,7 @@ protected function libStrIndex($args) return $result === false ? self::$null : new Node\Number($result + 1, ''); } - protected static $libStrInsert = array('string', 'insert', 'index'); + protected static $libStrInsert = ['string', 'insert', 'index']; protected function libStrInsert($args) { $string = $this->coerceString($args[0]); @@ -4873,12 +4873,12 @@ protected function libStrInsert($args) list(, $index) = $args[2]; - $string[2] = array(substr_replace($stringContent, $insertContent, $index - 1, 0)); + $string[2] = [substr_replace($stringContent, $insertContent, $index - 1, 0)]; return $string; } - protected static $libStrLength = array('string'); + protected static $libStrLength = ['string']; protected function libStrLength($args) { $string = $this->coerceString($args[0]); @@ -4887,7 +4887,7 @@ protected function libStrLength($args) return new Node\Number(strlen($stringContent), ''); } - protected static $libStrSlice = array('string', 'start-at', 'end-at'); + protected static $libStrSlice = ['string', 'start-at', 'end-at']; protected function libStrSlice($args) { if (isset($args[2]) && $args[2][1] == 0) { @@ -4907,35 +4907,35 @@ protected function libStrSlice($args) $length = $end < 0 ? $end + 1 : ($end > 0 ? $end - $start : $end); $string[2] = $length - ? array(substr($stringContent, $start, $length)) - : array(substr($stringContent, $start)); + ? [substr($stringContent, $start, $length)] + : [substr($stringContent, $start)]; return $string; } - protected static $libToLowerCase = array('string'); + protected static $libToLowerCase = ['string']; protected function libToLowerCase($args) { $string = $this->coerceString($args[0]); $stringContent = $this->compileStringContent($string); - $string[2] = array(mb_strtolower($stringContent)); + $string[2] = [mb_strtolower($stringContent)]; return $string; } - protected static $libToUpperCase = array('string'); + protected static $libToUpperCase = ['string']; protected function libToUpperCase($args) { $string = $this->coerceString($args[0]); $stringContent = $this->compileStringContent($string); - $string[2] = array(mb_strtoupper($stringContent)); + $string[2] = [mb_strtoupper($stringContent)]; return $string; } - protected static $libFeatureExists = array('feature'); + protected static $libFeatureExists = ['feature']; protected function libFeatureExists($args) { $string = $this->coerceString($args[0]); @@ -4946,7 +4946,7 @@ protected function libFeatureExists($args) ); } - protected static $libFunctionExists = array('name'); + protected static $libFunctionExists = ['name']; protected function libFunctionExists($args) { $string = $this->coerceString($args[0]); @@ -4969,7 +4969,7 @@ protected function libFunctionExists($args) return $this->toBool(is_callable($f)); } - protected static $libGlobalVariableExists = array('name'); + protected static $libGlobalVariableExists = ['name']; protected function libGlobalVariableExists($args) { $string = $this->coerceString($args[0]); @@ -4978,7 +4978,7 @@ protected function libGlobalVariableExists($args) return $this->has($name, $this->rootEnv); } - protected static $libMixinExists = array('name'); + protected static $libMixinExists = ['name']; protected function libMixinExists($args) { $string = $this->coerceString($args[0]); @@ -4987,7 +4987,7 @@ protected function libMixinExists($args) return $this->has(self::$namespaces['mixin'] . $name); } - protected static $libVariableExists = array('name'); + protected static $libVariableExists = ['name']; protected function libVariableExists($args) { $string = $this->coerceString($args[0]); @@ -5003,12 +5003,12 @@ protected function libVariableExists($args) */ protected function libCounter($args) { - $list = array_map(array($this, 'compileValue'), $args); + $list = array_map([$this, 'compileValue'], $args); - return array(Type::T_STRING, '', array('counter(' . implode(',', $list) . ')')); + return [Type::T_STRING, '', ['counter(' . implode(',', $list) . ')']]; } - protected static $libRandom = array('limit'); + protected static $libRandom = ['limit']; protected function libRandom($args) { if (isset($args[0])) { @@ -5036,14 +5036,14 @@ protected function libUniqueId() $id += mt_rand(0, 10) + 1; - return array(Type::T_STRING, '', array('u' . str_pad(base_convert($id, 10, 36), 8, '0', STR_PAD_LEFT))); + return [Type::T_STRING, '', ['u' . str_pad(base_convert($id, 10, 36), 8, '0', STR_PAD_LEFT)]]; } - protected static $libInspect = array('value'); + protected static $libInspect = ['value']; protected function libInspect($args) { if ($args[0] === self::$null) { - return array(Type::T_KEYWORD, 'null'); + return [Type::T_KEYWORD, 'null']; } return $args[0]; diff --git a/src/Formatter/Nested.php b/src/Formatter/Nested.php index 6fab43dc..ed9f075b 100644 --- a/src/Formatter/Nested.php +++ b/src/Formatter/Nested.php @@ -154,7 +154,7 @@ protected function block(OutputBlock $block) private function adjustAllChildren(OutputBlock $block) { // flatten empty nested blocks - $children = array(); + $children = []; foreach ($block->children as $i => $child) { if (empty($child->lines) && empty($child->children)) { diff --git a/src/Node/Number.php b/src/Node/Number.php index 6549f495..a803a6ca 100644 --- a/src/Node/Number.php +++ b/src/Node/Number.php @@ -38,8 +38,8 @@ class Number extends Node implements \ArrayAccess * * @var array */ - static protected $unitTable = array( - 'in' => array( + static protected $unitTable = [ + 'in' => [ 'in' => 1, 'pc' => 6, 'pt' => 72, @@ -47,27 +47,27 @@ class Number extends Node implements \ArrayAccess 'cm' => 2.54, 'mm' => 25.4, 'q' => 101.6, - ), - 'turn' => array( + ], + 'turn' => [ 'deg' => 360, 'grad' => 400, 'rad' => 6.28318530717958647692528676, // 2 * M_PI 'turn' => 1, - ), - 's' => array( + ], + 's' => [ 's' => 1, 'ms' => 1000, - ), - 'Hz' => array( + ], + 'Hz' => [ 'Hz' => 1, 'kHz' => 0.001, - ), - 'dpi' => array( + ], + 'dpi' => [ 'dpi' => 1, 'dpcm' => 2.54, 'dppx' => 96, - ), - ); + ], + ]; /** * @var integer|float @@ -91,8 +91,8 @@ public function __construct($dimension, $initialUnit) $this->dimension = $dimension; $this->units = is_array($initialUnit) ? $initialUnit - : ($initialUnit ? array($initialUnit => 1) - : array()); + : ($initialUnit ? [$initialUnit => 1] + : []); } /** @@ -128,7 +128,7 @@ public function coerce($units) public function normalize() { $dimension = $this->dimension; - $units = array(); + $units = []; $this->normalizeUnits($dimension, $units, 'in'); @@ -238,8 +238,8 @@ public function unitless() */ public function unitStr() { - $numerators = array(); - $denominators = array(); + $numerators = []; + $denominators = []; foreach ($this->units as $unit => $unitSize) { if ($unitSize > 0) { @@ -273,7 +273,7 @@ public function output(Compiler $compiler = null) if (count($units) > 1 && array_sum($units) === 0) { $dimension = $this->dimension; - $units = array(); + $units = []; $this->normalizeUnits($dimension, $units, 'in'); @@ -313,7 +313,7 @@ public function __toString() private function normalizeUnits(&$dimension, &$units, $baseUnit = 'in') { $dimension = $this->dimension; - $units = array(); + $units = []; foreach ($this->units as $unit => $exp) { if (isset(self::$unitTable[$baseUnit][$unit])) { diff --git a/src/Parser.php b/src/Parser.php index 562ea701..95e26049 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -31,7 +31,7 @@ class Parser /** * @var array */ - protected static $precedence = array( + protected static $precedence = [ '=' => 0, 'or' => 1, 'and' => 2, @@ -47,7 +47,7 @@ class Parser '*' => 6, '/' => 6, '%' => 6, - ); + ]; protected static $commentPattern; protected static $operatorPattern; @@ -321,8 +321,7 @@ protected function parseChunk() ($this->end() || $this->literal('{') && $hasBlock = true) ) { - $child = array(Type::T_INCLUDE, - $mixinName, isset($argValues) ? $argValues : null, null); + $child = [Type::T_INCLUDE, $mixinName, isset($argValues) ? $argValues : null, null]; if (! empty($hasBlock)) { $include = $this->pushSpecialBlock(Type::T_INCLUDE, $s); @@ -340,7 +339,7 @@ protected function parseChunk() $this->valueList($importPath) && $this->end() ) { - $this->append(array(Type::T_SCSSPHP_IMPORT_ONCE, $importPath), $s); + $this->append([Type::T_SCSSPHP_IMPORT_ONCE, $importPath], $s); return true; } @@ -351,7 +350,7 @@ protected function parseChunk() $this->valueList($importPath) && $this->end() ) { - $this->append(array(Type::T_IMPORT, $importPath), $s); + $this->append([Type::T_IMPORT, $importPath], $s); return true; } @@ -362,7 +361,7 @@ protected function parseChunk() $this->url($importPath) && $this->end() ) { - $this->append(array(Type::T_IMPORT, $importPath), $s); + $this->append([Type::T_IMPORT, $importPath], $s); return true; } @@ -375,7 +374,7 @@ protected function parseChunk() ) { // check for '!flag' $optional = $this->stripOptionalFlag($selectors); - $this->append(array(Type::T_EXTEND, $selectors, $optional), $s); + $this->append([Type::T_EXTEND, $selectors, $optional], $s); return true; } @@ -397,7 +396,7 @@ protected function parseChunk() $this->seek($s); if ($this->literal('@break') && $this->end()) { - $this->append(array(Type::T_BREAK), $s); + $this->append([Type::T_BREAK], $s); return true; } @@ -405,7 +404,7 @@ protected function parseChunk() $this->seek($s); if ($this->literal('@continue') && $this->end()) { - $this->append(array(Type::T_CONTINUE), $s); + $this->append([Type::T_CONTINUE], $s); return true; } @@ -414,7 +413,7 @@ protected function parseChunk() if ($this->literal('@return') && ($this->valueList($retVal) || true) && $this->end()) { - $this->append(array(Type::T_RETURN, isset($retVal) ? $retVal : array(Type::T_NULL)), $s); + $this->append([Type::T_RETURN, isset($retVal) ? $retVal : [Type::T_NULL]], $s); return true; } @@ -475,7 +474,7 @@ protected function parseChunk() if ($this->literal('@if') && $this->valueList($cond) && $this->literal('{')) { $if = $this->pushSpecialBlock(Type::T_IF, $s); $if->cond = $cond; - $if->cases = array(); + $if->cases = []; return true; } @@ -486,7 +485,7 @@ protected function parseChunk() $this->valueList($value) && $this->end() ) { - $this->append(array(Type::T_DEBUG, $value), $s); + $this->append([Type::T_DEBUG, $value], $s); return true; } @@ -497,7 +496,7 @@ protected function parseChunk() $this->valueList($value) && $this->end() ) { - $this->append(array(Type::T_WARN, $value), $s); + $this->append([Type::T_WARN, $value], $s); return true; } @@ -508,7 +507,7 @@ protected function parseChunk() $this->valueList($value) && $this->end() ) { - $this->append(array(Type::T_ERROR, $value), $s); + $this->append([Type::T_ERROR, $value], $s); return true; } @@ -516,7 +515,7 @@ protected function parseChunk() $this->seek($s); if ($this->literal('@content') && $this->end()) { - $this->append(array(Type::T_MIXIN_CONTENT), $s); + $this->append([Type::T_MIXIN_CONTENT], $s); return true; } @@ -553,7 +552,7 @@ protected function parseChunk() $this->end() ) { if (! isset($this->charset)) { - $statement = array(Type::T_CHARSET, $charset); + $statement = [Type::T_CHARSET, $charset]; list($line, $column) = $this->getSourcePosition($s); @@ -601,8 +600,8 @@ protected function parseChunk() $this->valueList($value) && $this->end() ) { - $name = array(Type::T_STRING, '', array($name)); - $this->append(array(Type::T_ASSIGN, $name, $value), $s); + $name = [Type::T_STRING, '', [$name]]; + $this->append([Type::T_ASSIGN, $name, $value], $s); return true; } @@ -617,7 +616,7 @@ protected function parseChunk() ) { // check for '!flag' $assignmentFlag = $this->stripAssignmentFlag($value); - $this->append(array(Type::T_ASSIGN, $name, $value, $assignmentFlag), $s); + $this->append([Type::T_ASSIGN, $name, $value, $assignmentFlag], $s); return true; } @@ -643,7 +642,7 @@ protected function parseChunk() $foundSomething = false; if ($this->valueList($value)) { - $this->append(array(Type::T_ASSIGN, $name, $value), $s); + $this->append([Type::T_ASSIGN, $name, $value], $s); $foundSomething = true; } @@ -673,7 +672,7 @@ protected function parseChunk() $this->append($include, $s); } elseif (empty($block->dontAppend)) { $type = isset($block->type) ? $block->type : Type::T_BLOCK; - $this->append(array($type, $block), $s); + $this->append([$type, $block], $s); } return true; @@ -706,18 +705,18 @@ protected function pushBlock($selectors, $pos = 0) $b->sourceColumn = $column; $b->sourceIndex = $this->sourceIndex; $b->selectors = $selectors; - $b->comments = array(); + $b->comments = []; $b->parent = $this->env; if (! $this->env) { - $b->children = array(); + $b->children = []; } elseif (empty($this->env->children)) { $this->env->children = $this->env->comments; - $b->children = array(); - $this->env->comments = array(); + $b->children = []; + $this->env->comments = []; } else { $b->children = $this->env->comments; - $this->env->comments = array(); + $this->env->comments = []; } $this->env = $b; @@ -824,7 +823,7 @@ protected function matchString(&$m, $delim) $end = strlen($this->buffer); // look for either ending delim, escape, or string interpolation - foreach (array('#{', '\\', $delim) as $lookahead) { + foreach (['#{', '\\', $delim] as $lookahead) { $pos = strpos($this->buffer, $lookahead, $this->count); if ($pos !== false && $pos < $end) { @@ -838,11 +837,11 @@ protected function matchString(&$m, $delim) } $match = substr($this->buffer, $this->count, $end - $this->count); - $m = array( + $m = [ $match . $token, $match, $token - ); + ]; $this->count = $end + strlen($token); return true; @@ -921,7 +920,7 @@ protected function whitespace() while (preg_match(self::$whitePattern, $this->buffer, $m, null, $this->count)) { if (isset($m[1]) && empty($this->commentsSeen[$this->count])) { - $this->appendComment(array(Type::T_COMMENT, $m[1])); + $this->appendComment([Type::T_COMMENT, $m[1]]); $this->commentsSeen[$this->count] = true; } @@ -940,7 +939,7 @@ protected function whitespace() */ protected function appendComment($comment) { - $comment[1] = substr(preg_replace(array('/^\s+/m', '/^(.)/m'), array('', ' \1'), $comment[1]), 1); + $comment[1] = substr(preg_replace(['/^\s+/m', '/^(.)/m'], ['', ' \1'], $comment[1]), 1); $this->env->comments[] = $comment; } @@ -967,7 +966,7 @@ protected function append($statement, $pos = null) if (count($comments)) { $this->env->children = array_merge($this->env->children, $comments); - $this->env->comments = array(); + $this->env->comments = []; } } @@ -1007,28 +1006,28 @@ protected function mediaQueryList(&$out) protected function mediaQuery(&$out) { $expressions = null; - $parts = array(); + $parts = []; if (($this->literal('only') && ($only = true) || $this->literal('not') && ($not = true) || true) && $this->mixedKeyword($mediaType) ) { - $prop = array(Type::T_MEDIA_TYPE); + $prop = [Type::T_MEDIA_TYPE]; if (isset($only)) { - $prop[] = array(Type::T_KEYWORD, 'only'); + $prop[] = [Type::T_KEYWORD, 'only']; } if (isset($not)) { - $prop[] = array(Type::T_KEYWORD, 'not'); + $prop[] = [Type::T_KEYWORD, 'not']; } - $media = array(Type::T_LIST, '', array()); + $media = [Type::T_LIST, '', []]; - foreach ((array)$mediaType as $type) { + foreach ((array) $mediaType as $type) { if (is_array($type)) { $media[2][] = $type; } else { - $media[2][] = array(Type::T_KEYWORD, $type); + $media[2][] = [Type::T_KEYWORD, $type]; } } @@ -1066,7 +1065,7 @@ protected function mediaExpression(&$out) ($this->literal(':') && $this->expression($value) || true) && $this->literal(')') ) { - $out = array(Type::T_MEDIA_EXPRESSION, $feature); + $out = [Type::T_MEDIA_EXPRESSION, $feature]; if ($value) { $out[] = $value; @@ -1117,7 +1116,7 @@ protected function argValue(&$out) } if ($this->genericList($value, 'expression')) { - $out = array($keyword, $value, false); + $out = [$keyword, $value, false]; $s = $this->seek(); if ($this->literal('...')) { @@ -1169,7 +1168,7 @@ protected function spaceList(&$out) protected function genericList(&$out, $parseItem, $delim = '', $flatten = true) { $s = $this->seek(); - $items = array(); + $items = []; while ($this->$parseItem($value)) { $items[] = $value; @@ -1190,7 +1189,7 @@ protected function genericList(&$out, $parseItem, $delim = '', $flatten = true) if ($flatten && count($items) === 1) { $out = $items[0]; } else { - $out = array(Type::T_LIST, $delim, $items); + $out = [Type::T_LIST, $delim, $items]; } return true; @@ -1209,7 +1208,7 @@ protected function expression(&$out) if ($this->literal('(')) { if ($this->literal(')')) { - $out = array(Type::T_LIST, '', array()); + $out = [Type::T_LIST, '', []]; return true; } @@ -1276,7 +1275,7 @@ protected function expHelper($lhs, $minP) $rhs = $this->expHelper($rhs, self::$precedence[$next[1]]); } - $lhs = array(Type::T_EXPRESSION, $op, $lhs, $rhs, $this->inParens, $whiteBefore, $whiteAfter); + $lhs = [Type::T_EXPRESSION, $op, $lhs, $rhs, $this->inParens, $whiteBefore, $whiteAfter]; $ss = $this->seek(); $whiteBefore = isset($this->buffer[$this->count - 1]) && ctype_space($this->buffer[$this->count - 1]); @@ -1299,7 +1298,7 @@ protected function value(&$out) $s = $this->seek(); if ($this->literal('not', false) && $this->whitespace() && $this->value($inner)) { - $out = array(Type::T_UNARY, 'not', $inner, $this->inParens); + $out = [Type::T_UNARY, 'not', $inner, $this->inParens]; return true; } @@ -1307,7 +1306,7 @@ protected function value(&$out) $this->seek($s); if ($this->literal('not', false) && $this->parenValue($inner)) { - $out = array(Type::T_UNARY, 'not', $inner, $this->inParens); + $out = [Type::T_UNARY, 'not', $inner, $this->inParens]; return true; } @@ -1315,7 +1314,7 @@ protected function value(&$out) $this->seek($s); if ($this->literal('+') && $this->value($inner)) { - $out = array(Type::T_UNARY, '+', $inner, $this->inParens); + $out = [Type::T_UNARY, '+', $inner, $this->inParens]; return true; } @@ -1328,7 +1327,7 @@ protected function value(&$out) $this->unit($inner) || $this->parenValue($inner)) ) { - $out = array(Type::T_UNARY, '-', $inner, $this->inParens); + $out = [Type::T_UNARY, '-', $inner, $this->inParens]; return true; } @@ -1349,9 +1348,9 @@ protected function value(&$out) if ($this->keyword($keyword)) { if ($keyword === 'null') { - $out = array(Type::T_NULL); + $out = [Type::T_NULL]; } else { - $out = array(Type::T_KEYWORD, $keyword); + $out = [Type::T_KEYWORD, $keyword]; } return true; @@ -1375,7 +1374,7 @@ protected function parenValue(&$out) if ($this->literal('(')) { if ($this->literal(')')) { - $out = array(Type::T_LIST, '', array()); + $out = [Type::T_LIST, '', []]; return true; } @@ -1414,9 +1413,9 @@ protected function progid(&$out) $this->openString(')', $args, '('); if ($this->literal(')')) { - $out = array(Type::T_STRING, '', array( + $out = [Type::T_STRING, '', [ 'progid:', $fn, '(', $args, ')' - )); + ]]; return true; } @@ -1442,7 +1441,7 @@ protected function func(&$func) $this->literal('(') ) { if ($name === 'alpha' && $this->argumentList($args)) { - $func = array(Type::T_FUNCTION, $name, array(Type::T_STRING, '', $args)); + $func = [Type::T_FUNCTION, $name, [Type::T_STRING, '', $args]]; return true; } @@ -1451,7 +1450,7 @@ protected function func(&$func) $ss = $this->seek(); if ($this->argValues($args) && $this->literal(')')) { - $func = array(Type::T_FUNCTION_CALL, $name, $args); + $func = [Type::T_FUNCTION_CALL, $name, $args]; return true; } @@ -1462,13 +1461,13 @@ protected function func(&$func) if (($this->openString(')', $str, '(') || true) && $this->literal(')') ) { - $args = array(); + $args = []; if (! empty($str)) { - $args[] = array(null, array(Type::T_STRING, '', array($str))); + $args[] = [null, [Type::T_STRING, '', [$str]]]; } - $func = array(Type::T_FUNCTION_CALL, $name, $args); + $func = [Type::T_FUNCTION_CALL, $name, $args]; return true; } @@ -1491,11 +1490,11 @@ protected function argumentList(&$out) $s = $this->seek(); $this->literal('('); - $args = array(); + $args = []; while ($this->keyword($var)) { if ($this->literal('=') && $this->expression($exp)) { - $args[] = array(Type::T_STRING, '', array($var . '=')); + $args[] = [Type::T_STRING, '', [$var . '=']]; $arg = $exp; } else { break; @@ -1507,7 +1506,7 @@ protected function argumentList(&$out) break; } - $args[] = array(Type::T_STRING, '', array(', ')); + $args[] = [Type::T_STRING, '', [', ']]; } if (! $this->literal(')') || ! count($args)) { @@ -1533,10 +1532,10 @@ protected function argumentDef(&$out) $s = $this->seek(); $this->literal('('); - $args = array(); + $args = []; while ($this->variable($var)) { - $arg = array($var[1], null, false); + $arg = [$var[1], null, false]; $ss = $this->seek(); @@ -1594,8 +1593,8 @@ protected function map(&$out) return false; } - $keys = array(); - $values = array(); + $keys = []; + $values = []; while ($this->genericList($key, 'expression') && $this->literal(':') && $this->genericList($value, 'expression') @@ -1614,7 +1613,7 @@ protected function map(&$out) return false; } - $out = array(Type::T_MAP, $keys, $values); + $out = [Type::T_MAP, $keys, $values]; return true; } @@ -1628,13 +1627,13 @@ protected function map(&$out) */ protected function color(&$out) { - $color = array(Type::T_COLOR); + $color = [Type::T_COLOR]; if ($this->match('(#([0-9a-f]{6})|#([0-9a-f]{3}))', $m)) { if (isset($m[3])) { $num = hexdec($m[3]); - foreach (array(3, 2, 1) as $i) { + foreach ([3, 2, 1] as $i) { $t = $num & 0xf; $color[$i] = $t << 4 | $t; $num >>= 4; @@ -1642,7 +1641,7 @@ protected function color(&$out) } else { $num = hexdec($m[2]); - foreach (array(3, 2, 1) as $i) { + foreach ([3, 2, 1] as $i) { $color[$i] = $num & 0xff; $num >>= 8; } @@ -1693,7 +1692,7 @@ protected function string(&$out) return false; } - $content = array(); + $content = []; $oldWhite = $this->eatWhiteDefault; $this->eatWhiteDefault = false; $hasInterpolation = false; @@ -1742,7 +1741,7 @@ protected function string(&$out) } } - $out = array(Type::T_STRING, $delim, $content); + $out = [Type::T_STRING, $delim, $content]; return true; } @@ -1761,7 +1760,7 @@ protected function string(&$out) */ protected function mixedKeyword(&$out) { - $parts = array(); + $parts = []; $oldWhite = $this->eatWhiteDefault; $this->eatWhiteDefault = false; @@ -1813,7 +1812,7 @@ protected function openString($end, &$out, $nestingOpen = null) $nestingLevel = 0; - $content = array(); + $content = []; while ($this->match($patt, $m, false)) { if (isset($m[1]) && $m[1] !== '') { @@ -1857,7 +1856,7 @@ protected function openString($end, &$out, $nestingOpen = null) $content[count($content) - 1] = rtrim(end($content)); } - $out = array(Type::T_STRING, '', $content); + $out = [Type::T_STRING, '', $content]; return true; } @@ -1885,7 +1884,7 @@ protected function interpolation(&$out, $lookWhite = true) $left = $right = false; } - $out = array(Type::T_INTERPOLATE, $value, $left, $right); + $out = [Type::T_INTERPOLATE, $value, $left, $right]; $this->eatWhiteDefault = $oldWhite; if ($this->eatWhiteDefault) { @@ -1910,7 +1909,7 @@ protected function interpolation(&$out, $lookWhite = true) */ protected function propertyName(&$out) { - $parts = array(); + $parts = []; $oldWhite = $this->eatWhiteDefault; $this->eatWhiteDefault = false; @@ -1957,7 +1956,7 @@ protected function propertyName(&$out) $this->whitespace(); // get any extra whitespace - $out = array(Type::T_STRING, '', $parts); + $out = [Type::T_STRING, '', $parts]; return true; } @@ -1972,7 +1971,7 @@ protected function propertyName(&$out) protected function selectors(&$out) { $s = $this->seek(); - $selectors = array(); + $selectors = []; while ($this->selector($sel)) { $selectors[] = $sel; @@ -2006,11 +2005,11 @@ protected function selectors(&$out) */ protected function selector(&$out) { - $selector = array(); + $selector = []; for (;;) { if ($this->match('[>+~]+', $m)) { - $selector[] = array($m[0]); + $selector[] = [$m[0]]; continue; } @@ -2021,7 +2020,7 @@ protected function selector(&$out) } if ($this->match('\/[^\/]+\/', $m)) { - $selector[] = array($m[0]); + $selector[] = [$m[0]]; continue; } @@ -2052,7 +2051,7 @@ protected function selectorSingle(&$out) $oldWhite = $this->eatWhiteDefault; $this->eatWhiteDefault = false; - $parts = array(); + $parts = []; if ($this->literal('*', false)) { $parts[] = '*'; @@ -2189,7 +2188,7 @@ protected function variable(&$out) $s = $this->seek(); if ($this->literal('$', false) && $this->keyword($name)) { - $out = array(Type::T_VARIABLE, $name); + $out = [Type::T_VARIABLE, $name]; return true; } @@ -2257,7 +2256,7 @@ protected function placeholder(&$placeholder) protected function url(&$out) { if ($this->match('(url\(\s*(["\']?)([^)]+)\2\s*\))', $m)) { - $out = array(Type::T_STRING, '', array('url(' . $m[2] . $m[3] . $m[2] . ')')); + $out = [Type::T_STRING, '', ['url(' . $m[2] . $m[3] . $m[2] . ')']]; return true; } @@ -2298,7 +2297,7 @@ protected function stripAssignmentFlag(&$value) for ($token = &$value; $token[0] === Type::T_LIST && ($s = count($token[2])); $token = &$lastNode) { $lastNode = &$token[2][$s - 1]; - if ($lastNode[0] === Type::T_KEYWORD && in_array($lastNode[1], array('!default', '!global'))) { + if ($lastNode[0] === Type::T_KEYWORD && in_array($lastNode[1], ['!default', '!global'])) { array_pop($token[2]); $token = $this->flattenList($token); @@ -2324,7 +2323,7 @@ protected function stripOptionalFlag(&$selectors) $selector = end($selectors); $part = end($selector); - if ($part === array('!optional')) { + if ($part === ['!optional']) { array_pop($selectors[count($selectors) - 1]); $optional = true; @@ -2410,7 +2409,7 @@ private function pregQuote($what) */ private function extractLineNumbers($buffer) { - $this->sourcePositions = array(0 => 0); + $this->sourcePositions = [0 => 0]; $prev = 0; while (($pos = strpos($buffer, "\n", $prev)) !== false) { @@ -2450,10 +2449,10 @@ private function getSourcePosition($pos) continue; } - return array($mid + 1, $pos - $this->sourcePositions[$mid]); + return [$mid + 1, $pos - $this->sourcePositions[$mid]]; } - return array($low + 1, $pos - $this->sourcePositions[$low]); + return [$low + 1, $pos - $this->sourcePositions[$low]]; } /** diff --git a/src/Server.php b/src/Server.php index e34ff5c3..221655ca 100644 --- a/src/Server.php +++ b/src/Server.php @@ -214,14 +214,14 @@ protected function compile($in, $out) file_put_contents($out, $css); file_put_contents( $this->metadataName($out), - serialize(array( + serialize([ 'etag' => $etag, 'imports' => $this->scss->getParsedFiles(), 'vars' => crc32(serialize($this->scss->getVariables())), - )) + ]) ); - return array($css, $etag); + return [$css, $etag]; } /** @@ -234,8 +234,8 @@ protected function compile($in, $out) protected function createErrorCSS(\Exception $error) { $message = str_replace( - array("'", "\n"), - array("\\'", "\\A"), + ["'", "\n"], + ["\\'", "\\A"], $error->getfile() . ":\n\n" . $error->getMessage() ); From 05369ac7ff3ce06b89dca0913da560aa7dcdba6c Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 16 Dec 2015 17:29:58 -0500 Subject: [PATCH 184/484] refactor semi-colon stripping (again) --- scss.inc.php | 1 - src/Formatter.php | 14 ++++++++++ src/Formatter/Compact.php | 1 + src/Formatter/Compressed.php | 15 +++++++++- src/Formatter/Crunched.php | 15 +++++++++- src/Formatter/Debug.php | 1 + src/Formatter/Expanded.php | 1 + src/Formatter/Nested.php | 1 + src/Formatter/StripSemiColons.php | 46 ------------------------------- 9 files changed, 46 insertions(+), 49 deletions(-) delete mode 100644 src/Formatter/StripSemiColons.php diff --git a/scss.inc.php b/scss.inc.php index ddfd1297..b6892fec 100644 --- a/scss.inc.php +++ b/scss.inc.php @@ -20,7 +20,6 @@ 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/Formatter/StripSemiColons.php'; include_once __DIR__ . '/src/Node.php'; include_once __DIR__ . '/src/Node/Number.php'; include_once __DIR__ . '/src/Parser.php'; diff --git a/src/Formatter.php b/src/Formatter.php index caa9d660..2770bb2d 100644 --- a/src/Formatter.php +++ b/src/Formatter.php @@ -55,6 +55,11 @@ abstract class Formatter */ public $assignSeparator; + /** + * @var boolea + */ + public $keepSemicolons; + /** * Initialize formatter * @@ -96,6 +101,15 @@ public function property($name, $value) */ 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); + } } /** diff --git a/src/Formatter/Compact.php b/src/Formatter/Compact.php index 721390d7..94abe329 100644 --- a/src/Formatter/Compact.php +++ b/src/Formatter/Compact.php @@ -32,6 +32,7 @@ public function __construct() $this->close = "}\n\n"; $this->tagSeparator = ','; $this->assignSeparator = ':'; + $this->keepSemicolons = true; } /** diff --git a/src/Formatter/Compressed.php b/src/Formatter/Compressed.php index af55da02..df7bc75f 100644 --- a/src/Formatter/Compressed.php +++ b/src/Formatter/Compressed.php @@ -21,7 +21,20 @@ */ class Compressed extends Formatter { - use StripSemiColons; + /** + * {@inheritdoc} + */ + public function __construct() + { + $this->indentLevel = 0; + $this->indentChar = ' '; + $this->break = ''; + $this->open = '{'; + $this->close = '}'; + $this->tagSeparator = ','; + $this->assignSeparator = ':'; + $this->keepSemicolons = false; + } /** * {@inheritdoc} diff --git a/src/Formatter/Crunched.php b/src/Formatter/Crunched.php index c31cac56..ccba1333 100644 --- a/src/Formatter/Crunched.php +++ b/src/Formatter/Crunched.php @@ -21,7 +21,20 @@ */ class Crunched extends Formatter { - use StripSemiColons; + /** + * {@inheritdoc} + */ + public function __construct() + { + $this->indentLevel = 0; + $this->indentChar = ' '; + $this->break = ''; + $this->open = '{'; + $this->close = '}'; + $this->tagSeparator = ','; + $this->assignSeparator = ':'; + $this->keepSemicolons = false; + } /** * {@inheritdoc} diff --git a/src/Formatter/Debug.php b/src/Formatter/Debug.php index e4555bb3..855742e7 100644 --- a/src/Formatter/Debug.php +++ b/src/Formatter/Debug.php @@ -33,6 +33,7 @@ public function __construct() $this->close = ' }'; $this->tagSeparator = ', '; $this->assignSeparator = ': '; + $this->keepSemicolons = true; } /** diff --git a/src/Formatter/Expanded.php b/src/Formatter/Expanded.php index b1a0bf24..54db742f 100644 --- a/src/Formatter/Expanded.php +++ b/src/Formatter/Expanded.php @@ -33,6 +33,7 @@ public function __construct() $this->close = '}'; $this->tagSeparator = ', '; $this->assignSeparator = ': '; + $this->keepSemicolons = true; } /** diff --git a/src/Formatter/Nested.php b/src/Formatter/Nested.php index ed9f075b..9fdb4dd0 100644 --- a/src/Formatter/Nested.php +++ b/src/Formatter/Nested.php @@ -38,6 +38,7 @@ public function __construct() $this->close = ' }'; $this->tagSeparator = ', '; $this->assignSeparator = ': '; + $this->keepSemicolons = true; } /** diff --git a/src/Formatter/StripSemiColons.php b/src/Formatter/StripSemiColons.php deleted file mode 100644 index 8e42c16e..00000000 --- a/src/Formatter/StripSemiColons.php +++ /dev/null @@ -1,46 +0,0 @@ - - */ -trait StripSemiColons -{ - /** - * {@inheritdoc} - */ - public function __construct() - { - $this->indentLevel = 0; - $this->indentChar = ' '; - $this->break = ''; - $this->open = '{'; - $this->close = '}'; - $this->tagSeparator = ','; - $this->assignSeparator = ':'; - } - - /** - * {@inheritdoc} - */ - public function stripSemicolon(&$lines) - { - if (($count = count($lines)) - && substr($lines[$count - 1], -1) === ';' - ) { - $lines[$count - 1] = substr($lines[$count - 1], 0, -1); - } - } -} From feecdbe1defcd89089e51bf89bf77e0332658517 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 16 Dec 2015 17:46:24 -0500 Subject: [PATCH 185/484] Bump version to v0.6.2 --- src/Version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Version.php b/src/Version.php index ef096e94..0d54dd51 100644 --- a/src/Version.php +++ b/src/Version.php @@ -18,5 +18,5 @@ */ class Version { - const VERSION = 'v0.6.1'; + const VERSION = 'v0.6.2'; } From aa18ff29729691ccbececb52d839c5a517505980 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Thu, 24 Dec 2015 12:33:50 -0500 Subject: [PATCH 186/484] fixes #396 extend+parent+placeholder --- src/Compiler.php | 19 +++++++++++++------ tests/inputs/extends.scss | 20 ++++++++++++++++++++ tests/outputs/extends.css | 9 +++++++++ tests/outputs_numbered/extends.css | 14 ++++++++++++++ 4 files changed, 56 insertions(+), 6 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 64dfb8b3..8c14d36d 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -521,21 +521,28 @@ protected function matchExtendsSingle($rawSingle, &$outOrigin) */ protected function combineSelectorSingle($base, $other) { - $tag = null; + $tag = []; $out = []; + $wasTag = true; foreach ([$base, $other] as $single) { foreach ($single as $part) { - if (preg_match('/^[^\[.#:]/', $part)) { - $tag = $part; - } else { + if (preg_match('/^[\[.:#]/', $part)) { $out[] = $part; + $wasTag = false; + } elseif (preg_match('/^[^_-]/', $part)) { + $tag[] = $part; + $wasTag = true; + } elseif ($wasTag) { + $tag[count($tag) - 1] .= $part; + } else { + $out[count($out) - 1] .= $part; } } } - if ($tag) { - array_unshift($out, $tag); + if (count($tag)) { + array_unshift($out, $tag[0]); } return $out; diff --git a/tests/inputs/extends.scss b/tests/inputs/extends.scss index 9da03502..467d3624 100644 --- a/tests/inputs/extends.scss +++ b/tests/inputs/extends.scss @@ -285,3 +285,23 @@ $var: ".selector1, .selector2"; a.important { @extend .notice !optional; } + +$color-base: white; + +%pagination-bullet { + width: 6px; + height: 6px; + margin-left: 5px; + &:first-child { + margin-left: 0; + } + &.is-active { + background-color: $color-base; + } +} + +.pagination { + &-bullet { + @extend %pagination-bullet; + } +} diff --git a/tests/outputs/extends.css b/tests/outputs/extends.css index 82a65823..8e2916a3 100644 --- a/tests/outputs/extends.css +++ b/tests/outputs/extends.css @@ -129,3 +129,12 @@ body .to-extend, body .test { font-size: 14px; } .main .block__element--modifier { font-weight: bold; } + +.pagination-bullet { + width: 6px; + height: 6px; + margin-left: 5px; } + .pagination-bullet:first-child { + margin-left: 0; } + .pagination-bullet.is-active { + background-color: white; } diff --git a/tests/outputs_numbered/extends.css b/tests/outputs_numbered/extends.css index 5d76d9d3..e32f01b7 100644 --- a/tests/outputs_numbered/extends.css +++ b/tests/outputs_numbered/extends.css @@ -201,3 +201,17 @@ body .to-extend, body .test { .main .block__element--modifier { font-weight: bold; } /* line 285, inputs/extends.scss */ +/* line 291, inputs/extends.scss */ + +.pagination-bullet { + width: 6px; + height: 6px; + margin-left: 5px; } +/* line 295, inputs/extends.scss */ +.pagination-bullet:first-child { + margin-left: 0; } +/* line 298, inputs/extends.scss */ +.pagination-bullet.is-active { + background-color: white; } +/* line 303, inputs/extends.scss */ +/* line 304, inputs/extends.scss */ From 3eef90cd8ecff0790713090d1cfd17e1cced589f Mon Sep 17 00:00:00 2001 From: josh Date: Sun, 27 Dec 2015 12:50:20 -0700 Subject: [PATCH 187/484] improve performance: use substr() instead of preg_match() for literals --- src/Parser.php | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/Parser.php b/src/Parser.php index 95e26049..3b44cfcd 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -64,6 +64,8 @@ class Parser private $buffer; private $utf8; private $encoding; + private $pattern_modifiers = 'Ais'; + /** * Constructor @@ -81,6 +83,10 @@ public function __construct($sourceName, $sourceIndex = 0, $encoding = 'utf-8') $this->charset = null; $this->utf8 = ! $encoding || strtolower($encoding) === 'utf-8'; + if ($this->utf8) { + $this->pattern_modifiers = 'Aisu'; + } + if (empty(self::$operatorPattern)) { self::$operatorPattern = '([*\/%+-]|[!=]\=|\>\=?|\<\=\>|\<\=?|and|or)'; @@ -782,7 +788,7 @@ protected function peek($regex, &$out, $from = null) $from = $this->count; } - $r = $this->utf8 ? '/' . $regex . '/Aisu' : '/' . $regex . '/Ais'; + $r = '/' . $regex . '/'.$this->pattern_modifiers; $result = preg_match($r, $this->buffer, $out, null, $from); return $result; @@ -862,7 +868,7 @@ protected function match($regex, &$out, $eatWhitespace = null) $eatWhitespace = $this->eatWhiteDefault; } - $r = $this->utf8 ? '/' . $regex . '/Aisu' : '/' . $regex . '/Ais'; + $r = '/' . $regex . '/'.$this->pattern_modifiers; if (preg_match($r, $this->buffer, $out, null, $this->count)) { $this->count += strlen($out[0]); @@ -891,24 +897,20 @@ protected function literal($what, $eatWhitespace = null) $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; + $len = strlen($what); + if( substr($this->buffer,$this->count,$len) === $what ){ + $this->count += $len; + if ($eatWhitespace) { + $this->whitespace(); } + return true; } - return $this->match($this->pregQuote($what), $m, $eatWhitespace); + return false; } + /** * Match some whitespace * From c45de613d9b31ccf02559fc5e2accb6b565feeed Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 29 Dec 2015 23:09:50 -0500 Subject: [PATCH 188/484] Fix PSR2 and naming convention violations --- src/Parser.php | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/Parser.php b/src/Parser.php index 3b44cfcd..daa61b96 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -64,7 +64,7 @@ class Parser private $buffer; private $utf8; private $encoding; - private $pattern_modifiers = 'Ais'; + private $patternModifiers = 'Ais'; /** @@ -84,7 +84,7 @@ public function __construct($sourceName, $sourceIndex = 0, $encoding = 'utf-8') $this->utf8 = ! $encoding || strtolower($encoding) === 'utf-8'; if ($this->utf8) { - $this->pattern_modifiers = 'Aisu'; + $this->patternModifiers = 'Aisu'; } if (empty(self::$operatorPattern)) { @@ -788,7 +788,7 @@ protected function peek($regex, &$out, $from = null) $from = $this->count; } - $r = '/' . $regex . '/'.$this->pattern_modifiers; + $r = '/' . $regex . '/'.$this->patternModifiers; $result = preg_match($r, $this->buffer, $out, null, $from); return $result; @@ -868,7 +868,7 @@ protected function match($regex, &$out, $eatWhitespace = null) $eatWhitespace = $this->eatWhiteDefault; } - $r = '/' . $regex . '/'.$this->pattern_modifiers; + $r = '/' . $regex . '/'.$this->patternModifiers; if (preg_match($r, $this->buffer, $out, null, $this->count)) { $this->count += strlen($out[0]); @@ -897,20 +897,21 @@ protected function literal($what, $eatWhitespace = null) $eatWhitespace = $this->eatWhiteDefault; } - $len = strlen($what); - if( substr($this->buffer,$this->count,$len) === $what ){ + + if (substr($this->buffer, $this->count, $len) === $what) { $this->count += $len; + if ($eatWhitespace) { $this->whitespace(); } + return true; } return false; } - /** * Match some whitespace * From b949c6e4effef797267744e0f421a7c6918240c2 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 29 Dec 2015 23:41:47 -0500 Subject: [PATCH 189/484] coding style --- src/Parser.php | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/src/Parser.php b/src/Parser.php index daa61b96..7874a97c 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -64,8 +64,7 @@ class Parser private $buffer; private $utf8; private $encoding; - private $patternModifiers = 'Ais'; - + private $patternModifiers; /** * Constructor @@ -78,14 +77,11 @@ class Parser */ public function __construct($sourceName, $sourceIndex = 0, $encoding = 'utf-8') { - $this->sourceName = $sourceName ?: '(stdin)'; - $this->sourceIndex = $sourceIndex; - $this->charset = null; - $this->utf8 = ! $encoding || strtolower($encoding) === 'utf-8'; - - if ($this->utf8) { - $this->patternModifiers = 'Aisu'; - } + $this->sourceName = $sourceName ?: '(stdin)'; + $this->sourceIndex = $sourceIndex; + $this->charset = null; + $this->utf8 = ! $encoding || strtolower($encoding) === 'utf-8'; + $this->patternModifiers = $this->utf8 ? 'Aisu' : 'Ais'; if (empty(self::$operatorPattern)) { self::$operatorPattern = '([*\/%+-]|[!=]\=|\>\=?|\<\=\>|\<\=?|and|or)'; @@ -788,7 +784,7 @@ protected function peek($regex, &$out, $from = null) $from = $this->count; } - $r = '/' . $regex . '/'.$this->patternModifiers; + $r = '/' . $regex . '/' . $this->patternModifiers; $result = preg_match($r, $this->buffer, $out, null, $from); return $result; @@ -868,7 +864,7 @@ protected function match($regex, &$out, $eatWhitespace = null) $eatWhitespace = $this->eatWhiteDefault; } - $r = '/' . $regex . '/'.$this->patternModifiers; + $r = '/' . $regex . '/' . $this->patternModifiers; if (preg_match($r, $this->buffer, $out, null, $this->count)) { $this->count += strlen($out[0]); From 42e863ec7d18a7894da472358dc70e0fda7f04de Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Thu, 31 Dec 2015 23:30:53 -0500 Subject: [PATCH 190/484] add unit test from #399 --- tests/inputs/content.scss | 19 +++++++++++++++++++ tests/outputs/content.css | 3 +++ tests/outputs_numbered/content.css | 3 +++ 3 files changed, 25 insertions(+) diff --git a/tests/inputs/content.scss b/tests/inputs/content.scss index 7fef7800..bae453b3 100644 --- a/tests/inputs/content.scss +++ b/tests/inputs/content.scss @@ -97,3 +97,22 @@ A { } } } + +@mixin two() { + @content; +} +@mixin three() { + @content; +} +@mixin one() { + @include two() { + @include three() { + @content; + } + } +} +.test { + @include one() { + display: none; + } +} diff --git a/tests/outputs/content.css b/tests/outputs/content.css index 93ed1372..6a8ba90b 100644 --- a/tests/outputs/content.css +++ b/tests/outputs/content.css @@ -33,3 +33,6 @@ A { top: 10px; } + +.test { + display: none; } diff --git a/tests/outputs_numbered/content.css b/tests/outputs_numbered/content.css index ace5a98a..a0bf6e1e 100644 --- a/tests/outputs_numbered/content.css +++ b/tests/outputs_numbered/content.css @@ -42,3 +42,6 @@ /* line 91, inputs/content.scss */ A { top: 10px; } +/* line 114, inputs/content.scss */ +.test { + display: none; } From 1176de902cfafee5779d6d1291be92e83392bb50 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Fri, 1 Jan 2016 00:07:40 -0500 Subject: [PATCH 191/484] fixes #399 - nested @content infinite loop --- src/Compiler.php | 5 +++-- tests/inputs/content.scss | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 8c14d36d..d6afc99a 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -1698,7 +1698,7 @@ protected function compileChild($child, OutputBlock $out) if (isset($content)) { $content->scope = $callingScope; - $this->setRaw(self::$namespaces['special'] . 'content', $content, $this->getStoreEnv()); + $this->setRaw(self::$namespaces['special'] . 'content', $content, $this->env); } if (isset($mixin->args)) { @@ -1713,7 +1713,8 @@ protected function compileChild($child, OutputBlock $out) break; case Type::T_MIXIN_CONTENT: - $content = $this->get(self::$namespaces['special'] . 'content', false, $this->getStoreEnv()); + $content = $this->get(self::$namespaces['special'] . 'content', false, $this->getStoreEnv()) + ?: $this->get(self::$namespaces['special'] . 'content', false, $this->env); if (! $content) { $this->throwError('Expected @content inside of mixin'); diff --git a/tests/inputs/content.scss b/tests/inputs/content.scss index bae453b3..a9c00c46 100644 --- a/tests/inputs/content.scss +++ b/tests/inputs/content.scss @@ -60,7 +60,7 @@ $color: white; } } -@mixin nested { +@mixin nested($args) { * body { @content; } From 6de8e373951a0094eabef2ab379a516d3ac7e131 Mon Sep 17 00:00:00 2001 From: Jonathan Rehm Date: Thu, 14 Jan 2016 17:07:32 -0700 Subject: [PATCH 192/484] Only divide by 100 if number is truly a percent Includes test --- src/Compiler.php | 2 +- tests/inputs/builtins.scss | 1 + tests/outputs/builtins.css | 1 + tests/outputs_numbered/builtins.css | 17 +++++++++-------- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index d6afc99a..ded0b7a0 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -3752,7 +3752,7 @@ protected function coerceString($value) protected function coercePercent($value) { if ($value[0] === Type::T_NUMBER) { - if (isset($value[2]['%'])) { + if (!empty($value[2]['%'])) { return $value[1] / 100; } diff --git a/tests/inputs/builtins.scss b/tests/inputs/builtins.scss index 70ec3012..a80db5b1 100644 --- a/tests/inputs/builtins.scss +++ b/tests/inputs/builtins.scss @@ -104,6 +104,7 @@ #number { color: percentage(100/40); + $test: 50%; $test: 0 + ($test / 100%); color: percentage($test); color: round(3.4); color: floor(3.4); color: ceil(3.4); diff --git a/tests/outputs/builtins.css b/tests/outputs/builtins.css index fa74c1e2..50e2a82e 100644 --- a/tests/outputs/builtins.css +++ b/tests/outputs/builtins.css @@ -76,6 +76,7 @@ #number { color: 250%; + color: 50%; color: 3; color: 3; color: 4; diff --git a/tests/outputs_numbered/builtins.css b/tests/outputs_numbered/builtins.css index b0170c8d..722c6af4 100644 --- a/tests/outputs_numbered/builtins.css +++ b/tests/outputs_numbered/builtins.css @@ -77,6 +77,7 @@ /* line 105, inputs/builtins.scss */ #number { color: 250%; + color: 50%; color: 3; color: 3; color: 4; @@ -85,7 +86,7 @@ width: 200%; bottom: 10px; padding: 3em 1in 96px 72pt; } -/* line 118, inputs/builtins.scss */ +/* line 119, inputs/builtins.scss */ #list { len: 3; len: 1; @@ -107,7 +108,7 @@ cool: great job one two three; zip: 1px solid, 2px dashed; zip: 1px solid red, 2px dashed green; } -/* line 154, inputs/builtins.scss */ +/* line 155, inputs/builtins.scss */ #introspection { t: number; t: string; @@ -127,35 +128,35 @@ c: true; c: false; c: true; } -/* line 178, inputs/builtins.scss */ +/* line 179, inputs/builtins.scss */ #if { color: yes; color: no; color: yes; color: yes; } -/* line 185, inputs/builtins.scss */ +/* line 186, inputs/builtins.scss */ .transparent { r: 0; g: 0; b: 0; a: 0; } -/* line 192, inputs/builtins.scss */ +/* line 193, inputs/builtins.scss */ .alpha { a: 1; a: 1; a: 1; a: 0.5; a: alpha(currentColor); } -/* line 201, inputs/builtins.scss */ +/* line 202, inputs/builtins.scss */ #exists { a: true; b: true; c: false; } -/* line 208, inputs/builtins.scss */ +/* line 209, inputs/builtins.scss */ div.call-tests { a: #0a64ff; b: #0058ef; c: b; } -/* line 215, inputs/builtins.scss */ +/* line 216, inputs/builtins.scss */ div.unquote-test { a: [type='text']; } From 11d2c785748fe2cb19fe9f9b14eb7f9bd53e70bf Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Thu, 14 Jan 2016 21:44:47 -0500 Subject: [PATCH 193/484] spacing --- src/Compiler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compiler.php b/src/Compiler.php index ded0b7a0..b19b9e56 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -3752,7 +3752,7 @@ protected function coerceString($value) protected function coercePercent($value) { if ($value[0] === Type::T_NUMBER) { - if (!empty($value[2]['%'])) { + if (! empty($value[2]['%'])) { return $value[1] / 100; } From a27edad3d16635a222d7204706572e24c338aa17 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Thu, 14 Jan 2016 21:50:06 -0500 Subject: [PATCH 194/484] bump version to 0.6.3 --- src/Version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Version.php b/src/Version.php index 0d54dd51..8dfa0e93 100644 --- a/src/Version.php +++ b/src/Version.php @@ -18,5 +18,5 @@ */ class Version { - const VERSION = 'v0.6.2'; + const VERSION = 'v0.6.3'; } From aef75351d3496ccd478ef169440da43a0005ea34 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sat, 23 Jan 2016 08:05:40 -0500 Subject: [PATCH 195/484] Update travis CI config --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 80b481f1..e5b70fb7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,7 @@ php: - 5.4 - 5.5 - 5.6 + - 7.0 - nightly script: From 0ac284821993e4eac001f5b3d32203a826ae7251 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Mon, 8 Feb 2016 10:34:46 -0500 Subject: [PATCH 196/484] fixes #411; regression from #397 --- src/Parser.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Parser.php b/src/Parser.php index 7874a97c..42d8f0dc 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -895,7 +895,7 @@ protected function literal($what, $eatWhitespace = null) $len = strlen($what); - if (substr($this->buffer, $this->count, $len) === $what) { + if (strcasecmp(substr($this->buffer, $this->count, $len), $what) === 0) { $this->count += $len; if ($eatWhitespace) { From b1905524a27fa62c8fec709c14008ef75048cae7 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Thu, 11 Feb 2016 00:40:00 -0500 Subject: [PATCH 197/484] fixes #412 - parsing multiple assignment flags --- src/Compiler.php | 6 +++--- src/Parser.php | 23 +++++++++++++---------- tests/inputs/variables.scss | 7 +++++++ tests/outputs/variables.css | 3 +++ tests/outputs_numbered/variables.css | 3 +++ 5 files changed, 29 insertions(+), 13 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index b19b9e56..8fbbc1a2 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -1460,9 +1460,9 @@ protected function compileChild($child, OutputBlock $out) list(, $name, $value) = $child; if ($name[0] === Type::T_VARIABLE) { - $flag = isset($child[3]) ? $child[3] : null; - $isDefault = $flag === '!default'; - $isGlobal = $flag === '!global'; + $flags = isset($child[3]) ? $child[3] : []; + $isDefault = in_array('!default', $flags); + $isGlobal = in_array('!global', $flags); if ($isGlobal) { $this->set($name[1], $this->reduce($value), false, $this->rootEnv); diff --git a/src/Parser.php b/src/Parser.php index 42d8f0dc..39b306eb 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -617,8 +617,8 @@ protected function parseChunk() $this->end() ) { // check for '!flag' - $assignmentFlag = $this->stripAssignmentFlag($value); - $this->append([Type::T_ASSIGN, $name, $value, $assignmentFlag], $s); + $assignmentFlags = $this->stripAssignmentFlags($value); + $this->append([Type::T_ASSIGN, $name, $value, $assignmentFlags], $s); return true; } @@ -2287,25 +2287,28 @@ protected function end() * * @param array $value * - * @return string + * @return array */ - protected function stripAssignmentFlag(&$value) + protected function stripAssignmentFlags(&$value) { - $token = &$value; + $flags = []; for ($token = &$value; $token[0] === Type::T_LIST && ($s = count($token[2])); $token = &$lastNode) { - $lastNode = &$token[2][$s - 1]; - - if ($lastNode[0] === Type::T_KEYWORD && in_array($lastNode[1], ['!default', '!global'])) { + for ($lastNode = &$token[2][$s - 1]; + $lastNode[0] === Type::T_KEYWORD && in_array($lastNode[1], ['!default', '!global']); + $lastNode = $node + ) { array_pop($token[2]); + $node = end($token[2]); + $token = $this->flattenList($token); - return $lastNode[1]; + $flags[] = $lastNode[1]; } } - return false; + return $flags; } /** diff --git a/tests/inputs/variables.scss b/tests/inputs/variables.scss index 5212c385..d6461a86 100644 --- a/tests/inputs/variables.scss +++ b/tests/inputs/variables.scss @@ -80,3 +80,10 @@ body { color: $цвет; } + +$test: 12 !default !global; + +* { + // Expected: 12 + data: $test; +} diff --git a/tests/outputs/variables.css b/tests/outputs/variables.css index ed3e6b21..7112efae 100644 --- a/tests/outputs/variables.css +++ b/tests/outputs/variables.css @@ -34,3 +34,6 @@ A { body { color: #000; } + +* { + data: 12; } diff --git a/tests/outputs_numbered/variables.css b/tests/outputs_numbered/variables.css index 206fa249..77153c44 100644 --- a/tests/outputs_numbered/variables.css +++ b/tests/outputs_numbered/variables.css @@ -37,3 +37,6 @@ A { /* line 79, inputs/variables.scss */ body { color: #000; } +/* line 86, inputs/variables.scss */ +* { + data: 12; } From b670910167016567c1891b7b59e94be249c58cbd Mon Sep 17 00:00:00 2001 From: Raul Date: Mon, 22 Feb 2016 20:53:24 +0100 Subject: [PATCH 198/484] Fix Issue #416 --- src/Compiler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compiler.php b/src/Compiler.php index 8fbbc1a2..e60ae331 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -1742,7 +1742,7 @@ protected function compileChild($child, OutputBlock $out) $line = $this->sourceLine; $value = $this->compileValue($this->reduce($value, true)); - echo "Line $line WARN: $value\n"; + fwrite($this->stderr, "Line $line WARN: $value\n"); break; case Type::T_ERROR: From fbe587471b5bfe1e57cde7fb42df8c3ce20626da Mon Sep 17 00:00:00 2001 From: Moises Date: Tue, 23 Feb 2016 20:46:43 +0100 Subject: [PATCH 199/484] Fix: ( null or/and 'foo' ) --- src/Compiler.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 8fbbc1a2..49beeccc 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -2216,7 +2216,7 @@ protected function opAnd($left, $right, $shouldEval) return; } - if ($left !== self::$false) { + if ($left !== self::$false and $left !== self::$null) { return $this->reduce($right, true); } @@ -2238,7 +2238,7 @@ protected function opOr($left, $right, $shouldEval) return; } - if ($left !== self::$false) { + if ($left !== self::$false and $left !== self::$null) { return $left; } From 9d763a8474ee2a8b0935db959a0e7bec269b3919 Mon Sep 17 00:00:00 2001 From: Moises Date: Tue, 23 Feb 2016 21:26:42 +0100 Subject: [PATCH 200/484] add unit test PR #418 --- tests/inputs/operators.scss | 2 + tests/outputs/operators.css | 3 +- tests/outputs_numbered/operators.css | 55 ++++++++++++++-------------- 3 files changed, 32 insertions(+), 28 deletions(-) diff --git a/tests/inputs/operators.scss b/tests/inputs/operators.scss index 79662604..31e65b17 100644 --- a/tests/inputs/operators.scss +++ b/tests/inputs/operators.scss @@ -99,10 +99,12 @@ div { and: (false and two); and: (one and two); and: (one and false); + and: (null and two); or: (false or two); or: (one or two); or: (one or false); + or: (null or two); } diff --git a/tests/outputs/operators.css b/tests/outputs/operators.css index 3582c871..fb492c82 100644 --- a/tests/outputs/operators.css +++ b/tests/outputs/operators.css @@ -77,7 +77,8 @@ div { and: false; or: two; or: one; - or: one; } + or: one; + or: two; } #nots-fail { not: false2; diff --git a/tests/outputs_numbered/operators.css b/tests/outputs_numbered/operators.css index acf984e3..c3ea3d1d 100644 --- a/tests/outputs_numbered/operators.css +++ b/tests/outputs_numbered/operators.css @@ -78,8 +78,9 @@ div { and: false; or: two; or: one; - or: one; } -/* line 109, inputs/operators.scss */ + or: one; + or: two; } +/* line 111, inputs/operators.scss */ #nots-fail { not: false2; not: not false; @@ -87,7 +88,7 @@ div { not: not 1; not: not ""; not: not hello; } -/* line 118, inputs/operators.scss */ +/* line 120, inputs/operators.scss */ #nots { not: false2; not: true; @@ -96,7 +97,7 @@ div { not: false; not: false; not: true; } -/* line 128, inputs/operators.scss */ +/* line 130, inputs/operators.scss */ #string-test { str: true; str: false; @@ -104,72 +105,72 @@ div { str: true; str: xhellohellofalse; str: true; } -/* line 144, inputs/operators.scss */ +/* line 146, inputs/operators.scss */ #special { cancel-unit: 1; } -/* line 151, inputs/operators.scss */ +/* line 153, inputs/operators.scss */ .row .a { margin: -0.5em; } -/* line 152, inputs/operators.scss */ +/* line 154, inputs/operators.scss */ .row .b { margin: -0.5em; } -/* line 153, inputs/operators.scss */ +/* line 155, inputs/operators.scss */ .row .c { margin: -0.5em; } -/* line 154, inputs/operators.scss */ +/* line 156, inputs/operators.scss */ .row .d { margin: -0.5em; } -/* line 155, inputs/operators.scss */ +/* line 157, inputs/operators.scss */ .row .e { margin: 0 -0.5em; } -/* line 157, inputs/operators.scss */ +/* line 159, inputs/operators.scss */ .alt .a { margin: -0.5em; } -/* line 158, inputs/operators.scss */ +/* line 160, inputs/operators.scss */ .alt .b { margin: -0.5em; } -/* line 159, inputs/operators.scss */ +/* line 161, inputs/operators.scss */ .alt .c { margin: -0.5em; } -/* line 160, inputs/operators.scss */ +/* line 162, inputs/operators.scss */ .alt .d { margin: 0 -0.5em; } -/* line 161, inputs/operators.scss */ +/* line 163, inputs/operators.scss */ .alt .e { margin: 0 -0.5em; } -/* line 163, inputs/operators.scss */ +/* line 165, inputs/operators.scss */ .row .f { margin: -2em; } -/* line 164, inputs/operators.scss */ +/* line 166, inputs/operators.scss */ .row .g { margin: -2em; } -/* line 165, inputs/operators.scss */ +/* line 167, inputs/operators.scss */ .row .h { margin: -2em; } -/* line 166, inputs/operators.scss */ +/* line 168, inputs/operators.scss */ .row .i { margin: -2em; } -/* line 167, inputs/operators.scss */ +/* line 169, inputs/operators.scss */ .row .j { margin: 0 -2em; } -/* line 169, inputs/operators.scss */ +/* line 171, inputs/operators.scss */ .alt .f { margin: -2em; } -/* line 170, inputs/operators.scss */ +/* line 172, inputs/operators.scss */ .alt .g { margin: -2em; } -/* line 171, inputs/operators.scss */ +/* line 173, inputs/operators.scss */ .alt .h { margin: -2em; } -/* line 172, inputs/operators.scss */ +/* line 174, inputs/operators.scss */ .alt .i { margin: 0 -2em; } -/* line 173, inputs/operators.scss */ +/* line 175, inputs/operators.scss */ .alt .j { margin: 0 -2em; } -/* line 182, inputs/operators.scss */ +/* line 184, inputs/operators.scss */ div { *margin-left: 2.07447%; } -/* line 188, inputs/operators.scss */ +/* line 190, inputs/operators.scss */ .foo { width: 12.5%; } From 9f4655e6c6324a6cff7b51c0dc2589b125bd10d5 Mon Sep 17 00:00:00 2001 From: jo Date: Wed, 20 Jan 2016 14:13:39 +0100 Subject: [PATCH 201/484] Compiler: change scope to faciliate subclassing --- src/Compiler.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 0120872a..19623e6a 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -125,20 +125,21 @@ class Compiler 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 $sourceNames; private $sourceIndex; private $sourceLine; private $sourceColumn; - private $storeEnv; - private $charsetSeen; private $stderr; private $shouldEvaluate; private $ignoreErrors; @@ -207,7 +208,7 @@ public function compile($code, $path = null) * * @return \Leafo\ScssPhp\Parser */ - private function parserFactory($path) + protected function parserFactory($path) { $parser = new Parser($path, count($this->sourceNames), $this->encoding); @@ -2944,7 +2945,6 @@ public function get($name, $shouldThrow = true, Environment $env = null) } $hasNamespace = $name[0] === '^' || $name[0] === '@' || $name[0] === '%'; - for (;;) { if (array_key_exists($name, $env->store)) { return $env->store[$name]; @@ -3303,7 +3303,7 @@ public function throwError($msg) * * @throws \Exception */ - private function handleImportLoop($name) + protected function handleImportLoop($name) { for ($env = $this->env; $env; $env = $env->parent) { $file = $this->sourceNames[$env->block->sourceIndex]; From c4ad181885e5f85a9f71002983597d9da2c11a8c Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 24 Feb 2016 10:41:07 -0500 Subject: [PATCH 202/484] phpcs --- src/Parser.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Parser.php b/src/Parser.php index 39b306eb..3cdd2c7e 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -2294,10 +2294,9 @@ protected function stripAssignmentFlags(&$value) $flags = []; for ($token = &$value; $token[0] === Type::T_LIST && ($s = count($token[2])); $token = &$lastNode) { - for ($lastNode = &$token[2][$s - 1]; - $lastNode[0] === Type::T_KEYWORD && in_array($lastNode[1], ['!default', '!global']); - $lastNode = $node - ) { + $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]); @@ -2305,6 +2304,8 @@ protected function stripAssignmentFlags(&$value) $token = $this->flattenList($token); $flags[] = $lastNode[1]; + + $lastNode = $node; } } From f73a054101715a2cd288658291c7425c39fdcfc8 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 15 Jun 2016 18:20:16 -0400 Subject: [PATCH 203/484] Bump version to v0.6.4 --- src/Version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Version.php b/src/Version.php index 8dfa0e93..45db5de9 100644 --- a/src/Version.php +++ b/src/Version.php @@ -18,5 +18,5 @@ */ class Version { - const VERSION = 'v0.6.3'; + const VERSION = 'v0.6.4'; } From 1d700dcf86c92144857ccd390cabd51feef4e087 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 15 Jun 2016 18:32:10 -0400 Subject: [PATCH 204/484] fixes #426 keep the un-normalized name for error reporting --- src/Compiler.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 19623e6a..87ffd68f 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -2938,16 +2938,16 @@ protected function setRaw($name, $value, Environment $env) */ public function get($name, $shouldThrow = true, Environment $env = null) { - $name = $this->normalizeName($name); + $normalizedName = $this->normalizeName($name); if (! isset($env)) { $env = $this->getStoreEnv(); } - $hasNamespace = $name[0] === '^' || $name[0] === '@' || $name[0] === '%'; + $hasNamespace = $normalizedName[0] === '^' || $normalizedName[0] === '@' || $normalizedName[0] === '%'; for (;;) { - if (array_key_exists($name, $env->store)) { - return $env->store[$name]; + if (array_key_exists($normalizedName, $env->store)) { + return $env->store[$normalizedName]; } if (! $hasNamespace && isset($env->marker)) { From b42b2e23128b06f482f8947533f7dfa0c49a3123 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Thu, 16 Jun 2016 11:33:35 -0400 Subject: [PATCH 205/484] Compiler: coerceValue support for #rgb values --- src/Compiler.php | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/Compiler.php b/src/Compiler.php index 87ffd68f..6e5e45dc 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -3622,6 +3622,29 @@ private function coerceValue($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]); + + foreach ([3, 2, 1] as $i) { + $color[$i] = $num & 0xff; + $num >>= 8; + } + } + + return $color; + } + return [Type::T_KEYWORD, $value]; } From 1270b5c5437559b07f9237aa11ee035c9a07d997 Mon Sep 17 00:00:00 2001 From: Matias Griese Date: Thu, 16 Jun 2016 21:28:23 +0300 Subject: [PATCH 206/484] Fix mixin and variable scopes (#440) --- src/Compiler.php | 10 ++++++++ tests/inputs/functions.scss | 38 ++++++++++++++++++++++++++++ tests/inputs/mixins.scss | 18 +++++++++++++ tests/outputs/functions.css | 4 +++ tests/outputs/mixins.css | 7 +++++ tests/outputs_numbered/functions.css | 4 +++ tests/outputs_numbered/mixins.css | 8 ++++++ 7 files changed, 89 insertions(+) diff --git a/src/Compiler.php b/src/Compiler.php index 6e5e45dc..96e71692 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -1696,6 +1696,9 @@ protected function compileChild($child, OutputBlock $out) $this->pushEnv(); $this->env->depth--; + $storeEnv = $this->storeEnv; + $this->storeEnv = $this->env; + if (isset($content)) { $content->scope = $callingScope; @@ -1710,6 +1713,8 @@ protected function compileChild($child, OutputBlock $out) $this->compileChildrenNoReturn($mixin->children, $out); + $this->storeEnv = $storeEnv; + $this->popEnv(); break; @@ -3346,6 +3351,9 @@ protected function callScssFunction($name, $argValues, &$returnValue) $this->pushEnv(); + $storeEnv = $this->storeEnv; + $this->storeEnv = $this->env; + // set the args if (isset($func->args)) { $this->applyArguments($func->args, $argValues); @@ -3360,6 +3368,8 @@ protected function callScssFunction($name, $argValues, &$returnValue) $ret = $this->compileChildren($func->children, $tmp); + $this->storeEnv = $storeEnv; + $this->popEnv(); $returnValue = ! isset($ret) ? self::$defaultValue : $ret; diff --git a/tests/inputs/functions.scss b/tests/inputs/functions.scss index bcceaffd..bf3edf1f 100644 --- a/tests/inputs/functions.scss +++ b/tests/inputs/functions.scss @@ -128,3 +128,41 @@ $str: 'global'; l4: inspect((a: 1, b: 2)); l5: inspect($value: (a: 1, b: 2)); } + +@function contains($list, $values...) { + @each $value in $values { + @if type-of(index($list, $value)) != "number" { + @return false; + } + } + + @return true; +} + +@function mapping($items) { + $src: (); + $map: ( + one: 1px 1px, + two: 2px 2px, + ); + + @each $key, $values in $map { + @if contains($items, $key) { + $value1: nth($values, 1); + $value2: nth($values, 2); + + $src: append($src, $value1 $value2, space); + } + } + + @return $src; +} + +@mixin test() { + padding: mapping(one two); +} + +div { + margin: mapping(one two); + @include test(); +} \ No newline at end of file diff --git a/tests/inputs/mixins.scss b/tests/inputs/mixins.scss index 63f9007b..02ed81d0 100644 --- a/tests/inputs/mixins.scss +++ b/tests/inputs/mixins.scss @@ -190,3 +190,21 @@ div.parameter-name-scope { .test{ @include test-mixin(); } + +@mixin inner($value) {} + +@mixin outer($value) { + #{$value} { + content: "#{$value}"; + @content; + content: "#{$value}"; + } +} + +@include outer(div) { + @include inner('break'); + @include outer(p) { + @include inner('break'); + } +} + diff --git a/tests/outputs/functions.css b/tests/outputs/functions.css index 252e3909..aa0bebbb 100644 --- a/tests/outputs/functions.css +++ b/tests/outputs/functions.css @@ -45,3 +45,7 @@ p { l3: 5, 6; l4: (a: 1, b: 2); l5: (a: 1, b: 2); } + +div { + margin: 1px 1px 2px 2px; + padding: 1px 1px 2px 2px; } diff --git a/tests/outputs/mixins.css b/tests/outputs/mixins.css index e2004a07..ee692574 100644 --- a/tests/outputs/mixins.css +++ b/tests/outputs/mixins.css @@ -95,3 +95,10 @@ div.parameter-name-scope { @media screen and (min-width:0\0) { .test { color: #000; } } + +div { + content: "div"; + content: "div"; } + div p { + content: "p"; + content: "p"; } diff --git a/tests/outputs_numbered/functions.css b/tests/outputs_numbered/functions.css index 9b525e8d..6583ff05 100644 --- a/tests/outputs_numbered/functions.css +++ b/tests/outputs_numbered/functions.css @@ -47,3 +47,7 @@ p { l3: 5, 6; l4: (a: 1, b: 2); l5: (a: 1, b: 2); } +/* line 165, inputs/functions.scss */ +div { + margin: 1px 1px 2px 2px; + padding: 1px 1px 2px 2px; } diff --git a/tests/outputs_numbered/mixins.css b/tests/outputs_numbered/mixins.css index 2df05142..ed1f4ffb 100644 --- a/tests/outputs_numbered/mixins.css +++ b/tests/outputs_numbered/mixins.css @@ -108,3 +108,11 @@ div.parameter-name-scope { @media screen and (min-width:0\0) { .test { color: #000; } } +/* line 197, inputs/mixins.scss */ +div { + content: "div"; + content: "div"; } +/* line 197, inputs/mixins.scss */ +div p { + content: "p"; + content: "p"; } From 24388fafc2e9e9d997da7ca753336b15022b4148 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Mon, 20 Jun 2016 11:18:53 -0400 Subject: [PATCH 207/484] fixes #444 - strip optional BOM (byte order marker) --- src/Parser.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Parser.php b/src/Parser.php index 3cdd2c7e..ed0621ea 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -142,11 +142,16 @@ public function throwParseError($msg = 'parse error') */ 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->buffer = rtrim($buffer, "\x00..\x1f"); $this->saveEncoding(); $this->extractLineNumbers($buffer); From 588ebe29c5dd5c0308584fc5b758798c63f10fba Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Mon, 20 Jun 2016 11:24:44 -0400 Subject: [PATCH 208/484] #444 - add related unit test --- tests/ApiTest.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/ApiTest.php b/tests/ApiTest.php index ca5f3a28..8d89b588 100644 --- a/tests/ApiTest.php +++ b/tests/ApiTest.php @@ -105,6 +105,15 @@ public function provideSetVariables() ); } + public function testCompileByteOrderMarker() + { + // test that BOM is stripped/ignored + $this->assertEquals( + '@import "main";', + $this->compile("\xEF\xBB\xBF@import \"main\";") + ); + } + public function compile($str) { return trim($this->scss->compile($str)); From 0649d38dfef6808be1a89040a3312e8bda0b3aed Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Mon, 20 Jun 2016 11:25:08 -0400 Subject: [PATCH 209/484] Bump version to v0.6.5 --- src/Version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Version.php b/src/Version.php index 45db5de9..3be9cc31 100644 --- a/src/Version.php +++ b/src/Version.php @@ -18,5 +18,5 @@ */ class Version { - const VERSION = 'v0.6.4'; + const VERSION = 'v0.6.5'; } From e9400176899e3e10c7f3da06e79599d82ca22ae4 Mon Sep 17 00:00:00 2001 From: Alexssssss Date: Thu, 23 Jun 2016 16:50:55 +0200 Subject: [PATCH 210/484] Preg_match error array > string When a null value is given to coerceValue the following error is returned: ''' _WARNING [2]: preg_match() expects parameter 2 to be string, array given /vendor/leafo/scssphp/src/Compiler.php, line: 3635 ''' This change fixes that for me. --- src/Compiler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compiler.php b/src/Compiler.php index 96e71692..7e93fcb0 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -3621,7 +3621,7 @@ private function coerceValue($value) } if ($value === null) { - $value = self::$null; + return self::$null; } if (is_numeric($value)) { From 1149b762d93204e0d4a3a42866acd6b3ce2edc67 Mon Sep 17 00:00:00 2001 From: Frederic Massart Date: Tue, 12 Jul 2016 20:44:47 +0800 Subject: [PATCH 211/484] Resolve function arguments using mixin content scope --- src/Compiler.php | 8 +++++++ tests/inputs/nested_mixins.scss | 28 ++++++++++++++++++++++++ tests/outputs/nested_mixins.css | 9 ++++++++ tests/outputs_numbered/nested_mixins.css | 14 ++++++++++++ 4 files changed, 59 insertions(+) create mode 100644 tests/inputs/nested_mixins.scss create mode 100644 tests/outputs/nested_mixins.css create mode 100644 tests/outputs_numbered/nested_mixins.css diff --git a/src/Compiler.php b/src/Compiler.php index 7e93fcb0..485e0fd4 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -2944,11 +2944,13 @@ protected function setRaw($name, $value, Environment $env) public function get($name, $shouldThrow = true, Environment $env = null) { $normalizedName = $this->normalizeName($name); + $specialContentKey = self::$namespaces['special'] . 'content'; if (! isset($env)) { $env = $this->getStoreEnv(); } + $nextIsRoot = false; $hasNamespace = $normalizedName[0] === '^' || $normalizedName[0] === '@' || $normalizedName[0] === '%'; for (;;) { if (array_key_exists($normalizedName, $env->store)) { @@ -2956,6 +2958,12 @@ public function get($name, $shouldThrow = true, Environment $env = null) } if (! $hasNamespace && isset($env->marker)) { + if (! $nextIsRoot && !empty($env->store[$specialContentKey])) { + $env = $env->store[$specialContentKey]->scope; + $nextIsRoot = true; + continue; + } + $env = $this->rootEnv; continue; } diff --git a/tests/inputs/nested_mixins.scss b/tests/inputs/nested_mixins.scss new file mode 100644 index 00000000..f72ce365 --- /dev/null +++ b/tests/inputs/nested_mixins.scss @@ -0,0 +1,28 @@ +@mixin container() { + .container { + @content; + } +} +@mixin add-size($size) { + width: $size; +} +@function add($a, $b) { + @return $a + $b; +} + +div { + @include container() { + $i: 10px; + @include container() { + width: $i; + max-width: add($i, 2px); + } + @include add-size($i); + .foo { + @include add-size($i); + } + .bar { + @include add-size(add($i, 2px)); + } + } +} diff --git a/tests/outputs/nested_mixins.css b/tests/outputs/nested_mixins.css new file mode 100644 index 00000000..432580b8 --- /dev/null +++ b/tests/outputs/nested_mixins.css @@ -0,0 +1,9 @@ +div .container { + width: 10px; } + div .container .container { + width: 10px; + max-width: 12px; } + div .container .foo { + width: 10px; } + div .container .bar { + width: 12px; } diff --git a/tests/outputs_numbered/nested_mixins.css b/tests/outputs_numbered/nested_mixins.css new file mode 100644 index 00000000..ae9c6029 --- /dev/null +++ b/tests/outputs_numbered/nested_mixins.css @@ -0,0 +1,14 @@ +/* line 13, inputs/nested_mixins.scss */ +/* line 2, inputs/nested_mixins.scss */ + div .container { + width: 10px; } +/* line 2, inputs/nested_mixins.scss */ +div .container .container { + width: 10px; + max-width: 12px; } +/* line 21, inputs/nested_mixins.scss */ +div .container .foo { + width: 10px; } +/* line 24, inputs/nested_mixins.scss */ +div .container .bar { + width: 12px; } From 3e1b715325f85cf357c18c0c66856ec117b19dd8 Mon Sep 17 00:00:00 2001 From: Frederic Massart Date: Fri, 22 Jul 2016 15:19:21 +0800 Subject: [PATCH 212/484] Nesting with immediate relationship combinators --- src/Compiler.php | 57 +++++++++++++++++++--- tests/inputs/extends.scss | 18 +++++++ tests/inputs/extends_nesting.scss | 44 +++++++++++++++++ tests/outputs/extends.css | 3 ++ tests/outputs/extends_nesting.css | 28 +++++++++++ tests/outputs_numbered/extends.css | 10 ++++ tests/outputs_numbered/extends_nesting.css | 37 ++++++++++++++ 7 files changed, 190 insertions(+), 7 deletions(-) create mode 100644 tests/inputs/extends_nesting.scss create mode 100644 tests/outputs/extends_nesting.css create mode 100644 tests/outputs_numbered/extends_nesting.css diff --git a/src/Compiler.php b/src/Compiler.php index 485e0fd4..c1c14617 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -397,23 +397,46 @@ protected function matchExtends($selector, &$out, $from = 0, $initial = true) } if ($this->matchExtendsSingle($part, $origin)) { - $before = array_slice($selector, 0, $i); + $after = array_slice($selector, $i + 1); - $s = count($before); + $j = $i; + $s = $i; + do { + $nonBreakableBefore = $j != $i ? array_slice($selector, $j, $i - $j) : []; + $before = array_slice($selector, 0, $j); + $slice = end($before); + $hasImmediateRelationshipCombinator = !empty($slice) && $this->isImmediateRelationshipCombinator($slice[0]); + if ($hasImmediateRelationshipCombinator) { + $j -= 2; + } + } while ($hasImmediateRelationshipCombinator); foreach ($origin as $new) { $k = 0; // remove shared parts if ($initial) { - while ($k < $s && isset($new[$k]) && $before[$k] === $new[$k]) { + while ($k < $s && isset($new[$k]) && $selector[$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) : []; + $result = array_merge( $before, - $k > 0 ? array_slice($new, $k) : $new, + $afterBefore, + $nonBreakableBefore, + $replacement, $after ); @@ -424,14 +447,22 @@ protected function matchExtends($selector, &$out, $from = 0, $initial = true) $out[] = $result; // recursively check for more matches - $this->matchExtends($result, $out, $i, false); + $this->matchExtends($result, $out, count($before) + count($afterBefore) + count($nonBreakableBefore), false); // selector sequence merging if (! empty($before) && count($new) > 1) { + $slice = !empty($afterBefore) ? end($afterBefore) : null; + + if ($slice && $this->isImmediateRelationshipCombinator(end($slice))) { + continue; + } + $result2 = array_merge( - array_slice($new, 0, -1), + $k > 0 ? array_slice($before, 0, $k) : [], + $afterBefore, $k > 0 ? array_slice($before, $k) : $before, - array_slice($new, -1), + $nonBreakableBefore, + $replacement, $after ); @@ -1808,6 +1839,18 @@ protected function isTruthy($value) 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 * diff --git a/tests/inputs/extends.scss b/tests/inputs/extends.scss index 467d3624..dbff56fc 100644 --- a/tests/inputs/extends.scss +++ b/tests/inputs/extends.scss @@ -305,3 +305,21 @@ $color-base: white; @extend %pagination-bullet; } } + +@mixin nested-foo() { + .in-nested-foo { + @content; + } +} + +.parent-nested-foo-include { + @include nested-foo() { + .child-nested-foo-include { + color: green; + } + } +} + +.beard + .mustache { + @extend .child-nested-foo-include; +} diff --git a/tests/inputs/extends_nesting.scss b/tests/inputs/extends_nesting.scss new file mode 100644 index 00000000..0cae18dc --- /dev/null +++ b/tests/inputs/extends_nesting.scss @@ -0,0 +1,44 @@ +.btn { + padding: 1px; +} +.btn-lg { + font-size: 10px; +} +.dropup .btn-lg .caret { + border-width: 100px; +} + +.dropup .btn { + content: "dropup btn"; + .caret { + content: "dropup btn caret"; + } +} +.dropup > .btn { + content: "dropup > btn"; + > .caret { + content: "dropup > btn > caret"; + } +} +.dropup + .btn { + content: "dropup + btn"; + + .caret { + content: "dropup + btn + caret"; + } +} +.dropup.btn { + content: "dropupbtn"; + &.caret { + content: "dropupbtncaret"; + } +} + +.btn-group-lg > .btn { + @extend .btn-lg; +} + +.extend { + .the-button { + @extend .btn; + } +} diff --git a/tests/outputs/extends.css b/tests/outputs/extends.css index 8e2916a3..7ced1cad 100644 --- a/tests/outputs/extends.css +++ b/tests/outputs/extends.css @@ -138,3 +138,6 @@ body .to-extend, body .test { margin-left: 0; } .pagination-bullet.is-active { background-color: white; } + +.parent-nested-foo-include .in-nested-foo .child-nested-foo-include, .parent-nested-foo-include .in-nested-foo .beard + .mustache { + color: green; } diff --git a/tests/outputs/extends_nesting.css b/tests/outputs/extends_nesting.css new file mode 100644 index 00000000..42329481 --- /dev/null +++ b/tests/outputs/extends_nesting.css @@ -0,0 +1,28 @@ +.btn, .extend .the-button { + padding: 1px; } + +.btn-lg, .btn-group-lg > .btn, .extend .btn-group-lg > .the-button { + font-size: 10px; } + +.dropup .btn-lg .caret, .dropup .btn-group-lg > .btn .caret, .dropup .extend .btn-group-lg > .the-button .caret, .extend .dropup .btn-group-lg > .the-button .caret { + border-width: 100px; } + +.dropup .btn, .dropup .extend .the-button, .extend .dropup .the-button { + content: "dropup btn"; } + .dropup .btn .caret, .dropup .extend .the-button .caret, .extend .dropup .the-button .caret { + content: "dropup btn caret"; } + +.dropup > .btn, .extend .dropup > .the-button { + content: "dropup > btn"; } + .dropup > .btn > .caret, .extend .dropup > .the-button > .caret { + content: "dropup > btn > caret"; } + +.dropup + .btn, .extend .dropup + .the-button { + content: "dropup + btn"; } + .dropup + .btn + .caret, .extend .dropup + .the-button + .caret { + content: "dropup + btn + caret"; } + +.dropup.btn, .extend .the-button.dropup { + content: "dropupbtn"; } + .dropup.btn.caret, .extend .the-button.dropup.caret { + content: "dropupbtncaret"; } diff --git a/tests/outputs_numbered/extends.css b/tests/outputs_numbered/extends.css index e32f01b7..51297cb4 100644 --- a/tests/outputs_numbered/extends.css +++ b/tests/outputs_numbered/extends.css @@ -215,3 +215,13 @@ body .to-extend, body .test { background-color: white; } /* line 303, inputs/extends.scss */ /* line 304, inputs/extends.scss */ + +/* line 315, inputs/extends.scss */ + +/* line 310, inputs/extends.scss */ + +/* line 317, inputs/extends.scss */ + +.parent-nested-foo-include .in-nested-foo .child-nested-foo-include, .parent-nested-foo-include .in-nested-foo .beard + .mustache { + color: green; } +/* line 323, inputs/extends.scss */ diff --git a/tests/outputs_numbered/extends_nesting.css b/tests/outputs_numbered/extends_nesting.css new file mode 100644 index 00000000..08c3027d --- /dev/null +++ b/tests/outputs_numbered/extends_nesting.css @@ -0,0 +1,37 @@ +/* line 1, inputs/extends_nesting.scss */ +.btn, .extend .the-button { + padding: 1px; } +/* line 4, inputs/extends_nesting.scss */ +.btn-lg, .btn-group-lg > .btn, .extend .btn-group-lg > .the-button { + font-size: 10px; } +/* line 7, inputs/extends_nesting.scss */ +.dropup .btn-lg .caret, .dropup .btn-group-lg > .btn .caret, .dropup .extend .btn-group-lg > .the-button .caret, .extend .dropup .btn-group-lg > .the-button .caret { + border-width: 100px; } +/* line 11, inputs/extends_nesting.scss */ +.dropup .btn, .dropup .extend .the-button, .extend .dropup .the-button { + content: "dropup btn"; } +/* line 13, inputs/extends_nesting.scss */ +.dropup .btn .caret, .dropup .extend .the-button .caret, .extend .dropup .the-button .caret { + content: "dropup btn caret"; } +/* line 17, inputs/extends_nesting.scss */ +.dropup > .btn, .extend .dropup > .the-button { + content: "dropup > btn"; } +/* line 19, inputs/extends_nesting.scss */ +.dropup > .btn > .caret, .extend .dropup > .the-button > .caret { + content: "dropup > btn > caret"; } +/* line 23, inputs/extends_nesting.scss */ +.dropup + .btn, .extend .dropup + .the-button { + content: "dropup + btn"; } +/* line 25, inputs/extends_nesting.scss */ +.dropup + .btn + .caret, .extend .dropup + .the-button + .caret { + content: "dropup + btn + caret"; } +/* line 29, inputs/extends_nesting.scss */ +.dropup.btn, .extend .the-button.dropup { + content: "dropupbtn"; } +/* line 31, inputs/extends_nesting.scss */ +.dropup.btn.caret, .extend .the-button.dropup.caret { + content: "dropupbtncaret"; } +/* line 36, inputs/extends_nesting.scss */ +/* line 40, inputs/extends_nesting.scss */ + +/* line 41, inputs/extends_nesting.scss */ From 62536ec1b32b63499684a91d67e8b6d90508bad2 Mon Sep 17 00:00:00 2001 From: Frederic Massart Date: Mon, 25 Jul 2016 19:12:10 +0800 Subject: [PATCH 213/484] Shared direct relationship are merged on extend --- src/Compiler.php | 96 +++++++++++++++++++++++------- tests/inputs/extends.scss | 20 +++++++ tests/outputs/extends.css | 10 ++++ tests/outputs_numbered/extends.css | 15 +++++ 4 files changed, 119 insertions(+), 22 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index c1c14617..d9dcc6e8 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -399,24 +399,15 @@ protected function matchExtends($selector, &$out, $from = 0, $initial = true) if ($this->matchExtendsSingle($part, $origin)) { $after = array_slice($selector, $i + 1); - $j = $i; - $s = $i; - do { - $nonBreakableBefore = $j != $i ? array_slice($selector, $j, $i - $j) : []; - $before = array_slice($selector, 0, $j); - $slice = end($before); - $hasImmediateRelationshipCombinator = !empty($slice) && $this->isImmediateRelationshipCombinator($slice[0]); - if ($hasImmediateRelationshipCombinator) { - $j -= 2; - } - } while ($hasImmediateRelationshipCombinator); + $before = array_slice($selector, 0, $i); + list($before, $nonBreakableBefore) = $this->extractRelationshipFromFragment($before); foreach ($origin as $new) { $k = 0; // remove shared parts if ($initial) { - while ($k < $s && isset($new[$k]) && $selector[$k] === $new[$k]) { + while ($k < $i && isset($new[$k]) && $selector[$k] === $new[$k]) { $k++; } } @@ -432,10 +423,12 @@ protected function matchExtends($selector, &$out, $from = 0, $initial = true) } $afterBefore = $l != 0 ? array_slice($tempReplacement, 0, $l) : []; + // Merge shared direct relationships. + $mergedBefore = $this->mergeDirectRelationships($afterBefore, $nonBreakableBefore); + $result = array_merge( $before, - $afterBefore, - $nonBreakableBefore, + $mergedBefore, $replacement, $after ); @@ -447,20 +440,20 @@ protected function matchExtends($selector, &$out, $from = 0, $initial = true) $out[] = $result; // recursively check for more matches - $this->matchExtends($result, $out, count($before) + count($afterBefore) + count($nonBreakableBefore), false); + $this->matchExtends($result, $out, count($before) + count($mergedBefore), false); // selector sequence merging if (! empty($before) && count($new) > 1) { - $slice = !empty($afterBefore) ? end($afterBefore) : null; - if ($slice && $this->isImmediateRelationshipCombinator(end($slice))) { - continue; - } + $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( - $k > 0 ? array_slice($before, 0, $k) : [], - $afterBefore, - $k > 0 ? array_slice($before, $k) : $before, + $sharedParts, + $injectBetweenSharedParts, + $postSharedParts, + $nonBreakable2, $nonBreakableBefore, $replacement, $after @@ -543,6 +536,37 @@ protected function matchExtendsSingle($rawSingle, &$outOrigin) 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); + + do { + $children = $j != $i ? array_slice($fragment, $j, $i - $j) : []; + $parents = array_slice($fragment, 0, $j); + $slice = end($parents); + $hasImmediateRelationshipCombinator = !empty($slice) && $this->isImmediateRelationshipCombinator($slice[0]); + if ($hasImmediateRelationshipCombinator) { + $j -= 2; + } + } while ($hasImmediateRelationshipCombinator); + + return [$parents, $children]; + } + /** * Combine selector single * @@ -1315,6 +1339,34 @@ protected function compileMediaQuery($queryList) 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) { + array_unshift($merged, $part1); + array_unshift($merged, [array_pop($selectors1)[0] . array_pop($selectors2)[0]]); + } else { + $merged = array_merge($selectors1, [$part1], $selectors2, [$part2], $merged); + break; + } + } while (!empty($selectors1) && !empty($selectors2)); + + return $merged; + } + /** * Merge media types * diff --git a/tests/inputs/extends.scss b/tests/inputs/extends.scss index dbff56fc..1024c93d 100644 --- a/tests/inputs/extends.scss +++ b/tests/inputs/extends.scss @@ -323,3 +323,23 @@ $color-base: white; .beard + .mustache { @extend .child-nested-foo-include; } + +// Shared direct relationship combinator. +.btn-group-lg > .btn { + @extend .btn-lg; +} +.btn-group > .btn-lg { + padding-right: 12px; + padding-left: 12px; +} + +// Reverse rules with direct relationship combinators. +.fp-content-center form + div { + @extend .form-group; + padding-left: 0; +} +.form-inline + .a { + .form-group > b { + margin-bottom: 0; + } +} diff --git a/tests/outputs/extends.css b/tests/outputs/extends.css index 7ced1cad..bc953bb8 100644 --- a/tests/outputs/extends.css +++ b/tests/outputs/extends.css @@ -141,3 +141,13 @@ body .to-extend, body .test { .parent-nested-foo-include .in-nested-foo .child-nested-foo-include, .parent-nested-foo-include .in-nested-foo .beard + .mustache { color: green; } + +.btn-group > .btn-lg, .btn-group-lg.btn-group > .btn, .edit .actions .btn-group-lg.btn-group > button, .edit .new .actions .btn-group-lg.btn-group > button { + padding-right: 12px; + padding-left: 12px; } + +.fp-content-center form + div { + padding-left: 0; } + +.form-inline + .a .form-group > b, .form-inline + .a .fp-content-center form + div > b, .fp-content-center .form-inline + .a form + div > b { + margin-bottom: 0; } diff --git a/tests/outputs_numbered/extends.css b/tests/outputs_numbered/extends.css index 51297cb4..c64df92a 100644 --- a/tests/outputs_numbered/extends.css +++ b/tests/outputs_numbered/extends.css @@ -225,3 +225,18 @@ body .to-extend, body .test { .parent-nested-foo-include .in-nested-foo .child-nested-foo-include, .parent-nested-foo-include .in-nested-foo .beard + .mustache { color: green; } /* line 323, inputs/extends.scss */ +/* line 328, inputs/extends.scss */ + +/* line 331, inputs/extends.scss */ + +.btn-group > .btn-lg, .btn-group-lg.btn-group > .btn, .edit .actions .btn-group-lg.btn-group > button, .edit .new .actions .btn-group-lg.btn-group > button { + padding-right: 12px; + padding-left: 12px; } +/* line 337, inputs/extends.scss */ +.fp-content-center form + div { + padding-left: 0; } +/* line 341, inputs/extends.scss */ +/* line 342, inputs/extends.scss */ + +.form-inline + .a .form-group > b, .form-inline + .a .fp-content-center form + div > b, .fp-content-center .form-inline + .a form + div > b { + margin-bottom: 0; } From b7675976a298570c27d875b51043a632fb0358c3 Mon Sep 17 00:00:00 2001 From: Frederic Massart Date: Tue, 26 Jul 2016 18:52:58 +0800 Subject: [PATCH 214/484] Do not extend decorated tags with another tag --- src/Compiler.php | 18 +++++++++++++++-- tests/inputs/extends.scss | 32 ++++++++++++++++++++++++++++++ tests/outputs/extends.css | 12 +++++++++++ tests/outputs_numbered/extends.css | 22 ++++++++++++++++++++ 4 files changed, 82 insertions(+), 2 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index d9dcc6e8..d008a844 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -395,7 +395,6 @@ protected function matchExtends($selector, &$out, $from = 0, $initial = true) if ($i < $from) { continue; } - if ($this->matchExtendsSingle($part, $origin)) { $after = array_slice($selector, $i + 1); @@ -492,6 +491,12 @@ protected function matchExtendsSingle($rawSingle, &$outOrigin) } } + $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) { @@ -521,7 +526,16 @@ protected function matchExtendsSingle($rawSingle, &$outOrigin) return false; } - $combined = $this->combineSelectorSingle(end($new), $rem); + $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; diff --git a/tests/inputs/extends.scss b/tests/inputs/extends.scss index 1024c93d..f9ea2a30 100644 --- a/tests/inputs/extends.scss +++ b/tests/inputs/extends.scss @@ -343,3 +343,35 @@ $color-base: white; margin-bottom: 0; } } + +// Decorated tags cannot be extended by tags of another type. +canvas { + color: yellow; +} +.popup { + color: red; +} +div.popup { + color: blue; +} +.before span.popup .after { + color: green; +} +input { + @extend .popup; +} +img.bar { + @extend .popup; +} +object { + @extend canvas; +} +embed.bar { + @extend canvas; +} +.prepend { + input.foo, + div.foo { + @extend .popup + } +} diff --git a/tests/outputs/extends.css b/tests/outputs/extends.css index bc953bb8..6a8dd855 100644 --- a/tests/outputs/extends.css +++ b/tests/outputs/extends.css @@ -151,3 +151,15 @@ body .to-extend, body .test { .form-inline + .a .form-group > b, .form-inline + .a .fp-content-center form + div > b, .fp-content-center .form-inline + .a form + div > b { margin-bottom: 0; } + +canvas, object, embed.bar { + color: yellow; } + +.popup, input, img.bar, .prepend input.foo, .prepend div.foo { + color: red; } + +div.popup, .prepend div.foo { + color: blue; } + +.before span.popup .after { + color: green; } diff --git a/tests/outputs_numbered/extends.css b/tests/outputs_numbered/extends.css index c64df92a..aa4c20ac 100644 --- a/tests/outputs_numbered/extends.css +++ b/tests/outputs_numbered/extends.css @@ -240,3 +240,25 @@ body .to-extend, body .test { .form-inline + .a .form-group > b, .form-inline + .a .fp-content-center form + div > b, .fp-content-center .form-inline + .a form + div > b { margin-bottom: 0; } +/* line 348, inputs/extends.scss */ +canvas, object, embed.bar { + color: yellow; } +/* line 351, inputs/extends.scss */ +.popup, input, img.bar, .prepend input.foo, .prepend div.foo { + color: red; } +/* line 354, inputs/extends.scss */ +div.popup, .prepend div.foo { + color: blue; } +/* line 357, inputs/extends.scss */ +.before span.popup .after { + color: green; } +/* line 360, inputs/extends.scss */ +/* line 363, inputs/extends.scss */ + +/* line 366, inputs/extends.scss */ + +/* line 369, inputs/extends.scss */ + +/* line 372, inputs/extends.scss */ + +/* line 373, inputs/extends.scss */ From f7c9a494de90ce13db84fb90cca13bf24dc60eb3 Mon Sep 17 00:00:00 2001 From: Dan Diemer Date: Tue, 23 Aug 2016 11:06:04 -0400 Subject: [PATCH 215/484] =?UTF-8?q?Let=20@content=20work=20when=20a=20bloc?= =?UTF-8?q?k=20isn=E2=80=99t=20passed=20in.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Compiler.php | 4 +++- tests/inputs/content.scss | 9 +++++++++ tests/outputs/content.css | 4 ++++ tests/outputs_numbered/content.css | 4 ++++ 4 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/Compiler.php b/src/Compiler.php index 7e93fcb0..07efa95a 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -1723,7 +1723,9 @@ protected function compileChild($child, OutputBlock $out) ?: $this->get(self::$namespaces['special'] . 'content', false, $this->env); if (! $content) { - $this->throwError('Expected @content inside of mixin'); + $content = new \stdClass(); + $content->scope = new \stdClass(); + $content->children = $this->storeEnv->parent->block->children; break; } diff --git a/tests/inputs/content.scss b/tests/inputs/content.scss index a9c00c46..0a16ae2f 100644 --- a/tests/inputs/content.scss +++ b/tests/inputs/content.scss @@ -116,3 +116,12 @@ A { display: none; } } + +@mixin empty_content() { + @content; +} +.test_empty_content { + display: inline; + font-weight: bold; + @include empty_content() +} diff --git a/tests/outputs/content.css b/tests/outputs/content.css index 6a8ba90b..6b24aca3 100644 --- a/tests/outputs/content.css +++ b/tests/outputs/content.css @@ -36,3 +36,7 @@ A { .test { display: none; } + +.test_empty_content { + display: inline; + font-weight: bold; } diff --git a/tests/outputs_numbered/content.css b/tests/outputs_numbered/content.css index a0bf6e1e..9d49e316 100644 --- a/tests/outputs_numbered/content.css +++ b/tests/outputs_numbered/content.css @@ -45,3 +45,7 @@ A { /* line 114, inputs/content.scss */ .test { display: none; } +/* line 123, inputs/content.scss */ +.test_empty_content { + display: inline; + font-weight: bold; } From e82bdfb9a41778288871a3f11cbffff6a304e6ce Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sat, 10 Sep 2016 21:23:35 -0400 Subject: [PATCH 216/484] coding style tweaks --- src/Compiler.php | 52 +++++++++++++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 01442e18..592f53b9 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -395,10 +395,11 @@ protected function matchExtends($selector, &$out, $from = 0, $initial = true) if ($i < $from) { continue; } - if ($this->matchExtendsSingle($part, $origin)) { + if ($this->matchExtendsSingle($part, $origin)) { $after = array_slice($selector, $i + 1); $before = array_slice($selector, 0, $i); + list($before, $nonBreakableBefore) = $this->extractRelationshipFromFragment($before); foreach ($origin as $new) { @@ -413,13 +414,16 @@ protected function matchExtends($selector, &$out, $from = 0, $initial = true) $replacement = []; $tempReplacement = $k > 0 ? array_slice($new, $k) : $new; - for ($l = count($tempReplacement) - 1; $l >= 0 ; $l--) { + + for ($l = count($tempReplacement) - 1; $l >= 0; $l--) { $slice = $tempReplacement[$l]; array_unshift($replacement, $slice); - if (!$this->isImmediateRelationshipCombinator(end($slice))) { + + if (! $this->isImmediateRelationshipCombinator(end($slice))) { break; } } + $afterBefore = $l != 0 ? array_slice($tempReplacement, 0, $l) : []; // Merge shared direct relationships. @@ -443,9 +447,9 @@ protected function matchExtends($selector, &$out, $from = 0, $initial = true) // 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( @@ -492,6 +496,7 @@ protected function matchExtendsSingle($rawSingle, &$outOrigin) } $extendingDecoratedTag = false; + if (count($single) > 1) { $matches = null; $extendingDecoratedTag = preg_match('/^[a-z0-9]+$/i', $single[0], $matches) ? $matches[0] : false; @@ -529,8 +534,9 @@ protected function matchExtendsSingle($rawSingle, &$outOrigin) $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])) { + if ($extendingDecoratedTag && $replacement[0] != $extendingDecoratedTag && + preg_match('/^[a-z0-9]+$/i', $replacement[0]) + ) { unset($origin[$j]); continue; } @@ -568,15 +574,17 @@ protected function extractRelationshipFromFragment(array $fragment) $children = []; $j = $i = count($fragment); - do { + for (;;) { $children = $j != $i ? array_slice($fragment, $j, $i - $j) : []; $parents = array_slice($fragment, 0, $j); $slice = end($parents); - $hasImmediateRelationshipCombinator = !empty($slice) && $this->isImmediateRelationshipCombinator($slice[0]); - if ($hasImmediateRelationshipCombinator) { - $j -= 2; + + if (empty($slice) || ! $this->isImmediateRelationshipCombinator($slice[0])) { + break; } - } while ($hasImmediateRelationshipCombinator); + + $j -= 2; + } return [$parents, $children]; } @@ -1353,30 +1361,33 @@ protected function compileMediaQuery($queryList) return $out; } - protected function mergeDirectRelationships($selectors1, $selectors2) { + 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) { + + 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) { - array_unshift($merged, $part1); - array_unshift($merged, [array_pop($selectors1)[0] . array_pop($selectors2)[0]]); - } else { + if ($this->isImmediateRelationshipCombinator($part1[0]) && $part1 !== $part2) { $merged = array_merge($selectors1, [$part1], $selectors2, [$part2], $merged); break; } - } while (!empty($selectors1) && !empty($selectors2)); + + array_unshift($merged, $part1); + array_unshift($merged, [array_pop($selectors1)[0] . array_pop($selectors2)[0]]); + } while (! empty($selectors1) && ! empty($selectors2)); return $merged; } @@ -1720,7 +1731,7 @@ protected function compileChild($child, OutputBlock $out) $end = $end[1]; $d = $start < $end ? 1 : -1; - while (true) { + for (;;) { if ((! $for->until && $start - $d == $end) || ($for->until && $start == $end) ) { @@ -3063,13 +3074,14 @@ public function get($name, $shouldThrow = true, Environment $env = null) $nextIsRoot = false; $hasNamespace = $normalizedName[0] === '^' || $normalizedName[0] === '@' || $normalizedName[0] === '%'; + for (;;) { if (array_key_exists($normalizedName, $env->store)) { return $env->store[$normalizedName]; } if (! $hasNamespace && isset($env->marker)) { - if (! $nextIsRoot && !empty($env->store[$specialContentKey])) { + if (! $nextIsRoot && ! empty($env->store[$specialContentKey])) { $env = $env->store[$specialContentKey]->scope; $nextIsRoot = true; continue; From 6fdfe19d2b13a3f12ba0792227f0718809ce4e4d Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sat, 10 Sep 2016 21:34:11 -0400 Subject: [PATCH 217/484] bump version --- src/Version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Version.php b/src/Version.php index 3be9cc31..80cdeee5 100644 --- a/src/Version.php +++ b/src/Version.php @@ -18,5 +18,5 @@ */ class Version { - const VERSION = 'v0.6.5'; + const VERSION = 'v0.6.6'; } From 8533a50c10f511ad63d03664d17dedd4cce29859 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sun, 11 Sep 2016 00:15:21 -0400 Subject: [PATCH 218/484] v0.6.6 fixes #199 --- tests/FailingTest.php | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/tests/FailingTest.php b/tests/FailingTest.php index 3cdf0a04..86865049 100644 --- a/tests/FailingTest.php +++ b/tests/FailingTest.php @@ -84,32 +84,6 @@ public function provideFailing() background: #eee; } .nav-bar > .item, header ul > .item, header ul > li { margin: 0 10px; } -END_OF_EXPECTED - ), - array( - '#199 - issue with selectors', <<<'END_OF_SCSS' -.abc { - color: #ddd; -} - -a.abc:hover { - text-decoration: underline; -} - -small { - @extend .abc; - font-weight: italic; -} -END_OF_SCSS - , << Date: Fri, 4 Nov 2016 14:37:08 -0400 Subject: [PATCH 219/484] fixes #471 - list interpolation --- src/Compiler.php | 33 ++++++++++++++++++++++++ tests/inputs/interpolation.scss | 6 +++++ tests/outputs/interpolation.css | 3 +++ tests/outputs_numbered/interpolation.css | 3 +++ 4 files changed, 45 insertions(+) diff --git a/src/Compiler.php b/src/Compiler.php index 592f53b9..f0eaff78 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -2723,6 +2723,39 @@ public function compileValue($value) $reduced = $this->reduce($exp); switch ($reduced[0]) { + case Type::T_LIST: + $reduced = $this->extractInterpolation($reduced); + + if ($reduced[0] !== Type::T_LIST) { + break; + } + + list(, $delim, $items) = $reduced; + + if ($delim !== ' ') { + $delim .= ' '; + } + + $filtered = []; + + foreach ($items as $item) { + if ($item[0] === Type::T_NULL) { + continue; + } + + $temp = $this->compileValue([Type::T_KEYWORD, $item]); + if ($temp[0] === Type::T_STRING) { + $filtered[] = $this->compileStringContent($temp); + } elseif ($temp[0] === Type::T_KEYWORD) { + $filtered[] = $temp[1]; + } else { + $filtered[] = $this->compileValue($temp); + } + } + + $reduced = [Type::T_KEYWORD, implode("$delim", $filtered)]; + break; + case Type::T_STRING: $reduced = [Type::T_KEYWORD, $this->compileStringContent($reduced)]; break; diff --git a/tests/inputs/interpolation.scss b/tests/inputs/interpolation.scss index e4df9c84..e3736ca5 100644 --- a/tests/inputs/interpolation.scss +++ b/tests/inputs/interpolation.scss @@ -91,3 +91,9 @@ body{ foo, #{x, y} { color: #abc; } + +// interpolate lists +$foo: ('a', 'b'); +div { + prop: #{$foo}; +} diff --git a/tests/outputs/interpolation.css b/tests/outputs/interpolation.css index ebe8463d..d3084575 100644 --- a/tests/outputs/interpolation.css +++ b/tests/outputs/interpolation.css @@ -58,3 +58,6 @@ body { foo, x, y { color: #abc; } + +div { + prop: a, b; } diff --git a/tests/outputs_numbered/interpolation.css b/tests/outputs_numbered/interpolation.css index 64ef9ebc..f6d5f5fa 100644 --- a/tests/outputs_numbered/interpolation.css +++ b/tests/outputs_numbered/interpolation.css @@ -68,3 +68,6 @@ body { /* line 91, inputs/interpolation.scss */ foo, x, y { color: #abc; } +/* line 97, inputs/interpolation.scss */ +div { + prop: a, b; } From 10c650529b5c2f72094cc885eca327ead519e8c6 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 8 Nov 2016 13:17:17 -0500 Subject: [PATCH 220/484] fixes #476 - pscss: enable --line-numbers and --debug-info for stdin --- bin/pscss | 4 ++-- src/Compiler.php | 9 ++++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/bin/pscss b/bin/pscss index 3175ea0b..4ec061b8 100755 --- a/bin/pscss +++ b/bin/pscss @@ -169,11 +169,11 @@ if ($dumpTree) { $scss = new Compiler(); -if ($debugInfo && $inputFile) { +if ($debugInfo) { $scss->setLineNumberStyle(Compiler::DEBUG_INFO); } -if ($lineNumbers && $inputFile) { +if ($lineNumbers) { $scss->setLineNumberStyle(Compiler::LINE_COMMENTS); } diff --git a/src/Compiler.php b/src/Compiler.php index f0eaff78..fdb68240 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -1015,12 +1015,15 @@ protected function compileBlock(Block $block) switch ($this->lineNumberStyle) { case self::LINE_COMMENTS: - $annotation->lines[] = '/* line ' . $line . ', ' . $file . ' */'; + $annotation->lines[] = '/* line ' . $line + . ($file ? ', ' . $file : '') + . ' */'; break; case self::DEBUG_INFO: - $annotation->lines[] = '@media -sass-debug-info{filename{font-family:"' . $file - . '"}line{font-family:' . $line . '}}'; + $annotation->lines[] = '@media -sass-debug-info{' + . ($file ? 'filename{font-family:"' . $file . '"}' : '') + . 'line{font-family:' . $line . '}}'; break; } From f3bb6b7de4a11c10ad6f5e0e922b9197e962e404 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 22 Nov 2016 15:21:33 -0500 Subject: [PATCH 221/484] Change self:: to static:: where possible --- src/Compiler.php | 98 ++++++++++++++++++++++----------------------- src/Node/Number.php | 10 ++--- src/Parser.php | 38 +++++++++--------- src/Server.php | 2 +- 4 files changed, 74 insertions(+), 74 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index fdb68240..df322273 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -729,7 +729,7 @@ protected function compileAtRoot(Block $block) { $env = $this->pushEnv($block); $envs = $this->compactEnv($env); - $without = isset($block->with) ? $this->compileWith($block->with) : self::WITH_RULE; + $without = isset($block->with) ? $this->compileWith($block->with) : static::WITH_RULE; // wrap inline selector if ($block->selector) { @@ -854,12 +854,12 @@ private function compileWith($with) ]; // exclude selectors by default - $without = self::WITH_RULE; + $without = static::WITH_RULE; - if ($this->libMapHasKey([$with, self::$with])) { - $without = self::WITH_ALL; + if ($this->libMapHasKey([$with, static::$with])) { + $without = static::WITH_ALL; - $list = $this->coerceList($this->libMapGet([$with, self::$with])); + $list = $this->coerceList($this->libMapGet([$with, static::$with])); foreach ($list[2] as $item) { $keyword = $this->compileStringContent($this->coerceString($item)); @@ -870,10 +870,10 @@ private function compileWith($with) } } - if ($this->libMapHasKey([$with, self::$without])) { + if ($this->libMapHasKey([$with, static::$without])) { $without = 0; - $list = $this->coerceList($this->libMapGet([$with, self::$without])); + $list = $this->coerceList($this->libMapGet([$with, static::$without])); foreach ($list[2] as $item) { $keyword = $this->compileStringContent($this->coerceString($item)); @@ -920,10 +920,10 @@ private function filterWithout($envs, $without) */ private function isWithout($without, Block $block) { - if ((($without & self::WITH_RULE) && isset($block->selectors)) || - (($without & self::WITH_MEDIA) && + if ((($without & static::WITH_RULE) && isset($block->selectors)) || + (($without & static::WITH_MEDIA) && isset($block->type) && $block->type === Type::T_MEDIA) || - (($without & self::WITH_SUPPORTS) && + (($without & static::WITH_SUPPORTS) && isset($block->type) && $block->type === Type::T_DIRECTIVE && isset($block->name) && $block->name === 'supports') ) { @@ -1014,13 +1014,13 @@ protected function compileBlock(Block $block) $line = $block->sourceLine; switch ($this->lineNumberStyle) { - case self::LINE_COMMENTS: + case static::LINE_COMMENTS: $annotation->lines[] = '/* line ' . $line . ($file ? ', ' . $file : '') . ' */'; break; - case self::DEBUG_INFO: + case static::DEBUG_INFO: $annotation->lines[] = '@media -sass-debug-info{' . ($file ? 'filename{font-family:"' . $file . '"}' : '') . 'line{font-family:' . $line . '}}'; @@ -1583,7 +1583,7 @@ protected function compileChild($child, OutputBlock $out) $shouldSet = $isDefault && (($result = $this->get($name[1], false)) === null - || $result === self::$null); + || $result === static::$null); if (! $isDefault || $shouldSet) { $this->set($name[1], $this->reduce($value)); @@ -1611,7 +1611,7 @@ protected function compileChild($child, OutputBlock $out) if ($value[0] !== Type::T_NULL) { $value = $this->reduce($value); - if ($value[0] === Type::T_NULL || $value === self::$nullString) { + if ($value[0] === Type::T_NULL || $value === static::$nullString) { break; } } @@ -1637,7 +1637,7 @@ protected function compileChild($child, OutputBlock $out) case Type::T_FUNCTION: list(, $block) = $child; - $this->set(self::$namespaces[$block->type] . $block->name, $block); + $this->set(static::$namespaces[$block->type] . $block->name, $block); break; case Type::T_EXTEND: @@ -1685,7 +1685,7 @@ protected function compileChild($child, OutputBlock $out) list(,, $values) = $this->coerceList($item); foreach ($each->vars as $i => $var) { - $this->set($var, isset($values[$i]) ? $values[$i] : self::$null, true); + $this->set($var, isset($values[$i]) ? $values[$i] : static::$null, true); } } @@ -1794,7 +1794,7 @@ protected function compileChild($child, OutputBlock $out) // including a mixin list(, $name, $argValues, $content) = $child; - $mixin = $this->get(self::$namespaces['mixin'] . $name, false); + $mixin = $this->get(static::$namespaces['mixin'] . $name, false); if (! $mixin) { $this->throwError("Undefined mixin $name"); @@ -1813,7 +1813,7 @@ protected function compileChild($child, OutputBlock $out) if (isset($content)) { $content->scope = $callingScope; - $this->setRaw(self::$namespaces['special'] . 'content', $content, $this->env); + $this->setRaw(static::$namespaces['special'] . 'content', $content, $this->env); } if (isset($mixin->args)) { @@ -1830,8 +1830,8 @@ protected function compileChild($child, OutputBlock $out) 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); + $content = $this->get(static::$namespaces['special'] . 'content', false, $this->getStoreEnv()) + ?: $this->get(static::$namespaces['special'] . 'content', false, $this->env); if (! $content) { $content = new \stdClass(); @@ -1918,7 +1918,7 @@ protected function expToString($exp) */ protected function isTruthy($value) { - return $value !== self::$false && $value !== self::$null; + return $value !== static::$false && $value !== static::$null; } /** @@ -1973,7 +1973,7 @@ protected function reduce($value, $inExp = false) case Type::T_EXPRESSION: list(, $op, $left, $right, $inParens) = $value; - $opName = isset(self::$operatorNames[$op]) ? self::$operatorNames[$op] : $op; + $opName = isset(static::$operatorNames[$op]) ? static::$operatorNames[$op] : $op; $inExp = $inExp || $this->shouldEval($left) || $this->shouldEval($right); $left = $this->reduce($left, true); @@ -2089,11 +2089,11 @@ protected function reduce($value, $inExp = false) if ($op === 'not') { if ($inExp || $inParens) { - if ($exp === self::$false || $exp === self::$null) { - return self::$true; + if ($exp === static::$false || $exp === static::$null) { + return static::$true; } - return self::$false; + return static::$false; } $op = $op . ' '; @@ -2347,7 +2347,7 @@ protected function opAnd($left, $right, $shouldEval) return; } - if ($left !== self::$false and $left !== self::$null) { + if ($left !== static::$false and $left !== static::$null) { return $this->reduce($right, true); } @@ -2369,7 +2369,7 @@ protected function opOr($left, $right, $shouldEval) return; } - if ($left !== self::$false and $left !== self::$null) { + if ($left !== static::$false and $left !== static::$null) { return $left; } @@ -2600,7 +2600,7 @@ protected function opCmpNumberNumber($left, $right) */ public function toBool($thing) { - return $thing ? self::$true : self::$false; + return $thing ? static::$true : static::$false; } /** @@ -2883,7 +2883,7 @@ protected function joinSelectors($parent, $child) $newPart = []; foreach ($part as $p) { - if ($p === self::$selfSelector) { + if ($p === static::$selfSelector) { $setSelf = true; foreach ($parent as $i => $parentPart) { @@ -3102,7 +3102,7 @@ protected function setRaw($name, $value, Environment $env) public function get($name, $shouldThrow = true, Environment $env = null) { $normalizedName = $this->normalizeName($name); - $specialContentKey = self::$namespaces['special'] . 'content'; + $specialContentKey = static::$namespaces['special'] . 'content'; if (! isset($env)) { $env = $this->getStoreEnv(); @@ -3510,7 +3510,7 @@ protected function fileExists($name) */ protected function callScssFunction($name, $argValues, &$returnValue) { - $func = $this->get(self::$namespaces['function'] . $name, false); + $func = $this->get(static::$namespaces['function'] . $name, false); if (! $func) { return false; @@ -3539,7 +3539,7 @@ protected function callScssFunction($name, $argValues, &$returnValue) $this->popEnv(); - $returnValue = ! isset($ret) ? self::$defaultValue : $ret; + $returnValue = ! isset($ret) ? static::$defaultValue : $ret; return true; } @@ -3563,7 +3563,7 @@ protected function callNativeFunction($name, $args, &$returnValue) list($f, $prototype) = $this->userFunctions[$name]; } elseif (($f = $this->getBuiltinFunction($name)) && is_callable($f)) { $libName = $f[1]; - $prototype = isset(self::$$libName) ? self::$$libName : null; + $prototype = isset(static::$$libName) ? static::$$libName : null; } else { return false; } @@ -3788,7 +3788,7 @@ private function coerceValue($value) } if ($value === null) { - return self::$null; + return static::$null; } if (is_numeric($value)) { @@ -3796,7 +3796,7 @@ private function coerceValue($value) } if ($value === '') { - return self::$emptyString; + return static::$emptyString; } if (preg_match('/^(#([0-9a-f]{6})|#([0-9a-f]{3}))$/i', $value, $m)) { @@ -3838,11 +3838,11 @@ protected function coerceMap($item) return $item; } - if ($item === self::$emptyList) { - return self::$emptyMap; + if ($item === static::$emptyList) { + return static::$emptyMap; } - return [Type::T_MAP, [$item], [self::$null]]; + return [Type::T_MAP, [$item], [static::$null]]; } /** @@ -4212,7 +4212,7 @@ protected function libIndex($args) list($list, $value) = $args; if ($value[0] === Type::T_MAP) { - return self::$null; + return static::$null; } if ($list[0] === Type::T_MAP || @@ -4224,7 +4224,7 @@ protected function libIndex($args) } if ($list[0] !== Type::T_LIST) { - return self::$null; + return static::$null; } $values = []; @@ -4235,7 +4235,7 @@ protected function libIndex($args) $key = array_search($this->normalizeValue($value), $values); - return false === $key ? self::$null : $key + 1; + return false === $key ? static::$null : $key + 1; } protected static $libRgb = ['red', 'green', 'blue']; @@ -4805,7 +4805,7 @@ protected function libNth($args) $n += count($list[2]); } - return isset($list[2][$n]) ? $list[2][$n] : self::$defaultValue; + return isset($list[2][$n]) ? $list[2][$n] : static::$defaultValue; } protected static $libSetNth = ['list', 'n', 'value']; @@ -4843,7 +4843,7 @@ protected function libMapGet($args) } } - return self::$null; + return static::$null; } protected static $libMapKeys = ['map']; @@ -4994,7 +4994,7 @@ protected function libTypeOf($args) switch ($value[0]) { case Type::T_KEYWORD: - if ($value === self::$true || $value === self::$false) { + if ($value === static::$true || $value === static::$false) { return 'bool'; } @@ -5067,7 +5067,7 @@ protected function libStrIndex($args) $result = strpos($stringContent, $substringContent); - return $result === false ? self::$null : new Node\Number($result + 1, ''); + return $result === false ? static::$null : new Node\Number($result + 1, ''); } protected static $libStrInsert = ['string', 'insert', 'index']; @@ -5099,7 +5099,7 @@ protected function libStrLength($args) protected function libStrSlice($args) { if (isset($args[2]) && $args[2][1] == 0) { - return self::$nullString; + return static::$nullString; } $string = $this->coerceString($args[0]); @@ -5161,7 +5161,7 @@ protected function libFunctionExists($args) $name = $this->compileStringContent($string); // user defined functions - if ($this->has(self::$namespaces['function'] . $name)) { + if ($this->has(static::$namespaces['function'] . $name)) { return true; } @@ -5192,7 +5192,7 @@ protected function libMixinExists($args) $string = $this->coerceString($args[0]); $name = $this->compileStringContent($string); - return $this->has(self::$namespaces['mixin'] . $name); + return $this->has(static::$namespaces['mixin'] . $name); } protected static $libVariableExists = ['name']; @@ -5250,7 +5250,7 @@ protected function libUniqueId() protected static $libInspect = ['value']; protected function libInspect($args) { - if ($args[0] === self::$null) { + if ($args[0] === static::$null) { return [Type::T_KEYWORD, 'null']; } diff --git a/src/Node/Number.php b/src/Node/Number.php index a803a6ca..55fec292 100644 --- a/src/Node/Number.php +++ b/src/Node/Number.php @@ -110,7 +110,7 @@ public function coerce($units) $dimension = $this->dimension; - foreach (self::$unitTable['in'] as $unit => $conv) { + foreach (static::$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); @@ -265,7 +265,7 @@ public function unitStr() */ public function output(Compiler $compiler = null) { - $dimension = round($this->dimension, self::$precision); + $dimension = round($this->dimension, static::$precision); $units = array_filter($this->units, function ($unitSize) { return $unitSize; @@ -277,7 +277,7 @@ public function output(Compiler $compiler = null) $this->normalizeUnits($dimension, $units, 'in'); - $dimension = round($dimension, self::$precision); + $dimension = round($dimension, static::$precision); $units = array_filter($units, function ($unitSize) { return $unitSize; }); @@ -316,8 +316,8 @@ private function normalizeUnits(&$dimension, &$units, $baseUnit = 'in') $units = []; foreach ($this->units as $unit => $exp) { - if (isset(self::$unitTable[$baseUnit][$unit])) { - $factor = pow(self::$unitTable[$baseUnit][$unit], $exp); + if (isset(static::$unitTable[$baseUnit][$unit])) { + $factor = pow(static::$unitTable[$baseUnit][$unit], $exp); $unit = $baseUnit; $dimension /= $factor; diff --git a/src/Parser.php b/src/Parser.php index ed0621ea..9f28a591 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -83,17 +83,17 @@ public function __construct($sourceName, $sourceIndex = 0, $encoding = 'utf-8') $this->utf8 = ! $encoding || strtolower($encoding) === 'utf-8'; $this->patternModifiers = $this->utf8 ? 'Aisu' : 'Ais'; - if (empty(self::$operatorPattern)) { - self::$operatorPattern = '([*\/%+-]|[!=]\=|\>\=?|\<\=\>|\<\=?|and|or)'; + if (empty(static::$operatorPattern)) { + static::$operatorPattern = '([*\/%+-]|[!=]\=|\>\=?|\<\=\>|\<\=?|and|or)'; $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'; + static::$commentPattern = $commentMultiLeft . '.*?' . $commentMultiRight; + static::$whitePattern = $this->utf8 + ? '/' . $commentSingle . '[^\n]*\s*|(' . static::$commentPattern . ')\s*|\s+/AisuS' + : '/' . $commentSingle . '[^\n]*\s*|(' . static::$commentPattern . ')\s*|\s+/AisS'; } } @@ -563,9 +563,9 @@ protected function parseChunk() list($line, $column) = $this->getSourcePosition($s); - $statement[self::SOURCE_LINE] = $line; - $statement[self::SOURCE_COLUMN] = $column; - $statement[self::SOURCE_INDEX] = $this->sourceIndex; + $statement[static::SOURCE_LINE] = $line; + $statement[static::SOURCE_COLUMN] = $column; + $statement[static::SOURCE_INDEX] = $this->sourceIndex; $this->charset = $statement; } @@ -922,7 +922,7 @@ protected function whitespace() { $gotWhite = false; - while (preg_match(self::$whitePattern, $this->buffer, $m, null, $this->count)) { + while (preg_match(static::$whitePattern, $this->buffer, $m, null, $this->count)) { if (isset($m[1]) && empty($this->commentsSeen[$this->count])) { $this->appendComment([Type::T_COMMENT, $m[1]]); @@ -959,9 +959,9 @@ 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; + $statement[static::SOURCE_LINE] = $line; + $statement[static::SOURCE_COLUMN] = $column; + $statement[static::SOURCE_INDEX] = $this->sourceIndex; } $this->env->children[] = $statement; @@ -1249,13 +1249,13 @@ protected function expression(&$out) */ protected function expHelper($lhs, $minP) { - $operators = self::$operatorPattern; + $operators = static::$operatorPattern; $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($operators, $m, false) && static::$precedence[$m[1]] >= $minP) { $whiteAfter = isset($this->buffer[$this->count]) && ctype_space($this->buffer[$this->count]); $varAfter = isset($this->buffer[$this->count]) && @@ -1275,8 +1275,8 @@ protected function expHelper($lhs, $minP) } // peek and see if rhs belongs to next operator - if ($this->peek($operators, $next) && self::$precedence[$next[1]] > self::$precedence[$op]) { - $rhs = $this->expHelper($rhs, self::$precedence[$next[1]]); + if ($this->peek($operators, $next) && static::$precedence[$next[1]] > static::$precedence[$op]) { + $rhs = $this->expHelper($rhs, static::$precedence[$next[1]]); } $lhs = [Type::T_EXPRESSION, $op, $lhs, $rhs, $this->inParens, $whiteBefore, $whiteAfter]; @@ -1812,7 +1812,7 @@ protected function openString($end, &$out, $nestingOpen = null) $oldWhite = $this->eatWhiteDefault; $this->eatWhiteDefault = false; - $patt = '(.*?)([\'"]|#\{|' . $this->pregQuote($end) . '|' . self::$commentPattern . ')'; + $patt = '(.*?)([\'"]|#\{|' . $this->pregQuote($end) . '|' . static::$commentPattern . ')'; $nestingLevel = 0; @@ -1946,7 +1946,7 @@ protected function propertyName(&$out) // match comment hack if (preg_match( - self::$whitePattern, + static::$whitePattern, $this->buffer, $m, null, diff --git a/src/Server.php b/src/Server.php index 221655ca..33d7b3de 100644 --- a/src/Server.php +++ b/src/Server.php @@ -457,7 +457,7 @@ public function __construct($dir, $cacheDir = null, $scss = null) */ public static function serveFrom($path) { - $server = new self($path); + $server = new static($path); $server->serve(); } } From bacbd7a0c03fa8af6bdba4fff41ffc13625608fa Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Thu, 5 Jan 2017 12:56:38 -0500 Subject: [PATCH 222/484] fixes #484 --- src/Compiler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compiler.php b/src/Compiler.php index df322273..10fee440 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -3496,7 +3496,7 @@ protected function handleImportLoop($name) */ protected function fileExists($name) { - return is_file($name); + return file_exists($name) && is_file($name); } /** From fe7e36b4250c916bf7e94f1e0ec8384fef75d617 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Thu, 5 Jan 2017 13:02:45 -0500 Subject: [PATCH 223/484] fixes #485 throw RangeException --- src/Exception/RangeException.php | 21 +++++++++++++++++++++ src/Util.php | 5 +++-- 2 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 src/Exception/RangeException.php diff --git a/src/Exception/RangeException.php b/src/Exception/RangeException.php new file mode 100644 index 00000000..22f46f54 --- /dev/null +++ b/src/Exception/RangeException.php @@ -0,0 +1,21 @@ + + */ +class RangeException extends \Exception +{ +} diff --git a/src/Util.php b/src/Util.php index 9f47c1d7..9e968d8a 100644 --- a/src/Util.php +++ b/src/Util.php @@ -12,6 +12,7 @@ namespace Leafo\ScssPhp; use Leafo\ScssPhp\Base\Range; +use Leafo\ScssPhp\Exception\RangeException; /** * Utilties @@ -31,7 +32,7 @@ class Util * * @return mixed `value` adjusted to fall within range, if it was outside by a floating-point margin. * - * @throws \Exception + * @throws \Leafo\ScssPhp\Exception\RangeException */ public static function checkRange($name, Range $range, $value, $unit = '') { @@ -50,6 +51,6 @@ public static function checkRange($name, Range $range, $value, $unit = '') return $range->last; } - throw new \Exception("$name {$val} must be between {$range->first} and {$range->last}$unit"); + throw new RangeException("$name {$val} must be between {$range->first} and {$range->last}$unit"); } } From e7aa77e74f31ab51dd72d95b40b6b5fc2ef46b9f Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Thu, 5 Jan 2017 13:05:38 -0500 Subject: [PATCH 224/484] Bump copyright year --- src/Base/Range.php | 2 +- src/Block.php | 2 +- src/Colors.php | 2 +- src/Compiler.php | 2 +- src/Compiler/Environment.php | 2 +- src/Exception/CompilerException.php | 2 +- src/Exception/ParserException.php | 2 +- src/Exception/RangeException.php | 2 +- src/Exception/ServerException.php | 2 +- src/Formatter.php | 2 +- src/Formatter/Compact.php | 2 +- src/Formatter/Compressed.php | 2 +- src/Formatter/Crunched.php | 2 +- src/Formatter/Debug.php | 2 +- src/Formatter/Expanded.php | 2 +- src/Formatter/Nested.php | 2 +- src/Formatter/OutputBlock.php | 2 +- src/Node.php | 2 +- src/Node/Number.php | 2 +- src/Parser.php | 2 +- src/Server.php | 2 +- src/Type.php | 2 +- src/Util.php | 2 +- src/Version.php | 2 +- 24 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/Base/Range.php b/src/Base/Range.php index a591d7b0..3f3a067d 100644 --- a/src/Base/Range.php +++ b/src/Base/Range.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2015 Leaf Corcoran + * @copyright 2017 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Block.php b/src/Block.php index 6b972a1b..a6b5f3f0 100644 --- a/src/Block.php +++ b/src/Block.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2015 Leaf Corcoran + * @copyright 2012-2017 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Colors.php b/src/Colors.php index ff48c199..61a71ab6 100644 --- a/src/Colors.php +++ b/src/Colors.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2015 Leaf Corcoran + * @copyright 2012-2017 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Compiler.php b/src/Compiler.php index 10fee440..36a0f7cc 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2015 Leaf Corcoran + * @copyright 2012-2017 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Compiler/Environment.php b/src/Compiler/Environment.php index d44bff76..b4b86e32 100644 --- a/src/Compiler/Environment.php +++ b/src/Compiler/Environment.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2015 Leaf Corcoran + * @copyright 2012-2017 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Exception/CompilerException.php b/src/Exception/CompilerException.php index 777e40ac..45fc1671 100644 --- a/src/Exception/CompilerException.php +++ b/src/Exception/CompilerException.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2015 Leaf Corcoran + * @copyright 2012-2017 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Exception/ParserException.php b/src/Exception/ParserException.php index fbe6388f..c0ee002d 100644 --- a/src/Exception/ParserException.php +++ b/src/Exception/ParserException.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2015 Leaf Corcoran + * @copyright 2012-2017 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Exception/RangeException.php b/src/Exception/RangeException.php index 22f46f54..47192ff5 100644 --- a/src/Exception/RangeException.php +++ b/src/Exception/RangeException.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2015 Leaf Corcoran + * @copyright 2012-2017 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Exception/ServerException.php b/src/Exception/ServerException.php index 5a878d2f..68d3a290 100644 --- a/src/Exception/ServerException.php +++ b/src/Exception/ServerException.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2015 Leaf Corcoran + * @copyright 2012-2017 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Formatter.php b/src/Formatter.php index 2770bb2d..0d2635f1 100644 --- a/src/Formatter.php +++ b/src/Formatter.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2015 Leaf Corcoran + * @copyright 2012-2017 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Formatter/Compact.php b/src/Formatter/Compact.php index 94abe329..aaf972b7 100644 --- a/src/Formatter/Compact.php +++ b/src/Formatter/Compact.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2015 Leaf Corcoran + * @copyright 2012-2017 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Formatter/Compressed.php b/src/Formatter/Compressed.php index df7bc75f..17aca54a 100644 --- a/src/Formatter/Compressed.php +++ b/src/Formatter/Compressed.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2015 Leaf Corcoran + * @copyright 2012-2017 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Formatter/Crunched.php b/src/Formatter/Crunched.php index ccba1333..0a89bc48 100644 --- a/src/Formatter/Crunched.php +++ b/src/Formatter/Crunched.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2015 Leaf Corcoran + * @copyright 2012-2017 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Formatter/Debug.php b/src/Formatter/Debug.php index 855742e7..321c5368 100644 --- a/src/Formatter/Debug.php +++ b/src/Formatter/Debug.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2015 Leaf Corcoran + * @copyright 2012-2017 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Formatter/Expanded.php b/src/Formatter/Expanded.php index 54db742f..dff17e6b 100644 --- a/src/Formatter/Expanded.php +++ b/src/Formatter/Expanded.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2015 Leaf Corcoran + * @copyright 2012-2017 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Formatter/Nested.php b/src/Formatter/Nested.php index 9fdb4dd0..c4ff803c 100644 --- a/src/Formatter/Nested.php +++ b/src/Formatter/Nested.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2015 Leaf Corcoran + * @copyright 2012-2017 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Formatter/OutputBlock.php b/src/Formatter/OutputBlock.php index bb8d99b4..88e86402 100644 --- a/src/Formatter/OutputBlock.php +++ b/src/Formatter/OutputBlock.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2015 Leaf Corcoran + * @copyright 2012-2017 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Node.php b/src/Node.php index e6ed178c..eb543c73 100644 --- a/src/Node.php +++ b/src/Node.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2015 Leaf Corcoran + * @copyright 2012-2017 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Node/Number.php b/src/Node/Number.php index 55fec292..d4f406fa 100644 --- a/src/Node/Number.php +++ b/src/Node/Number.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2015 Leaf Corcoran + * @copyright 2012-2017 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Parser.php b/src/Parser.php index 9f28a591..619b7ba8 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2015 Leaf Corcoran + * @copyright 2012-2017 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Server.php b/src/Server.php index 33d7b3de..e4caddb4 100644 --- a/src/Server.php +++ b/src/Server.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2015 Leaf Corcoran + * @copyright 2012-2017 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Type.php b/src/Type.php index 8c3886c8..2d659e4d 100644 --- a/src/Type.php +++ b/src/Type.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2015 Leaf Corcoran + * @copyright 2012-2017 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Util.php b/src/Util.php index 9e968d8a..b2a05db8 100644 --- a/src/Util.php +++ b/src/Util.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2015 Leaf Corcoran + * @copyright 2012-2017 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Version.php b/src/Version.php index 80cdeee5..dff3bd5b 100644 --- a/src/Version.php +++ b/src/Version.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2015 Leaf Corcoran + * @copyright 2012-2017 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * From d395f6f914e77031bbb822d55bc802e85441056e Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 10 Jan 2017 11:45:37 -0500 Subject: [PATCH 225/484] @import - implicit .scss extension when not specified; otherwise limit to explicit .css and .scss extension; mitigates #487 --- src/Compiler.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Compiler.php b/src/Compiler.php index 36a0f7cc..68003b90 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -3390,6 +3390,8 @@ public function findImport($url) $urls = [$url, preg_replace('/[^\/]+$/', '_\0', $url)]; } + $hasExtension = preg_match('/[.]s?css$/', $url); + foreach ($this->importPaths as $dir) { if (is_string($dir)) { // check urls for normal import paths @@ -3399,7 +3401,7 @@ public function findImport($url) . $full; if ($this->fileExists($file = $full . '.scss') || - $this->fileExists($file = $full) + ($hasExtension && $this->fileExists($file = $full)) ) { return $file; } From 562213cd803e42ea53b0735554794c4022d8db89 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Thu, 23 Feb 2017 00:07:33 -0500 Subject: [PATCH 226/484] Bump version --- src/Version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Version.php b/src/Version.php index dff3bd5b..2d6eabc1 100644 --- a/src/Version.php +++ b/src/Version.php @@ -18,5 +18,5 @@ */ class Version { - const VERSION = 'v0.6.6'; + const VERSION = 'v0.6.7'; } From ad205b8b7cdb066283a7cd2fd4b66fe55131cdef Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Thu, 23 Feb 2017 00:32:54 -0500 Subject: [PATCH 227/484] Server::serveFrom() removed --- src/Server.php | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/Server.php b/src/Server.php index e4caddb4..c3fa985d 100644 --- a/src/Server.php +++ b/src/Server.php @@ -449,15 +449,4 @@ public function __construct($dir, $cacheDir = null, $scss = null) date_default_timezone_set('UTC'); } } - - /** - * Helper method to serve compiled scss - * - * @param string $path Root path - */ - public static function serveFrom($path) - { - $server = new static($path); - $server->serve(); - } } From 3dc8ecfbf4900f2cc7b740ab98c8244d232a03d6 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Thu, 23 Feb 2017 00:59:07 -0500 Subject: [PATCH 228/484] Update .travis.yml --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index e5b70fb7..1a28f1ea 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,6 +6,7 @@ php: - 5.5 - 5.6 - 7.0 + - 7.1 - nightly script: From d2cfeb60986784c7c09257486b00f35c49b8c99c Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Thu, 23 Feb 2017 01:05:26 -0500 Subject: [PATCH 229/484] Update travis.yml Disable php nightly (7.2) - broken build --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 1a28f1ea..25e06b3e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,7 +7,7 @@ php: - 5.6 - 7.0 - 7.1 - - nightly +# - nightly script: - phpunit tests From b5db19b0dd98807279cea399917b38487ed4e8ba Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Fri, 24 Feb 2017 20:55:56 -0500 Subject: [PATCH 230/484] Add test for user function that returns null --- tests/ApiTest.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/ApiTest.php b/tests/ApiTest.php index 8d89b588..f39e7f62 100644 --- a/tests/ApiTest.php +++ b/tests/ApiTest.php @@ -38,6 +38,18 @@ public function testUserFunction() ); } + public function testUserFunctionNull() + { + $this->scss->registerFunction('get-null', function ($args) { + return Compiler::$null; + }); + + $this->assertEquals( + '', + $this->compile('result: get-null();') + ); + } + public function testUserFunctionKwargs() { $this->scss->registerFunction( From df07dde450669bc04081a7afb3ee68b8429c37fd Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 14 Mar 2017 07:04:41 -0400 Subject: [PATCH 231/484] Fix travis ci tests? --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 25e06b3e..df8c6adc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,10 +7,10 @@ php: - 5.6 - 7.0 - 7.1 -# - nightly + - nightly script: - - phpunit tests + - vendor/bin/phpunit tests branches: only: From 2b7ed3384a66ac5a69846c3d2e0eb204fb6cef34 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 14 Mar 2017 07:10:22 -0400 Subject: [PATCH 232/484] Travis: run composer install --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index df8c6adc..e0e9a57f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,6 +10,7 @@ php: - nightly script: + - composer install - vendor/bin/phpunit tests branches: From 3814a55a4efe56ac33eb586b97845a8e88093225 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 14 Mar 2017 08:28:16 -0400 Subject: [PATCH 233/484] fixes #505 - remove .phar build --- .gitignore | 2 +- Makefile | 3 --- box.json.dist | 12 ------------ composer.json | 4 +--- 4 files changed, 2 insertions(+), 19 deletions(-) delete mode 100644 box.json.dist diff --git a/.gitignore b/.gitignore index dbff826d..be6e5ecf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ .sass-cache +scss_cache composer.lock -pscss.phar /*.css /*.scss /_site/ diff --git a/Makefile b/Makefile index b42e4a2b..5115941c 100644 --- a/Makefile +++ b/Makefile @@ -6,6 +6,3 @@ compat: standard: vendor/bin/phpcs --standard=PSR2 bin src tests *.php - -phar: - php -dphar.readonly=0 vendor/bin/box build -v diff --git a/box.json.dist b/box.json.dist deleted file mode 100644 index 63d13baf..00000000 --- a/box.json.dist +++ /dev/null @@ -1,12 +0,0 @@ -{ - "chmod": "0755", - "files": [ - "LICENSE.md", - "scss.inc.php" - ], - "directories": ["src"], - "main": "bin/pscss", - "output": "pscss.phar", - "compactors": "Herrera\\Box\\Compactor\\Php", - "stub": true -} diff --git a/composer.json b/composer.json index 112a6bde..95118a32 100644 --- a/composer.json +++ b/composer.json @@ -25,8 +25,7 @@ }, "require-dev": { "squizlabs/php_codesniffer": "~2.5", - "phpunit/phpunit": "~3.7", - "kherge/box": "~2.5" + "phpunit/phpunit": "~4.6" }, "bin": ["bin/pscss"], "archive": { @@ -35,7 +34,6 @@ "/.gitattributes", "/.gitignore", "/.travis.yml", - "/box.json.dist", "/phpunit.xml.dist", "/tests" ] From b6f1ea6baac258aacc3e44cd48df9b4b0dd95ef2 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 14 Mar 2017 09:04:34 -0400 Subject: [PATCH 234/484] Move Server class to example/ folder --- {src => example}/Server.php | 4 ++-- scss.inc.php | 1 - tests/ServerTest.php | 3 ++- 3 files changed, 4 insertions(+), 4 deletions(-) rename {src => example}/Server.php (98%) diff --git a/src/Server.php b/example/Server.php similarity index 98% rename from src/Server.php rename to example/Server.php index c3fa985d..8dc3c7b2 100644 --- a/src/Server.php +++ b/example/Server.php @@ -434,7 +434,7 @@ public function __construct($dir, $cacheDir = null, $scss = null) $this->cacheDir = $cacheDir; if (! is_dir($this->cacheDir)) { - mkdir($this->cacheDir, 0755, true); + throw new ServerException('Cache directory doesn\'t exist: ' . $cacheDir); } if (! isset($scss)) { @@ -446,7 +446,7 @@ public function __construct($dir, $cacheDir = null, $scss = null) $this->showErrorsAsCSS = false; if (! ini_get('date.timezone')) { - date_default_timezone_set('UTC'); + throw new ServerException('Default date.timezone not set'); } } } diff --git a/scss.inc.php b/scss.inc.php index b6892fec..d2ec2639 100644 --- a/scss.inc.php +++ b/scss.inc.php @@ -26,5 +26,4 @@ include_once __DIR__ . '/src/Type.php'; include_once __DIR__ . '/src/Util.php'; include_once __DIR__ . '/src/Version.php'; - include_once __DIR__ . '/src/Server.php'; } diff --git a/tests/ServerTest.php b/tests/ServerTest.php index 2cecdd97..7c7ef6a9 100644 --- a/tests/ServerTest.php +++ b/tests/ServerTest.php @@ -8,9 +8,10 @@ * * @link http://leafo.github.io/scssphp */ - namespace Leafo\ScssPhp\Tests; +require_once __DIR__ . '/../example/Server.php'; + use Leafo\ScssPhp\Server; /** From 53e86eae35448fbcce8f682ffcb9fa224cd40cc3 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 14 Mar 2017 10:58:24 -0400 Subject: [PATCH 235/484] Fix failing ServerTest --- tests/ServerTest.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/ServerTest.php b/tests/ServerTest.php index 7c7ef6a9..fcaea917 100644 --- a/tests/ServerTest.php +++ b/tests/ServerTest.php @@ -23,6 +23,10 @@ class ServerTest extends \PHPUnit_Framework_TestCase { public function testCheckedCachedCompile() { + if (! file_exists(__DIR__ . '/inputs/scss_cache')) { + mkdir(__DIR__ . '/inputs/scss_cache', 0755); + } + $server = new Server(__DIR__ . '/inputs/'); $css = $server->checkedCachedCompile(__DIR__ . '/inputs/import.scss', '/tmp/scss.css'); From 942c61055d53a893f79a7d34261d609b618a1ced Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 22 Aug 2017 07:20:58 -0400 Subject: [PATCH 236/484] Update scss.inc.php --- scss.inc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scss.inc.php b/scss.inc.php index d2ec2639..83d23a6b 100644 --- a/scss.inc.php +++ b/scss.inc.php @@ -3,7 +3,7 @@ throw new \Exception('scssphp requires PHP 5.4 or above'); } -if (! class_exists('scssc', false)) { +if (! class_exists('Leafo\ScssPhp\Version', false)) { include_once __DIR__ . '/src/Base/Range.php'; include_once __DIR__ . '/src/Block.php'; include_once __DIR__ . '/src/Colors.php'; From 494c09f0c08261265ddca4ca51c51670e01680c4 Mon Sep 17 00:00:00 2001 From: Marina Glancy Date: Fri, 29 Sep 2017 16:00:39 +0800 Subject: [PATCH 237/484] fixes #532 Substitute deprecated php function each() --- src/Node/Number.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Node/Number.php b/src/Node/Number.php index d4f406fa..442d57d5 100644 --- a/src/Node/Number.php +++ b/src/Node/Number.php @@ -290,7 +290,7 @@ public function output(Compiler $compiler = null) } reset($units); - list($unit, ) = each($units); + $unit = key($units); return (string) $dimension . $unit; } From 9eaf3a6db4d88ce2c265a89e3fc495fbec9bf7a6 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Fri, 13 Oct 2017 11:52:30 -0400 Subject: [PATCH 238/484] Bump to version 0.7.0 --- src/Version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Version.php b/src/Version.php index 2d6eabc1..dd7cd384 100644 --- a/src/Version.php +++ b/src/Version.php @@ -18,5 +18,5 @@ */ class Version { - const VERSION = 'v0.6.7'; + const VERSION = 'v0.7.0'; } From f79f49b9aebf7c1885a27f1152f2d58d1ba4b05a Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Fri, 13 Oct 2017 12:06:15 -0400 Subject: [PATCH 239/484] Workaround PHP 7.2 code sniff --- src/Parser.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Parser.php b/src/Parser.php index 619b7ba8..44043e65 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -2468,7 +2468,13 @@ private function getSourcePosition($pos) */ private function saveEncoding() { - if (ini_get('mbstring.func_overload') & 2) { + if (version_compare(PHP_VERSION, '7.2.0') >= 0) { + return; + } + + $iniDirective = 'mbstring' . '.func_overload'; // deprecated in PHP 7.2 + + if (ini_get($iniDirective) & 2) { $this->encoding = mb_internal_encoding(); mb_internal_encoding('iso-8859-1'); From 192f9b29d41ba0e2c610c2343da61bdde28f6436 Mon Sep 17 00:00:00 2001 From: Thomas Eiling Date: Fri, 13 Oct 2017 12:48:50 -0400 Subject: [PATCH 240/484] misc - fix some phpdoc comments and code styles --- src/Compiler.php | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 68003b90..89119933 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -1181,7 +1181,7 @@ protected function flattenSelectorSingle($single) /** * Compile selector to string; self(&) should have been replaced by now * - * @param array $selector + * @param string|array $selector * * @return string */ @@ -1203,7 +1203,7 @@ protected function compileSelector($selector) /** * Compile selector part * - * @param arary $piece + * @param array $piece * * @return string */ @@ -1963,7 +1963,7 @@ protected function shouldEval($value) * @param array $value * @param boolean $inExp * - * @return array + * @return array|\Leafo\ScssPhp\Node\Number */ protected function reduce($value, $inExp = false) { @@ -2238,7 +2238,7 @@ public function normalizeValue($value) * @param array $left * @param array $right * - * @return array + * @return \Leafo\ScssPhp\Node\Number */ protected function opAddNumberNumber($left, $right) { @@ -2251,7 +2251,7 @@ protected function opAddNumberNumber($left, $right) * @param array $left * @param array $right * - * @return array + * @return \Leafo\ScssPhp\Node\Number */ protected function opMulNumberNumber($left, $right) { @@ -2264,7 +2264,7 @@ protected function opMulNumberNumber($left, $right) * @param array $left * @param array $right * - * @return array + * @return \Leafo\ScssPhp\Node\Number */ protected function opSubNumberNumber($left, $right) { @@ -2277,7 +2277,7 @@ protected function opSubNumberNumber($left, $right) * @param array $left * @param array $right * - * @return array + * @return array|\Leafo\ScssPhp\Node\Number */ protected function opDivNumberNumber($left, $right) { @@ -2294,7 +2294,7 @@ protected function opDivNumberNumber($left, $right) * @param array $left * @param array $right * - * @return array + * @return \Leafo\ScssPhp\Node\Number */ protected function opModNumberNumber($left, $right) { @@ -2580,7 +2580,7 @@ protected function opLtNumberNumber($left, $right) * @param array $left * @param array $right * - * @return array + * @return \Leafo\ScssPhp\Node\Number */ protected function opCmpNumberNumber($left, $right) { @@ -3505,7 +3505,7 @@ protected function fileExists($name) * Call SCSS @function * * @param string $name - * @param array $args + * @param array $argValues * @param array $returnValue * * @return boolean Returns true if returnValue is set; otherwise, false @@ -3777,7 +3777,7 @@ protected function applyArguments($argDef, $argValues) * * @param mixed $value * - * @return array + * @return array|\Leafo\ScssPhp\Node\Number */ private function coerceValue($value) { @@ -3850,7 +3850,8 @@ protected function coerceMap($item) /** * Coerce something to list * - * @param array $item + * @param array $item + * @param string $delim * * @return array */ @@ -5210,6 +5211,8 @@ protected function libVariableExists($args) * Workaround IE7's content counter bug. * * @param array $args + * + * @return array */ protected function libCounter($args) { From b413ff279f5f0ba5938e1315a8068a06643c9443 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Fri, 13 Oct 2017 13:29:38 -0400 Subject: [PATCH 241/484] Change default precision to 10 (introduced in sass 3.5.0) --- bin/pscss | 2 +- src/Node/Number.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/pscss b/bin/pscss index 4ec061b8..70a2be3c 100755 --- a/bin/pscss +++ b/bin/pscss @@ -76,7 +76,7 @@ Options include: -i=path Set import path --iso8859-1 Use iso8859-1 encoding instead of utf-8 (default utf-8) --line-numbers Annotate selectors with comments referring to the source file and line number - -p=precision Set decimal number precision (default 5) + -p=precision Set decimal number precision (default 10) -T Dump formatted parse tree -v, --version Print the version diff --git a/src/Node/Number.php b/src/Node/Number.php index 442d57d5..5faedfdf 100644 --- a/src/Node/Number.php +++ b/src/Node/Number.php @@ -31,7 +31,7 @@ class Number extends Node implements \ArrayAccess /** * @var integer */ - static public $precision = 5; + static public $precision = 10; /** * @see http://www.w3.org/TR/2012/WD-css3-values-20120308/ From 75c9fb79ae362a84e89736c2812fc022332e0f5a Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sat, 14 Oct 2017 13:29:08 -0400 Subject: [PATCH 242/484] Remove setlocale() to mitigate Windows side-effects --- src/Compiler.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 89119933..0d67c525 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -165,9 +165,6 @@ public function __construct() */ public function compile($code, $path = null) { - $locale = setlocale(LC_NUMERIC, 0); - setlocale(LC_NUMERIC, 'C'); - $this->indentLevel = -1; $this->commentsSeen = []; $this->extends = []; @@ -196,8 +193,6 @@ public function compile($code, $path = null) $out = $this->formatter->format($this->scope); - setlocale(LC_NUMERIC, $locale); - return $out; } From 9b3c1ff4eab71cdda612646fc94cfba0de9b6672 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sat, 14 Oct 2017 13:33:24 -0400 Subject: [PATCH 243/484] Use number_format to output locale-independent floats --- src/Node/Number.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Node/Number.php b/src/Node/Number.php index 5faedfdf..df1b79cc 100644 --- a/src/Node/Number.php +++ b/src/Node/Number.php @@ -292,7 +292,7 @@ public function output(Compiler $compiler = null) reset($units); $unit = key($units); - return (string) $dimension . $unit; + return preg_replace('/[.]0*$/', '', number_format($dimension, static::$precision)) . $unit; } /** From 3cbdd6330958420b949fd19ef24472edfd3ee517 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sat, 14 Oct 2017 14:31:52 -0400 Subject: [PATCH 244/484] Trim trailing zeros --- src/Node/Number.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Node/Number.php b/src/Node/Number.php index df1b79cc..59eb811a 100644 --- a/src/Node/Number.php +++ b/src/Node/Number.php @@ -291,8 +291,9 @@ public function output(Compiler $compiler = null) reset($units); $unit = key($units); - - return preg_replace('/[.]0*$/', '', number_format($dimension, static::$precision)) . $unit; + $dimension = number_format($dimension, static::$precision, '.', ''); + + return (static::$precision ? rtrim($dimension, '0') : $dimension) . $unit; } /** From db5273cd3a33cc0fb1b1fbba85dbb00deb61446b Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sat, 14 Oct 2017 14:35:20 -0400 Subject: [PATCH 245/484] Trim trailing decimal point. --- src/Node/Number.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Node/Number.php b/src/Node/Number.php index 59eb811a..acfdaf29 100644 --- a/src/Node/Number.php +++ b/src/Node/Number.php @@ -293,7 +293,7 @@ public function output(Compiler $compiler = null) $unit = key($units); $dimension = number_format($dimension, static::$precision, '.', ''); - return (static::$precision ? rtrim($dimension, '0') : $dimension) . $unit; + return (static::$precision ? rtrim($dimension, '.0') : $dimension) . $unit; } /** From c0ec63ff8eaff626ddeee1245b9119c82990c0ff Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sat, 14 Oct 2017 14:40:55 -0400 Subject: [PATCH 246/484] Fix previous edit from my phone. =P --- src/Node/Number.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Node/Number.php b/src/Node/Number.php index acfdaf29..3dcdd2ca 100644 --- a/src/Node/Number.php +++ b/src/Node/Number.php @@ -293,7 +293,7 @@ public function output(Compiler $compiler = null) $unit = key($units); $dimension = number_format($dimension, static::$precision, '.', ''); - return (static::$precision ? rtrim($dimension, '.0') : $dimension) . $unit; + return (static::$precision ? rtrim(rtrim($dimension, '0'), '.') : $dimension) . $unit; } /** From f735beeb2b8926f4d361d2c51bbefbc5271fbdfc Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sat, 14 Oct 2017 14:46:23 -0400 Subject: [PATCH 247/484] Fix unit test (1/2) --- tests/outputs/operators.css | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/outputs/operators.css b/tests/outputs/operators.css index fb492c82..50aaa8c0 100644 --- a/tests/outputs/operators.css +++ b/tests/outputs/operators.css @@ -6,7 +6,7 @@ body { color: 5px; top: 1.5em; left: -1cm; - top: 6.29921; } + top: 6.2992125984; } div { color: false; @@ -19,7 +19,7 @@ div { color: 1; } #units { - test: 2.5748in; + test: 2.5748031496in; test: 13mm; test: 4em; test: 11mm; @@ -169,7 +169,7 @@ div { margin: 0 -2em; } div { - *margin-left: 2.07447%; } + *margin-left: 2.0744680851%; } .foo { width: 12.5%; } From 69cb0ca734665c9a9ea41dd3e3d6f7a65dfdd93d Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sat, 14 Oct 2017 14:48:01 -0400 Subject: [PATCH 248/484] Fix unit test (2/2) --- tests/outputs_numbered/operators.css | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/outputs_numbered/operators.css b/tests/outputs_numbered/operators.css index c3ea3d1d..ad4d678e 100644 --- a/tests/outputs_numbered/operators.css +++ b/tests/outputs_numbered/operators.css @@ -7,7 +7,7 @@ body { color: 5px; top: 1.5em; left: -1cm; - top: 6.29921; } + top: 6.2992125984; } /* line 15, inputs/operators.scss */ div { color: false; @@ -20,7 +20,7 @@ div { color: 1; } /* line 29, inputs/operators.scss */ #units { - test: 2.5748in; + test: 2.5748031496in; test: 13mm; test: 4em; test: 11mm; @@ -170,7 +170,7 @@ div { margin: 0 -2em; } /* line 184, inputs/operators.scss */ div { - *margin-left: 2.07447%; } + *margin-left: 2.0744680851%; } /* line 190, inputs/operators.scss */ .foo { width: 12.5%; } From 4f605a51462c85cf6df5bb45460483a138e04025 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sat, 14 Oct 2017 17:35:01 -0400 Subject: [PATCH 249/484] Bump version to 0.7.2 --- src/Version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Version.php b/src/Version.php index dd7cd384..0dd95fe6 100644 --- a/src/Version.php +++ b/src/Version.php @@ -18,5 +18,5 @@ */ class Version { - const VERSION = 'v0.7.0'; + const VERSION = 'v0.7.2'; } From c96edd1bedd3ba3e8ebeaf5ddd1f8669fb6f5625 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20FRAN=C3=87OIS?= Date: Thu, 9 Nov 2017 11:16:12 +0100 Subject: [PATCH 250/484] add inline sourceMaps support --- src/Block.php | 5 + src/Compiler.php | 49 +++++- src/Formatter.php | 77 ++++++++- src/Formatter/Compressed.php | 4 +- src/Formatter/Crunched.php | 4 +- src/Formatter/Debug.php | 16 +- src/Formatter/Expanded.php | 4 +- src/Formatter/Nested.php | 20 ++- src/Formatter/OutputBlock.php | 15 ++ src/Parser.php | 1 + src/SourceMap/Base64VLQEncoder.php | 174 +++++++++++++++++++ src/SourceMap/SourceMapGenerator.php | 241 +++++++++++++++++++++++++++ 12 files changed, 579 insertions(+), 31 deletions(-) create mode 100644 src/SourceMap/Base64VLQEncoder.php create mode 100644 src/SourceMap/SourceMapGenerator.php diff --git a/src/Block.php b/src/Block.php index a6b5f3f0..87cfd6d8 100644 --- a/src/Block.php +++ b/src/Block.php @@ -28,6 +28,11 @@ class Block */ public $parent; + /** + * @var string; + */ + public $sourceName; + /** * @var integer */ diff --git a/src/Compiler.php b/src/Compiler.php index 0d67c525..a677760d 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -18,6 +18,7 @@ use Leafo\ScssPhp\Exception\CompilerException; use Leafo\ScssPhp\Formatter\OutputBlock; use Leafo\ScssPhp\Node; +use Leafo\ScssPhp\SourceMap\SourceMapGenerator; use Leafo\ScssPhp\Type; use Leafo\ScssPhp\Parser; use Leafo\ScssPhp\Util; @@ -64,6 +65,9 @@ class Compiler const WITH_SUPPORTS = 4; const WITH_ALL = 7; + const SOURCE_MAP_NONE = 0; + const SOURCE_MAP_INLINE = 1; + /** * @var array */ @@ -120,11 +124,16 @@ class Compiler protected $encoding = null; protected $lineNumberStyle = null; + protected $sourceMap = self::SOURCE_MAP_NONE; + protected $sourceMapOptions = []; + + /** @var string|Formatter */ protected $formatter = 'Leafo\ScssPhp\Formatter\Nested'; protected $rootEnv; protected $rootBlock; + /** @var Environment */ protected $env; protected $scope; protected $storeEnv; @@ -191,8 +200,21 @@ public function compile($code, $path = null) $this->compileRoot($tree); $this->popEnv(); - $out = $this->formatter->format($this->scope); + $sourceMapGenerator = null; + if($this->sourceMap && $this->sourceMap !== self::SOURCE_MAP_NONE) { + $sourceMapGenerator = new SourceMapGenerator($this->sourceMapOptions); + } + $out = $this->formatter->format($this->scope, $sourceMapGenerator); + if($this->sourceMap && $this->sourceMap !== self::SOURCE_MAP_NONE) { + $sourceMap = $sourceMapGenerator->generateJson(); + + $sourceMapUrl = null; + if($this->sourceMap == self::SOURCE_MAP_INLINE) { + $sourceMapUrl = sprintf('data:application/json,%s', self::encodeURIComponent($sourceMap)); + } + $out .= sprintf('/*# sourceMappingURL=%s */', $sourceMapUrl); + } return $out; } @@ -274,6 +296,9 @@ protected function makeOutputBlock($type, $selectors = null) $out->parent = $this->scope; $out->selectors = $selectors; $out->depth = $this->env->depth; + $out->sourceName = $this->env->block->sourceName; + $out->sourceLine = $this->env->block->sourceLine; + $out->sourceColumn = $this->env->block->sourceColumn; return $out; } @@ -656,6 +681,7 @@ protected function compileMedia(Block $media) if ($needsWrap) { $wrapped = new Block; + $wrapped->sourceName = $media->sourceName; $wrapped->sourceIndex = $media->sourceIndex; $wrapped->sourceLine = $media->sourceLine; $wrapped->sourceColumn = $media->sourceColumn; @@ -729,6 +755,7 @@ protected function compileAtRoot(Block $block) // wrap inline selector if ($block->selector) { $wrapped = new Block; + $wrapped->sourceName = $block->sourceName; $wrapped->sourceIndex = $block->sourceIndex; $wrapped->sourceLine = $block->sourceLine; $wrapped->sourceColumn = $block->sourceColumn; @@ -785,6 +812,7 @@ private function spliceTree($envs, Block $block, $without) } $b = new Block; + $b->sourceName = $e->block->sourceName; $b->sourceIndex = $e->block->sourceIndex; $b->sourceLine = $e->block->sourceLine; $b->sourceColumn = $e->block->sourceColumn; @@ -1076,6 +1104,13 @@ protected function evalSelectors($selectors) return $selectors; } + /** + * @param array $sourceMapOptions + */ + public function setSourceMapOptions($sourceMapOptions) { + $this->sourceMapOptions = $sourceMapOptions; + } + /** * Evaluate selector * @@ -3299,6 +3334,13 @@ public function setLineNumberStyle($lineNumberStyle) $this->lineNumberStyle = $lineNumberStyle; } + /** + * @param int $sourceMap + */ + public function setSourceMap($sourceMap) { + $this->sourceMap = $sourceMap; + } + /** * Register function * @@ -5256,4 +5298,9 @@ protected function libInspect($args) return $args[0]; } + + public static function encodeURIComponent($string){ + $revert = array('%21' => '!', '%2A' => '*', '%27' => "'", '%28' => '(', '%29' => ')'); + return strtr(rawurlencode($string), $revert); + } } diff --git a/src/Formatter.php b/src/Formatter.php index 0d2635f1..487b6612 100644 --- a/src/Formatter.php +++ b/src/Formatter.php @@ -12,6 +12,7 @@ namespace Leafo\ScssPhp; use Leafo\ScssPhp\Formatter\OutputBlock; +use Leafo\ScssPhp\SourceMap\SourceMapGenerator; /** * Base formatter @@ -56,10 +57,31 @@ abstract class Formatter public $assignSeparator; /** - * @var boolea + * @var boolean */ public $keepSemicolons; + /** + * @var OutputBlock; + */ + protected $currentBlock; + + + /** + * @var int + */ + protected $currentLine; + + /** + * @var int; + */ + protected $currentColumn; + + /** + * @var SourceMapGenerator + */ + protected $sourceMapGenerator; + /** * Initialize formatter * @@ -123,10 +145,10 @@ protected function blockLines(OutputBlock $block) $glue = $this->break . $inner; - echo $inner . implode($glue, $block->lines); + $this->write($inner . implode($glue, $block->lines)); if (! empty($block->children)) { - echo $this->break; + $this->write($this->break); } } @@ -139,9 +161,9 @@ protected function blockSelectors(OutputBlock $block) { $inner = $this->indentStr(); - echo $inner + $this->write($inner . implode($this->tagSeparator, $block->selectors) - . $this->open . $this->break; + . $this->open . $this->break); } /** @@ -167,6 +189,8 @@ protected function block(OutputBlock $block) return; } + $this->currentBlock = $block; + $pre = $this->indentStr(); if (! empty($block->selectors)) { @@ -187,10 +211,10 @@ protected function block(OutputBlock $block) $this->indentLevel--; if (empty($block->children)) { - echo $this->break; + $this->write($this->break); } - echo $pre . $this->close . $this->break; + $this->write($pre . $this->close . $this->break); } } @@ -201,10 +225,19 @@ protected function block(OutputBlock $block) * * @param \Leafo\ScssPhp\Formatter\OutputBlock $block An abstract syntax tree * + * @param SourceMapGenerator|null $sourceMapGenerator * @return string + * @internal param bool $collectSourceMap */ - public function format(OutputBlock $block) + public function format(OutputBlock $block, SourceMapGenerator $sourceMapGenerator = null) { + if($sourceMapGenerator) { + $this->currentLine = 1; + $this->currentColumn = 0; + $this->sourceMapGenerator = $sourceMapGenerator; + } else { + $this->sourceMapGenerator = null; + } ob_start(); $this->block($block); @@ -213,4 +246,32 @@ public function format(OutputBlock $block) return $out; } + + /** + * @param $str + */ + protected function write($str) { + if($this->sourceMapGenerator) { + $this->sourceMapGenerator->addMapping( + $this->currentLine, + $this->currentColumn, + $this->currentBlock->sourceLine, + $this->currentBlock->sourceColumn, + $this->currentBlock->sourceName + ); + + $lines = explode("\n", $str); + $lineCount = count($lines); + $this->currentLine += $lineCount-1; + + $lastLine = array_pop($lines); + if($lineCount == 1) { + $this->currentColumn += mb_strlen($lastLine); + } else { + $this->currentColumn = mb_strlen($lastLine); + } + } + + echo $str; + } } diff --git a/src/Formatter/Compressed.php b/src/Formatter/Compressed.php index 17aca54a..69a67850 100644 --- a/src/Formatter/Compressed.php +++ b/src/Formatter/Compressed.php @@ -53,10 +53,10 @@ public function blockLines(OutputBlock $block) } } - echo $inner . implode($glue, $block->lines); + $this->write( $inner . implode($glue, $block->lines)); if (! empty($block->children)) { - echo $this->break; + $this->write( $this->break); } } } diff --git a/src/Formatter/Crunched.php b/src/Formatter/Crunched.php index 0a89bc48..43d63833 100644 --- a/src/Formatter/Crunched.php +++ b/src/Formatter/Crunched.php @@ -51,10 +51,10 @@ public function blockLines(OutputBlock $block) } } - echo $inner . implode($glue, $block->lines); + $this->write($inner . implode($glue, $block->lines)); if (! empty($block->children)) { - echo $this->break; + $this->write($this->break); } } } diff --git a/src/Formatter/Debug.php b/src/Formatter/Debug.php index 321c5368..cb29641a 100644 --- a/src/Formatter/Debug.php +++ b/src/Formatter/Debug.php @@ -52,13 +52,13 @@ protected function blockLines(OutputBlock $block) $indent = $this->indentStr(); if (empty($block->lines)) { - echo "{$indent}block->lines: []\n"; + $this->write("{$indent}block->lines: []\n"); return; } foreach ($block->lines as $index => $line) { - echo "{$indent}block->lines[{$index}]: $line\n"; + $this->write( "{$indent}block->lines[{$index}]: $line\n"); } } @@ -70,13 +70,13 @@ protected function blockSelectors(OutputBlock $block) $indent = $this->indentStr(); if (empty($block->selectors)) { - echo "{$indent}block->selectors: []\n"; + $this->write( "{$indent}block->selectors: []\n"); return; } foreach ($block->selectors as $index => $selector) { - echo "{$indent}block->selectors[{$index}]: $selector\n"; + $this->write( "{$indent}block->selectors[{$index}]: $selector\n"); } } @@ -88,7 +88,7 @@ protected function blockChildren(OutputBlock $block) $indent = $this->indentStr(); if (empty($block->children)) { - echo "{$indent}block->children: []\n"; + $this->write( "{$indent}block->children: []\n"); return; } @@ -109,8 +109,10 @@ protected function block(OutputBlock $block) { $indent = $this->indentStr(); - echo "{$indent}block->type: {$block->type}\n" . - "{$indent}block->depth: {$block->depth}\n"; + $this->write( "{$indent}block->type: {$block->type}\n" . + "{$indent}block->depth: {$block->depth}\n"); + + $this->currentBlock = $block; $this->blockSelectors($block); $this->blockLines($block); diff --git a/src/Formatter/Expanded.php b/src/Formatter/Expanded.php index dff17e6b..0449787d 100644 --- a/src/Formatter/Expanded.php +++ b/src/Formatter/Expanded.php @@ -59,10 +59,10 @@ protected function blockLines(OutputBlock $block) } } - echo $inner . implode($glue, $block->lines); + $this->write($inner . implode($glue, $block->lines)); if (empty($block->selectors) || ! empty($block->children)) { - echo $this->break; + $this->write($this->break); } } } diff --git a/src/Formatter/Nested.php b/src/Formatter/Nested.php index c4ff803c..48e19ef7 100644 --- a/src/Formatter/Nested.php +++ b/src/Formatter/Nested.php @@ -12,7 +12,6 @@ namespace Leafo\ScssPhp\Formatter; use Leafo\ScssPhp\Formatter; -use Leafo\ScssPhp\Formatter\OutputBlock; /** * Nested formatter @@ -66,10 +65,10 @@ protected function blockLines(OutputBlock $block) } } - echo $inner . implode($glue, $block->lines); + $this->write( $inner . implode($glue, $block->lines)); if (! empty($block->children)) { - echo $this->break; + $this->write( $this->break); } } @@ -80,9 +79,9 @@ protected function blockSelectors(OutputBlock $block) { $inner = $this->indentStr(); - echo $inner + $this->write( $inner . implode($this->tagSeparator, $block->selectors) - . $this->open . $this->break; + . $this->open . $this->break); } /** @@ -94,13 +93,13 @@ protected function blockChildren(OutputBlock $block) $this->block($child); if ($i < count($block->children) - 1) { - echo $this->break; + $this->write( $this->break); 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; + $this->write( $this->break); } } } @@ -120,6 +119,9 @@ protected function block(OutputBlock $block) return; } + $this->currentBlock = $block; + + $this->depth = $block->depth; if (! empty($block->selectors)) { @@ -139,11 +141,11 @@ protected function block(OutputBlock $block) if (! empty($block->selectors)) { $this->indentLevel--; - echo $this->close; + $this->write( $this->close); } if ($block->type === 'root') { - echo $this->break; + $this->write( $this->break); } } diff --git a/src/Formatter/OutputBlock.php b/src/Formatter/OutputBlock.php index 88e86402..7d233a8d 100644 --- a/src/Formatter/OutputBlock.php +++ b/src/Formatter/OutputBlock.php @@ -47,4 +47,19 @@ class OutputBlock * @var \Leafo\ScssPhp\Formatter\OutputBlock */ public $parent; + + /** + * @var string + */ + public $sourceName; + + /** + * @var int + */ + public $sourceLine; + + /** + * @var int + */ + public $sourceColumn; } diff --git a/src/Parser.php b/src/Parser.php index 44043e65..cc0a51df 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -708,6 +708,7 @@ protected function pushBlock($selectors, $pos = 0) list($line, $column) = $this->getSourcePosition($pos); $b = new Block; + $b->sourceName = $this->sourceName; $b->sourceLine = $line; $b->sourceColumn = $column; $b->sourceIndex = $this->sourceIndex; diff --git a/src/SourceMap/Base64VLQEncoder.php b/src/SourceMap/Base64VLQEncoder.php new file mode 100644 index 00000000..14652fbe --- /dev/null +++ b/src/SourceMap/Base64VLQEncoder.php @@ -0,0 +1,174 @@ + 0, 'B' => 1, 'C' => 2, 'D' => 3, 'E' => 4, 'F' => 5, 'G' => 6, + 'H' => 7,'I' => 8, 'J' => 9, 'K' => 10, 'L' => 11, 'M' => 12, 'N' => 13, + 'O' => 14, 'P' => 15, 'Q' => 16, 'R' => 17, 'S' => 18, 'T' => 19, 'U' => 20, + 'V' => 21, 'W' => 22, 'X' => 23, 'Y' => 24, 'Z' => 25, 'a' => 26, 'b' => 27, + 'c' => 28, 'd' => 29, 'e' => 30, 'f' => 31, 'g' => 32, 'h' => 33, 'i' => 34, + 'j' => 35, 'k' => 36, 'l' => 37, 'm' => 38, 'n' => 39, 'o' => 40, 'p' => 41, + 'q' => 42, 'r' => 43, 's' => 44, 't' => 45, 'u' => 46, 'v' => 47, 'w' => 48, + 'x' => 49, 'y' => 50, 'z' => 51, 0 => 52, 1 => 53, 2 => 54, 3 => 55, 4 => 56, + 5 => 57, 6 => 58, 7 => 59, 8 => 60, 9 => 61, '+' => 62, '/' => 63, + ); + /** + * Integer to char map + * + * @var array + */ + private $intToCharMap = array( + 0 => 'A', 1 => 'B', 2 => 'C', 3 => 'D', 4 => 'E', 5 => 'F', 6 => 'G', + 7 => 'H', 8 => 'I', 9 => 'J', 10 => 'K', 11 => 'L', 12 => 'M', 13 => 'N', + 14 => 'O', 15 => 'P', 16 => 'Q', 17 => 'R', 18 => 'S', 19 => 'T', 20 => 'U', + 21 => 'V', 22 => 'W', 23 => 'X', 24 => 'Y', 25 => 'Z', 26 => 'a', 27 => 'b', + 28 => 'c', 29 => 'd', 30 => 'e', 31 => 'f', 32 => 'g', 33 => 'h', 34 => 'i', + 35 => 'j', 36 => 'k', 37 => 'l', 38 => 'm', 39 => 'n', 40 => 'o', 41 => 'p', + 42 => 'q', 43 => 'r', 44 => 's', 45 => 't', 46 => 'u', 47 => 'v', 48 => 'w', + 49 => 'x', 50 => 'y', 51 => 'z', 52 => '0', 53 => '1', 54 => '2', 55 => '3', + 56 => '4', 57 => '5', 58 => '6', 59 => '7', 60 => '8', 61 => '9', 62 => '+', + 63 => '/', + ); + /** + * Constructor + */ + public function __construct(){ + // I leave it here for future reference + // foreach(str_split('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/') as $i => $char) + // { + // $this->charToIntMap[$char] = $i; + // $this->intToCharMap[$i] = $char; + // } + } + /** + * Convert from a two-complement value to a value where the sign bit is + * is placed in the least significant bit. For example, as decimals: + * 1 becomes 2 (10 binary), -1 becomes 3 (11 binary) + * 2 becomes 4 (100 binary), -2 becomes 5 (101 binary) + * We generate the value for 32 bit machines, hence -2147483648 becomes 1, not 4294967297, + * even on a 64 bit machine. + * @param string $aValue + */ + public function toVLQSigned($aValue){ + return 0xffffffff & ($aValue < 0 ? ((-$aValue) << 1) + 1 : ($aValue << 1) + 0); + } + /** + * Convert to a two-complement value from a value where the sign bit is + * is placed in the least significant bit. For example, as decimals: + * 2 (10 binary) becomes 1, 3 (11 binary) becomes -1 + * 4 (100 binary) becomes 2, 5 (101 binary) becomes -2 + * We assume that the value was generated with a 32 bit machine in mind. + * Hence + * 1 becomes -2147483648 + * even on a 64 bit machine. + * @param integer $aValue + */ + public function fromVLQSigned($aValue){ + return $aValue & 1 ? $this->zeroFill(~$aValue + 2, 1) | (-1 - 0x7fffffff) : $this->zeroFill($aValue, 1); + } + /** + * Return the base 64 VLQ encoded value. + * + * @param string $aValue The value to encode + * @return string The encoded value + */ + public function encode($aValue){ + $encoded = ''; + $vlq = $this->toVLQSigned($aValue); + do + { + $digit = $vlq & $this->mask; + $vlq = $this->zeroFill($vlq, $this->shift); + if($vlq > 0){ + $digit |= $this->continuationBit; + } + $encoded .= $this->base64Encode($digit); + } while($vlq > 0); + return $encoded; + } + /** + * Return the value decoded from base 64 VLQ. + * + * @param string $encoded The encoded value to decode + * @return integer The decoded value + */ + public function decode($encoded){ + $vlq = 0; + $i = 0; + do + { + $digit = $this->base64Decode($encoded[$i]); + $vlq |= ($digit & $this->mask) << ($i * $this->shift); + $i++; + } while($digit & $this->continuationBit); + return $this->fromVLQSigned($vlq); + } + /** + * Right shift with zero fill. + * + * @param integer $a number to shift + * @param integer $b number of bits to shift + * @return integer + */ + public function zeroFill($a, $b){ + return ($a >= 0) ? ($a >> $b) : ($a >> $b) & (PHP_INT_MAX >> ($b - 1)); + } + /** + * Encode single 6-bit digit as base64. + * + * @param integer $number + * @return string + * @throws Exception If the number is invalid + */ + public function base64Encode($number){ + if($number < 0 || $number > 63){ + throw new Exception(sprintf('Invalid number "%s" given. Must be between 0 and 63.', $number)); + } + return $this->intToCharMap[$number]; + } + /** + * Decode single 6-bit digit from base64 + * + * @param string $char + * @return number + * @throws Exception If the number is invalid + */ + public function base64Decode($char){ + if(!array_key_exists($char, $this->charToIntMap)){ + throw new Exception(sprintf('Invalid base 64 digit "%s" given.', $char)); + } + return $this->charToIntMap[$char]; + } +} diff --git a/src/SourceMap/SourceMapGenerator.php b/src/SourceMap/SourceMapGenerator.php new file mode 100644 index 00000000..3d21f4fe --- /dev/null +++ b/src/SourceMap/SourceMapGenerator.php @@ -0,0 +1,241 @@ + '', + // an optional name of the generated code that this source map is associated with. + 'sourceMapFilename' => null, + // url of the map + 'sourceMapURL' => null, + // absolute path to a file to write the map to + 'sourceMapWriteTo' => null, + // output source contents? + 'outputSourceFiles' => false, + // base path for filename normalization + 'sourceMapRootpath' => '', + // base path for filename normalization + 'sourceMapBasepath' => '' + ); + + /** + * The base64 VLQ encoder + * + * @var Base64VLQEncoder + */ + protected $encoder; + /** + * Array of mappings + * + * @var array + */ + protected $mappings = array(); + + /** + * Array of contents map + * + * @var array + */ + protected $contentsMap = array(); + /** + * File to content map + * + * @var array + */ + protected $sources = array(); + protected $source_keys = array(); + /** + * @var array + */ + private $options; + + public function __construct(array $options = []) { + $this->options = array_merge($this->defaultOptions, $options); + $this->encoder = new Base64VLQEncoder(); + } + + /** + * Adds a mapping + * + * @param integer $generatedLine The line number in generated file + * @param integer $generatedColumn The column number in generated file + * @param integer $originalLine The line number in original file + * @param integer $originalColumn The column number in original file + * @param string $sourceFile The original source file + */ + public function addMapping($generatedLine, $generatedColumn, $originalLine, $originalColumn, $sourceFile) { + $this->mappings[] = array( + 'generated_line' => $generatedLine, + 'generated_column' => $generatedColumn, + 'original_line' => $originalLine, + 'original_column' => $originalColumn, + 'source_file' => $sourceFile + ); + + $this->sources[$sourceFile] = $sourceFile; + } + + /** + * Generates the JSON source map + * + * @return string + * @see https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit# + */ + public function generateJson() { + $sourceMap = array(); + $mappings = $this->generateMappings(); + // File version (always the first entry in the object) and must be a positive integer. + $sourceMap['version'] = self::VERSION; + // An optional name of the generated code that this source map is associated with. + $file = $this->options['sourceMapFilename']; + if($file) { + $sourceMap['file'] = $file; + } + // An optional source root, useful for relocating source files on a server or removing repeated values in the 'sources' entry. This value is prepended to the individual entries in the 'source' field. + $root = $this->options['sourceRoot']; + if($root) { + $sourceMap['sourceRoot'] = $root; + } + // A list of original sources used by the 'mappings' entry. + $sourceMap['sources'] = array(); + foreach($this->sources as $source_uri => $source_filename) { + $sourceMap['sources'][] = $this->normalizeFilename($source_filename); + } + // A list of symbol names used by the 'mappings' entry. + $sourceMap['names'] = array(); + // A string with the encoded mapping data. + $sourceMap['mappings'] = $mappings; + if($this->options['outputSourceFiles']) { + // An optional list of source content, useful when the 'source' can't be hosted. + // The contents are listed in the same order as the sources above. + // 'null' may be used if some original sources should be retrieved by name. + $sourceMap['sourcesContent'] = $this->getSourcesContent(); + } + // less.js compat fixes + if(count($sourceMap['sources']) && empty($sourceMap['sourceRoot'])) { + unset($sourceMap['sourceRoot']); + } + return json_encode($sourceMap); + } + + /** + * Returns the sources contents + * + * @return array|null + */ + protected function getSourcesContent() { + if(empty($this->sources)) { + return null; + } + $content = array(); + foreach($this->sources as $sourceFile) { + $content[] = file_get_contents($sourceFile); + } + return $content; + } + + /** + * Generates the mappings string + * + * @return string + */ + public function generateMappings() { + if(!count($this->mappings)) { + return ''; + } + $this->source_keys = array_flip(array_keys($this->sources)); + // group mappings by generated line number. + $groupedMap = $groupedMapEncoded = array(); + foreach($this->mappings as $m) { + $groupedMap[$m['generated_line']][] = $m; + } + ksort($groupedMap); + $lastGeneratedLine = $lastOriginalIndex = $lastOriginalLine = $lastOriginalColumn = 0; + foreach($groupedMap as $lineNumber => $line_map) { + while(++$lastGeneratedLine < $lineNumber) { + $groupedMapEncoded[] = ';'; + } + $lineMapEncoded = array(); + $lastGeneratedColumn = 0; + foreach($line_map as $m) { + $mapEncoded = $this->encoder->encode($m['generated_column'] - $lastGeneratedColumn); + $lastGeneratedColumn = $m['generated_column']; + // find the index + if($m['source_file']) { + $index = $this->findFileIndex($m['source_file']); + if($index !== false) { + $mapEncoded .= $this->encoder->encode($index - $lastOriginalIndex); + $lastOriginalIndex = $index; + // lines are stored 0-based in SourceMap spec version 3 + $mapEncoded .= $this->encoder->encode($m['original_line'] - 1 - $lastOriginalLine); + $lastOriginalLine = $m['original_line'] - 1; + $mapEncoded .= $this->encoder->encode($m['original_column'] - $lastOriginalColumn); + $lastOriginalColumn = $m['original_column']; + } + } + $lineMapEncoded[] = $mapEncoded; + } + $groupedMapEncoded[] = implode(',', $lineMapEncoded) . ';'; + } + return rtrim(implode($groupedMapEncoded), ';'); + } + + /** + * Finds the index for the filename + * + * @param string $filename + * @return integer|false + */ + protected function findFileIndex($filename) { + return $this->source_keys[$filename]; + } + + protected function normalizeFilename($filename) { + $filename = $this->fixWindowsPath($filename); + $rootpath = $this->options['sourceMapRootpath']; + $basePath = $this->options['sourceMapBasepath']; + // "Trim" the 'sourceMapBasepath' from the output filename. + if(strpos($filename, $basePath) === 0) { + $filename = substr($filename, strlen($basePath)); + } + // Remove extra leading path separators. + if(strpos($filename, '\\') === 0 || strpos($filename, '/') === 0) { + $filename = substr($filename, 1); + } + return $rootpath . $filename; + } + + /** + * fix windows paths + * @param string $path + * @param bool $addEndSlash + * @return string + */ + public function fixWindowsPath($path, $addEndSlash = false) { + $slash = ($addEndSlash) ? '/' : ''; + if(!empty($path)) { + $path = str_replace('\\', '/', $path); + $path = rtrim($path, '/') . $slash; + } + return $path; + } +} From 87c8b53442f0be9c8dd33cbac2308842dd240240 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20FRAN=C3=87OIS?= Date: Thu, 9 Nov 2017 13:37:18 +0100 Subject: [PATCH 251/484] inline sourceMaps: fix off-by-one column in source maps --- src/Formatter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Formatter.php b/src/Formatter.php index 487b6612..7939113b 100644 --- a/src/Formatter.php +++ b/src/Formatter.php @@ -256,7 +256,7 @@ protected function write($str) { $this->currentLine, $this->currentColumn, $this->currentBlock->sourceLine, - $this->currentBlock->sourceColumn, + $this->currentBlock->sourceColumn - 1, //columns from parser are off by one $this->currentBlock->sourceName ); From ce6c958bc90b729467b010d35fece984e6edec44 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Thu, 14 Dec 2017 12:34:12 -0500 Subject: [PATCH 252/484] Remove trailing whitespace --- src/Node/Number.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Node/Number.php b/src/Node/Number.php index 3dcdd2ca..efa83f5b 100644 --- a/src/Node/Number.php +++ b/src/Node/Number.php @@ -292,7 +292,7 @@ public function output(Compiler $compiler = null) reset($units); $unit = key($units); $dimension = number_format($dimension, static::$precision, '.', ''); - + return (static::$precision ? rtrim(rtrim($dimension, '0'), '.') : $dimension) . $unit; } From eb4ab3f9be1d7fe854017c3bf97a3ca8c27730fb Mon Sep 17 00:00:00 2001 From: dleffler Date: Fri, 15 Dec 2017 12:42:20 -0500 Subject: [PATCH 253/484] Adds File based source map (in addition to inline source map) - SOURCE_MAP_FILE, using code from less.php; ONLY if there is output from the scss file. --- scss.inc.php | 4 ++++ src/Compiler.php | 5 ++++- src/SourceMap/SourceMapGenerator.php | 24 ++++++++++++++++++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/scss.inc.php b/scss.inc.php index 83d23a6b..77303ba3 100644 --- a/scss.inc.php +++ b/scss.inc.php @@ -11,6 +11,7 @@ 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/RangeException.php'; //exp include_once __DIR__ . '/src/Exception/ServerException.php'; include_once __DIR__ . '/src/Formatter.php'; include_once __DIR__ . '/src/Formatter/Compact.php'; @@ -23,6 +24,9 @@ include_once __DIR__ . '/src/Node.php'; include_once __DIR__ . '/src/Node/Number.php'; include_once __DIR__ . '/src/Parser.php'; +// include_once __DIR__ . '/src/example/Server.php'; + include_once __DIR__ . '/src/SourceMap/Base64VLQEncoder.php'; + include_once __DIR__ . '/src/SourceMap/SourceMapGenerator.php'; include_once __DIR__ . '/src/Type.php'; include_once __DIR__ . '/src/Util.php'; include_once __DIR__ . '/src/Version.php'; diff --git a/src/Compiler.php b/src/Compiler.php index a677760d..2dc50531 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -67,6 +67,7 @@ class Compiler const SOURCE_MAP_NONE = 0; const SOURCE_MAP_INLINE = 1; + const SOURCE_MAP_FILE = 2; /** * @var array @@ -205,12 +206,14 @@ public function compile($code, $path = null) $sourceMapGenerator = new SourceMapGenerator($this->sourceMapOptions); } $out = $this->formatter->format($this->scope, $sourceMapGenerator); - if($this->sourceMap && $this->sourceMap !== self::SOURCE_MAP_NONE) { + if(!empty($out) && $this->sourceMap && $this->sourceMap !== self::SOURCE_MAP_NONE) { $sourceMap = $sourceMapGenerator->generateJson(); $sourceMapUrl = null; if($this->sourceMap == self::SOURCE_MAP_INLINE) { $sourceMapUrl = sprintf('data:application/json,%s', self::encodeURIComponent($sourceMap)); + } elseif ($this->sourceMap == self::SOURCE_MAP_FILE) { + $sourceMapUrl = $sourceMapGenerator->saveMap($sourceMap); } $out .= sprintf('/*# sourceMappingURL=%s */', $sourceMapUrl); diff --git a/src/SourceMap/SourceMapGenerator.php b/src/SourceMap/SourceMapGenerator.php index 3d21f4fe..300a5a08 100644 --- a/src/SourceMap/SourceMapGenerator.php +++ b/src/SourceMap/SourceMapGenerator.php @@ -8,6 +8,8 @@ namespace Leafo\ScssPhp\SourceMap; +use Leafo\ScssPhp\Exception\CompilerException; + class SourceMapGenerator { /** * What version of source map does the generator generate? @@ -94,6 +96,28 @@ public function addMapping($generatedLine, $generatedColumn, $originalLine, $ori $this->sources[$sourceFile] = $sourceFile; } + /** + * Saves the source map to a file + * + * @param string $file The absolute path to a file + * @param string $content The content to write + * @throws Exception If the file could not be saved + */ + public function saveMap($content){ + $file = $this->options['sourceMapWriteTo']; + $dir = dirname($file); + // directory does not exist + if( !is_dir($dir) ){ + // FIXME: create the dir automatically? + throw new CompilerException(sprintf('The directory "%s" does not exist. Cannot save the source map.', $dir)); + } + // FIXME: proper saving, with dir write check! + if(file_put_contents($file, $content) === false){ + throw new CompilerException(sprintf('Cannot save the source map to "%s"', $file)); + } + return $this->options['sourceMapURL']; + } + /** * Generates the JSON source map * From 201b8978143ac913f00ab5a04a11cf2ba7151091 Mon Sep 17 00:00:00 2001 From: dleffler Date: Fri, 15 Dec 2017 12:42:58 -0500 Subject: [PATCH 254/484] Adds cachedCompile function to Server example (taken/modified from less.php) --- example/Server.php | 63 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/example/Server.php b/example/Server.php index 8dc3c7b2..a6e8b986 100644 --- a/example/Server.php +++ b/example/Server.php @@ -416,6 +416,69 @@ public function checkedCachedCompile($in, $out, $force = false) return $css; } + /** + * Execute scssphp on a .scss file or a scssphp cache structure + * + * The scssphp cache structure contains information about a specific + * scss file having been parsed. It can be used as a hint for future + * calls to determine whether or not a rebuild is required. + * + * The cache structure contains two important keys that may be used + * externally: + * + * compiled: The final compiled CSS + * updated: The time (in seconds) the CSS was last compiled + * + * The cache structure is a plain-ol' PHP associative array and can + * be serialized and unserialized without a hitch. + * + * @param mixed $in Input + * @param bool $force Force rebuild? + * @return array scssphp cache structure + */ + public function cachedCompile($in, $force = false) { + // assume no root + $root = null; + + if (is_string($in)) { + $root = $in; + } elseif (is_array($in) and isset($in['root'])) { + if ($force or ! isset($in['files'])) { + // If we are forcing a recompile or if for some reason the + // structure does not contain any file information we should + // specify the root to trigger a rebuild. + $root = $in['root']; + } elseif (isset($in['files']) and is_array($in['files'])) { + foreach ($in['files'] as $fname => $ftime ) { + if (!file_exists($fname) or filemtime($fname) > $ftime) { + // One of the files we knew about previously has changed + // so we should look at our incoming root again. + $root = $in['root']; + break; + } + } + } + } else { + // TODO: Throw an exception? We got neither a string nor something + // that looks like a compatible lessphp cache structure. + return null; + } + + if ($root !== null) { + // If we have a root value which means we should rebuild. + $out = array(); + $out['root'] = $root; + $out['compiled'] = $this->compileFile($root); + $out['files'] = $this->scss->getParsedFiles(); + $out['updated'] = time(); + return $out; + } else { + // No changes, pass back the structure + // we were given initially. + return $in; + } + } + /** * Constructor * From 89d9dabd18f8f068c62d7d0949da0c608ae5ed94 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Mon, 18 Dec 2017 17:32:42 -0500 Subject: [PATCH 255/484] Coding style (phpcs) cleanup --- Makefile | 2 +- example/Server.php | 124 +++++++++--------- scss.inc.php | 3 +- src/Block.php | 2 +- src/Compiler.php | 48 +++++-- src/Formatter.php | 27 ++-- src/Formatter/Compressed.php | 4 +- src/Formatter/Debug.php | 10 +- src/Formatter/Nested.php | 14 +- src/Formatter/OutputBlock.php | 4 +- src/SourceMap/Base64VLQEncoder.php | 136 ++++++++++++-------- src/SourceMap/SourceMapGenerator.php | 186 ++++++++++++++++++--------- src/Util.php | 8 +- tests/ServerTest.php | 1 + 14 files changed, 342 insertions(+), 227 deletions(-) diff --git a/Makefile b/Makefile index 5115941c..fb50ffdb 100644 --- a/Makefile +++ b/Makefile @@ -5,4 +5,4 @@ compat: TEST_SCSS_COMPAT=1 vendor/bin/phpunit --colors tests | tail -2 standard: - vendor/bin/phpcs --standard=PSR2 bin src tests *.php + vendor/bin/phpcs --standard=PSR2 bin src example tests *.php diff --git a/example/Server.php b/example/Server.php index a6e8b986..a8dc0457 100644 --- a/example/Server.php +++ b/example/Server.php @@ -417,67 +417,69 @@ public function checkedCachedCompile($in, $out, $force = false) } /** - * Execute scssphp on a .scss file or a scssphp cache structure - * - * The scssphp cache structure contains information about a specific - * scss file having been parsed. It can be used as a hint for future - * calls to determine whether or not a rebuild is required. - * - * The cache structure contains two important keys that may be used - * externally: - * - * compiled: The final compiled CSS - * updated: The time (in seconds) the CSS was last compiled - * - * The cache structure is a plain-ol' PHP associative array and can - * be serialized and unserialized without a hitch. - * - * @param mixed $in Input - * @param bool $force Force rebuild? - * @return array scssphp cache structure - */ - public function cachedCompile($in, $force = false) { - // assume no root - $root = null; - - if (is_string($in)) { - $root = $in; - } elseif (is_array($in) and isset($in['root'])) { - if ($force or ! isset($in['files'])) { - // If we are forcing a recompile or if for some reason the - // structure does not contain any file information we should - // specify the root to trigger a rebuild. - $root = $in['root']; - } elseif (isset($in['files']) and is_array($in['files'])) { - foreach ($in['files'] as $fname => $ftime ) { - if (!file_exists($fname) or filemtime($fname) > $ftime) { - // One of the files we knew about previously has changed - // so we should look at our incoming root again. - $root = $in['root']; - break; - } - } - } - } else { - // TODO: Throw an exception? We got neither a string nor something - // that looks like a compatible lessphp cache structure. - return null; - } - - if ($root !== null) { - // If we have a root value which means we should rebuild. - $out = array(); - $out['root'] = $root; - $out['compiled'] = $this->compileFile($root); - $out['files'] = $this->scss->getParsedFiles(); - $out['updated'] = time(); - return $out; - } else { - // No changes, pass back the structure - // we were given initially. - return $in; - } - } + * Execute scssphp on a .scss file or a scssphp cache structure + * + * The scssphp cache structure contains information about a specific + * scss file having been parsed. It can be used as a hint for future + * calls to determine whether or not a rebuild is required. + * + * The cache structure contains two important keys that may be used + * externally: + * + * compiled: The final compiled CSS + * updated: The time (in seconds) the CSS was last compiled + * + * The cache structure is a plain-ol' PHP associative array and can + * be serialized and unserialized without a hitch. + * + * @param mixed $in Input + * @param boolean $force Force rebuild? + * + * @return array scssphp cache structure + */ + public function cachedCompile($in, $force = false) + { + // assume no root + $root = null; + + if (is_string($in)) { + $root = $in; + } elseif (is_array($in) and isset($in['root'])) { + if ($force or ! isset($in['files'])) { + // If we are forcing a recompile or if for some reason the + // structure does not contain any file information we should + // specify the root to trigger a rebuild. + $root = $in['root']; + } elseif (isset($in['files']) and is_array($in['files'])) { + foreach ($in['files'] as $fname => $ftime) { + if (! file_exists($fname) or filemtime($fname) > $ftime) { + // One of the files we knew about previously has changed + // so we should look at our incoming root again. + $root = $in['root']; + break; + } + } + } + } else { + // TODO: Throw an exception? We got neither a string nor something + // that looks like a compatible lessphp cache structure. + return null; + } + + if ($root !== null) { + // If we have a root value which means we should rebuild. + $out = array(); + $out['root'] = $root; + $out['compiled'] = $this->compileFile($root); + $out['files'] = $this->scss->getParsedFiles(); + $out['updated'] = time(); + return $out; + } else { + // No changes, pass back the structure + // we were given initially. + return $in; + } + } /** * Constructor diff --git a/scss.inc.php b/scss.inc.php index 77303ba3..13c84bf5 100644 --- a/scss.inc.php +++ b/scss.inc.php @@ -11,7 +11,7 @@ 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/RangeException.php'; //exp + include_once __DIR__ . '/src/Exception/RangeException.php'; include_once __DIR__ . '/src/Exception/ServerException.php'; include_once __DIR__ . '/src/Formatter.php'; include_once __DIR__ . '/src/Formatter/Compact.php'; @@ -24,7 +24,6 @@ include_once __DIR__ . '/src/Node.php'; include_once __DIR__ . '/src/Node/Number.php'; include_once __DIR__ . '/src/Parser.php'; -// include_once __DIR__ . '/src/example/Server.php'; include_once __DIR__ . '/src/SourceMap/Base64VLQEncoder.php'; include_once __DIR__ . '/src/SourceMap/SourceMapGenerator.php'; include_once __DIR__ . '/src/Type.php'; diff --git a/src/Block.php b/src/Block.php index 87cfd6d8..fc21e5a4 100644 --- a/src/Block.php +++ b/src/Block.php @@ -29,7 +29,7 @@ class Block public $parent; /** - * @var string; + * @var string */ public $sourceName; diff --git a/src/Compiler.php b/src/Compiler.php index 2dc50531..64399b2a 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -128,13 +128,17 @@ class Compiler protected $sourceMap = self::SOURCE_MAP_NONE; protected $sourceMapOptions = []; - /** @var string|Formatter */ + /** + * @var string|\Leafo\ScssPhp\Formatter + */ protected $formatter = 'Leafo\ScssPhp\Formatter\Nested'; protected $rootEnv; protected $rootBlock; - /** @var Environment */ + /** + * @var \Leafo\ScssPhp\Compiler\Environment + */ protected $env; protected $scope; protected $storeEnv; @@ -202,22 +206,30 @@ public function compile($code, $path = null) $this->popEnv(); $sourceMapGenerator = null; - if($this->sourceMap && $this->sourceMap !== self::SOURCE_MAP_NONE) { + + if ($this->sourceMap && $this->sourceMap !== self::SOURCE_MAP_NONE) { $sourceMapGenerator = new SourceMapGenerator($this->sourceMapOptions); } + $out = $this->formatter->format($this->scope, $sourceMapGenerator); - if(!empty($out) && $this->sourceMap && $this->sourceMap !== self::SOURCE_MAP_NONE) { - $sourceMap = $sourceMapGenerator->generateJson(); + if (! empty($out) && $this->sourceMap && $this->sourceMap !== self::SOURCE_MAP_NONE) { + $sourceMap = $sourceMapGenerator->generateJson(); $sourceMapUrl = null; - if($this->sourceMap == self::SOURCE_MAP_INLINE) { - $sourceMapUrl = sprintf('data:application/json,%s', self::encodeURIComponent($sourceMap)); - } elseif ($this->sourceMap == self::SOURCE_MAP_FILE) { - $sourceMapUrl = $sourceMapGenerator->saveMap($sourceMap); + + switch ($this->sourceMap) { + case self::SOURCE_MAP_INLINE: + $sourceMapUrl = sprintf('data:application/json,%s', self::encodeURIComponent($sourceMap)); + break; + + case self::SOURCE_MAP_FILE: + $sourceMapUrl = $sourceMapGenerator->saveMap($sourceMap); + break; } $out .= sprintf('/*# sourceMappingURL=%s */', $sourceMapUrl); } + return $out; } @@ -1110,7 +1122,8 @@ protected function evalSelectors($selectors) /** * @param array $sourceMapOptions */ - public function setSourceMapOptions($sourceMapOptions) { + public function setSourceMapOptions($sourceMapOptions) + { $this->sourceMapOptions = $sourceMapOptions; } @@ -1959,7 +1972,7 @@ protected function isTruthy($value) * * @param string $value * - * @return bool + * @return boolean */ protected function isImmediateRelationshipCombinator($value) { @@ -3338,9 +3351,14 @@ public function setLineNumberStyle($lineNumberStyle) } /** - * @param int $sourceMap + * Set source map option + * + * @api + * + * @param integer $sourceMap */ - public function setSourceMap($sourceMap) { + public function setSourceMap($sourceMap) + { $this->sourceMap = $sourceMap; } @@ -5302,8 +5320,10 @@ protected function libInspect($args) return $args[0]; } - public static function encodeURIComponent($string){ + public static function encodeURIComponent($string) + { $revert = array('%21' => '!', '%2A' => '*', '%27' => "'", '%28' => '(', '%29' => ')'); + return strtr(rawurlencode($string), $revert); } } diff --git a/src/Formatter.php b/src/Formatter.php index 7939113b..68d20c88 100644 --- a/src/Formatter.php +++ b/src/Formatter.php @@ -62,18 +62,18 @@ abstract class Formatter public $keepSemicolons; /** - * @var OutputBlock; + * @var \Leafo\ScssPhp\Formatter\OutputBlock */ protected $currentBlock; /** - * @var int + * @var integer */ protected $currentLine; /** - * @var int; + * @var integer */ protected $currentColumn; @@ -231,13 +231,14 @@ protected function block(OutputBlock $block) */ public function format(OutputBlock $block, SourceMapGenerator $sourceMapGenerator = null) { - if($sourceMapGenerator) { + $this->sourceMapGenerator = null; + + if ($sourceMapGenerator) { $this->currentLine = 1; $this->currentColumn = 0; $this->sourceMapGenerator = $sourceMapGenerator; - } else { - $this->sourceMapGenerator = null; } + ob_start(); $this->block($block); @@ -248,10 +249,11 @@ public function format(OutputBlock $block, SourceMapGenerator $sourceMapGenerato } /** - * @param $str + * @param string $str */ - protected function write($str) { - if($this->sourceMapGenerator) { + protected function write($str) + { + if ($this->sourceMapGenerator) { $this->sourceMapGenerator->addMapping( $this->currentLine, $this->currentColumn, @@ -265,11 +267,8 @@ protected function write($str) { $this->currentLine += $lineCount-1; $lastLine = array_pop($lines); - if($lineCount == 1) { - $this->currentColumn += mb_strlen($lastLine); - } else { - $this->currentColumn = mb_strlen($lastLine); - } + + $this->currentColumn = ($lineCount === 1 ? $this->currentColumn : 0) + strlen($lastLine); } echo $str; diff --git a/src/Formatter/Compressed.php b/src/Formatter/Compressed.php index 69a67850..21cbf28e 100644 --- a/src/Formatter/Compressed.php +++ b/src/Formatter/Compressed.php @@ -53,10 +53,10 @@ public function blockLines(OutputBlock $block) } } - $this->write( $inner . implode($glue, $block->lines)); + $this->write($inner . implode($glue, $block->lines)); if (! empty($block->children)) { - $this->write( $this->break); + $this->write($this->break); } } } diff --git a/src/Formatter/Debug.php b/src/Formatter/Debug.php index cb29641a..b31c8c7a 100644 --- a/src/Formatter/Debug.php +++ b/src/Formatter/Debug.php @@ -58,7 +58,7 @@ protected function blockLines(OutputBlock $block) } foreach ($block->lines as $index => $line) { - $this->write( "{$indent}block->lines[{$index}]: $line\n"); + $this->write("{$indent}block->lines[{$index}]: $line\n"); } } @@ -70,13 +70,13 @@ protected function blockSelectors(OutputBlock $block) $indent = $this->indentStr(); if (empty($block->selectors)) { - $this->write( "{$indent}block->selectors: []\n"); + $this->write("{$indent}block->selectors: []\n"); return; } foreach ($block->selectors as $index => $selector) { - $this->write( "{$indent}block->selectors[{$index}]: $selector\n"); + $this->write("{$indent}block->selectors[{$index}]: $selector\n"); } } @@ -88,7 +88,7 @@ protected function blockChildren(OutputBlock $block) $indent = $this->indentStr(); if (empty($block->children)) { - $this->write( "{$indent}block->children: []\n"); + $this->write("{$indent}block->children: []\n"); return; } @@ -109,7 +109,7 @@ protected function block(OutputBlock $block) { $indent = $this->indentStr(); - $this->write( "{$indent}block->type: {$block->type}\n" . + $this->write("{$indent}block->type: {$block->type}\n" . "{$indent}block->depth: {$block->depth}\n"); $this->currentBlock = $block; diff --git a/src/Formatter/Nested.php b/src/Formatter/Nested.php index 48e19ef7..7573a596 100644 --- a/src/Formatter/Nested.php +++ b/src/Formatter/Nested.php @@ -65,10 +65,10 @@ protected function blockLines(OutputBlock $block) } } - $this->write( $inner . implode($glue, $block->lines)); + $this->write($inner . implode($glue, $block->lines)); if (! empty($block->children)) { - $this->write( $this->break); + $this->write($this->break); } } @@ -79,7 +79,7 @@ protected function blockSelectors(OutputBlock $block) { $inner = $this->indentStr(); - $this->write( $inner + $this->write($inner . implode($this->tagSeparator, $block->selectors) . $this->open . $this->break); } @@ -93,13 +93,13 @@ protected function blockChildren(OutputBlock $block) $this->block($child); if ($i < count($block->children) - 1) { - $this->write( $this->break); + $this->write($this->break); if (isset($block->children[$i + 1])) { $next = $block->children[$i + 1]; if ($next->depth === max($block->depth, 1) && $child->depth >= $next->depth) { - $this->write( $this->break); + $this->write($this->break); } } } @@ -141,11 +141,11 @@ protected function block(OutputBlock $block) if (! empty($block->selectors)) { $this->indentLevel--; - $this->write( $this->close); + $this->write($this->close); } if ($block->type === 'root') { - $this->write( $this->break); + $this->write($this->break); } } diff --git a/src/Formatter/OutputBlock.php b/src/Formatter/OutputBlock.php index 7d233a8d..e4092175 100644 --- a/src/Formatter/OutputBlock.php +++ b/src/Formatter/OutputBlock.php @@ -54,12 +54,12 @@ class OutputBlock public $sourceName; /** - * @var int + * @var integer */ public $sourceLine; /** - * @var int + * @var integer */ public $sourceColumn; } diff --git a/src/SourceMap/Base64VLQEncoder.php b/src/SourceMap/Base64VLQEncoder.php index 14652fbe..d1d1ed2a 100644 --- a/src/SourceMap/Base64VLQEncoder.php +++ b/src/SourceMap/Base64VLQEncoder.php @@ -1,140 +1,167 @@ + * @author Nicolas FRANÇOIS + */ +class Base64VLQEncoder +{ /** * Shift * * @var integer */ private $shift = 5; + /** * Mask * * @var integer */ private $mask = 0x1F; // == (1 << shift) == 0b00011111 + /** * Continuation bit * * @var integer */ private $continuationBit = 0x20; // == (mask - 1 ) == 0b00100000 + /** * Char to integer map * * @var array */ private $charToIntMap = array( - 'A' => 0, 'B' => 1, 'C' => 2, 'D' => 3, 'E' => 4, 'F' => 5, 'G' => 6, - 'H' => 7,'I' => 8, 'J' => 9, 'K' => 10, 'L' => 11, 'M' => 12, 'N' => 13, - 'O' => 14, 'P' => 15, 'Q' => 16, 'R' => 17, 'S' => 18, 'T' => 19, 'U' => 20, - 'V' => 21, 'W' => 22, 'X' => 23, 'Y' => 24, 'Z' => 25, 'a' => 26, 'b' => 27, - 'c' => 28, 'd' => 29, 'e' => 30, 'f' => 31, 'g' => 32, 'h' => 33, 'i' => 34, - 'j' => 35, 'k' => 36, 'l' => 37, 'm' => 38, 'n' => 39, 'o' => 40, 'p' => 41, - 'q' => 42, 'r' => 43, 's' => 44, 't' => 45, 'u' => 46, 'v' => 47, 'w' => 48, - 'x' => 49, 'y' => 50, 'z' => 51, 0 => 52, 1 => 53, 2 => 54, 3 => 55, 4 => 56, - 5 => 57, 6 => 58, 7 => 59, 8 => 60, 9 => 61, '+' => 62, '/' => 63, + 'A' => 0, 'B' => 1, 'C' => 2, 'D' => 3, 'E' => 4, 'F' => 5, 'G' => 6, 'H' => 7, + 'I' => 8, 'J' => 9, 'K' => 10, 'L' => 11, 'M' => 12, 'N' => 13, 'O' => 14, 'P' => 15, + 'Q' => 16, 'R' => 17, 'S' => 18, 'T' => 19, 'U' => 20, 'V' => 21, 'W' => 22, 'X' => 23, + 'Y' => 24, 'Z' => 25, 'a' => 26, 'b' => 27, 'c' => 28, 'd' => 29, 'e' => 30, 'f' => 31, + 'g' => 32, 'h' => 33, 'i' => 34, 'j' => 35, 'k' => 36, 'l' => 37, 'm' => 38, 'n' => 39, + 'o' => 40, 'p' => 41, 'q' => 42, 'r' => 43, 's' => 44, 't' => 45, 'u' => 46, 'v' => 47, + 'w' => 48, 'x' => 49, 'y' => 50, 'z' => 51, 0 => 52, 1 => 53, 2 => 54, 3 => 55, + 4 => 56, 5 => 57, 6 => 58, 7 => 59, 8 => 60, 9 => 61, '+' => 62, '/' => 63, ); + /** * Integer to char map * * @var array */ private $intToCharMap = array( - 0 => 'A', 1 => 'B', 2 => 'C', 3 => 'D', 4 => 'E', 5 => 'F', 6 => 'G', - 7 => 'H', 8 => 'I', 9 => 'J', 10 => 'K', 11 => 'L', 12 => 'M', 13 => 'N', - 14 => 'O', 15 => 'P', 16 => 'Q', 17 => 'R', 18 => 'S', 19 => 'T', 20 => 'U', - 21 => 'V', 22 => 'W', 23 => 'X', 24 => 'Y', 25 => 'Z', 26 => 'a', 27 => 'b', - 28 => 'c', 29 => 'd', 30 => 'e', 31 => 'f', 32 => 'g', 33 => 'h', 34 => 'i', - 35 => 'j', 36 => 'k', 37 => 'l', 38 => 'm', 39 => 'n', 40 => 'o', 41 => 'p', - 42 => 'q', 43 => 'r', 44 => 's', 45 => 't', 46 => 'u', 47 => 'v', 48 => 'w', - 49 => 'x', 50 => 'y', 51 => 'z', 52 => '0', 53 => '1', 54 => '2', 55 => '3', - 56 => '4', 57 => '5', 58 => '6', 59 => '7', 60 => '8', 61 => '9', 62 => '+', - 63 => '/', + 0 => 'A', 1 => 'B', 2 => 'C', 3 => 'D', 4 => 'E', 5 => 'F', 6 => 'G', 7 => 'H', + 8 => 'I', 9 => 'J', 10 => 'K', 11 => 'L', 12 => 'M', 13 => 'N', 14 => 'O', 15 => 'P', + 16 => 'Q', 17 => 'R', 18 => 'S', 19 => 'T', 20 => 'U', 21 => 'V', 22 => 'W', 23 => 'X', + 24 => 'Y', 25 => 'Z', 26 => 'a', 27 => 'b', 28 => 'c', 29 => 'd', 30 => 'e', 31 => 'f', + 32 => 'g', 33 => 'h', 34 => 'i', 35 => 'j', 36 => 'k', 37 => 'l', 38 => 'm', 39 => 'n', + 40 => 'o', 41 => 'p', 42 => 'q', 43 => 'r', 44 => 's', 45 => 't', 46 => 'u', 47 => 'v', + 48 => 'w', 49 => 'x', 50 => 'y', 51 => 'z', 52 => '0', 53 => '1', 54 => '2', 55 => '3', + 56 => '4', 57 => '5', 58 => '6', 59 => '7', 60 => '8', 61 => '9', 62 => '+', 63 => '/', ); + /** * Constructor */ - public function __construct(){ + public function __construct() + { // I leave it here for future reference - // foreach(str_split('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/') as $i => $char) + // foreach (str_split('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/') as $i => $char) // { - // $this->charToIntMap[$char] = $i; - // $this->intToCharMap[$i] = $char; + // $this->charToIntMap[$char] = $i; + // $this->intToCharMap[$i] = $char; // } } + /** * Convert from a two-complement value to a value where the sign bit is - * is placed in the least significant bit. For example, as decimals: - * 1 becomes 2 (10 binary), -1 becomes 3 (11 binary) - * 2 becomes 4 (100 binary), -2 becomes 5 (101 binary) + * is placed in the least significant bit. For example, as decimals: + * 1 becomes 2 (10 binary), -1 becomes 3 (11 binary) + * 2 becomes 4 (100 binary), -2 becomes 5 (101 binary) * We generate the value for 32 bit machines, hence -2147483648 becomes 1, not 4294967297, * even on a 64 bit machine. * @param string $aValue */ - public function toVLQSigned($aValue){ + public function toVLQSigned($aValue) + { return 0xffffffff & ($aValue < 0 ? ((-$aValue) << 1) + 1 : ($aValue << 1) + 0); } + /** * Convert to a two-complement value from a value where the sign bit is * is placed in the least significant bit. For example, as decimals: - * 2 (10 binary) becomes 1, 3 (11 binary) becomes -1 - * 4 (100 binary) becomes 2, 5 (101 binary) becomes -2 + * 2 (10 binary) becomes 1, 3 (11 binary) becomes -1 + * 4 (100 binary) becomes 2, 5 (101 binary) becomes -2 * We assume that the value was generated with a 32 bit machine in mind. * Hence - * 1 becomes -2147483648 + * 1 becomes -2147483648 * even on a 64 bit machine. * @param integer $aValue */ - public function fromVLQSigned($aValue){ + public function fromVLQSigned($aValue) + { return $aValue & 1 ? $this->zeroFill(~$aValue + 2, 1) | (-1 - 0x7fffffff) : $this->zeroFill($aValue, 1); } + /** * Return the base 64 VLQ encoded value. * * @param string $aValue The value to encode * @return string The encoded value */ - public function encode($aValue){ + public function encode($aValue) + { $encoded = ''; $vlq = $this->toVLQSigned($aValue); - do - { + + do { $digit = $vlq & $this->mask; $vlq = $this->zeroFill($vlq, $this->shift); - if($vlq > 0){ + + if ($vlq > 0) { $digit |= $this->continuationBit; } + $encoded .= $this->base64Encode($digit); - } while($vlq > 0); + } while ($vlq > 0); + return $encoded; } + /** * Return the value decoded from base 64 VLQ. * * @param string $encoded The encoded value to decode * @return integer The decoded value */ - public function decode($encoded){ + public function decode($encoded) + { $vlq = 0; $i = 0; - do - { + + do { $digit = $this->base64Decode($encoded[$i]); $vlq |= ($digit & $this->mask) << ($i * $this->shift); $i++; - } while($digit & $this->continuationBit); + } while ($digit & $this->continuationBit); + return $this->fromVLQSigned($vlq); } + /** * Right shift with zero fill. * @@ -142,9 +169,11 @@ public function decode($encoded){ * @param integer $b number of bits to shift * @return integer */ - public function zeroFill($a, $b){ + public function zeroFill($a, $b) + { return ($a >= 0) ? ($a >> $b) : ($a >> $b) & (PHP_INT_MAX >> ($b - 1)); } + /** * Encode single 6-bit digit as base64. * @@ -152,12 +181,15 @@ public function zeroFill($a, $b){ * @return string * @throws Exception If the number is invalid */ - public function base64Encode($number){ - if($number < 0 || $number > 63){ + public function base64Encode($number) + { + if ($number < 0 || $number > 63) { throw new Exception(sprintf('Invalid number "%s" given. Must be between 0 and 63.', $number)); } + return $this->intToCharMap[$number]; } + /** * Decode single 6-bit digit from base64 * @@ -165,10 +197,12 @@ public function base64Encode($number){ * @return number * @throws Exception If the number is invalid */ - public function base64Decode($char){ - if(!array_key_exists($char, $this->charToIntMap)){ + public function base64Decode($char) + { + if (! array_key_exists($char, $this->charToIntMap)) { throw new Exception(sprintf('Invalid base 64 digit "%s" given.', $char)); } + return $this->charToIntMap[$char]; } } diff --git a/src/SourceMap/SourceMapGenerator.php b/src/SourceMap/SourceMapGenerator.php index 300a5a08..31905b10 100644 --- a/src/SourceMap/SourceMapGenerator.php +++ b/src/SourceMap/SourceMapGenerator.php @@ -1,20 +1,33 @@ + * @author Nicolas FRANÇOIS + */ +class SourceMapGenerator +{ /** * What version of source map does the generator generate? */ const VERSION = 3; + /** * Array of default options * @@ -25,16 +38,22 @@ class SourceMapGenerator { // on a server or removing repeated values in the 'sources' entry. // This value is prepended to the individual entries in the 'source' field. 'sourceRoot' => '', + // an optional name of the generated code that this source map is associated with. 'sourceMapFilename' => null, + // url of the map 'sourceMapURL' => null, + // absolute path to a file to write the map to 'sourceMapWriteTo' => null, + // output source contents? 'outputSourceFiles' => false, + // base path for filename normalization 'sourceMapRootpath' => '', + // base path for filename normalization 'sourceMapBasepath' => '' ); @@ -42,9 +61,10 @@ class SourceMapGenerator { /** * The base64 VLQ encoder * - * @var Base64VLQEncoder + * @var \Leafo\ScssPhp\SourceMap\Base64VLQEncoder */ protected $encoder; + /** * Array of mappings * @@ -58,6 +78,7 @@ class SourceMapGenerator { * @var array */ protected $contentsMap = array(); + /** * File to content map * @@ -65,12 +86,14 @@ class SourceMapGenerator { */ protected $sources = array(); protected $source_keys = array(); + /** * @var array */ private $options; - public function __construct(array $options = []) { + public function __construct(array $options = []) + { $this->options = array_merge($this->defaultOptions, $options); $this->encoder = new Base64VLQEncoder(); } @@ -84,7 +107,8 @@ public function __construct(array $options = []) { * @param integer $originalColumn The column number in original file * @param string $sourceFile The original source file */ - public function addMapping($generatedLine, $generatedColumn, $originalLine, $originalColumn, $sourceFile) { + public function addMapping($generatedLine, $generatedColumn, $originalLine, $originalColumn, $sourceFile) + { $this->mappings[] = array( 'generated_line' => $generatedLine, 'generated_column' => $generatedColumn, @@ -97,26 +121,30 @@ public function addMapping($generatedLine, $generatedColumn, $originalLine, $ori } /** - * Saves the source map to a file - * - * @param string $file The absolute path to a file - * @param string $content The content to write - * @throws Exception If the file could not be saved - */ - public function saveMap($content){ - $file = $this->options['sourceMapWriteTo']; - $dir = dirname($file); - // directory does not exist - if( !is_dir($dir) ){ - // FIXME: create the dir automatically? - throw new CompilerException(sprintf('The directory "%s" does not exist. Cannot save the source map.', $dir)); - } - // FIXME: proper saving, with dir write check! - if(file_put_contents($file, $content) === false){ - throw new CompilerException(sprintf('Cannot save the source map to "%s"', $file)); - } - return $this->options['sourceMapURL']; - } + * Saves the source map to a file + * + * @param string $file The absolute path to a file + * @param string $content The content to write + * @throws Exception If the file could not be saved + */ + public function saveMap($content) + { + $file = $this->options['sourceMapWriteTo']; + $dir = dirname($file); + + // directory does not exist + if (! is_dir($dir)) { + // FIXME: create the dir automatically? + throw new CompilerException(sprintf('The directory "%s" does not exist. Cannot save the source map.', $dir)); + } + + // FIXME: proper saving, with dir write check! + if (file_put_contents($file, $content) === false) { + throw new CompilerException(sprintf('Cannot save the source map to "%s"', $file)); + } + + return $this->options['sourceMapURL']; + } /** * Generates the JSON source map @@ -124,40 +152,44 @@ public function saveMap($content){ * @return string * @see https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit# */ - public function generateJson() { + public function generateJson() + { $sourceMap = array(); - $mappings = $this->generateMappings(); + $mappings = $this->generateMappings(); + // File version (always the first entry in the object) and must be a positive integer. $sourceMap['version'] = self::VERSION; + // An optional name of the generated code that this source map is associated with. $file = $this->options['sourceMapFilename']; - if($file) { + + if ($file) { $sourceMap['file'] = $file; } - // An optional source root, useful for relocating source files on a server or removing repeated values in the 'sources' entry. This value is prepended to the individual entries in the 'source' field. + + // An optional source root, useful for relocating source files on a server or removing repeated values in the + // 'sources' entry. This value is prepended to the individual entries in the 'source' field. $root = $this->options['sourceRoot']; - if($root) { - $sourceMap['sourceRoot'] = $root; - } - // A list of original sources used by the 'mappings' entry. - $sourceMap['sources'] = array(); - foreach($this->sources as $source_uri => $source_filename) { - $sourceMap['sources'][] = $this->normalizeFilename($source_filename); + + if ($root) { + $sourceMap['names'] = array(); } - // A list of symbol names used by the 'mappings' entry. - $sourceMap['names'] = array(); + // A string with the encoded mapping data. $sourceMap['mappings'] = $mappings; - if($this->options['outputSourceFiles']) { + + if ($this->options['outputSourceFiles']) { // An optional list of source content, useful when the 'source' can't be hosted. // The contents are listed in the same order as the sources above. // 'null' may be used if some original sources should be retrieved by name. $sourceMap['sourcesContent'] = $this->getSourcesContent(); } + // less.js compat fixes - if(count($sourceMap['sources']) && empty($sourceMap['sourceRoot'])) { + if (count($sourceMap['sources']) && empty($sourceMap['sourceRoot'])) { unset($sourceMap['sourceRoot']); } + return json_encode($sourceMap); } @@ -166,14 +198,18 @@ public function generateJson() { * * @return array|null */ - protected function getSourcesContent() { - if(empty($this->sources)) { + protected function getSourcesContent() + { + if (empty($this->sources)) { return null; } + $content = array(); - foreach($this->sources as $sourceFile) { + + foreach ($this->sources as $sourceFile) { $content[] = file_get_contents($sourceFile); } + return $content; } @@ -182,31 +218,41 @@ protected function getSourcesContent() { * * @return string */ - public function generateMappings() { - if(!count($this->mappings)) { + public function generateMappings() + { + if (! count($this->mappings)) { return ''; } + $this->source_keys = array_flip(array_keys($this->sources)); + // group mappings by generated line number. $groupedMap = $groupedMapEncoded = array(); - foreach($this->mappings as $m) { + + foreach ($this->mappings as $m) { $groupedMap[$m['generated_line']][] = $m; } + ksort($groupedMap); $lastGeneratedLine = $lastOriginalIndex = $lastOriginalLine = $lastOriginalColumn = 0; - foreach($groupedMap as $lineNumber => $line_map) { - while(++$lastGeneratedLine < $lineNumber) { + + foreach ($groupedMap as $lineNumber => $line_map) { + while (++$lastGeneratedLine < $lineNumber) { $groupedMapEncoded[] = ';'; } + $lineMapEncoded = array(); $lastGeneratedColumn = 0; - foreach($line_map as $m) { + + foreach ($line_map as $m) { $mapEncoded = $this->encoder->encode($m['generated_column'] - $lastGeneratedColumn); $lastGeneratedColumn = $m['generated_column']; + // find the index - if($m['source_file']) { + if ($m['source_file']) { $index = $this->findFileIndex($m['source_file']); - if($index !== false) { + + if ($index !== false) { $mapEncoded .= $this->encoder->encode($index - $lastOriginalIndex); $lastOriginalIndex = $index; // lines are stored 0-based in SourceMap spec version 3 @@ -216,10 +262,13 @@ public function generateMappings() { $lastOriginalColumn = $m['original_column']; } } + $lineMapEncoded[] = $mapEncoded; } + $groupedMapEncoded[] = implode(',', $lineMapEncoded) . ';'; } + return rtrim(implode($groupedMapEncoded), ';'); } @@ -227,39 +276,50 @@ public function generateMappings() { * Finds the index for the filename * * @param string $filename + * * @return integer|false */ - protected function findFileIndex($filename) { + protected function findFileIndex($filename) + { return $this->source_keys[$filename]; } - protected function normalizeFilename($filename) { + protected function normalizeFilename($filename) + { $filename = $this->fixWindowsPath($filename); $rootpath = $this->options['sourceMapRootpath']; $basePath = $this->options['sourceMapBasepath']; + // "Trim" the 'sourceMapBasepath' from the output filename. - if(strpos($filename, $basePath) === 0) { + if (strpos($filename, $basePath) === 0) { $filename = substr($filename, strlen($basePath)); } + // Remove extra leading path separators. - if(strpos($filename, '\\') === 0 || strpos($filename, '/') === 0) { + if (strpos($filename, '\\') === 0 || strpos($filename, '/') === 0) { $filename = substr($filename, 1); } + return $rootpath . $filename; } /** - * fix windows paths - * @param string $path - * @param bool $addEndSlash + * Fix windows paths + * + * @param string $path + * @param boolean $addEndSlash + * * @return string */ - public function fixWindowsPath($path, $addEndSlash = false) { + public function fixWindowsPath($path, $addEndSlash = false) + { $slash = ($addEndSlash) ? '/' : ''; - if(!empty($path)) { + + if (! empty($path)) { $path = str_replace('\\', '/', $path); $path = rtrim($path, '/') . $slash; } + return $path; } } diff --git a/src/Util.php b/src/Util.php index b2a05db8..f5aca7a4 100644 --- a/src/Util.php +++ b/src/Util.php @@ -25,10 +25,10 @@ class Util * Asserts that `value` falls within `range` (inclusive), leaving * room for slight floating-point errors. * - * @param string $name The name of the value. Used in the error message. - * @param Range $range Range of values. - * @param array $value The value to check. - * @param string $unit The unit of the value. Used in error reporting. + * @param string $name The name of the value. Used in the error message. + * @param \Leafo\ScssPhp\Base\Range $range Range of values. + * @param array $value The value to check. + * @param string $unit The unit of the value. Used in error reporting. * * @return mixed `value` adjusted to fall within range, if it was outside by a floating-point margin. * diff --git a/tests/ServerTest.php b/tests/ServerTest.php index fcaea917..290d8fbf 100644 --- a/tests/ServerTest.php +++ b/tests/ServerTest.php @@ -8,6 +8,7 @@ * * @link http://leafo.github.io/scssphp */ + namespace Leafo\ScssPhp\Tests; require_once __DIR__ . '/../example/Server.php'; From 266913f8bf86b0e6a144e75225c82dc12652bedc Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 19 Dec 2017 16:24:54 -0500 Subject: [PATCH 256/484] More coding style tweaks --- src/Compiler.php | 57 +++++++++++++--------------- src/Formatter.php | 8 ++-- src/Formatter/Nested.php | 1 + src/Parser.php | 2 +- src/SourceMap/Base64VLQEncoder.php | 19 +++++++--- src/SourceMap/SourceMapGenerator.php | 14 ++++--- src/Util.php | 16 +++++++- 7 files changed, 69 insertions(+), 48 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 64399b2a..f12cf7ee 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -65,9 +65,9 @@ class Compiler const WITH_SUPPORTS = 4; const WITH_ALL = 7; - const SOURCE_MAP_NONE = 0; + const SOURCE_MAP_NONE = 0; const SOURCE_MAP_INLINE = 1; - const SOURCE_MAP_FILE = 2; + const SOURCE_MAP_FILE = 2; /** * @var array @@ -219,7 +219,7 @@ public function compile($code, $path = null) switch ($this->sourceMap) { case self::SOURCE_MAP_INLINE: - $sourceMapUrl = sprintf('data:application/json,%s', self::encodeURIComponent($sourceMap)); + $sourceMapUrl = sprintf('data:application/json,%s', Util::encodeURIComponent($sourceMap)); break; case self::SOURCE_MAP_FILE: @@ -305,14 +305,14 @@ protected function pushExtends($target, $origin, $block) protected function makeOutputBlock($type, $selectors = null) { $out = new OutputBlock; - $out->type = $type; - $out->lines = []; - $out->children = []; - $out->parent = $this->scope; - $out->selectors = $selectors; - $out->depth = $this->env->depth; - $out->sourceName = $this->env->block->sourceName; - $out->sourceLine = $this->env->block->sourceLine; + $out->type = $type; + $out->lines = []; + $out->children = []; + $out->parent = $this->scope; + $out->selectors = $selectors; + $out->depth = $this->env->depth; + $out->sourceName = $this->env->block->sourceName; + $out->sourceLine = $this->env->block->sourceLine; $out->sourceColumn = $this->env->block->sourceColumn; return $out; @@ -696,7 +696,7 @@ protected function compileMedia(Block $media) if ($needsWrap) { $wrapped = new Block; - $wrapped->sourceName = $media->sourceName; + $wrapped->sourceName = $media->sourceName; $wrapped->sourceIndex = $media->sourceIndex; $wrapped->sourceLine = $media->sourceLine; $wrapped->sourceColumn = $media->sourceColumn; @@ -1119,14 +1119,6 @@ protected function evalSelectors($selectors) return $selectors; } - /** - * @param array $sourceMapOptions - */ - public function setSourceMapOptions($sourceMapOptions) - { - $this->sourceMapOptions = $sourceMapOptions; - } - /** * Evaluate selector * @@ -3351,7 +3343,7 @@ public function setLineNumberStyle($lineNumberStyle) } /** - * Set source map option + * Enable/disable source maps * * @api * @@ -3362,6 +3354,18 @@ public function setSourceMap($sourceMap) $this->sourceMap = $sourceMap; } + /** + * Set source map options + * + * @api + * + * @param array $sourceMapOptions + */ + public function setSourceMapOptions($sourceMapOptions) + { + $this->sourceMapOptions = $sourceMapOptions; + } + /** * Register function * @@ -5188,7 +5192,7 @@ protected function libToLowerCase($args) $string = $this->coerceString($args[0]); $stringContent = $this->compileStringContent($string); - $string[2] = [mb_strtolower($stringContent)]; + $string[2] = [function_exists('mb_strtolower') ? mb_strtolower($stringContent) : strtolower($stringContent)]; return $string; } @@ -5199,7 +5203,7 @@ protected function libToUpperCase($args) $string = $this->coerceString($args[0]); $stringContent = $this->compileStringContent($string); - $string[2] = [mb_strtoupper($stringContent)]; + $string[2] = [function_exists('mb_strtoupper') ? mb_strtoupper($stringContent) : strtoupper($stringContent)]; return $string; } @@ -5319,11 +5323,4 @@ protected function libInspect($args) return $args[0]; } - - public static function encodeURIComponent($string) - { - $revert = array('%21' => '!', '%2A' => '*', '%27' => "'", '%28' => '(', '%29' => ')'); - - return strtr(rawurlencode($string), $revert); - } } diff --git a/src/Formatter.php b/src/Formatter.php index 68d20c88..4d2fdd08 100644 --- a/src/Formatter.php +++ b/src/Formatter.php @@ -66,7 +66,6 @@ abstract class Formatter */ protected $currentBlock; - /** * @var integer */ @@ -78,7 +77,7 @@ abstract class Formatter protected $currentColumn; /** - * @var SourceMapGenerator + * @var \Leafo\ScssPhp\SourceMap\SourceMapGenerator */ protected $sourceMapGenerator; @@ -223,11 +222,10 @@ protected function block(OutputBlock $block) * * @api * - * @param \Leafo\ScssPhp\Formatter\OutputBlock $block An abstract syntax tree + * @param \Leafo\ScssPhp\Formatter\OutputBlock $block An abstract syntax tree + * @param \Leafo\ScssPhp\SourceMap\SourceMapGenerator|null $sourceMapGenerator Optional source map generator * - * @param SourceMapGenerator|null $sourceMapGenerator * @return string - * @internal param bool $collectSourceMap */ public function format(OutputBlock $block, SourceMapGenerator $sourceMapGenerator = null) { diff --git a/src/Formatter/Nested.php b/src/Formatter/Nested.php index 7573a596..626fec13 100644 --- a/src/Formatter/Nested.php +++ b/src/Formatter/Nested.php @@ -12,6 +12,7 @@ namespace Leafo\ScssPhp\Formatter; use Leafo\ScssPhp\Formatter; +use Leafo\ScssPhp\Formatter\OutputBlock; /** * Nested formatter diff --git a/src/Parser.php b/src/Parser.php index cc0a51df..ccac0d3f 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -708,7 +708,7 @@ protected function pushBlock($selectors, $pos = 0) list($line, $column) = $this->getSourcePosition($pos); $b = new Block; - $b->sourceName = $this->sourceName; + $b->sourceName = $this->sourceName; $b->sourceLine = $line; $b->sourceColumn = $column; $b->sourceIndex = $this->sourceIndex; diff --git a/src/SourceMap/Base64VLQEncoder.php b/src/SourceMap/Base64VLQEncoder.php index d1d1ed2a..435a7c60 100644 --- a/src/SourceMap/Base64VLQEncoder.php +++ b/src/SourceMap/Base64VLQEncoder.php @@ -94,6 +94,7 @@ public function __construct() * 2 becomes 4 (100 binary), -2 becomes 5 (101 binary) * We generate the value for 32 bit machines, hence -2147483648 becomes 1, not 4294967297, * even on a 64 bit machine. + * * @param string $aValue */ public function toVLQSigned($aValue) @@ -110,6 +111,7 @@ public function toVLQSigned($aValue) * Hence * 1 becomes -2147483648 * even on a 64 bit machine. + * * @param integer $aValue */ public function fromVLQSigned($aValue) @@ -121,6 +123,7 @@ public function fromVLQSigned($aValue) * Return the base 64 VLQ encoded value. * * @param string $aValue The value to encode + * * @return string The encoded value */ public function encode($aValue) @@ -146,6 +149,7 @@ public function encode($aValue) * Return the value decoded from base 64 VLQ. * * @param string $encoded The encoded value to decode + * * @return integer The decoded value */ public function decode($encoded) @@ -167,6 +171,7 @@ public function decode($encoded) * * @param integer $a number to shift * @param integer $b number of bits to shift + * * @return integer */ public function zeroFill($a, $b) @@ -178,13 +183,15 @@ public function zeroFill($a, $b) * Encode single 6-bit digit as base64. * * @param integer $number + * * @return string - * @throws Exception If the number is invalid + * + * @throws \Exception If the number is invalid */ public function base64Encode($number) { if ($number < 0 || $number > 63) { - throw new Exception(sprintf('Invalid number "%s" given. Must be between 0 and 63.', $number)); + throw new \Exception(sprintf('Invalid number "%s" given. Must be between 0 and 63.', $number)); } return $this->intToCharMap[$number]; @@ -194,13 +201,15 @@ public function base64Encode($number) * Decode single 6-bit digit from base64 * * @param string $char - * @return number - * @throws Exception If the number is invalid + * + * @return integer + * + * @throws \Exception If the number is invalid */ public function base64Decode($char) { if (! array_key_exists($char, $this->charToIntMap)) { - throw new Exception(sprintf('Invalid base 64 digit "%s" given.', $char)); + throw new \Exception(sprintf('Invalid base 64 digit "%s" given.', $char)); } return $this->charToIntMap[$char]; diff --git a/src/SourceMap/SourceMapGenerator.php b/src/SourceMap/SourceMapGenerator.php index 31905b10..cbfaa7cf 100644 --- a/src/SourceMap/SourceMapGenerator.php +++ b/src/SourceMap/SourceMapGenerator.php @@ -101,11 +101,11 @@ public function __construct(array $options = []) /** * Adds a mapping * - * @param integer $generatedLine The line number in generated file + * @param integer $generatedLine The line number in generated file * @param integer $generatedColumn The column number in generated file - * @param integer $originalLine The line number in original file - * @param integer $originalColumn The column number in original file - * @param string $sourceFile The original source file + * @param integer $originalLine The line number in original file + * @param integer $originalColumn The column number in original file + * @param string $sourceFile The original source file */ public function addMapping($generatedLine, $generatedColumn, $originalLine, $originalColumn, $sourceFile) { @@ -123,9 +123,10 @@ public function addMapping($generatedLine, $generatedColumn, $originalLine, $ori /** * Saves the source map to a file * - * @param string $file The absolute path to a file + * @param string $file The absolute path to a file * @param string $content The content to write - * @throws Exception If the file could not be saved + * + * @throws \Leafo\ScssPhp\Exception\CompilerException If the file could not be saved */ public function saveMap($content) { @@ -150,6 +151,7 @@ public function saveMap($content) * Generates the JSON source map * * @return string + * * @see https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit# */ public function generateJson() diff --git a/src/Util.php b/src/Util.php index f5aca7a4..d49b265c 100644 --- a/src/Util.php +++ b/src/Util.php @@ -15,7 +15,7 @@ use Leafo\ScssPhp\Exception\RangeException; /** - * Utilties + * Utilty functions * * @author Anthon Pang */ @@ -53,4 +53,18 @@ public static function checkRange($name, Range $range, $value, $unit = '') throw new RangeException("$name {$val} must be between {$range->first} and {$range->last}$unit"); } + + /** + * Encode URI component + * + * @param string $string + * + * @return string + */ + public static function encodeURIComponent($string) + { + $revert = array('%21' => '!', '%2A' => '*', '%27' => "'", '%28' => '(', '%29' => ')'); + + return strtr(rawurlencode($string), $revert); + } } From f92d3e46cf98f1ba7a6af0d8ceee1beeaff75827 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 19 Dec 2017 16:31:11 -0500 Subject: [PATCH 257/484] Version bump to v0.7.3; fixed #135 --- src/Version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Version.php b/src/Version.php index 0dd95fe6..e9c0218c 100644 --- a/src/Version.php +++ b/src/Version.php @@ -18,5 +18,5 @@ */ class Version { - const VERSION = 'v0.7.2'; + const VERSION = 'v0.7.3'; } From b373570479027fa226afec3762852e54ee560bdd Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 19 Dec 2017 16:45:24 -0500 Subject: [PATCH 258/484] Add --sourcemap flag to bin/pscss --- bin/pscss | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/bin/pscss b/bin/pscss index 70a2be3c..be1cb51c 100755 --- a/bin/pscss +++ b/bin/pscss @@ -32,6 +32,7 @@ $debugInfo = false; $lineNumbers = false; $ignoreErrors = false; $encoding = false; +$sourceMap = false; /** * Parse argument @@ -77,6 +78,7 @@ Options include: --iso8859-1 Use iso8859-1 encoding instead of utf-8 (default utf-8) --line-numbers Annotate selectors with comments referring to the source file and line number -p=precision Set decimal number precision (default 10) + --sourcemap Create source map file -T Dump formatted parse tree -v, --version Print the version @@ -108,6 +110,11 @@ EOT; continue; } + if ($argv[$i] === '--sourcemap') { + $sourceMap = true; + continue; + } + if ($argv[$i] === '-T') { $dumpTree = true; continue; @@ -193,6 +200,10 @@ if ($style) { $scss->setFormatter('Leafo\\ScssPhp\\Formatter\\' . ucfirst($style)); } +if ($sourceMap) { + $scss->setSourceMap(Compiler::SOURCE_MAP_FILE); +} + if ($encoding) { $scss->setEncoding($encoding); } From 1e862d7f28bbcce159e3c75eee8069e059f95563 Mon Sep 17 00:00:00 2001 From: dleffler Date: Wed, 20 Dec 2017 09:08:18 -0500 Subject: [PATCH 259/484] Fix/restore working source map generation in 0.7.3 --- src/SourceMap/SourceMapGenerator.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/SourceMap/SourceMapGenerator.php b/src/SourceMap/SourceMapGenerator.php index cbfaa7cf..3a55851a 100644 --- a/src/SourceMap/SourceMapGenerator.php +++ b/src/SourceMap/SourceMapGenerator.php @@ -174,9 +174,17 @@ public function generateJson() $root = $this->options['sourceRoot']; if ($root) { +// $sourceMap['sourceRoot'] = $root; + // A list of symbol names used by the 'mappings' entry. $sourceMap['names'] = array(); } + // A list of original sources used by the 'mappings' entry. + $sourceMap['sources'] = array(); + foreach($this->sources as $source_uri => $source_filename) { + $sourceMap['sources'][] = $this->normalizeFilename($source_filename); + } + // A string with the encoded mapping data. $sourceMap['mappings'] = $mappings; From e8e4df7c5447165638ac255bd86a08b093bc9fb0 Mon Sep 17 00:00:00 2001 From: dleffler Date: Wed, 20 Dec 2017 16:03:15 -0500 Subject: [PATCH 260/484] Regression fix last push to fix 0.7.3 source maps --- src/SourceMap/SourceMapGenerator.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/SourceMap/SourceMapGenerator.php b/src/SourceMap/SourceMapGenerator.php index 3a55851a..c0f96dab 100644 --- a/src/SourceMap/SourceMapGenerator.php +++ b/src/SourceMap/SourceMapGenerator.php @@ -172,11 +172,9 @@ public function generateJson() // An optional source root, useful for relocating source files on a server or removing repeated values in the // 'sources' entry. This value is prepended to the individual entries in the 'source' field. $root = $this->options['sourceRoot']; - if ($root) { -// $sourceMap['sourceRoot'] = $root; + $sourceMap['sourceRoot'] = $root; // A list of symbol names used by the 'mappings' entry. - $sourceMap['names'] = array(); } // A list of original sources used by the 'mappings' entry. @@ -184,6 +182,7 @@ public function generateJson() foreach($this->sources as $source_uri => $source_filename) { $sourceMap['sources'][] = $this->normalizeFilename($source_filename); } + $sourceMap['names'] = array(); // A string with the encoded mapping data. $sourceMap['mappings'] = $mappings; From 045f3087bcb7a293ca2f6da02109fa17d20834a5 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 20 Dec 2017 19:17:12 -0500 Subject: [PATCH 261/484] Update SourceMapGenerator.php --- src/SourceMap/SourceMapGenerator.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/SourceMap/SourceMapGenerator.php b/src/SourceMap/SourceMapGenerator.php index c0f96dab..08e06168 100644 --- a/src/SourceMap/SourceMapGenerator.php +++ b/src/SourceMap/SourceMapGenerator.php @@ -172,16 +172,19 @@ public function generateJson() // An optional source root, useful for relocating source files on a server or removing repeated values in the // 'sources' entry. This value is prepended to the individual entries in the 'source' field. $root = $this->options['sourceRoot']; + if ($root) { $sourceMap['sourceRoot'] = $root; - // A list of symbol names used by the 'mappings' entry. } // A list of original sources used by the 'mappings' entry. $sourceMap['sources'] = array(); - foreach($this->sources as $source_uri => $source_filename) { + + foreach ($this->sources as $source_uri => $source_filename) { $sourceMap['sources'][] = $this->normalizeFilename($source_filename); } + + // A list of symbol names used by the 'mappings' entry. $sourceMap['names'] = array(); // A string with the encoded mapping data. From d7296256bd1bfacb8bd24e69030fd5e187181835 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 20 Dec 2017 19:18:53 -0500 Subject: [PATCH 262/484] Version bump --- src/Version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Version.php b/src/Version.php index e9c0218c..6d07d5ab 100644 --- a/src/Version.php +++ b/src/Version.php @@ -18,5 +18,5 @@ */ class Version { - const VERSION = 'v0.7.3'; + const VERSION = 'v0.7.4'; } From 11f556571b2d4d531a6215398f39e313e48d1aa2 Mon Sep 17 00:00:00 2001 From: dleffler Date: Mon, 5 Feb 2018 09:23:38 -0500 Subject: [PATCH 263/484] Add option to pass a custom SourceMapGenerator object --- src/Compiler.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index f12cf7ee..35a3f866 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -207,8 +207,13 @@ public function compile($code, $path = null) $sourceMapGenerator = null; - if ($this->sourceMap && $this->sourceMap !== self::SOURCE_MAP_NONE) { - $sourceMapGenerator = new SourceMapGenerator($this->sourceMapOptions); + if ($this->sourceMap) { + if (is_object($this->sourceMap) && is_a($this->sourceMap, 'SourceMapGenerator')) { + $sourceMapGenerator = $this->sourceMap; + $this->sourceMap = self::SOURCE_MAP_FILE; + } elseif ($this->sourceMap !== self::SOURCE_MAP_NONE) { + $sourceMapGenerator = new SourceMapGenerator($this->sourceMapOptions); + } } $out = $this->formatter->format($this->scope, $sourceMapGenerator); From a73e32a5e110a8de9919939b3f5371d44c1299b5 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 6 Feb 2018 03:14:51 -0500 Subject: [PATCH 264/484] is_a() -> instanceof --- src/Compiler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compiler.php b/src/Compiler.php index 35a3f866..03c94d9a 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -208,7 +208,7 @@ public function compile($code, $path = null) $sourceMapGenerator = null; if ($this->sourceMap) { - if (is_object($this->sourceMap) && is_a($this->sourceMap, 'SourceMapGenerator')) { + if (is_object($this->sourceMap) && $this->sourceMap instanceof SourceMapGenerator) { $sourceMapGenerator = $this->sourceMap; $this->sourceMap = self::SOURCE_MAP_FILE; } elseif ($this->sourceMap !== self::SOURCE_MAP_NONE) { From 593b92929c3f0e9d82c84ace7d29e8550e386413 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 6 Feb 2018 03:19:06 -0500 Subject: [PATCH 265/484] No side-effect in abs(), ceil(), floor(), round(); fixes #551 --- src/Compiler.php | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 03c94d9a..8da8be77 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -4740,36 +4740,32 @@ protected function libPercentage($args) protected function libRound($args) { $num = $args[0]; - $num[1] = round($num[1]); - return $num; + return new Node\Number(round($num[1]), $num[2]); } protected static $libFloor = ['value']; protected function libFloor($args) { $num = $args[0]; - $num[1] = floor($num[1]); - return $num; + return new Node\Number(floor($num[1]), $num[2]); } protected static $libCeil = ['value']; protected function libCeil($args) { $num = $args[0]; - $num[1] = ceil($num[1]); - return $num; + return new Node\Number(ceil($num[1]), $num[2]); } protected static $libAbs = ['value']; protected function libAbs($args) { $num = $args[0]; - $num[1] = abs($num[1]); - return $num; + return new Node\Number(abs($num[1]), $num[2]); } protected function libMin($args) From d3801706dbf2423a380b7815383c0cbc9203c58a Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 6 Feb 2018 03:50:15 -0500 Subject: [PATCH 266/484] `@for` with units; fixes #548 --- src/Compiler.php | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 8da8be77..d46d5332 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -1772,9 +1772,18 @@ protected function compileChild($child, OutputBlock $out) list(, $for) = $child; $start = $this->reduce($for->start, true); + $end = $this->reduce($for->end, true); + + if ( ! ($start[2] == $end[2] || $end->unitless())) { + $this->throwError('Incompatible units: "%s" and "%s".', $start->unitStr(), $end->unitStr()); + + break; + } + + $unit = $start[2]; $start = $start[1]; - $end = $this->reduce($for->end, true); - $end = $end[1]; + $end = $end[1]; + $d = $start < $end ? 1 : -1; for (;;) { @@ -1784,7 +1793,7 @@ protected function compileChild($child, OutputBlock $out) break; } - $this->set($for->var, new Node\Number($start, '')); + $this->set($for->var, new Node\Number($start, $unit)); $start += $d; $ret = $this->compileChildren($for->children, $out); From a4b566f0571f63a2cc00ee03025f6a498092383f Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 6 Feb 2018 04:03:48 -0500 Subject: [PATCH 267/484] Update docblocks --- src/Base/Range.php | 2 +- src/Block.php | 2 +- src/Colors.php | 2 +- src/Compiler.php | 4 ++-- src/Compiler/Environment.php | 2 +- src/Exception/CompilerException.php | 2 +- src/Exception/ParserException.php | 2 +- src/Exception/RangeException.php | 2 +- src/Exception/ServerException.php | 2 +- src/Formatter.php | 2 +- src/Formatter/Compact.php | 2 +- src/Formatter/Compressed.php | 2 +- src/Formatter/Crunched.php | 2 +- src/Formatter/Debug.php | 2 +- src/Formatter/Expanded.php | 2 +- src/Formatter/Nested.php | 2 +- src/Formatter/OutputBlock.php | 2 +- src/Node.php | 2 +- src/Node/Number.php | 2 +- src/Parser.php | 2 +- src/SourceMap/Base64VLQEncoder.php | 2 +- src/SourceMap/SourceMapGenerator.php | 2 +- src/Type.php | 2 +- src/Util.php | 2 +- src/Version.php | 2 +- 25 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/Base/Range.php b/src/Base/Range.php index 3f3a067d..8bcc6ecf 100644 --- a/src/Base/Range.php +++ b/src/Base/Range.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2017 Leaf Corcoran + * @copyright 2015-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Block.php b/src/Block.php index fc21e5a4..a6ef8e03 100644 --- a/src/Block.php +++ b/src/Block.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Colors.php b/src/Colors.php index 61a71ab6..2314ea58 100644 --- a/src/Colors.php +++ b/src/Colors.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Compiler.php b/src/Compiler.php index d46d5332..292e960e 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * @@ -1774,7 +1774,7 @@ protected function compileChild($child, OutputBlock $out) $start = $this->reduce($for->start, true); $end = $this->reduce($for->end, true); - if ( ! ($start[2] == $end[2] || $end->unitless())) { + if (! ($start[2] == $end[2] || $end->unitless())) { $this->throwError('Incompatible units: "%s" and "%s".', $start->unitStr(), $end->unitStr()); break; diff --git a/src/Compiler/Environment.php b/src/Compiler/Environment.php index b4b86e32..fe309dd3 100644 --- a/src/Compiler/Environment.php +++ b/src/Compiler/Environment.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Exception/CompilerException.php b/src/Exception/CompilerException.php index 45fc1671..7ca2e2b3 100644 --- a/src/Exception/CompilerException.php +++ b/src/Exception/CompilerException.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Exception/ParserException.php b/src/Exception/ParserException.php index c0ee002d..6d64335f 100644 --- a/src/Exception/ParserException.php +++ b/src/Exception/ParserException.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Exception/RangeException.php b/src/Exception/RangeException.php index 47192ff5..3ba6bf14 100644 --- a/src/Exception/RangeException.php +++ b/src/Exception/RangeException.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Exception/ServerException.php b/src/Exception/ServerException.php index 68d3a290..d0ed0842 100644 --- a/src/Exception/ServerException.php +++ b/src/Exception/ServerException.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Formatter.php b/src/Formatter.php index 4d2fdd08..b4f90aa9 100644 --- a/src/Formatter.php +++ b/src/Formatter.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Formatter/Compact.php b/src/Formatter/Compact.php index aaf972b7..4efa1a08 100644 --- a/src/Formatter/Compact.php +++ b/src/Formatter/Compact.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Formatter/Compressed.php b/src/Formatter/Compressed.php index 21cbf28e..1faa7e11 100644 --- a/src/Formatter/Compressed.php +++ b/src/Formatter/Compressed.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Formatter/Crunched.php b/src/Formatter/Crunched.php index 43d63833..42d77b5f 100644 --- a/src/Formatter/Crunched.php +++ b/src/Formatter/Crunched.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Formatter/Debug.php b/src/Formatter/Debug.php index b31c8c7a..bfcbf41a 100644 --- a/src/Formatter/Debug.php +++ b/src/Formatter/Debug.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Formatter/Expanded.php b/src/Formatter/Expanded.php index 0449787d..d8c1e887 100644 --- a/src/Formatter/Expanded.php +++ b/src/Formatter/Expanded.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Formatter/Nested.php b/src/Formatter/Nested.php index 626fec13..8f72206f 100644 --- a/src/Formatter/Nested.php +++ b/src/Formatter/Nested.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Formatter/OutputBlock.php b/src/Formatter/OutputBlock.php index e4092175..5eb589c9 100644 --- a/src/Formatter/OutputBlock.php +++ b/src/Formatter/OutputBlock.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Node.php b/src/Node.php index eb543c73..b9f7945a 100644 --- a/src/Node.php +++ b/src/Node.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Node/Number.php b/src/Node/Number.php index efa83f5b..42c16803 100644 --- a/src/Node/Number.php +++ b/src/Node/Number.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Parser.php b/src/Parser.php index ccac0d3f..6fdea3e2 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/SourceMap/Base64VLQEncoder.php b/src/SourceMap/Base64VLQEncoder.php index 435a7c60..1189ce0e 100644 --- a/src/SourceMap/Base64VLQEncoder.php +++ b/src/SourceMap/Base64VLQEncoder.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/SourceMap/SourceMapGenerator.php b/src/SourceMap/SourceMapGenerator.php index 08e06168..70b47cdc 100644 --- a/src/SourceMap/SourceMapGenerator.php +++ b/src/SourceMap/SourceMapGenerator.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Type.php b/src/Type.php index 2d659e4d..e84c47e4 100644 --- a/src/Type.php +++ b/src/Type.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Util.php b/src/Util.php index d49b265c..7526e020 100644 --- a/src/Util.php +++ b/src/Util.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Version.php b/src/Version.php index 6d07d5ab..2bda80ff 100644 --- a/src/Version.php +++ b/src/Version.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * From 8b538d828bbb75276974605c4a1a435e939da74e Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Thu, 8 Feb 2018 21:04:21 -0500 Subject: [PATCH 268/484] Bump version --- src/Version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Version.php b/src/Version.php index 2bda80ff..3b8dcd00 100644 --- a/src/Version.php +++ b/src/Version.php @@ -18,5 +18,5 @@ */ class Version { - const VERSION = 'v0.7.4'; + const VERSION = 'v0.7.5'; } From 9410cbb398d17b096ce4bdd533f1712a651aca3d Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 28 Feb 2018 23:06:40 -0500 Subject: [PATCH 269/484] Fixes #559 - mix() fix --- src/Compiler.php | 2 +- tests/inputs/functions.scss | 9 ++++++++- tests/outputs/functions.css | 3 +++ tests/outputs_numbered/functions.css | 3 +++ 4 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 292e960e..698d4d89 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -4526,7 +4526,7 @@ protected function libMix($args) ]; if ($firstAlpha != 1.0 || $secondAlpha != 1.0) { - $new[] = $firstAlpha * $weight + $secondAlpha * ($weight - 1); + $new[] = $firstAlpha * $weight + $secondAlpha * (1 - $weight); } return $this->fixColor($new); diff --git a/tests/inputs/functions.scss b/tests/inputs/functions.scss index bf3edf1f..9ebd544f 100644 --- a/tests/inputs/functions.scss +++ b/tests/inputs/functions.scss @@ -165,4 +165,11 @@ $str: 'global'; div { margin: mapping(one two); @include test(); -} \ No newline at end of file +} + +$a: rgba(#000, 0.5); +$b: rgba(#fff, 0.75); + +.test { + color: mix($a, $b); +} diff --git a/tests/outputs/functions.css b/tests/outputs/functions.css index aa0bebbb..e89beb8f 100644 --- a/tests/outputs/functions.css +++ b/tests/outputs/functions.css @@ -49,3 +49,6 @@ p { div { margin: 1px 1px 2px 2px; padding: 1px 1px 2px 2px; } + +.test { + color: rgba(159, 159, 159, 0.625); } diff --git a/tests/outputs_numbered/functions.css b/tests/outputs_numbered/functions.css index 6583ff05..6841fa05 100644 --- a/tests/outputs_numbered/functions.css +++ b/tests/outputs_numbered/functions.css @@ -51,3 +51,6 @@ p { div { margin: 1px 1px 2px 2px; padding: 1px 1px 2px 2px; } +/* line 173, inputs/functions.scss */ +.test { + color: rgba(159, 159, 159, 0.625); } From 847f9f2d6cb64042ed6ca91938f8aa7d20dcadba Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Thu, 19 Apr 2018 11:42:23 -0400 Subject: [PATCH 270/484] fixes #568 example/Server: set default timezone to UTC --- example/Server.php | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/example/Server.php b/example/Server.php index a8dc0457..6ea97018 100644 --- a/example/Server.php +++ b/example/Server.php @@ -207,7 +207,7 @@ protected function compile($in, $out) $elapsed = round((microtime(true) - $start), 4); $v = Version::VERSION; - $t = date('r'); + $t = gmdate('r'); $css = "/* compiled by scssphp $v on $t (${elapsed}s) */\n\n" . $css; $etag = md5($css); @@ -329,7 +329,7 @@ public function serve($salt = '') try { list($css, $etag) = $this->compile($input, $output); - $lastModified = gmdate('D, d M Y H:i:s', filemtime($output)) . ' GMT'; + $lastModified = gmdate('r', filemtime($output)); header('Last-Modified: ' . $lastModified); header('Content-type: text/css'); @@ -371,7 +371,7 @@ public function serve($salt = '') return; } - $lastModified = gmdate('D, d M Y H:i:s', $mtime) . ' GMT'; + $lastModified = gmdate('r', $mtime); header('Last-Modified: ' . $lastModified); echo file_get_contents($output); @@ -510,8 +510,6 @@ public function __construct($dir, $cacheDir = null, $scss = null) $this->scss = $scss; $this->showErrorsAsCSS = false; - if (! ini_get('date.timezone')) { - throw new ServerException('Default date.timezone not set'); - } + date_default_timezone_set('UTC'); } } From 53bb5275a7e3fad3676f1e01f3014d39a0c6d675 Mon Sep 17 00:00:00 2001 From: AzJezz Date: Sun, 29 Apr 2018 16:32:07 +0100 Subject: [PATCH 271/484] Update Compiler.php --- src/Compiler.php | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 698d4d89..32e9b774 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -316,10 +316,15 @@ protected function makeOutputBlock($type, $selectors = null) $out->parent = $this->scope; $out->selectors = $selectors; $out->depth = $this->env->depth; - $out->sourceName = $this->env->block->sourceName; - $out->sourceLine = $this->env->block->sourceLine; - $out->sourceColumn = $this->env->block->sourceColumn; - + if($this->env->block instanceof Block) { + $out->sourceName = $this->env->block->sourceName; + $out->sourceLine = $this->env->block->sourceLine; + $out->sourceColumn = $this->env->block->sourceColumn; + } else { + $out->sourceName = null; + $out->sourceLine = null; + $out->sourceColum = null; + } return $out; } From ddebe1a68a53c4b8524c03f8cb2c24174cb028dc Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Fri, 18 May 2018 14:48:28 -0400 Subject: [PATCH 272/484] Fix #573 --- src/Compiler.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 32e9b774..1aad621b 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -2699,7 +2699,9 @@ public function compileValue($value) $b = round($b); if (count($value) === 5 && $value[4] !== 1) { // rgba - return 'rgba(' . $r . ', ' . $g . ', ' . $b . ', ' . $value[4] . ')'; + $a = new Node\Number($value[4]); + + return 'rgba(' . $r . ', ' . $g . ', ' . $b . ', ' . $a . ')'; } $h = sprintf('#%02x%02x%02x', $r, $g, $b); @@ -4336,7 +4338,7 @@ protected function libRgb($args) protected function libRgba($args) { if ($color = $this->coerceColor($args[0])) { - $num = ! isset($args[1]) ? $args[3] : $args[1]; + $num = isset($args[3]) ? $args[3] : $args[1]; $alpha = $this->assertNumber($num); $color[4] = $alpha; From 57292fba51af83b0e812bc1cb31c7816b3f61936 Mon Sep 17 00:00:00 2001 From: Tim Elsass Date: Sun, 20 May 2018 00:52:50 -0400 Subject: [PATCH 273/484] Fix #575 --- src/Compiler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compiler.php b/src/Compiler.php index 1aad621b..746c6422 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -2699,7 +2699,7 @@ public function compileValue($value) $b = round($b); if (count($value) === 5 && $value[4] !== 1) { // rgba - $a = new Node\Number($value[4]); + $a = new Node\Number($value[4],''); return 'rgba(' . $r . ', ' . $g . ', ' . $b . ', ' . $a . ')'; } From 04482345fc0dd1b5305bcd5032ac4deb8a9ebbeb Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sun, 20 May 2018 10:11:19 -0400 Subject: [PATCH 274/484] Update Compiler.php --- src/Compiler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compiler.php b/src/Compiler.php index 746c6422..b727f178 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -2699,7 +2699,7 @@ public function compileValue($value) $b = round($b); if (count($value) === 5 && $value[4] !== 1) { // rgba - $a = new Node\Number($value[4],''); + $a = new Node\Number($value[4], ''); return 'rgba(' . $r . ', ' . $g . ', ' . $b . ', ' . $a . ')'; } From 1a819c3b926c251891ede7c6bd12d91e403dc384 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 23 May 2018 18:05:54 -0400 Subject: [PATCH 275/484] Fix phpcs warnings --- src/Compiler.php | 4 ++-- src/Parser.php | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index b727f178..bfda455f 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -316,14 +316,14 @@ protected function makeOutputBlock($type, $selectors = null) $out->parent = $this->scope; $out->selectors = $selectors; $out->depth = $this->env->depth; - if($this->env->block instanceof Block) { + if ($this->env->block instanceof Block) { $out->sourceName = $this->env->block->sourceName; $out->sourceLine = $this->env->block->sourceLine; $out->sourceColumn = $this->env->block->sourceColumn; } else { $out->sourceName = null; $out->sourceLine = null; - $out->sourceColum = null; + $out->sourceColum = null; } return $out; } diff --git a/src/Parser.php b/src/Parser.php index 6fdea3e2..b8bfdc36 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -1302,6 +1302,21 @@ protected function value(&$out) { $s = $this->seek(); + if ($this->literal('url(') && $this->match('data:image\/([a-z0-9+-]+);base64,', $m, false)) { + $len = strspn($this->buffer, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwyxz0123456789+/=', $this->count); + + $this->count += $len; + + if ($this->literal(')')) { + $content = substr($this->buffer, $s, $this->count - $s); + $out = [Type::T_KEYWORD, $content]; + + return true; + } + } + + $this->seek($s); + if ($this->literal('not', false) && $this->whitespace() && $this->value($inner)) { $out = [Type::T_UNARY, 'not', $inner, $this->inParens]; From e94a5c21addf8541a710d5ac6507532118ba3295 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 23 May 2018 18:12:08 -0400 Subject: [PATCH 276/484] Refs #527 - parsing base64 --- src/Parser.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Parser.php b/src/Parser.php index b8bfdc36..de86f094 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -1302,7 +1302,7 @@ protected function value(&$out) { $s = $this->seek(); - if ($this->literal('url(') && $this->match('data:image\/([a-z0-9+-]+);base64,', $m, false)) { + if ($this->literal('url(') && $this->match('data:([a-z]+)\/([a-z0-9.+-]+);base64,', $m, false)) { $len = strspn($this->buffer, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwyxz0123456789+/=', $this->count); $this->count += $len; From deaf1dede19347fed905931ad80b495b077e4220 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 23 May 2018 21:33:11 -0400 Subject: [PATCH 277/484] Fix typo --- src/Compiler.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Compiler.php b/src/Compiler.php index bfda455f..72df991e 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -316,6 +316,7 @@ protected function makeOutputBlock($type, $selectors = null) $out->parent = $this->scope; $out->selectors = $selectors; $out->depth = $this->env->depth; + if ($this->env->block instanceof Block) { $out->sourceName = $this->env->block->sourceName; $out->sourceLine = $this->env->block->sourceLine; @@ -323,8 +324,9 @@ protected function makeOutputBlock($type, $selectors = null) } else { $out->sourceName = null; $out->sourceLine = null; - $out->sourceColum = null; + $out->sourceColumn = null; } + return $out; } From 585f6ae84de62ffecf69c23805f25d78d7e4b794 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 23 May 2018 22:18:53 -0400 Subject: [PATCH 278/484] Bump version --- src/Version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Version.php b/src/Version.php index 3b8dcd00..c8a56a5b 100644 --- a/src/Version.php +++ b/src/Version.php @@ -18,5 +18,5 @@ */ class Version { - const VERSION = 'v0.7.5'; + const VERSION = 'v0.7.6'; } From 0b94ebc2b0e61e797bfc0cb74acab5f3e95a09fd Mon Sep 17 00:00:00 2001 From: Stephan Date: Sun, 10 Jun 2018 01:09:50 +0200 Subject: [PATCH 279/484] Actually merge maps instead of just concatenating --- src/Compiler.php | 12 +++++++++++- tests/inputs/map.scss | 5 +++-- tests/outputs/map.css | 4 ++++ tests/outputs_numbered/map.css | 10 ++++++++-- 4 files changed, 26 insertions(+), 5 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 72df991e..8551f001 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -4985,7 +4985,17 @@ 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])]; + foreach ($map2[1] as $i2 => $key2) { + foreach ($map1[1] as $i1 => $key1) { + if ($this->compileStringContent($this->coerceString($key1)) === $this->compileStringContent($this->coerceString($key2))) { + $map1[2][$i1] = $map2[2][$i2]; + continue 2; + } + } + $map1[1][] = $map2[1][$i2]; + $map1[2][] = $map2[2][$i2]; + } + return $map1; } protected static $libKeywords = ['args']; diff --git a/tests/inputs/map.scss b/tests/inputs/map.scss index 0fb6be19..7f8adeba 100644 --- a/tests/inputs/map.scss +++ b/tests/inputs/map.scss @@ -25,9 +25,10 @@ div { bar: nth(nth($map, 1), 1); } -$color: ("black" : #000000); +$color: ("black" : #000000, "grey" : #777777); +$color2: ("grey" : #888888, "white" : #ffffff); -@each $color_name, $color_value in $color { +@each $color_name, $color_value in map_merge( $color, $color2 ) { .#{$color_name} { background-color: $color_value !important; } diff --git a/tests/outputs/map.css b/tests/outputs/map.css index 9c38094a..a8898915 100644 --- a/tests/outputs/map.css +++ b/tests/outputs/map.css @@ -12,6 +12,10 @@ div { bar: color; } .black { background-color: #000 !important; } + .grey { + background-color: #888 !important; } + .white { + background-color: #fff !important; } div { a: 1; diff --git a/tests/outputs_numbered/map.css b/tests/outputs_numbered/map.css index 16de52b9..23c144fb 100644 --- a/tests/outputs_numbered/map.css +++ b/tests/outputs_numbered/map.css @@ -11,10 +11,16 @@ div { div { foo: color black; bar: color; } -/* line 31, inputs/map.scss */ +/* line 32, inputs/map.scss */ .black { background-color: #000 !important; } -/* line 52, inputs/map.scss */ +/* line 32, inputs/map.scss */ +.grey { + background-color: #888 !important; } +/* line 32, inputs/map.scss */ +.white { + background-color: #fff !important; } +/* line 53, inputs/map.scss */ div { a: 1; b: 2; From 4963903c0a88db128026f1a78de205a7153d4b48 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 19 Jun 2018 18:06:25 -0400 Subject: [PATCH 280/484] #569 attempt to fix call() with ellipsis --- src/Compiler.php | 38 +++++++++++++++++++++-------- tests/inputs/builtins.scss | 20 +++++++++++++++ tests/outputs/builtins.css | 12 +++++++++ tests/outputs_numbered/builtins.css | 12 +++++++++ 4 files changed, 72 insertions(+), 10 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 72df991e..7d8db736 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -3655,7 +3655,7 @@ protected function callNativeFunction($name, $args, &$returnValue) return false; } - list($sorted, $kwargs) = $this->sortArgs($prototype, $args); + @list($sorted, $kwargs) = $this->sortArgs($prototype, $args); if ($name !== 'if' && $name !== 'call') { foreach ($sorted as &$val) { @@ -3714,7 +3714,7 @@ protected function sortArgs($prototype, $args) $key = $key[1]; if (empty($key)) { - $posArgs[] = $value; + $posArgs[] = empty($arg[2]) ? $value : $arg; } else { $keyArgs[$key] = $value; } @@ -4266,20 +4266,38 @@ 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 - ); + $posArgs = []; + + foreach ($args as $arg) { + if (empty($arg[0])) { + if ($arg[2] === true) { + $tmp = $this->reduce($arg[1]); + + if ($tmp[0] === Type::T_LIST) { + foreach ($tmp[2] as $item) { + $posArgs[] = [null, $item, false]; + } + } else { + $posArgs[] = [null, $tmp, true]; + } + + continue; + } + + $posArgs[] = [null, $this->reduce($arg), false]; + continue; + } + + $posArgs[] = [null, $arg, false]; + } if (count($kwargs)) { foreach ($kwargs as $key => $value) { - $args[] = [[Type::T_VARIABLE, $key], $value, false]; + $posArgs[] = [[Type::T_VARIABLE, $key], $value, false]; } } - return $this->reduce([Type::T_FUNCTION_CALL, $name, $args]); + return $this->reduce([Type::T_FUNCTION_CALL, $name, $posArgs]); } protected static $libIf = ['condition', 'if-true', 'if-false']; diff --git a/tests/inputs/builtins.scss b/tests/inputs/builtins.scss index a80db5b1..00cd3c63 100644 --- a/tests/inputs/builtins.scss +++ b/tests/inputs/builtins.scss @@ -216,3 +216,23 @@ $type: text; div.unquote-test { a: unquote('[type=\'#{$type}\']'); } + +.does-compile-1 { + color: call(lighten, #000, 40%); +} + +.does-compile-2 { + $color: #000; + $percent: 40%; + color: call(lighten, $color, $percent); +} + +.does-compile-3 { + $args: (#000, 40%); + color: call(lighten, nth($args, 1), nth($args, 2)); +} + +.does-compile-4 { + $args: (#000, 40%); + color: call(lighten, $args...); +} diff --git a/tests/outputs/builtins.css b/tests/outputs/builtins.css index 50e2a82e..db43727d 100644 --- a/tests/outputs/builtins.css +++ b/tests/outputs/builtins.css @@ -159,3 +159,15 @@ div.call-tests { div.unquote-test { a: [type='text']; } + +.does-compile-1 { + color: #666; } + +.does-compile-2 { + color: #666; } + +.does-compile-3 { + color: #666; } + +.does-compile-4 { + color: #666; } diff --git a/tests/outputs_numbered/builtins.css b/tests/outputs_numbered/builtins.css index 722c6af4..e84d727c 100644 --- a/tests/outputs_numbered/builtins.css +++ b/tests/outputs_numbered/builtins.css @@ -160,3 +160,15 @@ div.call-tests { /* line 216, inputs/builtins.scss */ div.unquote-test { a: [type='text']; } +/* line 220, inputs/builtins.scss */ +.does-compile-1 { + color: #666; } +/* line 224, inputs/builtins.scss */ +.does-compile-2 { + color: #666; } +/* line 230, inputs/builtins.scss */ +.does-compile-3 { + color: #666; } +/* line 235, inputs/builtins.scss */ +.does-compile-4 { + color: #666; } From 3d3fd508102242ab5744b91e51ff78e262db9467 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 19 Jun 2018 23:10:38 -0400 Subject: [PATCH 281/484] refs #548 - treat 0 as a special unitless number --- src/Compiler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compiler.php b/src/Compiler.php index 7d8db736..62d125d8 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -4856,7 +4856,7 @@ protected function getNormalizedNumbers($args) if (null === $unit) { $unit = $number[2]; $originalUnit = $item->unitStr(); - } elseif ($unit !== $number[2]) { + } elseif ($number[1] && $unit !== $number[2]) { $this->throwError('Incompatible units: "%s" and "%s".', $originalUnit, $item->unitStr()); break; } From 1d656f8c02a3a69404bba6b28ec4e06edddf0f49 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sat, 21 Jul 2018 21:22:08 -0400 Subject: [PATCH 282/484] refs #578 peephole optimization --- src/Compiler.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Compiler.php b/src/Compiler.php index 414e4401..637f1c1c 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -5004,15 +5004,19 @@ protected function libMapMerge($args) $map2 = $this->assertMap($args[1]); foreach ($map2[1] as $i2 => $key2) { + $key = $this->compileStringContent($this->coerceString($key2)); + foreach ($map1[1] as $i1 => $key1) { - if ($this->compileStringContent($this->coerceString($key1)) === $this->compileStringContent($this->coerceString($key2))) { + if ($key === $this->compileStringContent($this->coerceString($key1))) { $map1[2][$i1] = $map2[2][$i2]; continue 2; } } + $map1[1][] = $map2[1][$i2]; $map1[2][] = $map2[2][$i2]; } + return $map1; } From b72d613f682b714f498412483aff353cde977c00 Mon Sep 17 00:00:00 2001 From: Jiong Ye Date: Fri, 17 Aug 2018 16:24:08 -0400 Subject: [PATCH 283/484] leafo/scssphp#581 generate inline sourcemap in commandline --- bin/pscss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/pscss b/bin/pscss index be1cb51c..ce80e6a9 100755 --- a/bin/pscss +++ b/bin/pscss @@ -201,7 +201,7 @@ if ($style) { } if ($sourceMap) { - $scss->setSourceMap(Compiler::SOURCE_MAP_FILE); + $scss->setSourceMap(Compiler::SOURCE_MAP_INLINE); } if ($encoding) { From a7be38d3dabb6d3c15e28073541163428ad819ab Mon Sep 17 00:00:00 2001 From: Bastian Rihm Date: Thu, 30 Aug 2018 14:43:47 +0200 Subject: [PATCH 284/484] #515 Fix backslash escape --- src/Parser.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Parser.php b/src/Parser.php index de86f094..748d38ae 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -1737,6 +1737,8 @@ protected function string(&$out) $content[] = $m[2] . '"'; } elseif ($this->literal("'", false)) { $content[] = $m[2] . "'"; + } elseif ($this->literal("\\", false)) { + $content[] = $m[2] . "\\"; } else { $content[] = $m[2]; } @@ -1753,7 +1755,9 @@ protected function string(&$out) $delim = '"'; foreach ($content as &$string) { - if ($string === "\\'") { + if ($string === "\\\\") { + $string = "\\"; + } elseif ($string === "\\'") { $string = "'"; } elseif ($string === '\\"') { $string = '"'; From 0b589890bfbdd2e14d272f78d9d49038c051f12c Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 24 Jul 2018 10:47:05 -0400 Subject: [PATCH 285/484] Bump version --- src/Version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Version.php b/src/Version.php index c8a56a5b..ec0fee6f 100644 --- a/src/Version.php +++ b/src/Version.php @@ -18,5 +18,5 @@ */ class Version { - const VERSION = 'v0.7.6'; + const VERSION = 'v0.7.8'; } From 2a88d3e9ba903e1568cda7cd33add91d093a639c Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 26 Sep 2018 16:46:29 -0400 Subject: [PATCH 286/484] Update unit tests (fixes #515) --- tests/inputs/builtins.scss | 1 + tests/outputs/builtins.css | 1 + tests/outputs_numbered/builtins.css | 27 ++++++++++++++------------- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/tests/inputs/builtins.scss b/tests/inputs/builtins.scss index 00cd3c63..c75a854e 100644 --- a/tests/inputs/builtins.scss +++ b/tests/inputs/builtins.scss @@ -78,6 +78,7 @@ color: quote("I do?"); color: str_index(abc, b); color: str_insert(string, insert, 2); + color: str_insert(string, "\\", 2); color: str_length(string); color: str_slice(string, 2, 4); color: str-slice(string, 2, 0); diff --git a/tests/outputs/builtins.css b/tests/outputs/builtins.css index db43727d..94f29b4e 100644 --- a/tests/outputs/builtins.css +++ b/tests/outputs/builtins.css @@ -53,6 +53,7 @@ color: "I do?"; color: 2; color: sinserttring; + color: s\\tring; color: 6; color: tri; color: trin; diff --git a/tests/outputs_numbered/builtins.css b/tests/outputs_numbered/builtins.css index e84d727c..559b6a30 100644 --- a/tests/outputs_numbered/builtins.css +++ b/tests/outputs_numbered/builtins.css @@ -54,6 +54,7 @@ color: "I do?"; color: 2; color: sinserttring; + color: s\\tring; color: 6; color: tri; color: trin; @@ -74,7 +75,7 @@ color: st; color: string; color: strin; } -/* line 105, inputs/builtins.scss */ +/* line 106, inputs/builtins.scss */ #number { color: 250%; color: 50%; @@ -86,7 +87,7 @@ width: 200%; bottom: 10px; padding: 3em 1in 96px 72pt; } -/* line 119, inputs/builtins.scss */ +/* line 120, inputs/builtins.scss */ #list { len: 3; len: 1; @@ -108,7 +109,7 @@ cool: great job one two three; zip: 1px solid, 2px dashed; zip: 1px solid red, 2px dashed green; } -/* line 155, inputs/builtins.scss */ +/* line 156, inputs/builtins.scss */ #introspection { t: number; t: string; @@ -128,47 +129,47 @@ c: true; c: false; c: true; } -/* line 179, inputs/builtins.scss */ +/* line 180, inputs/builtins.scss */ #if { color: yes; color: no; color: yes; color: yes; } -/* line 186, inputs/builtins.scss */ +/* line 187, inputs/builtins.scss */ .transparent { r: 0; g: 0; b: 0; a: 0; } -/* line 193, inputs/builtins.scss */ +/* line 194, inputs/builtins.scss */ .alpha { a: 1; a: 1; a: 1; a: 0.5; a: alpha(currentColor); } -/* line 202, inputs/builtins.scss */ +/* line 203, inputs/builtins.scss */ #exists { a: true; b: true; c: false; } -/* line 209, inputs/builtins.scss */ +/* line 210, inputs/builtins.scss */ div.call-tests { a: #0a64ff; b: #0058ef; c: b; } -/* line 216, inputs/builtins.scss */ +/* line 217, inputs/builtins.scss */ div.unquote-test { a: [type='text']; } -/* line 220, inputs/builtins.scss */ +/* line 221, inputs/builtins.scss */ .does-compile-1 { color: #666; } -/* line 224, inputs/builtins.scss */ +/* line 225, inputs/builtins.scss */ .does-compile-2 { color: #666; } -/* line 230, inputs/builtins.scss */ +/* line 231, inputs/builtins.scss */ .does-compile-3 { color: #666; } -/* line 235, inputs/builtins.scss */ +/* line 236, inputs/builtins.scss */ .does-compile-4 { color: #666; } From f893748cd4482051bc503064c6a4396b10280957 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 26 Sep 2018 17:15:59 -0400 Subject: [PATCH 287/484] Add Base64VLQ encoder test (refs #600) --- tests/Base64VLQTest.php | 54 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 tests/Base64VLQTest.php diff --git a/tests/Base64VLQTest.php b/tests/Base64VLQTest.php new file mode 100644 index 00000000..21b373b8 --- /dev/null +++ b/tests/Base64VLQTest.php @@ -0,0 +1,54 @@ + + */ +class Base64VLQTest extends \PHPUnit_Framework_TestCase +{ + /** + * Test encode + * + * param string $expected + * param string $value + * + * @dataProvider getEncode + */ + public function testEncode($expected, $value) + { + $encoder = new Base64VLQEncoder; + + $this->assertEquals($expected, $encoder->encode($value)); + } + + /** + * Data provider for testEncode + * + * @return array + */ + public static function getEncode() + { + return [ + ['A', 0], + ['C', 1], + ['D', -1], + ['2H', 123], + ['qxmvrH', 123456789], + ['+/////D', 2147483647], // 2^31-1 + ]; + } +} From 0fcbdf599f9f8289d033f327532b6a2230b1d77f Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sun, 30 Sep 2018 20:57:13 -0400 Subject: [PATCH 288/484] Fixes #600 - change Base64 VLQ encoder/decoder implementtion --- scss.inc.php | 3 +- src/SourceMap/Base64.php | 184 +++++++++++++++++++++++++++ src/SourceMap/Base64VLQ.php | 137 ++++++++++++++++++++ src/SourceMap/SourceMapGenerator.php | 4 +- tests/Base64VLQTest.php | 4 +- 5 files changed, 327 insertions(+), 5 deletions(-) create mode 100644 src/SourceMap/Base64.php create mode 100644 src/SourceMap/Base64VLQ.php diff --git a/scss.inc.php b/scss.inc.php index 13c84bf5..d75ecb55 100644 --- a/scss.inc.php +++ b/scss.inc.php @@ -24,7 +24,8 @@ include_once __DIR__ . '/src/Node.php'; include_once __DIR__ . '/src/Node/Number.php'; include_once __DIR__ . '/src/Parser.php'; - include_once __DIR__ . '/src/SourceMap/Base64VLQEncoder.php'; + include_once __DIR__ . '/src/SourceMap/Base64.php'; + include_once __DIR__ . '/src/SourceMap/Base64VLQ.php'; include_once __DIR__ . '/src/SourceMap/SourceMapGenerator.php'; include_once __DIR__ . '/src/Type.php'; include_once __DIR__ . '/src/Util.php'; diff --git a/src/SourceMap/Base64.php b/src/SourceMap/Base64.php new file mode 100644 index 00000000..9d17e6f0 --- /dev/null +++ b/src/SourceMap/Base64.php @@ -0,0 +1,184 @@ + + */ +class Base64 +{ + /** + * @var array + */ + private static $encodingMap = array ( + 0 => 'A', + 1 => 'B', + 2 => 'C', + 3 => 'D', + 4 => 'E', + 5 => 'F', + 6 => 'G', + 7 => 'H', + 8 => 'I', + 9 => 'J', + 10 => 'K', + 11 => 'L', + 12 => 'M', + 13 => 'N', + 14 => 'O', + 15 => 'P', + 16 => 'Q', + 17 => 'R', + 18 => 'S', + 19 => 'T', + 20 => 'U', + 21 => 'V', + 22 => 'W', + 23 => 'X', + 24 => 'Y', + 25 => 'Z', + 26 => 'a', + 27 => 'b', + 28 => 'c', + 29 => 'd', + 30 => 'e', + 31 => 'f', + 32 => 'g', + 33 => 'h', + 34 => 'i', + 35 => 'j', + 36 => 'k', + 37 => 'l', + 38 => 'm', + 39 => 'n', + 40 => 'o', + 41 => 'p', + 42 => 'q', + 43 => 'r', + 44 => 's', + 45 => 't', + 46 => 'u', + 47 => 'v', + 48 => 'w', + 49 => 'x', + 50 => 'y', + 51 => 'z', + 52 => '0', + 53 => '1', + 54 => '2', + 55 => '3', + 56 => '4', + 57 => '5', + 58 => '6', + 59 => '7', + 60 => '8', + 61 => '9', + 62 => '+', + 63 => '/', + ); + + /** + * @var array + */ + private static $decodingMap = array( + 'A' => 0, + 'B' => 1, + 'C' => 2, + 'D' => 3, + 'E' => 4, + 'F' => 5, + 'G' => 6, + 'H' => 7, + 'I' => 8, + 'J' => 9, + 'K' => 10, + 'L' => 11, + 'M' => 12, + 'N' => 13, + 'O' => 14, + 'P' => 15, + 'Q' => 16, + 'R' => 17, + 'S' => 18, + 'T' => 19, + 'U' => 20, + 'V' => 21, + 'W' => 22, + 'X' => 23, + 'Y' => 24, + 'Z' => 25, + 'a' => 26, + 'b' => 27, + 'c' => 28, + 'd' => 29, + 'e' => 30, + 'f' => 31, + 'g' => 32, + 'h' => 33, + 'i' => 34, + 'j' => 35, + 'k' => 36, + 'l' => 37, + 'm' => 38, + 'n' => 39, + 'o' => 40, + 'p' => 41, + 'q' => 42, + 'r' => 43, + 's' => 44, + 't' => 45, + 'u' => 46, + 'v' => 47, + 'w' => 48, + 'x' => 49, + 'y' => 50, + 'z' => 51, + 0 => 52, + 1 => 53, + 2 => 54, + 3 => 55, + 4 => 56, + 5 => 57, + 6 => 58, + 7 => 59, + 8 => 60, + 9 => 61, + '+' => 62, + '/' => 63, + ); + + /** + * Convert to base64 + * + * @param integer $value + * + * @return string + */ + public static function encode($value) + { + return self::$encodingMap[$value]; + } + + /** + * Convert from base64 + * + * @param string $value + * + * @return integer + */ + public static function decode($value) + { + return self::$decodingMap[$value]; + } +} diff --git a/src/SourceMap/Base64VLQ.php b/src/SourceMap/Base64VLQ.php new file mode 100644 index 00000000..6de3b5ee --- /dev/null +++ b/src/SourceMap/Base64VLQ.php @@ -0,0 +1,137 @@ + + * @author Anthon Pang + */ +class Base64VLQ +{ + // A Base64 VLQ digit can represent 5 bits, so it is base-32. + const VLQ_BASE_SHIFT = 5; + + // A mask of bits for a VLQ digit (11111), 31 decimal. + const VLQ_BASE_MASK = 31; + + // The continuation bit is the 6th bit. + const VLQ_CONTINUATION_BIT = 32; + + /** + * Returns the VLQ encoded value. + * + * @param integer $value + * + * @return string + */ + public static function encode($value) + { + $encoded = ''; + $vlq = self::toVLQSigned($value); + + do { + $digit = $vlq & self::VLQ_BASE_MASK; + $vlq >>= self::VLQ_BASE_SHIFT; + + if ($vlq > 0) { + $digit |= self::VLQ_CONTINUATION_BIT; + } + + $encoded .= Base64::encode($digit); + } while ($vlq > 0); + + return $encoded; + } + + /** + * Decodes VLQValue. + * + * @param string $str + * @param integer $index + * + * @return integer + */ + public static function decode($str, &$index) + { + $result = 0; + $shift = 0; + + do { + $c = $str[$index++]; + $digit = Base64::decode($c); + $continuation = ($digit & self::VLQ_CONTINUATION_BIT) != 0; + $digit &= self::VLQ_BASE_MASK; + $result = $result + ($digit << $shift); + $shift = $shift + self::VLQ_BASE_SHIFT; + } while ($continuation); + + return self::fromVLQSigned($result); + } + + /** + * Converts from a two-complement value to a value where the sign bit is + * is placed in the least significant bit. For example, as decimals: + * 1 becomes 2 (10 binary), -1 becomes 3 (11 binary) + * 2 becomes 4 (100 binary), -2 becomes 5 (101 binary) + * + * @param integer $value + * + * @return integer + */ + private static function toVLQSigned($value) + { + if ($value < 0) { + return ((-$value) << 1) + 1; + } + + return ($value << 1) + 0; + } + + /** + * Converts to a two-complement value from a value where the sign bit is + * is placed in the least significant bit. For example, as decimals: + * 2 (10 binary) becomes 1, 3 (11 binary) becomes -1 + * 4 (100 binary) becomes 2, 5 (101 binary) becomes -2 + * + * @param integer $value + * + * @return integer + */ + private static function fromVLQSigned($value) + { + $negate = ($value & 1) === 1; + $value = $value >> 1; + + return $negate ? -$value : $value; + } +} diff --git a/src/SourceMap/SourceMapGenerator.php b/src/SourceMap/SourceMapGenerator.php index 70b47cdc..6e969cae 100644 --- a/src/SourceMap/SourceMapGenerator.php +++ b/src/SourceMap/SourceMapGenerator.php @@ -61,7 +61,7 @@ class SourceMapGenerator /** * The base64 VLQ encoder * - * @var \Leafo\ScssPhp\SourceMap\Base64VLQEncoder + * @var \Leafo\ScssPhp\SourceMap\Base64VLQ */ protected $encoder; @@ -95,7 +95,7 @@ class SourceMapGenerator public function __construct(array $options = []) { $this->options = array_merge($this->defaultOptions, $options); - $this->encoder = new Base64VLQEncoder(); + $this->encoder = new Base64VLQ(); } /** diff --git a/tests/Base64VLQTest.php b/tests/Base64VLQTest.php index 21b373b8..a3b638de 100644 --- a/tests/Base64VLQTest.php +++ b/tests/Base64VLQTest.php @@ -11,7 +11,7 @@ namespace Leafo\ScssPhp\Tests; -use Leafo\ScssPhp\SourceMap\Base64VLQEncoder; +use Leafo\ScssPhp\SourceMap\Base64VLQ; /** * Base64VLQ encoder test @@ -30,7 +30,7 @@ class Base64VLQTest extends \PHPUnit_Framework_TestCase */ public function testEncode($expected, $value) { - $encoder = new Base64VLQEncoder; + $encoder = new Base64VLQ; $this->assertEquals($expected, $encoder->encode($value)); } From c1ca4095952a431d4847862e048715bc6fa331fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Nguyen?= Date: Thu, 11 Oct 2018 13:27:55 +0200 Subject: [PATCH 289/484] fix doc for addImportPath, should also accept callable as input --- src/Compiler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compiler.php b/src/Compiler.php index 637f1c1c..ed80d8d3 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -3308,7 +3308,7 @@ public function getParsedFiles() * * @api * - * @param string $path + * @param string|callable $path */ public function addImportPath($path) { From b931793663ac56d57d5f87f9f6f3f50cf6b73aa0 Mon Sep 17 00:00:00 2001 From: Geoff Willings Date: Tue, 23 Oct 2018 23:12:14 +0100 Subject: [PATCH 290/484] Bug fix error thrown from strpos if needle (basePath) is empty and ignore PhpStorm .idea IDE directory --- .gitignore | 1 + src/SourceMap/SourceMapGenerator.php | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index be6e5ecf..999984a4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.idea .sass-cache scss_cache composer.lock diff --git a/src/SourceMap/SourceMapGenerator.php b/src/SourceMap/SourceMapGenerator.php index 6e969cae..39bfac1c 100644 --- a/src/SourceMap/SourceMapGenerator.php +++ b/src/SourceMap/SourceMapGenerator.php @@ -303,7 +303,7 @@ protected function normalizeFilename($filename) $basePath = $this->options['sourceMapBasepath']; // "Trim" the 'sourceMapBasepath' from the output filename. - if (strpos($filename, $basePath) === 0) { + if (strlen($basePath) && strpos($filename, $basePath) === 0) { $filename = substr($filename, strlen($basePath)); } From a0e5590e65e33ad6c10e4c98e4aa7c8563779ee8 Mon Sep 17 00:00:00 2001 From: Geoff Willings Date: Wed, 24 Oct 2018 00:41:31 +0100 Subject: [PATCH 291/484] Fix bug where rooted SCSS URIs aren't normalised properly with baseUri provided --- src/Compiler.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index ed80d8d3..ee4efc96 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -3481,9 +3481,12 @@ public function findImport($url) if (is_string($dir)) { // check urls for normal import paths foreach ($urls as $full) { - $full = $dir - . (! empty($dir) && substr($dir, -1) !== '/' ? '/' : '') - . $full; + $separator = ( + !empty($dir) && + substr($dir, -1) !== '/' && + substr($full, 0, 1) !== '/' + ) ? '/' : ''; + $full = $dir . $separator . $full; if ($this->fileExists($file = $full . '.scss') || ($hasExtension && $this->fileExists($file = $full)) From 51041822ae0e8b646e7d86de6c6f81100345f939 Mon Sep 17 00:00:00 2001 From: Geoff Willings Date: Wed, 24 Oct 2018 00:42:04 +0100 Subject: [PATCH 292/484] Json preference to not escape slashes (opinionated) --- src/SourceMap/SourceMapGenerator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SourceMap/SourceMapGenerator.php b/src/SourceMap/SourceMapGenerator.php index 39bfac1c..7bb005c4 100644 --- a/src/SourceMap/SourceMapGenerator.php +++ b/src/SourceMap/SourceMapGenerator.php @@ -202,7 +202,7 @@ public function generateJson() unset($sourceMap['sourceRoot']); } - return json_encode($sourceMap); + return json_encode($sourceMap, JSON_UNESCAPED_SLASHES); } /** From 94261339c3b11fdec1d230c57a54084c34a8c824 Mon Sep 17 00:00:00 2001 From: Vince Date: Wed, 13 Mar 2019 14:54:47 -0700 Subject: [PATCH 293/484] Fix for "continue" causing a warning in PHP 7.3 "continue statements targeting switch control flow structures will now generate a warning. In PHP such continue statements are equivalent to break, while they behave as continue 2 in other languages." http://php.net/manual/en/migration73.incompatible.php "In PHP the switch statement is considered a looping structure for the purposes of continue. continue behaves like break (when no arguments are passed). If a switch is inside a loop, continue 2 will continue with the next iteration of the outer loop." http://php.net/manual/en/control-structures.continue.php Signed-off-by: Vince --- tests/ScssTest.php | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/tests/ScssTest.php b/tests/ScssTest.php index 96f0d76e..9c412fe3 100644 --- a/tests/ScssTest.php +++ b/tests/ScssTest.php @@ -75,7 +75,7 @@ public function provideTests() if (preg_match('/^\s*def test_([a-z_]+)/', $line, $matches)) { $state = 1; // enter function $name = $matches[1]; - continue; + continue 2; } break; @@ -83,7 +83,7 @@ public function provideTests() case 1: // inside function if ($line === '' || $line[0] === '#') { - continue; + continue 2; } if (preg_match('/= <<([A-Z_]+)\s*$/', $line, $matches) @@ -95,14 +95,14 @@ public function provideTests() ; } - continue; + continue 2; } if (preg_match('/^\s*assert_equal\(< e' ) { - continue; + continue 2; } if (preg_match('/^\s*end\s*$/', $line)) { @@ -168,7 +168,7 @@ public function provideTests() $scss = array(); $css = array(); $style = null; - continue; + continue 2; } $skipped[] = $line; @@ -179,9 +179,9 @@ public function provideTests() // get css if (preg_match('/^CSS\s*$/', $line)) { $state = 3; // get scss - continue; + continue 2; } - + $css[] = $lines[$i]; break; @@ -190,9 +190,9 @@ public function provideTests() // get scss if (preg_match('/^SCSS\s*$/', $line)) { $state = 1; // end of parameter list - continue; + continue 2; } - + $scss[] = $lines[$i]; break; @@ -201,12 +201,12 @@ public function provideTests() // inside block if (preg_match('/^\s*end\s*$/', $line)) { $state = 1; // end block - continue; + continue 2; } if (preg_match('/^\s*assert_equal < Date: Sun, 17 Mar 2019 23:23:12 +0200 Subject: [PATCH 294/484] Avoid infinitely duplicating parts when extending selector --- src/Compiler.php | 7 ++++++- tests/inputs/extending_compound_selector.scss | 19 +++++++++++++++++++ tests/outputs/extending_compound_selector.css | 4 ++++ .../extending_compound_selector.css | 11 +++++++++++ 4 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 tests/inputs/extending_compound_selector.scss create mode 100644 tests/outputs/extending_compound_selector.css create mode 100644 tests/outputs_numbered/extending_compound_selector.css diff --git a/src/Compiler.php b/src/Compiler.php index ed80d8d3..c73f5b02 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -463,7 +463,12 @@ protected function matchExtends($selector, &$out, $from = 0, $initial = true) $tempReplacement = $k > 0 ? array_slice($new, $k) : $new; for ($l = count($tempReplacement) - 1; $l >= 0; $l--) { - $slice = $tempReplacement[$l]; + $slice = []; + foreach ($tempReplacement[$l] as $chunk) { + if (!in_array($chunk, $slice)) { + $slice[] = $chunk; + } + } array_unshift($replacement, $slice); if (! $this->isImmediateRelationshipCombinator(end($slice))) { diff --git a/tests/inputs/extending_compound_selector.scss b/tests/inputs/extending_compound_selector.scss new file mode 100644 index 00000000..669591fd --- /dev/null +++ b/tests/inputs/extending_compound_selector.scss @@ -0,0 +1,19 @@ +.foo { + &:after { + color: red; + } + &:hover:after { + color: blue; + } +} + +.bar { + @extend .foo; + &:before { + @extend .bar:after; + } +} + +.baz.bar:before { + @extend .bar:before; +} diff --git a/tests/outputs/extending_compound_selector.css b/tests/outputs/extending_compound_selector.css new file mode 100644 index 00000000..7e8cb599 --- /dev/null +++ b/tests/outputs/extending_compound_selector.css @@ -0,0 +1,4 @@ +.foo:after, .bar:after, .bar:before, .baz.bar:before { + color: red; } + .foo:hover:after, .bar:hover:after, .bar:before:hover, .baz.bar:before:hover { + color: blue; } diff --git a/tests/outputs_numbered/extending_compound_selector.css b/tests/outputs_numbered/extending_compound_selector.css new file mode 100644 index 00000000..50685dcf --- /dev/null +++ b/tests/outputs_numbered/extending_compound_selector.css @@ -0,0 +1,11 @@ +/* line 1, inputs/extending_compound_selector.scss */ +/* line 2, inputs/extending_compound_selector.scss */ + .foo:after, .bar:after, .bar:before, .baz.bar:before { + color: red; } +/* line 5, inputs/extending_compound_selector.scss */ +.foo:hover:after, .bar:hover:after, .bar:before:hover, .baz.bar:before:hover { + color: blue; } +/* line 10, inputs/extending_compound_selector.scss */ +/* line 12, inputs/extending_compound_selector.scss */ + +/* line 17, inputs/extending_compound_selector.scss */ From 405ebd336c86b397841eb3d17af1aa9200ca2b41 Mon Sep 17 00:00:00 2001 From: Vjacheslav Trushkin Date: Sun, 17 Mar 2019 23:56:10 +0200 Subject: [PATCH 295/484] Change private properties to protected in compiler --- src/Compiler.php | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index ed80d8d3..f9934aa2 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -145,18 +145,18 @@ class Compiler protected $charsetSeen; protected $sourceNames; - private $indentLevel; - private $commentsSeen; - private $extends; - private $extendsMap; - private $parsedFiles; - private $parser; - private $sourceIndex; - private $sourceLine; - private $sourceColumn; - private $stderr; - private $shouldEvaluate; - private $ignoreErrors; + protected $indentLevel; + protected $commentsSeen; + protected $extends; + protected $extendsMap; + protected $parsedFiles; + protected $parser; + protected $sourceIndex; + protected $sourceLine; + protected $sourceColumn; + protected $stderr; + protected $shouldEvaluate; + protected $ignoreErrors; /** * Constructor @@ -817,7 +817,7 @@ protected function compileAtRoot(Block $block) * * @return array */ - private function spliceTree($envs, Block $block, $without) + protected function spliceTree($envs, Block $block, $without) { $newBlock = null; @@ -894,7 +894,7 @@ private function spliceTree($envs, Block $block, $without) * * @return integer */ - private function compileWith($with) + protected function compileWith($with) { static $mapping = [ 'rule' => self::WITH_RULE, @@ -945,7 +945,7 @@ private function compileWith($with) * * @return \Leafo\ScssPhp\Compiler\Environment */ - private function filterWithout($envs, $without) + protected function filterWithout($envs, $without) { $filtered = []; @@ -968,7 +968,7 @@ private function filterWithout($envs, $without) * * @return boolean */ - private function isWithout($without, Block $block) + protected function isWithout($without, Block $block) { if ((($without & static::WITH_RULE) && isset($block->selectors)) || (($without & static::WITH_MEDIA) && @@ -2215,7 +2215,7 @@ protected function reduce($value, $inExp = false) * * @return array|null */ - private function fncall($name, $argValues) + protected function fncall($name, $argValues) { // SCSS @function if ($this->callScssFunction($name, $argValues, $returnValue)) { @@ -3016,7 +3016,7 @@ protected function multiplyMedia(Environment $env = null, $childQueries = null) * * @return array */ - private function compactEnv(Environment $env) + protected function compactEnv(Environment $env) { for ($envs = []; $env; $env = $env->parent) { $envs[] = $env; @@ -3032,7 +3032,7 @@ private function compactEnv(Environment $env) * * @return \Leafo\ScssPhp\Compiler\Environment */ - private function extractEnv($envs) + protected function extractEnv($envs) { for ($env = null; $e = array_pop($envs);) { $e->parent = $env; @@ -3864,7 +3864,7 @@ protected function applyArguments($argDef, $argValues) * * @return array|\Leafo\ScssPhp\Node\Number */ - private function coerceValue($value) + protected function coerceValue($value) { if (is_array($value) || $value instanceof \ArrayAccess) { return $value; @@ -4203,7 +4203,7 @@ public function toHSL($red, $green, $blue) * * @return float */ - private function hueToRGB($m1, $m2, $h) + protected function hueToRGB($m1, $m2, $h) { if ($h < 0) { $h += 1; From 20d88db8a832429c2a4cac035c32a63b968cb6f4 Mon Sep 17 00:00:00 2001 From: josh Date: Sun, 27 Dec 2015 13:30:27 -0700 Subject: [PATCH 296/484] add matchChar() --- src/Parser.php | 168 ++++++++++++++++++++++++++++--------------------- 1 file changed, 98 insertions(+), 70 deletions(-) diff --git a/src/Parser.php b/src/Parser.php index 748d38ae..5114cdab 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -286,7 +286,7 @@ protected function parseChunk() if ($this->literal('@at-root') && ($this->selectors($selector) || true) && ($this->map($with) || true) && - $this->literal('{') + $this->matchChar('{') ) { $atRoot = $this->pushSpecialBlock(Type::T_AT_ROOT, $s); $atRoot->selector = $selector; @@ -297,7 +297,7 @@ protected function parseChunk() $this->seek($s); - if ($this->literal('@media') && $this->mediaQueryList($mediaQueryList) && $this->literal('{')) { + if ($this->literal('@media') && $this->mediaQueryList($mediaQueryList) && $this->matchChar('{')) { $media = $this->pushSpecialBlock(Type::T_MEDIA, $s); $media->queryList = $mediaQueryList[2]; @@ -309,7 +309,7 @@ protected function parseChunk() if ($this->literal('@mixin') && $this->keyword($mixinName) && ($this->argumentDef($args) || true) && - $this->literal('{') + $this->matchChar('{') ) { $mixin = $this->pushSpecialBlock(Type::T_MIXIN, $s); $mixin->name = $mixinName; @@ -322,11 +322,11 @@ protected function parseChunk() if ($this->literal('@include') && $this->keyword($mixinName) && - ($this->literal('(') && + ($this->matchChar('(') && ($this->argValues($argValues) || true) && - $this->literal(')') || true) && + $this->matchChar(')') || true) && ($this->end() || - $this->literal('{') && $hasBlock = true) + $this->matchChar('{') && $hasBlock = true) ) { $child = [Type::T_INCLUDE, $mixinName, isset($argValues) ? $argValues : null, null]; @@ -391,7 +391,7 @@ protected function parseChunk() if ($this->literal('@function') && $this->keyword($fnName) && $this->argumentDef($args) && - $this->literal('{') + $this->matchChar('{') ) { $func = $this->pushSpecialBlock(Type::T_FUNCTION, $s); $func->name = $fnName; @@ -431,7 +431,7 @@ protected function parseChunk() $this->genericList($varNames, 'variable', ',', false) && $this->literal('in') && $this->valueList($list) && - $this->literal('{') + $this->matchChar('{') ) { $each = $this->pushSpecialBlock(Type::T_EACH, $s); @@ -448,7 +448,7 @@ protected function parseChunk() if ($this->literal('@while') && $this->expression($cond) && - $this->literal('{') + $this->matchChar('{') ) { $while = $this->pushSpecialBlock(Type::T_WHILE, $s); $while->cond = $cond; @@ -465,7 +465,7 @@ protected function parseChunk() ($this->literal('through') || ($forUntil = true && $this->literal('to'))) && $this->expression($end) && - $this->literal('{') + $this->matchChar('{') ) { $for = $this->pushSpecialBlock(Type::T_FOR, $s); $for->var = $varName[1]; @@ -478,7 +478,7 @@ protected function parseChunk() $this->seek($s); - if ($this->literal('@if') && $this->valueList($cond) && $this->literal('{')) { + if ($this->literal('@if') && $this->valueList($cond) && $this->matchChar('{')) { $if = $this->pushSpecialBlock(Type::T_IF, $s); $if->cond = $cond; $if->cases = []; @@ -535,9 +535,9 @@ protected function parseChunk() list(, $if) = $last; if ($this->literal('@else')) { - if ($this->literal('{')) { + if ($this->matchChar('{')) { $else = $this->pushSpecialBlock(Type::T_ELSE, $s); - } elseif ($this->literal('if') && $this->valueList($cond) && $this->literal('{')) { + } elseif ($this->literal('if') && $this->valueList($cond) && $this->matchChar('{')) { $else = $this->pushSpecialBlock(Type::T_ELSEIF, $s); $else->cond = $cond; } @@ -576,10 +576,10 @@ protected function parseChunk() $this->seek($s); // doesn't match built in directive, do generic one - if ($this->literal('@', false) && + if ($this->matchChar('@', false) && $this->keyword($dirName) && ($this->variable($dirValue) || $this->openString('{', $dirValue) || true) && - $this->literal('{') + $this->matchChar('{') ) { if ($dirName === 'media') { $directive = $this->pushSpecialBlock(Type::T_MEDIA, $s); @@ -617,7 +617,7 @@ protected function parseChunk() // variable assigns if ($this->variable($name) && - $this->literal(':') && + $this->matchChar(':') && $this->valueList($value) && $this->end() ) { @@ -636,7 +636,7 @@ protected function parseChunk() } // opening css block - if ($this->selectors($selectors) && $this->literal('{')) { + if ($this->selectors($selectors) && $this->matchChar('{')) { $this->pushBlock($selectors, $s); return true; @@ -645,7 +645,7 @@ protected function parseChunk() $this->seek($s); // property assign, or nested assign - if ($this->propertyName($name) && $this->literal(':')) { + if ($this->propertyName($name) && $this->matchChar(':')) { $foundSomething = false; if ($this->valueList($value)) { @@ -653,7 +653,7 @@ protected function parseChunk() $foundSomething = true; } - if ($this->literal('{')) { + if ($this->matchChar('{')) { $propBlock = $this->pushSpecialBlock(Type::T_NESTED_PROPERTY, $s); $propBlock->prefix = $name; $foundSomething = true; @@ -669,7 +669,7 @@ protected function parseChunk() $this->seek($s); // closing a block - if ($this->literal('}')) { + if ($this->matchChar('}')) { $block = $this->popBlock(); if (isset($block->type) && $block->type === Type::T_INCLUDE) { @@ -686,7 +686,7 @@ protected function parseChunk() } // extra stuff - if ($this->literal(';') || + if ($this->matchChar(';') || $this->literal('')) { + if ($this->literal('-->', 3)) { return true; } @@ -687,7 +687,7 @@ protected function parseChunk() // extra stuff if ($this->matchChar(';') || - $this->literal('