Add raw text elements and doctypes

Co-Authored-By: Ashi Krishnan <queerviolet@github.com>
This commit is contained in:
Max Brunsfeld 2018-06-11 16:56:33 -07:00
parent d9a504136e
commit 21b5fad6f0
6 changed files with 1156 additions and 465 deletions

View File

@ -87,3 +87,50 @@ Comments
(start_tag)
(comment)
(end_tag)))
==================================
Raw text elements
==================================
<script>
</s
</sc
</scr
</scri
</scrip
</script>
<style>
</ </s </st </sty </styl
</style>
---
(fragment
(raw_element
(start_tag)
(end_tag))
(raw_element
(start_tag)
(end_tag))
(text))
==================================
All-caps doctype
==================================
<!DOCTYPE html PUBLIC
"-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
---
(fragment
(doctype))
==================================
Lowercase doctype
==================================
<!doctype html>
---
(fragment
(doctype))

View File

@ -8,21 +8,32 @@ module.exports = grammar({
externals: $ => [
$._open_start_tag,
$._open_raw_start_tag,
$._close_start_tag,
$._self_close_start_tag,
$.end_tag,
$._implicit_end_tag,
$._erroneous_end_tag,
$._raw_text,
$.comment,
],
rules: {
fragment: $ => repeat($._node),
doctype: $ => seq(
'<!',
/[Dd][Oo][Cc][Tt][Yy][Pp][Ee]/,
/[^>]+/,
'>'
),
_node: $ => choice(
$.doctype,
$.text,
$._erroneous_end_tag,
$.element
$.element,
$.raw_element
),
element: $ => choice(
@ -34,12 +45,24 @@ module.exports = grammar({
$.self_closing_tag
),
raw_element: $ => seq(
alias($._raw_start_tag, $.start_tag),
optional($._raw_text),
$.end_tag
),
start_tag: $ => seq(
$._open_start_tag,
repeat($.attribute),
$._close_start_tag
),
_raw_start_tag: $ => seq(
$._open_raw_start_tag,
repeat($.attribute),
$._close_start_tag
),
self_closing_tag: $ => seq(
$._open_start_tag,
repeat($.attribute),

87
src/grammar.json vendored
View File

@ -8,9 +8,34 @@
"name": "_node"
}
},
"doctype": {
"type": "SEQ",
"members": [
{
"type": "STRING",
"value": "<!"
},
{
"type": "PATTERN",
"value": "[Dd][Oo][Cc][Tt][Yy][Pp][Ee]"
},
{
"type": "PATTERN",
"value": "[^>]+"
},
{
"type": "STRING",
"value": ">"
}
]
},
"_node": {
"type": "CHOICE",
"members": [
{
"type": "SYMBOL",
"name": "doctype"
},
{
"type": "SYMBOL",
"name": "text"
@ -22,6 +47,10 @@
{
"type": "SYMBOL",
"name": "element"
},
{
"type": "SYMBOL",
"name": "raw_element"
}
]
},
@ -63,6 +92,36 @@
}
]
},
"raw_element": {
"type": "SEQ",
"members": [
{
"type": "ALIAS",
"content": {
"type": "SYMBOL",
"name": "_raw_start_tag"
},
"named": true,
"value": "start_tag"
},
{
"type": "CHOICE",
"members": [
{
"type": "SYMBOL",
"name": "_raw_text"
},
{
"type": "BLANK"
}
]
},
{
"type": "SYMBOL",
"name": "end_tag"
}
]
},
"start_tag": {
"type": "SEQ",
"members": [
@ -83,6 +142,26 @@
}
]
},
"_raw_start_tag": {
"type": "SEQ",
"members": [
{
"type": "SYMBOL",
"name": "_open_raw_start_tag"
},
{
"type": "REPEAT",
"content": {
"type": "SYMBOL",
"name": "attribute"
}
},
{
"type": "SYMBOL",
"name": "_close_start_tag"
}
]
},
"self_closing_tag": {
"type": "SEQ",
"members": [
@ -236,6 +315,10 @@
"type": "SYMBOL",
"name": "_open_start_tag"
},
{
"type": "SYMBOL",
"name": "_open_raw_start_tag"
},
{
"type": "SYMBOL",
"name": "_close_start_tag"
@ -256,6 +339,10 @@
"type": "SYMBOL",
"name": "_erroneous_end_tag"
},
{
"type": "SYMBOL",
"name": "_raw_text"
},
{
"type": "SYMBOL",
"name": "comment"

1422
src/parser.c vendored

File diff suppressed because it is too large Load Diff

36
src/scanner.cc vendored
View File

@ -3,7 +3,6 @@
#include <vector>
#include <string>
#include <locale>
#include "tag.h"
namespace {
@ -13,12 +12,14 @@ using std::string;
enum TokenType {
OPEN_START_TAG,
OPEN_RAW_START_TAG,
CLOSE_START_TAG,
SELF_CLOSE_START_TAG,
END_TAG,
IMPLICIT_END_TAG,
ERRONEOUS_END_TAG,
COMMENT,
RAW_TEXT,
COMMENT
};
struct Scanner {
@ -97,6 +98,31 @@ struct Scanner {
return false;
}
bool raw_text(TSLexer *lexer) {
if (!tags.size()) return false;
lexer->mark_end(lexer);
const string &end_delimiter = tags.back().type == SCRIPT
? "</script"
: "</style";
unsigned delimiter_index = 0;
while (lexer->lookahead) {
if (lexer->lookahead == end_delimiter[delimiter_index]) {
delimiter_index++;
if (delimiter_index == end_delimiter.size()) break;
} else {
delimiter_index = 0;
lexer->mark_end(lexer);
}
lexer->advance(lexer, false);
}
lexer->result_symbol = RAW_TEXT;
return true;
}
bool start_tag(TSLexer *lexer) {
if (!tags.empty() && tags.back().is_void()) {
tags.pop_back();
@ -111,7 +137,7 @@ struct Scanner {
tags.push_back(tag);
lexer->mark_end(lexer);
lexer->result_symbol = OPEN_START_TAG;
lexer->result_symbol = tag.is_raw() ? OPEN_RAW_START_TAG : OPEN_START_TAG;
return true;
}
@ -150,6 +176,10 @@ struct Scanner {
lexer->advance(lexer, true);
}
if (valid_symbols[RAW_TEXT] && !valid_symbols[OPEN_START_TAG] && !valid_symbols[CLOSE_START_TAG]) {
return raw_text(lexer);
}
switch (lexer->lookahead) {
case '<':
lexer->mark_end(lexer);

4
src/tag.h vendored
View File

@ -279,6 +279,10 @@ struct Tag {
return type < END_OF_VOID_TAGS;
}
inline bool is_raw() const {
return type == SCRIPT || type == STYLE;
}
// string name() const {
// return type == TagType::CUSTOM
// ? custom_tag_name