Repository: alecthomas/chroma
Pull request #1032 (2024-12-29 16:10:12)

Add WebVTT lexer (#707)
Implements a WebVTT lexer (#707)


Patch:
diff --git a/lexers/embedded/webvtt.xml b/lexers/embedded/webvtt.xml
new file mode 100644
index 0000000..08a7efc
--- /dev/null
+++ b/lexers/embedded/webvtt.xml
@@ -0,0 +1,283 @@
+<lexer>
+  <config>
+    <name>WebVTT</name>
+    <alias>vtt</alias>
+    <filename>*.vtt</filename>
+    <mime_type>text/vtt</mime_type>
+  </config>
+  <!--
+    The WebVTT spec refers to a WebVTT line terminator as either CRLF, CR or LF.
+    (https://www.w3.org/TR/webvtt1/#webvtt-line-terminator) However, with this 
+    definition it is unclear whether CRLF is one line terminator (CRLF) or two
+    line terminators (CR and LF).
+
+    To work around this ambiguity, only CRLF and LF are considered as line terminators. 
+    To my knowledge only classic Mac OS uses CR as line terminators, so the lexer should
+    still work for most files.
+  -->
+  <rules>
+    <!-- https://www.w3.org/TR/webvtt1/#webvtt-file-body -->
+    <state name="root">
+      <rule pattern="(\AWEBVTT)((?:[ \t][^\r\n]*)?(?:\r?\n){2,})">
+        <bygroups>
+          <token type="Keyword" />
+          <token type="Text" />
+        </bygroups>
+      </rule>
+      <rule pattern="(^REGION)([ \t]*$)">
+        <bygroups>
+          <token type="Keyword" />
+          <token type="Text" />
+        </bygroups>
+        <push state="region-settings-list" />
+      </rule>
+      <rule
+        pattern="(^STYLE)([ \t]*$)((?:(?!&#45;&#45;&gt;)[\s\S])*?)((?:\r?\n){2})">
+        <bygroups>
+          <token type="Keyword" />
+          <token type="Text" />
+          <using lexer="CSS" />
+          <token type="Text" />
+        </bygroups>
+      </rule>
+      <rule>
+        <include state="comment" />
+      </rule>
+      <rule
+        pattern="(?=((?![^\r\n]*&#45;&#45;&gt;)[^\r\n]*\r?\n)?(\d{2}:)?(?:[0-5][0-9]):(?:[0-5][0-9])\.\d{3}[ \t]+&#45;&#45;&gt;[ \t]+(\d{2}:)?(?:[0-5][0-9]):(?:[0-5][0-9])\.\d{3})"
+      >
+        <push state="cues" />
+      </rule>
+    </state>
+
+    <!-- https://www.w3.org/TR/webvtt1/#webvtt-region-settings-list -->
+    <state name="region-settings-list">
+      <rule pattern="(?: |\t|\r?\n(?!\r?\n))+">
+        <token type="Text" />
+      </rule>
+      <rule pattern="(?:\r?\n){2}">
+        <token type="Text" />
+        <pop depth="1" />
+      </rule>
+      <rule pattern="(id)(:)(?!&#45;&#45;&gt;)(\S+)">
+        <bygroups>
+          <token type="Keyword" />
+          <token type="Punctuation" />
+          <token type="Literal" />
+        </bygroups>
+      </rule>
+      <rule pattern="(width)(:)((?:[1-9]?\d|100)(?:\.\d+)?)(%)">
+        <bygroups>
+          <token type="Keyword" />
+          <token type="Punctuation" />
+          <token type="Literal" />
+          <token type="KeywordType" />
+        </bygroups>
+      </rule>
+      <rule pattern="(lines)(:)(\d+)">
+        <bygroups>
+          <token type="Keyword" />
+          <token type="Punctuation" />
+          <token type="Literal" />
+        </bygroups>
+      </rule>
+      <rule
+        pattern="(regionanchor|viewportanchor)(:)((?:[1-9]?\d|100)(?:\.\d+)?)(%)(,)((?:[1-9]?\d|100)(?:\.\d+)?)(%)">
+        <bygroups>
+          <token type="Keyword" />
+          <token type="Punctuation" />
+          <token type="Literal" />
+          <token type="KeywordType" />
+          <token type="Punctuation" />
+          <token type="Literal" />
+          <token type="KeywordType" />
+        </bygroups>
+      </rule>
+      <rule pattern="(scroll)(:)(up)">
+        <bygroups>
+          <token type="Keyword" />
+          <token type="Punctuation" />
+          <token type="KeywordConstant" />
+        </bygroups>
+      </rule>
+    </state>
+
+    <!-- https://www.w3.org/TR/webvtt1/#webvtt-comment-block -->
+    <state name="comment">
+      <rule
+        pattern="^NOTE( |\t|\r?\n)((?!&#45;&#45;&gt;)[\s\S])*?(?:(\r?\n){2}|\Z)">
+        <token type="Comment" />
+      </rule>
+    </state>
+
+    <!-- 
+      "Zero or more WebVTT cue blocks and WebVTT comment blocks separated from each other by one or more
+      WebVTT line terminators." (https://www.w3.org/TR/webvtt1/#file-structure)
+    -->
+    <state name="cues">
+      <rule
+        pattern="(?:((?!&#45;&#45;&gt;)[^\r\n]+)?(\r?\n))?((?:\d{2}:)?(?:[0-5][0-9]):(?:[0-5][0-9])\.\d{3})([ \t]+)(&#45;&#45;&gt;)([ \t]+)((?:\d{2}:)?(?:[0-5][0-9]):(?:[0-5][0-9])\.\d{3})([ \t]*)">
+        <bygroups>
+          <token type="Name" />
+          <token type="Text" />
+          <token type="LiteralDate" />
+          <token type="Text" />
+          <token type="Operator" />
+          <token type="Text" />
+          <token type="LiteralDate" />
+          <token type="Text" />
+        </bygroups>
+        <push state="cue-settings-list" />
+      </rule>
+      <rule>
+        <include state="comment" />
+      </rule>
+    </state>
+
+    <!-- https://www.w3.org/TR/webvtt1/#webvtt-cue-settings-list -->
+    <state name="cue-settings-list">
+      <rule pattern="[ \t]+">
+        <token type="Text" />
+      </rule>
+      <rule pattern="(vertical)(:)?(rl|lr)?">
+        <bygroups>
+          <token type="Keyword" />
+          <token type="Punctuation" />
+          <token type="KeywordConstant" />
+        </bygroups>
+      </rule>
+      <rule
+        pattern="(line)(:)?(?:(?:((?:[1-9]?\d|100)(?:\.\d+)?)(%)|(-?\d+))(?:(,)(start|center|end))?)?">
+        <bygroups>
+          <token type="Keyword" />
+          <token type="Punctuation" />
+          <token type="Literal" />
+          <token type="KeywordType" />
+          <token type="Literal" />
+          <token type="Punctuation" />
+          <token type="KeywordConstant" />
+        </bygroups>
+      </rule>
+      <rule
+        pattern="(position)(:)?(?:(?:((?:[1-9]?\d|100)(?:\.\d+)?)(%)|(-?\d+))(?:(,)(line-left|center|line-right))?)?">
+        <bygroups>
+          <token type="Keyword" />
+          <token type="Punctuation" />
+          <token type="Literal" />
+          <token type="KeywordType" />
+          <token type="Literal" />
+          <token type="Punctuation" />
+          <token type="KeywordConstant" />
+        </bygroups>
+      </rule>
+      <rule pattern="(size)(:)?(?:((?:[1-9]?\d|100)(?:\.\d+)?)(%))?">
+        <bygroups>
+          <token type="Keyword" />
+          <token type="Punctuation" />
+          <token type="Literal" />
+          <token type="KeywordType" />
+        </bygroups>
+      </rule>
+      <rule pattern="(align)(:)?(start|center|end|left|right)?">
+        <bygroups>
+          <token type="Keyword" />
+          <token type="Punctuation" />
+          <token type="KeywordConstant" />
+        </bygroups>
+      </rule>
+      <rule pattern="(region)(:)?((?![^\r\n]*&#45;&#45;&gt;(?=[ \t]+?))[^ \t\r\n]+)?">
+        <bygroups>
+          <token type="Keyword" />
+          <token type="Punctuation" />
+          <token type="Literal" />
+        </bygroups>
+      </rule>
+      <rule
+        pattern="(?=\r?\n)">
+        <push state="cue-payload" />
+      </rule>
+    </state>
+
+    <!-- https://www.w3.org/TR/webvtt1/#cue-payload -->
+    <state name="cue-payload">
+      <rule pattern="(\r?\n){2,}">
+        <token type="Text" />
+        <pop depth="2" />
+      </rule>
+      <rule pattern="[^&lt;&amp;]+?">
+        <token type="Text" />
+      </rule>
+      <rule pattern="&amp;(#\d+|#x[0-9A-Fa-f]+|[a-zA-Z0-9]+);">
+        <token type="Text" />
+      </rule>
+      <rule pattern="(?=&lt;)">
+        <token type="Text" />
+        <push state="cue-span-tag" />
+      </rule>
+    </state>
+    <state name="cue-span-tag">
+      <rule
+        pattern="&lt;(?=c|i|b|u|ruby|rt|v|lang|(?:\d{2}:)?(?:[0-5][0-9]):(?:[0-5][0-9])\.\d{3})">
+        <token type="Punctuation" />
+        <push state="cue-span-start-tag-name" />
+      </rule>
+      <rule pattern="(&lt;/)(c|i|b|u|ruby|rt|v|lang)">
+        <bygroups>
+          <token type="Punctuation" />
+          <token type="NameTag" />
+        </bygroups>
+      </rule>
+      <rule pattern="&gt;">
+        <token type="Punctuation" />
+        <pop depth="1" />
+      </rule>
+    </state>
+    <state name="cue-span-start-tag-name">
+      <rule pattern="(c|i|b|u|ruby|rt)|((?:\d{2}:)?(?:[0-5][0-9]):(?:[0-5][0-9])\.\d{3})">
+        <bygroups>
+          <token type="NameTag" />
+          <token type="LiteralDate" />
+        </bygroups>
+        <push state="cue-span-classes-without-annotations" />
+      </rule>
+      <rule pattern="v|lang">
+        <token type="NameTag" />
+        <push state="cue-span-classes-with-annotations" />
+      </rule>
+    </state>
+    <state name="cue-span-classes-without-annotations">
+      <rule>
+        <include state="cue-span-classes" />
+      </rule>
+      <rule pattern="(?=&gt;)">
+        <pop depth="2" />
+      </rule>
+    </state>
+    <state name="cue-span-classes-with-annotations">
+      <rule>
+        <include state="cue-span-classes" />
+      </rule>
+      <rule pattern="(?=[ \t])">
+        <push state="cue-span-start-tag-annotations" />
+      </rule>
+    </state>
+    <state name="cue-span-classes">
+      <rule pattern="(\.)([^ \t\n\r&amp;&lt;&gt;\.]+)">
+        <bygroups>
+          <token type="Punctuation" />
+          <token type="NameTag" />
+        </bygroups>
+      </rule>
+    </state>
+    <state name="cue-span-start-tag-annotations">
+      <rule
+        pattern="[ \t](?:[^\n\r&amp;&gt;]|&amp;(?:#\d+|#x[0-9A-Fa-f]+|[a-zA-Z0-9]+);)+">
+        <token type="Text" />
+      </rule>
+      <rule pattern="(?=&gt;)">
+        <token type="Text" />
+        <pop depth="3" />
+      </rule>
+    </state>
+  </rules>
+</lexer>
\ No newline at end of file


Test patch:
diff --git a/lexers/testdata/webvtt/webvtt.actual b/lexers/testdata/webvtt/webvtt.actual
new file mode 100644
index 0000000..6ace873
--- /dev/null
+++ b/lexers/testdata/webvtt/webvtt.actual
@@ -0,0 +1,210 @@
+WEBVTT
+
+NOTE One-line comment
+
+NOTE
+This is a
+Multi-line
+comment
+
+NOTE Multi-line comment containing keywords: REGION STYLE
+REGION
+STYLE
+
+NOTE Many blank lines
+
+
+
+
+NOTE Valid region definition blocks
+
+REGION
+id:fred
+width:40%
+lines:3
+regionanchor:0%,100%
+viewportanchor:10%,90%
+scroll:up
+
+REGION
+id:bill
+width:40%
+lines:3
+regionanchor:100%,100%
+viewportanchor:90%,90%
+scroll:up
+
+NOTE Region definition values with decimals
+
+REGION
+width:40.5%
+lines:3
+regionanchor:0.123%,50.123%
+viewportanchor:10.123%,90.123%
+scroll:up
+
+NOTE Region keyword followed by combinations of one or more tab or space characters
+
+REGION 
+width:40.5%
+
+REGION	
+width:40.5%
+
+REGION  
+width:40.5%
+
+REGION		
+width:40.5%
+
+REGION 	
+width:40.5%
+
+REGION	 
+width:40.5%
+
+NOTE Region settings don't have to be on separate lines
+
+REGION
+id:bill
+width:40%   lines:3
+regionanchor:100%,100%			viewportanchor:90%,90%
+   scroll:up
+
+STYLE
+/* This is CSS.*/
+::cue(b) {
+  color: peachpuff;
+}
+
+NOTE Style keyword followed by comobinations of one or more tab or space characters
+
+STYLE 
+::cue(v[voice="Esme"]) { color: cyan }
+
+STYLE	
+::cue(v[voice="Esme"]) { color: cyan }
+
+STYLE  
+::cue(v[voice="Esme"]) { color: cyan }
+
+STYLE		
+::cue(v[voice="Esme"]) { color: cyan }
+
+STYLE 	
+::cue(v[voice="Esme"]) { color: cyan }
+
+STYLE	 
+::cue(v[voice="Esme"]) { color: cyan }
+
+STYLE
+::cue(v[voice="Esme"]) { color: cyan }
+::cue(v[voice="Mary"]) { color: lime }
+/* Blank lines can be simulated with a line containing only a space or tab character. */
+ 
+::cue(i) { font-style: italic }
+::cue(.loud) { font-size: 2em }
+
+00:00.000 --> 00:01.000
+A cue
+
+00:00:01.000 --> 00:00:02.000
+A cue with hours
+
+00:00.000   -->   00:01.000
+A cue with more than one space around the arrow
+
+00:00.000	-->	00:01.000
+A cue with tabs around the arrow
+
+00:00.000 	-->	 00:01.000
+A cue with spaces and tabs around the arrow
+
+00:00.000 --> 00:01.000   
+A cue with spaces after the timestamp
+
+00:00.000 --> 00:01.000			
+A cue with tabs after the timestamp
+
+NOTE Comments can be put inbetween cues.
+
+NOTE An empty cue
+
+00:00:01.000 --> 00:00:02.000
+
+This is an id.
+00:02.000 --> 00:03.000
+A cue with an ID.
+
+00:02.000 --> 00:03.000
+A multi-
+line cue.
+
+00:02.000 --> 00:03.000
+A <c>multi-
+line cue
+with</c> a
+span.
+
+00:02.000 --> 00:03.000
+Multiple blank lines after this cue.
+
+
+
+
+
+02:00.000 --> 02:05.000 vertical:lr line:33%,start position:33%,line-right size:33% align:start region:fred
+A cue with all possible cue settings.
+
+02:00.000 --> 02:05.000 vertical:lr	line:33%,start	 position:33%,line-right	 size:33% align:start  region:fred
+Cue settings delimited by combinations of tabs and spaces
+
+02:00.000 --> 02:05.000 verticallr line33%,start position33%,line-right size33% alignstart regionfred
+Cue settings without the optional colons
+
+02:00.000 --> 02:05.000 vertical: line: position: size: align: region:
+Cue settings without the optional values
+
+02:00.000 --> 02:05.000 vertical line position size align region
+Cue settings with neither colons nor values
+
+02:00.000 --> 02:05.000 line:33.344% position:33.344% size:33.344%
+Decimal percentages
+
+02:00.000 --> 02:05.000 line:5 position:5
+Integer values for line and position
+
+02:00.000 --> 02:05.000 line:-5 position:-5
+Negative integer values for line and position
+
+02:00.000 --> 02:05.000
+<c.yellow>One class.</c>
+
+02:00.000 --> 02:05.000
+<c.yellow.bg_blue>Two classes.</c>
+
+04:00.000 --> 04:05.000
+<c.yellow.bg_blue.magenta.bg_black>Three classes.</c>
+
+02:00.000 --> 02:05.000
+Spans can sit <i>inbetween</i> regular text. 
+
+02:00.000 --> 02:05.000
+<ruby>Spans can also be <rt>nested.</rt></ruby>.
+
+02:00.000 --> 02:05.000
+<i.class1.class2>I spans can have classes.</i>
+<b.class1.class2>B spans can have classes.</b>
+<c.class1.class2>C spans can have classes.</c>
+<u.class1.class2>U spans can have classes.</u>
+<ruby.class1.class2>Ruby spans can have classes.</ruby>
+<rt.class1.class2>Rt spans can have classes.</rt>
+<v.class1.class2 V spans must also have annotations>after their classes.</v>
+<lang.class1.class2 Lang spans must have annotations>after their classes.</lang>
+
+02:00.000 --> 02:05.000
+Chapter title text can contain &#8220;character references&#8220;.
+Chapter title text can contain &quot;character references&quot;.
+Chapter title text can contain &x22;character references&x22;.
+
+NOTE Comment at end of file
\ No newline at end of file
diff --git a/lexers/testdata/webvtt/webvtt.expected b/lexers/testdata/webvtt/webvtt.expected
new file mode 100644
index 0000000..020fbd9
--- /dev/null
+++ b/lexers/testdata/webvtt/webvtt.expected
@@ -0,0 +1,889 @@
+[
+  {"type":"Keyword","value":"WEBVTT"},
+  {"type":"Text","value":"\n\n"},
+  {"type":"Comment","value":"NOTE One-line comment\n\nNOTE\nThis is a\nMulti-line\ncomment\n\nNOTE Multi-line comment containing keywords: REGION STYLE\nREGION\nSTYLE\n\nNOTE Many blank lines\n\n"},
+  {"type":"Error","value":"\n\n\n"},
+  {"type":"Comment","value":"NOTE Valid region definition blocks\n\n"},
+  {"type":"Keyword","value":"REGION"},
+  {"type":"Text","value":"\n"},
+  {"type":"Keyword","value":"id"},
+  {"type":"Punctuation","value":":"},
+  {"type":"Literal","value":"fred"},
+  {"type":"Text","value":"\n"},
+  {"type":"Keyword","value":"width"},
+  {"type":"Punctuation","value":":"},
+  {"type":"Literal","value":"40"},
+  {"type":"KeywordType","value":"%"},
+  {"type":"Text","value":"\n"},
+  {"type":"Keyword","value":"lines"},
+  {"type":"Punctuation","value":":"},
+  {"type":"Literal","value":"3"},
+  {"type":"Text","value":"\n"},
+  {"type":"Keyword","value":"regionanchor"},
+  {"type":"Punctuation","value":":"},
+  {"type":"Literal","value":"0"},
+  {"type":"KeywordType","value":"%"},
+  {"type":"Punctuation","value":","},
+  {"type":"Literal","value":"100"},
+  {"type":"KeywordType","value":"%"},
+  {"type":"Text","value":"\n"},
+  {"type":"Keyword","value":"viewportanchor"},
+  {"type":"Punctuation","value":":"},
+  {"type":"Literal","value":"10"},
+  {"type":"KeywordType","value":"%"},
+  {"type":"Punctuation","value":","},
+  {"type":"Literal","value":"90"},
+  {"type":"KeywordType","value":"%"},
+  {"type":"Text","value":"\n"},
+  {"type":"Keyword","value":"scroll"},
+  {"type":"Punctuation","value":":"},
+  {"type":"KeywordConstant","value":"up"},
+  {"type":"Text","value":"\n\n"},
+  {"type":"Keyword","value":"REGION"},
+  {"type":"Text","value":"\n"},
+  {"type":"Keyword","value":"id"},
+  {"type":"Punctuation","value":":"},
+  {"type":"Literal","value":"bill"},
+  {"type":"Text","value":"\n"},
+  {"type":"Keyword","value":"width"},
+  {"type":"Punctuation","value":":"},
+  {"type":"Literal","value":"40"},
+  {"type":"KeywordType","value":"%"},
+  {"type":"Text","value":"\n"},
+  {"type":"Keyword","value":"lines"},
+  {"type":"Punctuation","value":":"},
+  {"type":"Literal","value":"3"},
+  {"type":"Text","value":"\n"},
+  {"type":"Keyword","value":"regionanchor"},
+  {"type":"Punctuation","value":":"},
+  {"type":"Literal","value":"100"},
+  {"type":"KeywordType","value":"%"},
+  {"type":"Punctuation","value":","},
+  {"type":"Literal","value":"100"},
+  {"type":"KeywordType","value":"%"},
+  {"type":"Text","value":"\n"},
+  {"type":"Keyword","value":"viewportanchor"},
+  {"type":"Punctuation","value":":"},
+  {"type":"Literal","value":"90"},
+  {"type":"KeywordType","value":"%"},
+  {"type":"Punctuation","value":","},
+  {"type":"Literal","value":"90"},
+  {"type":"KeywordType","value":"%"},
+  {"type":"Text","value":"\n"},
+  {"type":"Keyword","value":"scroll"},
+  {"type":"Punctuation","value":":"},
+  {"type":"KeywordConstant","value":"up"},
+  {"type":"Text","value":"\n\n"},
+  {"type":"Comment","value":"NOTE Region definition values with decimals\n\n"},
+  {"type":"Keyword","value":"REGION"},
+  {"type":"Text","value":"\n"},
+  {"type":"Keyword","value":"width"},
+  {"type":"Punctuation","value":":"},
+  {"type":"Literal","value":"40.5"},
+  {"type":"KeywordType","value":"%"},
+  {"type":"Text","value":"\n"},
+  {"type":"Keyword","value":"lines"},
+  {"type":"Punctuation","value":":"},
+  {"type":"Literal","value":"3"},
+  {"type":"Text","value":"\n"},
+  {"type":"Keyword","value":"regionanchor"},
+  {"type":"Punctuation","value":":"},
+  {"type":"Literal","value":"0.123"},
+  {"type":"KeywordType","value":"%"},
+  {"type":"Punctuation","value":","},
+  {"type":"Literal","value":"50.123"},
+  {"type":"KeywordType","value":"%"},
+  {"type":"Text","value":"\n"},
+  {"type":"Keyword","value":"viewportanchor"},
+  {"type":"Punctuation","value":":"},
+  {"type":"Literal","value":"10.123"},
+  {"type":"KeywordType","value":"%"},
+  {"type":"Punctuation","value":","},
+  {"type":"Literal","value":"90.123"},
+  {"type":"KeywordType","value":"%"},
+  {"type":"Text","value":"\n"},
+  {"type":"Keyword","value":"scroll"},
+  {"type":"Punctuation","value":":"},
+  {"type":"KeywordConstant","value":"up"},
+  {"type":"Text","value":"\n\n"},
+  {"type":"Comment","value":"NOTE Region keyword followed by combinations of one or more tab or space characters\n\n"},
+  {"type":"Keyword","value":"REGION"},
+  {"type":"Text","value":" \n"},
+  {"type":"Keyword","value":"width"},
+  {"type":"Punctuation","value":":"},
+  {"type":"Literal","value":"40.5"},
+  {"type":"KeywordType","value":"%"},
+  {"type":"Text","value":"\n\n"},
+  {"type":"Keyword","value":"REGION"},
+  {"type":"Text","value":"\t\n"},
+  {"type":"Keyword","value":"width"},
+  {"type":"Punctuation","value":":"},
+  {"type":"Literal","value":"40.5"},
+  {"type":"KeywordType","value":"%"},
+  {"type":"Text","value":"\n\n"},
+  {"type":"Keyword","value":"REGION"},
+  {"type":"Text","value":"  \n"},
+  {"type":"Keyword","value":"width"},
+  {"type":"Punctuation","value":":"},
+  {"type":"Literal","value":"40.5"},
+  {"type":"KeywordType","value":"%"},
+  {"type":"Text","value":"\n\n"},
+  {"type":"Keyword","value":"REGION"},
+  {"type":"Text","value":"\t\t\n"},
+  {"type":"Keyword","value":"width"},
+  {"type":"Punctuation","value":":"},
+  {"type":"Literal","value":"40.5"},
+  {"type":"KeywordType","value":"%"},
+  {"type":"Text","value":"\n\n"},
+  {"type":"Keyword","value":"REGION"},
+  {"type":"Text","value":" \t\n"},
+  {"type":"Keyword","value":"width"},
+  {"type":"Punctuation","value":":"},
+  {"type":"Literal","value":"40.5"},
+  {"type":"KeywordType","value":"%"},
+  {"type":"Text","value":"\n\n"},
+  {"type":"Keyword","value":"REGION"},
+  {"type":"Text","value":"\t \n"},
+  {"type":"Keyword","value":"width"},
+  {"type":"Punctuation","value":":"},
+  {"type":"Literal","value":"40.5"},
+  {"type":"KeywordType","value":"%"},
+  {"type":"Text","value":"\n\n"},
+  {"type":"Comment","value":"NOTE Region settings don't have to be on separate lines\n\n"},
+  {"type":"Keyword","value":"REGION"},
+  {"type":"Text","value":"\n"},
+  {"type":"Keyword","value":"id"},
+  {"type":"Punctuation","value":":"},
+  {"type":"Literal","value":"bill"},
+  {"type":"Text","value":"\n"},
+  {"type":"Keyword","value":"width"},
+  {"type":"Punctuation","value":":"},
+  {"type":"Literal","value":"40"},
+  {"type":"KeywordType","value":"%"},
+  {"type":"Text","value":"   "},
+  {"type":"Keyword","value":"lines"},
+  {"type":"Punctuation","value":":"},
+  {"type":"Literal","value":"3"},
+  {"type":"Text","value":"\n"},
+  {"type":"Keyword","value":"regionanchor"},
+  {"type":"Punctuation","value":":"},
+  {"type":"Literal","value":"100"},
+  {"type":"KeywordType","value":"%"},
+  {"type":"Punctuation","value":","},
+  {"type":"Literal","value":"100"},
+  {"type":"KeywordType","value":"%"},
+  {"type":"Text","value":"\t\t\t"},
+  {"type":"Keyword","value":"viewportanchor"},
+  {"type":"Punctuation","value":":"},
+  {"type":"Literal","value":"90"},
+  {"type":"KeywordType","value":"%"},
+  {"type":"Punctuation","value":","},
+  {"type":"Literal","value":"90"},
+  {"type":"KeywordType","value":"%"},
+  {"type":"Text","value":"\n   "},
+  {"type":"Keyword","value":"scroll"},
+  {"type":"Punctuation","value":":"},
+  {"type":"KeywordConstant","value":"up"},
+  {"type":"Text","value":"\n\n"},
+  {"type":"Keyword","value":"STYLE"},
+  {"type":"Text","value":"\n"},
+  {"type":"Comment","value":"/* This is CSS.*/"},
+  {"type":"Text","value":"\n"},
+  {"type":"Punctuation","value":"::"},
+  {"type":"NameDecorator","value":"cue"},
+  {"type":"Operator","value":"("},
+  {"type":"NameTag","value":"b"},
+  {"type":"Operator","value":")"},
+  {"type":"Text","value":" "},
+  {"type":"Punctuation","value":"{"},
+  {"type":"Text","value":"\n  "},
+  {"type":"Keyword","value":"color"},
+  {"type":"Punctuation","value":":"},
+  {"type":"Text","value":" "},
+  {"type":"KeywordConstant","value":"peachpuff"},
+  {"type":"Punctuation","value":";"},
+  {"type":"Text","value":"\n"},
+  {"type":"Punctuation","value":"}"},
+  {"type":"Text","value":"\n\n"},
+  {"type":"Comment","value":"NOTE Style keyword followed by comobinations of one or more tab or space characters\n\n"},
+  {"type":"Keyword","value":"STYLE"},
+  {"type":"Text","value":" \n"},
+  {"type":"Punctuation","value":"::"},
+  {"type":"NameDecorator","value":"cue"},
+  {"type":"Operator","value":"("},
+  {"type":"NameTag","value":"v"},
+  {"type":"Operator","value":"["},
+  {"type":"NameTag","value":"voice"},
+  {"type":"Operator","value":"="},
+  {"type":"LiteralStringDouble","value":"\"Esme\""},
+  {"type":"Operator","value":"])"},
+  {"type":"Text","value":" "},
+  {"type":"Punctuation","value":"{"},
+  {"type":"Text","value":" "},
+  {"type":"Keyword","value":"color"},
+  {"type":"Punctuation","value":":"},
+  {"type":"Text","value":" "},
+  {"type":"KeywordConstant","value":"cyan"},
+  {"type":"Text","value":" "},
+  {"type":"Punctuation","value":"}"},
+  {"type":"Text","value":"\n\n"},
+  {"type":"Keyword","value":"STYLE"},
+  {"type":"Text","value":"\t\n"},
+  {"type":"Punctuation","value":"::"},
+  {"type":"NameDecorator","value":"cue"},
+  {"type":"Operator","value":"("},
+  {"type":"NameTag","value":"v"},
+  {"type":"Operator","value":"["},
+  {"type":"NameTag","value":"voice"},
+  {"type":"Operator","value":"="},
+  {"type":"LiteralStringDouble","value":"\"Esme\""},
+  {"type":"Operator","value":"])"},
+  {"type":"Text","value":" "},
+  {"type":"Punctuation","value":"{"},
+  {"type":"Text","value":" "},
+  {"type":"Keyword","value":"color"},
+  {"type":"Punctuation","value":":"},
+  {"type":"Text","value":" "},
+  {"type":"KeywordConstant","value":"cyan"},
+  {"type":"Text","value":" "},
+  {"type":"Punctuation","value":"}"},
+  {"type":"Text","value":"\n\n"},
+  {"type":"Keyword","value":"STYLE"},
+  {"type":"Text","value":"  \n"},
+  {"type":"Punctuation","value":"::"},
+  {"type":"NameDecorator","value":"cue"},
+  {"type":"Operator","value":"("},
+  {"type":"NameTag","value":"v"},
+  {"type":"Operator","value":"["},
+  {"type":"NameTag","value":"voice"},
+  {"type":"Operator","value":"="},
+  {"type":"LiteralStringDouble","value":"\"Esme\""},
+  {"type":"Operator","value":"])"},
+  {"type":"Text","value":" "},
+  {"type":"Punctuation","value":"{"},
+  {"type":"Text","value":" "},
+  {"type":"Keyword","value":"color"},
+  {"type":"Punctuation","value":":"},
+  {"type":"Text","value":" "},
+  {"type":"KeywordConstant","value":"cyan"},
+  {"type":"Text","value":" "},
+  {"type":"Punctuation","value":"}"},
+  {"type":"Text","value":"\n\n"},
+  {"type":"Keyword","value":"STYLE"},
+  {"type":"Text","value":"\t\t\n"},
+  {"type":"Punctuation","value":"::"},
+  {"type":"NameDecorator","value":"cue"},
+  {"type":"Operator","value":"("},
+  {"type":"NameTag","value":"v"},
+  {"type":"Operator","value":"["},
+  {"type":"NameTag","value":"voice"},
+  {"type":"Operator","value":"="},
+  {"type":"LiteralStringDouble","value":"\"Esme\""},
+  {"type":"Operator","value":"])"},
+  {"type":"Text","value":" "},
+  {"type":"Punctuation","value":"{"},
+  {"type":"Text","value":" "},
+  {"type":"Keyword","value":"color"},
+  {"type":"Punctuation","value":":"},
+  {"type":"Text","value":" "},
+  {"type":"KeywordConstant","value":"cyan"},
+  {"type":"Text","value":" "},
+  {"type":"Punctuation","value":"}"},
+  {"type":"Text","value":"\n\n"},
+  {"type":"Keyword","value":"STYLE"},
+  {"type":"Text","value":" \t\n"},
+  {"type":"Punctuation","value":"::"},
+  {"type":"NameDecorator","value":"cue"},
+  {"type":"Operator","value":"("},
+  {"type":"NameTag","value":"v"},
+  {"type":"Operator","value":"["},
+  {"type":"NameTag","value":"voice"},
+  {"type":"Operator","value":"="},
+  {"type":"LiteralStringDouble","value":"\"Esme\""},
+  {"type":"Operator","value":"])"},
+  {"type":"Text","value":" "},
+  {"type":"Punctuation","value":"{"},
+  {"type":"Text","value":" "},
+  {"type":"Keyword","value":"color"},
+  {"type":"Punctuation","value":":"},
+  {"type":"Text","value":" "},
+  {"type":"KeywordConstant","value":"cyan"},
+  {"type":"Text","value":" "},
+  {"type":"Punctuation","value":"}"},
+  {"type":"Text","value":"\n\n"},
+  {"type":"Keyword","value":"STYLE"},
+  {"type":"Text","value":"\t \n"},
+  {"type":"Punctuation","value":"::"},
+  {"type":"NameDecorator","value":"cue"},
+  {"type":"Operator","value":"("},
+  {"type":"NameTag","value":"v"},
+  {"type":"Operator","value":"["},
+  {"type":"NameTag","value":"voice"},
+  {"type":"Operator","value":"="},
+  {"type":"LiteralStringDouble","value":"\"Esme\""},
+  {"type":"Operator","value":"])"},
+  {"type":"Text","value":" "},
+  {"type":"Punctuation","value":"{"},
+  {"type":"Text","value":" "},
+  {"type":"Keyword","value":"color"},
+  {"type":"Punctuation","value":":"},
+  {"type":"Text","value":" "},
+  {"type":"KeywordConstant","value":"cyan"},
+  {"type":"Text","value":" "},
+  {"type":"Punctuation","value":"}"},
+  {"type":"Text","value":"\n\n"},
+  {"type":"Keyword","value":"STYLE"},
+  {"type":"Text","value":"\n"},
+  {"type":"Punctuation","value":"::"},
+  {"type":"NameDecorator","value":"cue"},
+  {"type":"Operator","value":"("},
+  {"type":"NameTag","value":"v"},
+  {"type":"Operator","value":"["},
+  {"type":"NameTag","value":"voice"},
+  {"type":"Operator","value":"="},
+  {"type":"LiteralStringDouble","value":"\"Esme\""},
+  {"type":"Operator","value":"])"},
+  {"type":"Text","value":" "},
+  {"type":"Punctuation","value":"{"},
+  {"type":"Text","value":" "},
+  {"type":"Keyword","value":"color"},
+  {"type":"Punctuation","value":":"},
+  {"type":"Text","value":" "},
+  {"type":"KeywordConstant","value":"cyan"},
+  {"type":"Text","value":" "},
+  {"type":"Punctuation","value":"}"},
+  {"type":"Text","value":"\n"},
+  {"type":"Punctuation","value":"::"},
+  {"type":"NameDecorator","value":"cue"},
+  {"type":"Operator","value":"("},
+  {"type":"NameTag","value":"v"},
+  {"type":"Operator","value":"["},
+  {"type":"NameTag","value":"voice"},
+  {"type":"Operator","value":"="},
+  {"type":"LiteralStringDouble","value":"\"Mary\""},
+  {"type":"Operator","value":"])"},
+  {"type":"Text","value":" "},
+  {"type":"Punctuation","value":"{"},
+  {"type":"Text","value":" "},
+  {"type":"Keyword","value":"color"},
+  {"type":"Punctuation","value":":"},
+  {"type":"Text","value":" "},
+  {"type":"KeywordConstant","value":"lime"},
+  {"type":"Text","value":" "},
+  {"type":"Punctuation","value":"}"},
+  {"type":"Text","value":"\n"},
+  {"type":"Comment","value":"/* Blank lines can be simulated with a line containing only a space or tab character. */"},
+  {"type":"Text","value":"\n \n"},
+  {"type":"Punctuation","value":"::"},
+  {"type":"NameDecorator","value":"cue"},
+  {"type":"Operator","value":"("},
+  {"type":"NameTag","value":"i"},
+  {"type":"Operator","value":")"},
+  {"type":"Text","value":" "},
+  {"type":"Punctuation","value":"{"},
+  {"type":"Text","value":" "},
+  {"type":"Keyword","value":"font-style"},
+  {"type":"Punctuation","value":":"},
+  {"type":"Text","value":" "},
+  {"type":"KeywordConstant","value":"italic"},
+  {"type":"Text","value":" "},
+  {"type":"Punctuation","value":"}"},
+  {"type":"Text","value":"\n"},
+  {"type":"Punctuation","value":"::"},
+  {"type":"NameDecorator","value":"cue"},
+  {"type":"Operator","value":"("},
+  {"type":"Punctuation","value":"."},
+  {"type":"NameClass","value":"loud"},
+  {"type":"Operator","value":")"},
+  {"type":"Text","value":" "},
+  {"type":"Punctuation","value":"{"},
+  {"type":"Text","value":" "},
+  {"type":"Keyword","value":"font-size"},
+  {"type":"Punctuation","value":":"},
+  {"type":"Text","value":" "},
+  {"type":"LiteralNumberInteger","value":"2"},
+  {"type":"KeywordType","value":"em"},
+  {"type":"Text","value":" "},
+  {"type":"Punctuation","value":"}"},
+  {"type":"Text","value":"\n\n"},
+  {"type":"LiteralDate","value":"00:00.000"},
+  {"type":"Text","value":" "},
+  {"type":"Operator","value":"--\u003e"},
+  {"type":"Text","value":" "},
+  {"type":"LiteralDate","value":"00:01.000"},
+  {"type":"Text","value":"\nA cue\n\n"},
+  {"type":"LiteralDate","value":"00:00:01.000"},
+  {"type":"Text","value":" "},
+  {"type":"Operator","value":"--\u003e"},
+  {"type":"Text","value":" "},
+  {"type":"LiteralDate","value":"00:00:02.000"},
+  {"type":"Text","value":"\nA cue with hours\n\n"},
+  {"type":"LiteralDate","value":"00:00.000"},
+  {"type":"Text","value":"   "},
+  {"type":"Operator","value":"--\u003e"},
+  {"type":"Text","value":"   "},
+  {"type":"LiteralDate","value":"00:01.000"},
+  {"type":"Text","value":"\nA cue with more than one space around the arrow\n\n"},
+  {"type":"LiteralDate","value":"00:00.000"},
+  {"type":"Text","value":"\t"},
+  {"type":"Operator","value":"--\u003e"},
+  {"type":"Text","value":"\t"},
+  {"type":"LiteralDate","value":"00:01.000"},
+  {"type":"Text","value":"\nA cue with tabs around the arrow\n\n"},
+  {"type":"LiteralDate","value":"00:00.000"},
+  {"type":"Text","value":" \t"},
+  {"type":"Operator","value":"--\u003e"},
+  {"type":"Text","value":"\t "},
+  {"type":"LiteralDate","value":"00:01.000"},
+  {"type":"Text","value":"\nA cue with spaces and tabs around the arrow\n\n"},
+  {"type":"LiteralDate","value":"00:00.000"},
+  {"type":"Text","value":" "},
+  {"type":"Operator","value":"--\u003e"},
+  {"type":"Text","value":" "},
+  {"type":"LiteralDate","value":"00:01.000"},
+  {"type":"Text","value":"   \nA cue with spaces after the timestamp\n\n"},
+  {"type":"LiteralDate","value":"00:00.000"},
+  {"type":"Text","value":" "},
+  {"type":"Operator","value":"--\u003e"},
+  {"type":"Text","value":" "},
+  {"type":"LiteralDate","value":"00:01.000"},
+  {"type":"Text","value":"\t\t\t\nA cue with tabs after the timestamp\n\n"},
+  {"type":"Comment","value":"NOTE Comments can be put inbetween cues.\n\nNOTE An empty cue\n\n"},
+  {"type":"LiteralDate","value":"00:00:01.000"},
+  {"type":"Text","value":" "},
+  {"type":"Operator","value":"--\u003e"},
+  {"type":"Text","value":" "},
+  {"type":"LiteralDate","value":"00:00:02.000"},
+  {"type":"Text","value":"\n\n"},
+  {"type":"Name","value":"This is an id."},
+  {"type":"Text","value":"\n"},
+  {"type":"LiteralDate","value":"00:02.000"},
+  {"type":"Text","value":" "},
+  {"type":"Operator","value":"--\u003e"},
+  {"type":"Text","value":" "},
+  {"type":"LiteralDate","value":"00:03.000"},
+  {"type":"Text","value":"\nA cue with an ID.\n\n"},
+  {"type":"LiteralDate","value":"00:02.000"},
+  {"type":"Text","value":" "},
+  {"type":"Operator","value":"--\u003e"},
+  {"type":"Text","value":" "},
+  {"type":"LiteralDate","value":"00:03.000"},
+  {"type":"Text","value":"\nA multi-\nline cue.\n\n"},
+  {"type":"LiteralDate","value":"00:02.000"},
+  {"type":"Text","value":" "},
+  {"type":"Operator","value":"--\u003e"},
+  {"type":"Text","value":" "},
+  {"type":"LiteralDate","value":"00:03.000"},
+  {"type":"Text","value":"\nA "},
+  {"type":"Punctuation","value":"\u003c"},
+  {"type":"NameTag","value":"c"},
+  {"type":"Punctuation","value":"\u003e"},
+  {"type":"Text","value":"multi-\nline cue\nwith"},
+  {"type":"Punctuation","value":"\u003c/"},
+  {"type":"NameTag","value":"c"},
+  {"type":"Punctuation","value":"\u003e"},
+  {"type":"Text","value":" a\nspan.\n\n"},
+  {"type":"LiteralDate","value":"00:02.000"},
+  {"type":"Text","value":" "},
+  {"type":"Operator","value":"--\u003e"},
+  {"type":"Text","value":" "},
+  {"type":"LiteralDate","value":"00:03.000"},
+  {"type":"Text","value":"\nMultiple blank lines after this cue.\n\n\n\n\n\n"},
+  {"type":"LiteralDate","value":"02:00.000"},
+  {"type":"Text","value":" "},
+  {"type":"Operator","value":"--\u003e"},
+  {"type":"Text","value":" "},
+  {"type":"LiteralDate","value":"02:05.000"},
+  {"type":"Text","value":" "},
+  {"type":"Keyword","value":"vertical"},
+  {"type":"Punctuation","value":":"},
+  {"type":"KeywordConstant","value":"lr"},
+  {"type":"Text","value":" "},
+  {"type":"Keyword","value":"line"},
+  {"type":"Punctuation","value":":"},
+  {"type":"Literal","value":"33"},
+  {"type":"KeywordType","value":"%"},
+  {"type":"Punctuation","value":","},
+  {"type":"KeywordConstant","value":"start"},
+  {"type":"Text","value":" "},
+  {"type":"Keyword","value":"position"},
+  {"type":"Punctuation","value":":"},
+  {"type":"Literal","value":"33"},
+  {"type":"KeywordType","value":"%"},
+  {"type":"Punctuation","value":","},
+  {"type":"KeywordConstant","value":"line-right"},
+  {"type":"Text","value":" "},
+  {"type":"Keyword","value":"size"},
+  {"type":"Punctuation","value":":"},
+  {"type":"Literal","value":"33"},
+  {"type":"KeywordType","value":"%"},
+  {"type":"Text","value":" "},
+  {"type":"Keyword","value":"align"},
+  {"type":"Punctuation","value":":"},
+  {"type":"KeywordConstant","value":"start"},
+  {"type":"Text","value":" "},
+  {"type":"Keyword","value":"region"},
+  {"type":"Punctuation","value":":"},
+  {"type":"Literal","value":"fred"},
+  {"type":"Text","value":"\nA cue with all possible cue settings.\n\n"},
+  {"type":"LiteralDate","value":"02:00.000"},
+  {"type":"Text","value":" "},
+  {"type":"Operator","value":"--\u003e"},
+  {"type":"Text","value":" "},
+  {"type":"LiteralDate","value":"02:05.000"},
+  {"type":"Text","value":" "},
+  {"type":"Keyword","value":"vertical"},
+  {"type":"Punctuation","value":":"},
+  {"type":"KeywordConstant","value":"lr"},
+  {"type":"Text","value":"\t"},
+  {"type":"Keyword","value":"line"},
+  {"type":"Punctuation","value":":"},
+  {"type":"Literal","value":"33"},
+  {"type":"KeywordType","value":"%"},
+  {"type":"Punctuation","value":","},
+  {"type":"KeywordConstant","value":"start"},
+  {"type":"Text","value":"\t "},
+  {"type":"Keyword","value":"position"},
+  {"type":"Punctuation","value":":"},
+  {"type":"Literal","value":"33"},
+  {"type":"KeywordType","value":"%"},
+  {"type":"Punctuation","value":","},
+  {"type":"KeywordConstant","value":"line-right"},
+  {"type":"Text","value":"\t "},
+  {"type":"Keyword","value":"size"},
+  {"type":"Punctuation","value":":"},
+  {"type":"Literal","value":"33"},
+  {"type":"KeywordType","value":"%"},
+  {"type":"Text","value":" "},
+  {"type":"Keyword","value":"align"},
+  {"type":"Punctuation","value":":"},
+  {"type":"KeywordConstant","value":"start"},
+  {"type":"Text","value":"  "},
+  {"type":"Keyword","value":"region"},
+  {"type":"Punctuation","value":":"},
+  {"type":"Literal","value":"fred"},
+  {"type":"Text","value":"\nCue settings delimited by combinations of tabs and spaces\n\n"},
+  {"type":"LiteralDate","value":"02:00.000"},
+  {"type":"Text","value":" "},
+  {"type":"Operator","value":"--\u003e"},
+  {"type":"Text","value":" "},
+  {"type":"LiteralDate","value":"02:05.000"},
+  {"type":"Text","value":" "},
+  {"type":"Keyword","value":"vertical"},
+  {"type":"KeywordConstant","value":"lr"},
+  {"type":"Text","value":" "},
+  {"type":"Keyword","value":"line"},
+  {"type":"Literal","value":"33"},
+  {"type":"KeywordType","value":"%"},
+  {"type":"Punctuation","value":","},
+  {"type":"KeywordConstant","value":"start"},
+  {"type":"Text","value":" "},
+  {"type":"Keyword","value":"position"},
+  {"type":"Literal","value":"33"},
+  {"type":"KeywordType","value":"%"},
+  {"type":"Punctuation","value":","},
+  {"type":"KeywordConstant","value":"line-right"},
+  {"type":"Text","value":" "},
+  {"type":"Keyword","value":"size"},
+  {"type":"Literal","value":"33"},
+  {"type":"KeywordType","value":"%"},
+  {"type":"Text","value":" "},
+  {"type":"Keyword","value":"align"},
+  {"type":"KeywordConstant","value":"start"},
+  {"type":"Text","value":" "},
+  {"type":"Keyword","value":"region"},
+  {"type":"Literal","value":"fred"},
+  {"type":"Text","value":"\nCue settings without the optional colons\n\n"},
+  {"type":"LiteralDate","value":"02:00.000"},
+  {"type":"Text","value":" "},
+  {"type":"Operator","value":"--\u003e"},
+  {"type":"Text","value":" "},
+  {"type":"LiteralDate","value":"02:05.000"},
+  {"type":"Text","value":" "},
+  {"type":"Keyword","value":"vertical"},
+  {"type":"Punctuation","value":":"},
+  {"type":"Text","value":" "},
+  {"type":"Keyword","value":"line"},
+  {"type":"Punctuation","value":":"},
+  {"type":"Text","value":" "},
+  {"type":"Keyword","value":"position"},
+  {"type":"Punctuation","value":":"},
+  {"type":"Text","value":" "},
+  {"type":"Keyword","value":"size"},
+  {"type":"Punctuation","value":":"},
+  {"type":"Text","value":" "},
+  {"type":"Keyword","value":"align"},
+  {"type":"Punctuation","value":":"},
+  {"type":"Text","value":" "},
+  {"type":"Keyword","value":"region"},
+  {"type":"Punctuation","value":":"},
+  {"type":"Text","value":"\nCue settings without the optional values\n\n"},
+  {"type":"LiteralDate","value":"02:00.000"},
+  {"type":"Text","value":" "},
+  {"type":"Operator","value":"--\u003e"},
+  {"type":"Text","value":" "},
+  {"type":"LiteralDate","value":"02:05.000"},
+  {"type":"Text","value":" "},
+  {"type":"Keyword","value":"vertical"},
+  {"type":"Text","value":" "},
+  {"type":"Keyword","value":"line"},
+  {"type":"Text","value":" "},
+  {"type":"Keyword","value":"position"},
+  {"type":"Text","value":" "},
+  {"type":"Keyword","value":"size"},
+  {"type":"Text","value":" "},
+  {"type":"Keyword","value":"align"},
+  {"type":"Text","value":" "},
+  {"type":"Keyword","value":"region"},
+  {"type":"Text","value":"\nCue settings with neither colons nor values\n\n"},
+  {"type":"LiteralDate","value":"02:00.000"},
+  {"type":"Text","value":" "},
+  {"type":"Operator","value":"--\u003e"},
+  {"type":"Text","value":" "},
+  {"type":"LiteralDate","value":"02:05.000"},
+  {"type":"Text","value":" "},
+  {"type":"Keyword","value":"line"},
+  {"type":"Punctuation","value":":"},
+  {"type":"Literal","value":"33.344"},
+  {"type":"KeywordType","value":"%"},
+  {"type":"Text","value":" "},
+  {"type":"Keyword","value":"position"},
+  {"type":"Punctuation","value":":"},
+  {"type":"Literal","value":"33.344"},
+  {"type":"KeywordType","value":"%"},
+  {"type":"Text","value":" "},
+  {"type":"Keyword","value":"size"},
+  {"type":"Punctuation","value":":"},
+  {"type":"Literal","value":"33.344"},
+  {"type":"KeywordType","value":"%"},
+  {"type":"Text","value":"\nDecimal percentages\n\n"},
+  {"type":"LiteralDate","value":"02:00.000"},
+  {"type":"Text","value":" "},
+  {"type":"Operator","value":"--\u003e"},
+  {"type":"Text","value":" "},
+  {"type":"LiteralDate","value":"02:05.000"},
+  {"type":"Text","value":" "},
+  {"type":"Keyword","value":"line"},
+  {"type":"Punctuation","value":":"},
+  {"type":"Literal","value":"5"},
+  {"type":"Text","value":" "},
+  {"type":"Keyword","value":"position"},
+  {"type":"Punctuation","value":":"},
+  {"type":"Literal","value":"5"},
+  {"type":"Text","value":"\nInteger values for line and position\n\n"},
+  {"type":"LiteralDate","value":"02:00.000"},
+  {"type":"Text","value":" "},
+  {"type":"Operator","value":"--\u003e"},
+  {"type":"Text","value":" "},
+  {"type":"LiteralDate","value":"02:05.000"},
+  {"type":"Text","value":" "},
+  {"type":"Keyword","value":"line"},
+  {"type":"Punctuation","value":":"},
+  {"type":"Literal","value":"-5"},
+  {"type":"Text","value":" "},
+  {"type":"Keyword","value":"position"},
+  {"type":"Punctuation","value":":"},
+  {"type":"Literal","value":"-5"},
+  {"type":"Text","value":"\nNegative integer values for line and position\n\n"},
+  {"type":"LiteralDate","value":"02:00.000"},
+  {"type":"Text","value":" "},
+  {"type":"Operator","value":"--\u003e"},
+  {"type":"Text","value":" "},
+  {"type":"LiteralDate","value":"02:05.000"},
+  {"type":"Text","value":"\n"},
+  {"type":"Punctuation","value":"\u003c"},
+  {"type":"NameTag","value":"c"},
+  {"type":"Punctuation","value":"."},
+  {"type":"NameTag","value":"yellow"},
+  {"type":"Punctuation","value":"\u003e"},
+  {"type":"Text","value":"One class."},
+  {"type":"Punctuation","value":"\u003c/"},
+  {"type":"NameTag","value":"c"},
+  {"type":"Punctuation","value":"\u003e"},
+  {"type":"Text","value":"\n\n"},
+  {"type":"LiteralDate","value":"02:00.000"},
+  {"type":"Text","value":" "},
+  {"type":"Operator","value":"--\u003e"},
+  {"type":"Text","value":" "},
+  {"type":"LiteralDate","value":"02:05.000"},
+  {"type":"Text","value":"\n"},
+  {"type":"Punctuation","value":"\u003c"},
+  {"type":"NameTag","value":"c"},
+  {"type":"Punctuation","value":"."},
+  {"type":"NameTag","value":"yellow"},
+  {"type":"Punctuation","value":"."},
+  {"type":"NameTag","value":"bg_blue"},
+  {"type":"Punctuation","value":"\u003e"},
+  {"type":"Text","value":"Two classes."},
+  {"type":"Punctuation","value":"\u003c/"},
+  {"type":"NameTag","value":"c"},
+  {"type":"Punctuation","value":"\u003e"},
+  {"type":"Text","value":"\n\n"},
+  {"type":"LiteralDate","value":"04:00.000"},
+  {"type":"Text","value":" "},
+  {"type":"Operator","value":"--\u003e"},
+  {"type":"Text","value":" "},
+  {"type":"LiteralDate","value":"04:05.000"},
+  {"type":"Text","value":"\n"},
+  {"type":"Punctuation","value":"\u003c"},
+  {"type":"NameTag","value":"c"},
+  {"type":"Punctuation","value":"."},
+  {"type":"NameTag","value":"yellow"},
+  {"type":"Punctuation","value":"."},
+  {"type":"NameTag","value":"bg_blue"},
+  {"type":"Punctuation","value":"."},
+  {"type":"NameTag","value":"magenta"},
+  {"type":"Punctuation","value":"."},
+  {"type":"NameTag","value":"bg_black"},
+  {"type":"Punctuation","value":"\u003e"},
+  {"type":"Text","value":"Three classes."},
+  {"type":"Punctuation","value":"\u003c/"},
+  {"type":"NameTag","value":"c"},
+  {"type":"Punctuation","value":"\u003e"},
+  {"type":"Text","value":"\n\n"},
+  {"type":"LiteralDate","value":"02:00.000"},
+  {"type":"Text","value":" "},
+  {"type":"Operator","value":"--\u003e"},
+  {"type":"Text","value":" "},
+  {"type":"LiteralDate","value":"02:05.000"},
+  {"type":"Text","value":"\nSpans can sit "},
+  {"type":"Punctuation","value":"\u003c"},
+  {"type":"NameTag","value":"i"},
+  {"type":"Punctuation","value":"\u003e"},
+  {"type":"Text","value":"inbetween"},
+  {"type":"Punctuation","value":"\u003c/"},
+  {"type":"NameTag","value":"i"},
+  {"type":"Punctuation","value":"\u003e"},
+  {"type":"Text","value":" regular text. \n\n"},
+  {"type":"LiteralDate","value":"02:00.000"},
+  {"type":"Text","value":" "},
+  {"type":"Operator","value":"--\u003e"},
+  {"type":"Text","value":" "},
+  {"type":"LiteralDate","value":"02:05.000"},
+  {"type":"Text","value":"\n"},
+  {"type":"Punctuation","value":"\u003c"},
+  {"type":"NameTag","value":"ruby"},
+  {"type":"Punctuation","value":"\u003e"},
+  {"type":"Text","value":"Spans can also be "},
+  {"type":"Punctuation","value":"\u003c"},
+  {"type":"NameTag","value":"rt"},
+  {"type":"Punctuation","value":"\u003e"},
+  {"type":"Text","value":"nested."},
+  {"type":"Punctuation","value":"\u003c/"},
+  {"type":"NameTag","value":"rt"},
+  {"type":"Punctuation","value":"\u003e\u003c/"},
+  {"type":"NameTag","value":"ruby"},
+  {"type":"Punctuation","value":"\u003e"},
+  {"type":"Text","value":".\n\n"},
+  {"type":"LiteralDate","value":"02:00.000"},
+  {"type":"Text","value":" "},
+  {"type":"Operator","value":"--\u003e"},
+  {"type":"Text","value":" "},
+  {"type":"LiteralDate","value":"02:05.000"},
+  {"type":"Text","value":"\n"},
+  {"type":"Punctuation","value":"\u003c"},
+  {"type":"NameTag","value":"i"},
+  {"type":"Punctuation","value":"."},
+  {"type":"NameTag","value":"class1"},
+  {"type":"Punctuation","value":"."},
+  {"type":"NameTag","value":"class2"},
+  {"type":"Punctuation","value":"\u003e"},
+  {"type":"Text","value":"I spans can have classes."},
+  {"type":"Punctuation","value":"\u003c/"},
+  {"type":"NameTag","value":"i"},
+  {"type":"Punctuation","value":"\u003e"},
+  {"type":"Text","value":"\n"},
+  {"type":"Punctuation","value":"\u003c"},
+  {"type":"NameTag","value":"b"},
+  {"type":"Punctuation","value":"."},
+  {"type":"NameTag","value":"class1"},
+  {"type":"Punctuation","value":"."},
+  {"type":"NameTag","value":"class2"},
+  {"type":"Punctuation","value":"\u003e"},
+  {"type":"Text","value":"B spans can have classes."},
+  {"type":"Punctuation","value":"\u003c/"},
+  {"type":"NameTag","value":"b"},
+  {"type":"Punctuation","value":"\u003e"},
+  {"type":"Text","value":"\n"},
+  {"type":"Punctuation","value":"\u003c"},
+  {"type":"NameTag","value":"c"},
+  {"type":"Punctuation","value":"."},
+  {"type":"NameTag","value":"class1"},
+  {"type":"Punctuation","value":"."},
+  {"type":"NameTag","value":"class2"},
+  {"type":"Punctuation","value":"\u003e"},
+  {"type":"Text","value":"C spans can have classes."},
+  {"type":"Punctuation","value":"\u003c/"},
+  {"type":"NameTag","value":"c"},
+  {"type":"Punctuation","value":"\u003e"},
+  {"type":"Text","value":"\n"},
+  {"type":"Punctuation","value":"\u003c"},
+  {"type":"NameTag","value":"u"},
+  {"type":"Punctuation","value":"."},
+  {"type":"NameTag","value":"class1"},
+  {"type":"Punctuation","value":"."},
+  {"type":"NameTag","value":"class2"},
+  {"type":"Punctuation","value":"\u003e"},
+  {"type":"Text","value":"U spans can have classes."},
+  {"type":"Punctuation","value":"\u003c/"},
+  {"type":"NameTag","value":"u"},
+  {"type":"Punctuation","value":"\u003e"},
+  {"type":"Text","value":"\n"},
+  {"type":"Punctuation","value":"\u003c"},
+  {"type":"NameTag","value":"ruby"},
+  {"type":"Punctuation","value":"."},
+  {"type":"NameTag","value":"class1"},
+  {"type":"Punctuation","value":"."},
+  {"type":"NameTag","value":"class2"},
+  {"type":"Punctuation","value":"\u003e"},
+  {"type":"Text","value":"Ruby spans can have classes."},
+  {"type":"Punctuation","value":"\u003c/"},
+  {"type":"NameTag","value":"ruby"},
+  {"type":"Punctuation","value":"\u003e"},
+  {"type":"Text","value":"\n"},
+  {"type":"Punctuation","value":"\u003c"},
+  {"type":"NameTag","value":"rt"},
+  {"type":"Punctuation","value":"."},
+  {"type":"NameTag","value":"class1"},
+  {"type":"Punctuation","value":"."},
+  {"type":"NameTag","value":"class2"},
+  {"type":"Punctuation","value":"\u003e"},
+  {"type":"Text","value":"Rt spans can have classes."},
+  {"type":"Punctuation","value":"\u003c/"},
+  {"type":"NameTag","value":"rt"},
+  {"type":"Punctuation","value":"\u003e"},
+  {"type":"Text","value":"\n"},
+  {"type":"Punctuation","value":"\u003c"},
+  {"type":"NameTag","value":"v"},
+  {"type":"Punctuation","value":"."},
+  {"type":"NameTag","value":"class1"},
+  {"type":"Punctuation","value":"."},
+  {"type":"NameTag","value":"class2"},
+  {"type":"Text","value":" V spans must also have annotations"},
+  {"type":"Punctuation","value":"\u003e"},
+  {"type":"Text","value":"after their classes."},
+  {"type":"Punctuation","value":"\u003c/"},
+  {"type":"NameTag","value":"v"},
+  {"type":"Punctuation","value":"\u003e"},
+  {"type":"Text","value":"\n"},
+  {"type":"Punctuation","value":"\u003c"},
+  {"type":"NameTag","value":"lang"},
+  {"type":"Punctuation","value":"."},
+  {"type":"NameTag","value":"class1"},
+  {"type":"Punctuation","value":"."},
+  {"type":"NameTag","value":"class2"},
+  {"type":"Text","value":" Lang spans must have annotations"},
+  {"type":"Punctuation","value":"\u003e"},
+  {"type":"Text","value":"after their classes."},
+  {"type":"Punctuation","value":"\u003c/"},
+  {"type":"NameTag","value":"lang"},
+  {"type":"Punctuation","value":"\u003e"},
+  {"type":"Text","value":"\n\n"},
+  {"type":"LiteralDate","value":"02:00.000"},
+  {"type":"Text","value":" "},
+  {"type":"Operator","value":"--\u003e"},
+  {"type":"Text","value":" "},
+  {"type":"LiteralDate","value":"02:05.000"},
+  {"type":"Text","value":"\nChapter title text can contain \u0026#8220;character references\u0026#8220;.\nChapter title text can contain \u0026quot;character references\u0026quot;.\nChapter title text can contain \u0026x22;character references\u0026x22;.\n\n"},
+  {"type":"Comment","value":"NOTE Comment at end of file"}
+]
diff --git a/lexers/testdata/webvtt/webvtt_file_magic_1.actual b/lexers/testdata/webvtt/webvtt_file_magic_1.actual
new file mode 100644
index 0000000..47c19ef
--- /dev/null
+++ b/lexers/testdata/webvtt/webvtt_file_magic_1.actual
@@ -0,0 +1,4 @@
+WEBVTT	with some extra text after a tab character
+
+00:00.000 --> 00:01.000
+Mock cue
\ No newline at end of file
diff --git a/lexers/testdata/webvtt/webvtt_file_magic_1.expected b/lexers/testdata/webvtt/webvtt_file_magic_1.expected
new file mode 100644
index 0000000..dbbd24b
--- /dev/null
+++ b/lexers/testdata/webvtt/webvtt_file_magic_1.expected
@@ -0,0 +1,10 @@
+[
+  {"type":"Keyword","value":"WEBVTT"},
+  {"type":"Text","value":"\twith some extra text after a tab character\n\n"},
+  {"type":"LiteralDate","value":"00:00.000"},
+  {"type":"Text","value":" "},
+  {"type":"Operator","value":"--\u003e"},
+  {"type":"Text","value":" "},
+  {"type":"LiteralDate","value":"00:01.000"},
+  {"type":"Text","value":"\nMock cue"}
+]
diff --git a/lexers/testdata/webvtt/webvtt_file_magic_2.actual b/lexers/testdata/webvtt/webvtt_file_magic_2.actual
new file mode 100644
index 0000000..a299bf0
--- /dev/null
+++ b/lexers/testdata/webvtt/webvtt_file_magic_2.actual
@@ -0,0 +1,4 @@
+WEBVTT with some extra text after a space character
+
+00:00.000 --> 00:01.000
+Mock cue
\ No newline at end of file
diff --git a/lexers/testdata/webvtt/webvtt_file_magic_2.expected b/lexers/testdata/webvtt/webvtt_file_magic_2.expected
new file mode 100644
index 0000000..741fe18
--- /dev/null
+++ b/lexers/testdata/webvtt/webvtt_file_magic_2.expected
@@ -0,0 +1,10 @@
+[
+  {"type":"Keyword","value":"WEBVTT"},
+  {"type":"Text","value":" with some extra text after a space character\n\n"},
+  {"type":"LiteralDate","value":"00:00.000"},
+  {"type":"Text","value":" "},
+  {"type":"Operator","value":"--\u003e"},
+  {"type":"Text","value":" "},
+  {"type":"LiteralDate","value":"00:01.000"},
+  {"type":"Text","value":"\nMock cue"}
+]
diff --git a/lexers/testdata/webvtt/webvtt_file_magic_3.actual b/lexers/testdata/webvtt/webvtt_file_magic_3.actual
new file mode 100644
index 0000000..30d44fa
--- /dev/null
+++ b/lexers/testdata/webvtt/webvtt_file_magic_3.actual
@@ -0,0 +1,4 @@
+WEBVTT				with some extra text after multiple tab characters
+
+00:00.000 --> 00:01.000
+Mock cue
\ No newline at end of file
diff --git a/lexers/testdata/webvtt/webvtt_file_magic_3.expected b/lexers/testdata/webvtt/webvtt_file_magic_3.expected
new file mode 100644
index 0000000..62b3bf5
--- /dev/null
+++ b/lexers/testdata/webvtt/webvtt_file_magic_3.expected
@@ -0,0 +1,10 @@
+[
+  {"type":"Keyword","value":"WEBVTT"},
+  {"type":"Text","value":"\t\t\t\twith some extra text after multiple tab characters\n\n"},
+  {"type":"LiteralDate","value":"00:00.000"},
+  {"type":"Text","value":" "},
+  {"type":"Operator","value":"--\u003e"},
+  {"type":"Text","value":" "},
+  {"type":"LiteralDate","value":"00:01.000"},
+  {"type":"Text","value":"\nMock cue"}
+]
diff --git a/lexers/testdata/webvtt/webvtt_file_magic_4.actual b/lexers/testdata/webvtt/webvtt_file_magic_4.actual
new file mode 100644
index 0000000..a48e487
--- /dev/null
+++ b/lexers/testdata/webvtt/webvtt_file_magic_4.actual
@@ -0,0 +1,4 @@
+WEBVTT     with some extra text after multiple space characters
+
+00:00.000 --> 00:01.000
+Mock cue
\ No newline at end of file
diff --git a/lexers/testdata/webvtt/webvtt_file_magic_4.expected b/lexers/testdata/webvtt/webvtt_file_magic_4.expected
new file mode 100644
index 0000000..ab73def
--- /dev/null
+++ b/lexers/testdata/webvtt/webvtt_file_magic_4.expected
@@ -0,0 +1,10 @@
+[
+  {"type":"Keyword","value":"WEBVTT"},
+  {"type":"Text","value":"     with some extra text after multiple space characters\n\n"},
+  {"type":"LiteralDate","value":"00:00.000"},
+  {"type":"Text","value":" "},
+  {"type":"Operator","value":"--\u003e"},
+  {"type":"Text","value":" "},
+  {"type":"LiteralDate","value":"00:01.000"},
+  {"type":"Text","value":"\nMock cue"}
+]
diff --git a/lexers/testdata/webvtt/webvtt_file_magic_5.actual b/lexers/testdata/webvtt/webvtt_file_magic_5.actual
new file mode 100644
index 0000000..672f468
--- /dev/null
+++ b/lexers/testdata/webvtt/webvtt_file_magic_5.actual
@@ -0,0 +1,1 @@
+WEBVTT without two line terminators following
\ No newline at end of file
diff --git a/lexers/testdata/webvtt/webvtt_file_magic_5.expected b/lexers/testdata/webvtt/webvtt_file_magic_5.expected
new file mode 100644
index 0000000..864d676
--- /dev/null
+++ b/lexers/testdata/webvtt/webvtt_file_magic_5.expected
@@ -0,0 +1,3 @@
+[
+  {"type":"Error","value":"WEBVTT without two line terminators following"}
+]
diff --git a/lexers/testdata/webvtt/webvtt_file_magic_6.actual b/lexers/testdata/webvtt/webvtt_file_magic_6.actual
new file mode 100644
index 0000000..02798d7
--- /dev/null
+++ b/lexers/testdata/webvtt/webvtt_file_magic_6.actual
@@ -0,0 +1,8 @@
+WEBVTT with more than two line breaks following
+
+
+
+
+
+00:00.000 --> 00:01.000
+Mock cue
\ No newline at end of file
diff --git a/lexers/testdata/webvtt/webvtt_file_magic_6.expected b/lexers/testdata/webvtt/webvtt_file_magic_6.expected
new file mode 100644
index 0000000..a8eedea
--- /dev/null
+++ b/lexers/testdata/webvtt/webvtt_file_magic_6.expected
@@ -0,0 +1,10 @@
+[
+  {"type":"Keyword","value":"WEBVTT"},
+  {"type":"Text","value":" with more than two line breaks following\n\n\n\n\n\n"},
+  {"type":"LiteralDate","value":"00:00.000"},
+  {"type":"Text","value":" "},
+  {"type":"Operator","value":"--\u003e"},
+  {"type":"Text","value":" "},
+  {"type":"LiteralDate","value":"00:01.000"},
+  {"type":"Text","value":"\nMock cue"}
+]
diff --git a/lexers/testdata/webvtt/webvtt_invalid.actual b/lexers/testdata/webvtt/webvtt_invalid.actual
new file mode 100644
index 0000000..db9b97b
--- /dev/null
+++ b/lexers/testdata/webvtt/webvtt_invalid.actual
@@ -0,0 +1,128 @@
+WEBVTT Test file with invalid WebVTT code
+
+NOTE Comments may not contain the string "dash-dash-greater-than"
+-->
+
+NOTE The REGION keyword has to be on its own line
+
+REGION id:bill
+
+NOTE Some invalid region definition values
+
+REGION
+id:bill with spaces
+id:-->
+width:40
+lines:up
+regionanchor:100%
+regionanchor:100
+regionanchor:word
+regionanchor:100,100
+regionanchor:10%,10
+regionanchor:10,10%
+viewportanchor:100%
+viewportanchor:100
+viewportanchor:word
+viewportanchor:100,100
+viewportanchor:10%,10
+viewportanchor:10,10%
+scroll:down
+scroll: up
+scroll:100%
+
+NOTE Region definition values with invalid decimals
+
+REGION
+width:40,5%
+regionanchor:0,123%,50,123%
+viewportanchor:10,123%,90,123%
+
+NOTE Invalid region definition values with percentages above 100
+
+REGION
+width:140.5%
+regionanchor:123%,123%
+viewportanchor:123%,123%
+
+STYLE
+::cue(v[voice="Mary"]) { color: lime }
+/* A true blank line ends the style block. */
+
+::cue(v[voice="Mary"]) { color: lime } /* This should be lexed as invalid raw text. */
+
+NOTE The STYLE keyword needs to be on its own line.
+
+STYLE /* This is not allowed.*/
+
+NOTE Style blocks must not contain the string dash-dash-greater-than.
+
+STYLE
+-->
+
+NOTE Here are some invalid cues.
+
+IDs may not contain "-->"
+00:00.000 --> 00:01.000
+A cue
+
+IDs may not contain
+line breaks
+00:00.000 --> 00:01.000
+A cue
+
+00:60.000 --> 00:61.000
+A cue in the 61st second of the minute.
+
+60:00.000 --> 61:00.000
+A cue in the 61st minute of the hour second.
+
+00:01.000 -> 00:02.000
+A cue with an incorrect arrow.
+
+00:01.000 --> 00:02.000
+Incorrect cues don't prevent lexing of subsequent cues.
+
+00:01.00 --> 00:02.00
+A cue with incorrect milliseconds
+
+00:01.000 --> 00:02.000
+Disallowed characters in the classes.
+<c.cl	ass>Test</c>
+<i.cl ass>Test</i>
+<b.cl
+ass>Test</b>
+<u.cl&ass>Test</u>
+<u. class>Test</u>
+<u .class>Test</u>
+<i.cl<ass>Test</i>
+<i.cl.ass>Test</i>
+
+00:01.000 --> 00:02.000
+Disallowed characters in the annotations.
+<v Neil deGrasse&Tyson>Test
+<v Neil deGrasse>Tyson>Test
+
+00:01.000 --> 00:02.000
+A cue with an unknown span: <strong>Test</strong>.
+
+00:01.000 --> 00:02.000
+A cue with incorrect end tags:
+<i>Test< /i>.
+<i>Test</ i>.
+<i>Test</i >.
+
+00:01.000 --> 00:02.000
+A cue with annotations on annotation-less spans: <b.class annotation>Span content</b>
+
+00:01.000 --> 00:02.000
+A cue with missing annotations on spans that mandate annotations: <v.class>Span content</v>
+
+02:00.000 --> 02:05.000 vertical:rlr line:-33% position:line-right size:big align:end region: fred
+Invalid cue settings
+
+02:00.000 --> 02:05.000 line:133%,start position:133%,line-right size:133%
+Cue settings with percentages above 100.
+
+02:00.000 --> 02:05.000
+Cue settings with incorrect character references.
+&#8220character references&8220;.
diff --git a/lexers/testdata/webvtt/webvtt_invalid.expected b/lexers/testdata/webvtt/webvtt_invalid.expected
new file mode 100644
index 0000000..0009f24
--- /dev/null
+++ b/lexers/testdata/webvtt/webvtt_invalid.expected
@@ -0,0 +1,318 @@
+[
+  {"type":"Keyword","value":"WEBVTT"},
+  {"type":"Text","value":" Test file with invalid WebVTT code\n\n"},
+  {"type":"Error","value":"NOTE Comments may not contain the string \"dash-dash-greater-than\"\n--\u003e\n\n"},
+  {"type":"Comment","value":"NOTE The REGION keyword has to be on its own line\n\n"},
+  {"type":"Error","value":"REGION id:bill\n\n"},
+  {"type":"Comment","value":"NOTE Some invalid region definition values\n\n"},
+  {"type":"Keyword","value":"REGION"},
+  {"type":"Text","value":"\n"},
+  {"type":"Keyword","value":"id"},
+  {"type":"Punctuation","value":":"},
+  {"type":"Literal","value":"bill"},
+  {"type":"Text","value":" "},
+  {"type":"Error","value":"with"},
+  {"type":"Text","value":" "},
+  {"type":"Error","value":"spaces"},
+  {"type":"Text","value":"\n"},
+  {"type":"Error","value":"id:--\u003e"},
+  {"type":"Text","value":"\n"},
+  {"type":"Error","value":"width:40"},
+  {"type":"Text","value":"\n"},
+  {"type":"Error","value":"lines:up"},
+  {"type":"Text","value":"\n"},
+  {"type":"Error","value":"regionanchor:100%"},
+  {"type":"Text","value":"\n"},
+  {"type":"Error","value":"regionanchor:100"},
+  {"type":"Text","value":"\n"},
+  {"type":"Error","value":"regionanchor:word"},
+  {"type":"Text","value":"\n"},
+  {"type":"Error","value":"regionanchor:100,100"},
+  {"type":"Text","value":"\n"},
+  {"type":"Error","value":"regionanchor:10%,10"},
+  {"type":"Text","value":"\n"},
+  {"type":"Error","value":"regionanchor:10,10%"},
+  {"type":"Text","value":"\n"},
+  {"type":"Error","value":"viewportanchor:100%"},
+  {"type":"Text","value":"\n"},
+  {"type":"Error","value":"viewportanchor:100"},
+  {"type":"Text","value":"\n"},
+  {"type":"Error","value":"viewportanchor:word"},
+  {"type":"Text","value":"\n"},
+  {"type":"Error","value":"viewportanchor:100,100"},
+  {"type":"Text","value":"\n"},
+  {"type":"Error","value":"viewportanchor:10%,10"},
+  {"type":"Text","value":"\n"},
+  {"type":"Error","value":"viewportanchor:10,10%"},
+  {"type":"Text","value":"\n"},
+  {"type":"Error","value":"scroll:down"},
+  {"type":"Text","value":"\n"},
+  {"type":"Error","value":"scroll:"},
+  {"type":"Text","value":" "},
+  {"type":"Error","value":"up"},
+  {"type":"Text","value":"\n"},
+  {"type":"Error","value":"scroll:100%"},
+  {"type":"Text","value":"\n\n"},
+  {"type":"Comment","value":"NOTE Region definition values with invalid decimals\n\n"},
+  {"type":"Keyword","value":"REGION"},
+  {"type":"Text","value":"\n"},
+  {"type":"Error","value":"width:40,5%"},
+  {"type":"Text","value":"\n"},
+  {"type":"Error","value":"regionanchor:0,123%,50,123%"},
+  {"type":"Text","value":"\n"},
+  {"type":"Error","value":"viewportanchor:10,123%,90,123%"},
+  {"type":"Text","value":"\n\n"},
+  {"type":"Comment","value":"NOTE Invalid region definition values with percentages above 100\n\n"},
+  {"type":"Keyword","value":"REGION"},
+  {"type":"Text","value":"\n"},
+  {"type":"Error","value":"width:140.5%"},
+  {"type":"Text","value":"\n"},
+  {"type":"Error","value":"regionanchor:123%,123%"},
+  {"type":"Text","value":"\n"},
+  {"type":"Error","value":"viewportanchor:123%,123%"},
+  {"type":"Text","value":"\n\n"},
+  {"type":"Keyword","value":"STYLE"},
+  {"type":"Text","value":"\n"},
+  {"type":"Punctuation","value":"::"},
+  {"type":"NameDecorator","value":"cue"},
+  {"type":"Operator","value":"("},
+  {"type":"NameTag","value":"v"},
+  {"type":"Operator","value":"["},
+  {"type":"NameTag","value":"voice"},
+  {"type":"Operator","value":"="},
+  {"type":"LiteralStringDouble","value":"\"Mary\""},
+  {"type":"Operator","value":"])"},
+  {"type":"Text","value":" "},
+  {"type":"Punctuation","value":"{"},
+  {"type":"Text","value":" "},
+  {"type":"Keyword","value":"color"},
+  {"type":"Punctuation","value":":"},
+  {"type":"Text","value":" "},
+  {"type":"KeywordConstant","value":"lime"},
+  {"type":"Text","value":" "},
+  {"type":"Punctuation","value":"}"},
+  {"type":"Text","value":"\n"},
+  {"type":"Comment","value":"/* A true blank line ends the style block. */"},
+  {"type":"Text","value":"\n\n"},
+  {"type":"Error","value":"::cue(v[voice=\"Mary\"]) { color: lime } /* This should be lexed as invalid raw text. */\n\n"},
+  {"type":"Comment","value":"NOTE The STYLE keyword needs to be on its own line.\n\n"},
+  {"type":"Error","value":"STYLE /* This is not allowed.*/\n\n"},
+  {"type":"Comment","value":"NOTE Style blocks must not contain the string dash-dash-greater-than.\n\n"},
+  {"type":"Error","value":"STYLE\n--\u003e\n\n"},
+  {"type":"Comment","value":"NOTE Here are some invalid cues.\n\n"},
+  {"type":"Error","value":"IDs may not contain \"-"},
+  {"type":"Name","value":"-\u003e\""},
+  {"type":"Text","value":"\n"},
+  {"type":"LiteralDate","value":"00:00.000"},
+  {"type":"Text","value":" "},
+  {"type":"Operator","value":"--\u003e"},
+  {"type":"Text","value":" "},
+  {"type":"LiteralDate","value":"00:01.000"},
+  {"type":"Text","value":"\nA cue\n\n"},
+  {"type":"Error","value":"IDs may not contain\n"},
+  {"type":"Name","value":"line breaks"},
+  {"type":"Text","value":"\n"},
+  {"type":"LiteralDate","value":"00:00.000"},
+  {"type":"Text","value":" "},
+  {"type":"Operator","value":"--\u003e"},
+  {"type":"Text","value":" "},
+  {"type":"LiteralDate","value":"00:01.000"},
+  {"type":"Text","value":"\nA cue\n\n"},
+  {"type":"Error","value":"00:60.000 --\u003e 00:61.000\nA cue in the 61st second of the minute.\n\n60:00.000 --\u003e 61:00.000\nA cue in the 61st minute of the hour second.\n\n00:01.000 -\u003e 00:02.000\nA cue with an incorrect arrow.\n"},
+  {"type":"Text","value":"\n"},
+  {"type":"LiteralDate","value":"00:01.000"},
+  {"type":"Text","value":" "},
+  {"type":"Operator","value":"--\u003e"},
+  {"type":"Text","value":" "},
+  {"type":"LiteralDate","value":"00:02.000"},
+  {"type":"Text","value":"\nIncorrect cues don't prevent lexing of subsequent cues.\n\n"},
+  {"type":"Error","value":"00:01.00 --\u003e 00:02.00\nA cue with incorrect milliseconds\n"},
+  {"type":"Text","value":"\n"},
+  {"type":"LiteralDate","value":"00:01.000"},
+  {"type":"Text","value":" "},
+  {"type":"Operator","value":"--\u003e"},
+  {"type":"Text","value":" "},
+  {"type":"LiteralDate","value":"00:02.000"},
+  {"type":"Text","value":"\nDisallowed characters in the classes.\n"},
+  {"type":"Punctuation","value":"\u003c"},
+  {"type":"NameTag","value":"c"},
+  {"type":"Punctuation","value":"."},
+  {"type":"NameTag","value":"cl"},
+  {"type":"Error","value":"\tass"},
+  {"type":"Punctuation","value":"\u003e"},
+  {"type":"Text","value":"Test"},
+  {"type":"Punctuation","value":"\u003c/"},
+  {"type":"NameTag","value":"c"},
+  {"type":"Punctuation","value":"\u003e"},
+  {"type":"Text","value":"\n"},
+  {"type":"Punctuation","value":"\u003c"},
+  {"type":"NameTag","value":"i"},
+  {"type":"Punctuation","value":"."},
+  {"type":"NameTag","value":"cl"},
+  {"type":"Error","value":" ass"},
+  {"type":"Punctuation","value":"\u003e"},
+  {"type":"Text","value":"Test"},
+  {"type":"Punctuation","value":"\u003c/"},
+  {"type":"NameTag","value":"i"},
+  {"type":"Punctuation","value":"\u003e"},
+  {"type":"Text","value":"\n"},
+  {"type":"Punctuation","value":"\u003c"},
+  {"type":"NameTag","value":"b"},
+  {"type":"Punctuation","value":"."},
+  {"type":"NameTag","value":"cl"},
+  {"type":"Error","value":"\nass\u003eTest\u003c/b\u003e\n\u003cu.cl\u0026ass\u003eTest\u003c/u\u003e\n\u003cu. class\u003eTest\u003c/u\u003e\n\u003cu .class\u003eTest\u003c/u\u003e\n\u003ci.cl\u003cass\u003eTest\u003c/i\u003e\n\u003ci.cl.ass\u003eTest\u003c/i\u003e\n"},
+  {"type":"Text","value":"\n"},
+  {"type":"LiteralDate","value":"00:01.000"},
+  {"type":"Text","value":" "},
+  {"type":"Operator","value":"--\u003e"},
+  {"type":"Text","value":" "},
+  {"type":"LiteralDate","value":"00:02.000"},
+  {"type":"Text","value":"\nDisallowed characters in the annotations.\n"},
+  {"type":"Punctuation","value":"\u003c"},
+  {"type":"NameTag","value":"v"},
+  {"type":"Text","value":" Neil deGrasse"},
+  {"type":"Error","value":"\u0026Tyson"},
+  {"type":"Punctuation","value":"\u003e"},
+  {"type":"Text","value":"Test\n"},
+  {"type":"Punctuation","value":"\u003c"},
+  {"type":"NameTag","value":"v"},
+  {"type":"Text","value":" Neil deGrasse"},
+  {"type":"Punctuation","value":"\u003e"},
+  {"type":"Text","value":"Tyson\u003eTest\n\n"},
+  {"type":"LiteralDate","value":"00:01.000"},
+  {"type":"Text","value":" "},
+  {"type":"Operator","value":"--\u003e"},
+  {"type":"Text","value":" "},
+  {"type":"LiteralDate","value":"00:02.000"},
+  {"type":"Text","value":"\nA cue with an unknown span: "},
+  {"type":"Error","value":"\u003cstrong"},
+  {"type":"Punctuation","value":"\u003e"},
+  {"type":"Text","value":"Test"},
+  {"type":"Error","value":"\u003c/strong"},
+  {"type":"Punctuation","value":"\u003e"},
+  {"type":"Text","value":".\n\n"},
+  {"type":"LiteralDate","value":"00:01.000"},
+  {"type":"Text","value":" "},
+  {"type":"Operator","value":"--\u003e"},
+  {"type":"Text","value":" "},
+  {"type":"LiteralDate","value":"00:02.000"},
+  {"type":"Text","value":"\nA cue with incorrect end tags:\n"},
+  {"type":"Punctuation","value":"\u003c"},
+  {"type":"NameTag","value":"i"},
+  {"type":"Punctuation","value":"\u003e"},
+  {"type":"Text","value":"Test"},
+  {"type":"Error","value":"\u003c /i"},
+  {"type":"Punctuation","value":"\u003e"},
+  {"type":"Text","value":".\n"},
+  {"type":"Punctuation","value":"\u003c"},
+  {"type":"NameTag","value":"i"},
+  {"type":"Punctuation","value":"\u003e"},
+  {"type":"Text","value":"Test"},
+  {"type":"Error","value":"\u003c/ i"},
+  {"type":"Punctuation","value":"\u003e"},
+  {"type":"Text","value":".\n"},
+  {"type":"Punctuation","value":"\u003c"},
+  {"type":"NameTag","value":"i"},
+  {"type":"Punctuation","value":"\u003e"},
+  {"type":"Text","value":"Test"},
+  {"type":"Punctuation","value":"\u003c/"},
+  {"type":"NameTag","value":"i"},
+  {"type":"Error","value":" "},
+  {"type":"Punctuation","value":"\u003e"},
+  {"type":"Text","value":".\n\n"},
+  {"type":"LiteralDate","value":"00:01.000"},
+  {"type":"Text","value":" "},
+  {"type":"Operator","value":"--\u003e"},
+  {"type":"Text","value":" "},
+  {"type":"LiteralDate","value":"00:02.000"},
+  {"type":"Text","value":"\nA cue with annotations on annotation-less spans: "},
+  {"type":"Punctuation","value":"\u003c"},
+  {"type":"NameTag","value":"b"},
+  {"type":"Punctuation","value":"."},
+  {"type":"NameTag","value":"class"},
+  {"type":"Error","value":" annotation"},
+  {"type":"Punctuation","value":"\u003e"},
+  {"type":"Text","value":"Span content"},
+  {"type":"Punctuation","value":"\u003c/"},
+  {"type":"NameTag","value":"b"},
+  {"type":"Punctuation","value":"\u003e"},
+  {"type":"Text","value":"\n\n"},
+  {"type":"LiteralDate","value":"00:01.000"},
+  {"type":"Text","value":" "},
+  {"type":"Operator","value":"--\u003e"},
+  {"type":"Text","value":" "},
+  {"type":"LiteralDate","value":"00:02.000"},
+  {"type":"Text","value":"\nA cue with missing annotations on spans that mandate annotations: "},
+  {"type":"Punctuation","value":"\u003c"},
+  {"type":"NameTag","value":"v"},
+  {"type":"Punctuation","value":"."},
+  {"type":"NameTag","value":"class"},
+  {"type":"Error","value":"\u003eSpan"},
+  {"type":"Text","value":" content\u003c/v"},
+  {"type":"Punctuation","value":"\u003e"},
+  {"type":"Text","value":"\n\n"},
+  {"type":"LiteralDate","value":"02:00.000"},
+  {"type":"Text","value":" "},
+  {"type":"Operator","value":"--\u003e"},
+  {"type":"Text","value":" "},
+  {"type":"LiteralDate","value":"02:05.000"},
+  {"type":"Text","value":" "},
+  {"type":"Keyword","value":"vertical"},
+  {"type":"Punctuation","value":":"},
+  {"type":"KeywordConstant","value":"rl"},
+  {"type":"Error","value":"r"},
+  {"type":"Text","value":" "},
+  {"type":"Keyword","value":"line"},
+  {"type":"Punctuation","value":":"},
+  {"type":"Literal","value":"-33"},
+  {"type":"Error","value":"%"},
+  {"type":"Text","value":" "},
+  {"type":"Keyword","value":"position"},
+  {"type":"Punctuation","value":":"},
+  {"type":"Keyword","value":"line"},
+  {"type":"Error","value":"-right"},
+  {"type":"Text","value":" "},
+  {"type":"Keyword","value":"size"},
+  {"type":"Punctuation","value":":"},
+  {"type":"Error","value":"big"},
+  {"type":"Text","value":" "},
+  {"type":"Keyword","value":"align"},
+  {"type":"Punctuation","value":":"},
+  {"type":"KeywordConstant","value":"end"},
+  {"type":"Text","value":" "},
+  {"type":"Keyword","value":"region"},
+  {"type":"Punctuation","value":":"},
+  {"type":"Text","value":" "},
+  {"type":"Error","value":"fred"},
+  {"type":"Text","value":"\nInvalid cue settings\n\n"},
+  {"type":"LiteralDate","value":"02:00.000"},
+  {"type":"Text","value":" "},
+  {"type":"Operator","value":"--\u003e"},
+  {"type":"Text","value":" "},
+  {"type":"LiteralDate","value":"02:05.000"},
+  {"type":"Text","value":" "},
+  {"type":"Keyword","value":"line"},
+  {"type":"Punctuation","value":":"},
+  {"type":"Literal","value":"133"},
+  {"type":"Error","value":"%,start"},
+  {"type":"Text","value":" "},
+  {"type":"Keyword","value":"position"},
+  {"type":"Punctuation","value":":"},
+  {"type":"Literal","value":"133"},
+  {"type":"Error","value":"%,"},
+  {"type":"Keyword","value":"line"},
+  {"type":"Error","value":"-right"},
+  {"type":"Text","value":" "},
+  {"type":"Keyword","value":"size"},
+  {"type":"Punctuation","value":":"},
+  {"type":"Error","value":"133%"},
+  {"type":"Text","value":"\nCue settings with percentages above 100.\n\n"},
+  {"type":"LiteralDate","value":"02:00.000"},
+  {"type":"Text","value":" "},
+  {"type":"Operator","value":"--\u003e"},
+  {"type":"Text","value":" "},
+  {"type":"LiteralDate","value":"02:05.000"},
+  {"type":"Text","value":"\nCue settings with incorrect character references.\n"},
+  {"type":"Error","value":"\u0026"},
+  {"type":"Text","value":"#8220character references\u00268220;.\n"}
+]