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); } } }
/** * 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'))); }
public static function parse($tokenStream, $dataContext) { $scopes = array(); $expression = new PrimitiveExp(new Token('final')); while ($token = array_shift($tokenStream)) { // split non-primitive expression into primitive ones switch ($token->type) { case ExpType::$PAREN: switch ($token->value) { case '[': $expression->append(new Token(ExpType::$DOT, '.')); // drop through // drop through case '(': ExpParser::scopePush($expression, $scopes, $token); break; case ']': if ($expression->reason->value != '[') { throw new ExpParserException("Unbalanced [], should be detected in Lexer"); } ExpParser::scopePop($expression, $scopes, $dataContext); break; case ')': if ($expression->reason->value != '(') { throw new ExpParserException("Unbalanced (), should be detected in Lexer"); } ExpParser::scopePop($expression, $scopes, $dataContext); // close if it's a function if ($expression->reason->type == ExpType::$FUNCTION) { ExpParser::scopePop($expression, $scopes, $dataContext); } break; default: throw new ExpParserException("Token error: " . print_r($token, true)); } break; case ExpType::$FUNCTION: ExpParser::scopePush($expression, $scopes, $token); break; case ExpType::$TERNARY: if ($token->value != '?') { throw new ExpParserException("Ternary token error"); } $nextTernary = ExpParser::findNextTernary($tokenStream, 1); $indicator = ExpType::coerceToBool($expression->evaluate($dataContext)); if ($indicator->value) { // parsed?todo:skip $nextCloseSymbol = ExpParser::findNextCloseSymbol($tokenStream, $nextTernary + 1); array_splice($tokenStream, $nextTernary, $nextCloseSymbol - $nextTernary); } else { // parsed?skip:todo $tokenStream = array_slice($tokenStream, $nextTernary + 1); } $expression = new PrimitiveExp($expression->reason); break; case ExpType::$COMMA: if ($expression->reason->value != '(') { throw new ExpParserException("Unbalanced (), should be detected in Lexer"); } ExpParser::scopePop($expression, $scopes, $dataContext); ExpParser::scopePush($expression, $scopes, new Token(ExpType::$PAREN, '(')); break; default: $expression->append($token); } } if ($expression->reason->type != 'final') { throw new ExpParserException("Gramma error on the non-primitive expression"); } return $expression->evaluate($dataContext); }