2
0
mirror of https://github.com/tenrok/BBob.git synced 2026-06-11 18:02:26 +03:00

add eslint, travis config, test tasks

This commit is contained in:
Nikolay Kostyurin
2018-06-11 22:31:02 +02:00
parent 305643daa2
commit 792e949b6e
36 changed files with 6529 additions and 434 deletions
-1
View File
@@ -1,4 +1,3 @@
package-lock.json
coverage
lib
dist
+5 -2
View File
@@ -1,2 +1,5 @@
benchmark
*.test.js
package-lock.json
coverage
src
dist
!lib
+155 -152
View File
@@ -1,174 +1,177 @@
const {
convertTokenToText,
getTagName,
getTokenColumn,
getTokenLine,
getTokenValue,
isAttrNameToken,
isAttrValueToken,
isTagStart,
isTagToken,
isTextToken,
isTagEnd
} = require("./Tokenizer");
const Tokenizer = require("./Tokenizer");
convertTokenToText,
getTagName,
getTokenColumn,
getTokenLine,
getTokenValue,
isAttrNameToken,
isAttrValueToken,
isTagStart,
isTagToken,
isTextToken,
isTagEnd,
} = require('./Tokenizer');
const Tokenizer = require('./Tokenizer');
const TokenChar = Tokenizer.CHAR;
const getChar = Tokenizer.getChar;
const createTagNode = (name, attrs = {}, content = []) => ({tag: name, attrs, content});
const createTagNode = (name, attrs = {}, content = []) => ({ tag: name, attrs, content });
/**
*
{
tag: 'div',
attrs: {
class: 'foo'
},
content: ['hello world!']
}
{
tag: 'div',
attrs: {
class: 'foo'
},
content: ['hello world!']
}
*/
module.exports = class Parser {
constructor(tokens, options = {}) {
this.tokens = tokens;
this.options = options
}
constructor(tokens, options = {}) {
this.tokens = tokens;
this.options = options;
}
parse() {
const tokens = this.tokens;
const nodes = [];
const nestedNodes = [];
const curTags = [];
const curTagsAttrName = [];
parse() {
const nodes = [];
const nestedNodes = [];
const curTags = [];
const curTagsAttrName = [];
const closableTags = this.findNestedTags(tokens);
const closableTags = this.findNestedTags(this.tokens);
const isNestedTag = (token) => closableTags.indexOf(getTokenValue(token)) >= 0;
const isNestedTag = token => closableTags.indexOf(getTokenValue(token)) >= 0;
const getCurTag = () => {
if (curTags.length) {
return curTags[curTags.length - 1]
const getCurTag = () => {
if (curTags.length) {
return curTags[curTags.length - 1];
}
return null;
};
const createCurTag = (token) => {
curTags.push(createTagNode(getTokenValue(token)));
};
const getCurTagAttrName = () => {
if (curTagsAttrName.length) {
return curTagsAttrName[curTagsAttrName.length - 1];
}
return null;
};
const createCurTagAttrName = (token) => {
curTagsAttrName.push(getTokenValue(token));
};
const clearCurTagAttrName = () => {
if (curTagsAttrName.length) {
curTagsAttrName.pop();
}
};
const clearCurTag = () => {
if (curTags.length) {
curTags.pop();
clearCurTagAttrName();
}
};
const getNodes = () => {
if (nestedNodes.length) {
const nestedNode = nestedNodes[nestedNodes.length - 1];
return nestedNode.content;
}
return nodes;
};
let token;
// eslint-disable-next-line no-cond-assign
while (token = this.tokens.shift()) {
if (!token) {
// eslint-disable-next-line no-continue
continue;
}
if (isTagToken(token)) {
if (this.isAllowedTag(getTagName(token))) {
// [tag]
if (isTagStart(token)) {
createCurTag(token);
if (isNestedTag(token)) {
nestedNodes.push(getCurTag());
} else {
getNodes().push(getCurTag());
clearCurTag();
}
}
return null
};
// [/tag]
if (isTagEnd(token)) {
clearCurTag();
const createCurTag = (token) => {
curTags.push(createTagNode(getTokenValue(token)))
};
const lastNestedNode = nestedNodes.pop();
const clearCurTag = () => {
if (curTags.length) {
curTags.pop();
clearCurTagAttrName()
}
};
const getCurTagAttrName = () => {
if (curTagsAttrName.length) {
return curTagsAttrName[curTagsAttrName.length - 1]
}
return null
};
const createCurTagAttrName = (token) => {
curTagsAttrName.push(getTokenValue(token))
};
const clearCurTagAttrName = () => {
if (curTagsAttrName.length) {
curTagsAttrName.pop()
}
};
const getNodes = () => {
if (nestedNodes.length) {
const nestedNode = nestedNodes[nestedNodes.length - 1];
return nestedNode.content
}
return nodes
};
let token;
while (token = tokens.shift()) {
if (!token) {
continue;
}
if (isTagToken(token)) {
if (this.isAllowedTag(getTagName(token))) {
// [tag]
if (isTagStart(token)) {
createCurTag(token);
if (isNestedTag(token)) {
nestedNodes.push(getCurTag())
} else {
getNodes().push(getCurTag());
clearCurTag()
}
}
// [/tag]
if (isTagEnd(token)) {
clearCurTag();
const lastNestedNode = nestedNodes.pop();
if (lastNestedNode) {
getNodes().push(lastNestedNode)
} else {
console.warn(`Inconsistent tag '${getTokenValue(token)}' on line ${getTokenLine(token)} and column ${getTokenColumn(token)}`);
}
}
} else {
getNodes().push(convertTokenToText(token))
}
}
if (getCurTag()) {
if (isAttrNameToken(token)) {
createCurTagAttrName(token);
getCurTag().attrs[getCurTagAttrName()] = null
} else if (isAttrValueToken(token)) {
getCurTag().attrs[getCurTagAttrName()] = getTokenValue(token);
clearCurTagAttrName()
} else if (isTextToken(token)) {
getCurTag().content.push(getTokenValue(token))
}
} else if (isTextToken(token)) {
getNodes().push(getTokenValue(token))
if (lastNestedNode) {
getNodes().push(lastNestedNode);
} else {
console.warn(`Inconsistent tag '${getTokenValue(token)}' on line ${getTokenLine(token)} and column ${getTokenColumn(token)}`);
}
}
} else {
getNodes().push(convertTokenToText(token));
}
}
return nodes
}
findNestedTags(tokens) {
const tags = tokens.filter(isTagToken).reduce((acc, token) => {
acc[getTokenValue(token)] = true;
return acc
}, {});
const closeChar = getChar(TokenChar.SLASH);
return Object.keys(tags).reduce((arr, key) => {
if (tags[key] && tags[closeChar + key]) {
arr.push(key)
}
return arr;
}, [])
}
isAllowedTag(value) {
if (this.options.allowOnlyTags && this.options.allowOnlyTags.length) {
return this.options.allowOnlyTags.indexOf(value) >= 0
if (getCurTag()) {
if (isAttrNameToken(token)) {
createCurTagAttrName(token);
getCurTag().attrs[getCurTagAttrName()] = null;
} else if (isAttrValueToken(token)) {
getCurTag().attrs[getCurTagAttrName()] = getTokenValue(token);
clearCurTagAttrName();
} else if (isTextToken(token)) {
getCurTag().content.push(getTokenValue(token));
}
return true
} else if (isTextToken(token)) {
getNodes().push(getTokenValue(token));
}
}
};
return nodes;
}
findNestedTags(tokens) {
const tags = tokens.filter(isTagToken).reduce((acc, token) => {
acc[getTokenValue(token)] = true;
return acc;
}, {});
const closeChar = getChar(TokenChar.SLASH);
return Object.keys(tags).reduce((arr, key) => {
if (tags[key] && tags[closeChar + key]) {
arr.push(key);
}
return arr;
}, []);
}
isAllowedTag(value) {
if (this.options.allowOnlyTags && this.options.allowOnlyTags.length) {
return this.options.allowOnlyTags.indexOf(value) >= 0;
}
return true;
}
};
+8 -9
View File
@@ -1,12 +1,11 @@
const Parser = require('./Parser');
const TOKEN = require('./token');
describe("Parser", () => {
test("parse paired tags tokens", () => {
const parser = new Parser([
[TOKEN.TYPE_TAG, 'ch'],
[TOKEN.TYPE_TAG, '/ch']
]);
})
});
describe('Parser', () => {
test('parse paired tags tokens', () => {
const parser = new Parser([
[TOKEN.TYPE_TAG, 'ch'],
[TOKEN.TYPE_TAG, '/ch'],
]);
});
});
+1 -1
View File
@@ -52,7 +52,7 @@ class Tokenizer {
this.colPos = 0;
this.rowPos = 0;
this.index = 0;
this.tokenIndex = -1;
this.tokens = [];
}
+127 -126
View File
@@ -1,144 +1,145 @@
const Tokenizer = require('./Tokenizer');
const TYPE = Tokenizer.TYPE;
describe("Tokenizer", () => {
test("tokenize single tag", () => {
const input = `[SingleTag]`;
const tokens = new Tokenizer(input).tokenize();
describe('Tokenizer', () => {
test('tokenize single tag', () => {
const input = '[SingleTag]';
const tokens = new Tokenizer(input).tokenize();
expect(tokens).toBeInstanceOf(Array);
expect(tokens).toEqual([
[TYPE.TAG, 'SingleTag', 0, 0]
])
});
expect(tokens).toBeInstanceOf(Array);
expect(tokens).toEqual([
[TYPE.TAG, 'SingleTag', 0, 0],
]);
});
test("tokenize single tag with spaces", () => {
const input = `[Single Tag]`;
const tokens = new Tokenizer(input).tokenize();
test('tokenize single tag with spaces', () => {
const input = '[Single Tag]';
const tokens = new Tokenizer(input).tokenize();
expect(tokens).toBeInstanceOf(Array);
expect(tokens).toEqual([
[TYPE.TAG, 'Single Tag', 0, 0]
])
});
expect(tokens).toBeInstanceOf(Array);
expect(tokens).toEqual([
[TYPE.TAG, 'Single Tag', 0, 0],
]);
});
test("tokenize tag as param", () => {
const input = `[color="#ff0000"]Text[/color]`;
const tokens = new Tokenizer(input).tokenize();
test('tokenize tag as param', () => {
const input = '[color="#ff0000"]Text[/color]';
const tokens = new Tokenizer(input).tokenize();
expect(tokens).toBeInstanceOf(Array);
expect(tokens).toEqual([
[TYPE.TAG, 'color', 0, 0],
[TYPE.ATTR_VALUE, '#ff0000', 6, 0],
[TYPE.WORD, 'Text', 17, 0],
[TYPE.TAG, '/color', 21, 0]
])
});
expect(tokens).toBeInstanceOf(Array);
expect(tokens).toEqual([
[TYPE.TAG, 'color', 0, 0],
[TYPE.ATTR_VALUE, '#ff0000', 6, 0],
[TYPE.WORD, 'Text', 17, 0],
[TYPE.TAG, '/color', 21, 0],
]);
});
test("tokenize tag param without quotemarks", () => {
const input = `[style color=#ff0000]Text[/style]`;
const tokens = new Tokenizer(input).tokenize();
test('tokenize tag param without quotemarks', () => {
const input = '[style color=#ff0000]Text[/style]';
const tokens = new Tokenizer(input).tokenize();
expect(tokens).toBeInstanceOf(Array);
expect(tokens).toEqual([
[TYPE.TAG, 'style', 0, 0],
[TYPE.ATTR_NAME, 'color', 6, 0],
[TYPE.ATTR_VALUE, '#ff0000', 12, 0],
[TYPE.WORD, 'Text', 21, 0],
[TYPE.TAG, '/style', 25, 0]
])
});
expect(tokens).toBeInstanceOf(Array);
expect(tokens).toEqual([
[TYPE.TAG, 'style', 0, 0],
[TYPE.ATTR_NAME, 'color', 6, 0],
[TYPE.ATTR_VALUE, '#ff0000', 12, 0],
[TYPE.WORD, 'Text', 21, 0],
[TYPE.TAG, '/style', 25, 0],
]);
});
test("tokenize list tag with items", () => {
const input = `[list]
test('tokenize list tag with items', () => {
const input = `[list]
[*] Item 1.
[*] Item 2.
[*] Item 3.
[/list]`;
const tokens = new Tokenizer(input).tokenize();
const tokens = new Tokenizer(input).tokenize();
expect(tokens).toBeInstanceOf(Array);
expect(tokens).toEqual([
[TYPE.TAG, 'list', 0, 0],
[TYPE.NEW_LINE, '\n', 6, 0],
[TYPE.SPACE, ' ', 0, 1],
[TYPE.SPACE, ' ', 1, 1],
[TYPE.SPACE, ' ', 2, 1],
[TYPE.TAG, '*', 3, 1],
[TYPE.SPACE, ' ', 6, 1],
[TYPE.WORD, 'Item', 7, 1],
[TYPE.SPACE, ' ', 11, 1],
[TYPE.WORD, '1.', 11, 1],
[TYPE.NEW_LINE, '\n', 14, 1],
[TYPE.SPACE, ' ', 0, 2],
[TYPE.SPACE, ' ', 1, 2],
[TYPE.SPACE, ' ', 2, 2],
[TYPE.TAG, '*', 3, 2],
[TYPE.SPACE, ' ', 6, 2],
[TYPE.WORD, 'Item', 14, 1],
[TYPE.SPACE, ' ', 11, 2],
[TYPE.WORD, '2.', 11, 2],
[TYPE.NEW_LINE, '\n', 14, 2],
[TYPE.SPACE, ' ', 0, 3],
[TYPE.SPACE, ' ', 1, 3],
[TYPE.SPACE, ' ', 2, 3],
[TYPE.TAG, '*', 3, 3],
[TYPE.SPACE, ' ', 6, 3],
[TYPE.WORD, 'Item', 14, 2],
[TYPE.SPACE, ' ', 11, 3],
[TYPE.WORD, '3.', 11, 3],
[TYPE.NEW_LINE, '\n', 14, 3],
[TYPE.TAG, '/list', 0, 4]
])
expect(tokens).toBeInstanceOf(Array);
expect(tokens).toEqual([
[TYPE.TAG, 'list', 0, 0],
[TYPE.NEW_LINE, '\n', 6, 0],
[TYPE.SPACE, ' ', 0, 1],
[TYPE.SPACE, ' ', 1, 1],
[TYPE.SPACE, ' ', 2, 1],
[TYPE.TAG, '*', 3, 1],
[TYPE.SPACE, ' ', 6, 1],
[TYPE.WORD, 'Item', 7, 1],
[TYPE.SPACE, ' ', 11, 1],
[TYPE.WORD, '1.', 11, 1],
[TYPE.NEW_LINE, '\n', 14, 1],
[TYPE.SPACE, ' ', 0, 2],
[TYPE.SPACE, ' ', 1, 2],
[TYPE.SPACE, ' ', 2, 2],
[TYPE.TAG, '*', 3, 2],
[TYPE.SPACE, ' ', 6, 2],
[TYPE.WORD, 'Item', 14, 1],
[TYPE.SPACE, ' ', 11, 2],
[TYPE.WORD, '2.', 11, 2],
[TYPE.NEW_LINE, '\n', 14, 2],
[TYPE.SPACE, ' ', 0, 3],
[TYPE.SPACE, ' ', 1, 3],
[TYPE.SPACE, ' ', 2, 3],
[TYPE.TAG, '*', 3, 3],
[TYPE.SPACE, ' ', 6, 3],
[TYPE.WORD, 'Item', 14, 2],
[TYPE.SPACE, ' ', 11, 3],
[TYPE.WORD, '3.', 11, 3],
[TYPE.NEW_LINE, '\n', 14, 3],
[TYPE.TAG, '/list', 0, 4],
]);
});
test('tokenize bad tags as texts', () => {
const inputs = [
'[]',
'[=]',
'![](image.jpg)',
'x html([a. title][, alt][, classes]) x',
'[/y]',
'[sc',
// '[sc / [/sc]',
// '[sc arg="val',
];
const asserts = [
[[TYPE.WORD, '[]', 0, 0]],
[[TYPE.WORD, '[=]', 0, 0]],
[
[TYPE.WORD, '!', 0, 0],
[TYPE.WORD, '[](image.jpg)', 1, 0],
],
[
[TYPE.WORD, 'x', 0, 0],
[TYPE.SPACE, ' ', 1, 0],
[TYPE.WORD, 'html(', 1, 0],
[TYPE.TAG, 'a. title', 7, 0],
[TYPE.TAG, ', alt', 17, 0],
[TYPE.TAG, ', classes', 24, 0],
[TYPE.WORD, ')', 7, 0],
[TYPE.SPACE, ' ', 36, 0],
[TYPE.WORD, 'x', 36, 0],
],
[[TYPE.TAG, '/y', 0, 0]],
[[TYPE.WORD, '[sc', 0, 0]],
[
[TYPE.WORD, '[sc', 0, 0],
[TYPE.SPACE, ' ', 0, 0],
[TYPE.WORD, '/', 0, 0],
[TYPE.SPACE, ' ', 0, 0],
[TYPE.WORD, '[/sc]', 0, 0],
],
];
inputs.forEach((input, idx) => {
const tokens = new Tokenizer(input).tokenize();
expect(tokens).toBeInstanceOf(Array);
expect(tokens).toEqual(asserts[idx]);
});
test("tokenize bad tags as texts", () => {
const inputs = [
'[]',
'[=]',
'![](image.jpg)',
'x html([a. title][, alt][, classes]) x',
'[/y]',
'[sc',
'[sc / [/sc]',
'[sc arg="val',
];
const asserts = [
[[TYPE.WORD, '[]', 0, 0]],
[[TYPE.WORD, '[=]', 0, 0]],
[
[TYPE.WORD, '!', 0, 0],
[TYPE.WORD, '[](image.jpg)', 1, 0]
],
[
[TYPE.WORD, "x", 0, 0],
[TYPE.SPACE, " ", 1, 0],
[TYPE.WORD, "html(", 1, 0],
[TYPE.TAG, "a. title", 7, 0],
[TYPE.TAG, ", alt", 17, 0],
[TYPE.TAG, ", classes", 24, 0],
[TYPE.WORD, ")", 7, 0],
[TYPE.SPACE, " ", 36, 0],
[TYPE.WORD, "x", 36, 0]
],
[[TYPE.TAG, "/y", 0, 0]],
[[TYPE.WORD, '[sc', 0, 0]],
[
[TYPE.WORD, '[sc', 0, 0],
[TYPE.SPACE, ' ', 0, 0],
[TYPE.WORD, '/', 0, 0],
[TYPE.SPACE, ' ', 0, 0],
[TYPE.WORD, '[/sc]', 0, 0]
],
];
inputs.forEach((input, idx) => {
const tokens = new Tokenizer(input).tokenize();
expect(tokens).toBeInstanceOf(Array);
expect(tokens).toEqual(asserts[idx])
});
})
});
});
});
-215
View File
@@ -1,215 +0,0 @@
const attrNameChars = '[a-zA-Z0-9\\.\\-_:;/]'
const attrValueChars = '[a-zA-Z0-9\\.\\-_:;#/\\s]'
const pattern = `\\[(\/\\w*)\\]|\\[(\\w*)+(=(["])${attrValueChars}*\\4)?( (${attrNameChars}+)?=(["])(${attrValueChars}+)\\7)*\\]`
const TAG_RE = new RegExp(pattern, 'g')
const EOL = '\n'
const WHITESPACE = ' '
const isNode = el => typeof el === 'object' && el.tag
const isStringNode = el => typeof el === 'string'
const isChordNode = el => el.tag === 'ch'
const isTabNode = el => el.tag === 'tab'
const isSyllableNode = el => el.tag === 'syllable'
const isTextNode = el => el.tag === 'text'
const isEOL = el => el === EOL
const getNodeLength = node => {
if (isNode(node)) {
node.content.reduce((count, contentNode) => count + getNodeLength(contentNode), 0)
} else if (isStringNode(node)) {
return node.length
}
return 0
}
const tagsDefinition = {
ch: {
closable: true,
},
syllable: {
closable: true,
},
tab: {
closable: true,
},
}
// @TODO: Разбить на парсер и токенайзер, ноды и токены должны жить отдельно
/**
* Парсит контент таба с BB кодами в AST дерево [{tag:'ch', attrs:{..}, content:[...]}]
*
* @example
*
* textTabParser
* .parse('[Intro] [ch app=123]G[/ch] hello world', {ch: {closable: true}})
*
*/
module.exports = {
parse(str, tags = tagsDefinition) {
this.tags = tags
const tokens = this.tokenize(str)
const ast = this.parseTokens(tokens)
return ast
},
tokenize(str) {
let tokens = []
let match
let lastIndex = 0
// console.time('tokenize')
while (match = TAG_RE.exec(str)) {
const delta = match.index - lastIndex
if (delta > 0) {
tokens = tokens.concat(this.toTextTokens(str.substr(lastIndex, delta)))
}
tokens.push(this.tagToken(match))
lastIndex = TAG_RE.lastIndex
}
const delta = str.length - lastIndex
if (delta > 0) {
tokens = tokens.concat(this.toTextTokens(str.substr(lastIndex, delta)))
}
// console.timeEnd('tokenize')
return tokens
},
parseTokens(tokens) {
const nodes = []
let curToken
const nestedNodes = []
function getNodes() {
if (nestedNodes.length) {
const nestedNode = nestedNodes[nestedNodes.length - 1]
return nestedNode.content
}
return nodes
}
// console.time('parseTokens')
while (curToken = tokens.shift()) {
curToken = this.isTokenSupported(curToken) ? curToken : this.asTextToken(curToken)
if (curToken.isText) {
getNodes().push(curToken.text)
}
if (curToken.isTag) {
const node = this.tagNode(curToken.tagName, curToken.attributes)
if (curToken.isStart) {
if (this.isTokenHasCloseTag(curToken)) {
nestedNodes.push(node)
} else {
getNodes().push(node)
}
}
if (curToken.isEnd) {
const lastNestedNode = nestedNodes.pop()
if (lastNestedNode) {
getNodes().push(lastNestedNode)
} else {
console.error(`Inconsistent tag '${curToken.tagName}'`)
}
}
}
}
// console.timeEnd('parseTokens')
return nodes
},
isTokenSupported(token) {
return token.isTag && this.tags && this.tags[token.tagName]
},
isTokenHasCloseTag(token) {
return this.tags && this.tags[token.tagName] && this.tags[token.tagName].closable
},
tagNode(name, attrs, content = []) {
return { tag: name, attrs, content }
},
toTextTokens(text) {
const tokens = []
const chars = text.split('')
let currText = ''
const flushText = () => {
if (currText) {
tokens.push(this.textToken(currText))
currText = ''
}
}
chars.forEach((char) => {
if (char === EOL || char === WHITESPACE) {
flushText()
tokens.push(this.textToken(char))
} else {
currText += char
}
})
if (currText) {
tokens.push(this.textToken(currText))
}
return tokens
},
textToken(text) {
return { isText: true, text }
},
tagToken(match) {
if (typeof match[1] === 'undefined') { // Start tag
const tagName = match[2]
const attributes = {}
const ATTR_RE = new RegExp(`(${attrNameChars}+)?=(["])(${attrValueChars}+)\\2`, 'g')
const attrStr = match[0].substr(1 + tagName.length, match[0].length - 2 - tagName.length)
let attrMatch
while (attrMatch = ATTR_RE.exec(attrStr)) {
if (typeof attrMatch[1] === 'undefined') { // The tag attribute
attributes[tagName] = attrMatch[3]
} else { // Normal attribute
attributes[attrMatch[1]] = attrMatch[3]
}
}
return { isStart: true, isTag: true, tagName, attributes, text: match[0] }
}
// End tag
return { isEnd: true, isTag: true, tagName: match[1].substr(1, match[1].length - 1) }
},
asTextToken(token) {
if (token.isTag && token.isStart) {
return this.textToken(token.text)
}
if (token.isTag && token.isEnd) {
return this.textToken(`[/${token.tagName}]`)
}
return token
},
}
@@ -1,19 +0,0 @@
const parse = require('../index');
const options = {
closableTags: ['ch', 'syllable', 'tab']
};
const textStub = require("./test/stub");
const count = 0;
const parsers3 = [];
console.time('newParser');
for (let i = 0; i <= count; i++) {
const parser3 = parse(textStub, options);
parsers3.push(parser3);
}
console.timeEnd('newParser');
// console.log(JSON.stringify(parsers3));
@@ -1,15 +0,0 @@
const OldParser = require('./OldParser')
const textStub = require("./test/stub");
const count = 0;
const oldParsers3 = [];
console.time('oldParser');
for (let i = 0; i <= count; i++) {
const oldParser3 = OldParser.parse(textStub);
oldParsers3.push(oldParser3);
}
console.timeEnd('oldParser');
// console.log(JSON.stringify(oldParsers3));
File diff suppressed because it is too large Load Diff
+21 -21
View File
@@ -1,26 +1,26 @@
const N = "\n".charCodeAt(0);
const TAB = "\t".charCodeAt(0);
const F = "\f".charCodeAt(0);
const R = "\r".charCodeAt(0);
const N = '\n'.charCodeAt(0);
const TAB = '\t'.charCodeAt(0);
const F = '\f'.charCodeAt(0);
const R = '\r'.charCodeAt(0);
const EQ = "=".charCodeAt(0);
const QUOTEMARK = "\"".charCodeAt(0);
const SPACE = " ".charCodeAt(0);
const EQ = '='.charCodeAt(0);
const QUOTEMARK = '"'.charCodeAt(0);
const SPACE = ' '.charCodeAt(0);
const OPEN_BRAKET = "[".charCodeAt(0);
const CLOSE_BRAKET = "]".charCodeAt(0);
const OPEN_BRAKET = '['.charCodeAt(0);
const CLOSE_BRAKET = ']'.charCodeAt(0);
const SLASH = "/".charCodeAt(0);
const SLASH = '/'.charCodeAt(0);
module.exports = {
N,
F,
R,
TAB,
EQ,
QUOTEMARK,
SPACE,
OPEN_BRAKET,
CLOSE_BRAKET,
SLASH
};
N,
F,
R,
TAB,
EQ,
QUOTEMARK,
SPACE,
OPEN_BRAKET,
CLOSE_BRAKET,
SLASH,
};
+4 -6
View File
@@ -1,5 +1,5 @@
{
"name": "bbob-parser",
"name": "@bbob/parser",
"version": "1.0.1",
"description": "Fast BB Code parser written in pure javascript, no dependencies",
"main": "index.js",
@@ -7,14 +7,12 @@
"test": "test"
},
"scripts": {
"test": "jest"
"test": "../../node_modules/.bin/jest --",
"cover": "../../node_modules/.bin/jest --coverage",
"lint": "../../node_modules/.bin/eslint ."
},
"author": "Nikolay Kostyurin <jilizart@gmail.com>",
"license": "MIT",
"devDependencies": {
"jest": "^23.1.0",
"xbbcode-parser": "^0.1.2"
},
"publishConfig": {
"registry": "https://registry.npmjs.org/"
}
+8 -8
View File
@@ -1,11 +1,11 @@
const Tokenizer = require("./Tokenizer");
const Parser = require("./Parser");
const Tokenizer = require('./Tokenizer');
const Parser = require('./Parser');
module.exports = function parse(input, options) {
const tokenizer = new Tokenizer(input);
const tokens = tokenizer.tokenize();
const parser = new Parser(tokens, options);
const ast = parser.parse();
const tokenizer = new Tokenizer(input);
const tokens = tokenizer.tokenize();
const parser = new Parser(tokens, options);
const ast = parser.parse();
return ast
};
return ast;
};
+30 -30
View File
@@ -1,37 +1,37 @@
const parse = require('./index');
const options = {
allowOnlyTags: ['ch', 'syllable', 'tab'],
allowOnlyTags: ['ch', 'syllable', 'tab'],
};
describe("parse", () => {
test("tag with spaces", () => {
const ast = parse(`[Verse 2]`);
describe('parse', () => {
test('tag with spaces', () => {
const ast = parse('[Verse 2]');
expect(ast).toEqual([{tag: 'Verse 2', attrs: {}, content: []}]);
});
expect(ast).toEqual([{ tag: 'Verse 2', attrs: {}, content: [] }]);
});
// test("pass invalid tags", () => {
// const inputs = [
// '[]',
// '![](image.jpg)',
// 'x html([a. title][, alt][, classes]) x',
// '[/y]',
// '[sc',
// '[sc / [/sc]',
// '[sc arg="val',
// ];
//
// const ast1 = parse(inputs[0]);
//
//
//
// console.log('ast1', ast1);
//
//
//
// expect(ast1).toEqual([
//
// ]);
// })
});
// test("pass invalid tags", () => {
// const inputs = [
// '[]',
// '![](image.jpg)',
// 'x html([a. title][, alt][, classes]) x',
// '[/y]',
// '[sc',
// '[sc / [/sc]',
// '[sc arg="val',
// ];
//
// const ast1 = parse(inputs[0]);
//
//
//
// console.log('ast1', ast1);
//
//
//
// expect(ast1).toEqual([
//
// ]);
// })
});