Initial commit

This commit is contained in:
g1n 2022-03-09 21:37:45 +02:00
commit 4df92c74ad
Signed by: g1n
GPG Key ID: 8D352193D65D4E2C
25 changed files with 1634 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
build

19
README.org Normal file
View File

@ -0,0 +1,19 @@
#+TITLE: LibWeb - library, that implements Web standarts
** What standarts are implementing?
- HTML
** What can this library do now?
LibWeb can tokenize and parse HTML to DOM tree, that you can use in your applications.
** How to build?
- `cd src`
- `make`
If you want to also build utilities:
- `make utils`
After this `libweb.a` will be in `builds` directory and utils in `build/utils`

22
src/DOM/character_data.c Normal file
View File

@ -0,0 +1,22 @@
#include <LibWeb/DOM/character_data.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
CharacterData append_data(CharacterData character_data, DOMString data) {
// FIXME: implement in spec-compliant way
size_t len = strlen(character_data.data);
size_t data_len;
if (sizeof(data) > 1) {
data_len = strlen(data);
} else {
data_len = 1;
}
character_data.data = realloc(character_data.data, len+data_len+1);
strcpy(character_data.data+len, data);
return character_data;
}

29
src/DOM/document.c Normal file
View File

@ -0,0 +1,29 @@
#include <LibWeb/DOM/document.h>
#include <LibWeb/DOM/element.h>
#include <LibWeb/HTML/token.h>
#include <ctype.h>
#include <string.h>
#include <stddef.h>
Element create_element(Document document, DOMString local_name) { // FIXME: options
Element element;
// TODO: If localName does not match the Name production, then throw an "InvalidCharacterError" DOMException.
// TODO: If this is an HTML document, then set localName to localName in ASCII lowercase.
if (document.content_type == NULL || strcmp(document.content_type, "xml")) {
for (size_t i = 0; i < strlen(local_name); i++)
local_name[i] = tolower(local_name[i]);
element.local_name = local_name;
}
// TODO: Let is be null.
// TODO: If options is a dictionary and options["is"] exists, then set is to it.
// TODO: Let namespace be the HTML namespace, if this is an HTML document or thiss content type is "application/xhtml+xml"; otherwise null.
// TODO: Return the result of creating an element given this, localName, namespace, null, is, and with the synchronous custom elements flag set.
return element;
}

138
src/DOM/node.c Normal file
View File

@ -0,0 +1,138 @@
#include <LibWeb/DOM/node.h>
#include <LibWeb/HTML/stack.h>
#include <stddef.h>
#include <stdlib.h>
bool is_element_node(Node *node) {
return node->node_type == ELEMENT_NODE;
}
bool is_attribute_node(Node *node) {
return node->node_type == ATTRIBUTE_NODE;
}
bool is_text_node(Node *node) {
return node->node_type == TEXT_NODE;
}
bool is_cdata_section_node(Node *node) {
return node->node_type == CDATA_SECTION_NODE;
}
bool is_processing_instruction_node(Node *node) {
return node->node_type == PROCESSING_INSTRUCTION_NODE;
}
bool is_comment_node(Node *node) {
return node->node_type == COMMENT_NODE;
}
bool is_document_node(Node *node) {
return node->node_type == DOCUMENT_NODE;
}
bool is_document_type_node(Node *node) {
return node->node_type == DOCUMENT_TYPE_NODE;
}
bool is_document_fragment_node(Node *node) {
return node->node_type == DOCUMENT_FRAGMENT_NODE;
}
Node* init_document_node() {
Node *document_node = malloc(sizeof(Node));
document_node->node_type = DOCUMENT_NODE;
// FIXME: node name
document_node->parent_node = NULL;
document_node->first_child = document_node->last_child = document_node->previous_sibling = document_node->next_sibling = NULL;
return document_node;
}
Element create_element_for_token(Node* intended_parent, token_t token) {
// TODO: reimplement - https://html.spec.whatwg.org/multipage/parsing.html#create-an-element-for-the-token
Document document = intended_parent->owner_document;
document.content_type = "html"; // FIXME
DOMString local_name = tag_name(token);
Element element = create_element(document, local_name);
return element;
}
Node* make_node(node_type_t node_type, token_t token) {
Node *tmp = malloc(sizeof(Node));
tmp->node_type = node_type;
tmp->parent_node = NULL;
tmp->first_child = tmp->last_child = tmp->previous_sibling = tmp->next_sibling = NULL;
Document tmp_document;
switch (node_type) {
case ELEMENT_NODE:
tmp_document.document_element = create_element_for_token(tmp, token);
break;
case ATTRIBUTE_NODE:
case TEXT_NODE:
case CDATA_SECTION_NODE:
case ENTITY_REFERENCE_NODE:
case ENTITY_NODE:
case PROCESSING_INSTRUCTION_NODE:
case COMMENT_NODE:
case DOCUMENT_NODE:
break;
case DOCUMENT_TYPE_NODE:
tmp_document.doctype.name = token.doctype.name;
tmp_document.doctype.public_id = token.doctype.public_identifier;
tmp_document.doctype.system_id = token.doctype.system_identifier;
case DOCUMENT_FRAGMENT_NODE:
case NOTATION_NODE:
break;
}
tmp->owner_document = tmp_document;
return tmp;
}
Node* make_text_node(Text text) {
Node *tmp = malloc(sizeof(Node));
tmp->node_type = TEXT_NODE;
tmp->parent_node = NULL;
tmp->first_child = tmp->last_child = tmp->previous_sibling = tmp->next_sibling = NULL;
Document tmp_document;
tmp_document.text = text;
tmp->owner_document = tmp_document;
return tmp;
}
Node* append_node(Node *parent, Node *node) {
if (node->parent_node) {
return node;
}
if (parent->last_child) {
parent->last_child->next_sibling = node;
node->previous_sibling = parent->last_child;
}
node->parent_node = parent;
parent->last_child = node;
if (!parent->first_child)
parent->first_child = parent->last_child;
return node;
}
// TODO: implement insert, pre_insert and append_child in spec compliant way
Node *append_child(Node *parent, Node *node) {
return append_node(parent, node);
}

14
src/DOM/text.c Normal file
View File

@ -0,0 +1,14 @@
#include <LibWeb/DOM/text.h>
#include <stdlib.h>
#include <string.h>
Text text_constructor(Text text, DOMString data) {
text.data.data = malloc(strlen(data)+1);
strcpy(text.data.data, data);
return text;
}
Text text_append(Text text, DOMString data) {
text.data = append_data(text.data, data);
return text;
}

494
src/HTML/parser.c Normal file
View File

@ -0,0 +1,494 @@
#include <LibWeb/HTML/parser.h>
#include <LibWeb/HTML/token.h>
#include <LibWeb/HTML/tokenizer.h>
#include <LibWeb/HTML/stack.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define tag_name_is(name) \
!strcmp(tag_name(token), name)
Node *document_node;
Node *current_node;
Node *head_element = NULL;
Node *form_element = NULL;
insertion_mode_t insertion_mode = INITIAL_STATE;
bool stop_parsing = false; // TODO: implement function for end parsing
bool frameset_ok = true;
void set_insertion_mode_state(insertion_mode_t mode) {
insertion_mode = mode;
}
void dump_tree(Node *root, size_t spaces) {
if (root == NULL)
return;
for (size_t i = 0; i < spaces; i++)
putchar(' ');
switch(root->node_type) {
case ELEMENT_NODE:
printf("%s", root->owner_document.document_element.local_name);
break;
case ATTRIBUTE_NODE:
break;
case TEXT_NODE:
printf("#text: %s", root->owner_document.text.data.data);
break;
case CDATA_SECTION_NODE:
break;
case ENTITY_REFERENCE_NODE:
break;
case ENTITY_NODE:
break;
case PROCESSING_INSTRUCTION_NODE:
break;
case COMMENT_NODE:
break;
case DOCUMENT_NODE:
printf("Document");
break;
case DOCUMENT_TYPE_NODE:
printf("DOCTYPE: %s", root->owner_document.doctype.name);
break;
case DOCUMENT_FRAGMENT_NODE:
break;
case NOTATION_NODE:
break;
}
putchar('\n');
dump_tree(root->first_child, spaces+1);
dump_tree(root->next_sibling, spaces);
}
// https://html.spec.whatwg.org/multipage/parsing.html#appropriate-place-for-inserting-a-node
Node *find_appropriate_place_for_inserting_node(Node *node) { // TODO: optional
// TODO: If there was an override target specified, then let target be the override target.
Node *target = node;
Node *adjusted_insertion_location;
// TODO: If foster parenting is enabled and target is a table, tbody, tfoot, thead, or tr element
// FIXME
if (target->last_child == NULL)
adjusted_insertion_location = target;
else
adjusted_insertion_location = target->last_child;
// TODO: If the adjusted insertion location is inside a template element, let it instead be inside the template element's template contents, after its last child (if any).
return adjusted_insertion_location;
}
Node *insert_element(Node *node, token_t token) {
Node *adjusted_insertion_location = find_appropriate_place_for_inserting_node(node);
Node *element = make_node(ELEMENT_NODE, token); // FIXME
// FIXME: If the parser was not created as part of the HTML fragment parsing algorithm, then push a new element queue onto element's relevant agent's custom element reactions stack.
append_child(adjusted_insertion_location->parent_node, element);
push(element);
return element;
}
void insert_character(Node *node, token_t token) {
char data[1];
data[0] = character_data(token);
Node *adjusted_insertion_location = find_appropriate_place_for_inserting_node(node);
// FIXME
/*if (adjusted_insertion_location->parent_node->node_type == DOCUMENT_NODE) {
return;
}*/
if (adjusted_insertion_location->node_type == TEXT_NODE) { // FIXME
adjusted_insertion_location->owner_document.text \
= text_append(adjusted_insertion_location->owner_document.text, data);
return;
}
Text text;
text = text_constructor(text, data);
Node *text_node = make_text_node(text);
append_child(adjusted_insertion_location, text_node);
}
bool is_in_list(char *list[], char *string, size_t len) {
for (size_t i = 0; i < len; i++) {
if (!strcmp(list[i], string))
return true;
}
return false;
}
void generate_implied_end_tags() {
Node *node = last_node_on_stack();
char *elements[] = { "dd", "dt", "li", "optgroup", "p", "rb", "rp", "rtc" };
while (1) {
if (!is_in_list(elements, node->owner_document.document_element.local_name, 8)) {
break;
}
node = pop();
}
current_node = last_node_on_stack();
}
void close_p_element() {
// TODO: generate_implied_end_tags("p");
// TODO: If the current node is not a p element, then this is a parse error.
// TODO: Pop elements from the stack of open elements until a p element has been popped from the stack.
Node *node = last_node_on_stack();
if (strcmp(node->owner_document.document_element.local_name, "p"))
return; // Parse error.
while (1) {
if (!strcmp(node->owner_document.document_element.local_name, "p")) {
break;
}
node = pop();
}
current_node = last_node_on_stack();
}
int process_token_using(insertion_mode_t insertion_mode, token_t token);
void handle_initial(token_t token) {
if (is_character(token)) {
return;
} else if (is_comment(token)) {
// Insert a comment as the last child of the Document object.
return;
} else if (is_doctype(token)) {
/*if ((strlen(token.doctype.name) == 0 || strcmp(token.doctype.name, "html") != 0) ||
(strlen(token.doctype.public_identifier) != 0) ||
(strlen(token.doctype.system_identifier) != 0 && strcmp(token.doctype.system_identifier, "about:legacy-compat") != 0)) {
// Parse error
//return;
// FIXME: segfault
}*/
Node *node = make_node(DOCUMENT_TYPE_NODE, token);
append_child(current_node, node);
// TODO: Then, if the document is not an iframe srcdoc document, and the parser cannot change the mode flag is false, and the DOCTYPE token matches one of the conditions in the following list, then set the Document to quirks mode.
// TODO: Otherwise, if the document is not an iframe srcdoc document, and the parser cannot change the mode flag is false, and the DOCTYPE token matches one of the conditions in the following list, then then set the Document to limited-quirks mode.
// TODO: The system identifier and public identifier strings must be compared to the values given in the lists above in an ASCII case-insensitive manner. A system identifier whose value is the empty string is not considered missing for the purposes of the conditions above.
set_insertion_mode_state(BEFORE_HTML_STATE);
} else {
}
}
void handle_before_html(token_t token) {
if (is_doctype(token)) {
// Parse error
return;
} else if (is_comment(token)) {
// TODO: Insert a comment as the last child of the Document object.
return;
} else if (is_character(token) && isspace(character_data(token))) {
return;
} else if (is_start_tag(token) && tag_name_is("html")) {
// Create an element for the token in the HTML namespace, with the Document as the intended parent.
Node *html_node = make_node(ELEMENT_NODE, token);
// Append it to the Document object.
append_child(document_node, html_node);
current_node = html_node;
push(html_node);
set_insertion_mode_state(BEFORE_HEAD_STATE);
return;
} else if (is_end_tag(token) && \
is_in_list((char*[]){"head", "body", "html", "br"}, tag_name(token), 4)) {
goto BEFORE_HTML_ANYTHING_ELSE;
} else if (is_end_tag(token)) {
// Parse error
return;
} else {
BEFORE_HTML_ANYTHING_ELSE:
token_t html_token;
html_token.tag.name = "html";
Node *html_node = make_node(ELEMENT_NODE, html_token);
append_child(document_node, html_node);
current_node = html_node;
push(html_node);
set_insertion_mode_state(BEFORE_HEAD_STATE);
// FIXME: reprocess the token.
return;
}
}
void handle_before_head(token_t token) {
if (is_character(token) && isspace(character_data(token))) {
return;
} else if (is_comment(token)) {
// Insert a comment.
return;
} else if (is_doctype(token)) {
// Parse error.
return;
} else if (is_start_tag(token) && tag_name_is("html")) {
process_token_using(IN_BODY_STATE, token);
} else if (is_start_tag(token) && tag_name_is("head")) {
Node *head_node = insert_element(current_node, token);
current_node = head_node;
head_element = head_node;
set_insertion_mode_state(IN_HEAD_STATE);
return;
} else if (is_end_tag(token) &&
is_in_list((char*[]){"head", "body", "html", "br"}, tag_name(token), 4)) {
goto BEFORE_HEAD_ANYTHING_ELSE;
} else if (is_end_tag(token)) {
// Parse error.
return;
} else {
BEFORE_HEAD_ANYTHING_ELSE:
token_t head_token;
head_token.tag.name = "head";
Node *head_node = insert_element(current_node, head_token);
current_node = head_node;
head_element = head_node;
set_insertion_mode_state(IN_HEAD_STATE);
// FIXME: Reprocess the current token.
return;
}
}
void handle_in_head(token_t token) {
if (is_character(token) && isspace(character_data(token))) {
insert_character(current_node, token);
return;
} else if (is_comment(token)) {
// TODO: Insert a comment
return;
} else if (is_doctype(token)) {
// Parse error.
return;
} else if (is_start_tag(token) && tag_name_is("html")) {
process_token_using(IN_BODY_STATE, token);
} else if (is_end_tag(token) && tag_name_is("head")) {
pop();
set_insertion_mode_state(AFTER_HEAD_STATE);
return;
}
}
void handle_after_head(token_t token) {
if (is_character(token) && isspace(character_data(token))) {
insert_character(current_node, token);
return;
} else if (is_comment(token)) {
// TODO: Insert a comment
return;
} else if (is_doctype(token)) {
// Parse error.
return;
} else if (is_start_tag(token) && tag_name_is("html")) {
process_token_using(IN_BODY_STATE, token);
} else if (is_start_tag(token) && tag_name_is("body")) {
Node *node = insert_element(current_node, token);
current_node = node;
// TODO: Set the Document's awaiting parser-inserted body flag to false.
frameset_ok = false;
set_insertion_mode_state(IN_BODY_STATE);
return;
}
}
void handle_in_body(token_t token) {
if (is_character(token) && character_data(token) == '\0') {
// Parse error.
return;
} else if (is_character(token) && isspace(character_data(token))) {
// TODO: Reconstruct the active formatting elements, if any.
insert_character(current_node, token);
return;
} else if (is_character(token)) { // FIXME
// TODO: Reconstruct the active formatting elements, if any.
insert_character(current_node, token);
frameset_ok = false;
return;
} else if (is_comment(token)) {
// TODO: Insert a comment.
return;
} else if (is_doctype(token)) {
// Parse error.
return;
} else if (is_start_tag(token) &&
is_in_list((char*[]) {"address", \
"article", "aside", "blockquote", \
"center", "details", "dialog", "dir", \
"div", "dl", "fieldset", "figcaption", \
"figure", "footer", "header", "hgroup", \
"main", "menu", "nav", "ol", "p", \
"section", "summary", "ul"}, tag_name(token), 24)) {
// TODO: If the stack of open elements has a p element in button scope, then close a p element.
Node *node = insert_element(current_node, token);
current_node = node;
return;
} else if (is_start_tag(token) &&
is_in_list((char*[]) {"h1", "h2", "h3", "h4", "h5", "h6"}, tag_name(token), 6)) {
// TODO: If the stack of open elements has a p element in button scope, then close a p element.
Node *node = last_node_on_stack();
if (is_in_list((char*[]) {"h1", "h2", "h3", "h4", "h5", "h6"}, node->owner_document.document_element.local_name, 6)) {
current_node = pop();
return; // Parse error.
}
node = insert_element(current_node, token);
current_node = node;
return;
} else if (is_end_tag(token) && tag_name_is("p")) {
// TODO: If the stack of open elements does not have a p element in button scope, then this is a parse error; insert an HTML element for a "p" start tag token with no attributes.
close_p_element();
return;
} else if (is_end_tag(token) &&
is_in_list((char*[]) {"h1", "h2", "h3", "h4", "h5", "h6"}, tag_name(token), 6)) {
// TODO: If the stack of open elements does not have an element in scope that is an HTML element and whose tag name is one of "h1", "h2", "h3", "h4", "h5", or "h6", then this is a parse error; ignore the token.
generate_implied_end_tags();
Node *node = last_node_on_stack();
if (strcmp(node->owner_document.document_element.local_name, token.tag.name))
return; // Parse error.
while (1) {
if (!strcmp(node->owner_document.document_element.local_name, token.tag.name)) {
break;
}
node = pop();
}
} else if (is_end_tag(token) && tag_name_is("body")) {
// TODO: If the stack of open elements does not have a body element in scope, this is a parse error; ignore the token.
// TODO: Otherwise, if there is a node in the stack of open elements that is not either a dd element, a dt element, an li element, an optgroup element, an option element, a p element, an rb element, an rp element, an rt element, an rtc element, a tbody element, a td element, a tfoot element, a th element, a thead element, a tr element, the body element, or the html element, then this is a parse error.
set_insertion_mode_state(AFTER_BODY_STATE);
return;
}
}
void handle_after_body(token_t token) {
if (is_character(token) && isspace(character_data(token))) {
process_token_using(IN_BODY_STATE, token);
} else if (is_end_tag(token) && tag_name_is("html")) {
// TODO: If the parser was created as part of the HTML fragment parsing algorithm, this is a parse error; ignore the token. (fragment case)
set_insertion_mode_state(AFTER_AFTER_BODY_STATE);
return;
} else if (is_eof(token)) {
stop_parsing = true;
return;
}
}
void handle_after_after_body(token_t token) {
if (is_eof(token)) {
stop_parsing = true;
return;
}
}
int process_token_using(insertion_mode_t insertion_mode, token_t token) {
switch (insertion_mode) {
case INITIAL_STATE:
handle_initial(token);
break;
case BEFORE_HTML_STATE:
handle_before_html(token);
break;
case BEFORE_HEAD_STATE:
handle_before_head(token);
break;
case IN_HEAD_STATE:
handle_in_head(token);
break;
case IN_HEAD_NOSCRIPT_STATE:
break;
case AFTER_HEAD_STATE:
handle_after_head(token);
break;
case IN_BODY_STATE:
handle_in_body(token);
break;
case TEXT_STATE:
case IN_TABLE_STATE:
case IN_TABLE_TEXT_STATE:
case IN_CAPTION_STATE:
case IN_COLUMN_GROUP_STATE:
case IN_TABLE_BODY_STATE:
case IN_ROW_STATE:
case IN_CELL_STATE:
case IN_SELECT_STATE:
case IN_SELECT_IN_TABLE_STATE:
case IN_TEMPLATE_STATE:
break;
case AFTER_BODY_STATE:
handle_after_body(token);
break;
case IN_FRAMESET_STATE:
case AFTER_FRAMESET_STATE:
case AFTER_AFTER_BODY_STATE:
handle_after_after_body(token);
break;
case AFTER_AFTER_FRAMESET_STATE:
stop_parsing = true;
break;
default:
fprintf(stderr, "Not handled parsing mode\n");
stop_parsing = true;
return -1;
}
return 0;
}
Node *parse(char *data) { // FIXME
document_node = init_document_node();
current_node = document_node;
token_t token;
for (;;) {
token = next_token(data);
process_token_using(insertion_mode, token);
if (stop_parsing) {
return document_node;
}
}
}

30
src/HTML/stack.c Normal file
View File

@ -0,0 +1,30 @@
#include <LibWeb/HTML/stack.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
stack_t stack = NULL;
int top = 0;
int bottom = 0;
Node* last_node_on_stack() {
if (bottom-1 < 0) {
printf("WARNING: stack under-flow\n"); // FIXME
return stack[bottom];
}
return stack[bottom-1];
}
Node* pop() {
Node* element = stack[bottom];
bottom--;
return element;
}
void push(Node* element) {
stack_t tmp = realloc(stack, (bottom+1)+sizeof(Node));
tmp[bottom] = element;
stack = tmp;
bottom++;
}

65
src/HTML/token.c Normal file
View File

@ -0,0 +1,65 @@
#include <LibWeb/HTML/token.h>
#include <stdio.h>
bool is_doctype(token_t token) {
return token.type == DOCTYPE;
}
bool is_start_tag(token_t token) {
return token.type == START_TAG;
}
bool is_end_tag(token_t token) {
return token.type == END_TAG;
}
bool is_comment(token_t token) {
return token.type == COMMENT;
}
bool is_character(token_t token) {
return token.type == CHARACTER;
}
bool is_eof(token_t token) {
return token.type == END_OF_FILE;
}
char *doctype_name(token_t token) {
return token.doctype.name;
}
char *tag_name(token_t token) {
return token.tag.name;
}
char *comment_data(token_t token) {
return token.comment.data;
}
char character_data(token_t token) {
return token.character.data;
}
int dump_token(token_t token) {
if (token.type == DOCTYPE) {
printf("DOCTYPE token: %s\n", token.doctype.name);
} else if (token.type == START_TAG || token.type == END_TAG) {
if (token.tag.self_closing == false)
printf("START_TAG");
else
printf("END_TAG");
printf(" token: %s\n", token.tag.name);
} else if (token.type == COMMENT) {
printf("COMMENT token: %s\n", token.comment.data);
} else if (token.type == CHARACTER) {
printf("CHARACTER token: %c\n", token.character.data);
} else if (token.type == END_OF_FILE) {
printf("EOF token\n");
return -1;
} else {
printf("Error: unknown token type");
return -1;
}
return 0;
}

250
src/HTML/tokenizer.c Normal file
View File

@ -0,0 +1,250 @@
#include <LibWeb/HTML/tokenizer.h>
#include <ctype.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
tokenizing_state_t state = DATA_STATE;
tokenizing_state_t return_state = DATA_STATE;
size_t position = 0;
uint32_t next_char(char *string) {
if (position >= strlen(string))
return EOF;
return string[position++];
}
void set_tokenizing(tokenizing_state_t input_state) {
state = input_state;
}
void reconsume(tokenizing_state_t reconsume_state) {
position--;
set_tokenizing(reconsume_state);
}
bool next_few_characters_are(char *string, char *src) {
for (size_t i = 0; i < strlen(string); i++) {
if (string[i] != src[position+i])
return false;
}
return true;
}
void consume(char *string) {
position += strlen(string);
}
char *init_str(char c) {
char *temp = malloc(2);
temp[0] = c;
temp[1] = '\0';
return temp;
}
char *append(char *string, char c) {
size_t len = strlen(string);
char *str = init_str(c);
string = realloc(string, len+1+1);
strcpy(string+len, str);
free(str);
return string;
}
token_t next_token(char *string) {
token_t token = {0};
token.type = UNKNOWN;
uint32_t current_input_character = EOF;
for (;;) {
switch(state) {
case DATA_STATE:
current_input_character = next_char(string);
if (current_input_character == '&') {
return_state = DATA_STATE;
set_tokenizing(CHARACTER_REFERENCE_STATE);
continue;
} else if (current_input_character == '<') {
set_tokenizing(TAG_OPEN_STATE);
continue;
} else if (current_input_character == '\0') {
// TODO: This is an unexpected-null-character parse error. Emit the current input character as a character token.
continue;
} else if (current_input_character == (uint32_t)EOF) {
token.type = END_OF_FILE;
// Emit an end-of-file token.
return token;
} else {
// Emit the current input character as a character token.
token.type = CHARACTER;
token.character.data = current_input_character;
return token;
}
break;
case RCDATA_STATE:
current_input_character = next_char(string);
if (current_input_character == '&') {
return_state = RCDATA_STATE;
set_tokenizing(CHARACTER_REFERENCE_STATE);
continue;
} else if (current_input_character == '<') {
// Switch to the RCDATA less-than sign state.
set_tokenizing(RCDATA_LESS_SIGN_STATE);
continue;
} else if (current_input_character == '\0') {
// TODO: This is an unexpected-null-character parse error. Emit a U+FFFD REPLACEMENT CHARACTER character token.
continue;
} else if (current_input_character == (uint32_t)EOF) {
token.type = END_OF_FILE;
// Emit an end-of-file token.
return token;
} else {
// Emit the current input character as a character token.
token.type = CHARACTER;
token.character.data = current_input_character;
return token;
}
break;
case RAWTEXT_STATE:
current_input_character = next_char(string);
if (current_input_character == '<') {
set_tokenizing(RAWTEXT_LESS_SIGN_STATE);
continue;
} else if (current_input_character == '\0') {
// TODO: This is an unexpected-null-character parse error. Emit a U+FFFD REPLACEMENT CHARACTER character token.
continue;
} else if (current_input_character == (uint32_t)EOF) {
token.type = END_OF_FILE;
// Emit an end-of-file token.
return token;
} else {
// Emit the current input character as a character token.
token.type = CHARACTER;
token.character.data = current_input_character;
return token;
}
break;
case SCRIPT_DATA_STATE:
current_input_character = next_char(string);
if (current_input_character == '<') {
set_tokenizing(SCRIPT_DATA_LESS_SIGN_STATE);
continue;
} else if (current_input_character == '\0') {
// TODO: This is an unexpected-null-character parse error. Emit a U+FFFD REPLACEMENT CHARACTER character token.
continue;
} else if (current_input_character == (uint32_t)EOF) {
token.type = END_OF_FILE;
// Emit an end-of-file token.
return token;
} else {
// Emit the current input character as a character token.
token.type = CHARACTER;
token.character.data = current_input_character;
return token;
}
break;
case PLAINTEXT_STATE:
current_input_character = next_char(string);
if (current_input_character == '\0') {
// TODO: This is an unexpected-null-character parse error. Emit a U+FFFD REPLACEMENT CHARACTER character token.
continue;
} else if (current_input_character == (uint32_t)EOF) {
// Emit an end-of-file token.
token.type = END_OF_FILE;
return token;
} else {
// Emit the current input character as a character token.
token.type = CHARACTER;
token.character.data = current_input_character;
return token;
}
break;
case TAG_OPEN_STATE:
current_input_character = next_char(string);
if (current_input_character == '!') {
set_tokenizing(MARKUP_DECLARATION_OPEN_STATE);
continue;
} else if (current_input_character == '/') {
set_tokenizing(END_TAG_OPEN_STATE);
continue;
} else if (isalpha(current_input_character)) {
token.type = START_TAG;
token.tag.name = init_str('\0');
token.tag.self_closing = false;
reconsume(TAG_NAME_STATE);
}
break;
case MARKUP_DECLARATION_OPEN_STATE:
if (next_few_characters_are("--", string)) {
fprintf(stderr, "Not implemented: MARKUP_DECLARATION_OPEN_STATE, '-'");
} else if (next_few_characters_are("DOCTYPE", string)) {
consume("DOCTYPE");
set_tokenizing(DOCTYPE_STATE);
continue;
}
break;
case DOCTYPE_STATE:
current_input_character = next_char(string);
if (isspace(current_input_character)) {
set_tokenizing(BEFORE_DOCTYPE_NAME_STATE);
continue;
}
break;
case BEFORE_DOCTYPE_NAME_STATE:
current_input_character = next_char(string);
if (isspace(current_input_character)) {
continue;
} else {
token.type = DOCTYPE;
token.doctype.name = init_str(current_input_character);
set_tokenizing(DOCTYPE_NAME_STATE);
continue;
}
break;
case DOCTYPE_NAME_STATE:
current_input_character = next_char(string);
if (isspace(current_input_character)) {
set_tokenizing(AFTER_DOCTYPE_NAME_STATE);
continue;
} else if (current_input_character == '>') {
set_tokenizing(DATA_STATE);
// Emit the current DOCTYPE token.
return token;
continue;
} else {
token.doctype.name = append(token.doctype.name, current_input_character);
}
break;
case TAG_NAME_STATE:
current_input_character = next_char(string);
if (isspace(current_input_character)) {
// TODO: Switch to the before attribute name state.
} else if (current_input_character == '>') {
set_tokenizing(DATA_STATE);
// Emit the current tag token.
return token;
continue;
} else {
token.tag.name = append(token.tag.name, current_input_character);
}
break;
case END_TAG_OPEN_STATE:
current_input_character = next_char(string);
if (isalpha(current_input_character)) {
token.type = END_TAG;
token.tag.name = init_str('\0');
token.tag.self_closing = true;
reconsume(TAG_NAME_STATE);
}
break;
default:
fprintf(stderr, "Not implemented\n");
token.type = UNKNOWN;
return token;
break;
}
}
}

45
src/Makefile Normal file
View File

@ -0,0 +1,45 @@
CC=cc
DIRS=HTML DOM
BUILDDIR=../build
BUILDDIRS=$(BUILDDIR)/HTML $(BUILDDIR)/DOM
LIBFILE=$(BUILDDIR)/libweb.a
UTILSDIR=utils
UTILSCFILES=$(wildcard $(UTILSSDIR)/*.c)
UTILSSOFILES=$(patsubst %.c, $(BUILDDIR)/%.o, $(UTILSCFILES))
CFILES=$(wildcard HTML/*.c DOM/*.c)
OFILES=$(patsubst %.c, $(BUILDDIR)/%.o, $(CFILES))
INCLUDEFLAGS=-Iinclude/
DEBUGFLAGS=-fsanitize=address -fsanitize=leak -fsanitize=undefined -fsanitize=pointer-compare -lasan
CFLAGS=-Wall -Wextra $(INCLUDEFLAGS) -g
LINKFLAGS=-L$(BUILDDIR) -l:$(LIBFILE)
.PHONY: all clean test
.SUFFIXES: .o .c
all: $(BUILDDIRS) $(LIBFILE)
$(BUILDDIRS):
mkdir -p $(BUILDDIR)/$(UTILSDIR)
mkdir -p $(BUILDDIR)/HTML
mkdir -p $(BUILDDIR)/DOM
$(BUILDDIR)/HTML/%.o: HTML/%.c
$(CC) -c $< -o $@ $(CFLAGS)
$(BUILDDIR)/DOM/%.o: DOM/%.c
$(CC) -c $< -o $@ $(CFLAGS)
$(LIBFILE): $(OFILES)
ar ruv $(LIBFILE) $(OFILES)
ranlib $(LIBFILE)
utils: $(LIBFILE)
$(CC) utils/dump_tokens.c -o $(BUILDDIR)/$(UTILSDIR)/dump_tokens $(CFLAGS) $(LINKFLAGS)
$(CC) utils/dump_tree.c -o $(BUILDDIR)/$(UTILSDIR)/dump_tree $(CFLAGS) $(LINKFLAGS)
clean:
rm -rf $(BUILDDIR)

View File

@ -0,0 +1,19 @@
#ifndef LIBWEB_DOM_CHARACTER_DATA_H
#define LIBWEB_DOM_CHARACTER_DATA_H
#include <LibWeb/DOM/types.h>
typedef struct CharacterData {
DOMString data;
unsigned long length;
} CharacterData;
// TODO: implement functions:
DOMString substring_data(CharacterData character_data, unsigned long offset, unsigned long count);
CharacterData append_data(CharacterData character_data, DOMString data);
CharacterData insert_data(CharacterData character_data, unsigned long offset, DOMString data);
CharacterData delete_data(CharacterData character_data, unsigned long offset, unsigned long count);
CharacterData replace_data(CharacterData character_data, unsigned long offset, unsigned long count, DOMString data);
#endif

View File

@ -0,0 +1,62 @@
#ifndef LIBWEB_DOM_DOCUMENT_H
#define LIBWEB_DOM_DOCUMENT_H
#include <LibWeb/DOM/types.h>
#include <LibWeb/DOM/document_type.h>
#include <LibWeb/DOM/element.h>
#include <LibWeb/DOM/text.h>
//interface Document : Node {
// constructor();
typedef struct Document {
//[SameObject] readonly attribute DOMImplementation implementation;
/*
readonly attribute USVString URL;
readonly attribute USVString documentURI;*/
DOMString compat_mode;
DOMString character_set;
DOMString charset; // legacy alias of .characterSet
DOMString input_encoding; // legacy alias of .characterSet
DOMString content_type;
DocumentType doctype;
Element document_element;
// FIXME:
Text text;
} Document;
/*
HTMLCollection getElementsByTagName(DOMString qualifiedName);
HTMLCollection getElementsByTagNameNS(DOMString? namespace, DOMString localName);
HTMLCollection getElementsByClassName(DOMString classNames);
*/
// TODO: [CEReactions, NewObject] Element createElement(DOMString localName, optional (DOMString or ElementCreationOptions) options = {});
Element create_element(Document document, DOMString local_name); // FIXME: options
/* [CEReactions, NewObject] Element createElementNS(DOMString? namespace, DOMString qualifiedName, optional (DOMString or ElementCreationOptions) options = {});
[NewObject] DocumentFragment createDocumentFragment();
[NewObject] Text createTextNode(DOMString data);
[NewObject] CDATASection createCDATASection(DOMString data);
[NewObject] Comment createComment(DOMString data);
[NewObject] ProcessingInstruction createProcessingInstruction(DOMString target, DOMString data);
[CEReactions, NewObject] Node importNode(Node node, optional boolean deep = false);
[CEReactions] Node adoptNode(Node node);
[NewObject] Attr createAttribute(DOMString localName);
[NewObject] Attr createAttributeNS(DOMString? namespace, DOMString qualifiedName);
[NewObject] Event createEvent(DOMString interface); // legacy
[NewObject] Range createRange();
// NodeFilter.SHOW_ALL = 0xFFFFFFFF
[NewObject] NodeIterator createNodeIterator(Node root, optional unsigned long whatToShow = 0xFFFFFFFF, optional NodeFilter? filter = null);
[NewObject] TreeWalker createTreeWalker(Node root, optional unsigned long whatToShow = 0xFFFFFFFF, optional NodeFilter? filter = null);
};
*/
#endif

View File

@ -0,0 +1,12 @@
#ifndef LIBWEB_DOM_DOCUMENT_TYPE_H
#define LIBWEB_DOM_DOCUMENT_TYPE_H
#include <LibWeb/DOM/types.h>
typedef struct DocumentType {
DOMString name;
DOMString public_id;
DOMString system_id;
} DocumentType;
#endif

View File

@ -0,0 +1,52 @@
#ifndef LIBWEB_DOM_ELEMENT_H
#define LIBWEB_DOM_ELEMENT_H
#include <LibWeb/DOM/types.h>
typedef struct Element {
DOMString namespace_uri;
DOMString prefix;
DOMString local_name;
DOMString tag_name;
// TODO: [CEReactions] attribute DOMString id;
// TODO: [CEReactions] attribute DOMString className;
// TODO: [SameObject, PutForwards=value] readonly attribute DOMTokenList classList;
// TODO: [CEReactions, Unscopable] attribute DOMString slot;
} Element;
/* TODO:
boolean hasAttributes();
[SameObject] readonly attribute NamedNodeMap attributes;
sequence<DOMString> getAttributeNames();
DOMString? getAttribute(DOMString qualifiedName);
DOMString? getAttributeNS(DOMString? namespace, DOMString localName);
[CEReactions] undefined setAttribute(DOMString qualifiedName, DOMString value);
[CEReactions] undefined setAttributeNS(DOMString? namespace, DOMString qualifiedName, DOMString value);
[CEReactions] undefined removeAttribute(DOMString qualifiedName);
[CEReactions] undefined removeAttributeNS(DOMString? namespace, DOMString localName);
[CEReactions] boolean toggleAttribute(DOMString qualifiedName, optional boolean force);
boolean hasAttribute(DOMString qualifiedName);
boolean hasAttributeNS(DOMString? namespace, DOMString localName);
Attr? getAttributeNode(DOMString qualifiedName);
Attr? getAttributeNodeNS(DOMString? namespace, DOMString localName);
[CEReactions] Attr? setAttributeNode(Attr attr);
[CEReactions] Attr? setAttributeNodeNS(Attr attr);
[CEReactions] Attr removeAttributeNode(Attr attr);
ShadowRoot attachShadow(ShadowRootInit init);
readonly attribute ShadowRoot? shadowRoot;
Element? closest(DOMString selectors);
boolean matches(DOMString selectors);
boolean webkitMatchesSelector(DOMString selectors); // legacy alias of .matches
HTMLCollection getElementsByTagName(DOMString qualifiedName);
HTMLCollection getElementsByTagNameNS(DOMString? namespace, DOMString localName);
HTMLCollection getElementsByClassName(DOMString classNames);
[CEReactions] Element? insertAdjacentElement(DOMString where, Element element); // legacy
undefined insertAdjacentText(DOMString where, DOMString data); // legacy
};*/
#endif

View File

@ -0,0 +1,94 @@
#ifndef LIBWEB_DOM_NODE_H
#define LIBWEB_DOM_NODE_H
#include <LibWeb/DOM/document.h>
#include <LibWeb/DOM/text.h>
#include <LibWeb/DOM/types.h>
#include <LibWeb/HTML/token.h>
#include <stdbool.h>
typedef enum {
ELEMENT_NODE = 1,
ATTRIBUTE_NODE = 2,
TEXT_NODE = 3,
CDATA_SECTION_NODE = 4,
ENTITY_REFERENCE_NODE = 5, // legacy
ENTITY_NODE = 6, // legacy
PROCESSING_INSTRUCTION_NODE = 7,
COMMENT_NODE = 8,
DOCUMENT_NODE = 9,
DOCUMENT_TYPE_NODE = 10,
DOCUMENT_FRAGMENT_NODE = 11,
NOTATION_NODE = 12, // legacy
} node_type_t;
typedef enum {
DOCUMENT_POSITION_DISCONNECTED = 0x01,
DOCUMENT_POSITION_PRECEDING = 0x02,
DOCUMENT_POSITION_FOLLOWING = 0x04,
DOCUMENT_POSITION_CONTAINS = 0x08,
DOCUMENT_POSITION_CONTAINED_BY = 0x10,
DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC = 0x20,
} document_position_t;
typedef struct NodeList NodeList;
typedef struct Node {
node_type_t node_type;
DOMString node_name;
// TODO: readonly attribute USVString baseURI;
bool is_connected;
// TODO: readonly attribute Document? ownerDocument;
Document owner_document;
struct Node *parent_node;
// readonly attribute Element? parentElement;
// TODO: NodeList child_nodes;
struct Node *first_child;
struct Node *last_child;
struct Node *previous_sibling;
struct Node *next_sibling;
} Node;
Node *init_document_node();
Node* make_node(node_type_t node_type, token_t token);
Node* make_text_node(Text text);
// TODO: Node getRootNode(optional GetRootNodeOptions options = {});
// TODO: boolean hasChildNodes();
// TODO: [CEReactions] attribute DOMString? nodeValue;
// TODO: [CEReactions] attribute DOMString? textContent;
// TODO: [CEReactions] undefined normalize();
// TODO: [CEReactions, NewObject] Node cloneNode(optional boolean deep = false);
// TODO: boolean isEqualNode(Node? otherNode);
// TODO: boolean isSameNode(Node? otherNode); // legacy alias of ===
// TODO: unsigned short compareDocumentPosition(Node other);
// TODO: boolean contains(Node? other);
// TODO: DOMString? lookupPrefix(DOMString? namespace);
// TODO: DOMString? lookupNamespaceURI(DOMString? prefix);
// TODO: boolean isDefaultNamespace(DOMString? namespace);
// TODO: [CEReactions] Node insertBefore(Node node, Node? child);
// TODO: [CEReactions] Node appendChild(Node node);
Node *append_child(Node *parent, Node *node);
// TODO: [CEReactions] Node replaceChild(Node node, Node child);
// TODO: [CEReactions] Node removeChild(Node child);
bool is_element_node(Node *node);
bool is_attribute_node(Node *node);
bool is_text_node(Node *node);
bool is_cdata_section_node(Node *node);
bool is_processing_instruction_node(Node *node);
bool is_comment_node(Node *node);
bool is_document_node(Node *node);
bool is_document_type_node(Node *node);
bool is_document_fragment_node(Node *node);
#endif

View File

@ -0,0 +1,16 @@
#ifndef LIBWEB_DOM_TEXT_H
#define LIBWEB_DOM_TEXT_H
#include <LibWeb/DOM/character_data.h>
typedef struct Text {
CharacterData data;
DOMString whole_text;
} Text;
Text text_constructor(Text text, DOMString data);
// Wrappers for CharacterNode functions
Text text_append(Text text, DOMString data);
#endif

View File

@ -0,0 +1,6 @@
#ifndef LIBWEB_DOM_TYPES_H
#define LIBWEB_DOM_TYPES_H
#define DOMString char*
#endif

View File

@ -0,0 +1,36 @@
#ifndef LIBWEB_HTML_PARSER_H
#define LIBWEB_HTML_PARSER_H
#include <LibWeb/DOM/node.h>
#include <stddef.h>
typedef enum {
INITIAL_STATE,
BEFORE_HTML_STATE,
BEFORE_HEAD_STATE,
IN_HEAD_STATE,
IN_HEAD_NOSCRIPT_STATE,
AFTER_HEAD_STATE,
IN_BODY_STATE,
TEXT_STATE,
IN_TABLE_STATE,
IN_TABLE_TEXT_STATE,
IN_CAPTION_STATE,
IN_COLUMN_GROUP_STATE,
IN_TABLE_BODY_STATE,
IN_ROW_STATE,
IN_CELL_STATE,
IN_SELECT_STATE,
IN_SELECT_IN_TABLE_STATE,
IN_TEMPLATE_STATE,
AFTER_BODY_STATE,
IN_FRAMESET_STATE,
AFTER_FRAMESET_STATE,
AFTER_AFTER_BODY_STATE,
AFTER_AFTER_FRAMESET_STATE,
} insertion_mode_t;
Node *parse(char* data); // FIXME
void dump_tree(Node *root, size_t spaces);
#endif

View File

@ -0,0 +1,12 @@
#ifndef LIBWEB_STACK_H
#define LIBWEB_STACK_H
#include <LibWeb/DOM/node.h>
typedef Node** stack_t;
Node* last_node_on_stack();
Node* pop();
void push(Node* element);
#endif

View File

@ -0,0 +1,64 @@
#ifndef LIBWEB_HTML_TOKEN_H
#define LIBWEB_HTML_TOKEN_H
#include <stdbool.h>
typedef struct {
char *name;
char *public_identifier;
char *system_identifier;
bool force_quirks;
} doctype_t;
typedef struct {
char *name;
char *value;
} attribute_t;
typedef struct {
char *name;
bool self_closing;
attribute_t *attributes;
} tag_t;
typedef struct {
char *data;
} comment_t;
typedef struct {
char data;
} character_t;
typedef enum {
UNKNOWN,
DOCTYPE,
START_TAG,
END_TAG,
COMMENT,
CHARACTER,
END_OF_FILE,
} token_type_t;
typedef struct {
token_type_t type;
doctype_t doctype;
tag_t tag;
comment_t comment;
character_t character;
} token_t; // FIXME
bool is_doctype(token_t token);
bool is_start_tag(token_t token);
bool is_end_tag(token_t token);
bool is_comment(token_t token);
bool is_character(token_t token);
bool is_eof(token_t token);
char *doctype_name(token_t token);
char *tag_name(token_t token);
char *comment_data(token_t token);
char character_data(token_t token);
int dump_token(token_t token);
#endif

View File

@ -0,0 +1,100 @@
#ifndef LIBWEB_HTML_TOKENIZER_H
#define LIBWEB_HTML_TOKENIZER_H
#include <LibWeb/HTML/token.h>
typedef enum {
DATA_STATE,
RCDATA_STATE,
RAWTEXT_STATE,
SCRIPT_DATA_STATE,
PLAINTEXT_STATE,
TAG_OPEN_STATE,
END_TAG_OPEN_STATE,
TAG_NAME_STATE,
RCDATA_LESS_SIGN_STATE,
RCDATA_END_TAG_OPEN_STATE,
RCDATA_END_TAG_NAME_STATE,
RAWTEXT_LESS_SIGN_STATE,
RAWTEXT_END_TAG_OPEN_STATE,
RAWTEXT_END_TAG_NAME_STATE,
SCRIPT_DATA_LESS_SIGN_STATE,
SCRIPT_DATA_END_TAG_OPEN_STATE,
SCRIPT_DATA_END_TAG_NAME_STATE,
SCRIPT_DATA_ESCAPE_START_STATE,
SCRIPT_DATA_ESCAPE_START_DASH_STATE,
SCRIPT_DATA_ESCAPED_STATE,
SCRIPT_DATA_ESCAPED_DASH_STATE,
SCRIPT_DATA_ESCAPED_DASH_DASH_STATE,
SCRIPT_DATA_ESCAPED_LESS_SIGN_STATE,
SCRIPT_DATA_ESCAPED_END_TAG_OPEN_STATE,
SCRIPT_DATA_ESCAPED_END_TAG_NAME_STATE,
SCRIPT_DATA_DOUBLE_ESCAPE_START_STATE,
SCRIPT_DATA_DOUBLE_ESCAPED_STATE,
SCRIPT_DATA_DOUBLE_ESCAPED_DASH_STATE,
SCRIPT_DATA_DOUBLE_ESCAPED_DASH_DASH_STATE,
SCRIPT_DATA_DOUBLE_ESCAPED_LESS_SIGN_STATE,
SCRIPT_DATA_DOUBLE_ESCAPE_END_STATE,
BEFORE_ATTRIBUTE_NAME_STATE,
ATTRIBUTE_NAME_STATE,
AFTER_ATTRIBUTE_NAME_STATE,
BEFORE_ATTRIBUTE_VALUE_STATE,
ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE,
ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE,
ATTRIBUTE_VALUE_UNQUOTED_STATE,
AFTER_ATTRIBUTE_VALUE_QUOTED_STATE,
SELF_CLOSING_START_TAG_STATE,
BOGUS_COMMENT_STATE,
MARKUP_DECLARATION_OPEN_STATE,
COMMENT_START_STATE,
COMMENT_START_DASH_STATE,
COMMENT_STATE,
COMMENT_LESS_SIGN_STATE,
COMMENT_LESS_SIGN_BANG_STATE,
COMMENT_LESS_SIGN_BANG_DASH_STATE,
COMMENT_LESS_SIGN_BANG_DASH_DASH_STATE,
COMMENT_END_DASH_STATE,
COMMENT_END_STATE,
COMMENT_END_BANG_STATE,
DOCTYPE_STATE,
BEFORE_DOCTYPE_NAME_STATE,
DOCTYPE_NAME_STATE,
AFTER_DOCTYPE_NAME_STATE,
AFTER_DOCTYPE_PUBLIC_KEYWORD_STATE,
BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE,
DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE,
DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE,
AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE,
BETWEEN_DOCTYPE_PUBLIC_AND_SYSTEM_IDENTIFIERS_STATE,
AFTER_DOCTYPE_SYSTEM_KEYWORD_STATE,
BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE,
BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE,
BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE,
AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE,
BOGUS_DOCTYPE_STATE,
CDATA_SECTION_STATE,
CDATA_SECTION_BRACKET_STATE,
CDATA_SECTION_END_STATE,
CHARACTER_REFERENCE_STATE,
NAMED_CHARACTER_REFERENCE_STATE,
AMBIGUOUS_AMPERSAND_STATE,
NUMERIC_CHARACTER_REFERENCE_STATE,
HEX_CHARACTER_REFERENCE_START_STATE,
DEC_CHARACTER_REFERENCE_START_STATE,
HEX_CHARACTER_REFERENCE_STATE,
DEC_CHARACTER_REFERENCE_STATE,
NUMERIC_CHARACTER_REFERENCE_END_STATE
} tokenizing_state_t;
void set_tokenizing(tokenizing_state_t input_state);
token_t next_token(char *string);
#endif

8
src/tests/test.html Normal file
View File

@ -0,0 +1,8 @@
<!DOCTYPE html>
<html>
<head></head>
<body>
<h1>Header</h1>
<p>hi</p>
</body>
</html>

23
src/utils/dump_tokens.c Normal file
View File

@ -0,0 +1,23 @@
#include <unistd.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdio.h>
#include <sys/mman.h>
#include <LibWeb/HTML/tokenizer.h>
#include <LibWeb/HTML/token.h>
#include <LibWeb/HTML/parser.h>
int main(int argc, char *argv[]) {
if (argc < 2) {
fprintf(stderr, "Not enough arguments\n");
return 1;
}
int fd = open(argv[1], O_RDONLY);
if (fd < 0) {
fprintf(stderr, "Failed to open file: %s\n", argv[1]);
return 1;
}
size_t size = lseek(fd, 0, SEEK_END);
char *data = mmap(0, size, PROT_READ, MAP_PRIVATE, fd, 0);
while(dump_token(next_token(data)) != -1);
}

23
src/utils/dump_tree.c Normal file
View File

@ -0,0 +1,23 @@
#include <unistd.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdio.h>
#include <sys/mman.h>
#include <LibWeb/HTML/tokenizer.h>
#include <LibWeb/HTML/token.h>
#include <LibWeb/HTML/parser.h>
int main(int argc, char *argv[]) {
if (argc < 2) {
fprintf(stderr, "Not enough arguments\n");
return 1;
}
int fd = open(argv[1], O_RDONLY);
if (fd < 0) {
fprintf(stderr, "Failed to open file: %s\n", argv[1]);
return 1;
}
size_t size = lseek(fd, 0, SEEK_END);
char *data = mmap(0, size, PROT_READ, MAP_PRIVATE, fd, 0);
dump_tree(parse(data), 0);
}