@@ -19,9 +19,20 @@ function tokens($code) {
1919 $ attributeStarted = null ;
2020 $ attributeEnded = null ;
2121
22+ $ expressionLevel = 0 ;
23+ $ expressionStarted = null ;
24+ $ expressionEnded = null ;
25+
26+ $ nestedLevel = 0 ;
27+ $ inQuote = false ;
28+
2229 $ carry = 0 ;
2330
2431 while ($ cursor < $ length ) {
32+ if ($ code [$ cursor ] === '" ' || $ code [$ cursor ] === "' " && $ code [$ cursor - 1 ] !== "\\" ) {
33+ $ inQuote = !$ inQuote ;
34+ }
35+
2536 if ($ code [$ cursor ] === "{ " && $ elementStarted !== null ) {
2637 if ($ attributeLevel === 0 ) {
2738 $ attributeStarted = $ cursor ;
@@ -38,7 +49,7 @@ function tokens($code) {
3849 }
3950 }
4051
41- if ($ attributeStarted && $ attributeEnded ) {
52+ if ($ attributeStarted !== null && $ attributeEnded !== null ) {
4253 $ position = (string ) count ($ attributes );
4354 $ positionLength = strlen ($ position );
4455
@@ -62,10 +73,60 @@ function tokens($code) {
6273 continue ;
6374 }
6475
76+ if ($ code [$ cursor ] === "{ " && $ elementStarted === null && $ nestedLevel > 0 ) {
77+ if ($ expressionLevel === 0 ) {
78+ $ expressionStarted = $ cursor ;
79+ }
80+
81+ $ expressionLevel ++;
82+ }
83+
84+ if ($ code [$ cursor ] === "} " && $ elementStarted === null && $ nestedLevel > 0 ) {
85+ $ expressionLevel --;
86+
87+ if ($ expressionLevel === 0 ) {
88+ $ expressionEnded = $ cursor ;
89+ }
90+ }
91+
92+ if ($ expressionStarted !== null && $ expressionEnded !== null ) {
93+ $ distance = $ expressionEnded - $ expressionStarted ;
94+
95+ $ carry += $ cursor ;
96+
97+ $ before = trim (substr ($ code , 0 , $ expressionStarted ));
98+ $ expression = trim (substr ($ code , $ expressionStarted + 1 , $ distance - 1 ));
99+ $ after = trim (substr ($ code , $ expressionEnded + 1 ));
100+
101+ $ tokens [] = $ before ;
102+ $ tokens [] = ["expression " => compile ($ expression ), "started " => $ carry ];
103+
104+ $ code = $ after ;
105+ $ length = strlen ($ code );
106+ $ cursor = 0 ;
107+
108+ $ expressionStarted = null ;
109+ $ expressionEnded = null ;
110+
111+ continue ;
112+ }
113+
65114 preg_match ("#^</?[a-zA-Z]# " , substr ($ code , $ cursor , 3 ), $ matchesStart );
66115
67- if (count ($ matchesStart ) && $ attributeLevel < 1 ) {
116+ if (
117+ count ($ matchesStart )
118+ && $ attributeLevel < 1
119+ && $ expressionLevel < 1
120+ && !$ inQuote
121+ ) {
68122 $ elementLevel ++;
123+
124+ if ($ matchesStart [0 ][1 ] !== "/ " ) {
125+ $ nestedLevel ++;
126+ } else {
127+ $ nestedLevel --;
128+ }
129+
69130 $ elementStarted = $ cursor ;
70131 }
71132
@@ -77,6 +138,7 @@ function tokens($code) {
77138 && !$ matchesEqualBefore && !$ matchesEqualAfter
78139 && $ attributeLevel < 1
79140 && $ elementStarted !== null
141+ && $ expressionStarted === null
80142 ) {
81143 $ elementLevel --;
82144 $ elementEnded = $ cursor ;
@@ -101,9 +163,21 @@ function tokens($code) {
101163 $ token ["attributes " ] = $ attributes ;
102164 }
103165
104- $ tokens [] = $ before ;
166+ if ($ expressionStarted !== null ) {
167+ $ tokens [] = ["expression " => $ before , "started " => $ expressionStarted ];
168+ } else {
169+ $ tokens [] = $ before ;
170+ }
171+
105172 $ tokens [] = $ token ;
106173
174+ if (preg_match ("#/>$# " , $ tag )) {
175+ preg_match ("#<([a-zA-Z]+)# " , $ tag , $ matchesName );
176+
177+ $ name = $ matchesName [1 ];
178+ $ tokens [] = ["tag " => "</ {$ name }> " ];
179+ }
180+
107181 $ attributes = [];
108182
109183 $ code = $ after ;
@@ -234,7 +308,7 @@ function parse($nodes) {
234308 }
235309 }
236310
237- preg_match_all ("#([a-zA-Z]+)={([^}]+)}# " , $ node ["tag " ], $ dynamic );
311+ preg_match_all ("#([a-zA-Z][a-zA-Z-_] +)={([^}]+)}# " , $ node ["tag " ], $ dynamic );
238312
239313 if (count ($ dynamic [0 ])) {
240314 foreach ($ dynamic [1 ] as $ key => $ value ) {
@@ -253,30 +327,50 @@ function parse($nodes) {
253327 $ children [] = parse ([$ child ]);
254328 }
255329
330+ else if (isset ($ child ["expression " ])) {
331+ $ children [] = $ child ["expression " ];
332+ }
333+
256334 else {
257335 $ children [] = "\"" . addslashes ($ child ["text " ]) . "\"" ;
258336 }
259337 }
260338
261339 $ props ["children " ] = $ children ;
262340
263- if (function_exists ("Pre \\Phpx \\Renderer \\_ " . $ node ["name " ])) {
264- $ code .= "Pre \\Phpx \\Renderer \\_ " . $ node ["name " ] . "([ " . PHP_EOL ;
341+ if (function_exists ("\\ Pre \\Phpx \\Renderer \\__ " . $ node ["name " ])) {
342+ $ code .= "\\ Pre \\Phpx \\Renderer \\__ " . $ node ["name " ] . "([ " . PHP_EOL ;
265343 }
266344
267345 else {
268- $ code .= $ node ["name " ] . "([ " . PHP_EOL ;
346+ $ name = $ node ["name " ];
347+ $ code .= "(new {$ name })->render([ " . PHP_EOL ;
269348 }
270349
271350 foreach ($ props as $ key => $ value ) {
272351 if ($ key === "children " ) {
273- $ code .= "\"children \" => [ " . PHP_EOL ;
352+ $ children = array_filter ($ children , function ($ child ) {
353+ return trim ($ child , "\"' " );
354+ });
355+
356+ $ children = array_map ("trim " , $ children );
357+
358+ $ children = array_values ($ children );
274359
275- foreach ($ children as $ child ) {
276- $ code .= "{$ child }, " . PHP_EOL ;
360+ if (count ($ children ) === 1 ) {
361+ $ code .= "\"children \" => " . $ children [0 ] . ", " . PHP_EOL ;
362+ }
363+
364+ else {
365+ $ code .= "\"children \" => [ " . PHP_EOL ;
366+
367+ foreach ($ children as $ child ) {
368+ $ code .= "{$ child }, " . PHP_EOL ;
369+ }
370+
371+ $ code .= "], " . PHP_EOL ;
277372 }
278373
279- $ code .= "], " . PHP_EOL ;
280374 }
281375
282376 else {
@@ -294,196 +388,3 @@ function parse($nodes) {
294388function compile ($ code ) {
295389 return parse (nodes (tokens ($ code )));
296390}
297-
298- namespace Pre \Phpx \Renderer ;
299-
300- use Closure ;
301- use Pre \Collections \Collection ;
302-
303- function element ($ name , $ props , callable $ attrs = null ) {
304- $ code = "< {$ name }" ;
305-
306- $ className = $ props ["className " ] ?? null ;
307-
308- if (is_callable ($ className )) {
309- $ className = $ className ();
310- }
311-
312- if ($ className instanceof Collection) {
313- $ className = $ className ->toArray ();
314- }
315-
316- if (is_array ($ className )) {
317- $ combined = "" ;
318-
319- foreach ($ className as $ key => $ value ) {
320- if (is_string ($ key )) {
321- $ combined .= !!$ value ? " {$ key }" : "" ;
322- }
323-
324- else {
325- $ combined .= " {$ value }" ;
326- }
327- }
328-
329- $ className = trim ($ combined );
330- }
331-
332- if ($ className ) {
333- $ code .= " class=' {$ className }' " ;
334- }
335-
336- $ style = $ props ["style " ] ?? null ;
337-
338- if (is_callable ($ style )) {
339- $ style = $ style ();
340- }
341-
342- if ($ style instanceof Collection) {
343- $ style = $ style ->toArray ();
344- }
345-
346- if (is_array ($ style )) {
347- $ styles = [];
348-
349- foreach ($ style as $ key => $ value ) {
350- $ styles [] = "{$ key }: {$ value }" ;
351- }
352-
353- $ style = join ("; " , $ styles );
354- }
355-
356- if ($ style ) {
357- $ code .= " style=' {$ style }' " ;
358- }
359-
360- $ code .= "> " ;
361-
362- foreach ($ props ["children " ] as $ child ) {
363- $ code .= $ child ;
364- }
365-
366- $ code .= "</ {$ name }> " ;
367-
368- return $ code ;
369- }
370-
371-
372- define ("ELEMENTS " , [
373- "a " ,
374- "abbr " ,
375- "address " ,
376- "area " ,
377- "article " ,
378- "aside " ,
379- "audio " ,
380- "b " ,
381- "base " ,
382- "bdi " ,
383- "bdo " ,
384- "blockquote " ,
385- "body " ,
386- "br " ,
387- "button " ,
388- "canvas " ,
389- "caption " ,
390- "cite " ,
391- "code " ,
392- "col " ,
393- "colgroup " ,
394- "data " ,
395- "datalist " ,
396- "dd " ,
397- "del " ,
398- "details " ,
399- "dfn " ,
400- "div " ,
401- "dl " ,
402- "dt " ,
403- "em " ,
404- "embed " ,
405- "fieldset " ,
406- "figcaption " ,
407- "figure " ,
408- "footer " ,
409- "form " ,
410- "h1 " ,
411- "h2 " ,
412- "h3 " ,
413- "h4 " ,
414- "h5 " ,
415- "h6 " ,
416- "head " ,
417- "header " ,
418- "hr " ,
419- "html " ,
420- "i " ,
421- "iframe " ,
422- "img " ,
423- "input " ,
424- "ins " ,
425- "kbd " ,
426- "label " ,
427- "legend " ,
428- "li " ,
429- "link " ,
430- "main " ,
431- "map " ,
432- "mark " ,
433- "meta " ,
434- "meter " ,
435- "nav " ,
436- "noframes " ,
437- "noscript " ,
438- "object " ,
439- "ol " ,
440- "optgroup " ,
441- "option " ,
442- "output " ,
443- "p " ,
444- "param " ,
445- "pre " ,
446- "progress " ,
447- "q " ,
448- "rp " ,
449- "rt " ,
450- "rtc " ,
451- "ruby " ,
452- "s " ,
453- "samp " ,
454- "script " ,
455- "section " ,
456- "select " ,
457- "slot " ,
458- "small " ,
459- "source " ,
460- "span " ,
461- "strong " ,
462- "style " ,
463- "sub " ,
464- "summary " ,
465- "sup " ,
466- "table " ,
467- "tbody " ,
468- "td " ,
469- "template " ,
470- "textarea " ,
471- "tfoot " ,
472- "th " ,
473- "thead " ,
474- "time " ,
475- "title " ,
476- "tr " ,
477- "track " ,
478- "u " ,
479- "ul " ,
480- "var " ,
481- "video " ,
482- "wbr " ,
483- ]);
484-
485- foreach (ELEMENTS as $ element ) {
486- eval ("namespace Pre\Phpx\Renderer { function _ {$ element }( \$props) {
487- return element(' {$ element }', \$props);
488- } } " );
489- }
0 commit comments