Skip to content

Commit 02e3959

Browse files
author
vidy
committed
Improve filter lexer
1 parent 89b542f commit 02e3959

File tree

3 files changed

+76
-56
lines changed

3 files changed

+76
-56
lines changed

src/BladeFilterLexer.php

+31-18
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,12 @@ class BladeFilterLexer extends AbstractLexer
1010
* All tokens that are not valid identifiers must be < 100
1111
*/
1212
public const T_NONE = 1;
13-
public const T_STRING = 2;
14-
public const T_VARIABLE = 7;
15-
public const T_LITERAL = 8;
16-
public const T_INTEGER = 9;
17-
public const T_FLOAT = 10;
13+
public const T_VARIABLE_NAME = 2;
14+
public const T_STRING = 3;
15+
public const T_QUOTE = 4;
16+
public const T_INTEGER = 5;
17+
public const T_FLOAT = 6;
18+
public const T_VARIABLE_EXPRESSION = 7;
1819

1920
/**
2021
* All tokens that are also identifiers should be >= 100,
@@ -32,20 +33,23 @@ class BladeFilterLexer extends AbstractLexer
3233
protected function getCatchablePatterns()
3334
{
3435
return [
35-
'\(.*?\)',
36-
//Expression
36+
'\'(?:\\\\\'|.)*?\'',
37+
// Single quoted string
38+
39+
'"(?:\\\\\"|.)*?"',
40+
// Double quoted string
3741

38-
'[a-z_\\\][a-z0-9_]*[a-z0-9_]{1}',
39-
// safe string
42+
'\(.*?\)',
43+
// Expression
4044

41-
"[\'\"](?:[^'\"]|'')*[\'\"]",
42-
// single or double quoted string
45+
'[a-z_][a-z0-9_]*',
46+
// Safe string, filter name, filter argument name must be safe string
4347

4448
'(?:[0-9]+(?:[\.][0-9]+)*)(?:e[+-]?[0-9]+)?',
45-
//integer, float
49+
// Integer, float
4650

4751
'\$[a-z_][a-z0-9_]*(?:->[a-z_][a-z0-9_]*)*',
48-
// a variable
52+
// A variable expression, 变量表达式
4953
];
5054
}
5155

@@ -57,14 +61,15 @@ protected function getNonCatchablePatterns()
5761
/**
5862
* whitespace
5963
*/
60-
return ['\s+'];
64+
return ['\s+', '(.)'];
6165
}
6266

6367
/**
6468
* @inheritdoc
6569
*/
6670
protected function getType(&$value)
6771
{
72+
6873
switch (true) {
6974
/**
7075
* Recognize numeric values
@@ -81,14 +86,21 @@ protected function getType(&$value)
8186
*/
8287
case ($value[0] === '\'' || $value[0] == '"'):
8388
return self::T_STRING;
89+
90+
/**
91+
* Recognize variable name, can be valid filter name, filter argument_name
92+
*/
93+
case preg_match('/^[a-z_][a-z0-9_]*/', $value):
94+
return self::T_VARIABLE_NAME;
95+
8496
/**
8597
* Recognize variables
8698
*/
87-
case ($value[0] === '$'):
88-
return self::T_VARIABLE;
99+
case preg_match('/^\$[a-z_][a-z0-9_]*(?:->[a-z_][a-z0-9_]*)*/', $value):
100+
return self::T_VARIABLE_EXPRESSION;
89101

90102
/**
91-
* Recognize symbols
103+
* Recognize identifiers
92104
*/
93105
case ($value === '|'):
94106
return self::T_PIPE;
@@ -98,8 +110,9 @@ protected function getType(&$value)
98110
return self::T_COMMA;
99111
case ($value === '='):
100112
return self::T_EQUALS;
113+
101114
default:
102-
return self::T_LITERAL;;
115+
return self::T_NONE;;
103116
}
104117
}
105118
}

src/BladeFilterParser.php

+44-37
Original file line numberDiff line numberDiff line change
@@ -26,16 +26,17 @@ public function parse($input)
2626

2727
$this->lexer->moveNext();
2828

29-
$prefiltered = $this->collectPreFiltered($input);
29+
$preFiltered = $this->collectPreFiltered($input);
3030

3131
$filters = [];
3232
while ($this->lexer->isNextToken(BladeFilterLexer::T_PIPE)) {
33-
$this->lexer->moveNext(); //skip pipe
34-
$this->Filters($filters);
33+
$this->lexer->moveNext(); // Skip pipe
34+
35+
$this->Filters($filters);
3536
}
3637

3738
return [
38-
'prefiltered' => $prefiltered,
39+
'prefiltered' => $preFiltered,
3940
'filters' => $filters
4041
];
4142
}
@@ -54,25 +55,23 @@ private function collectPreFiltered(string $input)
5455
return $input;
5556
}
5657

57-
$prefiltered = trim($this->lexer->getInputUntilPosition($this->lexer->lookahead['position']));
58-
if ($prefiltered && $prefiltered[0] === '(') {
59-
$prefiltered = trim($prefiltered, '()');
58+
$preFiltered = trim($this->lexer->getInputUntilPosition($this->lexer->lookahead['position']));
59+
if ($preFiltered && $preFiltered[0] === '(') {
60+
$preFiltered = trim($preFiltered, '()');
6061
}
6162

62-
return $prefiltered;
63+
return $preFiltered;
6364
}
6465

6566
private function Filters(&$filters)
6667
{
67-
$this->lexer->moveNext();
68-
69-
$this->syntaxErrorIf(null === $this->lexer->token, sprintf('No filter found'));
68+
$this->syntaxErrorIf(null === $this->lexer->lookahead, sprintf('No filter found'));
69+
$this->match(BladeFilterLexer::T_VARIABLE_NAME, sprintf('Filter name is not valid, "%s"', $this->lexer->lookahead['value']));
7070

7171
$filter = [];
7272
$filter['name'] = $this->lexer->token['value'];
7373
$filterArguments = [];
7474
$filter['arguments'] = &$filterArguments;
75-
7675
$filters[] = $filter;
7776

7877
$next = $this->lexer->lookahead;
@@ -81,7 +80,7 @@ private function Filters(&$filters)
8180
}
8281

8382
if($next['type'] == BladeFilterLexer::T_COLON) {
84-
$this->lexer->moveNext();
83+
$this->lexer->moveNext(); //Skip to colon
8584

8685
list($argName, $argValue) = $this->collectFilterArgument();
8786
$filterArguments[$argName] = $argValue;
@@ -97,20 +96,20 @@ private function Filters(&$filters)
9796
$this->Filters($filters);
9897
} else if (null !== $this->lexer->token) {
9998
$this->syntaxErrorIf(true,
100-
sprintf('It supposed to be another argument, or filter, but %s given', $this->lexer->token['value'],
99+
sprintf('It supposed to be another argument, or filter, but "%s" given', $this->lexer->token['value'],
101100
)
102101
);
103102
}
104103

105104
$this->lexer->moveNext();
106105
}
107106
} else if ($next['type'] == BladeFilterLexer::T_PIPE) {
108-
$this->lexer->moveNext();
107+
$this->lexer->moveNext(); //Skip to pipe
109108

110109
$this->Filters($filters);
111110
} else if (null !== $next){
112111
$this->syntaxErrorIf(true, sprintf(
113-
'It supposed to be either arguments after filter or another filter, but %s given',
112+
'It supposed to be either arguments after filter or another filter, but "%s" given',
114113
$next['value']
115114
));
116115
}
@@ -123,47 +122,55 @@ private function Filters(&$filters)
123122
*/
124123
private function collectFilterArgument(): array
125124
{
126-
$this->lexer->moveNext();
127-
128125
// Argument name
129-
$token = $this->lexer->token;
130-
$this->syntaxErrorIf(null === $token,
131-
sprintf('No arguments found after ":"')
126+
$next = $this->lexer->lookahead;
127+
$this->syntaxErrorIf(null === $next,
128+
sprintf('No arguments found after %s', $this->lexer->token['value'])
132129
);
133-
$this->syntaxErrorIf(BladeFilterLexer::T_PIPE === $token['type'],
130+
$this->syntaxErrorIf(BladeFilterLexer::T_PIPE === $next['type'],
134131
sprintf('No argument found')
135132
);
136-
$this->syntaxErrorIf(BladeFilterLexer::T_LITERAL !== $token['type'],
137-
sprintf('The argument name must be literal, however %s given, for filter %s',
138-
$token['value'],
139-
$this->input,
140-
)
133+
$this->syntaxErrorIf(BladeFilterLexer::T_VARIABLE_NAME !== $next['type'],
134+
sprintf('The argument name must be literal, however "%s" given',$next['value'])
141135
);
142-
$argumentName = $token['value'];
136+
137+
$this->lexer->moveNext();
138+
$argumentName = $this->lexer->token['value'];
143139

144140
// Equal sign
145141
$this->match(
146142
BladeFilterLexer::T_EQUALS,
147-
sprintf('No equal sign found after argument %s',$argumentName)
143+
sprintf('No equal sign found after argument "%s"',$argumentName)
148144
);
149145

150146
// Argument value
151-
$this->lexer->moveNext();
152-
$token = $this->lexer->token;
147+
$token = $this->lexer->lookahead;
153148
$this->syntaxErrorIf(null === $token,
154-
sprintf('No value specified for argument %s',$argumentName)
149+
sprintf('No value specified for argument "%s"',$argumentName)
155150
);
156-
$argumentValue = $token['value'];
151+
$isValidArgumentValue = $this->lexer->isNextTokenAny([
152+
BladeFilterLexer::T_INTEGER,
153+
BladeFilterLexer::T_STRING,
154+
BladeFilterLexer::T_FLOAT,
155+
BladeFilterLexer::T_VARIABLE_EXPRESSION,
156+
]);
157+
158+
$this->syntaxErrorIf(!$isValidArgumentValue,
159+
sprintf(' The value of filter argument "%s" is not valid, it supposed to be string, integer, float or variable, got %s',
160+
$argumentName,
161+
$token['value']
162+
));
163+
$this->lexer->moveNext();
157164

158-
return [$argumentName, $argumentValue];
165+
return [$argumentName, $token['value']];
159166
}
160167

161168
private function syntaxErrorIf(bool $predicate, string $errMessage, ?array $token = null)
162169
{
163170
$errMessage = sprintf('%s for filter "%s"', $errMessage, $this->input);
164171

165172
if ($token === null) {
166-
$token = $this->lexer->token;
173+
$token = $this->lexer->lookahead;
167174
}
168175
if (null !== $token) {
169176
$errMessage = sprintf('%s at position %s',$errMessage, $token['position'] );
@@ -176,8 +183,8 @@ private function syntaxErrorIf(bool $predicate, string $errMessage, ?array $toke
176183

177184
private function match(int $token, $message): bool
178185
{
179-
if (! $this->lexer->isNextToken($token)) {
180-
throw $this->syntaxErrorIf(true, $this->lexer->getLiteral($token), $message);
186+
if (!$this->lexer->isNextToken($token)) {
187+
throw $this->syntaxErrorIf(true, $message);
181188
}
182189

183190
return $this->lexer->moveNext();

tests/BladeFilterParserTest.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -48,5 +48,5 @@ public function test_filter_with_missing_arguments()
4848

4949
$this->assertEquals($filter['prefiltered'], '"css/carousel.css"');
5050
$this->assertTrue(count($filter['filters']) == 1);
51-
}
51+
}
5252
}

0 commit comments

Comments
 (0)