cmark

My personal build of CMark ✏️

Commit
e7efe2af56ceae5df00f23bb6090c81bb734bb5c
Parent
5d16af6b134477d0e9b281cd1e9d962dc52b5088
Author
John MacFarlane <jgm@berkeley.edu>
Date

Merge pull request #227 from nwellnhof/process_emph

Optimize and clarify process_emph

Diffstat

2 files changed, 90 insertions, 95 deletions

Status File Name N° Changes Insertions Deletions
Modified src/inlines.c 180 85 95
Modified src/node.c 5 5 0
diff --git a/src/inlines.c b/src/inlines.c
@@ -20,8 +20,8 @@
 #define make_raw_html(s) make_literal(CMARK_NODE_INLINE_HTML, s)
 #define make_linebreak() make_simple(CMARK_NODE_LINEBREAK)
 #define make_softbreak() make_simple(CMARK_NODE_SOFTBREAK)
-#define make_emph(contents) make_inlines(CMARK_NODE_EMPH, contents)
-#define make_strong(contents) make_inlines(CMARK_NODE_STRONG, contents)
+#define make_emph() make_simple(CMARK_NODE_EMPH)
+#define make_strong() make_simple(CMARK_NODE_STRONG)
 
 typedef struct DelimiterStack {
 	struct DelimiterStack *previous;
@@ -41,6 +41,9 @@ typedef struct Subject {
 	delimiter_stack *delimiters;
 } subject;
 
+static delimiter_stack*
+S_insert_emph(subject *subj, delimiter_stack *opener, delimiter_stack *closer);
+
 static int parse_inline(subject* subj, cmark_node * parent);
 
 static void subject_from_buf(subject *e, strbuf *buffer,
@@ -83,24 +86,6 @@ static inline cmark_node* make_autolink(cmark_node* label, cmark_chunk url, int 
 	return make_link(label, cmark_clean_autolink(&url, is_email), NULL);
 }
 
-// Setting 'last_child' and the parent of 'contents' is up to the caller.
-static inline cmark_node* make_inlines(cmark_node_type t, cmark_node* contents)
-{
-	cmark_node * e = (cmark_node *)calloc(1, sizeof(*e));
-	if(e != NULL) {
-		e->type = t;
-		e->first_child = contents;
-		e->next = NULL;
-                e->prev = NULL;
-                e->parent = NULL;
-                // These fields aren't used for inlines:
-                e->start_line = 0;
-                e->start_column = 0;
-                e->end_line = 0;
-	}
-	return e;
-}
-
 // Create an inline with a literal string value.
 static inline cmark_node* make_literal(cmark_node_type t, cmark_chunk s)
 {
@@ -361,9 +346,7 @@ static cmark_node* handle_strong_emph(subject* subj, unsigned char c)
 static void process_emphasis(subject *subj, delimiter_stack *stack_bottom)
 {
 	delimiter_stack *closer = subj->delimiters;
-	delimiter_stack *opener, *tempstack, *nextstack;
-	int use_delims;
-	cmark_node *inl, *tmp, *emph;
+	delimiter_stack *opener;
 
 	// move back to first relevant delim.
 	while (closer != NULL && closer->previous != stack_bottom) {
@@ -384,78 +367,7 @@ static void process_emphasis(subject *subj, delimiter_stack *stack_bottom)
 				opener = opener->previous;
 			}
 			if (opener != NULL && opener != stack_bottom) {
-				// calculate the actual number of delimeters used from this closer
-				if (closer->delim_count < 3 || opener->delim_count < 3) {
-					use_delims = closer->delim_count <= opener->delim_count ?
-						closer->delim_count : opener->delim_count;
-				} else { // closer and opener both have >= 3 delims
-					use_delims = closer->delim_count % 2 == 0 ? 2 : 1;
-				}
-
-				inl = opener->first_inline;
-
-				// remove used delimiters from stack elements and associated inlines.
-				opener->delim_count -= use_delims;
-				closer->delim_count -= use_delims;
-				inl->as.literal.len = opener->delim_count;
-				closer->first_inline->as.literal.len = closer->delim_count;
-
-				// free delimiters between opener and closer
-				tempstack = closer->previous;
-				while (tempstack != NULL && tempstack != opener) {
-					nextstack = tempstack->previous;
-					remove_delimiter(subj, tempstack);
-					tempstack = nextstack;
-				}
-
-				// create new emph or strong, and splice it in to our inlines
-				// 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;
-				emph->parent = inl->parent;
-				inl->next = emph;
-
-				// if opener has 0 delims, remove it and its associated inline
-				if (opener->delim_count == 0) {
-					// replace empty opener inline with emph
-					chunk_free(&(inl->as.literal));
-					inl->type = emph->type;
-					inl->next = emph->next;
-					inl->first_child = emph->first_child;
-					free(emph);
-					emph = inl;
-					// remove opener from stack
-					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;
-					}
-					cmark_node_free(tmp);
-					// remove closer from stack
-					tempstack = closer->next;
-					remove_delimiter(subj, closer);
-					closer = tempstack;
-				}
+				closer = S_insert_emph(subj, opener, closer);
 			} else {
 				closer = closer->next;
 			}
@@ -469,6 +381,84 @@ static void process_emphasis(subject *subj, delimiter_stack *stack_bottom)
 	}
 }
 
+static delimiter_stack*
+S_insert_emph(subject *subj, delimiter_stack *opener, delimiter_stack *closer)
+{
+	delimiter_stack *tempstack, *nextstack;
+	int use_delims;
+	cmark_node *opener_inl = opener->first_inline;
+	cmark_node *closer_inl = closer->first_inline;
+	cmark_node *tmp, *emph, *first_child, *last_child;
+
+	// calculate the actual number of delimeters used from this closer
+	if (closer->delim_count < 3 || opener->delim_count < 3) {
+		use_delims = closer->delim_count <= opener->delim_count ?
+			closer->delim_count : opener->delim_count;
+	} else { // closer and opener both have >= 3 delims
+		use_delims = closer->delim_count % 2 == 0 ? 2 : 1;
+	}
+
+	// remove used delimiters from stack elements and associated inlines.
+	opener->delim_count -= use_delims;
+	closer->delim_count -= use_delims;
+	opener_inl->as.literal.len = opener->delim_count;
+	closer_inl->as.literal.len = closer->delim_count;
+
+	// free delimiters between opener and closer
+	tempstack = closer->previous;
+	while (tempstack != NULL && tempstack != opener) {
+		nextstack = tempstack->previous;
+		remove_delimiter(subj, tempstack);
+		tempstack = nextstack;
+	}
+
+	first_child = opener_inl->next;
+	last_child  = closer_inl->prev;
+
+	// if opener has 0 delims, remove it and its associated inline
+	if (opener->delim_count == 0) {
+		// replace empty opener inline with emph
+		chunk_free(&(opener_inl->as.literal));
+		emph = opener_inl;
+		emph->type = use_delims == 1 ? NODE_EMPH : NODE_STRONG;
+		// remove opener from stack
+		remove_delimiter(subj, opener);
+	}
+	else {
+		// create new emph or strong, and splice it in to our inlines
+		// between the opener and closer
+		emph = use_delims == 1 ? make_emph() : make_strong();
+		emph->parent = opener_inl->parent;
+		emph->prev = opener_inl;
+		opener_inl->next = emph;
+	}
+
+	// push children below emph
+	emph->next = closer_inl;
+	closer_inl->prev = emph;
+	emph->first_child = first_child;
+	emph->last_child  = last_child;
+
+	// fix children pointers
+	first_child->prev = NULL;
+	last_child->next  = NULL;
+	for (tmp = first_child; tmp != NULL; tmp = tmp->next) {
+		tmp->parent = emph;
+	}
+
+	// if closer has 0 delims, remove it and its associated inline
+	if (closer->delim_count == 0) {
+		// remove empty closer inline
+		cmark_node_free(closer_inl);
+		// remove closer from stack
+		tempstack = closer->next;
+		remove_delimiter(subj, closer);
+		closer = tempstack;
+	}
+
+	return closer;
+}
+
 // Parse backslash-escape or just a backslash, returning an inline.
 static cmark_node* handle_backslash(subject *subj)
 {
diff --git a/src/node.c b/src/node.c
@@ -607,6 +607,11 @@ cmark_node_check(cmark_node *node, FILE *out)
 	cur = node;
 	while (true) {
 		if (cur->first_child) {
+			if (cur->first_child->prev != NULL) {
+				S_print_error(out, cur->first_child, "prev");
+				cur->first_child->prev = NULL;
+				++errors;
+			}
 			if (cur->first_child->parent != cur) {
 				S_print_error(out, cur->first_child, "parent");
 				cur->first_child->parent = cur;