1
+ <?php
2
+ // This file is part of Moodle - http://moodle.org/
3
+ //
4
+ // Moodle is free software: you can redistribute it and/or modify
5
+ // it under the terms of the GNU General Public License as published by
6
+ // the Free Software Foundation, either version 3 of the License, or
7
+ // (at your option) any later version.
8
+ //
9
+ // Moodle is distributed in the hope that it will be useful,
10
+ // but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ // GNU General Public License for more details.
13
+ //
14
+ // You should have received a copy of the GNU General Public License
15
+ // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
16
+
17
+ namespace tool_dataflows ;
18
+
19
+ use Symfony \Component \ExpressionLanguage \Token ;
20
+ use Symfony \Component \ExpressionLanguage \SyntaxError ;
21
+ use Symfony \Component \ExpressionLanguage \TokenStream ;
22
+
23
+ /**
24
+ * Class adding modification to Symfony Lexer to suit dataflow needs.
25
+ *
26
+ * @package tool_dataflows
27
+ * @author Ghaly Marc-Alexandre <[email protected] >
28
+ * @copyright Catalyst IT, 2023
29
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
30
+ */
31
+ class dataflow_lexer extends \Symfony \Component \ExpressionLanguage \Lexer {
32
+ /**
33
+ * Tokenizes an expression.
34
+ *
35
+ * @param string $expression The expression to tokenize
36
+ *
37
+ * @return TokenStream A token stream instance
38
+ *
39
+ * @throws SyntaxError
40
+ */
41
+ public function tokenize ($ expression )
42
+ {
43
+ $ expression = str_replace (["\r" , "\n" , "\t" , "\v" , "\f" ], ' ' , $ expression );
44
+ $ cursor = 0 ;
45
+ $ tokens = [];
46
+ $ brackets = [];
47
+ $ end = \strlen ($ expression );
48
+
49
+ while ($ cursor < $ end ) {
50
+ if (' ' == $ expression [$ cursor ]) {
51
+ ++$ cursor ;
52
+
53
+ continue ;
54
+ }
55
+
56
+ if (preg_match ('/[0-9]+(?:\.[0-9]+)?/A ' , $ expression , $ match , 0 , $ cursor )) {
57
+ // Numbers.
58
+ // Floats.
59
+ $ number = (float ) $ match [0 ];
60
+ if (preg_match ('/^[0-9]+$/ ' , $ match [0 ]) && $ number <= \PHP_INT_MAX ) {
61
+ // Integers lower than the maximum.
62
+ $ number = (int ) $ match [0 ];
63
+ }
64
+ $ tokens [] = new Token (Token::NUMBER_TYPE , $ number , $ cursor + 1 );
65
+ $ cursor += \strlen ($ match [0 ]);
66
+ } elseif (preg_match ("/{'(\W[a-zA-Z_ \x7f- \xff][a-zA-Z0-9_. \x7f- \xff]*)'}/A " , $ expression , $ match , 0 , $ cursor )) {
67
+ // Names litteral.
68
+ $ tokens [] = new Token (Token::NAME_TYPE , $ match [1 ], $ cursor + 1 );
69
+ $ cursor += \strlen ($ match [0 ]);
70
+ } elseif (false !== strpos ('([{ ' , $ expression [$ cursor ])) {
71
+ // Opening bracket.
72
+ $ brackets [] = [$ expression [$ cursor ], $ cursor ];
73
+
74
+ $ tokens [] = new Token (Token::PUNCTUATION_TYPE , $ expression [$ cursor ], $ cursor + 1 );
75
+ ++$ cursor ;
76
+ } elseif (false !== strpos (')]} ' , $ expression [$ cursor ])) {
77
+ // Closing bracket.
78
+ if (empty ($ brackets )) {
79
+ throw new SyntaxError (sprintf ('Unexpected "%s". ' , $ expression [$ cursor ]), $ cursor , $ expression );
80
+ }
81
+
82
+ list ($ expect , $ cur ) = array_pop ($ brackets );
83
+ if ($ expression [$ cursor ] != strtr ($ expect , '([{ ' , ')]} ' )) {
84
+ throw new SyntaxError (sprintf ('Unclosed "%s". ' , $ expect ), $ cur , $ expression );
85
+ }
86
+
87
+ $ tokens [] = new Token (Token::PUNCTUATION_TYPE , $ expression [$ cursor ], $ cursor + 1 );
88
+ ++$ cursor ;
89
+ } elseif (preg_match ('/"([^" \\\\]*(?: \\\\.[^" \\\\]*)*)"| \'([^ \'\\\\]*(?: \\\\.[^ \'\\\\]*)*) \'/As ' , $ expression , $ match , 0 , $ cursor )) {
90
+ // Strings.
91
+ $ tokens [] = new Token (Token::STRING_TYPE , stripcslashes (substr ($ match [0 ], 1 , -1 )), $ cursor + 1 );
92
+ $ cursor += \strlen ($ match [0 ]);
93
+ } elseif (preg_match ('/(?<=^|[\s(])not in(?=[\s(])|\!\=\=|(?<=^|[\s(])not(?=[\s(])|(?<=^|[\s(])and(?=[\s(])|\=\=\=|\>\=|(?<=^|[\s(])or(?=[\s(])|\<\=|\*\*|\.\.|(?<=^|[\s(])in(?=[\s(])|&&|\|\||(?<=^|[\s(])matches|\=\=|\!\=|\*|~|%|\/|\>|\||\!|\^|&|\+|\<|\-/A ' , $ expression , $ match , 0 , $ cursor )) {
94
+ // Operators.
95
+ $ tokens [] = new Token (Token::OPERATOR_TYPE , $ match [0 ], $ cursor + 1 );
96
+ $ cursor += \strlen ($ match [0 ]);
97
+ } elseif (false !== strpos ('.,?: ' , $ expression [$ cursor ])) {
98
+ // Punctuation.
99
+ $ tokens [] = new Token (Token::PUNCTUATION_TYPE , $ expression [$ cursor ], $ cursor + 1 );
100
+ ++$ cursor ;
101
+ } elseif (preg_match ('/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/A ' , $ expression , $ match , 0 , $ cursor )) {
102
+ // Names.
103
+ $ tokens [] = new Token (Token::NAME_TYPE , $ match [0 ], $ cursor + 1 );
104
+ $ cursor += \strlen ($ match [0 ]);
105
+ } else {
106
+ // Unlexable.
107
+ throw new SyntaxError (sprintf ('Unexpected character "%s". ' , $ expression [$ cursor ]), $ cursor , $ expression );
108
+ }
109
+ }
110
+
111
+ $ tokens [] = new Token (Token::EOF_TYPE , null , $ cursor + 1 );
112
+
113
+ if (!empty ($ brackets )) {
114
+ list ($ expect , $ cur ) = array_pop ($ brackets );
115
+ throw new SyntaxError (sprintf ('Unclosed "%s". ' , $ expect ), $ cur , $ expression );
116
+ }
117
+
118
+ return new TokenStream ($ tokens , $ expression );
119
+ }
120
+ }
0 commit comments