Skip to content

Commit

Permalink
Implement logical operators (address #9)
Browse files Browse the repository at this point in the history
  • Loading branch information
Muqsit committed Jul 9, 2024
1 parent df0d2c2 commit 77f988c
Show file tree
Hide file tree
Showing 5 changed files with 133 additions and 58 deletions.
4 changes: 2 additions & 2 deletions src/muqsit/arithmexp/Scanner.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ public static function createDefault(OperatorManager $operator_manager) : self{
new ParenthesisTokenBuilder(),
new NumericLiteralTokenBuilder(),
new FunctionCallTokenBuilder(),
new IdentifierTokenBuilder(),
UnaryOperatorTokenBuilder::createDefault($operator_manager->unary_registry),
BinaryOperatorTokenBuilder::createDefault($operator_manager->binary_registry)
BinaryOperatorTokenBuilder::createDefault($operator_manager->binary_registry),
new IdentifierTokenBuilder()
]);
}

Expand Down
118 changes: 79 additions & 39 deletions src/muqsit/arithmexp/expression/RawExpression.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,28 +41,33 @@ public function __construct(string $expression, array $postfix_expression_tokens
$this->by_kind[($i++ << 5) | match(true){
$token instanceof OpcodeExpressionToken => match($token->code){
OpcodeToken::OP_BINARY_ADD => 0,
OpcodeToken::OP_BINARY_DIV => 1,
OpcodeToken::OP_BINARY_EQUAL => 2,
OpcodeToken::OP_BINARY_EQUAL_NOT => 3,
OpcodeToken::OP_BINARY_EXP => 4,
OpcodeToken::OP_BINARY_GREATER_THAN => 5,
OpcodeToken::OP_BINARY_GREATER_THAN_EQUAL_TO => 6,
OpcodeToken::OP_BINARY_IDENTICAL => 7,
OpcodeToken::OP_BINARY_IDENTICAL_NOT => 8,
OpcodeToken::OP_BINARY_LESSER_THAN => 9,
OpcodeToken::OP_BINARY_LESSER_THAN_EQUAL_TO => 10,
OpcodeToken::OP_BINARY_MOD => 11,
OpcodeToken::OP_BINARY_MUL => 12,
OpcodeToken::OP_BINARY_SPACESHIP => 13,
OpcodeToken::OP_BINARY_SUB => 14,
OpcodeToken::OP_UNARY_NOT => 15,
OpcodeToken::OP_UNARY_NVE => 16,
OpcodeToken::OP_UNARY_PVE => 17
OpcodeToken::OP_BINARY_AND_SYMBOL => 1,
OpcodeToken::OP_BINARY_AND_TEXTUAL => 2,
OpcodeToken::OP_BINARY_DIV => 3,
OpcodeToken::OP_BINARY_EQUAL => 4,
OpcodeToken::OP_BINARY_EQUAL_NOT => 5,
OpcodeToken::OP_BINARY_EXP => 6,
OpcodeToken::OP_BINARY_GREATER_THAN => 7,
OpcodeToken::OP_BINARY_GREATER_THAN_EQUAL_TO => 8,
OpcodeToken::OP_BINARY_IDENTICAL => 9,
OpcodeToken::OP_BINARY_IDENTICAL_NOT => 10,
OpcodeToken::OP_BINARY_LESSER_THAN => 11,
OpcodeToken::OP_BINARY_LESSER_THAN_EQUAL_TO => 12,
OpcodeToken::OP_BINARY_MOD => 13,
OpcodeToken::OP_BINARY_MUL => 14,
OpcodeToken::OP_BINARY_OR_SYMBOL => 15,
OpcodeToken::OP_BINARY_OR_TEXTUAL => 16,
OpcodeToken::OP_BINARY_SPACESHIP => 17,
OpcodeToken::OP_BINARY_SUB => 18,
OpcodeToken::OP_BINARY_XOR => 19,
OpcodeToken::OP_UNARY_NOT => 20,
OpcodeToken::OP_UNARY_NVE => 21,
OpcodeToken::OP_UNARY_PVE => 22
},
$token instanceof NumericLiteralExpressionToken,
$token instanceof BooleanLiteralExpressionToken => 18,
$token instanceof VariableExpressionToken => 19,
$token instanceof FunctionCallExpressionToken => 20,
$token instanceof BooleanLiteralExpressionToken => 23,
$token instanceof VariableExpressionToken => 24,
$token instanceof FunctionCallExpressionToken => 25,
default => 31
}] = $token;
}
Expand All @@ -80,122 +85,157 @@ public function evaluate(array $variable_values = []) : int|float|bool{
$stack[--$ptr] += $rvalue;
break;
case 1:
assert($token instanceof OpcodeExpressionToken);
assert($token->code === OpcodeToken::OP_BINARY_AND_SYMBOL);
$lvalue = $stack[$ptr - 1];
$rvalue = $stack[$ptr];
$stack[--$ptr] = $lvalue && $rvalue;
break;
case 2:
assert($token instanceof OpcodeExpressionToken);
assert($token->code === OpcodeToken::OP_BINARY_AND_TEXTUAL);
$lvalue = $stack[$ptr - 1];
$rvalue = $stack[$ptr];
$stack[--$ptr] = ($lvalue and $rvalue);
break;
case 3:
assert($token instanceof OpcodeExpressionToken);
assert($token->code === OpcodeToken::OP_BINARY_DIV);
$rvalue = $stack[$ptr];
$stack[--$ptr] /= $rvalue;
break;
case 2:
case 4:
assert($token instanceof OpcodeExpressionToken);
assert($token->code === OpcodeToken::OP_BINARY_EQUAL);
$lvalue = $stack[$ptr - 1];
$rvalue = $stack[$ptr];
$stack[--$ptr] = $lvalue == $rvalue;
break;
case 3:
case 5:
assert($token instanceof OpcodeExpressionToken);
assert($token->code === OpcodeToken::OP_BINARY_EQUAL_NOT);
$lvalue = $stack[$ptr - 1];
$rvalue = $stack[$ptr];
$stack[--$ptr] = $lvalue != $rvalue;
break;
case 4:
case 6:
assert($token instanceof OpcodeExpressionToken);
assert($token->code === OpcodeToken::OP_BINARY_EXP);
$rvalue = $stack[$ptr];
$stack[--$ptr] **= $rvalue;
break;
case 5:
case 7:
assert($token instanceof OpcodeExpressionToken);
assert($token->code === OpcodeToken::OP_BINARY_GREATER_THAN);
$lvalue = $stack[$ptr - 1];
$rvalue = $stack[$ptr];
$stack[--$ptr] = $lvalue > $rvalue;
break;
case 6:
case 8:
assert($token instanceof OpcodeExpressionToken);
assert($token->code === OpcodeToken::OP_BINARY_GREATER_THAN_EQUAL_TO);
$lvalue = $stack[$ptr - 1];
$rvalue = $stack[$ptr];
$stack[--$ptr] = $lvalue >= $rvalue;
break;
case 7:
case 9:
assert($token instanceof OpcodeExpressionToken);
assert($token->code === OpcodeToken::OP_BINARY_IDENTICAL);
$lvalue = $stack[$ptr - 1];
$rvalue = $stack[$ptr];
$stack[--$ptr] = $lvalue === $rvalue;
break;
case 8:
case 10:
assert($token instanceof OpcodeExpressionToken);
assert($token->code === OpcodeToken::OP_BINARY_IDENTICAL_NOT);
$lvalue = $stack[$ptr - 1];
$rvalue = $stack[$ptr];
$stack[--$ptr] = $lvalue !== $rvalue;
break;
case 9:
case 11:
assert($token instanceof OpcodeExpressionToken);
assert($token->code === OpcodeToken::OP_BINARY_LESSER_THAN);
$lvalue = $stack[$ptr - 1];
$rvalue = $stack[$ptr];
$stack[--$ptr] = $lvalue < $rvalue;
break;
case 10:
case 12:
assert($token instanceof OpcodeExpressionToken);
assert($token->code === OpcodeToken::OP_BINARY_LESSER_THAN_EQUAL_TO);
$lvalue = $stack[$ptr - 1];
$rvalue = $stack[$ptr];
$stack[--$ptr] = $lvalue <= $rvalue;
break;
case 11:
case 13:
assert($token instanceof OpcodeExpressionToken);
assert($token->code === OpcodeToken::OP_BINARY_MOD);
$rvalue = $stack[$ptr];
$stack[--$ptr] %= $rvalue;
break;
case 12:
case 14:
assert($token instanceof OpcodeExpressionToken);
assert($token->code === OpcodeToken::OP_BINARY_MUL);
$rvalue = $stack[$ptr];
$stack[--$ptr] *= $rvalue;
break;
case 13:
case 15:
assert($token instanceof OpcodeExpressionToken);
assert($token->code === OpcodeToken::OP_BINARY_OR_SYMBOL);
$lvalue = $stack[$ptr - 1];
$rvalue = $stack[$ptr];
$stack[--$ptr] = $lvalue || $rvalue;
break;
case 16:
assert($token instanceof OpcodeExpressionToken);
assert($token->code === OpcodeToken::OP_BINARY_OR_TEXTUAL);
$lvalue = $stack[$ptr - 1];
$rvalue = $stack[$ptr];
$stack[--$ptr] = ($lvalue or $rvalue);
break;
case 17:
assert($token instanceof OpcodeExpressionToken);
assert($token->code === OpcodeToken::OP_BINARY_SPACESHIP);
$lvalue = $stack[$ptr - 1];
$rvalue = $stack[$ptr];
$stack[--$ptr] = $lvalue <=> $rvalue;
break;
case 14:
case 18:
assert($token instanceof OpcodeExpressionToken);
assert($token->code === OpcodeToken::OP_BINARY_SUB);
$rvalue = $stack[$ptr];
$stack[--$ptr] -= $rvalue;
break;
case 15:
case 19:
assert($token instanceof OpcodeExpressionToken);
assert($token->code === OpcodeToken::OP_BINARY_XOR);
$lvalue = $stack[$ptr - 1];
$rvalue = $stack[$ptr];
$stack[--$ptr] = ($lvalue xor $rvalue);
break;
case 20:
assert($token instanceof OpcodeExpressionToken);
assert($token->code === OpcodeToken::OP_UNARY_NOT);
$stack[$ptr] = !$stack[$ptr];
break;
case 16:
case 21:
assert($token instanceof OpcodeExpressionToken);
assert($token->code === OpcodeToken::OP_UNARY_NVE);
$stack[$ptr] = -$stack[$ptr];
break;
case 17:
case 22:
assert($token instanceof OpcodeExpressionToken);
assert($token->code === OpcodeToken::OP_UNARY_PVE);
$stack[$ptr] = +$stack[$ptr];
break;
case 18:
case 23:
assert($token instanceof BooleanLiteralExpressionToken || $token instanceof NumericLiteralExpressionToken);
$stack[++$ptr] = $token->value;
break;
case 19:
case 24:
assert($token instanceof VariableExpressionToken);
$stack[++$ptr] = $variable_values[$token->label] ?? throw new InvalidArgumentException("No value supplied for variable \"{$token->label}\" in \"{$this->expression}\"");;
break;
case 20:
case 25:
assert($token instanceof FunctionCallExpressionToken);
$ptr -= $token->argument_count - 1;
$stack[$ptr] = ($token->function)(...array_slice($stack, $ptr, $token->argument_count));
Expand Down
5 changes: 5 additions & 0 deletions src/muqsit/arithmexp/operator/OperatorPrecedence.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,9 @@ interface OperatorPrecedence{
public const ADDITION_SUBTRACTION = 4;
public const COMPARISON_GREATER_LESSER = 5;
public const COMPARISON_EQUALITY = 6;
public const AND_SYMBOL = 7;
public const OR_SYMBOL = 8;
public const AND_TEXTUAL = 9;
public const XOR = 10;
public const OR_TEXTUAL = 11;
}
20 changes: 20 additions & 0 deletions src/muqsit/arithmexp/operator/binary/BinaryOperatorRegistry.php
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,26 @@ public static function createDefault() : self{
SimpleFunctionInfo::from(static fn(int|float|bool $x, int|float|bool $y) : int => $x <=> $y, FunctionFlags::DETERMINISTIC),
static fn(Parser $parser, string $expression, Token $token, string $function_name, int $argument_count, array $args) : array => [...$args, new OpcodeToken($token->getPos(), OpcodeToken::OP_BINARY_SPACESHIP, $token)]
)));
$registry->register(new SimpleBinaryOperator("&&", "And", OperatorPrecedence::AND_SYMBOL, LeftOperatorAssignment::instance(), new MacroFunctionInfo(
SimpleFunctionInfo::from(static fn(int|float|bool $x, int|float|bool $y) : bool => $x && $y, FunctionFlags::DETERMINISTIC),
static fn(Parser $parser, string $expression, Token $token, string $function_name, int $argument_count, array $args) : array => [...$args, new OpcodeToken($token->getPos(), OpcodeToken::OP_BINARY_AND_SYMBOL, $token)]
)));
$registry->register(new SimpleBinaryOperator("||", "Or", OperatorPrecedence::OR_SYMBOL, LeftOperatorAssignment::instance(), new MacroFunctionInfo(
SimpleFunctionInfo::from(static fn(int|float|bool $x, int|float|bool $y) : bool => $x || $y, FunctionFlags::DETERMINISTIC),
static fn(Parser $parser, string $expression, Token $token, string $function_name, int $argument_count, array $args) : array => [...$args, new OpcodeToken($token->getPos(), OpcodeToken::OP_BINARY_OR_SYMBOL, $token)]
)));
$registry->register(new SimpleBinaryOperator("and", "And", OperatorPrecedence::AND_TEXTUAL, LeftOperatorAssignment::instance(), new MacroFunctionInfo(
SimpleFunctionInfo::from(static fn(int|float|bool $x, int|float|bool $y) : bool => $x and $y, FunctionFlags::DETERMINISTIC),
static fn(Parser $parser, string $expression, Token $token, string $function_name, int $argument_count, array $args) : array => [...$args, new OpcodeToken($token->getPos(), OpcodeToken::OP_BINARY_AND_TEXTUAL, $token)]
)));
$registry->register(new SimpleBinaryOperator("or", "Or", OperatorPrecedence::OR_TEXTUAL, LeftOperatorAssignment::instance(), new MacroFunctionInfo(
SimpleFunctionInfo::from(static fn(int|float|bool $x, int|float|bool $y) : bool => $x or $y, FunctionFlags::DETERMINISTIC),
static fn(Parser $parser, string $expression, Token $token, string $function_name, int $argument_count, array $args) : array => [...$args, new OpcodeToken($token->getPos(), OpcodeToken::OP_BINARY_OR_TEXTUAL, $token)]
)));
$registry->register(new SimpleBinaryOperator("xor", "Xor", OperatorPrecedence::XOR, LeftOperatorAssignment::instance(), new MacroFunctionInfo(
SimpleFunctionInfo::from(static fn(int|float|bool $x, int|float|bool $y) : bool => $x xor $y, FunctionFlags::DETERMINISTIC),
static fn(Parser $parser, string $expression, Token $token, string $function_name, int $argument_count, array $args) : array => [...$args, new OpcodeToken($token->getPos(), OpcodeToken::OP_BINARY_XOR, $token)]
)));
return $registry;
}

Expand Down
44 changes: 27 additions & 17 deletions src/muqsit/arithmexp/token/OpcodeToken.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,28 @@
final class OpcodeToken extends SimpleToken{

public const OP_BINARY_ADD = 0;
public const OP_BINARY_DIV = 1;
public const OP_BINARY_EQUAL = 2;
public const OP_BINARY_EQUAL_NOT = 3;
public const OP_BINARY_EXP = 4;
public const OP_BINARY_GREATER_THAN = 5;
public const OP_BINARY_GREATER_THAN_EQUAL_TO = 6;
public const OP_BINARY_IDENTICAL = 7;
public const OP_BINARY_IDENTICAL_NOT = 8;
public const OP_BINARY_LESSER_THAN = 9;
public const OP_BINARY_LESSER_THAN_EQUAL_TO = 10;
public const OP_BINARY_MOD = 11;
public const OP_BINARY_MUL = 12;
public const OP_BINARY_SPACESHIP = 13;
public const OP_BINARY_SUB = 14;
public const OP_UNARY_NOT = 15;
public const OP_UNARY_NVE = 16;
public const OP_UNARY_PVE = 17;
public const OP_BINARY_AND_SYMBOL = 1;
public const OP_BINARY_AND_TEXTUAL = 2;
public const OP_BINARY_DIV = 3;
public const OP_BINARY_EQUAL = 4;
public const OP_BINARY_EQUAL_NOT = 5;
public const OP_BINARY_EXP = 6;
public const OP_BINARY_GREATER_THAN = 7;
public const OP_BINARY_GREATER_THAN_EQUAL_TO = 8;
public const OP_BINARY_IDENTICAL = 9;
public const OP_BINARY_IDENTICAL_NOT = 10;
public const OP_BINARY_LESSER_THAN = 11;
public const OP_BINARY_LESSER_THAN_EQUAL_TO = 12;
public const OP_BINARY_MOD = 13;
public const OP_BINARY_MUL = 14;
public const OP_BINARY_OR_SYMBOL = 15;
public const OP_BINARY_OR_TEXTUAL = 16;
public const OP_BINARY_SPACESHIP = 17;
public const OP_BINARY_SUB = 18;
public const OP_BINARY_XOR = 19;
public const OP_UNARY_NOT = 20;
public const OP_UNARY_NVE = 21;
public const OP_UNARY_PVE = 22;

/**
* @param self::OP_* $code
Expand All @@ -36,6 +41,8 @@ final class OpcodeToken extends SimpleToken{
public static function opcodeToString(int $code) : string{
return match($code){
self::OP_BINARY_ADD, self::OP_UNARY_PVE => "+",
self::OP_BINARY_AND_SYMBOL => "&&",
self::OP_BINARY_AND_TEXTUAL => "and",
self::OP_BINARY_DIV => "/",
self::OP_BINARY_EQUAL => "==",
self::OP_BINARY_EQUAL_NOT => "!=",
Expand All @@ -48,7 +55,10 @@ public static function opcodeToString(int $code) : string{
self::OP_BINARY_LESSER_THAN_EQUAL_TO => "<=",
self::OP_BINARY_MOD => "%",
self::OP_BINARY_MUL => "*",
self::OP_BINARY_OR_SYMBOL => "||",
self::OP_BINARY_OR_TEXTUAL => "or",
self::OP_BINARY_SUB, self::OP_UNARY_NVE => "-",
self::OP_BINARY_XOR => "xor",
self::OP_UNARY_NOT => "!"
};
}
Expand Down

0 comments on commit 77f988c

Please sign in to comment.