cmark

My personal build of CMark ✏️

Commit
59fd5633da5395cbd3627af4a2ab855dc43ce1e0
Parent
c38944d4565b6266b3ab1fd6fd576710fdec420b
Author
Nick Wellnhofer <wellnhofer@aevum.de>
Date

Set prev, parent and last_child for inlines

Diffstat

4 files changed, 107 insertions, 5 deletions

Status File Name N° Changes Insertions Deletions
Modified src/blocks.c 19 19 0
Modified src/inlines.c 32 27 5
Modified src/node.c 58 58 0
Modified src/node.h 3 3 0
diff --git a/src/blocks.c b/src/blocks.c
@@ -283,6 +283,19 @@ typedef struct BlockStack {
 	cmark_node *next_sibling;
 } block_stack;
 
+static void fix_parents(cmark_node *node) {
+	cmark_node *cur = node->first_child;
+	if (cur == NULL) {
+		return;
+	}
+	while (cur->next != NULL) {
+		cur->parent = node;
+		cur = cur->next;
+	}
+	cur->parent = node;
+	node->last_child = cur;
+}
+
 // Walk through cmark_node and all children, recursively, parsing
 // string content into inline content where appropriate.
 static void process_inlines(cmark_node* cur, reference_map *refmap)
@@ -296,6 +309,7 @@ static void process_inlines(cmark_node* cur, reference_map *refmap)
 			case NODE_ATX_HEADER:
 			case NODE_SETEXT_HEADER:
 				cur->first_child = parse_inlines(&cur->string_content, refmap);
+				fix_parents(cur);
 				break;
 
 			default:
@@ -817,6 +831,11 @@ cmark_node *cmark_finish(cmark_doc_parser *parser)
 {
 	finalize_document(parser);
 	strbuf_free(parser->curline);
+#if CMARK_DEBUG_NODES
+	if (cmark_node_check(parser->root)) {
+		abort();
+	}
+#endif
 	return parser->root;
 }
 
diff --git a/src/inlines.c b/src/inlines.c
@@ -193,6 +193,7 @@ static inline cmark_node* cmark_append_inlines(cmark_node* a, cmark_node* b)
 		cur = cur->next;
 	}
 	cur->next = b;
+	b->prev = cur;
 	return a;
 }
 
@@ -395,12 +396,8 @@ static void process_emphasis(subject *subj, delimiter_stack *stack_bottom)
 				// between the opener and closer
 				emph = use_delims == 1 ? make_emph(inl->next) : make_strong(inl->next);
 				emph->next = closer->first_inline;
+				emph->prev = inl;
 				inl->next = emph;
-				tmp = emph->first_child;
-				while (tmp->next != NULL && tmp->next != closer->first_inline) {
-					tmp = tmp->next;
-				}
-				tmp->next = NULL;
 
 				// if opener has 0 delims, remove it and its associated inline
 				if (opener->delim_count == 0) {
@@ -415,11 +412,27 @@ static void process_emphasis(subject *subj, delimiter_stack *stack_bottom)
 					remove_delimiter(subj, opener);
 				}
 
+				// fix tree structure
+				tmp = emph->first_child;
+				while (tmp->next != NULL && tmp->next != closer->first_inline) {
+					tmp->parent = emph;
+					tmp = tmp->next;
+				}
+				tmp->parent = emph;
+				if (tmp->next) {
+					tmp->next->prev = emph;
+				}
+				tmp->next = NULL;
+				emph->last_child = tmp;
+
 				// if closer has 0 delims, remove it and its associated inline
 				if (closer->delim_count == 0) {
 					// remove empty closer inline
 					tmp = closer->first_inline;
 					emph->next = tmp->next;
+					if (tmp->next) {
+						tmp->next->prev = emph;
+					}
 					tmp->next = NULL;
 					cmark_free_nodes(tmp);
 					// remove closer from stack
@@ -727,6 +740,15 @@ match:
 	inl->as.link.url   = url;
 	inl->as.link.title = title;
 	inl->next = NULL;
+	if (link_text) {
+		cmark_node *tmp;
+		link_text->prev = NULL;
+		for (tmp = link_text; tmp->next != NULL; tmp = tmp->next) {
+			tmp->parent = inl;
+		}
+		tmp->parent = inl;
+		inl->last_child = tmp;
+	}
 	*last = inl;
 
 	// process_emphasis will remove this delimiter and all later ones.
diff --git a/src/node.c b/src/node.c
@@ -249,6 +249,64 @@ static void splice_into_list(cmark_node* e, cmark_node* children) {
 	return ;
 }
 
+int
+cmark_node_check(cmark_node *node) {
+	cmark_node *cur = node;
+	int errors = 0;
+
+	while (cur) {
+		if (cur->first_child) {
+			if (cur->first_child->parent != cur) {
+				fprintf(stderr,
+					"Invalid 'parent' in node type %d\n",
+					cur->first_child->type);
+				cur->first_child->parent = cur;
+				++errors;
+			}
+			cur = cur->first_child;
+		}
+		else if (cur->next) {
+			if (cur->next->prev != cur) {
+				fprintf(stderr,
+					"Invalid 'prev' in node type %d\n",
+					cur->next->type);
+				cur->next->prev = cur;
+				++errors;
+			}
+			if (cur->next->parent != cur->parent) {
+				fprintf(stderr,
+					"Invalid 'parent' in node type %d\n",
+					cur->next->type);
+				cur->next->parent = cur->parent;
+				++errors;
+			}
+			cur = cur->next;
+		}
+		else {
+			if (cur->parent->last_child != cur) {
+				fprintf(stderr,
+					"Invalid 'last_child' in node type %d\n",
+					cur->parent->type);
+				cur->parent->last_child = cur;
+				++errors;
+			}
+
+			cmark_node *ancestor = cur->parent;
+			cur = NULL;
+
+			while (ancestor != node->parent) {
+				if (ancestor->next) {
+					cur = ancestor->next;
+					break;
+				}
+				ancestor = ancestor->parent;
+			}
+		}
+	}
+
+	return errors;
+}
+
 // Free a cmark_node list and any children.
 void cmark_free_nodes(cmark_node *e)
 {
diff --git a/src/node.h b/src/node.h
@@ -62,6 +62,9 @@ struct cmark_node {
 	} as;
 };
 
+int
+cmark_node_check(cmark_node *node);
+
 #ifdef __cplusplus
 }
 #endif