public function testWithersReturnNewModifiedInstance() { $value = 'bob'; $newValue = 'alice'; $type = new TokenType(TokenType::DYNAMIC_ARRAY_TYPE); $token = new Token($value, $type); $newToken = $token->withValue($newValue); $this->assertEquals($value, $token->getValue()); $this->assertEquals($type->getValue(), $token->getType()); $this->assertInstanceOf(Token::class, $newToken); $this->assertEquals($newValue, $newToken->getValue()); $this->assertEquals($type->getValue(), $newToken->getType()); }
public function __toString() { return sprintf('(%s) %s', $this->type->getValue(), $this->value); }
/** * @dataProvider provideAcceptableTypes */ public function testCanCreateType(string $typeConstant) { $type = new TokenType($typeConstant); $this->assertEquals($type->getValue(), constant(sprintf('%s::%s', TokenType::class, $typeConstant))); }
/** * Evaluate the next expression * @param string &$buffer The expression result value is written here * @param Token $token An optional start from token * @return int The Type ID of the expression result */ protected function evalExp(&$buffer, $token = NULL) { static $depth = -1; $depth++; $this->debug(__METHOD__, __LINE__, 'evalExp depth: ' . $depth); $buffer = ''; // write to arg by reference if (!$token) { $token = $this->getToken(); } if (!$token) { throw new SyntaxException($this->getLine(), $this->getColumn(), 'Failed evaluating expression', __FILE__, __LINE__); } switch ($token->getType()) { case TokenType::IDENTIFIER: $this->debug(__METHOD__, __LINE__, "Evaluating IDENTIFIER token"); $varname = $token->getValue(); // If identifier not found in memory if (!$this->memory->hasVar($varname)) { throw new SyntaxException($token->getLine(), $token->getColumn(), "Undefined identifier: '{$varname}'", __FILE__, __LINE__); } $variable = $this->memory->getVar($varname); $leftside = $variable->getValue(); $vartype = $variable->getType(); break; case TokenType::MINVOKE: $this->debug(__METHOD__, __LINE__, "Evaluating MINVOKE token"); $result = $this->procMethodInvoke($token); $leftside = $result->getValue(); $vartype = $result->getType(); unset($result); break; case TokenType::INTEGER: $this->debug(__METHOD__, __LINE__, "Evaluating INTEGER token"); $vartype = TokenType::INTEGER; $leftside = $token->getValue(); break; case TokenType::FLOAT: $this->debug(__METHOD__, __LINE__, "Evaluating FLOAT token"); $vartype = TokenType::FLOAT; $leftside = $token->getValue(); break; case TokenType::STRING: $this->debug(__METHOD__, __LINE__, "Evaluating STRING token"); $vartype = TokenType::STRING; $leftside = $token->getValue(); break; case TokenType::SYMBOL: $this->debug(__METHOD__, __LINE__, "Evaluating SYMBOL token"); list($leftside, $vartype) = $this->procSymbol($token); // $vartype = TokenType::SYMBOL; if (!$leftside) { die("O"); return; } break; case TokenType::KEYWORD: default: throw new SyntaxException($token->getLine(), $token->getColumn(), 'Invalid expression: ' . $token->getValue(), __FILE__, __LINE__); break; } $buffer = $leftside; // assign it incase we exit ;) $this->debug(__METHOD__, __LINE__, "Leftside_Value: {$buffer}"); // $this->debug(__METHOD__, __LINE__, "Leftside_Type: $vartype"); $this->debug(__METHOD__, __LINE__, "Leftside_TypeName: " . TokenType::map($vartype)); // Get the operator part of the expression $token = $this->getToken(); switch ($token->getType()) { case TokenType::SYMBOL: switch ($token->getValue()) { case ')': $depth--; $this->ungetCharacter(); return $vartype; // hello // hello case ';': $depth--; // $this->ungetCharacter(); return $vartype; // hello break; case ',': return $vartype; break; case '+': // also for string concat // also for string concat case '-': case '*': case '/': case '%': // math operators break; case '==': case '!=': case '<': case '>': case '>=': case '<=': // comparison operators break; case '&&': case '||': // logical break; case '&': case '|': // bitwise break; default: throw new SyntaxException($token->getLine(), $token->getColumn(), 'Invalid operator: ' . $token->getValue(), __FILE__, __LINE__); break; } break; default: throw new SyntaxException($token->getLine(), $token->getColumn(), 'Invalid symbol: ' . $token->getValue(), __FILE__, __LINE__); break; } $operator = $token->getValue(); $this->debug(__METHOD__, __LINE__, 'Operator_Value: ' . $operator); // $this->debug(__METHOD__, __LINE__, 'Operator_Type: ' . $token->getType()); $this->debug(__METHOD__, __LINE__, "Operator_TypeName: " . TokenType::map($token->getType())); // Recursive call to evaluate the expression further $rightside = ''; $vartype2 = $this->evalExp($rightside); $this->debug(__METHOD__, __LINE__, 'Rightside_Value: ' . $rightside); // $this->debug(__METHOD__, __LINE__, "Rightside_Type: $vartype2"); $this->debug(__METHOD__, __LINE__, "Rightside_TypeName: " . TokenType::map($vartype2)); switch ($vartype) { case TokenType::FLOAT: $n1 = floatval($leftside); $n2 = floatval($rightside); if ($operator == '+') { $n3 = $n1 + $n2; } elseif ($operator == '-') { $n3 = $n1 - $n2; } elseif ($operator == '*') { $n3 = $n1 * $n2; } elseif ($operator == '/') { $n3 = $n1 / $n2; } elseif ($operator == '==') { $n3 = $n1 == $n2; } elseif ($operator == '<') { $n3 = $n1 < $n2; } elseif ($operator == '>') { $n3 = $n1 > $n2; } elseif ($operator == '<=') { $n3 = $n1 <= $n2; } elseif ($operator == '>=') { $n3 = $n1 >= $n2; } elseif ($operator == '!=') { $n3 = $n1 != $n2; } else { throw new SyntaxException($token->getLine(), $token->getColumn(), 'Illegal float operator: ' . $operator, __FILE__, __LINE__); } // set the output buffer $buffer = floatval($n3); break; case TokenType::INTEGER: $n1 = intval($leftside); $n2 = intval($rightside); if ($operator == '+') { $n3 = $n1 + $n2; } elseif ($operator == '-') { $n3 = $n1 - $n2; } elseif ($operator == '*') { $n3 = $n1 * $n2; } elseif ($operator == '/') { $n3 = $n1 / $n2; } elseif ($operator == '%') { $n3 = $n1 % $n2; } elseif ($operator == '==') { $n3 = $n1 == $n2; } elseif ($operator == '<') { $n3 = $n1 < $n2; } elseif ($operator == '>') { $n3 = $n1 > $n2; } elseif ($operator == '<=') { $n3 = $n1 <= $n2; } elseif ($operator == '>=') { $n3 = $n1 >= $n2; } elseif ($operator == '!=') { $n3 = $n1 != $n2; } elseif ($operator == '&&') { $n3 = (int) ($n1 && $n2); } elseif ($operator == '||') { $n3 = (int) ($n1 || $n2); } elseif ($operator == '&') { $n3 = $n1 & $n2; } elseif ($operator == '|') { $n3 = $n1 | $n2; } else { throw new SyntaxException($token->getLine(), $token->getColumn(), 'Illegal numerical operator: ' . $operator, __FILE__, __LINE__); } // set the output buffer $buffer = intval($n3); break; case TokenType::STRING: $tmp = $rightside; if ($operator == '+') { $buffer .= $rightside; } elseif ($operator == '==') { $buffer = $buffer == $tmp ? '1' : '0'; } elseif ($operator == '<') { $buffer = $buffer < $tmp ? '1' : '0'; } elseif ($operator == '>') { $buffer = $buffer > $tmp ? '1' : '0'; } elseif ($operator == '!=') { $buffer = $buffer != $tmp ? '1' : '0'; } elseif ($operator == '<=') { $buffer = $buffer <= $tmp ? '1' : '0'; } elseif ($operator == '>=') { $buffer = $buffer >= $tmp ? '1' : '0'; } elseif ($operator == '&&') { $buffer = $buffer && $tmp ? '1' : '0'; } elseif ($operator == '||') { $buffer = $buffer || $tmp ? '1' : '0'; } elseif ($operator == '&') { $buffer = $buffer & $tmp ? '1' : '0'; } elseif ($operator == '|') { $buffer = $buffer | $tmp ? '1' : '0'; } else { throw new SyntaxException($token->getLine(), $token->getColumn(), 'Illegal string operator: ' . $operator, __FILE__, __LINE__); } break; default: throw new SyntaxException($token->getLine(), $token->getColumn(), 'Unexpected variable type: ' . TokenType::map($vartype), __FILE__, __LINE__); break; } $this->debug(__METHOD__, __LINE__, "Expression: {$leftside} {$operator} {$rightside}"); $this->debug(__METHOD__, __LINE__, 'Result: ' . $buffer); $this->debug(__METHOD__, __LINE__, 'Type: ' . TokenType::map($vartype)); $depth--; return $vartype; }