Skip to content

Commit

Permalink
Add more @at-root test cases and refactor; refs #354
Browse files Browse the repository at this point in the history
  • Loading branch information
robocoder committed Oct 21, 2015
1 parent 3301bc5 commit a1134f6
Show file tree
Hide file tree
Showing 4 changed files with 501 additions and 53 deletions.
225 changes: 203 additions & 22 deletions src/Compiler.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -546,20 +553,11 @@ protected function mediaParent($scope)
*/
protected function compileAtRoot($block)
{
$env = $this->pushEnv($block);

$envs = $this->compactEnv($env);
$env = $this->pushEnv($block);
$envs = $this->compactEnv($env);
$without = isset($block->with) ? $this->compileWith($block->with) : self::WITH_RULE;

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->env = $this->filterWithout($envs, $without);

// wrap inline selector
if ($block->selector) {
Expand All @@ -575,14 +573,197 @@ protected function compileAtRoot($block)
$block->children = array(array('block', $wrapped));
}

$this->compileChildren($block->children, $this->scope);
$newBlock = $this->spliceTree($envs, $block, $without);

$this->scope = $this->makeOutputBlock('at-root');
$this->scope->depth = 1;

$this->rootBlock->children[] = $this->scope;

$this->compileChild($newBlock, $this->scope);

$this->scope = $this->scope->parent;
$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)
{
// generic block
$b = new \stdClass;
$b->sourcePosition = $block->sourcePosition;
$b->sourceParser = $block->sourceParser;
$b->selectors = array();//$block->selectors;
$b->comments = $block->comments;
$b->children = $block->children;
$b->parent = null;

$newBlock = $b;

foreach ($envs as $e) {
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->sourceParser)) {
$b->sourceParser = $e->block->sourceParser;
}

$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;
}

$b->children[] = array('block', $newBlock);
$b->parent = null;

$newBlock->parent = $b;
$newBlock = $b;
}

return array('block', $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
*
Expand Down Expand Up @@ -4113,9 +4294,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');
Expand Down Expand Up @@ -4337,11 +4516,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');
Expand Down Expand Up @@ -4570,7 +4749,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');
Expand All @@ -4581,13 +4762,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
Expand Down
Loading

0 comments on commit a1134f6

Please sign in to comment.