@@ -13,8 +13,11 @@ namespace DotVVM.Framework.Compilation.Parser.Dothtml.Tokenizer
1313 /// </summary>
1414 public class DothtmlTokenizer : TokenizerBase < DothtmlToken , DothtmlTokenType >
1515 {
16- public DothtmlTokenizer ( ) : base ( DothtmlTokenType . Text , DothtmlTokenType . WhiteSpace )
16+ private readonly DotvvmSyntaxConfiguration config ;
17+
18+ public DothtmlTokenizer ( DotvvmSyntaxConfiguration ? config = null ) : base ( DothtmlTokenType . Text , DothtmlTokenType . WhiteSpace )
1719 {
20+ this . config = config ?? DotvvmSyntaxConfiguration . Default ;
1821 }
1922
2023 private static bool IsAllowedAttributeFirstChar ( char ch )
@@ -249,13 +252,15 @@ private ReadElementType ReadElement(bool wasOpenBraceRead = false)
249252 }
250253
251254 // read tag name
252- if ( ! ReadTagOrAttributeName ( isAttributeName : false ) )
255+ if ( ! ReadTagOrAttributeName ( isAttributeName : false , out var tagPrefix , out var tagName ) )
253256 {
254257 CreateToken ( DothtmlTokenType . Text , errorProvider : t => CreateTokenError ( t , DothtmlTokenType . OpenTag , DothtmlTokenizerErrors . TagNameExpected ) ) ;
255258 CreateToken ( DothtmlTokenType . CloseTag , errorProvider : t => CreateTokenError ( ) ) ;
256259 return ReadElementType . Error ;
257260 }
258261
262+ var tagFullName = tagPrefix is null ? tagName ?? "" : tagPrefix + ":" + tagName ;
263+
259264 // read tag attributes
260265 SkipWhitespace ( ) ;
261266 if ( ! isClosingTag )
@@ -291,11 +296,14 @@ private ReadElementType ReadElement(bool wasOpenBraceRead = false)
291296 }
292297 }
293298
299+ bool isSelfClosing = false ;
300+
294301 if ( Peek ( ) == '/' && ! isClosingTag )
295302 {
296303 // self closing tag
297304 Read ( ) ;
298305 CreateToken ( DothtmlTokenType . Slash , "/" ) ;
306+ isSelfClosing = true ;
299307 }
300308 if ( Peek ( ) != '>' )
301309 {
@@ -306,20 +314,74 @@ private ReadElementType ReadElement(bool wasOpenBraceRead = false)
306314
307315 Read ( ) ;
308316 CreateToken ( DothtmlTokenType . CloseTag , ">" ) ;
317+
318+ if ( ! isClosingTag && ! isSelfClosing && config . IsRawTextElement ( tagFullName ) )
319+ {
320+ // HTML <script>, <style> tags: read content until we find the closing the, i.e. the `</script` sequence
321+ ReadRawTextTag ( tagFullName ) ;
322+ return ReadElementType . RawTextTag ;
323+ }
324+
309325 return ReadElementType . ValidTag ;
310326 }
311327
312328 public enum ReadElementType
313329 {
314330 Error ,
315331 ValidTag ,
332+ RawTextTag ,
316333 CData ,
317334 Comment ,
318335 Doctype ,
319336 XmlProcessingInstruction ,
320337 ServerComment
321338 }
322339
340+ public void ReadRawTextTag ( string name )
341+ {
342+ // Read everything as raw text until the matching end tag
343+ // used to parsing <script>, <style>, <dot:InlineScript>, <dot:HtmlLiteral>
344+ while ( Peek ( ) != NullChar )
345+ {
346+ if ( PeekIsString ( "</" ) &&
347+ PeekSpan ( name . Length + 2 ) . Slice ( 2 ) . Equals ( name . AsSpan ( ) , StringComparison . OrdinalIgnoreCase ) &&
348+ ! char . IsLetterOrDigit ( Peek ( name . Length + 2 ) ) )
349+ {
350+ CreateToken ( DothtmlTokenType . Text ) ;
351+ Debug . Assert ( Peek ( ) == '<' ) ;
352+ Read ( ) ;
353+ CreateToken ( DothtmlTokenType . OpenTag ) ;
354+
355+ Debug . Assert ( Peek ( ) == '/' ) ;
356+ Read ( ) ;
357+ CreateToken ( DothtmlTokenType . Slash ) ;
358+
359+ if ( ! ReadTagOrAttributeName ( isAttributeName : false , out _ , out _ ) )
360+ {
361+ CreateToken ( DothtmlTokenType . Text , errorProvider : t => CreateTokenError ( t , DothtmlTokenType . OpenTag , DothtmlTokenizerErrors . TagNameExpected ) ) ;
362+ }
363+
364+ SkipWhitespace ( ) ;
365+
366+ if ( Read ( ) != '>' )
367+ {
368+ CreateToken ( DothtmlTokenType . CloseTag , errorProvider : t => CreateTokenError ( t , DothtmlTokenType . OpenTag , DothtmlTokenizerErrors . TagNotClosed ) ) ;
369+ }
370+ else
371+ {
372+ CreateToken ( DothtmlTokenType . CloseTag ) ;
373+ }
374+
375+ return ;
376+ }
377+ Read ( ) ;
378+ }
379+
380+ // not terminated
381+
382+ CreateToken ( DothtmlTokenType . Text , errorProvider : t => CreateTokenError ( t , DothtmlTokenType . OpenTag , DothtmlTokenizerErrors . TagNotClosed ) ) ;
383+ }
384+
323385 public ReadElementType ReadHtmlSpecial ( bool openBraceConsumed = false )
324386 {
325387 var s = ReadOneOf ( "![CDATA[" , "!--" , "!DOCTYPE" , "?" , "%--" ) ;
@@ -437,7 +499,7 @@ private void Assert(bool expression)
437499 /// <summary>
438500 /// Reads the name of the tag or attribute.
439501 /// </summary>
440- private bool ReadTagOrAttributeName ( bool isAttributeName )
502+ private bool ReadTagOrAttributeName ( bool isAttributeName , out string ? prefix , out string ? name )
441503 {
442504 var readIdentifierFunc = isAttributeName ? ( Func < DothtmlTokenType , char , bool > ) ReadAttributeName : ( Func < DothtmlTokenType , char , bool > ) ReadIdentifier ;
443505
@@ -446,6 +508,7 @@ private bool ReadTagOrAttributeName(bool isAttributeName)
446508 // read the identifier
447509 if ( ! readIdentifierFunc ( DothtmlTokenType . Text , ':' ) )
448510 {
511+ prefix = name = null ;
449512 return false ;
450513 }
451514 }
@@ -457,14 +520,23 @@ private bool ReadTagOrAttributeName(bool isAttributeName)
457520
458521 if ( Peek ( ) == ':' )
459522 {
523+ prefix = Tokens [ ^ 1 ] . Text ;
524+
460525 Read ( ) ;
461526 CreateToken ( DothtmlTokenType . Colon , ":" ) ;
462527
463528 if ( ! readIdentifierFunc ( DothtmlTokenType . Text , '\0 ' ) )
464529 {
465530 CreateToken ( DothtmlTokenType . Text , errorProvider : t => CreateTokenError ( t , DothtmlTokenType . OpenTag , DothtmlTokenizerErrors . MissingTagName ) ) ;
531+ name = null ;
466532 return true ;
467533 }
534+ name = Tokens [ ^ 1 ] . Text ;
535+ }
536+ else
537+ {
538+ prefix = null ;
539+ name = Tokens [ ^ 1 ] . Text ;
468540 }
469541
470542 SkipWhitespace ( ) ;
@@ -477,7 +549,7 @@ private bool ReadTagOrAttributeName(bool isAttributeName)
477549 private bool ReadAttribute ( )
478550 {
479551 // attribute name
480- if ( ! ReadTagOrAttributeName ( isAttributeName : true ) )
552+ if ( ! ReadTagOrAttributeName ( isAttributeName : true , out _ , out _ ) )
481553 {
482554 return false ;
483555 }
0 commit comments