Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 23
0.00% covered (danger)
0.00%
0 / 2
CRAP
0.00% covered (danger)
0.00%
0 / 1
Operator
0.00% covered (danger)
0.00%
0 / 23
0.00% covered (danger)
0.00%
0 / 2
30
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 operate
0.00% covered (danger)
0.00%
0 / 19
0.00% covered (danger)
0.00%
0 / 1
12
1<?php
2/**
3 * @author Niklas Laxström, Tim Starling
4 * @license GPL-2.0-or-later
5 * @file
6 */
7
8namespace CLDRPluralRuleParser\Converter;
9
10use CLDRPluralRuleParser\Converter;
11
12/**
13 * Helper for Converter.
14 * An operator object, representing a region of the input string (for error
15 * messages), and the binary operator at that location.
16 */
17class Operator extends Fragment {
18    /** @var string The name */
19    public $name;
20
21    /**
22     * Each op type has three characters: left operand type, right operand type and result type
23     *
24     *   b = boolean
25     *   n = number
26     *   r = range
27     *
28     * A number is a kind of range.
29     *
30     * @var array
31     */
32    private const OP_TYPES = [
33        'or' => 'bbb',
34        'and' => 'bbb',
35        'is' => 'nnb',
36        'is-not' => 'nnb',
37        'in' => 'nrb',
38        'not-in' => 'nrb',
39        'within' => 'nrb',
40        'not-within' => 'nrb',
41        'mod' => 'nnn',
42        ',' => 'rrr',
43        '..' => 'nnr',
44    ];
45
46    /**
47     * Map converting from the abbreviation to the full form.
48     *
49     * @var array
50     */
51    private const TYPE_SPEC_MAP = [
52        'b' => 'boolean',
53        'n' => 'number',
54        'r' => 'range',
55    ];
56
57    /**
58     * Map for converting the new operators introduced in Rev 33 to the old forms
59     *
60     * @var array
61     */
62    private const ALIAS_MAP = [
63        '%' => 'mod',
64        '!=' => 'not-in',
65        '=' => 'in'
66    ];
67
68    /**
69     * Initialize a new instance of a CLDRPluralRuleConverterOperator object
70     *
71     * @param Converter $parser The parser
72     * @param string $name The operator name
73     * @param int $pos The length
74     * @param int $length
75     */
76    public function __construct( Converter $parser, $name, $pos, $length ) {
77        parent::__construct( $parser, $pos, $length );
78        if ( isset( self::ALIAS_MAP[$name] ) ) {
79            $name = self::ALIAS_MAP[$name];
80        }
81        $this->name = $name;
82    }
83
84    /**
85     * Compute the operation
86     *
87     * @param Expression $left The left part of the expression
88     * @param Expression $right The right part of the expression
89     * @return Expression The result of the operation
90     */
91    public function operate( Expression $left, Expression $right ): Expression {
92        $typeSpec = self::OP_TYPES[$this->name];
93
94        $leftType = self::TYPE_SPEC_MAP[$typeSpec[0]];
95        $rightType = self::TYPE_SPEC_MAP[$typeSpec[1]];
96        $resultType = self::TYPE_SPEC_MAP[$typeSpec[2]];
97
98        $start = min( $this->pos, $left->pos, $right->pos );
99        $end = max( $this->end, $left->end, $right->end );
100        $length = $end - $start;
101
102        $newExpr = new Expression(
103            $this->parser,
104            $resultType,
105            "{$left->rpn} {$right->rpn} {$this->name}",
106            $start,
107            $length
108        );
109
110        if ( !$left->isType( $leftType ) ) {
111            $newExpr->error( "invalid type for left operand: expected $leftType, got {$left->type}" );
112        }
113
114        if ( !$right->isType( $rightType ) ) {
115            $newExpr->error( "invalid type for right operand: expected $rightType, got {$right->type}" );
116        }
117
118        return $newExpr;
119    }
120}