Completed
Push — master ( 68be21...073c54 )
by Kévin
02:41
created

Parser   A

Complexity

Total Complexity 28

Size/Duplication

Total Lines 192
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 192
wmc 28
lcom 1
cbo 4
rs 10

2 Methods

Rating   Name   Duplication   Size   Complexity  
A parse() 0 10 2
D visit() 0 138 26
1
<?php
2
3
namespace RulerZ\Parser;
4
5
use Hoa\Compiler;
6
use Hoa\File;
7
use Hoa\Ruler;
8
use Hoa\Visitor;
9
10
use RulerZ\Model;
11
12
/**
13
 * Parses a rule.
14
 */
15
class Parser implements Visitor\Visit
16
{
17
    /**
18
     * Parser.
19
     *
20
     * @var \Hoa\Compiler\Llk\Parser $parser
21
     */
22
    private $parser;
23
24
    /**
25
     * Root.
26
     *
27
     * @var \RulerZ\Model\Rule object
28
     */
29
    private $root;
30
31
    /**
32
     * Next positional parameter index.
33
     *
34
     * @var int
35
     */
36
    private $nextParameterIndex = 0;
37
38
    /**
39
     * Parses the rule into an equivalent AST.
40
     *
41
     * @param string $rule The rule represented as a string.
42
     *
43
     * @return \RulerZ\Model\Rule
44
     */
45
    public function parse($rule)
46
    {
47
        if ($this->parser === null) {
48
            $this->parser = Compiler\Llk::load(
49
                new File\Read(__DIR__ .'/../Grammar.pp')
50
            );
51
        }
52
53
        return $this->visit($this->parser->parse($rule));
54
    }
55
56
    /**
57
     * Visit an element.
58
     *
59
     * @access  public
60
     * @param   \Hoa\Visitor\Element  $element    Element to visit.
61
     * @param   mixed                 &$handle    Handle (reference).
62
     * @param   mixed                 $eldnah     Handle (not reference).
63
     *
64
     * @return  \RulerZ\Model\Rule
65
     *
66
     * @throws  \Hoa\Ruler\Exception\Interpreter
67
     */
68
    public function visit(Visitor\Element $element, &$handle = null, $eldnah = null)
69
    {
70
        /** @var \Hoa\Compiler\Llk\TreeNode $element */
71
        $id       = $element->getId();
72
        $variable = false !== $eldnah;
73
74
        switch ($id) {
75
            case '#expression':
76
                $this->root             = new Model\Rule();
77
                $this->root->expression = $element->getChild(0)->accept(
78
                    $this,
79
                    $handle,
80
                    $eldnah
81
                );
82
83
                return $this->root;
84
85
            case '#operation':
86
                $children = $element->getChildren();
87
                $left     = $children[0]->accept($this, $handle, $eldnah);
88
                $right    = $children[2]->accept($this, $handle, $eldnah);
89
                $name     = $children[1]->accept($this, $handle, false);
90
91
                return $this->root->_operator(
92
                    $name,
93
                    [$left, $right],
94
                    false
95
                );
96
97
            case '#variable_access':
98
                $children = $element->getChildren();
99
                $name     = $children[0]->accept($this, $handle, $eldnah);
100
                array_shift($children);
101
102
                foreach ($children as $child) {
103
                    $_child = $child->accept($this, $handle, $eldnah);
104
105
                    switch ($child->getId()) {
106
                        case '#attribute_access':
107
                            $name->attribute($_child);
108
                            break;
109
                    }
110
                }
111
112
                return $name;
113
114
            case '#attribute_access':
115
                return $element->getChild(0)->accept($this, $handle, false);
116
117
            case '#array_declaration':
118
                $out = [];
119
120
                foreach ($element->getChildren() as $child) {
121
                    $out[] = $child->accept($this, $handle, $eldnah);
122
                }
123
124
                return $out;
125
126
            case '#function_call':
127
                $children = $element->getChildren();
128
                $name     = $children[0]->accept($this, $handle, false);
129
                array_shift($children);
130
131
                $arguments = [];
132
133
                foreach ($children as $child) {
134
                    $arguments[] = $child->accept($this, $handle, $eldnah);
135
                }
136
137
                return $this->root->_operator(
138
                    $name,
139
                    $arguments,
140
                    true
141
                );
142
143
            case '#and':
144
            case '#or':
145
            case '#xor':
146
                $name     = substr($id, 1);
147
                $children = $element->getChildren();
148
                $left     = $children[0]->accept($this, $handle, $eldnah);
149
                $right    = $children[1]->accept($this, $handle, $eldnah);
150
151
                return $this->root->operation($name, [$left, $right]);
152
153
            case '#not':
154
                return $this->root->operation(
155
                    'not',
156
                    [$element->getChild(0)->accept($this, $handle, $eldnah)]
157
                );
158
159
            case 'token':
0 ignored issues
show
Coding Style introduced by
There must be a comment when fall-through is intentional in a non-empty case body
Loading history...
160
                $token = $element->getValueToken();
161
                $value = $element->getValueValue();
162
163
                switch ($token) {
164
                    case 'identifier':
165
                        return true === $variable ? $this->root->variable($value) : $value;
166
167
                    case 'named_parameter':
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
168
169
                        return new Model\Parameter(substr($value, 1));
170
171
                    case 'positional_parameter':
172
                        $index = $this->nextParameterIndex++;
173
174
                        return new Model\Parameter($index);
175
176
                    case 'true':
177
                        return true;
178
179
                    case 'false':
180
                        return false;
181
182
                    case 'null':
183
                        return null;
184
185
                    case 'float':
186
                        return (float) $value;
187
188
                    case 'integer':
189
                        return (int) $value;
190
191
                    case 'string':
192
                        return str_replace(
193
                            '\\' . $value[0],
194
                            $value[0],
195
                            substr($value, 1, -1)
196
                        );
197
198
                    default:
199
                        throw new Ruler\Exception\Interpreter('Token %s is unknown.', 0, $token);
200
                }
201
202
            default:
203
                throw new Ruler\Exception\Interpreter('Element %s is unknown.', 1, $id);
204
        }
205
    }
206
}
207