Last active
August 25, 2021 22:28
-
-
Save SpiffGreen/dfd77f18e63e143c13f445d30d315e58 to your computer and use it in GitHub Desktop.
A simple implementation of ast-to-html and html-to-ast algorithms
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const { htmlToAST, toks } = require("./html-to-ast"); | |
const SELF_CLOSING_TAG = "SELF_CLOSING_TAG"; | |
const OPENING_TAG = "OPENING_TAG"; | |
const TEXT_ELEMENT = "TEXT_ELEMENT"; | |
function astToHTML(ast) { | |
const start = "<" + ast.TagName + ">"; | |
let mid = ""; | |
const end = "</" + ast.TagName + ">"; | |
while(ast.children.length > 0) { | |
const child = ast.children.shift(); | |
switch(child.classification) { | |
case TEXT_ELEMENT: | |
mid += child.value; | |
break; | |
case OPENING_TAG: | |
mid += astToHTML(child); | |
break; | |
case SELF_CLOSING_TAG: | |
const selfClosing = "<" + child.TagName + " " + child.attribStrings + "/>"; | |
mid += selfClosing; | |
break; | |
default: | |
throw new Error("Sorry, an error occurred while parsing ast"); | |
} | |
} | |
return ast.TagName ? start + mid + end : mid; | |
} | |
console.log(astToHTML(htmlToAST(toks, {}))); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"use strict"; | |
// Constants | |
const CODE_EXAMPLE = `<p>hello <b>Spiff</b> <hr / >this is my picture <img src="/bill.png" /> is blah blak <br /> </p>`; | |
const SELF_CLOSING_TAG = "SELF_CLOSING_TAG"; | |
const CLOSING_TAG = "CLOSING_TAG"; | |
const OPENING_TAG = "OPENING_TAG"; | |
const TEXT_ELEMENT = "TEXT_ELEMENT"; | |
// Assertions | |
function isOpenTag(str) { | |
return str.match(/^<\s*[a-z]+\s*[^>]*>$/g) ? true : false; | |
} | |
function isSelfClosingTag(str) { | |
if(!str.trim().startsWith("<")) return false; | |
return str.match(/\/\s*>/g) ? true : false; | |
} | |
function isClosingTag(str) { | |
if(!str.trim().startsWith("<")) return false; | |
return str.match(/^<\s*\/\s*[a-zA-Z]+\s*>$/g); | |
} | |
// Classes to handle token types | |
class OpenHTMLTag { | |
constructor(token) { | |
this.type = "tag"; | |
this.TagName = ""; | |
this.classification = OPENING_TAG; | |
this.compile(token); | |
} | |
compile(token) { | |
const reg = /<\s*([a-zA-Z]+)\s*([^>]*)>/g; | |
let match = reg.exec(token.trim()); | |
this.TagName = match[1]; | |
this.attribStrings = match[2]; | |
} | |
} | |
class ClosingHTMLTag { | |
constructor(token) { | |
this.type = "tag"; | |
this.TagName = ""; | |
this.classification = CLOSING_TAG; | |
this.compile(token); | |
} | |
compile(token) { | |
const reg = /<\s*\/([a-zA-Z]+)\s*>/g; | |
let match = reg.exec(token.trim()); | |
this.TagName = match[1]; | |
} | |
} | |
class SelfClosingHTMLTag { | |
constructor(token) { | |
this.type = "tag"; | |
this.TagName = ""; | |
this.classification = SELF_CLOSING_TAG; | |
this.compile(token); | |
} | |
compile(token) { | |
const reg = /<\s*([a-zA-Z]+)\s*([^>]*)\/\s*>/g; | |
let match = reg.exec(token.trim()); | |
this.TagName = match[1]; | |
this.attribStrings = match[2]; | |
} | |
} | |
class Text { | |
constructor(token) { | |
this.classification = TEXT_ELEMENT; | |
this.value = token; | |
this.type = "text"; | |
} | |
} | |
/** | |
* @description A simple function to convert the said into tokens | |
* @param {String} str Code to tokenize | |
* @returns {Array<String>} An array of tokens | |
*/ | |
function tokenize(str) { | |
return str.match(/<[^>]+>|[A-Za-z0-9\s]*/g); | |
} | |
let tokens = tokenize(CODE_EXAMPLE); | |
const toks = tokens.map(i => { | |
if(isSelfClosingTag(i)) return new SelfClosingHTMLTag(i); | |
else if(isClosingTag(i)) return new ClosingHTMLTag(i); | |
else if(isOpenTag(i)) return new OpenHTMLTag(i); | |
else return new Text(i); | |
}); | |
module.exports.toks = toks; | |
module.exports.htmlToAST = function htmlToAST(toks, ast = {}) { | |
ast.children = []; | |
while(toks.length > 0) { | |
const item = toks.shift(); | |
switch(item.classification) { | |
case SELF_CLOSING_TAG: | |
ast.children.push(item); | |
break; | |
case OPENING_TAG: | |
ast.children.push(htmlToAST(toks, item)); | |
break; | |
case CLOSING_TAG: | |
return ast; | |
break; | |
case TEXT_ELEMENT: | |
ast.children.push(item); | |
break; | |
default: | |
throw new Error("Sorry, an error occured while parsing code"); | |
} | |
} | |
return ast; | |
} | |
// console.log(experimentalParse(toks)); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment