public function parse($expression, $context, $environment = self::DEFAULT_ENV)
{
$outputQueue = array();
$operatorStack = array();
$parenthesis = 0;
$tokens = $this->lexer->lex($expression, $context);
foreach ($tokens as $i => $token) {
// If two literals/expessions are seperated by whitespace use the concat operator
if (empty($token)) {
if (isset($tokens[$i + 1])) {
if ($i > 0 && (!$tokens[$i - 1] instanceof SassScriptOperation || $tokens[$i - 1]->operator === SassScriptOperation::$operators[')'][0]) && (!$tokens[$i + 1] instanceof SassScriptOperation || $tokens[$i + 1]->operator === SassScriptOperation::$operators['('][0])) {
$token = new SassScriptOperation(SassScriptOperation::$defaultOperator, $context);
} else {
continue;
}
}
} elseif ($token instanceof SassScriptVariable) {
$token = $token->evaluate($context);
$environment = self::DEFAULT_ENV;
}
// If the token is a number or function add it to the output queue.
if ($token instanceof SassLiteral || $token instanceof SassScriptFunction) {
if ($environment === self::CSS_PROPERTY && $token instanceof SassNumber && !$parenthesis) {
$token->inExpression = false;
}
array_push($outputQueue, $token);
} elseif ($token instanceof SassScriptOperation) {
// If the token is a left parenthesis push it onto the stack.
if ($token->operator == SassScriptOperation::$operators['('][0]) {
array_push($operatorStack, $token);
$parenthesis++;
} elseif ($token->operator == SassScriptOperation::$operators[')'][0]) {
$parenthesis--;
while ($c = count($operatorStack)) {
// If the token at the top of the stack is a left parenthesis
if ($operatorStack[$c - 1]->operator == SassScriptOperation::$operators['('][0]) {
// Pop the left parenthesis from the stack, but not onto the output queue.
array_pop($operatorStack);
break;
}
// else pop the operator off the stack onto the output queue.
array_push($outputQueue, array_pop($operatorStack));
}
// If the stack runs out without finding a left parenthesis
// there are mismatched parentheses.
if ($c <= 0) {
array_push($outputQueue, new SassString(')'));
break;
}
} else {
// while there is an operator, o2, at the top of the stack
while ($c = count($operatorStack)) {
$operation = $operatorStack[$c - 1];
// if o2 is left parenthesis, or
// the o1 has left associativty and greater precedence than o2, or
// the o1 has right associativity and lower or equal precedence than o2
if ($operation->operator == SassScriptOperation::$operators['('][0] || $token->associativity == 'l' && $token->precedence > $operation->precedence || $token->associativity == 'r' && $token->precedence <= $operation->precedence) {
break;
// stop checking operators
}
//pop o2 off the stack and onto the output queue
array_push($outputQueue, array_pop($operatorStack));
}
// push o1 onto the stack
array_push($operatorStack, $token);
}
}
}
// When there are no more tokens
while ($c = count($operatorStack)) {
// While there are operators on the stack:
if ($operatorStack[$c - 1]->operator !== SassScriptOperation::$operators['('][0]) {
array_push($outputQueue, array_pop($operatorStack));
} else {
throw new SassScriptParserException('Unmatched parentheses', $context->node);
}
}
return $outputQueue;
}