2
0
mirror of https://github.com/tenrok/BBob.git synced 2026-06-23 20:40:34 +03:00

add TagNode class for ast, some method names refactor

This commit is contained in:
Nikolay Kostyurin
2018-06-28 21:10:08 +02:00
parent 780483daca
commit 0d05a5497d
2 changed files with 110 additions and 66 deletions
+51 -66
View File
@@ -12,14 +12,10 @@ const {
isTagEnd, isTagEnd,
} = require('./Token'); } = require('./Token');
const {
SLASH,
getChar,
} = require('./char');
const Tokenizer = require('./Tokenizer'); const Tokenizer = require('./Tokenizer');
const TagNode = require('./TagNode');
const createTagNode = (tag, attrs = {}, content = []) => ({ tag, attrs, content }); const createTagNode = (tag, attrs = {}, content = []) => new TagNode(tag, attrs, content);
/** /**
* *
@@ -33,59 +29,66 @@ const createTagNode = (tag, attrs = {}, content = []) => ({ tag, attrs, content
*/ */
class Parser { class Parser {
constructor(input, options = {}) { constructor(input, options = {}) {
this.tokenizer = new Tokenizer(input, { this.createTokenizer(input);
onToken: (token) => {
this.parseToken(token);
},
});
this.options = options; this.options = options;
this.nodes = []; this.nodes = [];
this.nestedNodes = []; this.nestedNodes = [];
this.curTags = []; this.tagNodes = [];
this.curTagsAttrName = []; this.tagNodesAttrName = [];
} }
isNestedTag(token) { createTokenizer(input) {
this.tokenizer = new Tokenizer(input, {
onToken: (token) => {
this.parseToken(token);
},
});
}
isTagNested(token) {
return this.tokenizer.isTokenNested(token); return this.tokenizer.isTokenNested(token);
} }
getCurTag() { /**
if (this.curTags.length) { * @return {TagNode}
return this.curTags[this.curTags.length - 1]; */
getTagNode() {
if (this.tagNodes.length) {
return this.tagNodes[this.tagNodes.length - 1];
} }
return null; return null;
} }
createCurTag(token) { createTagNode(token) {
this.curTags.push(createTagNode(getTokenValue(token))); this.tagNodes.push(createTagNode(getTokenValue(token)));
} }
createCurTagAttrName(token) { createTagNodeAttrName(token) {
this.curTagsAttrName.push(getTokenValue(token)); this.tagNodesAttrName.push(getTokenValue(token));
} }
getCurTagAttrName() { getTagNodeAttrName() {
if (this.curTagsAttrName.length) { if (this.tagNodesAttrName.length) {
return this.curTagsAttrName[this.curTagsAttrName.length - 1]; return this.tagNodesAttrName[this.tagNodesAttrName.length - 1];
} }
return this.getCurTag().tag; return this.getTagNode().tag;
} }
clearCurTagAttrName() { clearTagNodeAttrName() {
if (this.curTagsAttrName.length) { if (this.tagNodesAttrName.length) {
this.curTagsAttrName.pop(); this.tagNodesAttrName.pop();
} }
} }
clearCurTag() { clearTagNode() {
if (this.curTags.length) { if (this.tagNodes.length) {
this.curTags.pop(); this.tagNodes.pop();
this.clearCurTagAttrName(); this.clearTagNodeAttrName();
} }
} }
@@ -104,20 +107,20 @@ class Parser {
handleTagStart(token) { handleTagStart(token) {
if (isTagStart(token)) { if (isTagStart(token)) {
this.createCurTag(token); this.createTagNode(token);
if (this.isNestedTag(token)) { if (this.isTagNested(token)) {
this.nestedNodes.push(this.getCurTag()); this.nestedNodes.push(this.getTagNode());
} else { } else {
this.appendNode(this.getCurTag()); this.appendNode(this.getTagNode());
this.clearCurTag(); this.clearTagNode();
} }
} }
} }
handleTagEnd(token) { handleTagEnd(token) {
if (isTagEnd(token)) { if (isTagEnd(token)) {
this.clearCurTag(); this.clearTagNode();
const lastNestedNode = this.nestedNodes.pop(); const lastNestedNode = this.nestedNodes.pop();
@@ -144,16 +147,18 @@ class Parser {
} }
} }
handleCurTag(token) { handleTagNode(token) {
if (this.getCurTag()) { const tagNode = this.getTagNode();
if (tagNode) {
if (isAttrNameToken(token)) { if (isAttrNameToken(token)) {
this.createCurTagAttrName(token); this.createTagNodeAttrName(token);
this.getCurTag().attrs[this.getCurTagAttrName()] = null; tagNode.attr(this.getTagNodeAttrName(), null);
} else if (isAttrValueToken(token)) { } else if (isAttrValueToken(token)) {
this.getCurTag().attrs[this.getCurTagAttrName()] = getTokenValue(token); tagNode.attr(this.getTagNodeAttrName(), getTokenValue(token));
this.clearCurTagAttrName(); this.clearTagNodeAttrName();
} else if (isTextToken(token)) { } else if (isTextToken(token)) {
this.getCurTag().content.push(getTokenValue(token)); tagNode.append(getTokenValue(token));
} }
} else if (isTextToken(token)) { } else if (isTextToken(token)) {
this.appendNode(getTokenValue(token)); this.appendNode(getTokenValue(token));
@@ -162,7 +167,7 @@ class Parser {
parseToken(token) { parseToken(token) {
this.handleTagToken(token); this.handleTagToken(token);
this.handleCurTag(token); this.handleTagNode(token);
} }
parse() { parse() {
@@ -184,24 +189,6 @@ class Parser {
return this.nodes; return this.nodes;
} }
findNestedTags() {
const tags = (this.tokens || []).filter(isTagToken).reduce((acc, token) => {
acc[getTokenValue(token)] = true;
return acc;
}, {});
const closeChar = getChar(SLASH);
return Object.keys(tags).reduce((arr, key) => {
if (tags[key] && tags[closeChar + key]) {
arr.push(key);
}
return arr;
}, []);
}
isAllowedTag(value) { isAllowedTag(value) {
if (this.options.onlyAllowTags && this.options.onlyAllowTags.length) { if (this.options.onlyAllowTags && this.options.onlyAllowTags.length) {
return this.options.onlyAllowTags.indexOf(value) >= 0; return this.options.onlyAllowTags.indexOf(value) >= 0;
@@ -211,7 +198,5 @@ class Parser {
} }
} }
new Parser('[Verse 2]').parse();
module.exports = Parser; module.exports = Parser;
module.exports.createTagNode = createTagNode; module.exports.createTagNode = createTagNode;
+59
View File
@@ -0,0 +1,59 @@
const {
getChar, N, CLOSE_BRAKET, OPEN_BRAKET, SLASH,
} = require('./char');
const EOL = getChar(N);
const isNode = el => typeof el === 'object' && el.tag;
const isStringNode = el => typeof el === 'string';
const isEOL = el => el === EOL;
const getNodeLength = (node) => {
if (isNode(node)) {
return node.content.reduce((count, contentNode) => count + getNodeLength(contentNode), 0);
} else if (isStringNode(node)) {
return node.length;
}
return 0;
};
const appendToNode = (node, value) => {
node.content.push(value);
};
class TagNode {
constructor(tag, attrs, content) {
this.tag = tag;
this.attrs = attrs;
this.content = content;
}
attr(name, value) {
if (typeof value !== 'undefined') {
this.attrs[name] = value;
}
return this.attrs[name];
}
append(value) {
return appendToNode(this, value);
}
get length() {
return getNodeLength(this);
}
toString() {
const OB = getChar(OPEN_BRAKET);
const CB = getChar(CLOSE_BRAKET);
return OB + this.tag + CB + this.content.reduce((r, node) => r + node.toString(), '') + OB + getChar(SLASH) + this.tag + CB;
}
}
module.exports = TagNode;
module.exports.isNode = isNode;
module.exports.isStringNode = isStringNode;
module.exports.isEOL = isEOL;
module.exports.appendToNode = appendToNode;