Completed
Push — master ( 00392a...f7852e )
by Richard
06:17
created

Tokenizer::source_position()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 10
ccs 9
cts 9
cp 1
rs 9.4285
cc 2
eloc 8
nc 2
nop 0
crap 2
1
<?php
2
/******************************************************************************
3
 * An implementation of dicto (scg.unibe.ch/dicto) in and for PHP.
4
 *
5
 * Copyright (c) 2016 Richard Klees <[email protected]>
6
 *
7
 * This software is licensed under The MIT License. You should have received
8
 * a copy of the license along with the code.
9
 */
10
11
namespace Lechimp\Dicto\Definition;
12
13
/**
14
 * Tokenizes a rules file.
15
 */
16
class Tokenizer implements \Iterator {
17
    /**
18
     * @var SymbolTable
19
     */
20
    protected $symbol_table;
21
22
    /**
23
     * @var mixed[]
24
     */
25
    protected $tokens;
26
27
    /**
28
     * @var int
29
     */
30
    protected $position;
31
32
    /**
33
     * @var string
34
     */
35
    protected $source;
36
37
    /**
38
     * @var string
39
     */
40
    protected $unparsed;
41
42
    /**
43
     * @var int
44
     */
45
    protected $parsing_position;
46
47
    /**
48
     * @var bool
49
     */
50
    protected $is_end_token_added;
51
52
    static $UNPARSED_PREVIEW_FOR_ERROR = 10;
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $UNPARSED_PREVIEW_FOR_ERROR.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
53
54 58
    public function __construct(SymbolTable $symbol_table, $source) {
55 58
        assert('is_string($source)');
56 58
        $this->symbol_table = $symbol_table;
57 58
        $this->tokens = array();
58 58
        $this->position = 0;
59 58
        $this->source = $source;
60 58
        $this->unparsed = $source;
61 58
        $this->is_end_token_added = false;
62 58
    }
63
64
    // Methods from Iterator-interface
65
66
    /**
67
     * @return  array (Symbol,$matches)
68
     */
69 52
    public function current() {
70 52
        $this->maybe_parse_next_token();
71 49
        return $this->tokens[$this->position];
72
    }
73
74
    /**
75
     * @inheritdocs
76
     */
77 1
    public function key() {
78 1
        return $this->position;
79
    }
80
81
    /**
82
     * @inheritdocs
83
     */
84 50
    public function next() {
85 50
        $this->position++;
86 50
        $this->maybe_parse_next_token();
87 50
    }
88
89
    /**
90
     * @inheritdocs
91
     */
92 2
    public function rewind() {
93 2
        $this->position = 0;
94 2
    }
95
96
    /**
97
     * @inheritdocs
98
     */
99 1
    public function valid() {
100 1
        $this->maybe_parse_next_token();
101 1
        return count($this->tokens) > $this->position;
102
    }
103
104
    /**
105
     * Get the current position in the source.
106
     *
107
     * @return  int[]   [line, column]
108
     */
109 5
    public function source_position() {
110 5
        $str = "";
111 5
        for ($i = 0; $i < $this->position; $i++) {
112 3
            $str .= $this->tokens[$i][1][0];
113 3
        }
114 5
        $lines = explode("\n", $str);
115 5
        $line = count($lines);
116 5
        $column = strlen(array_pop($lines)) + 1;
117 5
        return array($line, $column);
118
    }
119
120
    /**
121
     * Try to parse the next token if there are currently not enough tokens
122
     * in the tokens to get a token for the current position.
123
     *
124
     * @throws  ParserException if next token can not be parsed.
125
     */
126 57
    public function maybe_parse_next_token() {
127 57
        if (count($this->tokens) <= $this->position) {
128 57
            $this->parse_next_token();
129 54
        }
130 54
    }
131
132
133
    /**
134
     * Try to parse the next token from the source.
135
     *
136
     * @throws  ParserException if next token can not be parsed.
137
     */
138 57
    protected function parse_next_token() {
139 57
        if ($this->is_everything_parsed()) {
140 46
            if (!$this->is_end_token_added) {
141 46
                $this->tokens[] = array(new Symbol("", 0), array(""));
142 46
                $this->is_end_token_added = true;
143 46
            }
144 46
            return;
145
        }
146
147 54
        foreach ($this->symbol_table->symbols() as $symbol) {
148 53
            $re = $symbol->regexp();
149 53
            $matches = array();
150 53
            if (preg_match("%^($re)%s", $this->unparsed, $matches) == 1) {
151 51
                unset($matches[1]);
152 51
                $this->advance($matches[0]);
153 51
                $this->tokens[] = array($symbol, array_values($matches));
154 51
                return;
155
            }
156 45
        }
157
158 4
        $next = substr($this->unparsed, 0, static::$UNPARSED_PREVIEW_FOR_ERROR);
0 ignored issues
show
Bug introduced by
Since $UNPARSED_PREVIEW_FOR_ERROR is declared private, accessing it with static will lead to errors in possible sub-classes; consider using self, or increasing the visibility of $UNPARSED_PREVIEW_FOR_ERROR to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return static::$someVariable;
    }
}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass { }

YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class SomeClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return self::$someVariable; // self works fine with private.
    }
}
Loading history...
159 4
        throw new ParserException("Could not match \"$next\".");
160
    }
161
162
    /**
163
     * Go forward in the string we have parsed so far.
164
     *
165
     * @param  string   $match
166
     * @return null
167
     */
168 51
    public function advance($match) {
169 51
        assert('is_string($match)');
170 51
        $this->unparsed = ltrim
171 51
            ( substr($this->unparsed, strlen($match))
172 51
            , "\t \0\x0B" // don't trim linebreaks
173 51
            );
174 51
    }
175
176
    /**
177
     * Checkout if everything is parsed.
178
     *
179
     * @return  bool
180
     */
181 57
    protected function is_everything_parsed() {
182 57
        return empty($this->unparsed);
183
    }
184
}
185