1<?php
2
3namespace Sabberworm\CSS\Value;
4
5use Sabberworm\CSS\Parsing\ParserState;
6use Sabberworm\CSS\Parsing\UnexpectedTokenException;
7
8class CalcFunction extends CSSFunction {
9	const T_OPERAND  = 1;
10	const T_OPERATOR = 2;
11
12	public static function parse(ParserState $oParserState) {
13		$aOperators = array('+', '-', '*', '/');
14		$sFunction = trim($oParserState->consumeUntil('(', false, true));
15		$oCalcList = new CalcRuleValueList($oParserState->currentLine());
16		$oList = new RuleValueList(',', $oParserState->currentLine());
17		$iNestingLevel = 0;
18		$iLastComponentType = NULL;
19		while(!$oParserState->comes(')') || $iNestingLevel > 0) {
20			$oParserState->consumeWhiteSpace();
21			if ($oParserState->comes('(')) {
22				$iNestingLevel++;
23				$oCalcList->addListComponent($oParserState->consume(1));
24				continue;
25			} else if ($oParserState->comes(')')) {
26				$iNestingLevel--;
27				$oCalcList->addListComponent($oParserState->consume(1));
28				continue;
29			}
30			if ($iLastComponentType != CalcFunction::T_OPERAND) {
31				$oVal = Value::parsePrimitiveValue($oParserState);
32				$oCalcList->addListComponent($oVal);
33				$iLastComponentType = CalcFunction::T_OPERAND;
34			} else {
35				if (in_array($oParserState->peek(), $aOperators)) {
36					if (($oParserState->comes('-') || $oParserState->comes('+'))) {
37						if ($oParserState->peek(1, -1) != ' ' || !($oParserState->comes('- ') || $oParserState->comes('+ '))) {
38							throw new UnexpectedTokenException(" {$oParserState->peek()} ", $oParserState->peek(1, -1) . $oParserState->peek(2), 'literal', $oParserState->currentLine());
39						}
40					}
41					$oCalcList->addListComponent($oParserState->consume(1));
42					$iLastComponentType = CalcFunction::T_OPERATOR;
43				} else {
44					throw new UnexpectedTokenException(
45						sprintf(
46							'Next token was expected to be an operand of type %s. Instead "%s" was found.',
47							implode(', ', $aOperators),
48							$oVal
49						),
50						'',
51						'custom',
52						$oParserState->currentLine()
53					);
54				}
55			}
56		}
57		$oList->addListComponent($oCalcList);
58		$oParserState->consume(')');
59		return new CalcFunction($sFunction, $oList, ',', $oParserState->currentLine());
60	}
61
62}
63