/* * md ast - pluggable markdown parser */ const syntax = { bold: { paired: true, recursive: true, startRegexp: /\*\*\S.*/, endRegexp: /\S\*\*\s/, content: { start: { point: 'start', offset: 2 }, end: { point: 'end', offset: 1 } }, begin: 0, forward: { point: 'endEnd', //start, startEnd, end, endEnd offset: -1 } }, bold2: { paired: true, recursive: true, startRegexp: /\s__\S.*/, endRegexp: /\S__\s/, content: { start: { point: 'start', offset: 3 }, end: { point: 'end', offset: 1 } }, begin: 1, forward: { point: 'endEnd', //start, startEnd, end, endEnd offset: -1 } }, italic: { paired: true, recursive: true, startRegexp: /\*\S.*/, endRegexp: /\S\*\s/, content: { start: { point: 'start', offset: 2 }, end: { point: 'end', offset: 1 } }, begin: 0, forward: { point: 'endEnd', //start, startEnd, end, endEnd offset: -1 } }, italic2: { paired: true, recursive: true, startRegexp: /\s_\S.*/, endRegexp: /\S_\s/, content: { start: { point: 'start', offset: 2 }, end: { point: 'end', offset: 1 } }, begin: 1, forward: { point: 'endEnd', //start, startEnd, end, endEnd offset: -1 } }, root: { paired: true, recursive: true, startRegexp: /^/, endRegexp: /$/, content: { start: { point: 'start', offset: 1 }, end: { point: 'end', offset: 0 } }, begin: 0, forward: { point: 'endEnd', //start, startEnd, end, endEnd offset: -1 } }, } function findNearest(md, mdTags, offset=0){ let nearest, nearestMatch = {index: Infinity}; for (let [mdTag, {paired, startRegexp, rexexp}] of Object.entries(mdTags)) { if (mdTag === 'root') continue; let match = md.offsetMatch(offset, startRegexp || regexp) if (match && match.index < nearestMatch.index){ nearestMatch = match nearest = mdTag } } return [nearest, nearestMatch] } function cutNode(md, match, tagName, {paired, recursive, startRegexp, endRegexp, content: {start, end}, begin, forward}){ //if (paired){ //md.match() //} } //node: //{ // tag: 'keyFromSyntax', // children: [String, Node] // parent: node //} // String.prototype.offsetMatch = function(offset, ...params){ return this.slice(offset).match(...params) } Array.prototype.last = function(){ return this[this.length -1] } function buildAST(md, mdTags=syntax, offset=0, tree={tag: 'root'}, stack=[]){ const currentNode = stack.last() || tree currentNode.children = currentNode.children || [] const { children } = currentNode const { endRegexp, content: {end: {offset: offsetEnd} }, forward } = mdTags[currentNode.tag] while(offset < md.length){ const [nearest, nearestMatch] = findNearest(md, mdTags, offset) const endMatch = md.offsetMatch(offset, endRegexp) if (endMatch) { if (!nearest || endMatch.index < nearestMatch.index){ currentNode.endContent = offset + endMatch.index + offsetEnd children.push(md.slice(offset, currentNode.endContent)) offset += endMatch.index + endMatch[0].length + forward.offset currentNode.endOffset = offset console.log('endmatch current', currentNode) return currentNode } } if (nearest){ const {begin,content: {start}} = mdTags[nearest] if (nearestMatch.index){ children.push(md.slice(offset, offset + nearestMatch.index + begin)) offset += nearestMatch.index console.log(offset, md.slice(offset)) } else { const newNode = {tag: nearest, startOffset: offset, parent: currentNode} children.push(newNode) buildAST(md, mdTags, offset + start.offset, tree, [...stack, newNode]) console.log('same', newNode) offset = newNode.endOffset console.log(offset, md.slice(offset)) } } else { children.push(md) md = '' } } return currentNode } console.log(buildAST("I just **love _bold text_ adddd** hahaha").children[1])