97fce1b382f813f6538c81226edc424d82acb63b
[moodle.git] / lib / lessphp / Exception / Chunk.php
1 <?php\r
2 \r
3 /**\r
4  * Chunk Exception\r
5  *\r
6  * @package Less\r
7  * @subpackage exception\r
8  */\r
9 class Less_Exception_Chunk extends Less_Exception_Parser{\r
10 \r
11 \r
12         protected $parserCurrentIndex = 0;\r
13 \r
14         protected $emitFrom = 0;\r
15 \r
16         protected $input_len;\r
17 \r
18 \r
19         /**\r
20          * Constructor\r
21          *\r
22          * @param string $input\r
23          * @param Exception $previous Previous exception\r
24          * @param integer $index The current parser index\r
25          * @param Less_FileInfo|string $currentFile The file\r
26          * @param integer $code The exception code\r
27          */\r
28         public function __construct($input, Exception $previous = null, $index = null, $currentFile = null, $code = 0){\r
29 \r
30                 $this->message = 'ParseError: Unexpected input'; //default message\r
31 \r
32                 $this->index = $index;\r
33 \r
34                 $this->currentFile = $currentFile;\r
35 \r
36                 $this->input = $input;\r
37                 $this->input_len = strlen($input);\r
38 \r
39                 $this->Chunks();\r
40                 $this->genMessage();\r
41         }\r
42 \r
43 \r
44         /**\r
45          * See less.js chunks()\r
46          * We don't actually need the chunks\r
47          *\r
48          */\r
49         protected function Chunks(){\r
50                 $level = 0;\r
51                 $parenLevel = 0;\r
52                 $lastMultiCommentEndBrace = null;\r
53                 $lastOpening = null;\r
54                 $lastMultiComment = null;\r
55                 $lastParen = null;\r
56 \r
57                 for( $this->parserCurrentIndex = 0; $this->parserCurrentIndex < $this->input_len; $this->parserCurrentIndex++ ){\r
58                         $cc = $this->CharCode($this->parserCurrentIndex);\r
59                         if ((($cc >= 97) && ($cc <= 122)) || ($cc < 34)) {\r
60                                 // a-z or whitespace\r
61                                 continue;\r
62                         }\r
63 \r
64                         switch ($cc) {\r
65 \r
66                                 // (\r
67                                 case 40:\r
68                                         $parenLevel++;\r
69                                         $lastParen = $this->parserCurrentIndex;\r
70                                         continue;\r
71 \r
72                                 // )\r
73                                 case 41:\r
74                                         $parenLevel--;\r
75                                         if( $parenLevel < 0 ){\r
76                                                 return $this->fail("missing opening `(`");\r
77                                         }\r
78                                         continue;\r
79 \r
80                                 // ;\r
81                                 case 59:\r
82                                         //if (!$parenLevel) { $this->emitChunk();       }\r
83                                         continue;\r
84 \r
85                                 // {\r
86                                 case 123:\r
87                                         $level++;\r
88                                         $lastOpening = $this->parserCurrentIndex;\r
89                                         continue;\r
90 \r
91                                 // }\r
92                                 case 125:\r
93                                         $level--;\r
94                                         if( $level < 0 ){\r
95                                                 return $this->fail("missing opening `{`");\r
96 \r
97                                         }\r
98                                         //if (!$level && !$parenLevel) { $this->emitChunk(); }\r
99                                         continue;\r
100                                 // \\r
101                                 case 92:\r
102                                         if ($this->parserCurrentIndex < $this->input_len - 1) { $this->parserCurrentIndex++; continue; }\r
103                                         return $this->fail("unescaped `\\`");\r
104 \r
105                                 // ", ' and `\r
106                                 case 34:\r
107                                 case 39:\r
108                                 case 96:\r
109                                         $matched = 0;\r
110                                         $currentChunkStartIndex = $this->parserCurrentIndex;\r
111                                         for ($this->parserCurrentIndex = $this->parserCurrentIndex + 1; $this->parserCurrentIndex < $this->input_len; $this->parserCurrentIndex++) {\r
112                                                 $cc2 = $this->CharCode($this->parserCurrentIndex);\r
113                                                 if ($cc2 > 96) { continue; }\r
114                                                 if ($cc2 == $cc) { $matched = 1; break; }\r
115                                                 if ($cc2 == 92) {        // \\r
116                                                         if ($this->parserCurrentIndex == $this->input_len - 1) {\r
117                                                                 return $this->fail("unescaped `\\`");\r
118                                                         }\r
119                                                         $this->parserCurrentIndex++;\r
120                                                 }\r
121                                         }\r
122                                         if ($matched) { continue; }\r
123                                         return $this->fail("unmatched `" . chr($cc) . "`", $currentChunkStartIndex);\r
124 \r
125                                 // /, check for comment\r
126                                 case 47:\r
127                                         if ($parenLevel || ($this->parserCurrentIndex == $this->input_len - 1)) { continue; }\r
128                                         $cc2 = $this->CharCode($this->parserCurrentIndex+1);\r
129                                         if ($cc2 == 47) {\r
130                                                 // //, find lnfeed\r
131                                                 for ($this->parserCurrentIndex = $this->parserCurrentIndex + 2; $this->parserCurrentIndex < $this->input_len; $this->parserCurrentIndex++) {\r
132                                                         $cc2 = $this->CharCode($this->parserCurrentIndex);\r
133                                                         if (($cc2 <= 13) && (($cc2 == 10) || ($cc2 == 13))) { break; }\r
134                                                 }\r
135                                         } else if ($cc2 == 42) {\r
136                                                 // /*, find */\r
137                                                 $lastMultiComment = $currentChunkStartIndex = $this->parserCurrentIndex;\r
138                                                 for ($this->parserCurrentIndex = $this->parserCurrentIndex + 2; $this->parserCurrentIndex < $this->input_len - 1; $this->parserCurrentIndex++) {\r
139                                                         $cc2 = $this->CharCode($this->parserCurrentIndex);\r
140                                                         if ($cc2 == 125) { $lastMultiCommentEndBrace = $this->parserCurrentIndex; }\r
141                                                         if ($cc2 != 42) { continue; }\r
142                                                         if ($this->CharCode($this->parserCurrentIndex+1) == 47) { break; }\r
143                                                 }\r
144                                                 if ($this->parserCurrentIndex == $this->input_len - 1) {\r
145                                                         return $this->fail("missing closing `*/`", $currentChunkStartIndex);\r
146                                                 }\r
147                                         }\r
148                                         continue;\r
149 \r
150                                 // *, check for unmatched */\r
151                                 case 42:\r
152                                         if (($this->parserCurrentIndex < $this->input_len - 1) && ($this->CharCode($this->parserCurrentIndex+1) == 47)) {\r
153                                                 return $this->fail("unmatched `/*`");\r
154                                         }\r
155                                         continue;\r
156                         }\r
157                 }\r
158 \r
159                 if( $level !== 0 ){\r
160                         if( ($lastMultiComment > $lastOpening) && ($lastMultiCommentEndBrace > $lastMultiComment) ){\r
161                                 return $this->fail("missing closing `}` or `*/`", $lastOpening);\r
162                         } else {\r
163                                 return $this->fail("missing closing `}`", $lastOpening);\r
164                         }\r
165                 } else if ( $parenLevel !== 0 ){\r
166                         return $this->fail("missing closing `)`", $lastParen);\r
167                 }\r
168 \r
169 \r
170                 //chunk didn't fail\r
171 \r
172 \r
173                 //$this->emitChunk(true);\r
174         }\r
175 \r
176         public function CharCode($pos){\r
177                 return ord($this->input[$pos]);\r
178         }\r
179 \r
180 \r
181         public function fail( $msg, $index = null ){\r
182 \r
183                 if( !$index ){\r
184                         $this->index = $this->parserCurrentIndex;\r
185                 }else{\r
186                         $this->index = $index;\r
187                 }\r
188                 $this->message = 'ParseError: '.$msg;\r
189         }\r
190 \r
191 \r
192         /*\r
193         function emitChunk( $force = false ){\r
194                 $len = $this->parserCurrentIndex - $this->emitFrom;\r
195                 if ((($len < 512) && !$force) || !$len) {\r
196                         return;\r
197                 }\r
198                 $chunks[] = substr($this->input, $this->emitFrom, $this->parserCurrentIndex + 1 - $this->emitFrom );\r
199                 $this->emitFrom = $this->parserCurrentIndex + 1;\r
200         }\r
201         */\r
202 \r
203 }\r