/** * Tests ExpType::coerce */ public function testCoerce() { // coerce number $this->assertEquals($this->tokens['int'], ExpType::coerceToNumber($this->tokens['int'])); $this->assertEquals($this->tokens['float'], ExpType::coerceToNumber($this->tokens['float'])); $this->assertEquals(new Token(ExpType::$INT, 2), ExpType::coerceToNumber(new Token(ExpType::$RAW, '2'))); $this->assertEquals(new Token(ExpType::$INT, 2), ExpType::coerceToNumber(new Token(ExpType::$STRING, '2'))); $this->assertEquals(new Token(ExpType::$INT, 1), ExpType::coerceToNumber(new Token(ExpType::$BOOL, true))); $this->assertEquals(new Token(ExpType::$INT, 0), ExpType::coerceToNumber(new Token(ExpType::$BOOL, false))); $this->assertEquals(new Token(ExpType::$INT, 0), ExpType::coerceToNumber(new Token(ExpType::$NULL, null))); $this->assertEquals(new Token(ExpType::$FLOAT, 1.0), ExpType::coerceToNumber(new Token(ExpType::$RAW, '1.0'))); $this->assertEquals(new Token(ExpType::$FLOAT, 1.0), ExpType::coerceToNumber(new Token(ExpType::$STRING, '1.0'))); // coerce string $this->assertEquals($this->tokens['string'], ExpType::coerceToString($this->tokens['string'])); $this->assertEquals(new Token(ExpType::$STRING, '2'), ExpType::coerceToString(new Token(ExpType::$RAW, '2'))); $this->assertEquals(new Token(ExpType::$STRING, '2'), ExpType::coerceToString(new Token(ExpType::$INT, 2))); $this->assertEquals(new Token(ExpType::$STRING, '2'), ExpType::coerceToString(new Token(ExpType::$FLOAT, 2.0))); $this->assertEquals(new Token(ExpType::$STRING, '2.5'), ExpType::coerceToString(new Token(ExpType::$FLOAT, 2.5))); $this->assertEquals(new Token(ExpType::$STRING, 'true'), ExpType::coerceToString(new Token(ExpType::$BOOL, true))); $this->assertEquals(new Token(ExpType::$STRING, 'false'), ExpType::coerceToString(new Token(ExpType::$BOOL, false))); $this->assertEquals(new Token(ExpType::$STRING, 'null'), ExpType::coerceToString(new Token(ExpType::$NULL, null))); // coerce bool $this->assertEquals($this->tokens['bool'], ExpType::coerceToBool($this->tokens['bool'])); $this->assertEquals(new Token(ExpType::$BOOL, true), ExpType::coerceToBool(new Token(ExpType::$RAW, 'True'))); $this->assertEquals(new Token(ExpType::$BOOL, true), ExpType::coerceToBool(new Token(ExpType::$RAW, 'true'))); $this->assertEquals(new Token(ExpType::$BOOL, false), ExpType::coerceToBool(new Token(ExpType::$RAW, 'False'))); $this->assertEquals(new Token(ExpType::$BOOL, false), ExpType::coerceToBool(new Token(ExpType::$RAW, 'false'))); $this->assertEquals(new Token(ExpType::$BOOL, true), ExpType::coerceToBool(new Token(ExpType::$STRING, 'false'))); $this->assertEquals(new Token(ExpType::$BOOL, false), ExpType::coerceToBool(new Token(ExpType::$STRING, ''))); $this->assertEquals(new Token(ExpType::$BOOL, true), ExpType::coerceToBool(new Token(ExpType::$INT, 2))); $this->assertEquals(new Token(ExpType::$BOOL, false), ExpType::coerceToBool(new Token(ExpType::$INT, 0))); $this->assertEquals(new Token(ExpType::$BOOL, true), ExpType::coerceToBool(new Token(ExpType::$FLOAT, 2.0))); $this->assertEquals(new Token(ExpType::$BOOL, false), ExpType::coerceToBool(new Token(ExpType::$FLOAT, 0.0))); $this->assertEquals(new Token(ExpType::$BOOL, false), ExpType::coerceToBool(new Token(ExpType::$NULL, null))); // coerce null $this->assertEquals($this->tokens['null'], ExpType::coerceToNull($this->tokens['null'])); $this->assertEquals($this->tokens['null'], ExpType::coerceToNull(new Token(ExpType::$RAW, 'null'))); }
private static function evaluateLiterals(&$tokenStream) { for ($i = 0; $i < count($tokenStream); $i++) { $node = $tokenStream[$i]; if ($node->type != ExpType::$RAW) { continue; } $str = strtolower($node->value); if ($str == 'true' || $str == 'false') { $tokenStream[$i] = ExpType::coerceToBool($node); } elseif ($str == 'null') { $tokenStream[$i] = ExpType::coerceToNull($node); } elseif (preg_match(ExpLexer::$NUMBER_PATTERN, $str) == 1) { $tokenStream[$i] = ExpType::coerceToNumber($node); } elseif (preg_match(ExpLexer::$IDENTITY_PATTERN, $str) == 1) { // is identity $tokenStream[$i]->type = ExpType::$IDENTITY; } else { // mal-format throw new ExpLexerException("Mal-format expression segment: " . $node->value); } } }
private function compute($operandStack, $operator) { $ARITHMETIC_OPS = array('+', ' - ', '*', '/', '%'); // without unary operator ' -' $RELATIONAL_OPS = array('==', '!=', '<', '>', '<=', '>='); $LOGICAL_OPS = array('&&', '||'); // without '!' $sym = $operator->value; if ($operator->type == ExpType::$UNARY_OP) { $operand = $this->evaluateIdentity(array_pop($operandStack)); } else { $rhs = array_pop($operandStack); $rhs = $sym != '.' ? $this->evaluateIdentity($rhs) : $rhs; $lhs = $this->evaluateIdentity(array_pop($operandStack)); } if ($sym == '.') { if ($lhs->type == ExpType::$NULL || $rhs->type == ExpType::$NULL) { // Dealing with null type $result = new Token(ExpType::$NULL, null); } else { if ($lhs->type == ExpType::$ARRAY) { if ($rhs->type == ExpType::$INT || $rhs->type == ExpType::$STRING || $rhs->type == ExpType::$IDENTITY) { $resval = isset($lhs->value[$rhs->value]) ? $lhs->value[$rhs->value] : null; } else { throw new ExpParserException("Can't reference key typ " . print_r($rhs, true) . " on array"); } } elseif ($lhs->type == ExpType::$OBJECT) { if ($rhs->type == ExpType::$IDENTITY) { eval('$resval = isset($lhs->value->' . $rhs->value . ')? $lhs->value->' . $rhs->value . ': null;'); } else { throw new ExpParserException("Can't reference key typ " . print_r($rhs, true) . " on object"); } } else { throw new ExpParserException("Can't perform ./[] operation on primitive type " . print_r($lhs, true)); } $result = new Token(ExpType::detectType($resval), $resval); } } elseif ($sym == 'empty') { $result = new Token(ExpType::$BOOL, empty($operand->value)); } elseif (in_array($sym, $ARITHMETIC_OPS)) { $lhs = ExpType::coerceToNumber($lhs); $rhs = ExpType::coerceToNumber($rhs); eval('$resval = $lhs->value ' . $sym . '$rhs->value;'); $result = new Token(ExpType::detectType($resval), $resval); } elseif ($sym == ' -') { // Unary operator '-' $result = ExpType::coerceToNumber($operand); $result = new Token($result->type, -$result->value); } elseif (in_array($sym, $RELATIONAL_OPS)) { $result = new Token(ExpType::$BOOL); // special case: one of the operator is null if ($lhs->type == ExpType::$NULL && $rhs->type != ExpType::$NULL || $lhs->type != ExpType::$NULL && $rhs->type == ExpType::$NULL) { $result->value = in_array($sym, array('<', '>', '<=', '>=', '==')) ? false : true; } eval('$result->value = $lhs->value ' . $sym . ' $rhs->value;'); } elseif (in_array($sym, $LOGICAL_OPS)) { $result = new Token(ExpType::$BOOL); $lhs = ExpType::coerceToBool($lhs); $rhs = ExpType::coerceToBool($rhs); eval('$result->value = $lhs->value ' . $sym . ' $rhs->value;'); } elseif ($sym == '!') { $result = ExpType::coerceToBool($operand); $result = new Token(ExpType::$BOOL, !$result->value); } else { throw new ExpParserException("Uncovered operator: " . $sym); } array_push($operandStack, $result); return $operandStack; }