Skip to content

Commit

Permalink
Implement opcode token
Browse files Browse the repository at this point in the history
Opcode token operations are implemented inlined and execute much faster than closure-backed fcalls
  • Loading branch information
Muqsit committed Nov 10, 2022
1 parent de91e05 commit 998ec6a
Show file tree
Hide file tree
Showing 4 changed files with 148 additions and 3 deletions.
27 changes: 26 additions & 1 deletion src/muqsit/arithmexp/expression/RawExpression.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
namespace muqsit\arithmexp\expression;

use muqsit\arithmexp\expression\token\FunctionCallExpressionToken;
use muqsit\arithmexp\expression\token\OpcodeExpressionToken;
use muqsit\arithmexp\token\OpcodeToken;
use RuntimeException;
use function array_slice;

Expand All @@ -15,7 +17,30 @@ public function evaluate(array $variable_values = []) : int|float{
$stack = [];
$ptr = -1;
foreach($this->postfix_expression_tokens as $token){
if($token instanceof FunctionCallExpressionToken){
if($token instanceof OpcodeExpressionToken){
$code = $token->code;
$rvalue = $stack[$ptr];
--$ptr;
if($code === OpcodeToken::OP_BINARY_ADD){
$stack[$ptr] += $rvalue;
}elseif($code === OpcodeToken::OP_BINARY_DIV){
$stack[$ptr] /= $rvalue;
}elseif($code === OpcodeToken::OP_BINARY_EXP){
$stack[$ptr] **= $rvalue;
}elseif($code === OpcodeToken::OP_BINARY_MOD){
$stack[$ptr] %= $rvalue;
}elseif($code === OpcodeToken::OP_BINARY_MUL){
$stack[$ptr] *= $rvalue;
}elseif($code === OpcodeToken::OP_BINARY_SUB){
$stack[$ptr] -= $rvalue;
}elseif($code === OpcodeToken::OP_UNARY_NVE){
$stack[$ptr] = -$rvalue;
}elseif($code === OpcodeToken::OP_UNARY_PVE){
$stack[$ptr] = +$rvalue;
}else{
throw new RuntimeException("Don't know how to evaluate opcode: {$code}");
}
}elseif($token instanceof FunctionCallExpressionToken){
$ptr -= $token->argument_count - 1;
$stack[$ptr] = ($token->function)(...array_slice($stack, $ptr, $token->argument_count));
}else{
Expand Down
42 changes: 42 additions & 0 deletions src/muqsit/arithmexp/expression/token/OpcodeExpressionToken.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

declare(strict_types=1);

namespace muqsit\arithmexp\expression\token;

use muqsit\arithmexp\expression\Expression;
use muqsit\arithmexp\Position;
use muqsit\arithmexp\token\OpcodeToken;
use RuntimeException;

final class OpcodeExpressionToken implements ExpressionToken{

/**
* @param Position $position
* @param OpcodeToken::OP_* $code
*/
public function __construct(
public Position $position,
public int $code
){}

public function getPos() : Position{
return $this->position;
}

public function isDeterministic() : bool{
return true;
}

public function retrieveValue(Expression $expression, array $variables) : int|float{
throw new RuntimeException("Don't know how to retrieve value of " . self::class);
}

public function equals(ExpressionToken $other) : bool{
return $other instanceof self && $other->code === $this->code;
}

public function __toString() : string{
return OpcodeToken::opcodeToString($this->code);
}
}
72 changes: 72 additions & 0 deletions src/muqsit/arithmexp/token/OpcodeToken.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<?php

declare(strict_types=1);

namespace muqsit\arithmexp\token;

use muqsit\arithmexp\expression\token\OpcodeExpressionToken;
use muqsit\arithmexp\Position;
use muqsit\arithmexp\token\builder\ExpressionTokenBuilderState;

final class OpcodeToken extends SimpleToken{

public const OP_BINARY_ADD = 0;
public const OP_BINARY_DIV = 1;
public const OP_BINARY_EXP = 2;
public const OP_BINARY_MOD = 3;
public const OP_BINARY_MUL = 4;
public const OP_BINARY_SUB = 5;
public const OP_UNARY_NVE = 6;
public const OP_UNARY_PVE = 7;

/**
* @param self::OP_* $code
* @return string
*/
public static function opcodeToString(int $code) : string{
return match($code){
self::OP_BINARY_ADD, self::OP_UNARY_PVE => "+",
self::OP_BINARY_DIV => "/",
self::OP_BINARY_EXP => "**",
self::OP_BINARY_MOD => "%",
self::OP_BINARY_MUL => "*",
self::OP_BINARY_SUB, self::OP_UNARY_NVE => "-"
};
}

/**
* @param Position $position
* @param self::OP_* $code
*/
public function __construct(
Position $position,
private int $code
){
parent::__construct(TokenType::OPCODE(), $position);
}

/**
* @return self::OP_*
*/
public function getCode() : int{
return $this->code;
}

public function repositioned(Position $position) : self{
return new self($position, $this->code);
}

public function writeExpressionTokens(ExpressionTokenBuilderState $state) : void{
$state->current_group[$state->current_index] = new OpcodeExpressionToken($this->position, $this->code);
}

public function __debugInfo() : array{
$info = parent::__debugInfo();
$info["code"] = $this->code;
return $info;
}

public function jsonSerialize() : string{
return self::opcodeToString($this->code);
}
}
10 changes: 8 additions & 2 deletions src/muqsit/arithmexp/token/TokenType.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ final class TokenType{
public const FUNCTION_CALL_ARGUMENT_SEPARATOR = 2;
public const IDENTIFIER = 3;
public const NUMERIC_LITERAL = 4;
public const PARENTHESIS = 5;
public const UNARY_OPERATOR = 6;
public const OPCODE = 5;
public const PARENTHESIS = 6;
public const UNARY_OPERATOR = 7;

public static function BINARY_OPERATOR() : self{
static $instance = null;
Expand All @@ -39,6 +40,11 @@ public static function NUMERIC_LITERAL() : self{
return $instance ??= new self(self::NUMERIC_LITERAL, "Numeric Literal");
}

public static function OPCODE() : self{
static $instance = null;
return $instance ??= new self(self::NUMERIC_LITERAL, "Opcode");
}

public static function PARENTHESIS() : self{
static $instance = null;
return $instance ??= new self(self::PARENTHESIS, "Parenthesis");
Expand Down

0 comments on commit 998ec6a

Please sign in to comment.