/** * Lex an expression into SassScript tokens. * @param string $string expression to lex * @param SassContext $context the context in which the expression is lexed * @return array tokens */ public function lex($string, $context) { // if it's already lexed, just return it as-is if (is_object($string)) { return array($string); } if (is_array($string)) { return $string; } $tokens = array(); // whilst the string is not empty, split it into it's tokens. while ($string !== false) { if (($match = $this->isWhitespace($string)) !== false) { $tokens[] = null; } elseif (($match = SassScriptFunction::isa($string)) !== false) { preg_match(SassScriptFunction::MATCH_FUNC, $match, $matches); $args = array(); foreach (SassScriptFunction::extractArgs($matches[SassScriptFunction::ARGS], false, $context) as $key => $expression) { $args[$key] = $this->parser->evaluate($expression, $context); } $tokens[] = new SassScriptFunction($matches[SassScriptFunction::NAME], $args); } elseif (($match = SassBoolean::isa($string)) !== false) { $tokens[] = new SassBoolean($match); } elseif (($match = SassColour::isa($string)) !== false) { $tokens[] = new SassColour($match); } elseif (($match = SassNumber::isa($string)) !== false) { $tokens[] = new SassNumber($match); } elseif (($match = SassString::isa($string)) !== false) { $stringed = new SassString($match); if (strlen($stringed->quote) == 0 && SassList::isa($string) !== false && !preg_match("/^\\-\\w+\\-\\w+\$/", $stringed->value)) { $tokens[] = new SassList($string); } else { $tokens[] = $stringed; } } elseif ($string == '()') { $match = $string; $tokens[] = new SassList($match); } elseif (($match = SassScriptOperation::isa($string)) !== false) { $tokens[] = new SassScriptOperation($match); } elseif (($match = SassScriptVariable::isa($string)) !== false) { $tokens[] = new SassScriptVariable($match); } else { $_string = $string; $match = ''; while (strlen($_string) && !$this->isWhitespace($_string)) { foreach (SassScriptOperation::$inStrOperators as $operator) { if (substr($_string, 0, strlen($operator)) == $operator) { break 2; } } $match .= $_string[0]; $_string = substr($_string, 1); } $tokens[] = new SassString($match); } $string = substr($string, strlen($match)); } return $tokens; }
/** * SassMixinDefinitionNode constructor. * @param object source token * @return SassMixinNode */ public function __construct($token) { parent::__construct($token); preg_match(self::MATCH, $token->source, $matches); $this->name = $matches[self::NAME]; if (isset($matches[self::ARGS])) { $this->args = SassScriptFunction::extractArgs($matches[self::ARGS]); } }
/** * Evaluates the function. * Look for a user defined function first - this allows users to override * pre-defined functions, then try the pre-defined functions. * @return Function the value of this Function */ public function perform() { self::$context = new SassContext(SassScriptParser::$context); $name = preg_replace('/[^a-z0-9_]/', '_', strtolower($this->name)); $args = $this->process_arguments($this->args); foreach ($this->args as $k => $v) { if (!is_numeric($k)) { self::$context->setVariable($k, $v); } } try { if (SassScriptParser::$context->hasFunction($this->name)) { $return = SassScriptParser::$context->getFunction($this->name)->execute(SassScriptParser::$context, $this->args); return $return; } else { if (SassScriptParser::$context->hasFunction($name)) { $return = SassScriptParser::$context->getFunction($name)->execute(SassScriptParser::$context, $this->args); return $return; } } } catch (Exception $e) { throw $e; } if (isset(SassParser::$functions) && count(SassParser::$functions)) { foreach (SassParser::$functions as $fn => $callback) { if (($fn == $name || $fn == $this->name) && is_callable($callback)) { $result = call_user_func_array($callback, $args); if (!is_object($result)) { $lexed = SassScriptLexer::$instance->lex($result, self::$context); if (count($lexed) === 1) { return $lexed[0]; } return new SassString(implode('', $this->process_arguments($lexed))); } return $result; } } } if (method_exists('SassScriptFunctions', $name) || method_exists('SassScriptFunctions', $name = '_' . $name)) { $sig = self::get_reflection(array('SassScriptFunctions', $name)); list($args) = self::fill_parameters($sig, $this->args, SassScriptParser::$context, $this); return call_user_func_array(array('SassScriptFunctions', $name), $args); } foreach ($this->args as $i => $arg) { if (is_object($arg) && isset($arg->quote)) { $args[$i] = $arg->toString(); } if (!is_numeric($i) && SassScriptParser::$context->hasVariable($i)) { $args[$i] = SassScriptParser::$context->getVariable($i); } } // CSS function: create a SassString that will emit the function into the CSS return new SassString($this->name . '(' . join(', ', $args) . ')'); }
/** * SassMixinDefinitionNode constructor. * @param object source token * @return SassMixinDefinitionNode */ public function __construct($token) { preg_match(self::MATCH, $token->source, $matches); parent::__construct($token); if (empty($matches)) { throw new SassMixinDefinitionNodeException('Invalid Mixin', $this); } $this->name = $matches[self::NAME]; if (isset($matches[self::ARGUMENTS])) { $this->args = SassScriptFunction::extractArgs($matches[self::ARGUMENTS], true, new SassContext()); } }
/** * SassMixinDefinitionNode constructor. * @param object source token * @return SassMixinDefinitionNode */ public function __construct($token) { // if ($token->level > 1) { // throw new SassMixinDefinitionNodeException('Mixins can only be defined at root level. Token was set at level ' . $token->level, $token); // } preg_match(self::MATCH, $token->source, $matches); parent::__construct($token); if (empty($matches)) { throw new SassMixinDefinitionNodeException('Invalid Mixin', $this); } $this->name = $matches[self::NAME]; if (isset($matches[self::ARGUMENTS])) { $this->args = SassScriptFunction::extractArgs($matches[self::ARGUMENTS]); } }
/** * Parse this node. * Set passed arguments and any optional arguments not passed to their * defaults, then render the children of the mixin definition. * @param SassContext the context in which this node is parsed * @return array the parsed node */ public function parse($pcontext) { $mixin = $pcontext->getMixin($this->name); $context = new SassContext($pcontext); $context->content = $this->children; $argc = count($this->args); $count = 0; list($arguments) = SassScriptFunction::fill_parameters($mixin->args, $this->args, $context, $this); $context->setVariables($arguments); $children = array(); foreach ($mixin->children as $child) { $child->parent = $this; $children = array_merge($children, $child->parse($context)); } // $context->merge(); return $children; }
/** * Lex an expression into SassScript tokens. * @param string expression to lex * @param SassContext the context in which the expression is lexed * @return array tokens */ public function lex($string, $context) { $tokens = array(); while ($string !== false) { if (($match = $this->isWhitespace($string)) !== false) { $tokens[] = null; } elseif (($match = SassScriptFunction::isa($string)) !== false) { preg_match(SassScriptFunction::MATCH_FUNC, $match, $matches); $args = array(); foreach (SassScriptFunction::extractArgs($matches[SassScriptFunction::ARGS]) as $expression) { $args[] = $this->parser->evaluate($expression, $context); } $tokens[] = new SassScriptFunction($matches[SassScriptFunction::NAME], $args); } elseif (($match = SassString::isa($string)) !== false) { $tokens[] = new SassString($match); } elseif (($match = SassBoolean::isa($string)) !== false) { $tokens[] = new SassBoolean($match); } elseif (($match = SassColour::isa($string)) !== false) { $tokens[] = new SassColour($match); } elseif (($match = SassNumber::isa($string)) !== false) { $tokens[] = new SassNumber($match); } elseif (($match = SassScriptOperation::isa($string)) !== false) { $tokens[] = new SassScriptOperation($match); } elseif (($match = SassScriptVariable::isa($string)) !== false) { $tokens[] = new SassScriptVariable($match); } else { $_string = $string; $match = ''; while (strlen($_string) && !$this->isWhitespace($_string)) { foreach (SassScriptOperation::$inStrOperators as $operator) { if (substr($_string, 0, strlen($operator)) == $operator) { break 2; } } $match .= $_string[0]; $_string = substr($_string, 1); } $tokens[] = new SassString($match); } $string = substr($string, strlen($match)); } return $tokens; }
/** * Returns the next token from the string. * @param string string to tokenise * @return mixed token. Either a SassLiteral, a SassScriptOperation * @throws SassScriptLexerException if unable to tokenise string */ private function nextToken(&$string) { if (($match = $this->isWhitespace($string)) !== false) { $string = substr($string, strlen($match)); return $this->nextToken($string); } elseif (($match = SassNumber::isa($string)) !== false) { $string = substr($string, strlen($match)); return new SassNumber($match); } elseif (($match = SassColour::isa($string)) !== false) { $string = substr($string, strlen($match)); return new SassColour($match); } elseif (($match = SassBoolean::isa($string)) !== false) { $string = substr($string, strlen($match)); return new SassBoolean($match); } elseif (($match = SassString::isa($string)) !== false) { $string = substr($string, strlen($match)); return new SassString($match); } elseif (($match = SassScriptFunction::isa($string)) !== false) { $string = substr($string, strlen($match)); return new SassScriptFunction($match); } elseif (($match = SassScriptOperation::isa($string)) !== false) { $string = substr($string, strlen($match)); return new SassScriptOperation($match); } else { throw new SassScriptLexerException("Unable to tokenise \"{$string}\""); } }
/** * Evalutes the function in the given context, with the provided arguments * @param SassContext - the parent context * @param array - the list of provided variables * @throws SassReturn - if the @return is fired then this is thrown to break early * @return SassBoolean(false) - if no @return was fired, return false */ public function execute($pcontext, $provided) { list($arguments, $context) = SassScriptFunction::fill_parameters($this->args, $provided, $pcontext, $this); $context->setVariables($arguments); $parser = $this->parent->parser; $children = array(); try { foreach ($this->children as $child) { $child->parent = $this; $children = array_merge($children, $child->parse($context)); } } catch (SassReturn $e) { return $e->value; } return new SassBoolean('false'); }